Skip to content
Open
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/8676.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add Medicaid MAGI claimant tax unit links and missing-claimant fallback diagnostics.
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@
output:
medicaid_is_tax_dependent: [true, false]
medicaid_uses_non_filer_rules: [true, false]
medicaid_uses_missing_claimant_fallback: [true, false]
medicaid_missing_claimant_fallback_person_count: [1, 0]
medicaid_household_income_member: [5_000, 40_000]
medicaid_household_income: [5_000, 40_000]

Expand Down Expand Up @@ -173,3 +175,202 @@
output:
medicaid_tax_dependent_exception_non_custodial_parent: [false, true]
medicaid_uses_non_filer_rules: [false, true]

- name: Case 7, child claimed by a known outside tax unit uses the claimant tax household.
period: 2024
input:
people:
person1:
age: 40
employment_income: 30_000
person2:
age: 10
medicaid_claiming_tax_unit_id: 1
tax_units:
tax_unit1:
members: [person1]
tax_unit_id: 1
tax_unit2:
members: [person2]
tax_unit_id: 2
households:
household:
members: [person1, person2]
state_group_str: CONTIGUOUS_US
families:
family1:
members: [person1]
family2:
members: [person2]
marital_units:
marital_unit1:
members: [person1]
marital_unit2:
members: [person2]
output:
medicaid_is_tax_dependent: [false, true]
medicaid_has_known_claiming_tax_unit: [false, true]
medicaid_uses_missing_claimant_fallback: [false, false]
medicaid_uses_non_filer_rules: [false, false]
medicaid_household_size: [2, 2]
medicaid_household_income: [30_000, 30_000]
medicaid_missing_claimant_fallback_person_count: [0, 0]

- name: Case 8, child claimed by an unknown outside tax unit uses the missing-claimant fallback.
period: 2024
input:
people:
person1:
age: 40
employment_income: 30_000
own_children_in_household: 1
person2:
age: 10
claimed_as_dependent_on_another_return: true
tax_units:
tax_unit1:
members: [person1]
tax_unit_id: 1
tax_unit2:
members: [person2]
tax_unit_id: 2
households:
household:
members: [person1, person2]
state_group_str: CONTIGUOUS_US
families:
family:
members: [person1, person2]
marital_units:
marital_unit1:
members: [person1]
marital_unit2:
members: [person2]
output:
medicaid_has_known_claiming_tax_unit: [false, false]
medicaid_uses_missing_claimant_fallback: [false, true]
medicaid_uses_non_filer_rules: [false, true]
medicaid_household_size: [1, 2]
medicaid_household_income: [30_000, 30_000]
medicaid_missing_claimant_fallback_person_count: [0, 1]

- name: Case 9, known non-custodial-parent claim still uses the dependent exception.
period: 2024
input:
people:
person1:
age: 42
employment_income: 50_000
person2:
age: 40
employment_income: 10_000
own_children_in_household: 1
person3:
age: 10
claimed_as_dependent_on_another_return: true
medicaid_claiming_tax_unit_id: 1
medicaid_tax_dependent_exception_non_custodial_parent: true
tax_units:
tax_unit1:
members: [person1]
tax_unit_id: 1
tax_unit2:
members: [person2]
tax_unit_id: 2
tax_unit3:
members: [person3]
tax_unit_id: 3
households:
household1:
members: [person1]
state_group_str: CONTIGUOUS_US
household2:
members: [person2, person3]
state_group_str: CONTIGUOUS_US
families:
family1:
members: [person1]
family2:
members: [person2, person3]
marital_units:
marital_unit1:
members: [person1]
marital_unit2:
members: [person2]
marital_unit3:
members: [person3]
output:
medicaid_has_known_claiming_tax_unit: [false, false, true]
medicaid_tax_dependent_exception_non_custodial_parent: [false, false, true]
medicaid_uses_missing_claimant_fallback: [false, false, false]
medicaid_uses_non_filer_rules: [false, false, true]
medicaid_household_size: [2, 1, 2]
medicaid_household_income: [50_000, 10_000, 10_000]
medicaid_missing_claimant_fallback_person_count: [0, 0, 0]

- name: Case 10, single filer with no dependent uses the tax household.
period: 2024
input:
people:
person1:
age: 35
employment_income: 30_000
tax_units:
tax_unit:
members: [person1]
households:
household:
members: [person1]
state_group_str: CONTIGUOUS_US
output:
medicaid_has_known_claiming_tax_unit: [false]
medicaid_uses_missing_claimant_fallback: [false]
medicaid_uses_non_filer_rules: [false]
medicaid_household_size: [1]
medicaid_household_income: [30_000]
medicaid_missing_claimant_fallback_person_count: [0]

- name: Case 11, known outside claimant includes the claimant's cohabiting spouse.
period: 2024
input:
people:
person1:
age: 40
employment_income: 50_000
person2:
age: 39
employment_income: 10_000
person3:
age: 10
medicaid_claiming_tax_unit_id: 1
tax_units:
tax_unit1:
members: [person1]
tax_unit_id: 1
cohabitating_spouses: true
tax_unit2:
members: [person2]
tax_unit_id: 2
cohabitating_spouses: true
tax_unit3:
members: [person3]
tax_unit_id: 3
households:
household:
members: [person1, person2, person3]
state_group_str: CONTIGUOUS_US
families:
family1:
members: [person1, person2]
family2:
members: [person3]
marital_units:
marital_unit1:
members: [person1, person2]
marital_unit2:
members: [person3]
output:
medicaid_has_known_claiming_tax_unit: [false, false, true]
medicaid_uses_non_filer_rules: [false, false, false]
medicaid_household_size: [3, 2, 3]
medicaid_household_income: [60_000, 60_000, 60_000]
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from policyengine_us.model_api import *


NO_MEDICAID_CLAIMING_TAX_UNIT_ID = 0


def _sum_by_positive_id(target_id, member_id, values):
target_id = np.asarray(target_id).astype(int)
member_id = np.asarray(member_id).astype(int)
values = np.asarray(values, dtype=float)
result = np.zeros_like(target_id, dtype=float)
valid_members = member_id > NO_MEDICAID_CLAIMING_TAX_UNIT_ID

if not np.any(valid_members):
return result

unique_ids, inverse = np.unique(member_id[valid_members], return_inverse=True)
sums = np.bincount(
inverse,
weights=values[valid_members],
minlength=len(unique_ids),
)
index = np.searchsorted(unique_ids, target_id)
safe_index = np.clip(index, 0, len(unique_ids) - 1)
matched = (target_id > NO_MEDICAID_CLAIMING_TAX_UNIT_ID) & (
unique_ids[safe_index] == target_id
)
return where(matched, sums[safe_index], result)


def _value_by_positive_id(target_id, member_id, values, selector):
target_id = np.asarray(target_id).astype(int)
member_id = np.asarray(member_id).astype(int)
values = np.asarray(values, dtype=float)
selector = np.asarray(selector).astype(bool)
result = np.zeros_like(target_id, dtype=float)
valid_members = (member_id > NO_MEDICAID_CLAIMING_TAX_UNIT_ID) & selector

if not np.any(valid_members):
return result

valid_member_id = member_id[valid_members]
valid_values = values[valid_members]
unique_ids, index = np.unique(valid_member_id, return_index=True)
selected_values = valid_values[index]
lookup_index = np.searchsorted(unique_ids, target_id)
safe_index = np.clip(lookup_index, 0, len(unique_ids) - 1)
matched = (target_id > NO_MEDICAID_CLAIMING_TAX_UNIT_ID) & (
unique_ids[safe_index] == target_id
)
return where(matched, selected_values[safe_index], result)


def medicaid_external_claimed_sum(person, period, target_tax_unit_id, values):
current_tax_unit_id = person.tax_unit("tax_unit_id", period)
claiming_tax_unit_id = person("medicaid_claiming_tax_unit_id", period)
external_claim = person("medicaid_has_known_claiming_tax_unit", period) & (
claiming_tax_unit_id != current_tax_unit_id
)

return _sum_by_positive_id(
target_tax_unit_id,
claiming_tax_unit_id,
values * external_claim,
)


def medicaid_claiming_tax_unit_value(person, period, values):
claiming_tax_unit_id = person("medicaid_claiming_tax_unit_id", period)
current_tax_unit_id = person.tax_unit("tax_unit_id", period)
tax_unit_head = person("is_tax_unit_head", period)
return _value_by_positive_id(
claiming_tax_unit_id,
current_tax_unit_id,
values,
tax_unit_head,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from policyengine_us.model_api import *


class medicaid_claiming_tax_unit_id(Variable):
value_type = int
entity = Person
label = "Tax unit ID claiming this person for Medicaid MAGI household rules"
definition_period = YEAR
reference = "https://www.law.cornell.edu/cfr/text/42/435.603#f_2"
default_value = 0
documentation = (
"Optional identifier for the tax unit expected to claim this person as "
"a dependent. A value of 0 means no known claiming tax unit."
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from policyengine_us.model_api import *
from policyengine_us.variables.gov.hhs.medicaid.income._claiming_tax_unit import (
NO_MEDICAID_CLAIMING_TAX_UNIT_ID,
)


class medicaid_has_known_claiming_tax_unit(Variable):
value_type = bool
entity = Person
label = "Has a known claiming tax unit for Medicaid MAGI household rules"
definition_period = YEAR
reference = "https://www.law.cornell.edu/cfr/text/42/435.603#f_2"

def formula(person, period, parameters):
claiming_tax_unit_id = person("medicaid_claiming_tax_unit_id", period)
tax_unit_id = person.tax_unit("tax_unit_id", period)
return (claiming_tax_unit_id > NO_MEDICAID_CLAIMING_TAX_UNIT_ID) & np.isin(
claiming_tax_unit_id, tax_unit_id
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from policyengine_us.model_api import *
from policyengine_us.variables.gov.hhs.medicaid.income._claiming_tax_unit import (
medicaid_claiming_tax_unit_value,
medicaid_external_claimed_sum,
)


class medicaid_household_income(Variable):
Expand Down Expand Up @@ -52,5 +56,23 @@ def formula(person, period, parameters):
tax_household_income = (
person.tax_unit.sum(tax_member_income) + separate_spouse_income
)
tax_household_income = tax_household_income + medicaid_external_claimed_sum(
person,
period,
person.tax_unit("tax_unit_id", period),
tax_member_income,
)
known_claiming_tax_unit = person("medicaid_has_known_claiming_tax_unit", period)
claimant_tax_household_income = medicaid_claiming_tax_unit_value(
person, period, tax_household_income
)

return where(non_filer_rules, non_filer_household_income, tax_household_income)
return where(
non_filer_rules,
non_filer_household_income,
where(
known_claiming_tax_unit,
claimant_tax_household_income,
tax_household_income,
),
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from policyengine_us.model_api import *
from policyengine_us.variables.gov.hhs.medicaid.income._claiming_tax_unit import (
medicaid_claiming_tax_unit_value,
medicaid_external_claimed_sum,
)


class medicaid_household_size(Variable):
Expand Down Expand Up @@ -38,10 +42,27 @@ def formula(person, period, parameters):
tax_household_size = person.tax_unit("tax_unit_size", period) + (
cohabitating_separate.astype(int)
)
person_count = np.ones_like(person.tax_unit("tax_unit_id", period))
tax_household_size = tax_household_size + medicaid_external_claimed_sum(
person,
period,
person.tax_unit("tax_unit_id", period),
person_count,
).astype(int)
known_claiming_tax_unit = person("medicaid_has_known_claiming_tax_unit", period)
claimant_tax_household_size = medicaid_claiming_tax_unit_value(
person, period, tax_household_size
).astype(int)

# Count the applicant's unborn children in their own household size.
# The treatment of another household member's pregnancy is state-optional
# and is not yet parameterized here.
return where(
non_filer_rules, non_filer_household_size, tax_household_size
non_filer_rules,
non_filer_household_size,
where(
known_claiming_tax_unit,
claimant_tax_household_size,
tax_household_size,
),
) + person("current_pregnancies", period)
Loading