Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/la-2026-rib-26-019.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use Louisiana's official 2026 standard deduction ($12,838 single or separate, $25,676 joint, head of household or surviving spouse) and age-65 retirement income exemption ($12,324) from LDR Revenue Information Bulletin 26-019, and project both after 2026 with La. R.S. 47:294(B) and 47:44.1(A)'s annual CPI-U adjustment, using BLS's not seasonally adjusted December CPI-U (new gov.bls.cpi.cpi_u_nsa_december) where published.
26 changes: 26 additions & 0 deletions policyengine_us/parameters/gov/bls/cpi/cpi_u_nsa_december.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
description: The Bureau of Labor Statistics publishes this December value of the Consumer Price Index for All Urban Consumers, not seasonally adjusted, from which it reports the index's December-over-December percentage change.
values:
# BLS series CUUR0000SA0 (U.S. city average, all items, 1982-84=100).
2012-12-01: 229.601
2013-12-01: 233.049
2014-12-01: 234.812
2015-12-01: 236.525
2016-12-01: 241.432
2017-12-01: 246.524
2018-12-01: 251.233
2019-12-01: 256.974
2020-12-01: 260.474
2021-12-01: 278.802
2022-12-01: 296.797
2023-12-01: 306.746
2024-12-01: 315.605
2025-12-01: 324.054
metadata:
unit: currency-USD
label: CPI-U, not seasonally adjusted, December
economy: false
reference:
- title: BLS | CPI-U, U.S. city average, all items, not seasonally adjusted (CUUR0000SA0)
href: https://data.bls.gov/timeseries/CUUR0000SA0
- title: Consumer Price Index News Release - 2025 M12 Results
href: https://www.bls.gov/news.release/archives/cpi_01132026.htm
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,40 @@ metadata:
period: year
unit: currency-USD
label: Louisiana standard deduction amount
uprating: gov.bls.cpi.cpi_u
# Years after the last value below follow R.S. 47:294(B)'s annual CPI-U
# adjustment, computed by extend_la_cpi_u_indexed_amounts in
# policyengine_us/parameters/uprating_extensions.py.
propagate_metadata_to_children: true
breakdown:
- filing_status
reference:
- title: Louisiana Revised Statutes, RS 47:294 - Standard deduction
href: https://www.legis.la.gov/legis/Law.aspx?d=101761
- title: Louisiana Register, February 20, 2026, Income Tax Withholding Tables notice
href: https://bese.louisiana.gov/docs/default-source/rulemaking-docket/feb-louisiana-register.pdf#page=127
- title: Louisiana Department of Revenue, Revenue Information Bulletin 26-019 (September 28, 2026), Inflation Adjustment to Standard Deduction and Retirement Income Exemption
href: https://dam.ldr.la.gov/lawspolicies/RIB%2026-019.pdf#page=1
- title: Consumer Price Index News Release - 2025 M12 Results
href: https://www.bls.gov/news.release/archives/cpi_01132026.htm
- title: 2025 Louisiana Resident Income Tax Return Instructions, Line 8
href: https://dam.ldr.la.gov/taxforms/IT540i%20WEB(2025)D11.pdf#page=3

SINGLE:
2025-01-01: 12_500
# 2026 amount applies the December-to-December 2025 CPI-U increase:
# 12,500 * (324.054 / 315.605), rounded to the nearest dollar.
2026-01-01: 12_835
# RIB 26-019 applies a 2.7% CPI-U multiplier (BLS reported the December
# 2024 to December 2025 increase as 2.7%): 12,500 * 1.027 = 12,837.50,
# rounded to 12,838.
2026-01-01: 12_838
SURVIVING_SPOUSE:
2025-01-01: 25_000
2026-01-01: 25_670
2026-01-01: 25_676
SEPARATE:
2025-01-01: 12_500
2026-01-01: 12_835
# The legal code defines the HoH and Joint amounts as 200% of the
# Single amount.
2026-01-01: 12_838
# R.S. 47:294(A)(2) sets the head of household, joint and surviving spouse
# amounts at 200% of the single amount; RIB 26-019 doubles the rounded
# single amount (2 * 12,838 = 25,676, where 25,000 * 1.027 = 25,675).
HEAD_OF_HOUSEHOLD:
2025-01-01: 25_000
2026-01-01: 25_670
2026-01-01: 25_676
JOINT:
2025-01-01: 25_000
2026-01-01: 25_670
2026-01-01: 25_676
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
description: Louisiana caps the retirement income exemption at this amount for filers, based on age.

brackets:
- threshold:
- threshold:
2021-01-01: 0
amount:
amount:
2021-01-01: 0
- threshold:
2001-01-01: 65
amount:
2001-01-01: 6_000
2025-01-01: 12_000
# RIB 26-019 applies a 2.7% CPI-U multiplier (BLS reported the December
# 2024 to December 2025 increase as 2.7%): 12,000 * 1.027 = 12,324.
# Later years follow R.S. 47:44.1(A)'s annual CPI-U adjustment,
# computed by extend_la_cpi_u_indexed_amounts in
# policyengine_us/parameters/uprating_extensions.py.
2026-01-01: 12_324

metadata:
reference:
- title: Louisiana statutes, RS 47:44.1 - Annual retirement or disability income; exemption from taxation (A)
href: https://www.legis.la.gov/legis/Law.aspx?d=102133
- title: Louisiana Department of Revenue, Revenue Information Bulletin 26-019 (September 28, 2026), Inflation Adjustment to Standard Deduction and Retirement Income Exemption
href: https://dam.ldr.la.gov/lawspolicies/RIB%2026-019.pdf#page=1
- title: 2025 Louisiana Resident Income Tax Return Instructions, What's New - Code 6E
href: https://dam.ldr.la.gov/taxforms/IT540i%20WEB(2025)D11.pdf#page=1
- title: 2024 Louisiana Resident Income Tax Return
Expand All @@ -24,7 +32,7 @@ metadata:
- title: 2022 Louisiana Resident Income Tax Return Instructions, Exemption Code 06E
href: https://dam.ldr.la.gov/taxforms/IT540iWEB(2022)D1.pdf#page=6
threshold_unit: year
amount_unit: currency-USD
amount_unit: currency-USD
type: single_amount
period: year
label: Louisiana retirement income exemption cap
173 changes: 172 additions & 1 deletion policyengine_us/parameters/uprating_extensions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
"""Unified script to extend all uprating factors through 2100."""

import math
from decimal import ROUND_HALF_UP, Decimal
from typing import Optional, Tuple

from policyengine_us.model_api import *
from policyengine_core.periods import instant
from policyengine_core.periods import Instant, instant


LONG_RUN_CBO_INCOME_BY_SOURCE_PARAMETERS = (
Expand Down Expand Up @@ -202,6 +203,170 @@ def extend_or_ctc_parameters(parameters: ParameterNode, end_year: int) -> None:
)


def get_projected_cpi_u_for_month(cpi: Parameter, month: Instant) -> float:
"""CPI-U for a month, interpolated toward CBO's projections if unobserved.

The model's CPI-U series holds BLS monthly observations, then CBO's
calendar-year averages as projection points at February instants (see
``get_average_for_12_months_ending_august`` and #9608; the "# YYYY
value" comments in cpi_u.yaml label the tax year a point feeds, not its
calendar year). A calendar-year average sits halfway between June and
July of its year. So an unobserved month is interpolated geometrically,
by month, between the last observation and the next projection point
placed there, or between two such points; past the last point the index
holds flat.

As in ``get_average_for_12_months_ending_august``, only non-February
instants identify the end of the observed series, and no branch reads
an instant a refresh could have turned from projection into
observation: a February in the last observation's year is never an
anchor. That only matters when observations end in January (any later
month already follows that February): the February may then hold that
year's projection or an observation, so it is skipped, and the months
after January interpolate toward the next year's point.
"""
last_observation = max(
instant(value.instant_str)
for value in cpi.values_list
if not value.instant_str.endswith("-02-01")
)
if month <= last_observation:
return cpi(month)

def month_index(date: Instant) -> float:
return 12 * date.year + date.month - 1

anchors = [(month_index(last_observation), cpi(last_observation))] + sorted(
(12 * int(value.instant_str[:4]) + 5.5, value.value)
for value in cpi.values_list
if value.instant_str.endswith("-02-01")
and int(value.instant_str[:4]) > last_observation.year
)
target = month_index(month)
for (start, start_level), (end, end_level) in zip(anchors, anchors[1:]):
if target <= end:
share = (target - start) / (end - start)
return start_level * (end_level / start_level) ** share
return anchors[-1][1]


def get_la_cpi_u_percentage_increase(cpi: ParameterNode, year: int) -> float:
"""The CPI-U percentage increase for calendar ``year``, as BLS reports it.

La. R.S. 47:294(B) and 47:44.1(A) multiply the prior year's amount by
"the percentage increase in the Consumer Price Index ... (CPI-U), as
reported by the United States Department of Labor, Bureau of Labor
Statistics ... for the previous calendar year". For tax year 2026, LDR
Revenue Information Bulletin 26-019 applied 2.7%. The bulletin names no
comparison months, but 2.7% is the December 2024 to December 2025 change
as BLS reported it (324.054 / 315.605, not seasonally adjusted: 2.677%).
The 2025 annual-average change (321.943 / 313.689) was 2.6%, and the
unrounded 2.677% would give $12,835 rather than the bulletin's $12,838.
So the percentage is the December-over-December change rounded to one
decimal place, as BLS reports it. A fall in the index is not an
increase, so the percentage is never negative.

``cpi`` is the ``gov.bls.cpi`` node. BLS reports the change from the not
seasonally adjusted index, so the published Decembers in
``cpi_u_nsa_december`` are used when both years have one. Otherwise
both Decembers come from the model's CPI-U series (seasonally adjusted
from January 2024), through ``get_projected_cpi_u_for_month``; mixing
the two would add December's seasonal factor (about 0.6% in 2025) to
the change.
"""
december = cpi.cpi_u_nsa_december
published = {int(value.instant_str[:4]) for value in december.values_list}
if {year - 1, year} <= published:
current = december(f"{year}-12-01")
prior = december(f"{year - 1}-12-01")
else:
current = get_projected_cpi_u_for_month(cpi.cpi_u, instant(f"{year}-12-01"))
prior = get_projected_cpi_u_for_month(cpi.cpi_u, instant(f"{year - 1}-12-01"))
change = 100 * (Decimal(repr(current)) / Decimal(repr(prior)) - 1)
percentage = change.quantize(Decimal("0.1"), rounding=ROUND_HALF_UP)
return float(max(percentage, 0))


def adjust_la_amount_for_inflation(prior_amount: float, percentage: float) -> float:
"""Apply a La. R.S. 47:294(B) / 47:44.1(A) adjustment to a prior amount.

RIB 26-019's amounts are the adjusted amounts rounded to the nearest
dollar, with half dollars rounding up: $12,500 x 1.027 = $12,837.50
became $12,838. The arithmetic is decimal because in binary floating
point 12,500 x 1.027 is just below 12,837.5 and would round down.
"""
adjusted = Decimal(str(prior_amount)) * (100 + Decimal(str(percentage))) / 100
return float(adjusted.quantize(Decimal(1), rounding=ROUND_HALF_UP))


def extend_la_cpi_u_indexed_amounts(parameters: ParameterNode, end_year: int) -> None:
"""Project Louisiana's CPI-U-indexed standard deduction and retirement cap.

Each year after the last LDR-published amount, the single and
married-separate standard deduction (R.S. 47:294(A)(1)) and the age-65
retirement income exemption (R.S. 47:44.1(A)) become the prior year's
amount adjusted by the prior calendar year's CPI-U percentage increase
(see ``get_la_cpi_u_percentage_increase``), rounded to the nearest
dollar. The joint, head of household and surviving spouse deduction is
200% of the single amount (R.S. 47:294(A)(2)), so it doubles the rounded
single amount rather than being adjusted on its own. Amounts encoded in
the YAML take precedence for their own years.

Like the other projections in this module, the values are fixed when
the parameter tree is built. A reform that changes the single amount
does not carry through to the other filing statuses or later years.
"""
la = parameters.gov.states.la.tax.income
cpi = parameters.gov.bls.cpi
standard = la.deductions.standard.amount
retirement_caps = [
bracket.amount
for bracket in la.exempt_income.retirement.cap.brackets
if bracket.threshold(f"{end_year}-01-01") == 65
]
if len(retirement_caps) != 1:
raise ValueError(
"Expected one age-65 bracket in the Louisiana retirement income "
f"exemption cap; found {len(retirement_caps)}."
)
percentages = {}
for parameter in (standard.SINGLE, retirement_caps[0]):
first_projected_year = 1 + max(
int(value.instant_str[:4]) for value in parameter.values_list
)
for year in range(first_projected_year, end_year + 1):
if year - 1 not in percentages:
percentages[year - 1] = get_la_cpi_u_percentage_increase(cpi, year - 1)
parameter.update(
period=f"year:{year}-01-01:1",
value=adjust_la_amount_for_inflation(
parameter(f"{year - 1}-01-01"), percentages[year - 1]
),
)
parameter.update(
start=instant(f"{end_year}-01-01"),
value=parameter(f"{end_year}-01-01"),
)
for parameter, share_of_single in (
(standard.SEPARATE, 1),
(standard.JOINT, 2),
(standard.HEAD_OF_HOUSEHOLD, 2),
(standard.SURVIVING_SPOUSE, 2),
):
first_projected_year = 1 + max(
int(value.instant_str[:4]) for value in parameter.values_list
)
for year in range(first_projected_year, end_year + 1):
parameter.update(
period=f"year:{year}-01-01:1",
value=share_of_single * standard.SINGLE(f"{year}-01-01"),
)
parameter.update(
start=instant(f"{end_year}-01-01"),
value=parameter(f"{end_year}-01-01"),
)


def extend_wa_millionaires_standard_deduction(
parameters: ParameterNode,
end_year: int,
Expand Down Expand Up @@ -633,6 +798,12 @@ def set_all_uprating_parameters(parameters: ParameterNode) -> ParameterNode:
# above so projected windows are available.
extend_or_ctc_parameters(parameters, end_year=END_YEAR)

# Louisiana's standard deduction and age-65 retirement income exemption
# follow R.S. 47:294(B) and 47:44.1(A): the prior year's amount times the
# prior calendar year's CPI-U increase. Must run after the CPI-U extension
# above so projected Decembers are available.
extend_la_cpi_u_indexed_amounts(parameters, end_year=END_YEAR)

# The limits on a dependent's standard deduction follow 26 U.S.C.
# 63(c)(4)'s chained CPI-U schedule, computed from the statutory 1987 and
# 1997 bases. Must run after the Chained CPI-U extension above so
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,42 @@
output:
la_standard_deduction: 25_000

- name: 2026 single filer uses prior-calendar-year CPI-U adjustment
- name: 2026 single filer gets the RIB 26-019 amount, 12,500 x 1.027 rounded
period: 2026
input:
state_code: LA
filing_status: SINGLE
output:
la_standard_deduction: 12_835
la_standard_deduction: 12_838

- name: 2026 head of household filer gets twice the single amount
- name: 2026 married filing separately filer gets the single amount
period: 2026
input:
state_code: LA
filing_status: SEPARATE
output:
la_standard_deduction: 12_838

- name: 2026 head of household filer gets twice the rounded single amount
period: 2026
input:
state_code: LA
filing_status: HEAD_OF_HOUSEHOLD
output:
la_standard_deduction: 25_670
la_standard_deduction: 25_676

- name: 2026 joint filer gets twice the rounded single amount, not 25,000 x 1.027
period: 2026
input:
state_code: LA
filing_status: JOINT
output:
la_standard_deduction: 25_676

- name: 2026 surviving spouse gets twice the rounded single amount
period: 2026
input:
state_code: LA
filing_status: SURVIVING_SPOUSE
output:
la_standard_deduction: 25_676
Loading
Loading