question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Add support for Units of Measure for Decision

See original GitHub issue

Enable 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:closed
  • Created 3 years ago
  • Comments:11

github_iconTop GitHub Comments

1reaction
matthewcrewscommented, Jul 11, 2020

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.

1reaction
wxinixcommented, Jul 1, 2020

Probably a separate module is a better (clearer) design that doesn’t mix tastes. Using separate modules provide two tastes, which is good.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Types for units of measure | varkor's blog
I want to take a brief look at exactly what a “unit of measure” is from a type theoretic perspective, as it's not...
Read more >
Units of Measure (UOM)
Category UOM Label Aliases Acceleration Gravity at Earth's Surface grav Angle Planar Radian rad radian, rads, radians Revolution rev r
Read more >
Representing Units of Measure (For Use with Numerical ...
UCUM is a syntax for representing units of measure for use with numerical references and values. It is not an enumerated set of...
Read more >
Unit of Measure (UoM) Conversions Business Processes Best ...
How much of an impact might UoM conversions have on an ERP implementation? Well, if you asked a seasoned guy who has been...
Read more >
Unit of measure with language Key
In CUNI in tope menu in GOTO option you can see translation for Dimensions, ISO codes (Press F6 in cuni) and Unit of...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found