2929 _load_raw_spm_capped_housing_subsidy ,
3030 _apply_post_processing ,
3131 _build_clone_test_frame ,
32+ _build_ssi_disability_clone_receiver ,
3233 _cps_clone_feature_variables_for_data ,
3334 _derive_overtime_occupation_inputs ,
3435 _impute_clone_cps_features ,
@@ -216,6 +217,8 @@ def test_clone_feature_candidates_include_person_level_cps_only_flags(self):
216217 "household_weight" : {2024 : np .array ([1.0 , 1.0 , 0.0 , 0.0 ])},
217218 "state_fips" : {2024 : np .array ([6 , 36 , 6 , 36 ])},
218219 "employment_income" : {2024 : np .array ([1.0 , 2.0 , 3.0 , 4.0 ])},
220+ "is_household_head" : {2024 : np .array ([True , True , True , True ])},
221+ "is_tax_unit_head" : {2024 : np .array ([True , False , True , False ])},
219222 "is_disabled" : {2024 : np .array ([True , False , True , False ])},
220223 "difficulty_hearing" : {2024 : np .array ([False , True , False , True ])},
221224 "meets_ssi_disability_criteria" : {
@@ -232,6 +235,8 @@ def test_clone_feature_candidates_include_person_level_cps_only_flags(self):
232235 assert "household_weight" not in result
233236 assert "state_fips" not in result
234237 assert "employment_income" not in result
238+ assert "is_household_head" not in result
239+ assert "is_tax_unit_head" not in result
235240 assert "meets_ssi_disability_criteria" not in result
236241
237242 def test_spm_threshold_is_formula_output_not_qrf_imputed (self ):
@@ -881,6 +886,83 @@ def test_leaves_data_unchanged_when_pe_us_lacks_llc_inputs(self, monkeypatch):
881886
882887
883888class TestStage2PostProcessing :
889+ def test_ssi_disability_clone_receiver_uses_stage2_disability_benefits (self ):
890+ data = {
891+ "person_id" : {2024 : np .array ([1 , 2 , 101 , 102 ])},
892+ "difficulty_hearing" : {2024 : np .array ([False , False , True , False ])},
893+ }
894+ predictions = pd .DataFrame ({"disability_benefits" : [0.0 , 500.0 ]})
895+ x_test = pd .DataFrame (
896+ {
897+ "age" : [40 , 40 ],
898+ "is_male" : [False , True ],
899+ "employment_income" : [0.0 , 0.0 ],
900+ }
901+ )
902+
903+ result = _build_ssi_disability_clone_receiver (
904+ predictions ,
905+ x_test ,
906+ data ,
907+ 2024 ,
908+ )
909+
910+ np .testing .assert_array_equal (result ["difficulty_hearing" ], [True , False ])
911+ np .testing .assert_array_equal (result ["has_disability_income" ], [False , True ])
912+ np .testing .assert_array_equal (result ["is_female" ], [True , False ])
913+
914+ def test_post_processing_replaces_generic_ssi_disability_predictions (
915+ self ,
916+ monkeypatch ,
917+ ):
918+ class AlwaysTrueModel :
919+ def predict (self , X_test ):
920+ return pd .DataFrame (
921+ {
922+ "meets_ssi_disability_criteria" : np .ones (
923+ len (X_test ),
924+ dtype = bool ,
925+ )
926+ }
927+ )
928+
929+ monkeypatch .setattr (
930+ extended_cps_module ,
931+ "get_ssi_disability_model" ,
932+ lambda time_period : AlwaysTrueModel (),
933+ )
934+ data = {
935+ "person_id" : {2024 : np .arange (6 )},
936+ "difficulty_walking_or_climbing_stairs" : {
937+ 2024 : np .array ([False , False , False , True , False , False ])
938+ },
939+ }
940+ predictions = pd .DataFrame (
941+ {
942+ "meets_ssi_disability_criteria" : [False , False , True ],
943+ "disability_benefits" : [0.0 , 500.0 , 0.0 ],
944+ }
945+ )
946+ x_test = pd .DataFrame (
947+ {
948+ "age" : [40 , 40 , 40 ],
949+ "is_male" : [False , True , False ],
950+ "employment_income" : [0.0 , 0.0 , 0.0 ],
951+ }
952+ )
953+
954+ result = _apply_post_processing (
955+ predictions = predictions ,
956+ X_test = x_test ,
957+ time_period = 2024 ,
958+ data = data ,
959+ )
960+
961+ np .testing .assert_array_equal (
962+ result ["meets_ssi_disability_criteria" ],
963+ np .array ([True , True , False ]),
964+ )
965+
884966 def test_splice_replaces_clone_half_ssi_disability_criteria (self , monkeypatch ):
885967 import policyengine_us
886968
0 commit comments