Flips 3.0 Design Proposal
See original GitHub issueThinking some more about this, it would make sense to split the library into the domain / types parts (everything to build a Model
object) and then have separate libraries for each solver which implement translation of Model back and forth with specific solver backends and provide the same simplified API as currently in the Solver
module.
Using discriminated unions for the solver type and putting it in the Settings record is not the right approach.
The split between the three stages would remain :
- formulation
- runtime
- result extraction
but the runtime part would be opened up, while remaining simple for the simplest use cases:
// formulation
let formulation : Model =
Model.create ...
// runtime
let solver = Flips.Solver.GoogleOrTools.Solver.createSolver Flips.Solver.GoogleOrTools.SolverSettings.basic
let result = Solver.solve formulation solver
// result extraction
match result.Status with
| Optimal ->
result.Decisions
result.Objectives
| _ -> ...
For the specific solver settings, studying how Feliz model typed DSL over HTML format in a way which is convenient to use.
I think such API would benefit more advanced use case, also the benefit of splitting the assemblies / nuget packages, then you can reference only the model in projects dealing with formulation and handling the results back, and break down the dependencies in solver specific packages into Flips.Solver.*
runtime packages.
Proposed assemblies:
Flips
everything up toModel
Flips.Solver
the abstraction defining the SolveResult and interface for running the solver with settings and mapping the results back to theModel
Flips.Solver.GoogleOrTools
backend using google-ortools, implementing the interfaces inFlips.Solver
- …
Issue Analytics
- State:
- Created 3 years ago
- Comments:28 (5 by maintainers)
Top GitHub Comments
Something that I started to formulate in my head is a way to support the iterative workflow of:
This would be a really useful way of iteratively exploring a model. Now, here’s the problem, how do you support that?
Right now the
Model
type is a simple record with two fields:I can think of at least two approaches.
Brute Force
Change the
Constraints
field to be an indexed collection and have the index be the Constraint Name. Then add a function:Model.updateConstraint
which would change the value the index is bound to and return a newModel
. That’s simple but here’s a big problem, how do you make it efficient to update the underlying model in the Solver with the updated constraint? Do you you just have to build an entirely new model? If you want to incrementally change it, how do you keep track of what changed since you last solved it? Technically that would work but it would be a poor experience and unusable in the situations I’m thinking about.Model becomes a diff list
Instead of having the
Model
type be a record, have it be a list of diffs. The final state of the model is just a fold over the changes to the initial model. Each change that is performed could be provided a version number that is just an incrementingint
. I imagine it as something like this:Now when you go so solve a model, the solution you get back includes the model and the underlying Solver Model which would depend on the library.
You can look at the first element of
Solutuion.Model.Changes
to get the last version number. Now, if you update or add constraints to the Model, you can instead callSolver.reSolve
This a rough idea and I think it is Solver dependent. I know that CBC, GLOPS, and Gurobi can do this. I’m betting CPLEX can as well. You definitely don’t want to use a
List
to store the changes in theModel
type because you will want to traverse up and down the values. I’m not sure if there is an efficient way to do that but I haven’t looked.Also, when building the model you want to start with the initial change and then work forward in time. I’m sure there’s a data structure for this. I just don’t know if off the to of my head.
I don’t have a use case at this point. Could be useful for performance (or convenience) reasons, but I’m at least not seeing that as a problem so far and the iteration can easily be done on a higher level on the application side.