Skip to content

Commit 5320bec

Browse files
Migrate to APIv2 for economic impacts (#2398)
* Migrate to APIv2 for economic impacts Fixes #2397 * Re-add v1 impacts, just turn off by default * Lint
1 parent 3e02d82 commit 5320bec

3 files changed

Lines changed: 39 additions & 126 deletions

File tree

changelog_entry.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- bump: minor
2+
changes:
3+
changed:
4+
- Economic impacts from APIv1 to APIv2

gcp/policyengine_api/app.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
runtime: custom
22
env: flex
33
resources:
4-
cpu: 24
5-
memory_gb: 128
6-
disk_size_gb: 128
4+
cpu: 4
5+
memory_gb: 16
6+
disk_size_gb: 64
77
automatic_scaling:
88
min_num_instances: 1
99
max_num_instances: 1

policyengine_api/jobs/calculate_economy_simulation_job.py

Lines changed: 32 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,18 @@
3737
CPS = "hf://policyengine/policyengine-us-data/cps_2023.h5"
3838
POOLED_CPS = "hf://policyengine/policyengine-us-data/pooled_3_year_cps_2023.h5"
3939

40-
check_against_api_v2 = (
41-
os.environ.get("GOOGLE_APPLICATION_CREDENTIALS") is not None
42-
)
40+
use_api_v2 = os.environ.get("GOOGLE_APPLICATION_CREDENTIALS") is not None
4341

44-
if not check_against_api_v2:
42+
if not use_api_v2:
4543
logging.warn(
46-
"Didn't find any GOOGLE_APPLICATION_CREDENTIALS, so will not check results for matches against APIv2."
44+
"Didn't find any GOOGLE_APPLICATION_CREDENTIALS, so will not use APIv2."
4745
)
4846

4947

5048
class CalculateEconomySimulationJob(BaseJob):
5149
def __init__(self):
5250
super().__init__()
53-
if check_against_api_v2:
51+
if use_api_v2:
5452
self.api_v2 = SimulationAPIv2()
5553

5654
def run(
@@ -152,7 +150,7 @@ def run(
152150
comment("Computing baseline")
153151

154152
# Kick off APIv2 job
155-
if check_against_api_v2:
153+
if use_api_v2:
156154
input_data = {
157155
"country": country_id,
158156
"scope": "macro",
@@ -162,51 +160,35 @@ def run(
162160
}
163161
execution = self.api_v2.run(input_data)
164162

165-
# Compute baseline economy
166-
baseline_economy = self._compute_economy(
167-
country_id=country_id,
168-
region=region,
169-
dataset=dataset,
170-
time_period=time_period,
171-
options=options,
172-
policy_json=baseline_policy,
173-
)
174-
comment("Computing reform")
175-
176-
# Compute reform economy
177-
reform_economy = self._compute_economy(
178-
country_id=country_id,
179-
region=region,
180-
dataset=dataset,
181-
time_period=time_period,
182-
options=options,
183-
policy_json=reform_policy,
184-
)
185-
186-
baseline_economy = baseline_economy["result"]
187-
reform_economy = reform_economy["result"]
188-
comment("Comparing baseline and reform")
189-
impact = compare_economic_outputs(
190-
baseline_economy, reform_economy, country_id=country_id
191-
)
163+
impact = self.api_v2.wait_for_completion(execution)
164+
else:
165+
# Compute baseline economy
166+
baseline_economy = self._compute_economy(
167+
country_id=country_id,
168+
region=region,
169+
dataset=dataset,
170+
time_period=time_period,
171+
options=options,
172+
policy_json=baseline_policy,
173+
)
174+
comment("Computing reform")
192175

193-
# Wait for APIv2 job to complete
194-
if check_against_api_v2:
195-
result = self.api_v2.wait_for_completion(execution)
196-
if result is None:
197-
print("APIv2 COMPARISON failed: result is not JSON.")
198-
else:
199-
try:
200-
print(
201-
f"APIv2 COMPARISON: match={is_similar(result, json.loads(json.dumps(impact)))}"
202-
)
203-
except:
204-
print("APIv2 COMPARISON: ERROR COMPARING", result)
176+
# Compute reform economy
177+
reform_economy = self._compute_economy(
178+
country_id=country_id,
179+
region=region,
180+
dataset=dataset,
181+
time_period=time_period,
182+
options=options,
183+
policy_json=reform_policy,
184+
)
205185

206-
if options.get("apiv2", False):
207-
# If the APIv2 job was successful, use its result
208-
if result is not None:
209-
impact = result
186+
baseline_economy = baseline_economy["result"]
187+
reform_economy = reform_economy["result"]
188+
comment("Comparing baseline and reform")
189+
impact = compare_economic_outputs(
190+
baseline_economy, reform_economy, country_id=country_id
191+
)
210192

211193
# Finally, update all reform impact rows with the same baseline and reform policy IDs
212194
reform_impacts_service.set_complete_reform_impact(
@@ -468,79 +450,6 @@ def _compute_cliff_impacts(self, simulation: Microsimulation) -> Dict:
468450
}
469451

470452

471-
def is_similar(x, y, parent_name: str = "") -> bool:
472-
if x is None or x == {}:
473-
if y is None or y == {}:
474-
return True
475-
# Handle None values
476-
if x is None or y is None:
477-
equal = x is y
478-
if not equal:
479-
print(f"Not equal: {x} vs {y} in {parent_name}")
480-
return equal
481-
482-
# Handle different types
483-
if type(x) != type(y):
484-
if float in ((type(x), type(y))) and int in ((type(x), type(y))):
485-
pass
486-
else:
487-
print(f"Different types: {type(x)} vs {type(y)} in {parent_name}")
488-
return False
489-
490-
# Handle numeric values
491-
if isinstance(x, (int, float)):
492-
close = (abs(y - x) < 1e-2) or (abs(y - x) / abs(x) < 0.01)
493-
if not close:
494-
print(f"Not close: {x} vs {y} in {parent_name}")
495-
return close
496-
497-
# Handle boolean values
498-
elif isinstance(x, bool):
499-
equal = x == y
500-
if not equal:
501-
print(f"Not equal: {x} vs {y} in {parent_name}")
502-
return equal
503-
504-
# Handle string values
505-
elif isinstance(x, str):
506-
equal = x == y
507-
if not equal:
508-
print(f"Not equal: {x} vs {y} in {parent_name}")
509-
return equal
510-
511-
# Handle dictionaries
512-
elif isinstance(x, dict):
513-
# Check for keys in both dictionaries
514-
all_keys = set(x.keys()) | set(y.keys())
515-
for k in all_keys:
516-
if k not in x:
517-
print(f"Key {k} missing in first dict in {parent_name}")
518-
return False
519-
if k not in y:
520-
print(f"Key {k} missing in second dict in {parent_name}")
521-
return False
522-
if not is_similar(x[k], y[k], parent_name=parent_name + "/" + k):
523-
return False
524-
return True
525-
526-
# Handle lists
527-
elif isinstance(x, list):
528-
if len(x) != len(y):
529-
print(f"Different lengths: {len(x)} vs {len(y)} in {parent_name}")
530-
return False
531-
return all(
532-
is_similar(x[i], y[i], parent_name=parent_name + f"[{i}]")
533-
for i in range(len(x))
534-
)
535-
536-
# Handle other types
537-
else:
538-
equal = x == y
539-
if not equal:
540-
print(f"Not equal: {x} vs {y} in {parent_name}")
541-
return equal
542-
543-
544453
class SimulationAPIv2:
545454
project: str
546455
location: str

0 commit comments

Comments
 (0)