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

Switch parameters to YAML #552

Merged
merged 106 commits into from
Aug 28, 2017
Merged

Switch parameters to YAML #552

merged 106 commits into from
Aug 28, 2017

Conversation

michelbl
Copy link
Contributor

@michelbl michelbl commented Aug 3, 2017

Connected to #523

See changelog (TODO: copy the changelog here after merge)

@michelbl michelbl changed the title Params yaml Switch parameters to YAML Aug 3, 2017
@michelbl
Copy link
Contributor Author

michelbl commented Aug 3, 2017

Still to do :

  • Update scripts to import IPP parameters. There are already a script to convert XLSX files from IPP to XML, a script to convert XML to YAML and a script to merge XML. What is needed is to combine the former 2 and update the later to YAML. I think this has not to be done in this PR.
  • Write a script to find expected values. I think this has not to be done in this PR.
  • The french reform Landais-Piketty-Saez uses a LinearAverageTaxScale. This scale cannot be declared with the current YAML syntax. What should we do (some solutions:)
    • Add a keyword to YAML files
    • Remove the Landais-Piketty-Saez from Openfisca-France
    • Find a way to express this scale using the interface
    • Allow to create this type of scale but not from YAML (using the constructor of class Scale). This is the closest solution to what exists now.
  • A test broke with no link to parameters. I need to investigate with the clue that @fpagnoux gave me.
  • Rename legislation to parameters in internal variables. This has to be debated and this has not to be done in this PR.
  • OpenFisca-WebAPI requires OpenFisca-Core < 0.17. https://github.com/openfisca/openfisca-web-api/blob/master/setup.py#L63

@michelbl michelbl requested review from fpagnoux and Anna-Livia August 3, 2017 15:14
@michelbl michelbl self-assigned this Aug 3, 2017
Copy the baseline TaxBenefitSystem legislation_json attribute and return it.
Used by reforms which need to modify the legislation_json, usually in the build_reform() function.
Validates the new legislation.
Used by reforms which need to modify the legislation parameters.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use if the reform asks for legislation parameter modification

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified

class Reform(TaxBenefitSystem):
"""A modified TaxBenefitSystem

A reformed TaxBenefitSystem must subclass `Reform` and implement a method `apply()`. Such a function can add or replace variables and call `self.modify_legislation()` to modify the parameters of the legislation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In OpenFisca, a reform is a modified TaxBenefitSystem.
It can add or replace variables and call self.modify_legislation()to modify the parameters of the legislation. All reforms must subclassReformand implement aapply()` method .

@@ -48,10 +55,10 @@ class VariableNameConflict(Exception):

class TaxBenefitSystem(object):
"""
Represents the legislation.
Represents the legislation. It stores parameters (values defined for everyone) and variables (values defined for some given entity e.g. a person).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'enlèverait "Represents the legislation" --> cela reste déroutant, quand legislation peut représenter seulement les paramètres dans certain contextes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est plutôt dans l'autre sens qu'il faudrait aller: le TBS représente la législation, mais les paramètres sont... des paramètres.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, je ne change rien

Copy link
Member

@fpagnoux fpagnoux left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not check the code deeply, but here is my first review.

The main thing that puzzles me is the type attribute. This was not mentioned in the issue discussions, and I find it a little heavy (espacially the almost empty files that need to be put in each intermediate directory)

CHANGELOG.md Outdated

Now:
```yaml
type: node
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that necessary ?

Can't we figure out if it's a nose just by checking its properties ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

CHANGELOG.md Outdated
descrption: Taux d'impôt sur les salaires
unit: /1
values:
'2016-01-01':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the quotes necessary ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

CHANGELOG.md Outdated
type: node
taux:
type: parameter
descrption: Taux d'impôt sur les salaires
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

CHANGELOG.md Outdated
impot:
type: node
taux:
type: parameter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that necessary ?

Can't we figure out if it's a nose just by checking its properties ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

CHANGELOG.md Outdated

- The XML attributes `format` and `type` are replaced by the YAML attribute `unit` than can take as values `year`, `currency` and `/1`.

* Refactor the internal representation and the interface of legislation parameters
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the "Breaking changes" part, as a user, I want to check if something that I was using has been removed/changed.

Reading this part doesn't help me much. I know something has been refactored (by that's not really my business), and I know that new classes have been introduced. But what former classes/methods/functions/attributes have been removed ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to another section

CHANGELOG.md Outdated
#### Technical changes

* The variables `*legislation_json*` are replaced by `*legislation*` because the format json is no longer used.
* The expression "compact legislation" is replaced by the expression "legislation at instant". For example, `taxbenefitsystem.compact_legislation_by_instant_cache` is renamed as `taxbenefitsystem.legislation_at_instant_cache`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't that a breaking change ?

Copy link
Contributor Author

@michelbl michelbl Aug 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I clarified it a bit

debug = False
debug_all = False # When False, log only formula calls with non-default parameters.
period = None
baseline_compact_legislation_by_instant_cache = None
baseline_legislation_at_instant_cache = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaking change must be documented in the changelog.
Maybe we should also make this attribute private ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes !

@@ -11,11 +11,11 @@


class Simulation(object):
compact_legislation_by_instant_cache = None
legislation_at_instant_cache = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaking change must be documented in the changelog.

Maybe we should also make this attribute private ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes !

@@ -48,10 +55,10 @@ class VariableNameConflict(Exception):

class TaxBenefitSystem(object):
"""
Represents the legislation.
Represents the legislation. It stores parameters (values defined for everyone) and variables (values defined for some given entity e.g. a person).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C'est plutôt dans l'autre sens qu'il faudrait aller: le TBS représente la législation, mais les paramètres sont... des paramètres.

# TODO: Currently: Don't use a weakref, because they are cleared by Paste (at least) at each call.
self.compact_legislation_by_instant_cache = {} # weakref.WeakValueDictionary()
self.legislation_at_instant_cache = {} # weakref.WeakValueDictionary()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we are renaming it, should we be opportunistic and rename it to parameters_at_instant_cache ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

@MattiSG
Copy link
Member

MattiSG commented Aug 16, 2017

  • Replace mandatory _.yaml by optional index.yaml.
  • Remove the need to specify the type property.

@MattiSG
Copy link
Member

MattiSG commented Aug 16, 2017

Update scripts to import IPP parameters.

Ok to postpone, but that is quite important to get back.

Write a script to find expected values.

Ok to postpone.

The french reform Landais-Piketty-Saez uses a LinearAverageTaxScale. This scale cannot be declared with the current YAML syntax.

This is used only in one reform at the moment, but the economical logic behind it is not unique. In this case, replace the rate property by average_rate and load accordingly.

A test broke with no link to parameters.
OpenFisca-WebAPI requires OpenFisca-Core < 0.17.

It seems to be the Web API, which cannot serve the type and format properties anymore.

If regaining the type property is easy thanks to Python introspection, let's bring it back and not fill in the format optional property at all (or expose the unit contents).

We just need to obtain the list of currently exposed type parameters. This can be done by inspecting the swagger endpoint.

Rename legislation_json to parameters in internal variables.

@benjello is this ok with you? It does seem more readable to me to have parameters rather than legislation, would it be only for similarity with variables, and avoiding the ambiguity between legislation and tax_benefit_system.

@benjello
Copy link
Member

@MattiSG : the trade-off for me is choosing the less puzzling ambiguity:

  • legislation/tax_benefit_system
  • variables/parameters

I do not have a clear preference ;-) so it is ok change legislation in parameters

@michelbl
Copy link
Contributor Author

Thanks for your reviews.

I'm not sure about how the caching of the parameters should be tested.

@MattiSG
Copy link
Member

MattiSG commented Aug 21, 2017

For caching:

  • The pickle cache must not be versioned. It can be packaged and deployed, but not be versioned in the source.
  • If we choose not to systematically validate, we'll validate on the first exception in the constructors and output either the validation result or a recommendation to open an issue (as an exception with no validation error means there is most probably an issue in the core itself).
  • A separate command for validation can be provided, or an option in all CLI commands.

The current options seem to be:

  • Caching: 50 ms.
  • Parsing with libyaml: 500 ms.
  • Parsing with pyyaml: 3 000 ms.
  • Parsing & validation with libyaml: 8 500 ms.
  • Parsing & validation with pyyaml: 12 000 ms.

In which case, I favour optional validation and recommendation to install libyaml over caching, as caching means we have to design deployment and, worst of all, cache invalidation.

CHANGELOG.md Outdated
value: 0.3
```

- The XML attributes `format` and `type` are replaced by the YAML attribute `unit` than can take as values `year`, `currency` and `/1`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep it a free text field for now.

CHANGELOG.md Outdated
```
- Now:
```python
parameters.impot_revenu.bareme[1].threshold.update(period=reform_period, value=6011)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spaces around =

CHANGELOG.md Outdated
from openfisca_core.parameters import ParameterNode

inflation = .001
reform_parameters_subtree = Node('plf2016_conterfactual', validated_yaml = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParameterNode

CHANGELOG.md Outdated

- This way of creating parameters is deprecated (see below), except when using dynamically computed values. It is the case in the example because `round(1135 * (1 + inflation))` is computed at run time.

* The function `reforms.compose_reforms()` is removed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove function reforms.compose_reforms()

self.values_list = values_list

def __repr__(self):
return 'ValuesHistory "{}"\n'.format(self.name) + ''.join(' {}: {}\n'.format(value.instant_str, value.value) for value in self.values_list)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use os.linesep

i += 1

return new_items
assert isinstance(reform_parameters, ParameterNode)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. If this is false, there will be no exploitable error message. Don't use assertions.

return self._get_baseline_parameters_at_instant(instant)
return self._get_parameters_at_instant(instant)

def legislation_at(self, instant, use_baseline = False):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this retro-compatibility layer is really necessary


self.parameters = parameters

def get_parameters(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think parameters is clean enough so that we can expose it directly

"""
return self.parameters

def get_legislation(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this retro-compatibility layer really necessary ?

path_fragments = path_fragments + [child_name],
)
for child_name, child in children.items():
child_type = type(child).__name__
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import the types instead of using the names

@fpagnoux fpagnoux force-pushed the params-yaml branch 2 times, most recently from ea25cca to fd63109 Compare August 28, 2017 17:47
@fpagnoux fpagnoux merged commit 4e50320 into master Aug 28, 2017
@fpagnoux fpagnoux deleted the params-yaml branch August 28, 2017 21:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants