Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce simulation generators #604

Merged
merged 3 commits into from
Feb 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
# Changelog

### 21.3.4 [#604](https://github.com/openfisca/openfisca-core/pull/604)

- Introduce [simulation generator](http://openfisca.readthedocs.io/en/latest/simulation_generator.html)

### 21.3.3 [#608](https://github.com/openfisca/openfisca-core/pull/608)

- Improve API response time

## 21.3.2 [#617](https://github.com/openfisca/openfisca-core/pull/611)
### 21.3.2 [#617](https://github.com/openfisca/openfisca-core/pull/611)

- Make decompositions more robust

## 21.3.1 [#617](https://github.com/openfisca/openfisca-core/pull/617)
### 21.3.1 [#617](https://github.com/openfisca/openfisca-core/pull/617)

- Fix bug on API `/variable/{id}`
- Encode API `/variable/{id}` output to `utf-8`
Expand All @@ -23,7 +27,7 @@ Add `--only-variables` and `--ignore-variables` options to `openfisca-run-test`
- When a variable file is loaded twice in the same python interpreter, make sure the second loading doesn't corrupt the first one.
- This fixes a bug introduced in 21.0.2, which could lead to a corruption of the tax and benefit in rare edge cases

## 21.2.1 [#613](https://github.com/openfisca/openfisca-core/pull/613)
### 21.2.1 [#613](https://github.com/openfisca/openfisca-core/pull/613)

- Fix two bugs that appeared with 21.2.0:
- Properly encode the result of a formula returning an Enum value
Expand Down
1 change: 1 addition & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Modules:
tracer
enum_array
holder
simulation_generator

Scripts:

Expand Down
6 changes: 6 additions & 0 deletions doc/source/simulation_generator.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
====================
Simulation generator
====================

.. automodule:: openfisca_core.scripts.simulation_generator
:members:
68 changes: 68 additions & 0 deletions openfisca_core/scripts/simulation_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import numpy as np

import random
from openfisca_core.simulations import Simulation
from openfisca_core.periods import period as make_period


def make_simulation(tax_benefit_system, nb_persons, nb_groups, **kwargs):
"""
Generate a simulation containing nb_persons persons spread in nb_groups groups.

Exemple:

>>> from openfisca_core.tools.simulation_generator import make_simulation
>>> from openfisca_france import CountryTaxBenefitSystem
>>> tbs = CountryTaxBenefitSystem()
>>> simulation = make_simulation(tbs, 400, 100) # Create a simulation with 400 persons, spread among 100 families
>>> simulation.calculate('revenu_disponible', 2017)
"""
simulation = Simulation(tax_benefit_system = tax_benefit_system, **kwargs)
simulation.persons.ids = np.arange(nb_persons)
simulation.persons.count = nb_persons
adults = [0] + sorted(random.sample(xrange(1, nb_persons), nb_groups - 1))

members_entity_id = np.empty(nb_persons, dtype = int)

# A legacy role is an index that every person within an entity has. For instance, the 'demandeur' has legacy role 0, the 'conjoint' 1, the first 'child' 2, the second 3, etc.
members_legacy_role = np.empty(nb_persons, dtype = int)

id_group = -1
for id_person in range(nb_persons):
if id_person in adults:
id_group += 1
legacy_role = 0
else:
legacy_role = 2 if legacy_role == 0 else legacy_role + 1
members_legacy_role[id_person] = legacy_role
members_entity_id[id_person] = id_group

for entity in simulation.entities.itervalues():
if not entity.is_person:
entity.members_entity_id = members_entity_id
entity.members_legacy_role = members_legacy_role
entity.count = nb_groups
entity.members_role = np.where(members_legacy_role == 0, entity.flattened_roles[0], entity.flattened_roles[-1])
return simulation


def randomly_init_variable(simulation, variable_name, period, max_value, condition = None):
"""
Initialise a variable with random values (from 0 to max_value) for the given period.
If a condition vector is provided, only set the value of persons or groups for which condition is True.

Exemple:

>>> from openfisca_core.tools.simulation_generator import make_simulation, randomly_init_variable
>>> from openfisca_france import CountryTaxBenefitSystem
>>> tbs = CountryTaxBenefitSystem()
>>> simulation = make_simulation(tbs, 400, 100) # Create a simulation with 400 persons, spread among 100 families
>>> randomly_init_variable(simulation, 'salaire_net', 2017, max_value = 50000, condition = simulation.persons.has_role(simulation.famille.DEMANDEUR)) # Randomly set a salaire_net for all persons between 0 and 50000?
>>> simulation.calculate('revenu_disponible', 2017)
"""
if condition is None:
condition = True
variable = simulation.tax_benefit_system.get_variable(variable_name)
entity = simulation.get_variable_entity(variable_name)
value = (np.random.rand(entity.count) * max_value * condition).astype(variable.dtype)
entity.get_holder(variable_name).set_input(make_period(period), value)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

setup(
name = 'OpenFisca-Core',
version = '21.3.3',
version = '21.3.4',
author = 'OpenFisca Team',
author_email = 'contact@openfisca.fr',
classifiers = [
Expand Down