Linear Functions

Linear FunctionsΒΆ

In many discrete choice models, the probability of selecting any particular alternative is represented as some function based on the utility of the various alternatives.

In Larch, the utility is created based on one or more linear-in-parameters functions, which combine raw or pre-computed data values with named model parameters. To facilitate writing these functions, Larch provides two special classes: parameter references (P) and data references (X).

from larch import P, X

Parameter and data references can be defined using either a function-like notation, or a attribute-like notation.

P('NamedParameter')
P.NamedParameter
X.NamedDataValue
X.NamedDataValue

In either case, if the named value contains any spaces or non-alphanumeric characters, it must be given in function-like notation only, as Python will not accept those characters in the attribute-like form.

P('Named Parameter')
P('Named Parameter')

Data references can name an exact data element that appears in the data used for model estimation or application, or can include simple transformations of that data, so long as these transformations can be done without regard to any estimated parameter. For example, we can use the log of income:

X("log(INCOME)")
X('log(INCOME)')

To write a linear-in-parameters utility function, we simply multiply together a parameter reference and a data reference, and then optionally add that to one or more similar terms.

P.InVehTime * X.AUTO_TIME + P.Cost * X.AUTO_COST
P.InVehTime * X.AUTO_TIME
+ P.Cost * X.AUTO_COST

It is permissible to omit the data reference on a term (in which case it is implicitly set to 1.0).

P.ASC + P.InVehTime * X.AUTO_TIME + P.Cost * X.AUTO_COST
P.ASC
+ P.InVehTime * X.AUTO_TIME
+ P.Cost * X.AUTO_COST

On the other hand, Larch does not currently permit you to omit the parameter reference from a term.

P.InVehTime * X.AUTO_TIME + P.Cost * X.AUTO_COST + X.GEN_COST
---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
Input In [12], in <cell line: 1>()
----> 1 P.InVehTime * X.AUTO_TIME + P.Cost * X.AUTO_COST + X.GEN_COST

File ~/work/larch/larch/larch/larch/model/linear.pyx:951, in larch.model.linear.LinearFunction_C.__add__()

NotImplementedError: <LinearFunction_C> + X.GEN_COST

Although you cannot include a term with an implicit parameter set to 1, you can achieve the same model structure by including that parameter explicitly. Later in the model setup process you can explicitly lock any parameter to have a specific fixed value.

P.InVehTime * X.AUTO_TIME + P.Cost * X.AUTO_COST + X.GEN_COST * P.One
P.InVehTime * X.AUTO_TIME
+ P.Cost * X.AUTO_COST
+ P.One * X.GEN_COST

In addition to writing out a linear function as a single command, you can also compose such functions over several Python commands, using both in-place and regular addition.

f = P.ASC + P.InVehTime * X.AUTO_TIME
f += P.Cost * X.AUTO_COST
f
P.ASC
+ P.InVehTime * X.AUTO_TIME
+ P.Cost * X.AUTO_COST
f + P.Cost * X.AUTO_TOLL
P.ASC
+ P.InVehTime * X.AUTO_TIME
+ P.Cost * X.AUTO_COST
+ P.Cost * X.AUTO_TOLL

Functional simplification is not automatic. Thus, while you can subtract term from a linear function, it does not cancel out existing terms from the function.

f = P.ASC + P.InVehTime * X.AUTO_TIME
f - P.InVehTime * X.AUTO_TIME
P.ASC
+ P.InVehTime * X.AUTO_TIME
+ P.InVehTime * -1.0 * X.AUTO_TIME

Instead, to actually remove terms from a linear function, use the remove_param or remove_data methods.

f = P.ASC + P.InVehTime * X.AUTO_TIME + P.Cost * X.AUTO_TOLL
f.remove_param(P.InVehTime)
P.ASC
+ P.Cost * X.AUTO_TOLL
f = P.ASC + P.InVehTime * X.AUTO_TIME + P.Cost * X.AUTO_TOLL
f.remove_data('AUTO_TOLL')
P.ASC
+ P.InVehTime * X.AUTO_TIME