from larch import P, X

"""
Explanation: (linear-funcs)=
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).
End of explanation
"""


P('NamedParameter')

# TEST
named_param = P('NamedParameter')
assert isinstance(named_param, larch.model.linear.ParameterRef_C)
assert named_param == 'NamedParameter'

X.NamedDataValue

# TEST
named_data = X.NamedDataValue
assert isinstance(named_data, larch.model.linear.DataRef_C)
assert named_data == 'NamedDataValue'

"""
Explanation: Parameter and data references can be defined using either a function-like notation,
or a attribute-like notation.
End of explanation
"""


P('Named Parameter')

# TEST
named_param = P('Named Parameter')
assert isinstance(named_param, larch.model.linear.ParameterRef_C)
assert named_param == 'Named Parameter'

"""
Explanation: 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.
End of explanation
"""


X("log(INCOME)")

"""
Explanation: 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:
End of explanation
"""


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

"""
Explanation: 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.
End of explanation
"""


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

"""
Explanation: It is permissible to omit the data reference on a term 
(in which case it is implicitly set to 1.0).
End of explanation
"""


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

"""
Explanation: On the other hand, Larch does not currently permit you to omit the parameter 
reference from a term.
End of explanation
"""


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

# TEST
f = _
assert len(f) == 3

"""
Explanation: 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.
End of explanation
"""


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

f + P.Cost * X.AUTO_TOLL

"""
Explanation: 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.
End of explanation
"""


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

"""
Explanation: 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.
End of explanation
"""


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

# TEST
assert len(f) == 2
assert f[0].param == 'ASC'
assert f[1].param == 'Cost'

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

# TEST
assert len(f) == 2
assert f[0].param == 'ASC'
assert f[1].param == 'InVehTime'

"""
Explanation: Instead, to actually remove terms from a linear function, use the remove_param or remove_data methods.
End of explanation
"""


