def test_dump(): directory = tempfile.mkdtemp(prefix = "openfisca_") simulation = Simulation(tax_benefit_system = tax_benefit_system, simulation_json = couple) calculated_value = simulation.calculate('disposable_income', '2018-01') dump_simulation(simulation, directory) simulation_2 = restore_simulation(directory, tax_benefit_system) # Check entities structure have been restored assert_array_equal(simulation.person.ids, simulation_2.person.ids) assert_array_equal(simulation.person.count, simulation_2.person.count) assert_array_equal(simulation.household.ids, simulation_2.household.ids) assert_array_equal(simulation.household.count, simulation_2.household.count) assert_array_equal(simulation.household.members_position, simulation_2.household.members_position) assert_array_equal(simulation.household.members_entity_id, simulation_2.household.members_entity_id) assert_array_equal(simulation.household.members_legacy_role, simulation_2.household.members_legacy_role) assert_array_equal(simulation.household.members_role, simulation_2.household.members_role) # Check calculated values are in cache disposable_income_holder = simulation_2.person.get_holder('disposable_income') cached_value = disposable_income_holder.get_array('2018-01') assert cached_value is not None assert_array_equal(cached_value, calculated_value) shutil.rmtree(directory)
def calculate(): tax_benefit_system = data['tax_benefit_system'] request.on_json_loading_failed = handle_invalid_json input_data = request.get_json() try: simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=input_data) except SituationParsingError as e: abort(make_response(jsonify(e.error), e.code or 400)) requested_computations = dpath.util.search(input_data, '*/*/*/*', afilter=lambda t: t is None, yielded=True) for computation in requested_computations: path = computation[0] entity_plural, entity_id, variable_name, period = path.split('/') result = simulation.calculate(variable_name, period).tolist() entity = simulation.get_entity(plural=entity_plural) entity_index = entity.ids.index(entity_id) variable = tax_benefit_system.get_variable(variable_name) if variable.value_type == Enum: entity_result = result[entity_index].name else: entity_result = result[entity_index] dpath.util.set(input_data, path, entity_result) return jsonify(input_data)
def trace(tax_benefit_system, input_data): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=input_data, trace=True) requested_computations = dpath.util.search(input_data, '*/*/*/*', afilter=lambda t: t is None, yielded=True) for computation in requested_computations: path = computation[0] entity_plural, entity_id, variable_name, period = path.split('/') simulation.calculate(variable_name, period) trace = deepcopy(simulation.tracer.trace) for vector_key, vector_trace in trace.items(): value = vector_trace['value'].tolist() if isinstance(value[0], Enum): value = [item.name for item in value] if isinstance(value[0], bytes): value = [to_unicode(item) for item in value] vector_trace['value'] = value return { "trace": trace, "entitiesDescription": {entity.plural: entity.ids for entity in simulation.entities.values()}, "requestedCalculations": list(simulation.tracer.requested_calculations) }
def calculate(tax_benefit_system, input_data): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=input_data) requested_computations = dpath.util.search(input_data, '*/*/*/*', afilter=lambda t: t is None, yielded=True) computation_results = {} for computation in requested_computations: path = computation[0] entity_plural, entity_id, variable_name, period = path.split('/') variable = tax_benefit_system.get_variable(variable_name) result = simulation.calculate(variable_name, period) entity = simulation.get_entity(plural=entity_plural) entity_index = entity.ids.index(entity_id) if variable.value_type == Enum: entity_result = result.decode()[entity_index].name elif variable.value_type == float: entity_result = float( str(result[entity_index]) ) # To turn the float32 into a regular float without adding confusing extra decimals. There must be a better way. elif variable.value_type == str: entity_result = to_unicode( result[entity_index]) # From bytes to unicode else: entity_result = result.tolist()[entity_index] dpath.util.new(computation_results, path, entity_result) dpath.merge(input_data, computation_results) return input_data
def test_get_memory_usage(): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=single) simulation.calculate('disposable_income', '2017-01') memory_usage = simulation.get_memory_usage(variables=['salary']) assert (memory_usage['total_nb_bytes'] > 0) assert (len(memory_usage['by_variable']) == 1)
def trace(): tax_benefit_system = data['tax_benefit_system'] request.on_json_loading_failed = handle_invalid_json input_data = request.get_json() try: simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=input_data, trace=True) except SituationParsingError as e: abort(make_response(jsonify(e.error), e.code or 400)) requested_computations = dpath.util.search(input_data, '*/*/*/*', afilter=lambda t: t is None, yielded=True) for computation in requested_computations: path = computation[0] entity_plural, entity_id, variable_name, period = path.split('/') simulation.calculate(variable_name, period).tolist() return jsonify({ "trace": simulation.tracer.trace, "entitiesDescription": { entity.plural: entity.ids for entity in simulation.entities.itervalues() }, "requestedCalculations": list(simulation.tracer.requested_calculations) })
def test_clone(): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json={ "persons": { "bill": { "salary": { "2017-01": 3000 } }, }, "households": { "household": { "parents": ["bill"] } } }) simulation_clone = simulation.clone() assert simulation != simulation_clone for entity_id, entity in simulation.entities.items(): assert entity != simulation_clone.entities[entity_id] assert simulation.persons != simulation_clone.persons salary_holder = simulation.person.get_holder('salary') salary_holder_clone = simulation_clone.person.get_holder('salary') assert salary_holder != salary_holder_clone assert salary_holder_clone.simulation == simulation_clone assert salary_holder_clone.entity == simulation_clone.person
def test_calculate_output_divide(): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=single) simulation.set_input('variable_with_calculate_output_divide', 2017, [12000]) assert_near( simulation.calculate_output('variable_with_calculate_output_divide', '2017-06'), 1000)
def calculate(): tax_benefit_system = data['tax_benefit_system'] request.on_json_loading_failed = handle_invalid_json input_data = request.get_json() try: simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=input_data) except SituationParsingError as e: abort(make_response(jsonify(e.error), e.code or 400)) except UnicodeEncodeError as e: abort( make_response( jsonify({ "error": "'" + e[1] + "' is not a valid ASCII value." }), 400)) requested_computations = dpath.util.search(input_data, '*/*/*/*', afilter=lambda t: t is None, yielded=True) results = {} try: for computation in requested_computations: path = computation[0] entity_plural, entity_id, variable_name, period = path.split( '/') variable = tax_benefit_system.get_variable(variable_name) result = simulation.calculate(variable_name, period) entity = simulation.get_entity(plural=entity_plural) entity_index = entity.ids.index(entity_id) if variable.value_type == Enum: entity_result = result.decode()[entity_index].name elif variable.value_type == float: entity_result = float( str(result[entity_index]) ) # To turn the float32 into a regular float without adding confusing extra decimals. There must be a better way. elif variable.value_type == str: entity_result = to_unicode( result[entity_index]) # From bytes to unicode else: entity_result = result.tolist()[entity_index] dpath.util.new(results, path, entity_result) except UnicodeEncodeError as e: abort( make_response( jsonify({ "error": "'" + e[1] + "' is not a valid ASCII value." }), 400)) dpath.util.merge(input_data, results) return jsonify(input_data)
def test_set_input_enum(): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=couple) status_occupancy = np.asarray(['owner']) period = make_period('2017-12') HousingOccupancyStatus = tax_benefit_system.get_variable( 'housing_occupancy_status').possible_values simulation.household.get_holder('housing_occupancy_status').set_input( period, status_occupancy) result = simulation.calculate('housing_occupancy_status', period) assert_equal(result, HousingOccupancyStatus.owner)
def test_entity_variables_with_constructor(): simulation_json = { "persons": { "bill": {}, "bob": {}, "claudia": {}, "janet": {}, "tom": {}, }, "households": { "first_household": { "parents": ['bill', 'bob'], "children": ['janet', 'tom'], "rent": { "2017-06": 800 } }, "second_household": { "parents": ["claudia"], "rent": { "2017-06": 600 } } } } simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=simulation_json) household = simulation.household assert_near(household('rent', "2017-06"), [800, 600])
def test_entity_structure_with_constructor(): simulation_json = { "persons": { "bill": {}, "bob": {}, "claudia": {}, "janet": {}, "tom": {}, }, "households": { "first_household": { "parents": ['bill', 'bob'], "children": ['janet', 'tom'] }, "second_household": { "parents": ["claudia"] } } } simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=simulation_json) household = simulation.household assert_near(household.members_entity_id, [0, 0, 1, 0, 0]) assert ((household.members_role == [ FIRST_PARENT, SECOND_PARENT, FIRST_PARENT, CHILD, CHILD ]).all()) assert_near(household.members_legacy_role, [0, 1, 0, 2, 3]) assert_near(household.members_position, [0, 1, 0, 2, 3])
def restore_simulation(directory, tax_benefit_system, **kwargs): """ Restore simulation from directory """ simulation = Simulation(tax_benefit_system, tax_benefit_system.instantiate_entities()) entities_dump_dir = os.path.join(directory, "__entities__") for population in simulation.populations.values(): if population.entity.is_person: continue person_count = _restore_entity(population, entities_dump_dir) for population in simulation.populations.values(): if not population.entity.is_person: continue _restore_entity(population, entities_dump_dir) population.count = person_count variables_to_restore = (variable for variable in os.listdir(directory) if variable != "__entities__") for variable in variables_to_restore: _restore_holder(simulation, variable, directory) return simulation
def test_get_formula(): simulation = Simulation(tax_benefit_system = tax_benefit_system, simulation_json = couple) disposable_income_formula = tax_benefit_system.get_variable('disposable_income').get_formula() person = simulation.person disposable_income = person('disposable_income', '2017-01') disposable_income_2 = disposable_income_formula(person, '2017-01', None) # No need for parameters here assert_near(disposable_income, disposable_income_2)
def build_from_entities(self, tax_benefit_system, input_dict, default_period=None, **kwargs): """ Build a simulation from a Python dict ``input_dict`` fully specifying entities. Examples: >>> simulation_builder.build_from_entities({ 'persons': {'Javier': { 'salary': {'2018-11': 2000}}}, 'households': {'household': {'parents': ['Javier']}} }) """ simulation = kwargs.pop( 'simulation', None ) # Only for backward compatibility with previous Simulation constructor if simulation is None: simulation = Simulation(tax_benefit_system, **kwargs) check_type(input_dict, dict, ['error']) unexpected_entities = [ entity for entity in input_dict if entity not in tax_benefit_system.entities_plural() ] if unexpected_entities: unexpected_entity = unexpected_entities[0] raise SituationParsingError([unexpected_entity], ''.join([ "Some entities in the situation are not defined in the loaded tax and benefit system.", "These entities are not found: {0}.", "The defined entities are: {1}." ]).format(', '.join(unexpected_entities), ', '.join(tax_benefit_system.entities_plural()))) persons_json = input_dict.get(tax_benefit_system.person_entity.plural, None) if not persons_json: raise SituationParsingError([ tax_benefit_system.person_entity.plural ], 'No {0} found. At least one {0} must be defined to run a simulation.' .format(tax_benefit_system. person_entity.key)) self.hydrate_entity(simulation.persons, persons_json, default_period=default_period) for entity_class in tax_benefit_system.group_entities: entities_json = input_dict.get(entity_class.plural) self.hydrate_entity(simulation.entities[entity_class.key], entities_json, default_period=default_period) return simulation
def test_calculate_output_add(): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=single) simulation.set_input('variable_with_calculate_output_add', '2017-01', [10]) simulation.set_input('variable_with_calculate_output_add', '2017-05', [20]) simulation.set_input('variable_with_calculate_output_add', '2017-12', [70]) assert_near( simulation.calculate_output('variable_with_calculate_output_add', 2017), 100)
def run_situation(reform_name, reform, situation_json, calculs): with open(situation_json) as json_data: json_str = json_data.read() situation = json.loads(json_str) results = {"Situation": situation_json, "Legislation": reform_name} # Initialisez la simulation (rencontre des entités avec la legislation) simulation = Simulation(tax_benefit_system=reform, simulation_json=situation) # Demandez l'ensemble des calculs for calcul, period in calculs.iteritems(): results try: results[calcul] = simulation.calculate(calcul, period)[0] print('. ' + calcul) except ParameterNotFound as e: print 'x ' + calcul print e.message + os.linesep return results
def trace(): tax_benefit_system = data['tax_benefit_system'] request.on_json_loading_failed = handle_invalid_json input_data = request.get_json() try: simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=input_data, trace=True) except SituationParsingError as e: abort(make_response(jsonify(e.error), e.code or 400)) requested_computations = dpath.util.search(input_data, '*/*/*/*', afilter=lambda t: t is None, yielded=True) for computation in requested_computations: path = computation[0] entity_plural, entity_id, variable_name, period = path.split('/') simulation.calculate(variable_name, period) trace = deepcopy(simulation.tracer.trace) for vector_key, vector_trace in trace.items(): value = vector_trace['value'].tolist() if isinstance(value[0], Enum): value = [item.name for item in value] if isinstance(value[0], bytes): value = [to_unicode(item) for item in value] vector_trace['value'] = value return jsonify({ "trace": trace, "entitiesDescription": { entity.plural: entity.ids for entity in simulation.entities.values() }, "requestedCalculations": list(simulation.tracer.requested_calculations) })
def build_default_simulation(self, tax_benefit_system, count = 1): """ Build a simulation where: - There are ``count`` persons - There are ``count`` instances of each group entity, containing one person - Every person has, in each entity, the first role """ simulation = Simulation(tax_benefit_system, tax_benefit_system.instantiate_entities()) for population in simulation.populations.values(): population.count = count population.ids = np.array(range(count)) if not population.entity.is_person: population.members_entity_id = population.ids # Each person is its own group entity return simulation
def restore_simulation(directory, tax_benefit_system, **kwargs): """ Restore simulation from directory """ simulation = Simulation(tax_benefit_system, **kwargs) entities_dump_dir = os.path.join(directory, "__entities__") for entity in simulation.entities.values(): _restore_entity(entity, entities_dump_dir) variables_to_restore = (variable for variable in os.listdir(directory) if variable != "__entities__") for variable in variables_to_restore: _restore_holder(simulation, variable, directory) return simulation
def build_default_simulation(self, tax_benefit_system, count=1, **kwargs): """ Build a simulation where: - There are ``count`` persons - There are ``count`` instances of each group entity, containing one person - Every person has, in each entity, the first role """ simulation = Simulation(tax_benefit_system, **kwargs) for entity in simulation.entities.values(): entity.count = count entity.ids = np.array(range(count)) if not entity.is_person: entity.members_entity_id = entity.ids # Each person is its own group entity entity.members_role = entity.filled_array( entity.flattened_roles[0]) return simulation
def test_set_input_with_constructor(): simulation_json = { "persons": { "bill": { "salary": { "2017": 24000, "2017-11": 2000, "2017-12": 2000 } }, "bob": { "salary": { "2017": 30000, "2017-11": 0, "2017-12": 0 } }, "claudia": { "salary": { "2017": 24000, "2017-11": 4000, "2017-12": 4000, } }, "janet": {}, "tom": {}, }, "households": { "first_household": { "parents": ['bill', 'bob'], "children": ['janet', 'tom'], }, "second_household": { "parents": ["claudia"], } } } simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=simulation_json) person = simulation.person assert_near(person('salary', "2017-12"), [2000, 0, 4000, 0, 0]) assert_near(person('salary', "2017-10"), [2000, 3000, 1600, 0, 0])
def test_finalize_households(tax_benefit_system): simulation = Simulation(tax_benefit_system, tax_benefit_system.instantiate_entities()) simulation_builder = SimulationBuilder() simulation_builder.add_group_entity( 'persons', ['Alicia', 'Javier', 'Sarah', 'Tom'], simulation.household.entity, { 'Household_1': { 'parents': ['Alicia', 'Javier'] }, 'Household_2': { 'parents': ['Tom'], 'children': ['Sarah'] }, }) simulation_builder.finalize_variables_init(simulation.household) tools.assert_near(simulation.household.members_entity_id, [0, 0, 1, 1]) tools.assert_near(simulation.persons.has_role(entities.Household.PARENT), [True, True, False, True])
def make_simulation(tax_benefit_system, nb_persons, nb_groups, **kwargs): """ Generate a simulation containing nb_persons persons spread in nb_groups groups. Example: >>> from openfisca_core.scripts.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(range(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.values(): 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 test_projectors_methods(): simulation = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=couple) household = simulation.household person = simulation.person projected_vector = household.first_parent.has_role(Household.FIRST_PARENT) assert (len(projected_vector) == 1) # Must be of a household dimension salary_i = person.household.members('salary', '2017-01') assert (len(person.household.sum(salary_i)) == 2 ) # Must be of a person dimension assert (len(person.household.max(salary_i)) == 2 ) # Must be of a person dimension assert (len(person.household.min(salary_i)) == 2 ) # Must be of a person dimension assert (len(person.household.all(salary_i)) == 2 ) # Must be of a person dimension assert (len(person.household.any(salary_i)) == 2 ) # Must be of a person dimension assert (len(household.first_parent.get_rank(household, salary_i)) == 1 ) # Must be of a person dimension
'af': allMonths, 'af_nbenf': allMonths, 'af_taux_modulation': allMonths, 'prestations_familiales_base_ressources': allMonths, } calculs = { 'af': allMonths, } import experiment_af import situations situation = utils.merge(utils.prefix('7618153', situations.situation_7618153), utils.prefix('6771069', situations.situation_6771069)) simulation_actuelle = Simulation(tax_benefit_system=tax_benefit_system, simulation_json=situation) checkPeriod = "2018-07" for calcul, periods in calculs.iteritems(): ids = simulation_actuelle.get_variable_entity(calcul).ids for period in periods: data = simulation_actuelle.calculate(calcul, period) result = dict(zip(ids, data)) for mid, value in result.iteritems(): print(mid, value, value * 0.995) # source = situation["familles"][mid]["af"][checkPeriod] # if abs(source - value)>1: # print (mid, source, value) from pprint import pprint #pprint(situation)
from datetime import datetime from openfisca_core.simulations import Simulation import sys from pprint import pprint filename = '../doc/CMUCACS.csv' if len(sys.argv) > 1: filename = sys.argv[len(sys.argv) - 1] legislation_francaise = openfisca_france.FranceTaxBenefitSystem() with open(filename, 'r') as file: rows = csv.DictReader(file) for data in rows: period = datetime.strptime('2018-08-01', '%Y-%m-%d') situation = cpam_utils.createSituation(data, period) pprint(situation) simulation_actuelle = Simulation( tax_benefit_system=legislation_francaise, simulation_json=situation) variable = 'cmu_c' periodText = period.strftime('%Y-%m') resultat = simulation_actuelle.calculate(variable, periodText) print('{0} - {1} : {2}'.format(variable, periodText, resultat)) print('')
def build_from_entities(self, tax_benefit_system, input_dict): """ Build a simulation from a Python dict ``input_dict`` fully specifying entities. Examples: >>> simulation_builder.build_from_entities({ 'persons': {'Javier': { 'salary': {'2018-11': 2000}}}, 'households': {'household': {'parents': ['Javier']}} }) """ input_dict = deepcopy(input_dict) simulation = Simulation(tax_benefit_system, tax_benefit_system.instantiate_entities()) # Register variables so get_variable_entity can find them for (variable_name, _variable) in tax_benefit_system.variables.items(): self.register_variable(variable_name, simulation.get_variable_population(variable_name).entity) check_type(input_dict, dict, ['error']) axes = input_dict.pop('axes', None) unexpected_entities = [entity for entity in input_dict if entity not in tax_benefit_system.entities_plural()] if unexpected_entities: unexpected_entity = unexpected_entities[0] raise SituationParsingError([unexpected_entity], ''.join([ "Some entities in the situation are not defined in the loaded tax and benefit system.", "These entities are not found: {0}.", "The defined entities are: {1}."] ) .format( ', '.join(unexpected_entities), ', '.join(tax_benefit_system.entities_plural()) ) ) persons_json = input_dict.get(tax_benefit_system.person_entity.plural, None) if not persons_json: raise SituationParsingError([tax_benefit_system.person_entity.plural], 'No {0} found. At least one {0} must be defined to run a simulation.'.format(tax_benefit_system.person_entity.key)) persons_ids = self.add_person_entity(simulation.persons.entity, persons_json) for entity_class in tax_benefit_system.group_entities: instances_json = input_dict.get(entity_class.plural) if instances_json is not None: self.add_group_entity(self.persons_plural, persons_ids, entity_class, instances_json) else: self.add_default_group_entity(persons_ids, entity_class) if axes: self.axes = axes self.expand_axes() try: self.finalize_variables_init(simulation.persons) except PeriodMismatchError as e: self.raise_period_mismatch(simulation.persons.entity, persons_json, e) for entity_class in tax_benefit_system.group_entities: try: population = simulation.populations[entity_class.key] self.finalize_variables_init(population) except PeriodMismatchError as e: self.raise_period_mismatch(population.entity, instances_json, e) return simulation
def build(self, tax_benefit_system): return Simulation(tax_benefit_system, self.populations)
def get_simulation(json, **kwargs): return Simulation(tax_benefit_system=tax_benefit_system, simulation_json=json, **kwargs)