From 6db54b5d9f48b3b1097126cdf0fb5223523355b4 Mon Sep 17 00:00:00 2001 From: Nikhil Date: Mon, 29 Nov 2021 01:04:26 +0000 Subject: [PATCH 1/9] Complete AMT --- .../parameters/irs/income/amt/brackets.yaml | 23 +++ .../income/amt/capital_gains/brackets.yaml | 106 ++++++++++++ .../irs/income/amt/exemption/amount.yaml | 54 ++++++ .../income/amt/exemption/child/amount.yaml | 11 ++ .../income/amt/exemption/child/max_age.yaml | 5 + .../income/amt/exemption/phaseout/rate.yaml | 5 + .../income/amt/exemption/phaseout/start.yaml | 54 ++++++ .../income/amt/exemption/separate_limit.yaml | 12 ++ openfisca_us/variables/irs/inputs.py | 30 ++++ openfisca_us/variables/irs/outputs.py | 160 ++++++++++++++++++ 10 files changed, 460 insertions(+) create mode 100644 openfisca_us/parameters/irs/income/amt/brackets.yaml create mode 100644 openfisca_us/parameters/irs/income/amt/capital_gains/brackets.yaml create mode 100644 openfisca_us/parameters/irs/income/amt/exemption/amount.yaml create mode 100644 openfisca_us/parameters/irs/income/amt/exemption/child/amount.yaml create mode 100644 openfisca_us/parameters/irs/income/amt/exemption/child/max_age.yaml create mode 100644 openfisca_us/parameters/irs/income/amt/exemption/phaseout/rate.yaml create mode 100644 openfisca_us/parameters/irs/income/amt/exemption/phaseout/start.yaml create mode 100644 openfisca_us/parameters/irs/income/amt/exemption/separate_limit.yaml diff --git a/openfisca_us/parameters/irs/income/amt/brackets.yaml b/openfisca_us/parameters/irs/income/amt/brackets.yaml new file mode 100644 index 00000000000..dfe00dbfa8d --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/brackets.yaml @@ -0,0 +1,23 @@ +rates: + metadata: + unit: /1 + 1: + values: + 2013-01-01: 0.26 + 2: + values: + 2013-01-01: 0.02 +thresholds: + description: AMT brackets (upper thresholds) + metadata: + unit: currency-USD + period: year + 1: + values: + 2013-01-01: 179_500.0 + 2014-01-01: 182_500.0 + 2015-01-01: 185_400.0 + 2016-01-01: 186_300.0 + 2017-01-01: 187_800.0 + 2018-01-01: 191_100.0 + 2019-01-01: 194_800.0 \ No newline at end of file diff --git a/openfisca_us/parameters/irs/income/amt/capital_gains/brackets.yaml b/openfisca_us/parameters/irs/income/amt/capital_gains/brackets.yaml new file mode 100644 index 00000000000..df215b6c78f --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/capital_gains/brackets.yaml @@ -0,0 +1,106 @@ +rates: + description: Long term capital gain and qualified dividends (AMT) rates + metadata: + unit: /1 + 1: + 2013-01-01: 0.0 + 2: + 2013-01-01: 0.15 + 3: + 2013-01-01: 0.2 +thresholds: + description: Tops of long-term capital gains and qualified dividends (AMT) tax brackets + metadata: + unit: currency-USD + 1: + HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 48_600.0 + 2014-01-01: 49_400.0 + 2015-01-01: 50_200.0 + 2016-01-01: 50_400.0 + 2017-01-01: 50_800.0 + 2018-01-01: 51_700.0 + 2019-01-01: 52_750.0 + JOINT: + values: + 2013-01-01: 72_500.0 + 2014-01-01: 73_800.0 + 2015-01-01: 74_900.0 + 2016-01-01: 75_300.0 + 2017-01-01: 75_900.0 + 2018-01-01: 77_200.0 + 2019-01-01: 78_750.0 + SEPARATE: + values: + 2013-01-01: 36_250.0 + 2014-01-01: 36_900.0 + 2015-01-01: 37_450.0 + 2016-01-01: 37_650.0 + 2017-01-01: 37_950.0 + 2018-01-01: 38_600.0 + 2019-01-01: 39_375.0 + SINGLE: + values: + 2013-01-01: 36_250.0 + 2014-01-01: 36_900.0 + 2015-01-01: 37_450.0 + 2016-01-01: 37_650.0 + 2017-01-01: 37_950.0 + 2018-01-01: 38_600.0 + 2019-01-01: 39_375.0 + WIDOW: + values: + 2013-01-01: 72_500.0 + 2014-01-01: 73_800.0 + 2015-01-01: 74_900.0 + 2016-01-01: 75_300.0 + 2017-01-01: 75_900.0 + 2018-01-01: 77_200.0 + 2019-01-01: 78_750.0 + 2: + HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 425_000.0 + 2014-01-01: 432_200.0 + 2015-01-01: 439_000.0 + 2016-01-01: 441_000.0 + 2017-01-01: 444_550.0 + 2018-01-01: 452_400.0 + 2019-01-01: 461_700.0 + JOINT: + values: + 2013-01-01: 450_000.0 + 2014-01-01: 457_600.0 + 2015-01-01: 464_850.0 + 2016-01-01: 466_950.0 + 2017-01-01: 470_700.0 + 2018-01-01: 479_000.0 + 2019-01-01: 488_850.0 + SEPARATE: + values: + 2013-01-01: 225_000.0 + 2014-01-01: 228_800.0 + 2015-01-01: 232_425.0 + 2016-01-01: 233_475.0 + 2017-01-01: 235_350.0 + 2018-01-01: 239_500.0 + 2019-01-01: 244_425.0 + SINGLE: + values: + 2013-01-01: 400_000.0 + 2014-01-01: 406_750.0 + 2015-01-01: 413_200.0 + 2016-01-01: 415_050.0 + 2017-01-01: 418_400.0 + 2018-01-01: 425_800.0 + 2019-01-01: 434_550.0 + WIDOW: + values: + 2013-01-01: 450_000.0 + 2014-01-01: 457_600.0 + 2015-01-01: 464_850.0 + 2016-01-01: 466_950.0 + 2017-01-01: 470_700.0 + 2018-01-01: 479_000.0 + 2019-01-01: 488_850.0 diff --git a/openfisca_us/parameters/irs/income/amt/exemption/amount.yaml b/openfisca_us/parameters/irs/income/amt/exemption/amount.yaml new file mode 100644 index 00000000000..8063719b43d --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/exemption/amount.yaml @@ -0,0 +1,54 @@ +description: AMT exemption amount +metadata: + unit: currency-USD + period: year +HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 51_900.0 + 2014-01-01: 52_800.0 + 2015-01-01: 53_600.0 + 2016-01-01: 53_900.0 + 2017-01-01: 54_300.0 + 2018-01-01: 70_300.0 + 2019-01-01: 71_700.0 + 2026-01-01: 62_898.0 +JOINT: + values: + 2013-01-01: 80_800.0 + 2014-01-01: 82_100.0 + 2015-01-01: 83_400.0 + 2016-01-01: 83_800.0 + 2017-01-01: 84_500.0 + 2018-01-01: 109_400.0 + 2019-01-01: 111_700.0 + 2026-01-01: 97_880.0 +SEPARATE: + values: + 2013-01-01: 40_400.0 + 2014-01-01: 41_050.0 + 2015-01-01: 41_700.0 + 2016-01-01: 41_900.0 + 2017-01-01: 42_250.0 + 2018-01-01: 54_700.0 + 2019-01-01: 55_850.0 + 2026-01-01: 48_940.0 +SINGLE: + values: + 2013-01-01: 51_900.0 + 2014-01-01: 52_800.0 + 2015-01-01: 53_600.0 + 2016-01-01: 53_900.0 + 2017-01-01: 54_300.0 + 2018-01-01: 70_300.0 + 2019-01-01: 71_700.0 + 2026-01-01: 62_898.0 +WIDOW: + values: + 2013-01-01: 80_800.0 + 2014-01-01: 82_100.0 + 2015-01-01: 83_400.0 + 2016-01-01: 83_800.0 + 2017-01-01: 84_500.0 + 2018-01-01: 10_9400.0 + 2019-01-01: 111_700.0 + 2026-01-01: 97_880.0 diff --git a/openfisca_us/parameters/irs/income/amt/exemption/child/amount.yaml b/openfisca_us/parameters/irs/income/amt/exemption/child/amount.yaml new file mode 100644 index 00000000000..5ca3a2ce4f9 --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/exemption/child/amount.yaml @@ -0,0 +1,11 @@ +description: Child AMT exemption additional income base +metadata: + unit: currency-USD +values: + 2013-01-01: 7_150.0 + 2014-01-01: 7_250.0 + 2015-01-01: 7_400.0 + 2016-01-01: 7_400.0 + 2017-01-01: 7_500.0 + 2018-01-01: 7_600.0 + 2019-01-01: 0.0 diff --git a/openfisca_us/parameters/irs/income/amt/exemption/child/max_age.yaml b/openfisca_us/parameters/irs/income/amt/exemption/child/max_age.yaml new file mode 100644 index 00000000000..d42f0068df7 --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/exemption/child/max_age.yaml @@ -0,0 +1,5 @@ +description: Age ceiling for special AMT exemption +metadata: + unit: year +values: + 2013-01-01: 18.0 diff --git a/openfisca_us/parameters/irs/income/amt/exemption/phaseout/rate.yaml b/openfisca_us/parameters/irs/income/amt/exemption/phaseout/rate.yaml new file mode 100644 index 00000000000..884b567ffb1 --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/exemption/phaseout/rate.yaml @@ -0,0 +1,5 @@ +description: AMT exemption phaseout rate +metadata: + unit: currency-USD +values: + 2013-01-01: 0.25 diff --git a/openfisca_us/parameters/irs/income/amt/exemption/phaseout/start.yaml b/openfisca_us/parameters/irs/income/amt/exemption/phaseout/start.yaml new file mode 100644 index 00000000000..e903d8cc24d --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/exemption/phaseout/start.yaml @@ -0,0 +1,54 @@ +description: AMT exemption phaseout start +metadata: + unit: currency-USD + period: year +HEAD_OF_HOUSEHOLD: + values: + 2013-01-01: 115_400.0 + 2014-01-01: 117_300.0 + 2015-01-01: 119_200.0 + 2016-01-01: 119_700.0 + 2017-01-01: 120_700.0 + 2018-01-01: 500_000.0 + 2019-01-01: 510_300.0 + 2026-01-01: 139_812.0 +JOINT: + values: + 2013-01-01: 153_900.0 + 2014-01-01: 156_500.0 + 2015-01-01: 158_900.0 + 2016-01-01: 159_700.0 + 2017-01-01: 160_900.0 + 2018-01-01: 1_000_000.0 + 2019-01-01: 1_020_600.0 + 2026-01-01: 186_378.0 +SEPARATE: + values: + 2013-01-01: 76_950.0 + 2014-01-01: 78_250.0 + 2015-01-01: 79_450.0 + 2016-01-01: 79_850.0 + 2017-01-01: 80_450.0 + 2018-01-01: 500_000.0 + 2019-01-01: 510_300.0 + 2026-01-01: 93_189.0 +SINGLE: + values: + 2013-01-01: 115_400.0 + 2014-01-01: 117_300.0 + 2015-01-01: 119_200.0 + 2016-01-01: 119_700.0 + 2017-01-01: 120_700.0 + 2018-01-01: 500_000.0 + 2019-01-01: 510_300.0 + 2026-01-01: 139_812.0 +WIDOW: + values: + 2013-01-01: 153_900.0 + 2014-01-01: 156_500.0 + 2015-01-01: 158_900.0 + 2016-01-01: 159_700.0 + 2017-01-01: 160_900.0 + 2018-01-01: 1_000_000.0 + 2019-01-01: 1_020_600.0 + 2026-01-01: 186_378.0 diff --git a/openfisca_us/parameters/irs/income/amt/exemption/separate_limit.yaml b/openfisca_us/parameters/irs/income/amt/exemption/separate_limit.yaml new file mode 100644 index 00000000000..97dc869c0dd --- /dev/null +++ b/openfisca_us/parameters/irs/income/amt/exemption/separate_limit.yaml @@ -0,0 +1,12 @@ +description: AMT exemption phaseout ending AMT taxable income for Married filing Separately. The AMT exemption is entirely disallowed beyond this AMT taxable income level for individuals who are married but filing separately. +metadata: + unit: currency-USD +values: + 2013-01-01: 238_550.0 + 2014-01-01: 242_450.0 + 2015-01-01: 246_250.0 + 2016-01-01: 247_450.0 + 2017-01-01: 249_450.0 + 2018-01-01: 718_800.0 + 2019-01-01: 733_700.0 + 2026-01-01: 288_949.0 diff --git a/openfisca_us/variables/irs/inputs.py b/openfisca_us/variables/irs/inputs.py index bfb19300e78..0fe3375c8db 100644 --- a/openfisca_us/variables/irs/inputs.py +++ b/openfisca_us/variables/irs/inputs.py @@ -644,6 +644,16 @@ class e07300(Variable): documentation = """Foreign tax credit from Form 1116""" +class filer_e07300(Variable): + value_type = float + entity = TaxUnit + label = u"Foreign tax credit (Form 1114) for the tax unit (excluding dependents)" + definition_period = YEAR + + def formula(tax_unit, period, parameters): + return tax_unit_non_dep_sum("e07300", tax_unit, period) + + class e07400(Variable): value_type = float entity = Person @@ -831,6 +841,16 @@ class e24515(Variable): documentation = """Sch D: Un-Recaptured Section 1250 Gain""" +class filer_e24515(Variable): + value_type = float + entity = TaxUnit + label = u"Section 1250 Gain (un-recaptured) for the tax unit (excluding dependents)" + definition_period = YEAR + + def formula(tax_unit, period, parameters): + return tax_unit_non_dep_sum("e24515", tax_unit, period) + + class e24518(Variable): value_type = float entity = Person @@ -897,6 +917,16 @@ class e62900(Variable): ) +class filer_e62900(Variable): + value_type = float + entity = TaxUnit + label = u"AMT foreign tax credit (Form 6251) for the tax unit (excluding dependents)" + definition_period = YEAR + + def formula(tax_unit, period, parameters): + return tax_unit_non_dep_sum("e62900", tax_unit, period) + + class e87530(Variable): value_type = float entity = Person diff --git a/openfisca_us/variables/irs/outputs.py b/openfisca_us/variables/irs/outputs.py index 1abd9e5a706..9d4da4a4e54 100644 --- a/openfisca_us/variables/irs/outputs.py +++ b/openfisca_us/variables/irs/outputs.py @@ -644,8 +644,12 @@ class c05800(Variable): value_type = float entity = TaxUnit definition_period = YEAR + label = "Total income tax liability before credits" documentation = """Total (regular + AMT) income tax liability before credits (equals taxbc plus c09600)""" + def formula(tax_unit, period, parameters): + return add(tax_unit, period, "taxbc", "c09600") + class c07100(Variable): value_type = float @@ -754,8 +758,121 @@ class c09600(Variable): value_type = float entity = TaxUnit definition_period = YEAR + label = "Alternative Minimum Tax" + unit = "currency-GBP" documentation = """Alternative Minimum Tax (AMT) liability""" + def formula(tax_unit, period, parameters): + c62100 = tax_unit("c62100", period) + # Form 6251, Part II top + amt = parameters(period).irs.income.amt + phaseout = amt.exemption.phaseout + mars = tax_unit("mars", period) + line29 = max_( + 0, + ( + amt.exemption.amount[mars] + - phaseout.rate * max_(0, c62100 - phaseout.start[mars]) + ), + ) + age_head = tax_unit("age_head", period) + child = amt.exemption.child + young_head = (age_head != 0) & (age_head < child.max_age) + no_or_young_spouse = tax_unit("age_spouse", period) < child.max_age + line29 = where( + young_head | no_or_young_spouse, + min_(line29, tax_unit("filer_earned", period) + child.amount), + line29, + ) + line30 = max_(0, c62100 - line29) + brackets = amt.brackets + amount_over_threshold = line30 - brackets.thresholds["1"] / tax_unit( + "sep", period + ) + line3163 = brackets.rates["1"] * line30 + brackets.rates["2"] * max_( + 0, amount_over_threshold + ) + dwks10, dwks13, dwks14, dwks19, e24515 = [ + tax_unit(variable, period) + for variable in [ + "dwks10", + "dwks13", + "dwks14", + "dwks19", + "filer_e24515", + ] + ] + form_6251_part_iii_requred = np.any( + [ + variable > 0 + for variable in [ + dwks10, + dwks13, + dwks14, + dwks19, + e24515, + ] + ] + ) + + # Complete Form 6251, Part III + + line37 = dwks13 + line38 = e24515 + line39 = min_(line37 + line38, dwks10) + line40 = min_(line30, line39) + line41 = max_(0, line30 - line40) + amount_over_threshold = max_( + 0, line41 - amt.brackets.thresholds["1"] / tax_unit("sep", period) + ) + line42 = ( + amt.brackets.rates["1"] * line41 + + amt.brackets.rates["2"] * amount_over_threshold + ) + line44 = dwks14 + cg = amt.capital_gains.brackets + line45 = max_(0, cg.thresholds["1"][mars] - line44) + line46 = min_(line30, line37) + line47 = min_(line45, line46) + cgtax1 = line47 * cg.rates["1"] + line48 = line46 - line47 + line51 = dwks19 + line52 = line45 + line51 + line53 = max_(0, cg.thresholds["2"][mars] - line52) + line54 = min_(line48, line53) + cgtax2 = line54 * cg.rates["2"] + line56 = line47 + line54 + line57 = where(line41 == line56, 0, line46 - line56) + linex2 = where(line41 == line56, 0, max_(0, line54 - line48)) + cgtax3 = line57 * cg.rates["3"] + line61 = where( + line38 == 0, + 0, + 0.25 * max_(0, (line30 - line41 - line56 - line57 - linex2)), + ) + line62 = line42 + cgtax1 + cgtax2 + cgtax3 + line61 + line64 = min_(line3163, line62) + line31 = where(form_6251_part_iii_requred, line64, line3163) + e07300 = tax_unit("filer_e07300", period) + + # Form 6251, Part II bottom + line32 = where( + tax_unit("f6251", period), tax_unit("filer_e62900", period), e07300 + ) + line33 = line31 - line32 + return max_( + 0, + line33 + - max_( + 0, + ( + tax_unit("taxbc", period) + - e07300 + - tax_unit("c05700", period) + ), + ), + ) + class c10960(Variable): value_type = float @@ -1013,8 +1130,51 @@ class c62100(Variable): value_type = float entity = TaxUnit definition_period = YEAR + label = "AMT taxable income" + unit = "currency-USD" documentation = """Alternative Minimum Tax (AMT) taxable income""" + def formula(tax_unit, period, parameters): + # Form 6251, Part I + c00100 = tax_unit("c00100", period) + e00700 = tax_unit("filer_e00700", period) + c62100_if_no_standard = ( + c00100 + - e00700 + - tax_unit("c04470", period) + + max_( + 0, + min_( + tax_unit("c17000", period), + 0.025 * c00100, + ), + ) + + tax_unit("c18300", period) + + tax_unit("c20800", period) + - tax_unit("c21040", period) + ) + c62100 = where( + tax_unit("standard", period) == 0, + c62100_if_no_standard, + c00100 - e00700, + ) + tax_unit( + "filer_cmbtp", period + ) # add income not in AGI but considered income for AMT + amt = parameters(period).irs.income.amt + mars = tax_unit("mars", period) + separate_addition = ( + max_( + 0, + min_( + amt.exemption.amount[mars], + amt.exemption.phaseout.rate + * (c62100 - amt.exemption.separate_limit), + ), + ) + * (mars == mars.possible_values.SEPARATE) + ) + return c62100 + separate_addition + class c87668(Variable): value_type = float From cc37e6559fce0dc8a2e74abd72e77715c3d7bafe Mon Sep 17 00:00:00 2001 From: Nikhil Date: Mon, 29 Nov 2021 17:41:05 +0000 Subject: [PATCH 2/9] Add suggested changes --- openfisca_us/parameters/irs/income/amt/brackets.yaml | 2 +- openfisca_us/variables/irs/inputs.py | 3 +++ openfisca_us/variables/irs/outputs.py | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openfisca_us/parameters/irs/income/amt/brackets.yaml b/openfisca_us/parameters/irs/income/amt/brackets.yaml index dfe00dbfa8d..538ed1897b9 100644 --- a/openfisca_us/parameters/irs/income/amt/brackets.yaml +++ b/openfisca_us/parameters/irs/income/amt/brackets.yaml @@ -20,4 +20,4 @@ thresholds: 2016-01-01: 186_300.0 2017-01-01: 187_800.0 2018-01-01: 191_100.0 - 2019-01-01: 194_800.0 \ No newline at end of file + 2019-01-01: 194_800.0 diff --git a/openfisca_us/variables/irs/inputs.py b/openfisca_us/variables/irs/inputs.py index 0fe3375c8db..8d9dc65a4c4 100644 --- a/openfisca_us/variables/irs/inputs.py +++ b/openfisca_us/variables/irs/inputs.py @@ -648,6 +648,7 @@ class filer_e07300(Variable): value_type = float entity = TaxUnit label = u"Foreign tax credit (Form 1114) for the tax unit (excluding dependents)" + unit = "currency-USD" definition_period = YEAR def formula(tax_unit, period, parameters): @@ -845,6 +846,7 @@ class filer_e24515(Variable): value_type = float entity = TaxUnit label = u"Section 1250 Gain (un-recaptured) for the tax unit (excluding dependents)" + unit = "currency-USD" definition_period = YEAR def formula(tax_unit, period, parameters): @@ -921,6 +923,7 @@ class filer_e62900(Variable): value_type = float entity = TaxUnit label = u"AMT foreign tax credit (Form 6251) for the tax unit (excluding dependents)" + unit = "currency-USD" definition_period = YEAR def formula(tax_unit, period, parameters): diff --git a/openfisca_us/variables/irs/outputs.py b/openfisca_us/variables/irs/outputs.py index 9d4da4a4e54..42894f60093 100644 --- a/openfisca_us/variables/irs/outputs.py +++ b/openfisca_us/variables/irs/outputs.py @@ -759,7 +759,7 @@ class c09600(Variable): entity = TaxUnit definition_period = YEAR label = "Alternative Minimum Tax" - unit = "currency-GBP" + unit = "currency-USD" documentation = """Alternative Minimum Tax (AMT) liability""" def formula(tax_unit, period, parameters): @@ -802,7 +802,7 @@ def formula(tax_unit, period, parameters): "filer_e24515", ] ] - form_6251_part_iii_requred = np.any( + form_6251_part_iii_required = np.any( [ variable > 0 for variable in [ @@ -852,7 +852,7 @@ def formula(tax_unit, period, parameters): ) line62 = line42 + cgtax1 + cgtax2 + cgtax3 + line61 line64 = min_(line3163, line62) - line31 = where(form_6251_part_iii_requred, line64, line3163) + line31 = where(form_6251_part_iii_required, line64, line3163) e07300 = tax_unit("filer_e07300", period) # Form 6251, Part II bottom From dbf28a068ce99b88326ba10830aacfcde5e6d17d Mon Sep 17 00:00:00 2001 From: Nikhil Date: Mon, 29 Nov 2021 17:41:51 +0000 Subject: [PATCH 3/9] Add missing unit --- openfisca_us/variables/irs/outputs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openfisca_us/variables/irs/outputs.py b/openfisca_us/variables/irs/outputs.py index 42894f60093..9579f21ed46 100644 --- a/openfisca_us/variables/irs/outputs.py +++ b/openfisca_us/variables/irs/outputs.py @@ -645,6 +645,7 @@ class c05800(Variable): entity = TaxUnit definition_period = YEAR label = "Total income tax liability before credits" + unit = "currency-USD" documentation = """Total (regular + AMT) income tax liability before credits (equals taxbc plus c09600)""" def formula(tax_unit, period, parameters): From 976902c8cf149d3edae21a017f166d8b562b0275 Mon Sep 17 00:00:00 2001 From: Nikhil Date: Tue, 7 Dec 2021 14:38:45 +0000 Subject: [PATCH 4/9] Add script to generate taxcalc test cases --- openfisca_us/tools/dev/run_taxcalc_test.py | 244 +++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 openfisca_us/tools/dev/run_taxcalc_test.py diff --git a/openfisca_us/tools/dev/run_taxcalc_test.py b/openfisca_us/tools/dev/run_taxcalc_test.py new file mode 100644 index 00000000000..ffaf56fd7a4 --- /dev/null +++ b/openfisca_us/tools/dev/run_taxcalc_test.py @@ -0,0 +1,244 @@ +from argparse import ArgumentParser +import sys +import subprocess +import pandas as pd +from pathlib import Path +import yaml + +TAXCALC_REQUIRED_INPUTS = [ + "RECID", + "MARS", +] + +TAXCALC_INPUT_VARIABLES = [ + 'DSI', + 'EIC', + 'FLPDYR', + 'MARS', + 'MIDR', + 'RECID', + 'XTOT', + 'age_head', + 'age_spouse', + 'agi_bin', + 'blind_head', + 'blind_spouse', + 'cmbtp', + 'e00200', + 'e00200p', + 'e00200s', + 'pencon_p', + 'pencon_s', + 'e00300', + 'e00400', + 'e00600', + 'e00650', + 'e00700', + 'e00800', + 'e00900', + 'e00900p', + 'e00900s', + 'e01100', + 'e01200', + 'e01400', + 'e01500', + 'e01700', + 'e02000', + 'e02100', + 'e02100p', + 'e02100s', + 'e02300', + 'e02400', + 'e03150', + 'e03210', + 'e03220', + 'e03230', + 'e03240', + 'e03270', + 'e03290', + 'e03300', + 'e03400', + 'e03500', + 'e07240', + 'e07260', + 'e07300', + 'e07400', + 'e07600', + 'e09700', + 'e09800', + 'e09900', + 'e11200', + 'e17500', + 'e18400', + 'e18500', + 'e19200', + 'e19800', + 'e20100', + 'e20400', + 'g20500', + 'e24515', + 'e24518', + 'e26270', + 'e27200', + 'e32800', + 'e58990', + 'e62900', + 'e87530', + 'elderly_dependents', + 'f2441', + 'f6251', + 'a_lineno', + 'ffpos', + 'fips', + 'h_seq', + 'data_source', + 'k1bx14p', + 'k1bx14s', + 'mcaid_ben', + 'mcare_ben', + 'n24', + 'nu06', + 'nu13', + 'nu18', + 'n1820', + 'n21', + 'other_ben', + 'p08000', + 'p22250', + 'p23250', + 'e87521', + 's006', + 'snap_ben', + 'housing_ben', + 'ssi_ben', + 'tanf_ben', + 'vet_ben', + 'wic_ben', + 'PT_SSTB_income', + 'PT_binc_w2_wages', + 'PT_ubia_property' +] + +TAXCALC_OUTPUT_VARIABLES = [ + 'niit', + 'combined', + 'earned', + 'earned_p', + 'earned_s', + 'was_plus_sey_p', + 'was_plus_sey_s', + 'eitc', + 'rptc', + 'rptc_p', + 'rptc_s', + 'exact', + 'expanded_income', + 'iitax', + 'num', + 'othertaxes', + 'payrolltax', + 'refund', + 'sep', + 'sey', + 'standard', + 'surtax', + 'taxbc', + 'c00100', + 'c01000', + 'c02500', + 'c02900', + 'c03260', + 'c04470', + 'c04600', + 'qbided', + 'c04800', + 'c05200', + 'c05700', + 'c05800', + 'c07100', + 'c07180', + 'CDCC_refund', + 'c07200', + 'c07220', + 'c07230', + 'c07240', + 'c07260', + 'c07300', + 'c07400', + 'c07600', + 'c08000', + 'c09200', + 'c09600', + 'c10960', + 'c11070', + 'c17000', + 'e17500_capped', + 'c18300', + 'e18400_capped', + 'e18500_capped', + 'c19200', + 'e19200_capped', + 'c19700', + 'e19800_capped', + 'e20100_capped', + 'c20500', + 'g20500_capped', + 'c20800', + 'e20400_capped', + 'c21040', + 'c21060', + 'c23650', + 'c59660', + 'c62100', + 'c87668', + 'care_deduction', + 'ctc_new', + 'odc', + 'personal_refundable_credit', + 'recovery_rebate_credit', + 'personal_nonrefundable_credit', + 'charity_credit', + 'dwks10', + 'dwks13', + 'dwks14', + 'dwks19', + 'fstax', + 'invinc_agi_ec', + 'invinc_ec_base', + 'lumpsum_tax', + 'pre_c04600', + 'codtc_limited', + 'ptax_amc', + 'ptax_oasdi', + 'ptax_was', + 'setax', + 'ymod', + 'ymod1', + 'ubi', + 'taxable_ubi', + 'nontaxable_ubi', + 'mtr_paytax', + 'mtr_inctax', + 'aftertax_income', + 'benefit_cost_total', + 'benefit_value_total', +] + +parser = ArgumentParser(description="Utility to run taxcalc on a single tax unit") + +for variable in TAXCALC_INPUT_VARIABLES: + parser.add_argument('--' + variable, required=variable in TAXCALC_REQUIRED_INPUTS) +parser.add_argument('--output', nargs='+', default=TAXCALC_OUTPUT_VARIABLES) +args = parser.parse_args() + +output_dir = Path("./taxcalc_output") +output_dir.mkdir(exist_ok=True) +pd.DataFrame({variable: [getattr(args, variable)] for variable in TAXCALC_INPUT_VARIABLES if getattr(args, variable) is not None}).to_csv("taxcalc_output/input.csv", index=False) +process = subprocess.Popen("tc taxcalc_output/input.csv 2020 --dump --outdir taxcalc_output", shell=True, stdout=subprocess.PIPE) +process.wait() +result = pd.read_csv(output_dir / "input-20-#-#-#.csv") +output = {} +for col in result.columns: + if col in args.output or args.output == []: + output[col] = float(result[col].iloc[0]) +yaml.dump(output, sys.stdout) \ No newline at end of file From 1c7971e9e71229d87a05718663368c071cdd927f Mon Sep 17 00:00:00 2001 From: Nikhil Date: Tue, 7 Dec 2021 14:39:18 +0000 Subject: [PATCH 5/9] Add test, incl failing test --- .gitignore | 4 ++- .../tests/policy/baseline/irs/c05800.yaml | 8 +++++ .../tests/policy/baseline/irs/c09600.yaml | 31 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 openfisca_us/tests/policy/baseline/irs/c05800.yaml create mode 100644 openfisca_us/tests/policy/baseline/irs/c09600.yaml diff --git a/.gitignore b/.gitignore index 58f55cef259..b6dc40ecc51 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,6 @@ dist **/*.csv **/_build -**/.ipynb_checkpoints \ No newline at end of file +**/.ipynb_checkpoints +taxcalc_output +.coverage diff --git a/openfisca_us/tests/policy/baseline/irs/c05800.yaml b/openfisca_us/tests/policy/baseline/irs/c05800.yaml new file mode 100644 index 00000000000..fdef9475f73 --- /dev/null +++ b/openfisca_us/tests/policy/baseline/irs/c05800.yaml @@ -0,0 +1,8 @@ +- name: Test components are added + period: 2020 + absolute_error_margin: 0 + input: + taxbc: 1 + c09600: 1 + output: + c05800: 2 diff --git a/openfisca_us/tests/policy/baseline/irs/c09600.yaml b/openfisca_us/tests/policy/baseline/irs/c09600.yaml new file mode 100644 index 00000000000..a3196610632 --- /dev/null +++ b/openfisca_us/tests/policy/baseline/irs/c09600.yaml @@ -0,0 +1,31 @@ +- name: Single filer, small earnings, young + period: 2020 + absolute_error_margin: 0 + input: + age_head: 25 + mars: SINGLE + e00200: 2_000 + output: + c09600: 0 +- name: Single filer, high earnings, young + period: 2020 + absolute_error_margin: 0 + input: + c62100: 2100000.0 + mars: SINGLE + age_head: 0 + age_spouse: 0 + filer_earned: 100000.0 + sep: true + dwks10: 2000000.0 + dwks13: 2000000.0 + dwks14: 87612.12 + dwks19: 87612.12 + filer_e24515: 0 + filer_e07300: 0 + f6251: 0 + filer_e62900: 0 + taxbc: 397430.2 + c05700: 0 + output: + c09600: 10888.3 From 79007acf69aa4aeb76294f3b29f6d393996e03bf Mon Sep 17 00:00:00 2001 From: Nikhil Date: Tue, 28 Dec 2021 00:20:25 +0000 Subject: [PATCH 6/9] Add taxcalc utilities and AMT unit tests --- .../policy/baseline/calcfunctions/amt.yaml | 321 ++ openfisca_us/tools/dev/calcfunctions.py | 4622 +++++++++++++++++ .../tools/dev/generate_taxcalc_unit_tests.py | 234 + openfisca_us/tools/dev/run_taxcalc_test.py | 468 +- .../tools/dev/run_taxcalc_unit_test.py | 0 openfisca_us/variables/irs/outputs.py | 1 + 6 files changed, 5422 insertions(+), 224 deletions(-) create mode 100644 openfisca_us/tests/policy/baseline/calcfunctions/amt.yaml create mode 100644 openfisca_us/tools/dev/calcfunctions.py create mode 100644 openfisca_us/tools/dev/generate_taxcalc_unit_tests.py create mode 100644 openfisca_us/tools/dev/run_taxcalc_unit_test.py diff --git a/openfisca_us/tests/policy/baseline/calcfunctions/amt.yaml b/openfisca_us/tests/policy/baseline/calcfunctions/amt.yaml new file mode 100644 index 00000000000..e4efb394482 --- /dev/null +++ b/openfisca_us/tests/policy/baseline/calcfunctions/amt.yaml @@ -0,0 +1,321 @@ +- name: AMT unit test 0 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 0 + standard: 6200 + f6251: 0 + c00100: 20310 + c18300: 0 + taxbc: 1070 + c04470: 0 + c17000: 0 + c20800: 0 + c21040: 0 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 0 + dwks14: 0 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 0 + age_head: 13 + age_spouse: 0 + earned: 0 + cmbtp: 0 + output: + c62100: 20310.0 + c09600: 4210.6 + c05800: 5280.6 +- name: AMT unit test 1 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 0 + standard: 6200 + f6251: 0 + c00100: 20310 + c18300: 0 + taxbc: 1070 + c04470: 0 + c17000: 0 + c20800: 0 + c21040: 0 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 0 + dwks14: 0 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 0 + age_head: 13 + age_spouse: 0 + earned: 0 + cmbtp: 0 + output: + c62100: 20310.0 + c09600: 4210.6 + c05800: 5280.6 +- name: AMT unit test 2 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 3592 + standard: 1000 + f6251: 0 + c00100: 17071 + c18300: 0 + taxbc: 1418 + c04470: 0 + c17000: 0 + c20800: 0 + c21040: 0 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 12479 + dwks14: 12479 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 3592 + age_head: 16 + age_spouse: 0 + earned: 0 + cmbtp: 0 + output: + c62100: 17071.0 + c09600: 2086.54 + c05800: 3504.54 +- name: AMT unit test 3 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 909730 + standard: 0 + f6251: 0 + c00100: 977574 + c18300: 3922 + taxbc: 177220 + c04470: 1991 + c17000: 0 + c20800: 0 + c21040: 7966 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 65852 + dwks14: 65852 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 909730 + age_head: 44 + age_spouse: 0 + earned: 67685 + cmbtp: 0 + output: + c62100: 971539.0 + c09600: 2361.4400000000023 + c05800: 179581.44 +- name: AMT unit test 4 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 0 + standard: 6200 + f6251: 0 + c00100: 15227 + c18300: 0 + taxbc: 507 + c04470: 0 + c17000: 0 + c20800: 0 + c21040: 0 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 0 + dwks14: 0 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 0 + age_head: 17 + age_spouse: 0 + earned: 0 + cmbtp: 0 + output: + c62100: 15227.0 + c09600: 3452.02 + c05800: 3959.02 +- name: AMT unit test 5 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 1675310 + standard: 0 + f6251: 0 + c00100: 1858855 + c18300: 20572 + taxbc: 353283 + c04470: 20350 + c17000: 0 + c20800: 0 + c21040: 46614 + e24515: 0 + mars: JOINT + sep: 1 + dwks19: 163194 + dwks14: 163194 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 1675310 + age_head: 40 + age_spouse: 40 + earned: 142238 + cmbtp: 0 + output: + c62100: 1812463.0 + c09600: 1155.9799999999814 + c05800: 354438.98 +- name: AMT unit test 6 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 0 + standard: 0 + f6251: 0 + c00100: 15410 + c18300: 844 + taxbc: 526 + c04470: 6194 + c17000: 0 + c20800: 0 + c21040: 0 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 0 + dwks14: 0 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 0 + age_head: 13 + age_spouse: 0 + earned: 0 + cmbtp: 0 + output: + c62100: 10060.0 + c09600: 2089.6 + c05800: 2615.6 +- name: AMT unit test 7 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 1675310 + standard: 0 + f6251: 0 + c00100: 1858855 + c18300: 20572 + taxbc: 353283 + c04470: 20350 + c17000: 0 + c20800: 0 + c21040: 46614 + e24515: 0 + mars: JOINT + sep: 1 + dwks19: 163194 + dwks14: 163194 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 1675310 + age_head: 40 + age_spouse: 40 + earned: 142238 + cmbtp: 0 + output: + c62100: 1812463.0 + c09600: 1155.9799999999814 + c05800: 354438.98 +- name: AMT unit test 8 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 0 + standard: 0 + f6251: 0 + c00100: 24723 + c18300: 1012 + taxbc: 1444 + c04470: 8117 + c17000: 0 + c20800: 0 + c21040: 0 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 0 + dwks14: 0 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 0 + age_head: 6 + age_spouse: 0 + earned: 0 + cmbtp: 0 + output: + c62100: 17618.0 + c09600: 3136.6800000000003 + c05800: 4580.68 +- name: AMT unit test 9 + period: 2019 + absolute_error_margin: 1 + input: + e07300: 0 + dwks13: 830501 + standard: 0 + f6251: 0 + c00100: 895821 + c18300: 6713 + taxbc: 159427 + c04470: 5958 + c17000: 0 + c20800: 0 + c21040: 19248 + e24515: 0 + mars: SINGLE + sep: 1 + dwks19: 59361 + dwks14: 59361 + c05700: 0 + e62900: 0 + e00700: 0 + dwks10: 830501 + age_head: 59 + age_spouse: 0 + earned: 63661 + cmbtp: 0 + output: + c62100: 877328.0 + c09600: 88.77000000001863 + c05800: 159515.77000000002 + diff --git a/openfisca_us/tools/dev/calcfunctions.py b/openfisca_us/tools/dev/calcfunctions.py new file mode 100644 index 00000000000..21aa12e7756 --- /dev/null +++ b/openfisca_us/tools/dev/calcfunctions.py @@ -0,0 +1,4622 @@ +""" +Tax-Calculator functions that calculate payroll and individual income taxes. + +These functions are imported into the Calculator class. + +Note: the parameter_indexing_CPI_offset policy parameter is the only +policy parameter that does not appear here; it is used in the policy.py +file to possibly adjust the price inflation rate used to index policy +parameters (as would be done in a reform that introduces chained-CPI +indexing). +""" +# CODING-STYLE CHECKS: +# pycodestyle calcfunctions.py +# pylint --disable=locally-disabled calcfunctions.py +# +# pylint: disable=too-many-lines +# pylint: disable=invalid-name +# pylint: disable=too-many-arguments +# pylint: disable=too-many-locals + +import math +import copy +import numpy as np +from taxcalc.decorators import iterate_jit, JIT + + +def BenefitPrograms(calc): + """ + Calculate total government cost and consumption value of benefits + delivered by non-repealed benefit programs. + + Parameters + ---------- + calc: Calculator object + calc represents the reform while self represents the baseline + + Returns + ------- + None: + The function modifies calc + """ + # zero out benefits delivered by repealed programs + zero = np.zeros(calc.array_len) + if calc.policy_param("BEN_housing_repeal"): + calc.array("housing_ben", zero) + if calc.policy_param("BEN_ssi_repeal"): + calc.array("ssi_ben", zero) + if calc.policy_param("BEN_snap_repeal"): + calc.array("snap_ben", zero) + if calc.policy_param("BEN_tanf_repeal"): + calc.array("tanf_ben", zero) + if calc.policy_param("BEN_vet_repeal"): + calc.array("vet_ben", zero) + if calc.policy_param("BEN_wic_repeal"): + calc.array("wic_ben", zero) + if calc.policy_param("BEN_mcare_repeal"): + calc.array("mcare_ben", zero) + if calc.policy_param("BEN_mcaid_repeal"): + calc.array("mcaid_ben", zero) + if calc.policy_param("BEN_oasdi_repeal"): + calc.array("e02400", zero) + if calc.policy_param("BEN_ui_repeal"): + calc.array("e02300", zero) + if calc.policy_param("BEN_other_repeal"): + calc.array("other_ben", zero) + # calculate government cost of all benefits + cost = np.array( + calc.array("housing_ben") + + calc.array("ssi_ben") + + calc.array("snap_ben") + + calc.array("tanf_ben") + + calc.array("vet_ben") + + calc.array("wic_ben") + + calc.array("mcare_ben") + + calc.array("mcaid_ben") + + calc.array("e02400") + + calc.array("e02300") + + calc.array("ubi") + + calc.array("other_ben") + ) + calc.array("benefit_cost_total", cost) + # calculate consumption value of all benefits + # (assuming that cash benefits have full value) + value = np.array( + calc.array("housing_ben") * calc.consump_param("BEN_housing_value") + + calc.array("ssi_ben") + + calc.array("snap_ben") * calc.consump_param("BEN_snap_value") + + calc.array("tanf_ben") * calc.consump_param("BEN_tanf_value") + + calc.array("vet_ben") * calc.consump_param("BEN_vet_value") + + calc.array("wic_ben") * calc.consump_param("BEN_wic_value") + + calc.array("mcare_ben") * calc.consump_param("BEN_mcare_value") + + calc.array("mcaid_ben") * calc.consump_param("BEN_mcaid_value") + + calc.array("e02400") + + calc.array("e02300") + + calc.array("ubi") + + calc.array("other_ben") * calc.consump_param("BEN_other_value") + ) + calc.array("benefit_value_total", value) + + +def EI_PayrollTax( + SS_Earnings_c, + e00200p, + e00200s, + pencon_p, + pencon_s, + FICA_ss_trt, + FICA_mc_trt, + ALD_SelfEmploymentTax_hc, + SS_Earnings_thd, + e00900p, + e00900s, + e02100p, + e02100s, + k1bx14p, + k1bx14s, + payrolltax, + ptax_was, + setax, + c03260, + ptax_oasdi, + sey, + earned, + earned_p, + earned_s, + was_plus_sey_p, + was_plus_sey_s, +): + """ + Compute part of total OASDI+HI payroll taxes and earned income variables. + + Parameters + ---------- + SS_Earnings_c: float + Maximum taxable earnings for Social Security. + Individual earnings below this amount are subjected to OASDI payroll tax. + This parameter is indexed by rate of growth in average wages not by the price inflation rate. + e00200p: float + Wages, salaries, and tips for taxpayer net of pension contributions + e00200s: float + Wages, salaries, and tips for spouse net of pension contributions + pencon_p: float + Contributions to defined-contribution pension plans for taxpayer + pencon_s: float + Contributions to defined-contribution pension plans for spouse + FICA_ss_trt: float + Social security payroll tax rate, including both employer and employee + FICA_mc_trt: float + Medicare payroll tax rate, including both employer and employee + ALD_SelfEmploymentTax_hc: float + Adjustment for self-employment tax haircut + If greater than zero, reduces the employer equivalent portion of self-employment adjustment + Final adjustment amount = (1-Haircut)*SelfEmploymentTaxAdjustment + SS_Earnings_thd: float + Additional taxable earnings threshold for Social Security + Individual earnings above this threshold are subjected to OASDI payroll tax, in addtion to + earnings below the maximum taxable earnings threshold. + e00900p: float + Schedule C business net profit/loss for taxpayer + e00900s: float + Schedule C business net profit/loss for spouse + e02100p: float + Farm net income/loss for taxpayer + e02100s: float + Farm net income/loss for spouse + k1bx14p: float + Partner self-employment earnings/loss for taxpayer (included in e26270 total) + k1bx14s: float + Partner self-employment earnings/loss for spouse (included in e26270 total) + payrolltax: float + Total (employee and employer) payroll tax liability + payrolltax = ptax_was + setax + ptax_amc + ptax_was: float + Employee and employer OASDI plus HI FICA tax + setax: float + Self-employment tax + c03260: float + Deductible part of self-employment tax + c03260 = (1 - ALD_SelfEmploymentTax_hc) * 0.5 * setax + ptax_oasdi: float + Employee and employer OASDI FICA tax plus self employment tax + Excludes HI FICA so positive ptax_oasdi is less than ptax_was + setax + sey: float + Total self-employment income for filing unit + earned: float + Earned income for filing unit + earned_p: float + Earned income for taxpayer + earned_s: float + Earned income for spouse + was_plus_sey_p: float + Wage and salary income plus taxable self employment income for taxpayer + was_plus_sey_s: float + Wage and salary income plus taxable self employment income for spouse + + Returns + ------- + sey: float + Total self-employment income for filing unit + payrolltax: float + Total (employee and employer) payroll tax liability + payrolltax = ptax_was + setax + ptax_amc + ptax_was: float + Employee and employer OASDI plus HI FICA tax + setax: float + Self-employment tax + c03260: float + Deductible part of self-employment tax + c03260 = (1 - ALD_SelfEmploymentTax_hc) * 0.5 * setax + ptax_oasdi: float + Employee and employer OASDI FICA tax plus self employment tax + Excludes HI FICA so positive ptax_oasdi is less than ptax_was + setax + earned: float + Earned income for filing unit + earned_p: float + Earned income for taxpayer + earned_s: float + Earned income for spouse + was_plus_sey_p: float + Wage and salary income plus taxable self employment income for taxpayer + was_plus_sey_s: float + Wage and salary income plus taxable self employment income for spouse + """ + # compute sey and its individual components + sey_p = e00900p + e02100p + k1bx14p + sey_s = e00900s + e02100s + k1bx14s + sey = sey_p + sey_s # total self-employment income for filing unit + + # compute gross wage and salary income ('was' denotes 'wage and salary') + gross_was_p = e00200p + pencon_p + gross_was_s = e00200s + pencon_s + + # compute taxable gross earnings for OASDI FICA + txearn_was_p = min(SS_Earnings_c, gross_was_p) + txearn_was_s = min(SS_Earnings_c, gross_was_s) + + # compute OASDI and HI payroll taxes on wage-and-salary income, FICA + ptax_ss_was_p = FICA_ss_trt * txearn_was_p + ptax_ss_was_s = FICA_ss_trt * txearn_was_s + ptax_mc_was_p = FICA_mc_trt * gross_was_p + ptax_mc_was_s = FICA_mc_trt * gross_was_s + ptax_was = ptax_ss_was_p + ptax_ss_was_s + ptax_mc_was_p + ptax_mc_was_s + + # compute taxable self-employment income for OASDI SECA + sey_frac = 1.0 - 0.5 * (FICA_ss_trt + FICA_mc_trt) + txearn_sey_p = min( + max(0.0, sey_p * sey_frac), SS_Earnings_c - txearn_was_p + ) + txearn_sey_s = min( + max(0.0, sey_s * sey_frac), SS_Earnings_c - txearn_was_s + ) + + # compute self-employment tax on taxable self-employment income, SECA + setax_ss_p = FICA_ss_trt * txearn_sey_p + setax_ss_s = FICA_ss_trt * txearn_sey_s + setax_mc_p = FICA_mc_trt * max(0.0, sey_p * sey_frac) + setax_mc_s = FICA_mc_trt * max(0.0, sey_s * sey_frac) + setax_p = setax_ss_p + setax_mc_p + setax_s = setax_ss_s + setax_mc_s + setax = setax_p + setax_s + + # compute extra OASDI payroll taxes on the portion of the sum + # of wage-and-salary income and taxable self employment income + # that exceeds SS_Earnings_thd + sey_frac = 1.0 - 0.5 * FICA_ss_trt + was_plus_sey_p = gross_was_p + max(0.0, sey_p * sey_frac) + was_plus_sey_s = gross_was_s + max(0.0, sey_s * sey_frac) + extra_ss_income_p = max(0.0, was_plus_sey_p - SS_Earnings_thd) + extra_ss_income_s = max(0.0, was_plus_sey_s - SS_Earnings_thd) + extra_payrolltax = ( + extra_ss_income_p * FICA_ss_trt + extra_ss_income_s * FICA_ss_trt + ) + + # compute part of total payroll taxes for filing unit + # (the ptax_amc part of total payroll taxes for the filing unit is + # computed in the AdditionalMedicareTax function below) + payrolltax = ptax_was + setax + extra_payrolltax + + # compute OASDI part of payroll taxes + ptax_oasdi = ( + ptax_ss_was_p + + ptax_ss_was_s + + setax_ss_p + + setax_ss_s + + extra_payrolltax + ) + + # compute earned* variables and AGI deduction for + # "employer share" of self-employment tax, c03260 + # Note: c03260 is the amount on 2015 Form 1040, line 27 + c03260 = (1.0 - ALD_SelfEmploymentTax_hc) * 0.5 * setax + earned = max(0.0, e00200p + e00200s + sey - c03260) + earned_p = max( + 0.0, + (e00200p + sey_p - (1.0 - ALD_SelfEmploymentTax_hc) * 0.5 * setax_p), + ) + earned_s = max( + 0.0, + (e00200s + sey_s - (1.0 - ALD_SelfEmploymentTax_hc) * 0.5 * setax_s), + ) + return ( + sey, + payrolltax, + ptax_was, + setax, + c03260, + ptax_oasdi, + earned, + earned_p, + earned_s, + was_plus_sey_p, + was_plus_sey_s, + ) + + +def DependentCare( + nu13, + elderly_dependents, + earned, + MARS, + ALD_Dependents_thd, + ALD_Dependents_hc, + ALD_Dependents_Child_c, + ALD_Dependents_Elder_c, + care_deduction, +): + """ + Computes dependent-care above-the-line deduction. + + Parameters + ---------- + nu13: int + Number of dependents under 13 years old + elderly_dependents: int + Number of elderly dependents age 65+ in filing unit excluding taxpayer and spouse + earned: float + Earned income for filing unit + MARS: int + Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + ALD_Dependents_thd: list + Maximum income to qualify for dependent care deduction + ALD_Dependents_hc: float + Deduction for childcare costs haircut + ALD_Dependents_Child_c: float + National weighted average cost of childcare, ceiling for available childcare deduction + ALD_Dependents_Elder_c: float + Eldercare deduction ceiling + + Returns + ------- + care_deduction: float + Total above the line deductions for dependent care. + """ + + if earned <= ALD_Dependents_thd[MARS - 1]: + care_deduction = ( + (1.0 - ALD_Dependents_hc) * nu13 * ALD_Dependents_Child_c + ) + ( + (1.0 - ALD_Dependents_hc) + * elderly_dependents + * ALD_Dependents_Elder_c + ) + else: + care_deduction = 0.0 + return care_deduction + + +def Adj( + e03150, + e03210, + c03260, + e03270, + e03300, + e03400, + e03500, + e00800, + e03220, + e03230, + e03240, + e03290, + care_deduction, + ALD_StudentLoan_hc, + ALD_SelfEmp_HealthIns_hc, + ALD_KEOGH_SEP_hc, + ALD_EarlyWithdraw_hc, + ALD_AlimonyPaid_hc, + ALD_AlimonyReceived_hc, + ALD_EducatorExpenses_hc, + ALD_HSADeduction_hc, + ALD_IRAContributions_hc, + ALD_DomesticProduction_hc, + ALD_Tuition_hc, + c02900, +): + """ + Adj calculates Form 1040 AGI adjustments (i.e., Above-the-Line Deductions). + + Parameters + ----- + e03210: float + Student loan interest paid + e03220: float + Educator expenses + e03150: float + Total deductible IRA plan contributions + e03230: float + Tuition and fees (Form 8917) + e03240: float + Domestic production activity deduction (Form 8903) + c03260: float + Self-employment tax deduction (after haircut) + e03270: float + Self-employed health insurance premiums + e03290: float + HSA deduction (Form 8889) + e03300: float + Total deductible KEOGH/SEP/SIMPLE/etc. plan contributions + e03400: float + Penalty on early withdrawal of savings deduction + e03500: float + Alimony paid + e00800: float + Alimony received + care_deduction: float + Dependent care expense deduction + ALD_StudentLoan_hc: float + Student loan interest deduction haircut + ALD_SelfEmp_HealthIns_hc: float + Self-employed h.i. deduction haircut + ALD_KEOGH_SEP_hc: float + KEOGH/etc. plan contribution deduction haircut + ALD_EarlyWithdraw_hc: float + Penalty on early withdrawal deduction haricut + ALD_AlimonyPaid_hc: float + Alimony paid deduction haircut + ALD_AlimonyReceived_hc: float + Alimony received deduction haircut + ALD_EducatorExpenses_hc: float + Eductor expenses haircut + ALD_HSADeduction_hc: float + HSA Deduction haircut + ALD_IRAContributions_hc: float + IRA Contribution haircut + ALD_DomesticProduction_hc: float + Domestic production haircut + ALD_Tuition_hc: float + Tuition and fees haircut + + Returns + ------- + c02900: float + Total of all "above the line" income adjustments to get AGI + """ + # Form 2555 foreign earned income exclusion is assumed to be zero + # Form 1040 adjustments that are included in expanded income: + c02900 = ( + (1.0 - ALD_StudentLoan_hc) * e03210 + + c03260 + + (1.0 - ALD_EarlyWithdraw_hc) * e03400 + + (1.0 - ALD_AlimonyPaid_hc) * e03500 + + (1.0 - ALD_AlimonyReceived_hc) * e00800 + + (1.0 - ALD_EducatorExpenses_hc) * e03220 + + (1.0 - ALD_Tuition_hc) * e03230 + + (1.0 - ALD_DomesticProduction_hc) * e03240 + + (1.0 - ALD_HSADeduction_hc) * e03290 + + (1.0 - ALD_SelfEmp_HealthIns_hc) * e03270 + + (1.0 - ALD_IRAContributions_hc) * e03150 + + (1.0 - ALD_KEOGH_SEP_hc) * e03300 + + care_deduction + ) + return c02900 + + +def ALD_InvInc_ec_base( + p22250, p23250, sep, e00300, e00600, e01100, e01200, invinc_ec_base +): + """ + Computes invinc_ec_base. + + Parameters + ---------- + p22250: float + Net short-term capital gails/losses (Schedule D) + p23250: float + Net long-term capital gains/losses (Schedule D) + sep: int + 2 when MARS is 3 (married filing separately); otherwise 1 + e00300: float + Taxable interest income + e00600: float + Ordinary dividends included in AGI + e01100: float + Capital gains distributions not reported on Schedule D + e01200: float + Other net gain/loss from Form 4797 + invinc_ec_base: float + Exclusion of investment income from AGI + + Returns + ------- + invinc_ec_base: float + Exclusion of investment income from AGI + """ + # limitation on net short-term and long-term capital losses + cgain = max((-3000.0 / sep), p22250 + p23250) + # compute exclusion of investment income from AGI + invinc_ec_base = e00300 + e00600 + cgain + e01100 + e01200 + return invinc_ec_base + + +def CapGains( + p23250, + p22250, + sep, + ALD_StudentLoan_hc, + ALD_InvInc_ec_rt, + invinc_ec_base, + e00200, + e00300, + e00600, + e00650, + e00700, + e00800, + CG_nodiff, + CG_ec, + CG_reinvest_ec_rt, + ALD_BusinessLosses_c, + MARS, + e00900, + e01100, + e01200, + e01400, + e01700, + e02000, + e02100, + e02300, + e00400, + e02400, + c02900, + e03210, + e03230, + e03240, + c01000, + c23650, + ymod, + ymod1, + invinc_agi_ec, +): + """ + CapGains function: ... + + Parameters + ---------- + p23250: float + Net long-term capital gains/losses (Schedule D) + p22250: float + Net short-term capital gails/losses (Schedule D) + sep: int + 2 when MARS is 3 (married filing separately); otherwise 1 + ALD_StudentLoan_hc: float + Student loan interest deduction haircut + ALD_InvInc_ec_rt: float + Investment income exclusion rate haircut + invinc_ec_base: float + Exclusion of investment income from AGI + e00200: float + Wages, salaries, tips for filing unit net of pension contributions + e00300: float + Taxable interest income + e00600: float + Ordinary dividends included in AGI + e00650: float + Qualified dividends included in ordinary dividends + e00700: float + Taxable refunds of state and local income taxes + e00800: float + Alimony received + CG_nodiff: bool + Long term capital gains and qualified dividends taxed no differently than regular taxable income + CG_ec: float + Dollar amount of all capital gains and qualified dividends that are excluded from AGI + CG_reinvest_ec_rt: float + Fraction of all capital gains and qualified dividends in excess of the dollar exclusion that are excluded from AGI + ALD_BusinessLosses_c: list + Maximm amount of business losses deductible + MARS: int + Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + e00900: float + Schedule C business net profit/loss for filing unit + e01100: float + Capital gain distributions not reported on Schedule D + e01200: float + Other net gain/loss from Form 4797 + e01400: float + Taxable IRA distributions + e01700: float + Taxable pensions and annunities + e02000: float + Schedule E total rental, royalty, partnership, S-corporation, etc, income/loss (includes e26270 and e27200) + e02100: float + Farm net income/loss for filing unit from Schedule F + e02300: float + Unemployment insurance benefits + e00400: float + Tax-exempt interest income + e02400: float + Total social security (OASDI) benefits + c02900: float + Total of all "above the line" income adjustments to get AGI + e03210: float + Student loan interest + e03230: float + Tuition and fees from Form 8917 + e03240: float + Domestic production activities from Form 8903 + c01000: float + Limitation on capital losses + c23650: float + Net capital gains (long and short term) before exclusion + ymod: float + Variable that is used in OASDI benefit taxation logic + ymod1: float + Variable that is included in AGI + invinc_agi_ec: float + Exclusion of investment income from AGI + + Returns + ------- + c01000: float + Limitation on capital losses + c23650: float + Net capital gains (long and short term) before exclusion + ymod: float + Variable that is used in OASDI benefit taxation logic + ymod1: float + Variable that is included in AGI + invinc_agi_ec: float + Exclusion of investment income from AGI + """ + # net capital gain (long term + short term) before exclusion + c23650 = p23250 + p22250 + # limitation on capital losses + c01000 = max((-3000.0 / sep), c23650) + # compute total investment income + invinc = e00300 + e00600 + c01000 + e01100 + e01200 + # compute exclusion of investment income from AGI + invinc_agi_ec = ALD_InvInc_ec_rt * max(0.0, invinc_ec_base) + # compute ymod1 variable that is included in AGI + ymod1 = ( + e00200 + + e00700 + + e00800 + + e01400 + + e01700 + + invinc + - invinc_agi_ec + + e02100 + + e02300 + + max(e00900 + e02000, -ALD_BusinessLosses_c[MARS - 1]) + ) + if CG_nodiff: + # apply QDIV+CG exclusion if QDIV+LTCG receive no special tax treatment + qdcg_pos = max(0.0, e00650 + c01000) + qdcg_exclusion = min(CG_ec, qdcg_pos) + CG_reinvest_ec_rt * max( + 0.0, qdcg_pos - CG_ec + ) + ymod1 = max(0.0, ymod1 - qdcg_exclusion) + invinc_agi_ec += qdcg_exclusion + # compute ymod variable that is used in OASDI benefit taxation logic + ymod2 = e00400 + (0.50 * e02400) - c02900 + ymod3 = (1.0 - ALD_StudentLoan_hc) * e03210 + e03230 + e03240 + ymod = ymod1 + ymod2 + ymod3 + return (c01000, c23650, ymod, ymod1, invinc_agi_ec) + + +def SSBenefits( + MARS, + ymod, + e02400, + SS_thd50, + SS_thd85, + SS_percentage1, + SS_percentage2, + c02500, +): + """ + Calculates OASDI benefits included in AGI, c02500. + + Parameters + ---------- + MARS: int + Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + ymod: float + Variable that is used in OASDI benefit taxation logic + e02400: float + Total social security (OASDI) benefits + SS_thd50: list + Threshold for social security benefit taxability (1) + SS_thd85: list + Threshold for social security benefit taxability (2) + SS_percentage1: float + Social security taxable income decimal fraction (1) + SS_percentage2: float + Social security taxable income decimal fraction (2) + c02500: float + Social security (OASDI) benefits included in AGI + + Returns + ------- + c02500: float + Social security (OASDI) benefits included in AGI + """ + if ymod < SS_thd50[MARS - 1]: + c02500 = 0.0 + elif ymod < SS_thd85[MARS - 1]: + c02500 = SS_percentage1 * min(ymod - SS_thd50[MARS - 1], e02400) + else: + c02500 = min( + SS_percentage2 * (ymod - SS_thd85[MARS - 1]) + + SS_percentage1 + * min(e02400, SS_thd85[MARS - 1] - SS_thd50[MARS - 1]), + SS_percentage2 * e02400, + ) + return c02500 + + +def UBI( + nu18, + n1820, + n21, + UBI_u18, + UBI_1820, + UBI_21, + UBI_ecrt, + ubi, + taxable_ubi, + nontaxable_ubi, +): + """ + Calculates total and taxable Universal Basic Income (UBI) amount. + + Parameters + ---------- + nu18: int + Number of people in the tax unit under 18 + n1820: int + Number of people in the tax unit age 18-20 + n21: int + Number of people in the tax unit age 21+ + UBI_u18: float + UBI benefit for those under 18 + UBI_1820: float + UBI benefit for those between 18 to 20 + UBI_21: float + UBI benefit for those 21 or more + UBI_ecrt: float + Fraction of UBI benefits that are not included in AGI + ubi: float + Total UBI received by the tax unit (is included in expanded_income) + taxable_ubi: float + Amount of UBI that is taxable (is added to AGI) + nontaxable_ubi: float + Amount of UBI that is nontaxable + + Returns + ------- + ubi: float + Total UBI received by the tax unit (is included in expanded_income) + taxable_ubi: float + Amount of UBI that is taxable (is added to AGI) + nontaxable_ubi: float + Amount of UBI that is nontaxable + """ + ubi = nu18 * UBI_u18 + n1820 * UBI_1820 + n21 * UBI_21 + taxable_ubi = ubi * (1.0 - UBI_ecrt) + nontaxable_ubi = ubi - taxable_ubi + return ubi, taxable_ubi, nontaxable_ubi + + +def AGI( + ymod1, + c02500, + c02900, + XTOT, + MARS, + sep, + DSI, + exact, + nu18, + taxable_ubi, + II_em, + II_em_ps, + II_prt, + II_no_em_nu18, + c00100, + pre_c04600, + c04600, +): + """ + Computes Adjusted Gross Income (AGI), c00100, and + compute personal exemption amount, c04600. + + Parameters + ---------- + ymod1: float + Variable that is included in AGI + c02500: float + Social security (OASDI) benefits included in AGI + c02900: float + Total of all "above the line" income adjustments to get AGI + XTOT: int + Total number of exemptions for filing unit + MARS: int + Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + sep: int + 2 when MARS is 3 (married filing separately); otherwise 1 + DSI: int + 1 if claimed as dependent on another return; otherwise 0 + exact: int + Whether or not to do rounding of phaseout fraction + nu18: int + Number of people in the tax unit under 18 + taxable_ubi: float + Amount of UBI that is taxable (is added to AGI) + II_em: float + Personal and dependent exemption amount + II_em_ps: list + Personal exemption phaseout starting income + II_prt: float + Personal exemption phaseout rate + II_no_em_nu18: float + Repeal personal exemptions for dependents under age 18 + c00100: float + Adjusted Gross Income (AGI) + pre_c04600: float + Personal exemption before phase-out + c04600: float + Personal exemptions after phase-out + Returns + ------- + c00100: float + Adjusted Gross Income (AGI) + pre_c04600: float + Personal exemption before phase-out + c04600: float + Personal exemptions after phase-out + """ + # calculate AGI assuming no foreign earned income exclusion + c00100 = ymod1 + c02500 - c02900 + taxable_ubi + # calculate personal exemption amount + if II_no_em_nu18: # repeal of personal exemptions for deps. under 18 + pre_c04600 = max(0, XTOT - nu18) * II_em + else: + pre_c04600 = XTOT * II_em + if DSI: + pre_c04600 = 0.0 + # phase-out personal exemption amount + if exact == 1: # exact calculation as on tax forms + line5 = max(0.0, c00100 - II_em_ps[MARS - 1]) + line6 = math.ceil(line5 / (2500.0 / sep)) + line7 = II_prt * line6 + c04600 = max(0.0, pre_c04600 * (1.0 - line7)) + else: # smoothed calculation needed for sensible mtr calculation + dispc_numer = II_prt * (c00100 - II_em_ps[MARS - 1]) + dispc_denom = 2500.0 / sep + dispc = min(1.0, max(0.0, dispc_numer / dispc_denom)) + c04600 = pre_c04600 * (1.0 - dispc) + return (c00100, pre_c04600, c04600) + + +def ItemDedCap( + e17500, + e18400, + e18500, + e19200, + e19800, + e20100, + e20400, + g20500, + c00100, + ID_AmountCap_rt, + ID_AmountCap_Switch, + e17500_capped, + e18400_capped, + e18500_capped, + e19200_capped, + e19800_capped, + e20100_capped, + e20400_capped, + g20500_capped, +): + """ + Applies a cap to gross itemized deductions. + + Parameters + ---------- + e17500: float + Itemizable medical and dental expenses + e18400: float + Itemizable state and local income/sales taxes + e18500: float + Itemizable real-estate taxes paid + e19200: float + Itemizable interest paid + e19800: float + Itemizable charitable giving: cash/check contributions + e20100: float + Itemizable charitalb giving: other than cash/check contributions + e20400: float + Itemizable gross (before 10% AGI disregard) casualty or theft loss + g20500: float + Itemizable gross (before 10% AGI disregard) casualty or theft loss + c00100: float + Adjusted gross income (AGI) + ID_AmountCap_rt: float + Ceiling on the gross amount of itemized deductions allowed; decimal fraction of AGI + ID_AmountCap_Switch: list + Deductions subject to the cap on itemized deduction benefits + e17500_capped: float + Schedule A: medical expenses, capped by ItemDedCap as a decimal fraction of AGI + e18400_capped: float + Schedule A: state and local income taxes deductlbe, capped by ItemDedCap as a decimal fraction of AGI + e18500_capped: float + Schedule A: state and local real estate taxes deductible, capped by ItemDedCap as a decimal fraction of AGI + e19200_capped: float + Schedule A: interest deduction deductible, capped by ItemDedCap as decimal fraction of AGI + e19800_capped: float + Schedule A: charity cash contributions deductible, capped by ItemDedCap as a decimal fraction of AGI + e20100_capped: float + Schedule A: charity noncash contributions deductible, capped aby ItemDedCap s a decimal fraction of AGI + e20400_capped: float + Schedule A: gross miscellaneous deductions deductible, capped by ItemDedCap as a decimal fraction of AGI + g20500_capped: float + Schedule A: gross casualty or theft loss deductible, capped aby ItemDedCap s a decimal fraction of AGI + + Returns + ------- + e17500_capped: float + Schedule A: medical expenses, capped by ItemDedCap as a decimal fraction of AGI + e18400_capped: float + Schedule A: state and local income taxes deductlbe, capped by ItemDedCap as a decimal fraction of AGI + e18500_capped: float + Schedule A: state and local real estate taxes deductible, capped by ItemDedCap as a decimal fraction of AGI + e19200_capped: float + Schedule A: interest deduction deductible, capped by ItemDedCap as decimal fraction of AGI + e19800_capped: float + Schedule A: charity cash contributions deductible, capped by ItemDedCap as a decimal fraction of AGI + e20100_capped: float + Schedule A: charity noncash contributions deductible, capped by ItemDedCap as a decimal fraction of AGI + e20400_capped: float + Schedule A: gross miscellaneous deductions deductible, capped by ItemDedCap as a decimal fraction of AGI + g20500_capped: float + Schedule A: gross casualty or theft loss deductible, capped by ItemDedCap as a decimal fraction of AGI + """ + # pylint: disable=too-many-branches + + cap = max(0.0, ID_AmountCap_rt * c00100) + + gross_ded_amt = 0 + if ID_AmountCap_Switch[0]: # medical + gross_ded_amt += e17500 + if ID_AmountCap_Switch[1]: # statelocal + gross_ded_amt += e18400 + if ID_AmountCap_Switch[2]: # realestate + gross_ded_amt += e18500 + if ID_AmountCap_Switch[3]: # casualty + gross_ded_amt += g20500 + if ID_AmountCap_Switch[4]: # misc + gross_ded_amt += e20400 + if ID_AmountCap_Switch[5]: # interest + gross_ded_amt += e19200 + if ID_AmountCap_Switch[6]: # charity + gross_ded_amt += e19800 + e20100 + + overage = max(0.0, gross_ded_amt - cap) + + e17500_capped = e17500 + e18400_capped = e18400 + e18500_capped = e18500 + g20500_capped = g20500 + e20400_capped = e20400 + e19200_capped = e19200 + e19800_capped = e19800 + e20100_capped = e20100 + + if overage > 0.0 and c00100 > 0.0: + if ID_AmountCap_Switch[0]: # medical + e17500_capped -= (e17500 / gross_ded_amt) * overage + if ID_AmountCap_Switch[1]: # statelocal + e18400_capped -= e18400 / (gross_ded_amt) * overage + if ID_AmountCap_Switch[2]: # realestate + e18500_capped -= (e18500 / gross_ded_amt) * overage + if ID_AmountCap_Switch[3]: # casualty + g20500_capped -= (g20500 / gross_ded_amt) * overage + if ID_AmountCap_Switch[4]: # misc + e20400_capped -= (e20400 / gross_ded_amt) * overage + if ID_AmountCap_Switch[5]: # interest + e19200_capped -= (e19200 / gross_ded_amt) * overage + if ID_AmountCap_Switch[6]: # charity + e19800_capped -= (e19800 / gross_ded_amt) * overage + e20100_capped -= (e20100 / gross_ded_amt) * overage + + return ( + e17500_capped, + e18400_capped, + e18500_capped, + g20500_capped, + e20400_capped, + e19200_capped, + e19800_capped, + e20100_capped, + ) + + +def ItemDed( + e17500_capped, + e18400_capped, + e18500_capped, + e19200_capped, + e19800_capped, + e20100_capped, + e20400_capped, + g20500_capped, + MARS, + age_head, + age_spouse, + c00100, + c04470, + c21040, + c21060, + c17000, + c18300, + c19200, + c19700, + c20500, + c20800, + ID_ps, + ID_Medical_frt, + ID_Medical_frt_add4aged, + ID_Medical_hc, + ID_Casualty_frt, + ID_Casualty_hc, + ID_Miscellaneous_frt, + ID_Miscellaneous_hc, + ID_Charity_crt_all, + ID_Charity_crt_noncash, + ID_prt, + ID_crt, + ID_c, + ID_StateLocalTax_hc, + ID_Charity_frt, + ID_Charity_hc, + ID_InterestPaid_hc, + ID_RealEstate_hc, + ID_Medical_c, + ID_StateLocalTax_c, + ID_RealEstate_c, + ID_InterestPaid_c, + ID_Charity_c, + ID_Casualty_c, + ID_Miscellaneous_c, + ID_AllTaxes_c, + ID_AllTaxes_hc, + ID_StateLocalTax_crt, + ID_RealEstate_crt, + ID_Charity_f, +): + """ + Calculates itemized deductions, Form 1040, Schedule A. + + Parameters + ---------- + e17500_capped: float + Schedule A: medical expenses, capped by ItemDedCap as a decimal fraction of AGI + e18400_capped: float + Schedule A: state and local income taxes deductlbe, capped by ItemDedCap as a decimal fraction of AGI + e18500_capped: float + Schedule A: state and local real estate taxes deductible, capped by ItemDedCap as a decimal fraction of AGI + e19200_capped: float + Schedule A: interest deduction deductible, capped by ItemDedCap as decimal fraction of AGI + e19800_capped: float + Schedule A: charity cash contributions deductible, capped by ItemDedCap as a decimal fraction of AGI + e20100_capped: float + Schedule A: charity noncash contributions deductible, capped by ItemDedCap as a decimal fraction of AGI + e20400_capped: float + Schedule A: gross miscellaneous deductions deductible, capped by ItemDedCap as a decimal fraction of AGI + g20500_capped: float + Schedule A: gross casualty or theft loss deductible, capped by ItemDedCap as a decimal fraction of AGI + MARS: int + Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + age_head: int + Age in years of taxpayer + age_spouse: int + Age in years of spouse + c00100: float + Adjusted gross income (AGI) + c04470: float + Itemized deductions after phase out (0 for non itemizers) + c21040: float + Itemized deductions that are phased out + c21060: float + Itemized deductions before phase out (0 for non itemizers) + c17000: float + Schedule A: medical expenses deducted + c18300: float + Schedule A: state and local taxes plus real estate taxes deducted + c19200: float + Schedule A: interest deducted + c19700: float + Schedule A: charity contributions deducted + c20500: float + Schedule A: net casualty or theft loss deducted + c20800: float + Schedule A: net limited miscellaneous deductions deducted + ID_ps: list + Itemized deduction phaseout AGI start (Pease) + ID_Medical_frt: float + Floor (as decimal fraction of AGI) for deductible medical expenses + ID_Medical_frt_add4aged: float + Add on floor (as decimal fraction of AGI) for deductible medical expenses for elderly filing units + ID_Medical_hc: float + Medical expense deduction haircut + ID_Casualty_frt: float + Floor (as decimal fraction of AGI) for deductible casualty loss + ID_Casualty_hc: float + Casualty expense deduction haircut + ID_Miscellaneous_frt: float + Floor (as decimal fraction of AGI) for deductible miscellaneous expenses + ID_Miscellaneous_hc: float + Miscellaneous expense deduction haircut + ID_Charity_crt_all: float + Ceiling (as decimal fraction of AGI) for all charitable contribution deductions + ID_Charity_crt_noncash: float + Ceiling (as decimal fraction of AGI) for noncash charitable contribution deductions + ID_prt: float + Itemized deduction phaseout rate (Pease) + ID_crt: float + Itemized deduction maximum phaseout as a decimal fraction of total itemized deductions (Pease) + ID_c: list + Ceiling on the amount of itemized deductions allowed (dollars) + ID_StateLocalTax_hc: float + State and local income and sales taxes deduction haircut + ID_Charity_frt: float + Floor (as decimal fraction of AGI) for deductible charitable contributions + ID_Charity_hc: float + Charity expense deduction haircut + ID_InterestPaid_hc: float + Interest paid deduction haircut + ID_RealEstate_hc: float + State, local, and foreign real estate taxes deductions haircut + ID_Medical_c: list + Ceiling on the amount of medical expense deduction allowed (dollars) + ID_StateLocalTax_c: list + Ceiling on the amount of state and local income and sales taxes deduction allowed (dollars) + ID_RealEstate_c: list + Ceiling on the amount of state, local, and foreign real estate taxes deduction allowed (dollars) + ID_InterestPaid_c: list + Ceiling on the amount of interest paid deduction allowed (dollars) + ID_Charity_c: list + Ceiling on the amount of charity expense deduction allowed (dollars) + ID_Casualty_c: list + Ceiling on the amount of casualty expense deduction allowed (dollars) + ID_Miscellaneous_c: list + Ceiling on the amount of miscellaneous expense deduction allowed (dollars) + ID_AllTaxes_c: list + Ceiling on the amount of state and local income, stales, and real estate deductions allowed (dollars) + ID_AllTaxes_hc: float + State and local income, sales, and real estate tax deduciton haircut + ID_StateLocalTax_crt: float + Ceiling (as decimal fraction of AGI) for the combination of all state and local income and sales tax deductions + ID_RealEstate_crt: float + Ceiling (as decimal fraction of AGI) for the combination of all state, local, and foreign real estate tax deductions + ID_Charity_f: list + Floor on the amount of charity expense deduction allowed (dollars) + + Returns + ------- + c17000: float + Schedule A: medical expenses deducted + c18300: float + Schedule A: state and local taxes plus real estate taxes deducted + c19200: float + Schedule A: interest deducted + c19700: float + Schedule A: charity contributions deducted + c20500: float + Schedule A: net casualty or theft loss deducted + c20800: float + Schedule A: net limited miscellaneous deductions deducted + c21040: float + Itemized deductions that are phased out + c21060: float + Itemized deductions before phase out (0 for non itemizers) + c04470: float + Itemized deductions after phase out (0 for non itemizers) + """ + posagi = max(c00100, 0.0) + # Medical + medical_frt = ID_Medical_frt + if age_head >= 65 or (MARS == 2 and age_spouse >= 65): + medical_frt += ID_Medical_frt_add4aged + c17750 = medical_frt * posagi + c17000 = max(0.0, e17500_capped - c17750) * (1.0 - ID_Medical_hc) + c17000 = min(c17000, ID_Medical_c[MARS - 1]) + # State and local taxes + c18400 = min( + (1.0 - ID_StateLocalTax_hc) * max(e18400_capped, 0.0), + ID_StateLocalTax_c[MARS - 1], + ) + c18500 = min( + (1.0 - ID_RealEstate_hc) * e18500_capped, ID_RealEstate_c[MARS - 1] + ) + # following two statements implement a cap on c18400 and c18500 in a way + # that those with negative AGI, c00100, are not capped under current law, + # hence the 0.0001 rather than zero + c18400 = min(c18400, ID_StateLocalTax_crt * max(c00100, 0.0001)) + c18500 = min(c18500, ID_RealEstate_crt * max(c00100, 0.0001)) + c18300 = (c18400 + c18500) * (1.0 - ID_AllTaxes_hc) + c18300 = min(c18300, ID_AllTaxes_c[MARS - 1]) + # Interest paid + c19200 = e19200_capped * (1.0 - ID_InterestPaid_hc) + c19200 = min(c19200, ID_InterestPaid_c[MARS - 1]) + # Charity + lim30 = min(ID_Charity_crt_noncash * posagi, e20100_capped) + c19700 = min(ID_Charity_crt_all * posagi, lim30 + e19800_capped) + # charity floor is zero in present law + charity_floor = max(ID_Charity_frt * posagi, ID_Charity_f[MARS - 1]) + c19700 = max(0.0, c19700 - charity_floor) * (1.0 - ID_Charity_hc) + c19700 = min(c19700, ID_Charity_c[MARS - 1]) + # Casualty + c20500 = max(0.0, g20500_capped - ID_Casualty_frt * posagi) * ( + 1.0 - ID_Casualty_hc + ) + c20500 = min(c20500, ID_Casualty_c[MARS - 1]) + # Miscellaneous + c20400 = e20400_capped + c20750 = ID_Miscellaneous_frt * posagi + c20800 = max(0.0, c20400 - c20750) * (1.0 - ID_Miscellaneous_hc) + c20800 = min(c20800, ID_Miscellaneous_c[MARS - 1]) + # Gross total itemized deductions + c21060 = c17000 + c18300 + c19200 + c19700 + c20500 + c20800 + # Limitations on total itemized deductions + # (no attempt to adjust c04470 components for limitations) + nonlimited = c17000 + c20500 + limitstart = ID_ps[MARS - 1] + if c21060 > nonlimited and c00100 > limitstart: + dedmin = ID_crt * (c21060 - nonlimited) + dedpho = ID_prt * max(0.0, posagi - limitstart) + c21040 = min(dedmin, dedpho) + c04470 = c21060 - c21040 + else: + c21040 = 0.0 + c04470 = c21060 + c04470 = min(c04470, ID_c[MARS - 1]) + # Return total itemized deduction amounts and components + return ( + c17000, + c18300, + c19200, + c19700, + c20500, + c20800, + c21040, + c21060, + c04470, + ) + + +def AdditionalMedicareTax( + e00200, + MARS, + AMEDT_ec, + sey, + AMEDT_rt, + FICA_mc_trt, + FICA_ss_trt, + ptax_amc, + payrolltax, +): + """ + Computes Additional Medicare Tax (Form 8959) included in payroll taxes. + + Parameters + ----- + MARS: int + Filing marital status (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + AMEDT_ec: list + Additional Medicare Tax earnings exclusion + AMEDT_rt: float + Additional Medicare Tax rate + FICA_ss_trt: float + FICA Social Security tax rate + FICA_mc_trt: float + FICA Medicare tax rate + e00200: float + Wages and salaries + sey: float + Self-employment income + ptax_amc: float + Additional Medicare Tax + payrolltax: float + payroll tax augmented by Additional Medicare Tax + + Returns + ------- + ptax_amc: float + Additional Medicare Tax + payrolltax: float + payroll tax augmented by Additional Medicare Tax + """ + line8 = max(0.0, sey) * (1.0 - 0.5 * (FICA_mc_trt + FICA_ss_trt)) + line11 = max(0.0, AMEDT_ec[MARS - 1] - e00200) + ptax_amc = AMEDT_rt * ( + max(0.0, e00200 - AMEDT_ec[MARS - 1]) + max(0.0, line8 - line11) + ) + payrolltax += ptax_amc + return (ptax_amc, payrolltax) + + +def StdDed( + DSI, + earned, + STD, + age_head, + age_spouse, + STD_Aged, + STD_Dep, + MARS, + MIDR, + blind_head, + blind_spouse, + standard, + c19700, + STD_allow_charity_ded_nonitemizers, + STD_charity_ded_nonitemizers_max, +): + """ + Calculates standard deduction, including standard deduction for + dependents, aged and bind. + + Parameters + ----- + DSI: int + 1 if claimed as dependent on another return; otherwise 0 + earned: float + Earned income for filing unit + STD: list + Standard deduction amount + age_head: int + Age in years of taxpayer + age_spouse: int + Age in years of spouse + STD_Aged: list + Additional standard deduction for blind and aged + STD_Dep: float + Standard deduction for dependents + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + MIDR: int + 1 if separately filing spouse itemizes, 0 otherwise + blind_head: int + 1 if taxpayer is blind, 0 otherwise + blind_spouse: int + 1 if spouse is blind, 0 otherwise + standard: float + Standard deduction (zero for itemizers) + c19700: float + Schedule A: charity contributions deducted + STD_allow_charity_ded_nonitemizers: bool + Allow standard deduction filers to take the charitable contributions deduction + STD_charity_ded_nonitemizers_max: float + Ceiling amount (in dollars) for charitable deductions for non-itemizers + + Returns + ------- + standard: float + Standard deduction (zero for itemizers) + """ + # calculate deduction for dependents + if DSI == 1: + c15100 = max(350.0 + earned, STD_Dep) + basic_stded = min(STD[MARS - 1], c15100) + else: + c15100 = 0.0 + if MIDR == 1: + basic_stded = 0.0 + else: + basic_stded = STD[MARS - 1] + # calculate extra standard deduction for aged and blind + num_extra_stded = blind_head + blind_spouse + if age_head >= 65: + num_extra_stded += 1 + if MARS == 2 and age_spouse >= 65: + num_extra_stded += 1 + extra_stded = num_extra_stded * STD_Aged[MARS - 1] + # calculate the total standard deduction + standard = basic_stded + extra_stded + if MARS == 3 and MIDR == 1: + standard = 0.0 + if STD_allow_charity_ded_nonitemizers: + standard += min(c19700, STD_charity_ded_nonitemizers_max) + return standard + + +def TaxInc( + c00100, + standard, + c04470, + c04600, + MARS, + e00900, + e26270, + e02100, + e27200, + e00650, + c01000, + e02300, + PT_SSTB_income, + PT_binc_w2_wages, + PT_ubia_property, + PT_qbid_rt, + PT_qbid_taxinc_thd, + PT_qbid_taxinc_gap, + PT_qbid_w2_wages_rt, + PT_qbid_alt_w2_wages_rt, + PT_qbid_alt_property_rt, + c04800, + PT_qbid_ps, + PT_qbid_prt, + qbided, + PT_qbid_limit_switch, + UI_em, + UI_thd, +): + """ + Calculates taxable income, c04800, and + qualified business income deduction, qbided. + + Parameters + ---------- + c00100: float + Adjusted Gross Income (AGI) + standard: float + Standard deduction (zero for itemizers) + c04470: float + Itemized deductions after phase-out (zero for non-itemizers) + c04600: float + Personal exemptions after phase-out + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + e00900: float + Schedule C business net profit/loss for filing unit + e26270: float + Schedule E: combined partnership and S-corporation net income/loss + e02100: float + Farm net income/loss for filing unit from Schedule F + e27200: float + Schedule E: farm rent net income or loss + e00650: float + Qualified dividends included in ordinary dividends + c01000: float + Limitation on capital losses + PT_SSTB_income: int + Value of one implies business income is from a specified service trade or business (SSTB) + Value of zero implies business income is from a qualified trade or business + PT_binc_w2_wages: float + Filing unit's share of total W-2 wages paid by the pass-through business + PT_ubia_property: float + Filing unit's share of total business property owned by the pass-through business + PT_qbid_rt: float + Pass-through qualified business income deduction rate + PT_qbid_taxinc_thd: list + Lower threshold of pre-QBID taxable income + PT_qbid_taxinc_gap: list + Dollar gap between upper and lower threshold of pre-QBID taxable income + PT_qbid_w2_wages_rt: float + QBID cap rate on pass-through business W-2 wages paid + PT_qbid_alt_w2_wages_rt: float + Alternative QBID cap rate on pass-through business W-2 wages paid + PT_qbid_alt_property_rt: float + Alternative QBID cap rate on pass-through business property owned + c04800: float + Regular taxable income + PT_qbid_ps: list + QBID phaseout taxable income start + PT_qbid_prt: float + QBID phaseout rate + qbided: float + Qualified Business Income (QBI) deduction + PT_qbid_limit_switch: bool + QBID wage and capital limitations switch + + Returns + ------- + c04800: float + Regular taxable income + qbided: float + Qualified Business Income (QBI) deduction + """ + # calculate UI excluded from taxable income + if (c00100 - e02300) <= UI_thd[MARS - 1]: + ui_excluded = min(e02300, UI_em) + else: + ui_excluded = 0.0 + # calculate taxable income before qualified business income deduction + pre_qbid_taxinc = max( + 0.0, c00100 - max(c04470, standard) - c04600 - ui_excluded + ) + # calculate qualified business income deduction + qbided = 0.0 + qbinc = max(0.0, e00900 + e26270 + e02100 + e27200) + if qbinc > 0.0 and PT_qbid_rt > 0.0: + qbid_before_limits = qbinc * PT_qbid_rt + lower_thd = PT_qbid_taxinc_thd[MARS - 1] + if pre_qbid_taxinc <= lower_thd: + qbided = qbid_before_limits + else: + pre_qbid_taxinc_gap = PT_qbid_taxinc_gap[MARS - 1] + upper_thd = lower_thd + pre_qbid_taxinc_gap + if PT_SSTB_income == 1 and pre_qbid_taxinc >= upper_thd: + qbided = 0.0 + # if PT_qbid_limit_switch is True, apply wage/capital + # limitations. + elif PT_qbid_limit_switch: + wage_cap = PT_binc_w2_wages * PT_qbid_w2_wages_rt + alt_cap = ( + PT_binc_w2_wages * PT_qbid_alt_w2_wages_rt + + PT_ubia_property * PT_qbid_alt_property_rt + ) + full_cap = max(wage_cap, alt_cap) + if PT_SSTB_income == 0 and pre_qbid_taxinc >= upper_thd: + # apply full cap + qbided = min(full_cap, qbid_before_limits) + elif PT_SSTB_income == 0 and pre_qbid_taxinc < upper_thd: + # apply adjusted cap as in Part III of Worksheet 12-A + # in 2018 IRS Publication 535 (Chapter 12) + prt = (pre_qbid_taxinc - lower_thd) / pre_qbid_taxinc_gap + adj = prt * (qbid_before_limits - full_cap) + qbided = qbid_before_limits - adj + else: # PT_SSTB_income == 1 and pre_qbid_taxinc < upper_thd + prti = (upper_thd - pre_qbid_taxinc) / pre_qbid_taxinc_gap + qbid_adjusted = prti * qbid_before_limits + cap_adjusted = prti * full_cap + prt = (pre_qbid_taxinc - lower_thd) / pre_qbid_taxinc_gap + adj = prt * (qbid_adjusted - cap_adjusted) + qbided = qbid_adjusted - adj + # if PT_qbid_limit_switch is False, assume all taxpayers + # have sufficient wage expenses and capital income to avoid + # QBID limitations. + else: + qbided = qbid_before_limits + # apply taxinc cap (assuning cap rate is equal to PT_qbid_rt) + net_cg = e00650 + c01000 # per line 34 in 2018 Pub 535 Worksheet 12-A + taxinc_cap = PT_qbid_rt * max(0.0, pre_qbid_taxinc - net_cg) + qbided = min(qbided, taxinc_cap) + + # apply qbid phaseout + if qbided > 0.0 and pre_qbid_taxinc > PT_qbid_ps[MARS - 1]: + excess = pre_qbid_taxinc - PT_qbid_ps[MARS - 1] + qbided = max(0.0, qbided - PT_qbid_prt * excess) + + # calculate taxable income after qualified business income deduction + c04800 = max(0.0, pre_qbid_taxinc - qbided) + return (c04800, qbided) + + +@JIT(nopython=True) +def SchXYZ( + taxable_income, + MARS, + e00900, + e26270, + e02000, + e00200, + PT_rt1, + PT_rt2, + PT_rt3, + PT_rt4, + PT_rt5, + PT_rt6, + PT_rt7, + PT_rt8, + PT_brk1, + PT_brk2, + PT_brk3, + PT_brk4, + PT_brk5, + PT_brk6, + PT_brk7, + II_rt1, + II_rt2, + II_rt3, + II_rt4, + II_rt5, + II_rt6, + II_rt7, + II_rt8, + II_brk1, + II_brk2, + II_brk3, + II_brk4, + II_brk5, + II_brk6, + II_brk7, + PT_EligibleRate_active, + PT_EligibleRate_passive, + PT_wages_active_income, + PT_top_stacking, +): + """ + Returns Schedule X, Y, Z tax amount for specified taxable_income. + + Parameters + ---------- + taxable_income: float + Taxable income + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + e00900: float + Schedule C business net profit/loss for filing unit + e26270: float + Schedule E: combined partnership and S-corporation net income/loss + e02000: float + Schedule E total rental, royalty, parternship, S-corporation, etc, income/loss + e00200: float + Wages, salaries, and tips for filing unit net of pension contributions + PT_rt1: float + Pass through income tax rate 1 + PT_rt2: float + Pass through income tax rate 2 + PT_rt3: float + Pass through income tax rate 3 + PT_rt4: float + Pass through income tax rate 4 + PT_rt5: float + Pass through income tax rate 5 + PT_rt6: float + Pass through income tax rate 6 + PT_rt7: float + Pass through income tax rate 7 + PT_rt8: float + Pass through income tax rate 8 + PT_brk1: list + Pass through income tax bracket (upper threshold) 1 + PT_brk2: list + Pass through income tax bracket (upper threshold) 2 + PT_brk3: list + Pass through income tax bracket (upper threshold) 3 + PT_brk4: list + Pass through income tax bracket (upper threshold) 4 + PT_brk5: list + Pass through income tax bracket (upper threshold) 5 + PT_brk6: list + Pass through income tax bracket (upper threshold) 6 + PT_brk7: list + Pass through income tax bracket (upper threshold) 7 + II_rt1: float + Personal income (regular/non-AMT/non-pass-through) tax rate 1 + II_rt2: float + Personal income (regular/non-AMT/non-pass-through) tax rate 2 + II_rt3: float + Personal income (regular/non-AMT/non-pass-through) tax rate 3 + II_rt4: float + Personal income (regular/non-AMT/non-pass-through) tax rate 4 + II_rt5: float + Personal income (regular/non-AMT/non-pass-through) tax rate 5 + II_rt6: float + Personal income (regular/non-AMT/non-pass-through) tax rate 6 + II_rt7: float + Personal income (regular/non-AMT/non-pass-through) tax rate 7 + II_rt8: float + Personal income (regular/non-AMT/non-pass-through) tax rate 8 + II_brk1: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 1 + II_brk2: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 2 + II_brk3: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 3 + II_brk4: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 4 + II_brk5: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 5 + II_brk6: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 6 + II_brk7: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 7 + PT_EligibleRate_active: float + Share of active business income eligible for PT rate schedule + PT_EligibleRate_passive: float + Share of passive business income eligible for PT rate schedule + PT_wages_active_income: bool + Wages included in (positive) active business eligible for PT rates + PT_top_stacking: bool + PT taxable income stacked on top of regular taxable income + + Returns + ------- + reg_tax: float + Individual income tax liability on non-pass-through income + pt_tax: float + Individual income tax liability from pass-through income + """ + # separate non-negative taxable income into two non-negative components, + # doing this in a way so that the components add up to taxable income + # define pass-through income eligible for PT schedule + pt_passive = PT_EligibleRate_passive * (e02000 - e26270) + pt_active_gross = e00900 + e26270 + if (pt_active_gross > 0) and PT_wages_active_income: + pt_active_gross = pt_active_gross + e00200 + pt_active = PT_EligibleRate_active * pt_active_gross + pt_active = min(pt_active, e00900 + e26270) + pt_taxinc = max(0.0, pt_passive + pt_active) + if pt_taxinc >= taxable_income: + pt_taxinc = taxable_income + reg_taxinc = 0.0 + else: + # pt_taxinc is unchanged + reg_taxinc = taxable_income - pt_taxinc + # determine stacking order + if PT_top_stacking: + reg_tbase = 0.0 + pt_tbase = reg_taxinc + else: + reg_tbase = pt_taxinc + pt_tbase = 0.0 + # compute Schedule X,Y,Z tax using the two components of taxable income + if reg_taxinc > 0.0: + reg_tax = Taxes( + reg_taxinc, + MARS, + reg_tbase, + II_rt1, + II_rt2, + II_rt3, + II_rt4, + II_rt5, + II_rt6, + II_rt7, + II_rt8, + II_brk1, + II_brk2, + II_brk3, + II_brk4, + II_brk5, + II_brk6, + II_brk7, + ) + else: + reg_tax = 0.0 + if pt_taxinc > 0.0: + pt_tax = Taxes( + pt_taxinc, + MARS, + pt_tbase, + PT_rt1, + PT_rt2, + PT_rt3, + PT_rt4, + PT_rt5, + PT_rt6, + PT_rt7, + PT_rt8, + PT_brk1, + PT_brk2, + PT_brk3, + PT_brk4, + PT_brk5, + PT_brk6, + PT_brk7, + ) + else: + pt_tax = 0.0 + return reg_tax + pt_tax + + +def SchXYZTax( + c04800, + MARS, + e00900, + e26270, + e02000, + e00200, + PT_rt1, + PT_rt2, + PT_rt3, + PT_rt4, + PT_rt5, + PT_rt6, + PT_rt7, + PT_rt8, + PT_brk1, + PT_brk2, + PT_brk3, + PT_brk4, + PT_brk5, + PT_brk6, + PT_brk7, + II_rt1, + II_rt2, + II_rt3, + II_rt4, + II_rt5, + II_rt6, + II_rt7, + II_rt8, + II_brk1, + II_brk2, + II_brk3, + II_brk4, + II_brk5, + II_brk6, + II_brk7, + PT_EligibleRate_active, + PT_EligibleRate_passive, + PT_wages_active_income, + PT_top_stacking, + c05200, +): + """ + SchXYZTax calls SchXYZ function and sets c05200 to returned amount. + + Parameters + ---------- + c04800: float + Regular taxable income + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + e00900: float + Schedule C business net profit/loss for filing unit + e26270: float + Schedule E: combined partnership and S-corporation net income/loss + e02000: float + Farm net income/loss for filing unit from Schedule F + e00200: float + Farm net income/loss for filing unit from Schedule F + PT_rt1: float + Pass through income tax rate 1 + PT_rt2: float + Pass through income tax rate 2 + PT_rt3: float + Pass through income tax rate 3 + PT_rt4: float + Pass through income tax rate 4 + PT_rt5: float + Pass through income tax rate 5 + PT_rt6: float + Pass through income tax rate 6 + PT_rt7: float + Pass through income tax rate 7 + PT_rt8: float + Pass through income tax rate 8 + PT_brk1: list + Pass through income tax bracket (upper threshold) 1 + PT_brk2: list + Pass through income tax bracket (upper threshold) 2 + PT_brk3: list + Pass through income tax bracket (upper threshold) 3 + PT_brk4: list + Pass through income tax bracket (upper threshold) 4 + PT_brk5: list + Pass through income tax bracket (upper threshold) 5 + PT_brk6: list + Pass through income tax bracket (upper threshold) 6 + PT_brk7: list + Pass through income tax bracket (upper threshold) 7 + II_rt1: float + Personal income (regular/non-AMT/non-pass-through) tax rate 1 + II_rt2: float + Personal income (regular/non-AMT/non-pass-through) tax rate 2 + II_rt3: float + Personal income (regular/non-AMT/non-pass-through) tax rate 3 + II_rt4: float + Personal income (regular/non-AMT/non-pass-through) tax rate 4 + II_rt5: float + Personal income (regular/non-AMT/non-pass-through) tax rate 5 + II_rt6: float + Personal income (regular/non-AMT/non-pass-through) tax rate 6 + II_rt7: float + Personal income (regular/non-AMT/non-pass-through) tax rate 7 + II_rt8: float + Personal income (regular/non-AMT/non-pass-through) tax rate 8 + II_brk1: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 1 + II_brk2: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 2 + II_brk3: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 3 + II_brk4: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 4 + II_brk5: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 5 + II_brk6: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 6 + II_brk7: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 7 + PT_EligibleRate_active: float + Share of active business income eligible for PT rate schedule + PT_EligibleRate_passive: float + Share of passive business income eligible for PT rate schedule + PT_wages_active_income: bool + Wages included in (positive) active business eligible for PT rates + PT_top_stacking: bool + PT taxable income stacked on top of regular taxable income + c05200: float + Tax amount from Schedule X,Y,Z tables + + Returns + ------- + c05200: float + Tax aount from Schedule X, Y, Z tables + """ + c05200 = SchXYZ( + c04800, + MARS, + e00900, + e26270, + e02000, + e00200, + PT_rt1, + PT_rt2, + PT_rt3, + PT_rt4, + PT_rt5, + PT_rt6, + PT_rt7, + PT_rt8, + PT_brk1, + PT_brk2, + PT_brk3, + PT_brk4, + PT_brk5, + PT_brk6, + PT_brk7, + II_rt1, + II_rt2, + II_rt3, + II_rt4, + II_rt5, + II_rt6, + II_rt7, + II_rt8, + II_brk1, + II_brk2, + II_brk3, + II_brk4, + II_brk5, + II_brk6, + II_brk7, + PT_EligibleRate_active, + PT_EligibleRate_passive, + PT_wages_active_income, + PT_top_stacking, + ) + return c05200 + + +def GainsTax( + e00650, + c01000, + c23650, + p23250, + e01100, + e58990, + e00200, + e24515, + e24518, + MARS, + c04800, + c05200, + e00900, + e26270, + e02000, + II_rt1, + II_rt2, + II_rt3, + II_rt4, + II_rt5, + II_rt6, + II_rt7, + II_rt8, + II_brk1, + II_brk2, + II_brk3, + II_brk4, + II_brk5, + II_brk6, + II_brk7, + PT_rt1, + PT_rt2, + PT_rt3, + PT_rt4, + PT_rt5, + PT_rt6, + PT_rt7, + PT_rt8, + PT_brk1, + PT_brk2, + PT_brk3, + PT_brk4, + PT_brk5, + PT_brk6, + PT_brk7, + CG_nodiff, + PT_EligibleRate_active, + PT_EligibleRate_passive, + PT_wages_active_income, + PT_top_stacking, + CG_rt1, + CG_rt2, + CG_rt3, + CG_rt4, + CG_brk1, + CG_brk2, + CG_brk3, + dwks10, + dwks13, + dwks14, + dwks19, + c05700, + taxbc, +): + """ + GainsTax function implements (2015) Schedule D Tax Worksheet logic for + the special taxation of long-term capital gains and qualified dividends + if CG_nodiff is false. + + Parameters + ---------- + e00650: float + Qualified dividends included in ordinary dividends + c01000: float + Limitation on capital losses + c23650: float + Net capital gain (long term + short term) before exclusion + p23250: float + Schedule D: net long-term capital gains/losses + e01100: float + Capital gains distributions not reported on Schedule D + e58990: float + Investment income elected amount from Form 4952 + e00200: float + Wages, salaries, and tips for filing unit net of pension contributions + e24515: float + Schedule D: un-recaptured section 1250 Gain + e24518: float + Schedule D: 28% rate gain or loss + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + c04800: float + Regular taxable income + c05200: float + Tax amount from Schedule X,Y,Z tables + e00900: float + Schedule C business net profit/loss for filing unit + e26270: float + Schedule E: combined partnership and S-corporation net income/loss + e02000: float + Schedule E total rental, royalty, partnership, S-corporation, etc, income/loss + II_rt1: float + Personal income (regular/non-AMT/non-pass-through) tax rate 1 + II_rt2: float + Personal income (regular/non-AMT/non-pass-through) tax rate 2 + II_rt3: float + Personal income (regular/non-AMT/non-pass-through) tax rate 3 + II_rt4: float + Personal income (regular/non-AMT/non-pass-through) tax rate 4 + II_rt5: float + Personal income (regular/non-AMT/non-pass-through) tax rate 5 + II_rt6: float + Personal income (regular/non-AMT/non-pass-through) tax rate 6 + II_rt7: float + Personal income (regular/non-AMT/non-pass-through) tax rate 7 + II_rt8: float + Personal income (regular/non-AMT/non-pass-through) tax rate 8 + II_brk1: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 1 + II_brk2: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 2 + II_brk3: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 3 + II_brk4: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 4 + II_brk5: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 5 + II_brk6: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 6 + II_brk7: list + Personal income (regular/non-AMT/non-pass/through) tax bracket (upper threshold) 7 + PT_rt1: float + Pass through income tax rate 1 + PT_rt2: float + Pass through income tax rate 2 + PT_rt3: float + Pass through income tax rate 3 + PT_rt4: float + Pass through income tax rate 4 + PT_rt5: float + Pass through income tax rate 5 + PT_rt6: float + Pass through income tax rate 6 + PT_rt7: float + Pass through income tax rate 7 + PT_rt8: float + Pass through income tax rate 8 + PT_brk1: list + Pass through income tax bracket (upper threshold) 1 + PT_brk2: list + Pass through income tax bracket (upper threshold) 2 + PT_brk3: list + Pass through income tax bracket (upper threshold) 3 + PT_brk4: list + Pass through income tax bracket (upper threshold) 4 + PT_brk5: list + Pass through income tax bracket (upper threshold) 5 + PT_brk6: list + Pass through income tax bracket (upper threshold) 6 + PT_brk7: list + Pass through income tax bracket (upper threshold) 7 + CG_nodiff: bool + Long term capital gains and qualified dividends taxed no differently than regular taxable income + PT_EligibleRate_active: float + Share of active business income eligible for PT rate schedule + PT_EligibleRate_passive: float + Share of passive business income eligible for PT rate schedule + PT_wages_active_income: bool + Wages included in (positive) active business eligible for PT rates + PT_top_stacking: bool + PT taxable income stacked on top of regular taxable income + CG_rt1: float + Long term capital gain and qualified dividends (regular/non-AMT) rate 1 + CG_rt2: float + Long term capital gain and qualified dividends (regular/non-AMT) rate 2 + CG_rt3: float + Long term capital gain and qualified dividends (regular/non-AMT) rate 3 + CG_rt4: float + Long term capital gain and qualified dividends (regular/non-AMT) rate 4 + CG_brk1: list + Top of long-term capital gains and qualified dividends (regular/non-AMT) tax bracket 1 + CG_brk2: list + Top of long-term capital gains and qualified dividends (regular/non-AMT) tax bracket 2 + CG_brk3: list + Top of long-term capital gains and qualified dividends (regular/non-AMT) tax bracket 3 + dwks10: float + Sum of dwks6 + dwks9 + dwks13: float + Difference of dwks10 - dwks12 + dwks14: float + Maximum of 0 and dwks1 - dwks13 + dwks19: float + Maximum of dwks17 and dwks16 + c05700: float + Lump sum distributions + taxbc: float + Regular tax on regular taxable income before credits + + Returns + ------- + dwks10: float + Sum of dwks6 + dwks9 + dwks13: float + Difference of dwks10 - dwks12 + dwks14: float + Maximum of 0 and dwks1 - dwks13 + dwks19: float + Maximum of dwks17 and dwks16 + c05700: float + Lump sum distributions + taxbc: float + Regular tax on regular taxable income before credits + """ + # pylint: disable=too-many-statements + if ( + c01000 > 0.0 + or c23650 > 0.0 + or p23250 > 0.0 + or e01100 > 0.0 + or e00650 > 0.0 + ): + hasqdivltcg = 1 # has qualified dividends or long-term capital gains + else: + hasqdivltcg = 0 # no qualified dividends or long-term capital gains + + if CG_nodiff: + hasqdivltcg = 0 # no special taxation of qual divids and l-t cap gains + + if hasqdivltcg == 1: + + dwks1 = c04800 + dwks2 = e00650 + dwks3 = e58990 + dwks4 = 0.0 # always assumed to be zero + dwks5 = max(0.0, dwks3 - dwks4) + dwks6 = max(0.0, dwks2 - dwks5) + dwks7 = min(p23250, c23650) # SchD lines 15 and 16, respectively + # dwks8 = min(dwks3, dwks4) + # dwks9 = max(0., dwks7 - dwks8) + # BELOW TWO STATEMENTS ARE UNCLEAR IN LIGHT OF dwks9=... COMMENT + if e01100 > 0.0: + c24510 = e01100 + else: + c24510 = max(0.0, dwks7) + e01100 + dwks9 = max(0.0, c24510 - min(0.0, e58990)) + # ABOVE TWO STATEMENTS ARE UNCLEAR IN LIGHT OF dwks9=... COMMENT + dwks10 = dwks6 + dwks9 + dwks11 = e24515 + e24518 # SchD lines 18 and 19, respectively + dwks12 = min(dwks9, dwks11) + dwks13 = dwks10 - dwks12 + dwks14 = max(0.0, dwks1 - dwks13) + dwks16 = min(CG_brk1[MARS - 1], dwks1) + dwks17 = min(dwks14, dwks16) + dwks18 = max(0.0, dwks1 - dwks10) + dwks19 = max(dwks17, dwks18) + dwks20 = dwks16 - dwks17 + lowest_rate_tax = CG_rt1 * dwks20 + # break in worksheet lines + dwks21 = min(dwks1, dwks13) + dwks22 = dwks20 + dwks23 = max(0.0, dwks21 - dwks22) + dwks25 = min(CG_brk2[MARS - 1], dwks1) + dwks26 = dwks19 + dwks20 + dwks27 = max(0.0, dwks25 - dwks26) + dwks28 = min(dwks23, dwks27) + dwks29 = CG_rt2 * dwks28 + dwks30 = dwks22 + dwks28 + dwks31 = dwks21 - dwks30 + dwks32 = CG_rt3 * dwks31 + # compute total taxable CG for additional top bracket + cg_all = dwks20 + dwks28 + dwks31 + hi_base = max(0.0, cg_all - CG_brk3[MARS - 1]) + hi_incremental_rate = CG_rt4 - CG_rt3 + highest_rate_incremental_tax = hi_incremental_rate * hi_base + # break in worksheet lines + dwks33 = min(dwks9, e24515) + dwks34 = dwks10 + dwks19 + dwks36 = max(0.0, dwks34 - dwks1) + dwks37 = max(0.0, dwks33 - dwks36) + dwks38 = 0.25 * dwks37 + # break in worksheet lines + dwks39 = dwks19 + dwks20 + dwks28 + dwks31 + dwks37 + dwks40 = dwks1 - dwks39 + dwks41 = 0.28 * dwks40 + dwks42 = SchXYZ( + dwks19, + MARS, + e00900, + e26270, + e02000, + e00200, + PT_rt1, + PT_rt2, + PT_rt3, + PT_rt4, + PT_rt5, + PT_rt6, + PT_rt7, + PT_rt8, + PT_brk1, + PT_brk2, + PT_brk3, + PT_brk4, + PT_brk5, + PT_brk6, + PT_brk7, + II_rt1, + II_rt2, + II_rt3, + II_rt4, + II_rt5, + II_rt6, + II_rt7, + II_rt8, + II_brk1, + II_brk2, + II_brk3, + II_brk4, + II_brk5, + II_brk6, + II_brk7, + PT_EligibleRate_active, + PT_EligibleRate_passive, + PT_wages_active_income, + PT_top_stacking, + ) + dwks43 = ( + dwks29 + + dwks32 + + dwks38 + + dwks41 + + dwks42 + + lowest_rate_tax + + highest_rate_incremental_tax + ) + dwks44 = c05200 + dwks45 = min(dwks43, dwks44) + c24580 = dwks45 + + else: # if hasqdivltcg is zero + + c24580 = c05200 + dwks10 = max(0.0, min(p23250, c23650)) + e01100 + dwks13 = 0.0 + dwks14 = 0.0 + dwks19 = 0.0 + + # final calculations done no matter what the value of hasqdivltcg + c05100 = c24580 # because foreign earned income exclusion is assumed zero + c05700 = 0.0 # no Form 4972, Lump Sum Distributions + taxbc = c05700 + c05100 + return (dwks10, dwks13, dwks14, dwks19, c05700, taxbc) + + +def AGIsurtax(c00100, MARS, AGI_surtax_trt, AGI_surtax_thd, taxbc, surtax): + """ + Computes surtax on AGI above some threshold. + + Parameters + ---------- + c00100: float + Adjusted Gross Income (AGI) + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + AGI_surtax_trt: float + New AGI surtax rate + AGI_surtax_thd: list + Threshold for the new AGI surtax + taxbc: float + Regular tax on regular taxable income before credits + surtax: float + Surtax on AGI above some threshold + + Returns + ------- + taxbc: float + Regular tax on regular taxable income before credits + surtax: float + Surtax on AGI above some threshold + """ + if AGI_surtax_trt > 0.0: + hiAGItax = AGI_surtax_trt * max(c00100 - AGI_surtax_thd[MARS - 1], 0.0) + taxbc += hiAGItax + surtax += hiAGItax + return (taxbc, surtax) + + +def AMT( + e07300, + dwks13, + standard, + f6251, + c00100, + c18300, + taxbc, + c04470, + c17000, + c20800, + c21040, + e24515, + MARS, + sep, + dwks19, + dwks14, + c05700, + e62900, + e00700, + dwks10, + age_head, + age_spouse, + earned, + cmbtp, + AMT_child_em_c_age, + AMT_brk1, + AMT_em, + AMT_prt, + AMT_rt1, + AMT_rt2, + AMT_child_em, + AMT_em_ps, + AMT_em_pe, + AMT_CG_brk1, + AMT_CG_brk2, + AMT_CG_brk3, + AMT_CG_rt1, + AMT_CG_rt2, + AMT_CG_rt3, + AMT_CG_rt4, + c05800, + c09600, + c62100, +): + """ + Computes Alternative Minimum Tax (AMT) taxable income and liability, where + c62100 is AMT taxable income, + c09600 is AMT tax liability, and + c05800 is total (regular + AMT) income tax liability before credits. + + Note that line-number variable names refer to 2015 Form 6251. + + Parameters + ----------- + e07300: float + Foreign tax credit from Form 1116 + dwks13: float + Difference of dwks10 - dwks12 + standard: float + Standard deduction (zero for itemizers) + f6251: int + 1 if Form 6251 (AMT) attached to return, otherwise 0 + c00100: float + Adjusted Gross Income (AGI) + c18300: float + Schedule A: state and local taxes plus real estate taxes deducted + taxbc: float + Regular tax on regular taxable income before credits + c04470: float + Itemized deductions after phase-out (zero for non itemizers) + c17000: float + Schedule A: Medical expenses deducted + c20800: float + Schedule A: net limited miscellaneous deductions deducted + c21040: float + Itemized deductiosn that are phased out + e24515: float + Schedule D: Un-Recaptured Section 1250 Gain + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + sep: int + 2 when MARS is 3 (married filing separately), otherwise 1 + dwks19: float + Maximum of 0 and dwks1 - dwks13 + dwks14: float + Maximum of 0 and dwks1 - dwks13 + c05700: float + Lump sum distributions + e62900: float + Alternative Minimum Tax foreign tax credit from Form 6251 + e00700: float + Schedule C business net profit/loss for filing unit + dwks10: float + Sum of dwks6 + dwks9 + age_head: int + Age in years of taxpayer (i.e. primary adult) + age_spouse: int + Age in years of spouse (i.e. secondary adult if present) + earned: float + Earned income for filing unit + cmbtp: float + Estimate of income on (AMT) Form 6251 but not in AGI + AMT_child_em_c_age: float + Age ceiling for special AMT exemption + AMT_brk1: float + AMT bracket 1 (upper threshold) + AMT_em: list + AMT exemption amount + AMT_prt: float + AMT exemption phaseout rate + AMT_rt1: float + AMT rate 1 + AMT_rt2: float + Additional AMT rate for AMT taxable income about AMT bracket 1 + AMT_child_em: float + Child AMT exemption additional income base + AMT_em_ps: list + AMT exemption phaseout start + AMT_em_pe: float + AMT exemption phaseout ending AMT taxable income for Married Filing Separately + AMT_CG_brk1: list + Top of long-term capital gains and qualified dividends (AMT) tax bracket 1 + AMT_CG_brk2: list + Top of long-term capital gains and qualified dividends (AMT) tax bracket 2 + AMT_CG_brk3: list + Top of long-term capital gains and qualified dividends (AMT) tax bracket 3 + AMT_CG_rt1: float + Long term capital gain and qualified dividends (AMT) rate 1 + AMT_CG_rt2: float + Long term capital gain and qualified dividends (AMT) rate 2 + AMT_CG_rt3: float + Long term capital gain and qualified dividends (AMT) rate 3 + AMT_CG_rt4: float + Long term capital gain and qualified dividends (AMT) rate 4 + c05800: float + Total (regular + AMT) income tax liability before credits + c09600: float + Alternative Minimum Tax (AMT) liability + c62100: float + Alternative Minimum Tax (AMT) + + Returns + ------- + c62100: float + Alternative Minimum Tax (AMT) + c09600: float + Alternative Minimum Tax (AMT) liability + c05800: float + Total (regular + AMT) income tax liability before credits + """ + # pylint: disable=too-many-statements,too-many-branches + # Form 6251, Part I + if standard == 0.0: + c62100 = ( + c00100 + - e00700 + - c04470 + + max(0.0, min(c17000, 0.025 * c00100)) + + c18300 + + c20800 + - c21040 + ) + if standard > 0.0: + c62100 = c00100 - e00700 + c62100 += cmbtp # add income not in AGI but considered income for AMT + if MARS == 3: + amtsepadd = max( + 0.0, min(AMT_em[MARS - 1], AMT_prt * (c62100 - AMT_em_pe)) + ) + else: + amtsepadd = 0.0 + c62100 = c62100 + amtsepadd # AMT taxable income, which is line28 + # Form 6251, Part II top + line29 = max( + 0.0, + AMT_em[MARS - 1] - AMT_prt * max(0.0, c62100 - AMT_em_ps[MARS - 1]), + ) + young_head = age_head != 0 and age_head < AMT_child_em_c_age + no_or_young_spouse = age_spouse < AMT_child_em_c_age + if young_head and no_or_young_spouse: + line29 = min(line29, earned + AMT_child_em) + line30 = max(0.0, c62100 - line29) + line3163 = AMT_rt1 * line30 + AMT_rt2 * max( + 0.0, (line30 - (AMT_brk1 / sep)) + ) + if ( + dwks10 > 0.0 + or dwks13 > 0.0 + or dwks14 > 0.0 + or dwks19 > 0.0 + or e24515 > 0.0 + ): + # complete Form 6251, Part III (line36 is equal to line30) + line37 = dwks13 + line38 = e24515 + line39 = min(line37 + line38, dwks10) + line40 = min(line30, line39) + line41 = max(0.0, line30 - line40) + line42 = AMT_rt1 * line41 + AMT_rt2 * max( + 0.0, (line41 - (AMT_brk1 / sep)) + ) + line44 = dwks14 + line45 = max(0.0, AMT_CG_brk1[MARS - 1] - line44) + line46 = min(line30, line37) + line47 = min(line45, line46) # line47 is amount taxed at AMT_CG_rt1 + cgtax1 = line47 * AMT_CG_rt1 + line48 = line46 - line47 + line51 = dwks19 + line52 = line45 + line51 + line53 = max(0.0, AMT_CG_brk2[MARS - 1] - line52) + line54 = min(line48, line53) # line54 is amount taxed at AMT_CG_rt2 + cgtax2 = line54 * AMT_CG_rt2 + line56 = line47 + line54 # total amount in lower two brackets + if line41 == line56: + line57 = 0.0 # line57 is amount taxed at AMT_CG_rt3 + linex2 = 0.0 # linex2 is amount taxed at AMT_CG_rt4 + else: + line57 = line46 - line56 + linex1 = min( + line48, max(0.0, AMT_CG_brk3[MARS - 1] - line44 - line45) + ) + linex2 = max(0.0, line54 - linex1) + cgtax3 = line57 * AMT_CG_rt3 + cgtax4 = linex2 * AMT_CG_rt4 + if line38 == 0.0: + line61 = 0.0 + else: + line61 = 0.25 * max( + 0.0, line30 - line41 - line56 - line57 - linex2 + ) + line62 = line42 + cgtax1 + cgtax2 + cgtax3 + cgtax4 + line61 + line64 = min(line3163, line62) + line31 = line64 + else: # if not completing Form 6251, Part III + line31 = line3163 + # Form 6251, Part II bottom + if f6251 == 1: + line32 = e62900 + else: + line32 = e07300 + line33 = line31 - line32 + c09600 = max(0.0, line33 - max(0.0, taxbc - e07300 - c05700)) + c05800 = taxbc + c09600 + return (c62100, c09600, c05800) + + +def NetInvIncTax( + e00300, + e00600, + e02000, + e26270, + c01000, + c00100, + NIIT_thd, + MARS, + NIIT_PT_taxed, + NIIT_rt, + niit, +): + """ + Computes Net Investment Income Tax (NIIT) amount assuming that + all annuity income is excluded from net investment income. + + Parameters + ---------- + e00300: float + Tax-exempt interest income + e00600: float + Ordinary dividends included in AGI + e02000: float + Schedule E total rental, royalty, parternship, S-corporation, etc, income/loss + e26270: float + Schedule E: combined partnership and S-corporation net income/loss + c01000: float + Limitation on capital losses + c00100: float + Adjusted Gross Income (AGI) + NIIT_thd: list + Net Investment Income Tax modified AGI threshold + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + NIIT_PT_taxed: bool + Whether or not partnership and S-corp income is NIIT based + NIIT_rt: float + Net Investment Income Tax rate + niit: float + Net investment income tax from Form 8960 + + Returns + ------- + niit: float + Net investment income tax from Form 8960 + """ + modAGI = c00100 # no foreign earned income exclusion to add + if not NIIT_PT_taxed: + NII = max(0.0, e00300 + e00600 + c01000 + e02000 - e26270) + else: # do not subtract e26270 from e02000 + NII = max(0.0, e00300 + e00600 + c01000 + e02000) + niit = NIIT_rt * min(NII, max(0.0, modAGI - NIIT_thd[MARS - 1])) + return niit + + +def F2441( + MARS, + earned_p, + earned_s, + f2441, + CDCC_c, + e32800, + exact, + c00100, + CDCC_ps, + CDCC_ps2, + CDCC_crt, + CDCC_frt, + CDCC_prt, + CDCC_refundable, + c05800, + e07300, + c07180, + CDCC_refund, +): + """ + Calculates Form 2441 child and dependent care expense credit, c07180. + + Parameters + ---------- + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + earned_p: float + Earned income for taxpayer + earned_s: float + Earned income for spouse + f2441: int + Number of child/dependent care qualifying persons + CDCC_c: float + Maximum child/dependent care credit per dependent + e32800: float + Child/dependent care expenses for qualifying persons from Form 2441 + exact: int + Whether or not to do rounding of phaseout fraction + c00100: float + Adjusted Gross Income (AGI) + CDCC_ps: float + Child/dependent care credit phaseout start + CDCC_crt: float + Child/dependent care credit phaseout percentage rate ceiling + c05800: float + Total (regular + AMT) income tax liability before credits + e07300: float + Foreign tax credit from Form 1116 + c07180: float + Credit for child and dependent care expenses from Form 2441 + + Returns + ------- + c07180: float + Credit for child and dependent care expenses from Form 2441 + """ + # credit for at most two cared-for individuals and for actual expenses + max_credit = min(f2441, 2) * CDCC_c + c32800 = max(0.0, min(e32800, max_credit)) + # credit is limited to minimum of individuals' earned income + c32880 = earned_p # earned income of taxpayer + if MARS == 2: + c32890 = earned_s # earned income of spouse when present + else: + c32890 = earned_p + c33000 = max(0.0, min(c32800, min(c32880, c32890))) + # credit is limited by AGI-related fraction + if exact == 1: # exact calculation as on tax forms + # first phase-down from 35 to 20 percent + tratio1 = math.ceil(max(((c00100 - CDCC_ps) * CDCC_prt), 0.0)) + crate = max(CDCC_frt, CDCC_crt - min(CDCC_crt - CDCC_frt, tratio1)) + # second phase-down from 20 percent to zero + if c00100 > CDCC_ps2: + tratio2 = math.ceil(max(((c00100 - CDCC_ps2) * CDCC_prt), 0.0)) + crate = max(0.0, CDCC_frt - min(CDCC_frt, tratio2)) + else: + crate = max( + CDCC_frt, CDCC_crt - max(((c00100 - CDCC_ps) * CDCC_prt), 0.0) + ) + if c00100 > CDCC_ps2: + crate = max( + 0.0, CDCC_frt - max(((c00100 - CDCC_ps2) * CDCC_prt), 0.0) + ) + c33200 = c33000 * 0.01 * crate + # credit is limited by tax liability if not refundable + if CDCC_refundable: + c07180 = 0.0 + CDCC_refund = c33200 + else: + c07180 = min(max(0.0, c05800 - e07300), c33200) + CDCC_refund = 0.0 + return (c07180, CDCC_refund) + + +@JIT(nopython=True) +def EITCamount( + basic_frac, + phasein_rate, + earnings, + max_amount, + phaseout_start, + agi, + phaseout_rate, +): + """ + Returns EITC amount given specified parameters. + English parameter names are used in this function because the + EITC formula is not available on IRS forms or in IRS instructions; + the extensive IRS EITC look-up table does not reveal the formula. + + Parameters + ---------- + basic_frac: list + Fraction of maximum earned income credit paid at zero earnings + phasein_rate: list + Earned income credit phasein rate + earnings: float + Earned income for filing unit + max_amount: list + Maximum earned income credit + phaseout_start: list + Earned income credit phaseout start AGI + agi: float + Adjusted Gross Income (AGI) + phaseout_rate: list + Earned income credit phaseout rate + + Returns + ------- + eitc: float + Earned Income Credit + """ + eitc = min( + ( + basic_frac * max_amount + + (1.0 - basic_frac) * phasein_rate * earnings + ), + max_amount, + ) + if earnings > phaseout_start or agi > phaseout_start: + eitcx = max( + 0.0, + ( + max_amount + - phaseout_rate * max(0.0, max(earnings, agi) - phaseout_start) + ), + ) + eitc = min(eitc, eitcx) + return eitc + + +def EITC( + MARS, + DSI, + EIC, + c00100, + e00300, + e00400, + e00600, + c01000, + e02000, + e26270, + age_head, + age_spouse, + earned, + earned_p, + earned_s, + EITC_ps, + EITC_MinEligAge, + EITC_MaxEligAge, + EITC_ps_MarriedJ, + EITC_rt, + EITC_c, + EITC_prt, + EITC_basic_frac, + EITC_InvestIncome_c, + EITC_excess_InvestIncome_rt, + EITC_indiv, + EITC_sep_filers_elig, + c59660, +): + """ + Computes EITC amount, c59660. + + Parameters + ---------- + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + DSI: int + 1 if claimed as dependent on another return, otherwise 0 + EIC: int + Number of EIC qualifying children + c00100: float + Adjusted Gross Income (AGI) + e00300: float + Taxable interest income + e00400: float + Tax exempt interest income + e00600: float + Ordinary dividends included in AGI + c01000: float + Limitation on capital losses + e02000: float + Schedule E total rental, royalty, partnership, S-corporation, etc, income/loss + e26270: float + Schedule E combined partnership and S-corporation net income/loss + age_head: int + Age in years of taxpayer (primary adult) + age_spouse: int + Age in years of spouse (secondary adult, if present) + earned: float + Earned income for filing unit + earned_p: float + Earned income for taxpayer + earned_s: float + Earned income for spouse + EITC_ps: list + Earned income credit phaseout start AGI + EITC_MinEligAge: int + Minimum age for childless EITC eligibility + EITC_MaxEligAge: int + Maximum age for childless EITC eligibility + EITC_ps_MarriedJ: list + Extra earned income credit phaseout start AGI for married filling jointly + EITC_rt: list + Earned income credit phasein rate + EITC_c: list + Maximum earned income credit + EITC_prt: list + Earned income credit phaseout rate + EITC_basic_frac: float + Fraction of maximum earned income credit paid at zero earnings + EITC_InvestIncome_c: float + Maximum investment income before EITC reduction + EITC_excess_InvestIncome_rt: float + Rate of EITC reduction when investemtn income exceeds ceiling + EITC_indiv: bool + EITC is computed for each spouse based in individual earnings + EITC_sep_filers_elig: bool + Separate filers are eligible for the EITC + c59660: float + EITC amount + + Returns + ------- + c59660: float + EITC amount + """ + # pylint: disable=too-many-branches + if MARS != 2: + eitc = EITCamount( + EITC_basic_frac, + EITC_rt[EIC], + earned, + EITC_c[EIC], + EITC_ps[EIC], + c00100, + EITC_prt[EIC], + ) + if EIC == 0: + # enforce age eligibility rule for those with no EITC-eligible + # kids assuming that an unknown age_* value implies EITC age + # eligibility + h_age_elig = EITC_MinEligAge <= age_head <= EITC_MaxEligAge + if age_head == 0 or h_age_elig: + c59660 = eitc + else: + c59660 = 0.0 + else: # if EIC != 0 + c59660 = eitc + + if MARS == 2: + po_start = EITC_ps[EIC] + EITC_ps_MarriedJ[EIC] + if not EITC_indiv: + # filing unit EITC rather than individual EITC + eitc = EITCamount( + EITC_basic_frac, + EITC_rt[EIC], + earned, + EITC_c[EIC], + po_start, + c00100, + EITC_prt[EIC], + ) + if EITC_indiv: + # individual EITC rather than a filing-unit EITC + eitc_p = EITCamount( + EITC_basic_frac, + EITC_rt[EIC], + earned_p, + EITC_c[EIC], + po_start, + earned_p, + EITC_prt[EIC], + ) + eitc_s = EITCamount( + EITC_basic_frac, + EITC_rt[EIC], + earned_s, + EITC_c[EIC], + po_start, + earned_s, + EITC_prt[EIC], + ) + eitc = eitc_p + eitc_s + + if EIC == 0: + h_age_elig = EITC_MinEligAge <= age_head <= EITC_MaxEligAge + s_age_elig = EITC_MinEligAge <= age_spouse <= EITC_MaxEligAge + if age_head == 0 or age_spouse == 0 or h_age_elig or s_age_elig: + c59660 = eitc + else: + c59660 = 0.0 + else: + c59660 = eitc + + if (MARS == 3 and not EITC_sep_filers_elig) or DSI == 1: + c59660 = 0.0 + + # reduce positive EITC if investment income exceeds ceiling + if c59660 > 0.0: + invinc = ( + e00400 + + e00300 + + e00600 + + max(0.0, c01000) + + max(0.0, (e02000 - e26270)) + ) + if invinc > EITC_InvestIncome_c: + eitc = c59660 - EITC_excess_InvestIncome_rt * ( + invinc - EITC_InvestIncome_c + ) + c59660 = max(0.0, eitc) + return c59660 + + +def RefundablePayrollTaxCredit( + was_plus_sey_p, was_plus_sey_s, RPTC_c, RPTC_rt, rptc_p, rptc_s, rptc +): + """ + Computes refundable payroll tax credit amounts. + + Parameters + ---------- + was_plus_sey_p: float + Wage and salary income plus taxable self employment income for taxpayer + was_plus_sey_s: float + Wage and salary income plus taxable self employment income for spouse + RPTC_c: float + Maximum refundable payroll tax credit + RPTC_rt: float + Refundable payroll tax credit phasein rate + rptc_p: float + Refundable Payroll Tax Credit for taxpayer + rptc_s: float + Refundable Payroll Tax Credit for spouse + rptc: float + Refundable Payroll Tax Credit for filing unit + + Returns + ------- + rptc_p: float + Refundable Payroll Tax Credit for taxpayer + rptc_s: float + Refundable Payroll Tax Credit for spouse + rptc: float + Refundable Payroll Tax Credit for filing unit + """ + rptc_p = min(was_plus_sey_p * RPTC_rt, RPTC_c) + rptc_s = min(was_plus_sey_s * RPTC_rt, RPTC_c) + rptc = rptc_p + rptc_s + return (rptc_p, rptc_s, rptc) + + +def ChildDepTaxCredit( + n24, + MARS, + c00100, + XTOT, + num, + c05800, + e07260, + CR_ResidentialEnergy_hc, + e07300, + CR_ForeignTax_hc, + c07180, + c07230, + e07240, + CR_RetirementSavings_hc, + c07200, + CTC_c, + CTC_ps, + CTC_prt, + exact, + ODC_c, + CTC_c_under6_bonus, + nu06, + CTC_refundable, + CTC_include17, + n21, + n1820, + c07220, + odc, + codtc_limited, +): + """ + Computes amounts on "Child Tax Credit and Credit for Other Dependents + Worksheet" in 2018 Publication 972, which pertain to these two + nonrefundable tax credits. + + Parameters + ---------- + n24: int + Number of children who are Child-Tax-Credit eligible, one condition for which is being under age 17 + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + c00100: float + Adjusted Gross Income (AGI) + XTOT: int + Total number of exemptions for filing unit + num: int + 2 when MARS is 2 (married filing jointly), otherwise 1 + c05800: float + Total (regular + AMT) income tax liability before credits + e07260: float + Residential energy credit from Form 5695 + CR_ResidentialEnergy_hc: float + Credit for residential energy haircut + e07300: float + Foreign tax credit from Form 1116 + CR_ForeignTax_hc: float + Credit for foreign tax credit + c07180: float + Credit for child and dependent care expenses from Form 2441 + c07230: float + Education tax credits non-refundable amount from Form 8863 + e07240: float + Retirement savings contributions credit from Form 8880 + CR_RetirementSavings_hc: float + Credit for retirement savings haircut + c07200: float + Schedule R credit for the elderly and the disabled + CTC_c: float + Maximum nonrefundable child tax credit per child + CTC_ps: list + Child tax credit phaseout MAGI start + CTC_prt: float + Child and dependent tax credit phaseout rate + exact: int + Whether or not to do rounding of phaseout fraction + ODC_c: float + Maximum nonrefundable other-dependent credit + CTC_c_under6_bonus: float + Bonus child tax credit maximum for qualifying children under six + nu06: int + Number of dependents under 6 years old + c07220: float + Child tax credit (adjusted) from Form 8812 + odc: float + Other Dependent Credit + codtc_limited: float + Maximum of 0 and line 10 minus line 16 + + Returns + ------- + c07220: float + Child tax credit (adjusted) from Form 8812 + odc: float + Other Dependent Credit + codtc_limited: float + Maximum of 0 and line 10 minus line 16 + """ + # Worksheet Part 1 + if CTC_include17: + childnum = n24 + max(0, XTOT - n21 - n1820 - n24 - num) + else: + childnum = n24 + line1 = CTC_c * childnum + CTC_c_under6_bonus * nu06 + line2 = ODC_c * max(0, XTOT - childnum - num) + line3 = line1 + line2 + modAGI = c00100 # no foreign earned income exclusion to add to AGI (line6) + if line3 > 0.0 and modAGI > CTC_ps[MARS - 1]: + excess = modAGI - CTC_ps[MARS - 1] + if exact == 1: # exact calculation as on tax forms + excess = 1000.0 * math.ceil(excess / 1000.0) + line10 = max(0.0, line3 - CTC_prt * excess) + else: + line10 = line3 + if line10 > 0.0: + # Worksheet Part 2 + line11 = c05800 + line12 = ( + e07260 * (1.0 - CR_ResidentialEnergy_hc) + + e07300 * (1.0 - CR_ForeignTax_hc) + + c07180 # child & dependent care expense credit + + c07230 # education credit + + e07240 * (1.0 - CR_RetirementSavings_hc) + + c07200 + ) # Schedule R credit + line13 = line11 - line12 + line14 = 0.0 + line15 = max(0.0, line13 - line14) + if CTC_refundable: + c07220 = line10 * line1 / line3 + odc = min(max(0.0, line10 - c07220), line15) + codtc_limited = max(0.0, line10 - c07220 - odc) + else: + line16 = min(line10, line15) # credit is capped by tax liability + # separate the CTC and ODTC amounts + c07220 = line16 * line1 / line3 + odc = max(0.0, line16 - c07220) + # compute codtc_limited for use in AdditionalCTC function + codtc_limited = max(0.0, line10 - line16) + else: + line16 = 0.0 + c07220 = 0.0 + odc = 0.0 + codtc_limited = 0.0 + return (c07220, odc, codtc_limited) + + +def PersonalTaxCredit( + MARS, + c00100, + XTOT, + nu18, + II_credit, + II_credit_ps, + II_credit_prt, + II_credit_nr, + II_credit_nr_ps, + II_credit_nr_prt, + RRC_c, + RRC_ps, + RRC_pe, + RRC_prt, + RRC_c_kids, + RRC_c_unit, + personal_refundable_credit, + personal_nonrefundable_credit, + recovery_rebate_credit, +): + """ + Computes personal_refundable_credit and personal_nonrefundable_credit, + neither of which are part of current-law policy. + + Parameters + ---------- + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + c00100: float + Adjusted Gross Income (AGI) + XTOT: int + Total number of exemptions for filing unit + nu18: int + Number of people under 18 years old in the filing unit + II_credit: list + Personal refundable credit maximum amount + II_credit_ps: list + Personal refundable credit phaseout start + II_credit_prt: float + Personal refundable credit phaseout rate + II_credit_nr: list + Personal nonrefundable credit maximum amount + II_credit_nr_ps: list + Personal nonrefundable credit phaseout start + II_credit_nr_prt: float + Personal nonrefundable credit phaseout rate + RRC_c: float + Maximum amount of Recovery Rebate Credit + RRC_ps: list + Recovery Rebate Credit phase out start + RRC_pe: list + Recovery Rebate Credit phase out end + RRC_prt: float + Recovery Rebate Credit phase out rate + RRC_c_kids: float + Credit amount per child as part of the Recovery Rebate Credit + RRC_c_unit: list + Maximum credit for filing unit as part of the Recovery Rebate Credit + personal_refundable_credit: float + Personal refundable credit + personal_nonrefundable_credit: float + Personal nonrefundable credit + + Returns + ------- + personal_refundable_credit: float + Personal refundable credit + personal_nonrefundable_credit: float + Personal nonrefundable credit + personal_rebate_credit: float + Personal rebate credit + """ + # calculate personal refundable credit amount with phase-out + personal_refundable_credit = II_credit[MARS - 1] + if II_credit_prt > 0.0 and c00100 > II_credit_ps[MARS - 1]: + pout = II_credit_prt * (c00100 - II_credit_ps[MARS - 1]) + fully_phasedout = personal_refundable_credit - pout + personal_refundable_credit = max(0.0, fully_phasedout) + # calculate personal nonrefundable credit amount with phase-out + personal_nonrefundable_credit = II_credit_nr[MARS - 1] + if II_credit_nr_prt > 0.0 and c00100 > II_credit_nr_ps[MARS - 1]: + pout = II_credit_nr_prt * (c00100 - II_credit_nr_ps[MARS - 1]) + fully_phasedout = personal_nonrefundable_credit - pout + personal_nonrefundable_credit = max(0.0, fully_phasedout) + # calculate Recovery Rebate Credit from CARES Act 2020 and/or ARPA 2021 + if c00100 < RRC_ps[MARS - 1]: + recovery_rebate_credit = RRC_c * XTOT + recovery_rebate_credit += RRC_c_unit[MARS - 1] + RRC_c_kids * nu18 + elif c00100 < RRC_pe[MARS - 1] and c00100 > 0: + prt = (c00100 - RRC_ps[MARS - 1]) / ( + RRC_pe[MARS - 1] - RRC_ps[MARS - 1] + ) + recovery_rebate_credit = RRC_c * XTOT * (1 - prt) + else: + recovery_rebate_credit = max( + 0, + RRC_c_unit[MARS - 1] + + RRC_c_kids * nu18 + - RRC_prt * (c00100 - RRC_ps[MARS - 1]), + ) + return ( + personal_refundable_credit, + personal_nonrefundable_credit, + recovery_rebate_credit, + ) + + +def AmOppCreditParts( + exact, + e87521, + num, + c00100, + CR_AmOppRefundable_hc, + CR_AmOppNonRefundable_hc, + c10960, + c87668, +): + """ + Applies a phaseout to the Form 8863, line 1, American Opportunity Credit + amount, e87521, and then applies the 0.4 refundable rate. + Logic corresponds to Form 8863, Part I. + + Parameters + ---------- + exact: int + Whether or not to do rounding of phaseout fraction + e87521: float + Total tentative AmOppCredit amount for all students. From Form 8863, line 1. + num: int + 2 when MARS is 2 (married filing jointly), otherwise 1 + c00100: float + Adjusted Gross Income (AGI) + CR_AmOppRefundable_hc: float + Refundable portion of the American Opportunity Credit haircut + CR_AmOppNonRefundable_hc: float + Nonrefundable portion of the American Opportunity Credit haircut + c10960: float + American Opportunity Credit refundable amount from Form 8863 + c87668: float + American Opportunity Credit non-refundable amount from Form 8863 + + Returns + ------- + c10960: float + American Opportunity Credit refundable amount from Form 8863 + c87668: float + American Opportunity Credit non-refundable amount from Form 8863 + + Notes + ----- + Tax Law Paramters that are not parameterized: + 90000: American Opportunity phaseout income base + 10000: American Opportunity Credit phaseout income range length + 1/1000: American Opportunity Credit phaseout rate + 0.3: American Opportunity Credit refundable rate + """ + if e87521 > 0.0: + c87658 = max(0.0, 90000.0 * num - c00100) + c87660 = 10000.0 * num + if exact == 1: # exact calculation as on tax forms + c87662 = 1000.0 * min(1.0, round(c87658 / c87660, 3)) + else: + c87662 = 1000.0 * min(1.0, c87658 / c87660) + c87664 = c87662 * e87521 / 1000.0 + c10960 = 0.4 * c87664 * (1.0 - CR_AmOppRefundable_hc) + c87668 = c87664 - c10960 * (1.0 - CR_AmOppNonRefundable_hc) + else: + c10960 = 0.0 + c87668 = 0.0 + return (c10960, c87668) + + +def SchR( + age_head, + age_spouse, + MARS, + c00100, + c05800, + e07300, + c07180, + e02400, + c02500, + e01500, + e01700, + CR_SchR_hc, + c07200, +): + """ + Calculates Schedule R credit for the elderly and the disabled, c07200. + + Note that no Schedule R policy parameters are inflation indexed. + + Note that all Schedule R policy parameters are hard-coded, and therefore, + are not able to be changed using Policy class parameters. + + Note that the CR_SchR_hc policy parameter allows the user to eliminate + or reduce total Schedule R credits. + + Parameters + ---------- + age_head: int + Age in years of taxpayer (primary adult) + age_spouse: int + Age in years of spouse (secondary adult, if present) + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + c00100: float + Adjusted Gross Income (AGI) + c05800: float + Total (regular + AMT) income tax liability before credit + e07300: float + Foreign tax credit from Form 1116 + c07180: float + Credit for child and dependent care expenses from Form 2441 + e02400: float + Total social security (OASDI) benefits + c02500: float + Social security (OASDI) benefits included in AGI + e01500: float + Total pensions and annuities + e01700: float + Taxable pensions and annuities + CR_SchR_hc: float + Schedule R credit haircut + c07200: float + Schedule R credit for the elderly and the disabled + + Returns + ------- + c07200: float + Schedule R credit for the elderly and the disabled + """ + if age_head >= 65 or (MARS == 2 and age_spouse >= 65): + # calculate credit assuming nobody is disabled (so line12 = line10) + if MARS == 2: + if age_head >= 65 and age_spouse >= 65: + schr12 = 7500.0 + else: + schr12 = 5000.0 + schr15 = 10000.0 + elif MARS == 3: + schr12 = 3750.0 + schr15 = 5000.0 + elif MARS in (1, 4): + schr12 = 5000.0 + schr15 = 7500.0 + else: + schr12 = 0.0 + schr15 = 0.0 + # nontaxable portion of OASDI benefits, line 13a + schr13a = max(0.0, e02400 - c02500) + # nontaxable portion of pension benefits, line 13b + # NOTE: the following approximation (required because of inadequate IRS + # data) will be accurate if all pensions are partially taxable + # or if all pensions are fully taxable. But if a filing unit + # receives at least one partially taxable pension and at least + # one fully taxable pension, then the approximation in the + # following line is not exactly correct. + schr13b = max(0.0, e01500 - e01700) + schr13c = schr13a + schr13b + schr16 = max(0.0, c00100 - schr15) + schr17 = 0.5 * schr16 + schr18 = schr13c + schr17 + schr19 = max(0.0, schr12 - schr18) + schr20 = 0.15 * schr19 + schr21 = max(0.0, (c05800 - e07300 - c07180)) + c07200 = min(schr20, schr21) * (1.0 - CR_SchR_hc) + else: # if not calculating Schedule R credit + c07200 = 0.0 + return c07200 + + +def EducationTaxCredit( + exact, + e87530, + MARS, + c00100, + num, + c05800, + e07300, + c07180, + c07200, + c87668, + LLC_Expense_c, + ETC_pe_Single, + ETC_pe_Married, + CR_Education_hc, + c07230, +): + """ + Computes Education Tax Credits (Form 8863) nonrefundable amount, c07230. + Logic corresponds to Form 8863, Part II. + + Parameters + ---------- + exact: int + Whether or not to do rounding of phaseout fraction + e87530: float + Adjusted qualified lifetime learning expenses for all students + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + c00100: float + Adjusted Gross Income (AGI) + num: int + 2 when MARS is 2 (married filing jointly), otherwise 1 + c05800: float + Total (regular + AMT) income tax liability before credits + e07300: float + Foreign tax credit from Form 1116 + c07180: float + Credit for child and dependent care expenses from Form 2441 + c07200: float + Schedule R credit for the elderly and the disabled + c87668: float + American Opportunity Credit non-refundalbe amount from Form 8863 + LLC_Expense_c: float + Lifetime learning credit expense limit + ETC_pe_Single: float + Education tax credit phaseout ends (single) + ETC_pe_Married: float + Education tax credit phaseout ends (married) + CR_Education_hc: float + Education Credits haircut + c07230: float + Education tax credits non-refundable amount from Form 8863 + + Returns + ------- + c07230: float + Education tax credits non-refundable amount from Form 8863 + + Notes + ----- + Tax Law Parameters that are not parameterized: + 0.2: Lifetime Learning Credit ratio against expense + """ + c87560 = 0.2 * min(e87530, LLC_Expense_c) + if MARS == 2: + c87570 = ETC_pe_Married * 1000.0 + else: + c87570 = ETC_pe_Single * 1000.0 + c87590 = max(0.0, c87570 - c00100) + c87600 = 10000.0 * num + if exact == 1: # exact calculation as on tax forms + c87610 = min(1.0, round(c87590 / c87600, 3)) + else: + c87610 = min(1.0, c87590 / c87600) + c87620 = c87560 * c87610 + xline4 = max(0.0, c05800 - (e07300 + c07180 + c07200)) + xline5 = min(c87620, xline4) + xline9 = max(0.0, c05800 - (e07300 + c07180 + c07200 + xline5)) + xline10 = min(c87668, xline9) + c87680 = xline5 + xline10 + c07230 = c87680 * (1.0 - CR_Education_hc) + return c07230 + + +def CharityCredit( + e19800, + e20100, + c00100, + CR_Charity_rt, + CR_Charity_f, + CR_Charity_frt, + MARS, + charity_credit, +): + """ + Computes nonrefundable charity credit, charity_credit. + This credit is not part of current-law policy. + + Parameters + ---------- + e19800: float + Itemizable charitable giving for cash and check contributions + e20100: float + Itemizable charitable giving other than cash and check contributions + c00100: float + Adjusted Gross Income (AGI) + CR_Charity_rt: float + Charity credit rate + CR_Charity_f: list + Charity credit floor + CR_Charity_frt: float + Charity credit floor rate + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + charity_credit: float + Credit for charitable giving + + Returns + ------- + charity_credit: float + Credit for charitable giving + """ + total_charity = e19800 + e20100 + floor = max(CR_Charity_frt * c00100, CR_Charity_f[MARS - 1]) + charity_cr_floored = max(total_charity - floor, 0) + charity_credit = CR_Charity_rt * (charity_cr_floored) + return charity_credit + + +def NonrefundableCredits( + c05800, + e07240, + e07260, + e07300, + e07400, + e07600, + p08000, + odc, + personal_nonrefundable_credit, + CTC_refundable, + CR_RetirementSavings_hc, + CR_ForeignTax_hc, + CR_ResidentialEnergy_hc, + CR_GeneralBusiness_hc, + CR_MinimumTax_hc, + CR_OtherCredits_hc, + charity_credit, + c07180, + c07200, + c07220, + c07230, + c07240, + c07260, + c07300, + c07400, + c07600, + c08000, +): + """ + NonRefundableCredits function sequentially limits credits to tax liability. + + Parameters + ---------- + c05800: float + Total (regular + AMT) income tax liability before credits + e07240: float + Retirement savings contributions credit from Form 8880 + e07260: float + Residential energy credit from Form 5695 + e07300: float + Foreign tax credit from Form 1116 + e07400: float + General business credit from Form 3800 + e07600: float + Prior year minimum tax credit from Form 8801 + p08000: float + Other tax credits + odc: float + Other Dependent Credit + personal_nonrefundable_credit: float + Personal nonrefundable credit + CTC_refundable: bool + Whether the child tax credit is fully refundable + CR_RetirementSavings_hc: float + Credit for retirement savings haircut + CR_ForeignTax_hc: float + Credit for foreign tax credit + CR_ResidentialEnergy_hc: float + Credit for residential energy haircut + CR_GeneralBusiness_hc: float + Credit for general business haircut + CR_MinimumTax_hc: float + Credit for previous year minimum tax credit haircut + CR_OtherCredits_hc: float + Other Credit haircut + charity_credit: float + Credit for charitable giving + c07180: float + Credit for child and dependent care expenses from Form 2441 + c07200: float + Schedule R credit for the elderly and the disabled + c07220: float + Child tax credit (adjusted) from From 8812 + c07230: float + Education tax credits non-refundable amount from Form 8863 + c07240: float + Retirement savings credit - Form 8880 + c07260: float + Residential energy credit - Form 5695 + c07300: float + Foreign tax credit - Form 1116 + c07400: float + General business credit - Form 3800 + c07600: float + Prior year minimum tax credit - Form 8801 + c08000: float + Other credits + Returns + ------- + c07180: float + Credit for child and dependent care expenses from Form 2441 + c07200: float + Schedule R credit for the elderly and the disabled + c07220: float + Child tax credit (adjusted) from From 8812 + c07230: float + Education tax credits non-refundable amount from Form 8863 + c07240: float + Retirement savings credit - Form 8880 + odc: float + Other Dependent Credit + c07260: float + Residential energy credit - Form 5695 + c07300: float + Foreign tax credit - Form 1116 + c07400: float + General business credit - Form 3800 + c07600: float + Prior year minimum tax credit - Form 8801 + c08000: float + Other credits + charity_credit: float + Credit for charitable giving + personal_nonrefundable_credit: float + Personal nonrefundable credit + """ + # limit tax credits to tax liability in order they are on 2015 1040 form + avail = c05800 + # Foreign tax credit - Form 1116 + c07300 = min(e07300 * (1.0 - CR_ForeignTax_hc), avail) + avail = avail - c07300 + # Child & dependent care expense credit + c07180 = min(c07180, avail) + avail = avail - c07180 + # Education tax credit + c07230 = min(c07230, avail) + avail = avail - c07230 + # Retirement savings credit - Form 8880 + c07240 = min(e07240 * (1.0 - CR_RetirementSavings_hc), avail) + avail = avail - c07240 + # Child tax credit + if not CTC_refundable: + c07220 = min(c07220, avail) + avail = avail - c07220 + # Other dependent credit + odc = min(odc, avail) + avail = avail - odc + # Residential energy credit - Form 5695 + c07260 = min(e07260 * (1.0 - CR_ResidentialEnergy_hc), avail) + avail = avail - c07260 + # General business credit - Form 3800 + c07400 = min(e07400 * (1.0 - CR_GeneralBusiness_hc), avail) + avail = avail - c07400 + # Prior year minimum tax credit - Form 8801 + c07600 = min(e07600 * (1.0 - CR_MinimumTax_hc), avail) + avail = avail - c07600 + # Schedule R credit + c07200 = min(c07200, avail) + avail = avail - c07200 + # Other credits + c08000 = min(p08000 * (1.0 - CR_OtherCredits_hc), avail) + avail = avail - c08000 + charity_credit = min(charity_credit, avail) + avail = avail - charity_credit + # Personal nonrefundable credit + personal_nonrefundable_credit = min(personal_nonrefundable_credit, avail) + avail = avail - personal_nonrefundable_credit + return ( + c07180, + c07200, + c07220, + c07230, + c07240, + odc, + c07260, + c07300, + c07400, + c07600, + c08000, + charity_credit, + personal_nonrefundable_credit, + ) + + +def AdditionalCTC( + codtc_limited, + ACTC_c, + n24, + earned, + ACTC_Income_thd, + ACTC_rt, + nu06, + ACTC_rt_bonus_under6family, + ACTC_ChildNum, + CTC_refundable, + CTC_include17, + XTOT, + n21, + n1820, + num, + ptax_was, + c03260, + e09800, + c59660, + e11200, + c11070, +): + """ + Calculates refundable Additional Child Tax Credit (ACTC), c11070, + following 2018 Form 8812 logic. + + Parameters + ---------- + codtc_limited: float + Maximum of 0 and line 10 minus line 16 + ACTC_c: float + Maximum refundable additional child tax credit + n24: int + Number of children who are Child-Tax-Credit eligible, one condition for which is being under age 17 + earned: float + Earned income for filing unit + ACTC_Income_thd: float + Additional Child Tax Credit income threshold + ACTC_rt: float + Additional Child Tax Credit rate + nu06: int + Number of dependents under 6 years old + ACTC_rt_bonus_under6family: float + Bonus additional child tax credit rate for families with qualifying children under 6 + ACTC_ChildNum: float + Additional Child Tax Credit minimum number of qualified children for different formula + ptax_was: float + Employee and employer OASDI plus HI FICA tax + c03260: float + Self-employment tax deduction (after haircut) + e09800: float + Unreported payroll taxes from Form 4137 or 8919 + c59660: float + EITC amount + e11200: float + Excess payroll (FICA/RRTA) tax withheld + c11070: float + Child tax credit (refunded) from Form 8812 + + Returns + ------- + c11070: float + Child tax credit (refunded) from Form 8812 + """ + # Part I + line3 = codtc_limited + + if CTC_refundable: + line4 = 0.0 + else: + if CTC_include17: + childnum = n24 + max(0, XTOT - n21 - n1820 - n24 - num) + else: + childnum = n24 + line4 = ACTC_c * childnum + c11070 = 0.0 # line15 + if line3 > 0.0 and line4 > 0.0: + line5 = min(line3, line4) + line7 = max(0.0, earned - ACTC_Income_thd) + # accommodate ACTC rate bonus for families with children under 5 + if nu06 == 0: + ACTC_rate = ACTC_rt + else: + ACTC_rate = ACTC_rt + ACTC_rt_bonus_under6family + line8 = ACTC_rate * line7 + if childnum < ACTC_ChildNum: + if line8 > 0.0: + c11070 = min(line5, line8) + else: # if childnum >= ACTC_ChildNum + if line8 >= line5: + c11070 = line5 + else: # complete Part II + line9 = 0.5 * ptax_was + line10 = c03260 + e09800 + line11 = line9 + line10 + line12 = c59660 + e11200 + line13 = max(0.0, line11 - line12) + line14 = max(line8, line13) + c11070 = min(line5, line14) + return c11070 + + +def C1040( + c05800, + c07180, + c07200, + c07220, + c07230, + c07240, + c07260, + c07300, + c07400, + c07600, + c08000, + e09700, + e09800, + e09900, + niit, + othertaxes, + c07100, + c09200, + odc, + charity_credit, + personal_nonrefundable_credit, + CTC_refundable, +): + """ + Computes total used nonrefundable credits, c07100, othertaxes, and + income tax before refundable credits, c09200. + + Parameters + ---------- + c05800: float + Total (regular + AMT) income tax liability before credits + c07180: float + Credit for child and dependent care expenses from Form 2441 + c07200: float + Schedule R credit for the elderly and the disabled + c07220: float + Child tax credit (adjusted) from Form 8812 + c07230: float + Education tax credit non-refundable amount from Form 8863 + c07240: float + Retirement savings credit - Form 8880 + c07260: float + Residential energy credit - Form 5695 + c07300: float + Foreign tax credit - Form 1116 + c07400: float + General business credit - Form 3800 + c07600: float + Prior year minimum tax credit - Form 8801 + c08000: float + Other credits + e09700: float + Recapture of Investment Credit + e09800: float + Unreported payroll taxes from Form 4137 or 8919 + e09900: float + Penalty tax on qualified retirement plans + niit: float + Net Investment Income Tax from Form 8960 + othertaxes: float + Sum of niit, e09700, e09800, and e09900 + c07100: float + Total non-refundable credits used to reduce positive tax liability + c09200: float + Income tax liabilities (including othertaxes) after non-refundable credits are used, but before refundable credits are applied + odc: float + Other Dependent Credit + charity_credit: float + Credit for charitable giving + personal_nonrefundable_credit: float + Personal nonrefundable credit + + Returns + ------- + c07100: float + Total non-refundable credits used to reduce positive tax liability + othertaxes: float + Sum of niit, e09700, e09800, and e09900 + c09200: float + Income tax liabilities (including othertaxes) after non-refundable credits are used, but before refundable credits are applied + """ + # total used nonrefundable credits (as computed in NonrefundableCredits) + c07100 = ( + c07180 + + c07200 + + c07600 + + c07300 + + c07400 + + c07220 * (1.0 - CTC_refundable) + + c08000 + + c07230 + + c07240 + + c07260 + + odc + + charity_credit + + personal_nonrefundable_credit + ) + # tax after credits (2016 Form 1040, line 56) + tax_net_nonrefundable_credits = max(0.0, c05800 - c07100) + # tax (including othertaxes) before refundable credits + othertaxes = e09700 + e09800 + e09900 + niit + c09200 = othertaxes + tax_net_nonrefundable_credits + return (c07100, othertaxes, c09200) + + +def CTC_new( + CTC_new_c, + CTC_new_rt, + CTC_new_c_under6_bonus, + CTC_new_ps, + CTC_new_prt, + CTC_new_for_all, + CTC_include17, + CTC_new_refund_limited, + CTC_new_refund_limit_payroll_rt, + CTC_new_refund_limited_all_payroll, + payrolltax, + n24, + nu06, + XTOT, + n21, + n1820, + num, + c00100, + MARS, + ptax_oasdi, + c09200, + ctc_new, +): + """ + Computes new refundable child tax credit using specified parameters. + + Parameters + ---------- + CTC_new_c: float + New refundable child tax credit maximum amount per child + CTC_new_rt: float + New refundalbe child tax credit amount phasein rate + CTC_new_c_under6_bonus: float + Bonus new refundable child tax credit maximum for qualifying children under six + CTC_new_ps: list + New refundable child tax credit phaseout starting AGI + CTC_new_prt: float + New refundable child tax credit amount phaseout rate + CTC_new_for_all: bool + Whether or not maximum amount of the new refundable child tax credit is available to all + CTC_new_refund_limited: bool + New child tax credit refund limited to a decimal fraction of payroll taxes + CTC_new_refund_limit_payroll_rt: float + New child tax credit refund limit rate (decimal fraction of payroll taxes) + CTC_new_refund_limited_all_payroll: bool + New child tax credit refund limit applies to all FICA taxes, not just OASDI + payrolltax: float + Total (employee + employer) payroll tax liability + n24: int + Number of children who are Child-Tax-Credit eligible, one condition for which is being under age 17 + nu06: int + Number of dependents under 6 years old + c00100: float + Adjusted Gross Income (AGI) + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + ptax_oasdi: float + Employee and employer OASDI FICA tax plus self employment tax + Excludes HI FICA so positive ptax_oasdi is less than ptax_was + setax + c09200: float + Income tax liabilities (including othertaxes) after non-refundable credits are used, but before refundable credits are applied + ctc_new: float + New refundable child tax credit + + Returns + ------- + ctc_new: float + New refundable child tax credit + """ + if CTC_include17: + childnum = n24 + max(0, XTOT - n21 - n1820 - n24 - num) + else: + childnum = n24 + if childnum > 0: + posagi = max(c00100, 0.0) + ctc_new = CTC_new_c * childnum + CTC_new_c_under6_bonus * nu06 + if not CTC_new_for_all: + ctc_new = min(CTC_new_rt * posagi, ctc_new) + ymax = CTC_new_ps[MARS - 1] + if posagi > ymax: + ctc_new_reduced = max(0.0, ctc_new - CTC_new_prt * (posagi - ymax)) + ctc_new = min(ctc_new, ctc_new_reduced) + if ctc_new > 0.0 and CTC_new_refund_limited: + refund_new = max(0.0, ctc_new - c09200) + if not CTC_new_refund_limited_all_payroll: + limit_new = CTC_new_refund_limit_payroll_rt * ptax_oasdi + if CTC_new_refund_limited_all_payroll: + limit_new = CTC_new_refund_limit_payroll_rt * payrolltax + limited_new = max(0.0, refund_new - limit_new) + ctc_new = max(0.0, ctc_new - limited_new) + else: + ctc_new = 0.0 + return ctc_new + + +def IITAX( + c59660, + c11070, + c10960, + personal_refundable_credit, + ctc_new, + rptc, + c09200, + payrolltax, + CDCC_refund, + recovery_rebate_credit, + eitc, + c07220, + CTC_refundable, + refund, + iitax, + combined, +): + """ + Computes final taxes. + + Parameters + ---------- + c59660: float + EITC amount + c11070: float + Child tax credit (refunded) from Form 8812 + c10960: float + American Opportunity Credit refundable amount from Form 8863 + personal_refundable_credit: float + Personal refundable credit + ctc_new: float + New refundable child tax credit + rptc: float + Refundable Payroll Tax Credit for filing unit + c09200: float + Income tax liabilities (including othertaxes) after non-refundable + credits are used, but before refundable credits are applied + payrolltax: float + Total (employee + employer) payroll tax liability + eitc: float + Earned Income Credit + refund: float + Total refundable income tax credits + iitax: float + Total federal individual income tax liability + combined: float + Sum of iitax and payrolltax and lumpsum_tax + + Returns + ------- + eitc: float + Earned Income Credit + refund: float + Total refundable income tax credits + iitax: float + Total federal individual income tax liability + combined: float + Sum of iitax and payrolltax and lumpsum_tax + """ + eitc = c59660 + if CTC_refundable: + ctc_refund = c07220 + else: + ctc_refund = 0.0 + refund = ( + eitc + + c11070 + + c10960 + + CDCC_refund + + recovery_rebate_credit + + personal_refundable_credit + + ctc_new + + rptc + + ctc_refund + ) + iitax = c09200 - refund + combined = iitax + payrolltax + return (eitc, refund, iitax, combined) + + +@JIT(nopython=True) +def Taxes( + income, + MARS, + tbrk_base, + rate1, + rate2, + rate3, + rate4, + rate5, + rate6, + rate7, + rate8, + tbrk1, + tbrk2, + tbrk3, + tbrk4, + tbrk5, + tbrk6, + tbrk7, +): + """ + Taxes function returns tax amount given the progressive tax rate + schedule specified by the rate* and (upper) tbrk* parameters and + given income, filing status (MARS), and tax bracket base (tbrk_base). + + Parameters + ---------- + income: float + Taxable income + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + tbrk_base: float + Amount of income used to determine the braket the filer is in + rate1: list + Income tax rate 1 + rate2: list + Income tax rate 2 + rate3: list + Income tax rate 3 + rate4: list + Income tax rate 4 + rate5: list + Income tax rate 5 + rate6: list + Income tax rate 6 + rate7: list + Income tax rate 7 + rate8: list + Income tax rate 8 + tbrk1: list + Income tax bracket (upper threshold) 1 + tbrk2: list + Income tax bracket (upper threshold) 2 + tbrk3: list + Income tax bracket (upper threshold) 3 + tbrk4: list + Income tax bracket (upper threshold) 4 + tbrk5: list + Income tax bracket (upper threshold) 5 + tbrk6: list + Income tax bracket (upper threshold) 6 + tbrk7: list + Income tax bracket (upper threshold) 7 + + Returns + ------- + None + """ + if tbrk_base > 0.0: + brk1 = max(tbrk1[MARS - 1] - tbrk_base, 0.0) + brk2 = max(tbrk2[MARS - 1] - tbrk_base, 0.0) + brk3 = max(tbrk3[MARS - 1] - tbrk_base, 0.0) + brk4 = max(tbrk4[MARS - 1] - tbrk_base, 0.0) + brk5 = max(tbrk5[MARS - 1] - tbrk_base, 0.0) + brk6 = max(tbrk6[MARS - 1] - tbrk_base, 0.0) + brk7 = max(tbrk7[MARS - 1] - tbrk_base, 0.0) + else: + brk1 = tbrk1[MARS - 1] + brk2 = tbrk2[MARS - 1] + brk3 = tbrk3[MARS - 1] + brk4 = tbrk4[MARS - 1] + brk5 = tbrk5[MARS - 1] + brk6 = tbrk6[MARS - 1] + brk7 = tbrk7[MARS - 1] + return ( + rate1 * min(income, brk1) + + rate2 * min(brk2 - brk1, max(0.0, income - brk1)) + + rate3 * min(brk3 - brk2, max(0.0, income - brk2)) + + rate4 * min(brk4 - brk3, max(0.0, income - brk3)) + + rate5 * min(brk5 - brk4, max(0.0, income - brk4)) + + rate6 * min(brk6 - brk5, max(0.0, income - brk5)) + + rate7 * min(brk7 - brk6, max(0.0, income - brk6)) + + rate8 * max(0.0, income - brk7) + ) + + +def ComputeBenefit(calc, ID_switch): + """ + Calculates the value of the benefits accrued from itemizing. + + Parameters + ---------- + calc: Calculator object + calc represents the reform while self represents the baseline + ID_switch: list + Deductions subject to the surtax on itemized deduction benefits + + Returns + ------- + benefit: float + Imputed benefits from itemizing deductions + """ + # compute income tax liability with no itemized deductions allowed for + # the types of itemized deductions covered under the BenefitSurtax + no_ID_calc = copy.deepcopy(calc) + if ID_switch[0]: + no_ID_calc.policy_param("ID_Medical_hc", [1.0]) + if ID_switch[1]: + no_ID_calc.policy_param("ID_StateLocalTax_hc", [1.0]) + if ID_switch[2]: + no_ID_calc.policy_param("ID_RealEstate_hc", [1.0]) + if ID_switch[3]: + no_ID_calc.policy_param("ID_Casualty_hc", [1.0]) + if ID_switch[4]: + no_ID_calc.policy_param("ID_Miscellaneous_hc", [1.0]) + if ID_switch[5]: + no_ID_calc.policy_param("ID_InterestPaid_hc", [1.0]) + if ID_switch[6]: + no_ID_calc.policy_param("ID_Charity_hc", [1.0]) + no_ID_calc._calc_one_year() # pylint: disable=protected-access + diff_iitax = no_ID_calc.array("iitax") - calc.array("iitax") + benefit = np.where(diff_iitax > 0.0, diff_iitax, 0.0) + return benefit + + +def BenefitSurtax(calc): + """ + Computes itemized-deduction-benefit surtax and adds the surtax amount + to income tax, combined tax, and surtax liabilities. + + Parameters + ---------- + calc: Calculator object + calc represents the reform while self represents the baseline + + Returns + ------- + None: + The function modifies calc + """ + if calc.policy_param("ID_BenefitSurtax_crt") != 1.0: + ben = ComputeBenefit( + calc, calc.policy_param("ID_BenefitSurtax_Switch") + ) + agi = calc.array("c00100") + ben_deduct = calc.policy_param("ID_BenefitSurtax_crt") * agi + ben_exempt_array = calc.policy_param("ID_BenefitSurtax_em") + ben_exempt = ben_exempt_array[calc.array("MARS") - 1] + ben_dedem = ben_deduct + ben_exempt + ben_surtax = calc.policy_param("ID_BenefitSurtax_trt") * np.where( + ben > ben_dedem, ben - ben_dedem, 0.0 + ) + # add ben_surtax to income & combined taxes and to surtax subtotal + calc.incarray("iitax", ben_surtax) + calc.incarray("combined", ben_surtax) + calc.incarray("surtax", ben_surtax) + + +def BenefitLimitation(calc): + """ + Limits the benefits of select itemized deductions to a fraction of + deductible expenses. + + Parameters + ---------- + calc: Calculator object + calc represents the reform while self represents the baseline + + Returns + ------- + None: + The function modifies calc + """ + if calc.policy_param("ID_BenefitCap_rt") != 1.0: + benefit = ComputeBenefit( + calc, calc.policy_param("ID_BenefitCap_Switch") + ) + # Calculate total deductible expenses under the cap + deduct_exps = 0.0 + if calc.policy_param("ID_BenefitCap_Switch")[0]: # medical + deduct_exps += calc.array("c17000") + if calc.policy_param("ID_BenefitCap_Switch")[1]: # statelocal + one_minus_hc = 1.0 - calc.policy_param("ID_StateLocalTax_hc") + deduct_exps += one_minus_hc * np.maximum( + calc.array("e18400_capped"), 0.0 + ) + if calc.policy_param("ID_BenefitCap_Switch")[2]: # realestate + one_minus_hc = 1.0 - calc.policy_param("ID_RealEstate_hc") + deduct_exps += one_minus_hc * calc.array("e18500_capped") + if calc.policy_param("ID_BenefitCap_Switch")[3]: # casualty + deduct_exps += calc.array("c20500") + if calc.policy_param("ID_BenefitCap_Switch")[4]: # misc + deduct_exps += calc.array("c20800") + if calc.policy_param("ID_BenefitCap_Switch")[5]: # interest + deduct_exps += calc.array("c19200") + if calc.policy_param("ID_BenefitCap_Switch")[6]: # charity + deduct_exps += calc.array("c19700") + # Calculate cap value for itemized deductions + benefit_limit = deduct_exps * calc.policy_param("ID_BenefitCap_rt") + # Add the difference between the actual benefit and capped benefit + # to income tax and combined tax liabilities. + excess_benefit = np.maximum(benefit - benefit_limit, 0) + calc.incarray("iitax", excess_benefit) + calc.incarray("surtax", excess_benefit) + calc.incarray("combined", excess_benefit) + + +def FairShareTax( + c00100, + MARS, + ptax_was, + setax, + ptax_amc, + FST_AGI_trt, + FST_AGI_thd_lo, + FST_AGI_thd_hi, + fstax, + iitax, + combined, + surtax, +): + """ + Computes Fair Share Tax, or "Buffet Rule", types of reforms. + + Parameters + ---------- + c00100: float + Adjusted Gross Income (AGI) + MARS: int + Filing (marital) status. (1=single, 2=joint, 3=separate, 4=household-head, 5=widow(er)) + ptax_was: float + Employee and employer OASDI plus HI FICA tax + setax: float + Self-employment tax + ptax_amc: float + Additional Medicare Tax + FST_AGI_trt: float + New minimum tax; rate as a decimal fraction of AGI + FST_AGI_thd_lo: list + Minimum AGI needed to be subject to the new minimum tax + FST_AGI_thd_hi: list + AGI level at which the New Minimum Tax is fully phased in + fstax: float + Fair Share Tax amount + iitax: float + Total federal individual income tax liability + combined: float + Sum of iitax and payrolltax and lumpsum_tax + surtax: float + Individual income tax subtotal augmented by fstax + + Returns + ------- + fstax: float + Fair Share Tax amount + iitax: float + Total federal individual income tax liability + combined: float + Sum of iitax and payrolltax and lumpsum_tax + surtax: float + Individual income tax subtotal augmented by fstax + """ + if FST_AGI_trt > 0.0 and c00100 >= FST_AGI_thd_lo[MARS - 1]: + employee_share = 0.5 * ptax_was + 0.5 * setax + ptax_amc + fstax = max(c00100 * FST_AGI_trt - iitax - employee_share, 0.0) + thd_gap = max(FST_AGI_thd_hi[MARS - 1] - FST_AGI_thd_lo[MARS - 1], 0.0) + if thd_gap > 0.0 and c00100 < FST_AGI_thd_hi[MARS - 1]: + fstax *= (c00100 - FST_AGI_thd_lo[MARS - 1]) / thd_gap + iitax += fstax + combined += fstax + surtax += fstax + else: + fstax = 0.0 + return (fstax, iitax, combined, surtax) + + +def LumpSumTax(DSI, num, XTOT, LST, lumpsum_tax, combined): + """ + Computes lump-sum tax and add it to combined taxes. + + Parameters + ---------- + DSI: int + 1 if claimed as dependent on another return, otherwise 0 + num: int + 2 when MARS is 2 (married filing jointly); otherwise 1 + XTOT: int + Total number of exemptions for filing unit + LST: float + Dollar amount of lump-sum tax + lumpsum_tax: float + Lumpsum (or head) tax + combined: float + Sum of iitax and payrolltax and lumpsum_tax + + Returns + ------- + lumpsum_tax: float + Lumpsum (or head) tax + combined: float + Sum of iitax and payrolltax and lumpsum_tax + """ + if LST == 0.0 or DSI == 1: + lumpsum_tax = 0.0 + else: + lumpsum_tax = LST * max(num, XTOT) + combined += lumpsum_tax + return (lumpsum_tax, combined) + + +def ExpandIncome( + e00200, + pencon_p, + pencon_s, + e00300, + e00400, + e00600, + e00700, + e00800, + e00900, + e01100, + e01200, + e01400, + e01500, + e02000, + e02100, + p22250, + p23250, + cmbtp, + ptax_was, + benefit_value_total, + expanded_income, +): + """ + Calculates expanded_income from component income types. + + Parameters + ---------- + e00200: float + Wages, salaries, and tips for filing unit net of pension contributions + pencon_p: float + Contributions to defined-contribution pension plans for taxpayer + pencon_s: float + Contributions to defined-contribution pension plans for spouse + e00300: float + Taxable interest income + e00400: float + Tax-exempt interest income + e00600: float + Ordinary dividends included in AGI + e00700: float + Taxable refunds of state and local income taxes + e00800: float + Alimony received + e00900: float + Schedule C business net profit/loss for filing unit + e01100: float + Capital gain distributions not reported on Schedule D + e01200: float + Other net gain/loss from Form 4797 + e01400: float + Taxable IRA distributions + e01500: float + Total pensions and annuities + e02000: float + Schedule E total rental, royalty, partnership, S-corporation, etc, income/loss + e02100: float + Farm net income/loss for filing unit from Schedule F + p22250: float + Schedule D net short term capital gains/losses + p23250:float + Schedule D net long term capital gains/losses + cmbtp: float + Estimate of inome on (AMT) Form 6251 but not in AGI + ptax_was: float + Employee and employer OASDI and HI FICA tax + benefit_value_total: float + Consumption value of all benefits received by tax unit, which is included in expanded income + expanded_income: float + Broad income measure that includes benefit_value_total + + Returns + ------- + expanded_income: float + Broad income measure that includes benefit_value_total + """ + expanded_income = ( + e00200 # wage and salary income net of DC pension contributions + + pencon_p # tax-advantaged DC pension contributions for taxpayer + + pencon_s # tax-advantaged DC pension contributions for spouse + + e00300 # taxable interest income + + e00400 # non-taxable interest income + + e00600 # dividends + + e00700 # state and local income tax refunds + + e00800 # alimony received + + e00900 # Sch C business net income/loss + + e01100 # capital gain distributions not reported on Sch D + + e01200 # Form 4797 other net gain/loss + + e01400 # taxable IRA distributions + + e01500 # total pension & annuity income (including DB-plan benefits) + + e02000 # Sch E total rental, ..., partnership, S-corp income/loss + + e02100 # Sch F farm net income/loss + + p22250 # Sch D: net short-term capital gain/loss + + p23250 # Sch D: net long-term capital gain/loss + + cmbtp # other AMT taxable income items from Form 6251 + + 0.5 * ptax_was # employer share of FICA taxes on wages/salaries + + benefit_value_total # consumption value of all benefits received; + # see the BenefitPrograms function in this file for details on + # exactly how the benefit_value_total variable is computed + ) + return expanded_income + + +def AfterTaxIncome(combined, expanded_income, aftertax_income): + """ + Calculates after-tax expanded income. + + Parameters + ---------- + combined: float + Sum of iitax and payrolltax and lumpsum_tax + expanded_income: float + Broad income measure that includes benefit_value_total + aftertax_income: float + After tax income is equal to expanded_income minus combined + + Returns + ------- + aftertax_income: float + After tax income is equal to expanded_income minus combined + """ + aftertax_income = expanded_income - combined + return aftertax_income + + +CALCFUNCTIONS_LIST = ( + TaxInc, + SchXYZTax, + GainsTax, + AGIsurtax, + NetInvIncTax, + AMT, + EI_PayrollTax, + Adj, + DependentCare, + ALD_InvInc_ec_base, + CapGains, + SSBenefits, + UBI, + AGI, + ItemDedCap, + ItemDed, + StdDed, + AdditionalMedicareTax, + F2441, + EITC, + RefundablePayrollTaxCredit, + ChildDepTaxCredit, + AdditionalCTC, + CTC_new, + PersonalTaxCredit, + SchR, + AmOppCreditParts, + EducationTaxCredit, + CharityCredit, + NonrefundableCredits, + C1040, + IITAX, + BenefitSurtax, + BenefitLimitation, + FairShareTax, + LumpSumTax, + BenefitPrograms, + ExpandIncome, + AfterTaxIncome, +) + +CALCFUNCTIONS = {fn.__name__: fn for fn in CALCFUNCTIONS_LIST} diff --git a/openfisca_us/tools/dev/generate_taxcalc_unit_tests.py b/openfisca_us/tools/dev/generate_taxcalc_unit_tests.py new file mode 100644 index 00000000000..be867daf01d --- /dev/null +++ b/openfisca_us/tools/dev/generate_taxcalc_unit_tests.py @@ -0,0 +1,234 @@ +from time import time +from pathlib import Path +from typing import Callable, Dict, List, Tuple +from taxcalc import Calculator, Policy, Records +from openfisca_us.tools.dev.calcfunctions import * +import pandas as pd +import ast +import yaml +import argparse + +np.random.seed(int(time())) + + +def extract_return(fname): + for x in ast.walk( + ast.parse(open(Path(__file__).parent / "calcfunctions.py").read()) + ): + if not (isinstance(x, ast.FunctionDef)): + continue + if not (x.name == fname): + continue + for b in x.body: + if isinstance(b, ast.Return): + if isinstance(b.value, ast.Name): + yield b.value.id + elif isinstance(b.value, ast.Tuple): + yield from [element.id for element in b.value.elts] + + +def get_inputs(fn: Callable) -> Tuple: + """ + Return the inputs of a function. + """ + return fn.__code__.co_varnames[: fn.__code__.co_argcount] + + +def get_outputs(fn: Callable) -> Tuple: + """ + Return the outputs of a function. + """ + return list(extract_return(fn.__name__)) + + +policy = Policy() +TEST_YEAR = 2019 +DEBUG_MODE = False +TAXCALC_CPS_CSV_FILE = "~/PSLmodels/Tax-Calculator/cps.csv" +cps = pd.read_csv(TAXCALC_CPS_CSV_FILE, nrows=1 if DEBUG_MODE else None) +policy.set_year(TEST_YEAR) + + +def generate_input_values( + inputs: List[str], output_names: List[str], overrides: Dict[str, float] +) -> Dict[str, float]: + """Fetches the parameter value if the input is a parameter, + otherwise returns the value from a random CPS tax unit. + + Args: + inputs (List[str]): The input names. + output_names (List[str]): The names of the outputs. + overrides (Dict[str, float]): A dictionary of overrides. + + Returns: + Dict[str, float]: The generated values. + """ + if not DEBUG_MODE: + cps_tax_unit = cps[(cps[output_names] != 0).min(axis=1)].sample(n=1) + result = {} + for input_ in inputs: + try: + result[input_] = getattr(policy, input_)[0] + except AttributeError: + if input_ in overrides: + result[input_] = overrides[input_] + elif DEBUG_MODE: + result[input_] = 0 + else: + result[input_] = int(cps_tax_unit[input_].values[0]) + return result + + +def generate_unit_test( + fn_name: str, input_overrides: Dict[str, float] = None +) -> Tuple[dict, Callable[[dict], Tuple[float]], Tuple[float]]: + """Generates a unit test for a calcfunctions function. + + Args: + fn_name (str): The name of the taxcalc function. + input_overrides (Dict[str, float]): A dictionary of overrides. + + Returns: + Tuple[dict, Callable[[dict, Tuple[float]]], Tuple[float]]: Keyword args, the function taking them and the expected result. + """ + if input_overrides is None: + input_overrides = {} + fn = CALCFUNCTIONS[fn_name] + + def fn_(inputs): + return fn(**inputs) + + kwargs = generate_input_values( + get_inputs(fn), get_outputs(fn), overrides=input_overrides + ) + + return kwargs, fn_, fn_(kwargs) + + +def attempt_generate_all_nonzero_unit_test( + fn_name: str, +) -> Tuple[dict, Callable[[dict], Tuple[float]], Tuple[float]]: + for _ in range(1_000): + kwargs, fn_, result = generate_unit_test(fn_name) + if all([i != 0 for i in result]): + return kwargs, fn_, result + + +def generate_yaml_test_dict(fn_name: str, name: str = None): + kwargs, _, result = attempt_generate_all_nonzero_unit_test(fn_name) + outputs = get_outputs(CALCFUNCTIONS[fn_name]) + test_dict = dict( + name=f"Unit test for {fn_name}" if name is None else name, + period=2019, + absolute_error_margin=1, + input={ + rename_variable(x): translate_value(x, y) + for x, y in kwargs.items() + if x in cps and x not in outputs + }, + output={ + rename_variable(name): float(res) + for name, res in zip(outputs, result) + }, + ) + return test_dict + + +def generate_yaml_tests(fn_name: str, n: int = 10) -> str: + return yaml.dump( + [ + generate_yaml_test_dict(fn_name, name=f"{fn_name} unit test {i}") + for i in range(n) + ], + sort_keys=False, + ) + + +RENAMES = { + "MARS": "mars", +} + +VALUE_RENAMES = { + "MARS": { + 1: "SINGLE", + 2: "JOINT", + 3: "SEPARATE", + 4: "HOUSEHOLD_HEAD", + 5: "WIDOW", + } +} + +INVERSE_RENAMES = {y: x for x, y in RENAMES.items()} + +INVERSE_VALUE_RENAMES = { + x: {b: a for a, b in y.items()} for x, y in VALUE_RENAMES.items() +} + + +def rename_variable(x): + return RENAMES.get(x, x) + + +def translate_value(name, x): + return VALUE_RENAMES[name].get(x, x) if name in VALUE_RENAMES else x + + +def inverse_rename_variable(x): + return INVERSE_RENAMES.get(x, x) + + +def inverse_translate_value(name, x): + return ( + INVERSE_VALUE_RENAMES[name].get(x, x) + if name in INVERSE_VALUE_RENAMES + else x + ) + + +def debug_test_yaml(fn_name: str, file: str, i: int = 0): + with open(file, "r") as f: + tests = yaml.safe_load(f) + test = tests[i] + input_overrides = { + inverse_rename_variable(x): inverse_translate_value( + inverse_rename_variable(x), y + ) + for x, y in test["input"].items() + } + kwargs, fn_, _ = generate_unit_test(fn_name, input_overrides) + fn_(kwargs) + + +def generate_taxcalc_cps_df(): + r = Records.cps_constructor() + p = Policy(year=TEST_YEAR) + c = Calculator(policy=p, records=r) + df = c.dataframe(None, all_vars=True) + df.to_csv("cps.csv") + + +if __name__ == "__main__": + if not DEBUG_MODE: + parser = argparse.ArgumentParser( + description="Generate unit tests for calcfunctions." + ) + parser.add_argument( + "function", + type=str, + help="The name of the function to generate unit tests for.", + ) + parser.add_argument( + "--n", + type=int, + default=10, + help="The number of unit tests to generate.", + ) + args = parser.parse_args() + print(generate_yaml_tests(args.function, args.n)) + else: + # An example of debugging a unit test + debug_test_yaml( + "AMT", + "openfisca_us/tests/policy/baseline/calcfunctions/amt.yaml", + 0, + ) diff --git a/openfisca_us/tools/dev/run_taxcalc_test.py b/openfisca_us/tools/dev/run_taxcalc_test.py index ffaf56fd7a4..3effe59ad7f 100644 --- a/openfisca_us/tools/dev/run_taxcalc_test.py +++ b/openfisca_us/tools/dev/run_taxcalc_test.py @@ -11,234 +11,254 @@ ] TAXCALC_INPUT_VARIABLES = [ - 'DSI', - 'EIC', - 'FLPDYR', - 'MARS', - 'MIDR', - 'RECID', - 'XTOT', - 'age_head', - 'age_spouse', - 'agi_bin', - 'blind_head', - 'blind_spouse', - 'cmbtp', - 'e00200', - 'e00200p', - 'e00200s', - 'pencon_p', - 'pencon_s', - 'e00300', - 'e00400', - 'e00600', - 'e00650', - 'e00700', - 'e00800', - 'e00900', - 'e00900p', - 'e00900s', - 'e01100', - 'e01200', - 'e01400', - 'e01500', - 'e01700', - 'e02000', - 'e02100', - 'e02100p', - 'e02100s', - 'e02300', - 'e02400', - 'e03150', - 'e03210', - 'e03220', - 'e03230', - 'e03240', - 'e03270', - 'e03290', - 'e03300', - 'e03400', - 'e03500', - 'e07240', - 'e07260', - 'e07300', - 'e07400', - 'e07600', - 'e09700', - 'e09800', - 'e09900', - 'e11200', - 'e17500', - 'e18400', - 'e18500', - 'e19200', - 'e19800', - 'e20100', - 'e20400', - 'g20500', - 'e24515', - 'e24518', - 'e26270', - 'e27200', - 'e32800', - 'e58990', - 'e62900', - 'e87530', - 'elderly_dependents', - 'f2441', - 'f6251', - 'a_lineno', - 'ffpos', - 'fips', - 'h_seq', - 'data_source', - 'k1bx14p', - 'k1bx14s', - 'mcaid_ben', - 'mcare_ben', - 'n24', - 'nu06', - 'nu13', - 'nu18', - 'n1820', - 'n21', - 'other_ben', - 'p08000', - 'p22250', - 'p23250', - 'e87521', - 's006', - 'snap_ben', - 'housing_ben', - 'ssi_ben', - 'tanf_ben', - 'vet_ben', - 'wic_ben', - 'PT_SSTB_income', - 'PT_binc_w2_wages', - 'PT_ubia_property' + "DSI", + "EIC", + "FLPDYR", + "MARS", + "MIDR", + "RECID", + "XTOT", + "age_head", + "age_spouse", + "agi_bin", + "blind_head", + "blind_spouse", + "cmbtp", + "e00200", + "e00200p", + "e00200s", + "pencon_p", + "pencon_s", + "e00300", + "e00400", + "e00600", + "e00650", + "e00700", + "e00800", + "e00900", + "e00900p", + "e00900s", + "e01100", + "e01200", + "e01400", + "e01500", + "e01700", + "e02000", + "e02100", + "e02100p", + "e02100s", + "e02300", + "e02400", + "e03150", + "e03210", + "e03220", + "e03230", + "e03240", + "e03270", + "e03290", + "e03300", + "e03400", + "e03500", + "e07240", + "e07260", + "e07300", + "e07400", + "e07600", + "e09700", + "e09800", + "e09900", + "e11200", + "e17500", + "e18400", + "e18500", + "e19200", + "e19800", + "e20100", + "e20400", + "g20500", + "e24515", + "e24518", + "e26270", + "e27200", + "e32800", + "e58990", + "e62900", + "e87530", + "elderly_dependents", + "f2441", + "f6251", + "a_lineno", + "ffpos", + "fips", + "h_seq", + "data_source", + "k1bx14p", + "k1bx14s", + "mcaid_ben", + "mcare_ben", + "n24", + "nu06", + "nu13", + "nu18", + "n1820", + "n21", + "other_ben", + "p08000", + "p22250", + "p23250", + "e87521", + "s006", + "snap_ben", + "housing_ben", + "ssi_ben", + "tanf_ben", + "vet_ben", + "wic_ben", + "PT_SSTB_income", + "PT_binc_w2_wages", + "PT_ubia_property", ] TAXCALC_OUTPUT_VARIABLES = [ - 'niit', - 'combined', - 'earned', - 'earned_p', - 'earned_s', - 'was_plus_sey_p', - 'was_plus_sey_s', - 'eitc', - 'rptc', - 'rptc_p', - 'rptc_s', - 'exact', - 'expanded_income', - 'iitax', - 'num', - 'othertaxes', - 'payrolltax', - 'refund', - 'sep', - 'sey', - 'standard', - 'surtax', - 'taxbc', - 'c00100', - 'c01000', - 'c02500', - 'c02900', - 'c03260', - 'c04470', - 'c04600', - 'qbided', - 'c04800', - 'c05200', - 'c05700', - 'c05800', - 'c07100', - 'c07180', - 'CDCC_refund', - 'c07200', - 'c07220', - 'c07230', - 'c07240', - 'c07260', - 'c07300', - 'c07400', - 'c07600', - 'c08000', - 'c09200', - 'c09600', - 'c10960', - 'c11070', - 'c17000', - 'e17500_capped', - 'c18300', - 'e18400_capped', - 'e18500_capped', - 'c19200', - 'e19200_capped', - 'c19700', - 'e19800_capped', - 'e20100_capped', - 'c20500', - 'g20500_capped', - 'c20800', - 'e20400_capped', - 'c21040', - 'c21060', - 'c23650', - 'c59660', - 'c62100', - 'c87668', - 'care_deduction', - 'ctc_new', - 'odc', - 'personal_refundable_credit', - 'recovery_rebate_credit', - 'personal_nonrefundable_credit', - 'charity_credit', - 'dwks10', - 'dwks13', - 'dwks14', - 'dwks19', - 'fstax', - 'invinc_agi_ec', - 'invinc_ec_base', - 'lumpsum_tax', - 'pre_c04600', - 'codtc_limited', - 'ptax_amc', - 'ptax_oasdi', - 'ptax_was', - 'setax', - 'ymod', - 'ymod1', - 'ubi', - 'taxable_ubi', - 'nontaxable_ubi', - 'mtr_paytax', - 'mtr_inctax', - 'aftertax_income', - 'benefit_cost_total', - 'benefit_value_total', + "niit", + "combined", + "earned", + "earned_p", + "earned_s", + "was_plus_sey_p", + "was_plus_sey_s", + "eitc", + "rptc", + "rptc_p", + "rptc_s", + "exact", + "expanded_income", + "iitax", + "num", + "othertaxes", + "payrolltax", + "refund", + "sep", + "sey", + "standard", + "surtax", + "taxbc", + "c00100", + "c01000", + "c02500", + "c02900", + "c03260", + "c04470", + "c04600", + "qbided", + "c04800", + "c05200", + "c05700", + "c05800", + "c07100", + "c07180", + "CDCC_refund", + "c07200", + "c07220", + "c07230", + "c07240", + "c07260", + "c07300", + "c07400", + "c07600", + "c08000", + "c09200", + "c09600", + "c10960", + "c11070", + "c17000", + "e17500_capped", + "c18300", + "e18400_capped", + "e18500_capped", + "c19200", + "e19200_capped", + "c19700", + "e19800_capped", + "e20100_capped", + "c20500", + "g20500_capped", + "c20800", + "e20400_capped", + "c21040", + "c21060", + "c23650", + "c59660", + "c62100", + "c87668", + "care_deduction", + "ctc_new", + "odc", + "personal_refundable_credit", + "recovery_rebate_credit", + "personal_nonrefundable_credit", + "charity_credit", + "dwks10", + "dwks13", + "dwks14", + "dwks19", + "fstax", + "invinc_agi_ec", + "invinc_ec_base", + "lumpsum_tax", + "pre_c04600", + "codtc_limited", + "ptax_amc", + "ptax_oasdi", + "ptax_was", + "setax", + "ymod", + "ymod1", + "ubi", + "taxable_ubi", + "nontaxable_ubi", + "mtr_paytax", + "mtr_inctax", + "aftertax_income", + "benefit_cost_total", + "benefit_value_total", ] -parser = ArgumentParser(description="Utility to run taxcalc on a single tax unit") -for variable in TAXCALC_INPUT_VARIABLES: - parser.add_argument('--' + variable, required=variable in TAXCALC_REQUIRED_INPUTS) -parser.add_argument('--output', nargs='+', default=TAXCALC_OUTPUT_VARIABLES) -args = parser.parse_args() +def main(args): + output_dir = Path("./taxcalc_output") + output_dir.mkdir(exist_ok=True) + pd.DataFrame( + { + variable: [getattr(args, variable)] + for variable in TAXCALC_INPUT_VARIABLES + if getattr(args, variable) is not None + } + ).to_csv("taxcalc_output/input.csv", index=False) + process = subprocess.Popen( + "tc taxcalc_output/input.csv 2020 --dump --outdir taxcalc_output", + shell=True, + stdout=subprocess.PIPE, + ) + process.wait() + result = pd.read_csv(output_dir / "input-20-#-#-#.csv") + output = {} + for col in result.columns: + if col in args.output or args.output == []: + output[col] = float(result[col].iloc[0]) + yaml.dump(output, sys.stdout) + -output_dir = Path("./taxcalc_output") -output_dir.mkdir(exist_ok=True) -pd.DataFrame({variable: [getattr(args, variable)] for variable in TAXCALC_INPUT_VARIABLES if getattr(args, variable) is not None}).to_csv("taxcalc_output/input.csv", index=False) -process = subprocess.Popen("tc taxcalc_output/input.csv 2020 --dump --outdir taxcalc_output", shell=True, stdout=subprocess.PIPE) -process.wait() -result = pd.read_csv(output_dir / "input-20-#-#-#.csv") -output = {} -for col in result.columns: - if col in args.output or args.output == []: - output[col] = float(result[col].iloc[0]) -yaml.dump(output, sys.stdout) \ No newline at end of file +if __name__ == "__main__": + parser = ArgumentParser( + description="Utility to run taxcalc on a single tax unit" + ) + for variable in TAXCALC_INPUT_VARIABLES: + parser.add_argument( + "--" + variable, required=variable in TAXCALC_REQUIRED_INPUTS + ) + parser.add_argument( + "--output", nargs="+", default=TAXCALC_OUTPUT_VARIABLES + ) + args = parser.parse_args() + main(args) diff --git a/openfisca_us/tools/dev/run_taxcalc_unit_test.py b/openfisca_us/tools/dev/run_taxcalc_unit_test.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/openfisca_us/variables/irs/outputs.py b/openfisca_us/variables/irs/outputs.py index 23c6a13716f..741afcd873b 100644 --- a/openfisca_us/variables/irs/outputs.py +++ b/openfisca_us/variables/irs/outputs.py @@ -1199,6 +1199,7 @@ def formula(tax_unit, period, parameters): ) * (mars == mars.possible_values.SEPARATE) ) + print(separate_addition) return c62100 + separate_addition From b289227057e7ad11f4448036988655fb745355a4 Mon Sep 17 00:00:00 2001 From: Nikhil Date: Tue, 28 Dec 2021 00:22:25 +0000 Subject: [PATCH 7/9] Remove incorrect test --- .../tests/policy/baseline/irs/c09600.yaml | 22 ------------------- openfisca_us/variables/irs/outputs.py | 1 - 2 files changed, 23 deletions(-) diff --git a/openfisca_us/tests/policy/baseline/irs/c09600.yaml b/openfisca_us/tests/policy/baseline/irs/c09600.yaml index a3196610632..4b59f659731 100644 --- a/openfisca_us/tests/policy/baseline/irs/c09600.yaml +++ b/openfisca_us/tests/policy/baseline/irs/c09600.yaml @@ -7,25 +7,3 @@ e00200: 2_000 output: c09600: 0 -- name: Single filer, high earnings, young - period: 2020 - absolute_error_margin: 0 - input: - c62100: 2100000.0 - mars: SINGLE - age_head: 0 - age_spouse: 0 - filer_earned: 100000.0 - sep: true - dwks10: 2000000.0 - dwks13: 2000000.0 - dwks14: 87612.12 - dwks19: 87612.12 - filer_e24515: 0 - filer_e07300: 0 - f6251: 0 - filer_e62900: 0 - taxbc: 397430.2 - c05700: 0 - output: - c09600: 10888.3 diff --git a/openfisca_us/variables/irs/outputs.py b/openfisca_us/variables/irs/outputs.py index 741afcd873b..23c6a13716f 100644 --- a/openfisca_us/variables/irs/outputs.py +++ b/openfisca_us/variables/irs/outputs.py @@ -1199,7 +1199,6 @@ def formula(tax_unit, period, parameters): ) * (mars == mars.possible_values.SEPARATE) ) - print(separate_addition) return c62100 + separate_addition From 922100f7e65dbdbd4f7efc8425624a492939ae9f Mon Sep 17 00:00:00 2001 From: Nikhil Date: Tue, 28 Dec 2021 00:23:39 +0000 Subject: [PATCH 8/9] Remove old scripts --- openfisca_us/tools/dev/run_taxcalc_test.py | 0 openfisca_us/tools/dev/run_taxcalc_unit_test.py | 0 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 openfisca_us/tools/dev/run_taxcalc_test.py delete mode 100644 openfisca_us/tools/dev/run_taxcalc_unit_test.py diff --git a/openfisca_us/tools/dev/run_taxcalc_test.py b/openfisca_us/tools/dev/run_taxcalc_test.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/openfisca_us/tools/dev/run_taxcalc_unit_test.py b/openfisca_us/tools/dev/run_taxcalc_unit_test.py deleted file mode 100644 index e69de29bb2d..00000000000 From a56874a5ce0292b48fbfe040ef14c5280f0da629 Mon Sep 17 00:00:00 2001 From: Nikhil Date: Tue, 28 Dec 2021 00:28:04 +0000 Subject: [PATCH 9/9] Update changelog and version --- CHANGELOG.md | 5 +++++ setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f0f95b5819..e9a0bb87070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,3 +20,8 @@ ### 0.5.0 * Added Medicaid Tests + Income Threshold Formula for California + +### 0.6.0 + +* Added Alternative Minimum Tax (AMT) income and liability logic +* Added development tools for auto-generating unit tests for Tax-Calculator functions diff --git a/setup.py b/setup.py index 7442ff90f2e..0094cae863d 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ setup( name="OpenFisca-US", - version="0.5.0", + version="0.6.0", author="Nikhil Woodruff", author_email="nikhil@policyengine.org", classifiers=[