A few days ago, I began trying out OpenFisca, a fiscal policy simulation framework developed with backing from the French government. While OpenFisca describes itself as a “legislation-as-code” system to “Turn law into software”, I found that its approach to legal automation was different from the way I was used to thinking about the problem. OpenFisca understands regulations as a collection of mathematical rules that combine together to create a regulatory system. This system is then applied to the members of a population all at once to produce a monetary outcome for each member of the population. This is implemented by applying the tax liability calculation to every row of a numpy array. A typical use case for OpenFisca would be to write a function describing a hypothetical tax reform, and then run a simulation applying the modified tax rules in a large number of case studies, to get a view of what impact the reform would have on the population as a whole or on particular individuals.

The design philosophy of OpenFisca is based on the goal of running a large number of calculations side-by-side in numpy. However, you can also run calculations representing a single individual or household. If you implement any new OpenFisca modules, you’ll notice that the drafting style has a few quirks, which are needed to make the functions work on vectors as opposed to ordinary scalar Python variables like integers and floats.

The core documentation is in English, but the OpenFisca model for France seems to be better-implemented than any other, and the documentation for that model is only in French, which is a language I’ve neglected to learn. I ended up sticking with the beginner examples in the core documentation, but I think there would be more to learn about OpenFisca design patterns if I crossed over into the French documentation.

A great feature is the ability to turn on tracing to see what variables OpenFisca is looking at to make a calculation. For instance, in the example in the tutorial, the tracer looks at this example formula for a housing credit. This example comes from the openfisca-country-template package, which is in English.

def formula_1980(household, period, parameters):
    '''
    To compute this allowance, the 'rent' value must be provided for the same month,
    but 'housing_occupancy_status' is not necessary.
    '''
    return household('rent', period) * parameters(period).benefits.housing_allowance
description: Housing allowance amount (as a fraction of the rent)
metadata:
  unit: /1
  reference: https://law.gov.example/housing-allowance-rate
documentation: |
  A fraction of the rent.
  From the 1st of Dec 2016, the housing allowance no longer exists.
values:
  # This parameter is only defined from the 1st of Jan 2010 to the 3Oth of Nov 2016.
  2010-01-01:
    value: 0.25
  2016-12-01:
    value: null

When an OpenFisca simulation is run on this formula, it shows that it looks first at “housing_allowance”, but that it then has to look at “rent” to determine the housing allowance. In order to enable this kind of introspection, OpenFisca has to implement its own “Variable” parent class, which has to be inherited by all variables indicating characteristics of taxpayers, households, etc.

What about rules determining which rules apply to a particular tax return? OpenFisca can handle temporal changes in parameters, like tax rate multipliers. If the changes that happen over time are more complex than just changing a multiplier, then different functions with different date ranges can exist side by side (explaining why the formula function in the example has the label “1980” in its name).

“Compliance” and “Procedure” Models

This seems like a good, but not perfect, model for legal domains like the tax liability and welfare issues OpenFisca was built for. Because of my background writing about appellate litigation, one of the first questions I have about any legal rule formalism is how to create a process-oriented model demonstrating how a person’s interactions with the legal system can change the person’s outcomes. As far as I noticed, OpenFisca doesn’t have a convenient way to model a decision point that a taxpayer might face, such as a choice between different categories of tax filing. You could model a taxpayer’s decision with a formula indicating that the taxpayer should evaluate both options and select whichever one result in less tax liability. But automating such evaluations might not be easy, because some tax decisions might involve weighing the value of current tax savings against potential future savings that might not be guaranteed. Another option might be to model the taxpayer’s filing decision as a variable. The documentation gives examples of typical variables including “the birth date of a person” and “whether a family is living in Paris”. If you chose to treat something like “whether a married taxpayer chooses to file jointly” as a variable, it would be like treating that decision as an intrinsic characteristic of the taxpayer, so that you would have to tell OpenFisca which decision the taxpayer would make instead of using OpenFisca to understand the implications of a range of possibilities.

OpenFisca seems based on the idea that there is a correct amount of tax liability and a correct level of government benefits for each person, and when the law is properly formalized, those numbers can be calculated for each person. But even if the tax domain, there’s an alternate, and more procedural, understanding of how the law works. I think if you were to ask the current President of the United States, he wouldn’t agree that there’s a “correct” amount of income tax liability for a particular person, and he’d instead describe the tax filing process as a series of procedural gambits that reduce tax liability without any fixed minimum amount that needs to be paid.

These two categories of legal rule model could be called “compliance” models and “procedural” models. Both types of model try to describe kinds of human behavior, and they each work best when the behavior they cover is closely constrained. For instance, despite the ambiguities suggested above, there’s only a small range of tax returns a particular taxpayer could turn in that would be considered in compliance with the law, so tax returns are an ideal subject for a compliance model. But it’s much harder to model the range of behavior that would allow a person to remain in compliance with a criminal statute prohibiting theft (and it’s nearly as hard to model the range of behavior that would violate the statute). A model of litigation procedure might be easier to implement for a simple criminal statute, but imagine a model of litigation procedure in a jurisdiction allowing declaratory judgment lawsuits for guidance about how to comply with a law or contract. There would be a limitless range of declaratory judgment actions that a party could file, and in that context, the full complexity of a “compliance” rule model is potentially subsumed within a procedural model of declaratory judgment. Procedural models of legal rules are also difficult when the rules are applied by administrative agencies or other entities with wide discretion. Because both paradigms are so uneven in their usefulness for modeling large categories of laws, OpenFisca is certainly impressive in applying as broadly as it does.

Arithmetic in Legal Rule Models

Lots of legal rules contain arithmetic operations like addition and multiplication. It’s a significant accomplishment for OpenFisca that it handles these operations while also allowing the use of its tracing module to record the history of what calculations were applied. I have to admit OpenFisca has the edge here over the legal rule engine I created, AuthoritySpoke, because AuthoritySpoke can’t handle most arithmetic operations. Although AuthoritySpoke allows legal rules to contain quantities, the only operations it can perform on those quantities are comparisons for equality, greater-than, or less-than. Moreover, it doesn’t let users assign quantities to a variable and reference them more than once in the same rule. Here’s the best that AuthoritySpoke can do in modeling the OpenFisca housing allowance rule quoted above.

from authorityspoke.io import readers

housing_benefit_calculation = {
  'inputs': [
    {
      'type': 'fact',
      'content': "{Ari's household} was a household"
    },
    {
      'type': 'fact',
      'content': "the rent paid by {Ari's household} was == 300"
    }
    ],
 'outputs': {
    'type': 'fact',
    'content': "the housing credit for {Ari's household} was == 75"
  },
  'universal': True,
  'mandatory': True
  }

housing_benefit_rule = readers.read_rule(housing_benefit_calculation)
print(housing_benefit_rule)
the Rule that the court MUST ALWAYS impose the
  RESULT:
    the Fact that the housing credit for <Ari's household> was exactly equal to 75
  GIVEN:
    the Fact that <Ari's household> was a household
    the Fact that the rent paid by <Ari's household> was exactly equal to 300

For comparison, I’ll create a contradictory rule stating that the housing credit would be different under the same circumstances.

from copy import deepcopy
wrong_housing_benefit_calculation = deepcopy(housing_benefit_calculation)
wrong_housing_benefit_calculation['outputs']['content'] = "the housing credit for {Ari's household} was == 999"

wrong_housing_benefit_rule = readers.read_rule(wrong_housing_benefit_calculation)
print(wrong_housing_benefit_rule)
the Rule that the court MUST ALWAYS impose the
  RESULT:
    the Fact that the housing credit for <Ari's household> was exactly equal to 999
  GIVEN:
    the Fact that <Ari's household> was a household
    the Fact that the rent paid by <Ari's household> was exactly equal to 300
wrong_housing_benefit_rule.contradicts(housing_benefit_rule)
True

Even though AuthoritySpoke finds a contradiction, it misses the point. It only understands that the specific input of 300 requires the specific output of a housing credit of 75. There’s no way in AuthoritySpoke (as of version 0.4) to express the general rule that the rent must by multiplied by 0.25 to get the housing credit. Trying out OpenFisca has certainly made me wonder whether AuthoritySpoke could be redesigned to give users more access to idiomatic Python syntax, including math operators and the ability to assign numbers to variables, when they’re annotating legal rules.

Have any suggestions or feature requests for AuthoritySpoke? Open an issue for AuthoritySpoke! OpenFisca is also open source software and its maintainers welcome code contributions to OpenFisca.