Compare outcomes for large population scenarios

Use Simulation.calculate_economy_comparison() to use PolicyEngine’s tax-benefit model to compare how taxes, benefits and other household properties change under a reform scenario. This notebook demonstrates how to use this function.

from policyengine import Simulation

sim = Simulation(
    scope="macro", # Required for this
    country="us", # or "us"
    time_period=2025, # Defaults to 2025
    reform={
        "gov.usda.snap.income.deductions.earned_income": {
            "2025": 0.05
        }
    }
)

sim.calculate_economy_comparison()
/opt/hostedtoolcache/Python/3.11.12/x64/lib/python3.11/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm
No data provided, using default dataset: gs://policyengine-us-data/cps_2023.h5
Downloading cps_2023.h5 from bucket policyengine-us-data
INFO:root:Using Google Cloud Storage for download.
---------------------------------------------------------------------------
DefaultCredentialsError                   Traceback (most recent call last)
Cell In[1], line 3
      1 from policyengine import Simulation
----> 3 sim = Simulation(
      4     scope="macro", # Required for this
      5     country="us", # or "us"
      6     time_period=2025, # Defaults to 2025
      7     reform={
      8         "gov.usda.snap.income.deductions.earned_income": {
      9             "2025": 0.05
     10         }
     11     }
     12 )
     14 sim.calculate_economy_comparison()

File ~/work/policyengine.py/policyengine.py/policyengine/simulation.py:109, in Simulation.__init__(self, **options)
    105 if not isinstance(self.options.data, dict) and not isinstance(
    106     self.options.data, Dataset
    107 ):
    108     logging.debug("Loading data")
--> 109     self._set_data(self.options.data)
    110     logging.info("Data loaded")
    111 self._initialise_simulations()

File ~/work/policyengine.py/policyengine.py/policyengine/simulation.py:171, in Simulation._set_data(self, file_address)
    166     version = None
    168 else:
    169     # All official PolicyEngine datasets are stored in GCS;
    170     # load accordingly
--> 171     filename, version = self._set_data_from_gs(file_address)
    172     self.data_version = version
    174 time_period = self._set_data_time_period(file_address)

File ~/work/policyengine.py/policyengine.py/policyengine/simulation.py:405, in Simulation._set_data_from_gs(self, file_address)
    401 version = self.options.data_version
    403 print(f"Downloading {filename} from bucket {bucket}", file=sys.stderr)
--> 405 filepath, version = download(
    406     filepath=filename,
    407     gcs_bucket=bucket,
    408     version=version,
    409     return_version=True,
    410 )
    412 return filename, version

File ~/work/policyengine.py/policyengine.py/policyengine/utils/data_download.py:17, in download(filepath, gcs_bucket, version, return_version)
     10 def download(
     11     filepath: str,
     12     gcs_bucket: str,
     13     version: Optional[str] = None,
     14     return_version: bool = False,
     15 ) -> Tuple[str, str] | str:
     16     logging.info("Using Google Cloud Storage for download.")
---> 17     downloaded_version = download_file_from_gcs(
     18         bucket_name=gcs_bucket,
     19         file_name=filepath,
     20         destination_path=filepath,
     21         version=version,
     22     )
     23     if return_version:
     24         return filepath, downloaded_version

File ~/work/policyengine.py/policyengine.py/policyengine/utils/google_cloud_bucket.py:41, in download_file_from_gcs(bucket_name, file_name, destination_path, version)
     23 def download_file_from_gcs(
     24     bucket_name: str,
     25     file_name: str,
     26     destination_path: str,
     27     version: Optional[str] = None,
     28 ) -> str | None:
     29     """
     30     Download a file from Google Cloud Storage to a local path.
     31 
   (...)
     38         version (str): The version of the file that was downloaded, if available.
     39     """
---> 41     version = _get_client().download(
     42         bucket_name,
     43         file_name,
     44         Path(destination_path),
     45         version=version,
     46         return_version=True,
     47     )
     48     return version

File ~/work/policyengine.py/policyengine.py/policyengine/utils/google_cloud_bucket.py:14, in _get_client()
     12 if _caching_client is not None:
     13     return _caching_client
---> 14 _caching_client = CachingGoogleStorageClient()
     15 return _caching_client

File ~/work/policyengine.py/policyengine.py/policyengine/utils/data/caching_google_storage_client.py:19, in CachingGoogleStorageClient.__init__(self)
     18 def __init__(self):
---> 19     self.client = SimplifiedGoogleStorageClient()
     20     self.cache = diskcache.Cache()

File ~/work/policyengine.py/policyengine.py/policyengine/utils/data/simplified_google_storage_client.py:19, in SimplifiedGoogleStorageClient.__init__(self)
     18 def __init__(self):
---> 19     self.client = Client()

File /opt/hostedtoolcache/Python/3.11.12/x64/lib/python3.11/site-packages/google/cloud/storage/client.py:247, in Client.__init__(self, project, credentials, _http, client_info, client_options, use_auth_w_custom_endpoint, extra_headers, api_key)
    244             no_project = True
    245             project = "<none>"
--> 247 super(Client, self).__init__(
    248     project=project,
    249     credentials=credentials,
    250     client_options=client_options,
    251     _http=_http,
    252 )
    254 # Validate that the universe domain of the credentials matches the
    255 # universe domain of the client.
    256 if self._credentials.universe_domain != self.universe_domain:

File /opt/hostedtoolcache/Python/3.11.12/x64/lib/python3.11/site-packages/google/cloud/client/__init__.py:338, in ClientWithProject.__init__(self, project, credentials, client_options, _http)
    337 def __init__(self, project=None, credentials=None, client_options=None, _http=None):
--> 338     _ClientProjectMixin.__init__(self, project=project, credentials=credentials)
    339     Client.__init__(
    340         self, credentials=credentials, client_options=client_options, _http=_http
    341     )

File /opt/hostedtoolcache/Python/3.11.12/x64/lib/python3.11/site-packages/google/cloud/client/__init__.py:286, in _ClientProjectMixin.__init__(self, project, credentials)
    283     project = getattr(credentials, "project_id", None)
    285 if project is None:
--> 286     project = self._determine_default(project)
    288 if project is None:
    289     raise EnvironmentError(
    290         "Project was not passed and could not be "
    291         "determined from the environment."
    292     )

File /opt/hostedtoolcache/Python/3.11.12/x64/lib/python3.11/site-packages/google/cloud/client/__init__.py:305, in _ClientProjectMixin._determine_default(project)
    302 @staticmethod
    303 def _determine_default(project):
    304     """Helper:  use default project detection."""
--> 305     return _determine_default_project(project)

File /opt/hostedtoolcache/Python/3.11.12/x64/lib/python3.11/site-packages/google/cloud/_helpers/__init__.py:152, in _determine_default_project(project)
    140 """Determine default project ID explicitly or implicitly as fall-back.
    141 
    142 See :func:`google.auth.default` for details on how the default project
   (...)
    149 :returns: Default project if it can be determined.
    150 """
    151 if project is None:
--> 152     _, project = google.auth.default()
    153 return project

File /opt/hostedtoolcache/Python/3.11.12/x64/lib/python3.11/site-packages/google/auth/_default.py:685, in default(scopes, request, quota_project_id, default_scopes)
    677             _LOGGER.warning(
    678                 "No project ID could be determined. Consider running "
    679                 "`gcloud config set project` or setting the %s "
    680                 "environment variable",
    681                 environment_vars.PROJECT,
    682             )
    683         return credentials, effective_project_id
--> 685 raise exceptions.DefaultCredentialsError(_CLOUD_SDK_MISSING_CREDENTIALS)

DefaultCredentialsError: Your default credentials were not found. To set up Application Default Credentials, see https://cloud.google.com/docs/authentication/external/set-up-adc for more information.

Output schema

calculate_economy_comparison or calculate (when scope=household and reform is not None) return the following schema.

from policyengine.outputs.macro.comparison.calculate_economy_comparison import EconomyComparison

EconomyComparison.model_json_schema()
{'$defs': {'DecileImpacts': {'properties': {'income': {'$ref': '#/$defs/IncomeMeasureSpecificDecileImpacts'},
    'wealth': {'anyOf': [{'$ref': '#/$defs/IncomeMeasureSpecificDecileImpacts'},
      {'type': 'null'}]}},
   'required': ['income', 'wealth'],
   'title': 'DecileImpacts',
   'type': 'object'},
  'FiscalComparison': {'properties': {'baseline': {'$ref': '#/$defs/FiscalSummary'},
    'reform': {'$ref': '#/$defs/FiscalSummary'},
    'change': {'$ref': '#/$defs/FiscalSummary'},
    'relative_change': {'$ref': '#/$defs/FiscalSummary'}},
   'required': ['baseline', 'reform', 'change', 'relative_change'],
   'title': 'FiscalComparison',
   'type': 'object'},
  'FiscalSummary': {'properties': {'tax_revenue': {'title': 'Tax Revenue',
     'type': 'number'},
    'federal_tax': {'title': 'Federal Tax', 'type': 'number'},
    'federal_balance': {'title': 'Federal Balance', 'type': 'number'},
    'state_tax': {'title': 'State Tax', 'type': 'number'},
    'government_spending': {'title': 'Government Spending', 'type': 'number'},
    'tax_benefit_programs': {'additionalProperties': {'type': 'number'},
     'title': 'Tax Benefit Programs',
     'type': 'object'},
    'household_net_income': {'title': 'Household Net Income',
     'type': 'number'}},
   'required': ['tax_revenue',
    'federal_tax',
    'federal_balance',
    'state_tax',
    'government_spending',
    'tax_benefit_programs',
    'household_net_income'],
   'title': 'FiscalSummary',
   'type': 'object'},
  'Headlines': {'properties': {'budgetary_impact': {'title': 'Budgetary Impact',
     'type': 'number'},
    'poverty_impact': {'title': 'Poverty Impact', 'type': 'number'},
    'winner_share': {'title': 'Winner Share', 'type': 'number'}},
   'required': ['budgetary_impact', 'poverty_impact', 'winner_share'],
   'title': 'Headlines',
   'type': 'object'},
  'IncomeMeasureSpecificDecileImpacts': {'properties': {'income_change': {'$ref': '#/$defs/IncomeMeasureSpecificDecileIncomeChange'},
    'winners_and_losers': {'$ref': '#/$defs/IncomeMeasureSpecificDecileWinnersLosers'}},
   'required': ['income_change', 'winners_and_losers'],
   'title': 'IncomeMeasureSpecificDecileImpacts',
   'type': 'object'},
  'IncomeMeasureSpecificDecileIncomeChange': {'properties': {'relative': {'additionalProperties': {'type': 'number'},
     'title': 'Relative',
     'type': 'object'},
    'average': {'additionalProperties': {'type': 'number'},
     'title': 'Average',
     'type': 'object'}},
   'required': ['relative', 'average'],
   'title': 'IncomeMeasureSpecificDecileIncomeChange',
   'type': 'object'},
  'IncomeMeasureSpecificDecileWinnersLosers': {'properties': {'deciles': {'additionalProperties': {'$ref': '#/$defs/IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes'},
     'title': 'Deciles',
     'type': 'object'},
    'all': {'$ref': '#/$defs/IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes'}},
   'required': ['deciles', 'all'],
   'title': 'IncomeMeasureSpecificDecileWinnersLosers',
   'type': 'object'},
  'IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes': {'properties': {'lose_more_than_5_percent_share': {'title': 'Lose More Than 5 Percent Share',
     'type': 'number'},
    'lose_less_than_5_percent_share': {'title': 'Lose Less Than 5 Percent Share',
     'type': 'number'},
    'lose_share': {'title': 'Lose Share', 'type': 'number'},
    'no_change_share': {'title': 'No Change Share', 'type': 'number'},
    'gain_share': {'title': 'Gain Share', 'type': 'number'},
    'gain_less_than_5_percent_share': {'title': 'Gain Less Than 5 Percent Share',
     'type': 'number'},
    'gain_more_than_5_percent_share': {'title': 'Gain More Than 5 Percent Share',
     'type': 'number'}},
   'required': ['lose_more_than_5_percent_share',
    'lose_less_than_5_percent_share',
    'lose_share',
    'no_change_share',
    'gain_share',
    'gain_less_than_5_percent_share',
    'gain_more_than_5_percent_share'],
   'title': 'IncomeMeasureSpecificDecileWinnersLosersGroupOutcomes',
   'type': 'object'},
  'InequalityComparison': {'properties': {'baseline': {'$ref': '#/$defs/InequalitySummary'},
    'reform': {'$ref': '#/$defs/InequalitySummary'},
    'change': {'$ref': '#/$defs/InequalitySummary'},
    'relative_change': {'$ref': '#/$defs/InequalitySummary'}},
   'required': ['baseline', 'reform', 'change', 'relative_change'],
   'title': 'InequalityComparison',
   'type': 'object'},
  'InequalitySummary': {'properties': {'gini': {'title': 'Gini',
     'type': 'number'},
    'top_10_share': {'title': 'Top 10 Share', 'type': 'number'},
    'top_1_share': {'title': 'Top 1 Share', 'type': 'number'}},
   'required': ['gini', 'top_10_share', 'top_1_share'],
   'title': 'InequalitySummary',
   'type': 'object'},
  'LaborSupplyMetricImpact': {'properties': {'elasticity': {'enum': ['income',
      'substitution',
      'all'],
     'title': 'Elasticity',
     'type': 'string'},
    'decile': {'enum': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'all'],
     'title': 'Decile'},
    'unit': {'enum': ['earnings', 'hours'], 'title': 'Unit', 'type': 'string'},
    'baseline': {'title': 'Baseline', 'type': 'number'},
    'reform': {'title': 'Reform', 'type': 'number'},
    'change': {'title': 'Change', 'type': 'number'},
    'relative_change': {'title': 'Relative Change', 'type': 'number'},
    'average_change': {'title': 'Average Change', 'type': 'number'}},
   'required': ['elasticity',
    'decile',
    'unit',
    'baseline',
    'reform',
    'change',
    'relative_change',
    'average_change'],
   'title': 'LaborSupplyMetricImpact',
   'type': 'object'},
  'PovertyRateMetricComparison': {'properties': {'age_group': {'enum': ['child',
      'working_age',
      'senior',
      'all'],
     'title': 'Age Group',
     'type': 'string'},
    'gender': {'enum': ['male', 'female', 'all'],
     'title': 'Gender',
     'type': 'string'},
    'racial_group': {'enum': ['white', 'black', 'hispanic', 'other', 'all'],
     'title': 'Racial Group',
     'type': 'string'},
    'relative': {'title': 'Relative', 'type': 'boolean'},
    'poverty_rate': {'enum': ['regular',
      'deep',
      'uk_hbai_bhc',
      'uk_hbai_bhc_half',
      'us_spm',
      'us_spm_half'],
     'title': 'Poverty Rate',
     'type': 'string'},
    'baseline': {'title': 'Baseline', 'type': 'number'},
    'reform': {'title': 'Reform', 'type': 'number'},
    'change': {'title': 'Change', 'type': 'number'},
    'relative_change': {'title': 'Relative Change', 'type': 'number'}},
   'required': ['age_group',
    'gender',
    'racial_group',
    'relative',
    'poverty_rate',
    'baseline',
    'reform',
    'change',
    'relative_change'],
   'title': 'PovertyRateMetricComparison',
   'type': 'object'}},
 'properties': {'headlines': {'$ref': '#/$defs/Headlines'},
  'fiscal': {'$ref': '#/$defs/FiscalComparison'},
  'inequality': {'$ref': '#/$defs/InequalityComparison'},
  'distributional': {'$ref': '#/$defs/DecileImpacts'},
  'poverty': {'items': {'$ref': '#/$defs/PovertyRateMetricComparison'},
   'title': 'Poverty',
   'type': 'array'},
  'labor_supply': {'items': {'$ref': '#/$defs/LaborSupplyMetricImpact'},
   'title': 'Labor Supply',
   'type': 'array'}},
 'required': ['headlines',
  'fiscal',
  'inequality',
  'distributional',
  'poverty',
  'labor_supply'],
 'title': 'EconomyComparison',
 'type': 'object'}