Add support for Units of Measure for Decision
See original GitHub issueEnable the use of Units of Measure with the Decision type
It would be nice for the user to be able to “opt-in” to using UoM with the basic building blocks of modeling but still be able to work without them. This would be akin to how float
and float<'Measure>
behaves in F#.
It would be nice for the following types to support UoM:
- Scalar
- Decision
- LinearExpression
Here is how they should interact:
Note: Instead of writing out
'Measure
for the UoM, I abbreviate using'M
,'M1
,'M2
, etc.
Function signatures for the +
operator
The addition operator should force the UoM to match on both sides of the operator for types where addition is allowed. All of the function signatures are commutative
// Note: The Scalar type is a single case DU to wrap a float to override equality
// Scalar, float addition
Scalar * float -> Scalar
Scalar<'M> * float<'M> -> Scalar<'M>
Scalar<'M> * float // Compiler error: UoM do not match
Scalar<'M1> * float<'M2> // Compiler error: UoM do not match
// Scalar, Scalar addition
Scalar * Scalar -> Scalar
Scalar<'M> * Scalar<'M> -> Scalar<'M>
Scalar<'M> * Scalar // Compiler error: UoM do not match
Scalar<'M1> * Scalar<'M2> // Compiler error: UoM do not match
// Scalar, Decision addition
Scalar * Decision -> LinearExpression
Scalar<'M> * Decision<'M> -> LinearExpression<'M>
Scalar<'M> * Decision // Compiler error: UoM do not match
Scalar * Decision<'M> // Compiler error: UoM do not match
Scalar<'M1> * Decision<'M2> -> // Compiler error: UoM do not match
// Scalar, LinearExpression addition
Scalar * LinearExpression -> LinearExpression
Scalar<'M> * LinearExpression<'M> -> LinearExpression<'M>
Scalar<'M> * LinearExpression // Compiler error: UoM do not match
Scalar * LinearExpression<'M> // Compiler error: UoM do not match
Scalar<'M1> * LinearExpression<'M2> // Compiler error: UoM do not match
// Decision, Decision addition
Decision * Decision -> LinearExpression
Decision<'M> * Decision<'M> -> LinearExpression<'M>
Decision<'M> * Decision // Compiler error: UoM do not match
Decision<'M1> * Decision<'M2> // Compiler error: UoM do not match
// Decision, LinearExpression addition
Decision * LinearExpression -> LinearExpression
Decision<'M> * LinearExpression<'M> -> LinearExpression<'M>
Decision<'M> * LinearExpression // Compiler error: UoM do not match
Decision * LinearExpression<'M> // Compiler error: UoM do not match
// LinearExpression, LinearExpression addition
LinearExpression * LinearExpression -> LinearExpression
LinearExpression<'M> * LinearExpression<'M> -> LinearExpression<'M>
LinearExpression<'M> * LinearExpression // Compiler error: UoM do not match
LinearExpression<'M1> * LinearExpression<'M2> // Compiler error: UoM do not match
Function signatures for the *
operator
The *
operator should have the same behaviors around UoM that the float
type follows.
// Scalar, float multiplication
Scalar * float -> Scalar
Scalar<'M> * float -> Scalar<'M>
Scalar * float<'M> -> Scalar<'M>
Scalar<'M1> * float<'M2> -> Scalar<'M1 * 'M2>
// Scalar, Scalar multiplication
Scalar * Scalar -> Scalar
Scalar<'M> * Scalar -> Scalar<'M>
// Scalar, Decision multiplication
Scalar * Decision -> LinearExpression
Scalar<'M> * Decision -> LinearExpression<'M>
Scalar * Decision<'M> -> LinearExpression<'M>
Scalar<'M1> * Decision<'M2> -> LinearExpression<'M1 * 'M2>
// Scalar, LinearExpression multiplication
Scalar * LinearExpression -> LinearExpression
Scalar<'M> * LinearExpression -> LinearExpression<'M>
Scalar * LinearExpression<'M> -> LinearExpression<'M>
Scalar<'M1> * LinearExpression<'M2> -> LinearExpression<'M1 * 'M2>
The Comparison Operators
In Flips <==
, >==
, and ==
are binary infix operators and are used to create ConstraintExpression
by comparing two differnet LinearExpression
. The Comparison Operators should enforce UoM when they exist.
// All the comparison operators have the following signatures
LinearExpression * LinearExpression -> ConstraintExpression
LinearExpression<'M> * LinearExpression<'M> -> ConstraintExpression
LinearExpression<'M> * LinearExpression // Compiler error: UoM do not match
LinearExpression * LinearExpression<'M> // Compiler error: UoM do not match
LinearExpression<'M1> * LinearExpression<'M2> // Compiler error: UoM do not match
The challenge is that when we construct the ConstraintExpression
we have to ensure UoM match. Once we have a ConstraintExpression
, we no longer care about UoM. A ConstraintExpression
is a element of a Constraint
type and a Model
contains a Constraint list
. Since UoM is a kind of type information we need to drop it or ignore it at some point so that a Model
can have a Constraint list
where the LinearExpression
inside may be of different UoM.
Issue Analytics
- State:
- Created 3 years ago
- Comments:11
Top GitHub Comments
So I got this working and the ergonomics are good. I need to add some functions for getting the results values in the original UoM but this is looking pretty close. This will likely be a 2.0.0 release since I had to move some stuff around and this is a significant increase in functionality (at least for me 😂). The complexity of using the library hasn’t increased, things were just not laid out as well as they should have been initially.
I consider keeping UoM code distinct from the non-UoM code to prevent a user from accidentally mixing them and causing horrible compiler errors that do not make sense. UoM is meant to be all or nothing.
I hope to release this by early next week. I am also wanting to include a major refactor of the README to turn it into cleaner docs.
Probably a separate module is a better (clearer) design that doesn’t mix tastes. Using separate modules provide two tastes, which is good.