Repository: PolicyEngine/policyengine-us (language: python)

Issue:
Bug in `meets_school_meal_categorical_eligibility`
Visible from this household: https://policyengine.org/us/household?focus=householdOutput.earnings&household=49385

![image](https://github.com/user-attachments/assets/2bacc839-0b68-45f5-9b07-37da2ec88ffd)

A few things to note:
1. This is immediately reproducible and confirmed across two systems running two different browsers
2. This is very likely a duplicate of https://github.com/PolicyEngine/policyengine-api/issues/1792, but not 100% sure - here and there, there's a difference of $127 between the line and dot
3. I believe this issue is separate from the one solved through https://github.com/PolicyEngine/policyengine-core/pull/288, https://github.com/PolicyEngine/policyengine-core/pull/294, and related fixes in the API
Bug in `meets_school_meal_categorical_eligibility`
Visible from this household: https://policyengine.org/us/household?focus=householdOutput.earnings&household=49385

![image](https://github.com/user-attachments/assets/2bacc839-0b68-45f5-9b07-37da2ec88ffd)

A few things to note:
1. This is immediately reproducible and confirmed across two systems running two different browsers
2. This is very likely a duplicate of https://github.com/PolicyEngine/policyengine-api/issues/1792, but not 100% sure - here and there, there's a difference of $127 between the line and dot
3. I believe this issue is separate from the one solved through https://github.com/PolicyEngine/policyengine-core/pull/288, https://github.com/PolicyEngine/policyengine-core/pull/294, and related fixes in the API


Discussion / hints:
This stems from differing calculations between households with axes and households without on school meal subsidies, particularly the following values (as visible in the "Varying your earnings" component):
1. Free and reduced price school meals
2. School meal subsidies per child per day
3. Reduced price school meals
4. Free school meals (3 and 4 are exact inverses of each other)
5. School meal tier (this is an enum, so it's possible that it just erroneously displays 0 for all outputs, but I would expect it to go 0-1-2 for the three tiers)

It's looking like the error will be in the `-us` code establishing the free and reduced school meal program. If so, will transfer this error to that repo.
The cause of this issue is the following:
1. `meets_school_meal_categorial_eligibility` determines whether a household should qualify for free (and not reduced or none) school meals based on the fact that they receive another government program, as opposed to their income
2. The existing formula takes a scalar, sums its categorial benefits, then (and this is the problem) returns `np.any()` on the scalar. For all axes that we'd ever pass via the API, where there's a market income of $0 at the bottom end, this scalar will always contain received benefits at the low end, meaning `np.any()` will always return `True`.
3. This, in turn, will give all households (all the way up to $200k a year in market income) a `school_meal_tier` value of `FREE`, as opposed to `REDUCED` (which is where the household in the example at the top of this issue should be) or `NONE`.
4. This, then, awards all households free school meals instead of reduced (which is a flat $127 less, hence the common disparity across households around $28k a year in income) or free (which would be like $1,000, but we simulate these households less often).
This stems from differing calculations between households with axes and households without on school meal subsidies, particularly the following values (as visible in the "Varying your earnings" component):
1. Free and reduced price school meals
2. School meal subsidies per child per day
3. Reduced price school meals
4. Free school meals (3 and 4 are exact inverses of each other)
5. School meal tier (this is an enum, so it's possible that it just erroneously displays 0 for all outputs, but I would expect it to go 0-1-2 for the three tiers)

It's looking like the error will be in the `-us` code establishing the free and reduced school meal program. If so, will transfer this error to that repo.
The cause of this issue is the following:
1. `meets_school_meal_categorial_eligibility` determines whether a household should qualify for free (and not reduced or none) school meals based on the fact that they receive another government program, as opposed to their income
2. The existing formula takes a scalar, sums its categorial benefits, then (and this is the problem) returns `np.any()` on the scalar. For all axes that we'd ever pass via the API, where there's a market income of $0 at the bottom end, this scalar will always contain received benefits at the low end, meaning `np.any()` will always return `True`.
3. This, in turn, will give all households (all the way up to $200k a year in market income) a `school_meal_tier` value of `FREE`, as opposed to `REDUCED` (which is where the household in the example at the top of this issue should be) or `NONE`.
4. This, then, awards all households free school meals instead of reduced (which is a flat $127 less, hence the common disparity across households around $28k a year in income) or free (which would be like $1,000, but we simulate these households less often).

Fix patch:
diff --git a/changelog_entry.yaml b/changelog_entry.yaml
index e69de29bb2d..99484778e0a 100644
--- a/changelog_entry.yaml
+++ b/changelog_entry.yaml
@@ -0,0 +1,6 @@
+- bump: patch
+  changes:
+    fixed:
+    - Corrected meets_school_meal_categorical_eligilibity to properly calculate eligibility for vectorized inputs.
+    added:
+    - Test for meets_school_meal_categorical_eligibility with vectorized inputs.
diff --git a/policyengine_us/variables/gov/usda/school_meals/meets_school_meal_categorical_eligibility.py b/policyengine_us/variables/gov/usda/school_meals/meets_school_meal_categorical_eligibility.py
index a6baa464a28..7076727c35f 100644
--- a/policyengine_us/variables/gov/usda/school_meals/meets_school_meal_categorical_eligibility.py
+++ b/policyengine_us/variables/gov/usda/school_meals/meets_school_meal_categorical_eligibility.py
@@ -8,8 +8,4 @@ class meets_school_meal_categorical_eligibility(Variable):
     documentation = "Whether this SPM unit is eligible for free school meal via participation in other programs"
     definition_period = YEAR
     reference = "https://www.law.cornell.edu/cfr/text/7/245.2"
-
-    def formula(spm_unit, period, parameters):
-        p = parameters(period).gov.usda.school_meals
-        programs = add(spm_unit, period, p.categorical_eligibility)
-        return np.any(programs)
+    adds = "gov.usda.school_meals.categorical_eligibility"


Test patch:
diff --git a/policyengine_us/tests/policy/baseline/gov/usda/school_meals/meets_school_meal_categorical_eligibility.yaml b/policyengine_us/tests/policy/baseline/gov/usda/school_meals/meets_school_meal_categorical_eligibility.yaml
index 5735ce27dfd..4876bb6399a 100644
--- a/policyengine_us/tests/policy/baseline/gov/usda/school_meals/meets_school_meal_categorical_eligibility.yaml
+++ b/policyengine_us/tests/policy/baseline/gov/usda/school_meals/meets_school_meal_categorical_eligibility.yaml
@@ -81,3 +81,12 @@
       is_head_start_eligible: false
   output:
       meets_school_meal_categorical_eligibility: false
+
+- name: Vectorized operations
+  period: 2024
+  input:
+      snap: [1, 0, 0, 0, 0, 0, 0, 0]
+      fdpir: [0, 1, 1, 0, 0, 0, 0, 0]
+      is_homeless: [False, False, True, True, False, False, False, False]
+  output:
+      meets_school_meal_categorical_eligibility: [True, True, True, True, False, False, False, False]