You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While looking into possible refactors to support #9, I've had a few thoughts/realizations. Broadly speaking, I'm reconsidering the degree to which ilpy and C++ code is necessary for setting up the problem (vs actually calling the solve() method and solving it). Most of what motile is doing is creating a nice API on top of heavy duty ILP solvers (right?) and until we actually call solve(), we're mostly just managing coefficients (something that could quite easily be done in pure python with acceptable performance).
Motile uses ilpy types liberally throughout the library, when perhaps pure python objects would be easier maintain (in terms of development)
add_costs and motile.Costs mostly deal with ilpy.Variables and motile.Weights ... both pure python objects
add_constraints either deal with ilpy.Expressions (which are essentially just combined ilpy.Variables) or ilpy.Constraint objects, which, while backed by C++ objects in ilpy, is really just a simple container for coefficients and a relation. Similarly, the Solver.constratints object is an ilpy.Constraints object, which is just a std::vector container for ilpy.Constraint. Something which could be represented by a python list or set. In fact, Solver.constratints can be swapped out quite easily for a python set without breaking tests, which would make Support removal of constraints #9 much easier to solve.
The primary place where the power of the C-extension is leveraged is in Solver.solve()... and that method actually rebuilds almost all of the ilpy types, including recreating the ilpy.Solver and ilpy.Objective. So it would be very feasible to make that the only method that works with ilpy, leaving all of the rest of the logic internal to motile, likely facilitating/easing development.
In some quick tests, I was actually able to swap ilpy almost entirely for pulp another python library quite similar to ilpy that I just discovered. Pulp also wraps SCIP and gurobi, as ilpy does, in addition to a bunch of other solvers. It seems well maintained (80 contributors, ~2k stars, last commit this month) and is available on conda forge (which would help solve consider putting SolverFactory logic in python ilpy#14 wherein you must have gurobi installed to run ilpy, even if you don't have the license). To change Solver.solve() to use pulp, it would look like this:
In my tests, the pulp solver did come up with the same final objective value for every test run in motile, but it yielded with different values for each variables... So I might need some help understanding expectations in the results. (@cmalinmayor, perhaps you could help me evaluate that?)
this might be particularly useful in the context of the napari plugin you're building, as that may require your full package (and all of its dependencies) to be on conda-forge if you want it to be easily installable (and currently, we can't build ilpy on conda-forge because it demands gurobi be present at build time, and that's not on conda-froge)
While looking into possible refactors to support #9, I've had a few thoughts/realizations. Broadly speaking, I'm reconsidering the degree to which ilpy and C++ code is necessary for setting up the problem (vs actually calling the
solve()method and solving it). Most of what motile is doing is creating a nice API on top of heavy duty ILP solvers (right?) and until we actually callsolve(), we're mostly just managing coefficients (something that could quite easily be done in pure python with acceptable performance).ilpytypes liberally throughout the library, when perhaps pure python objects would be easier maintain (in terms of development)add_costsandmotile.Costsmostly deal withilpy.Variablesandmotile.Weights... both pure python objectsadd_constraintseither deal withilpy.Expressions(which are essentially just combinedilpy.Variables) orilpy.Constraintobjects, which, while backed by C++ objects in ilpy, is really just a simple container for coefficients and a relation. Similarly, theSolver.constratintsobject is anilpy.Constraintsobject, which is just astd::vectorcontainer forilpy.Constraint. Something which could be represented by a python list or set. In fact,Solver.constratintscan be swapped out quite easily for a python set without breaking tests, which would make Support removal of constraints #9 much easier to solve.Solver.solve()... and that method actually rebuilds almost all of the ilpy types, including recreating theilpy.Solverandilpy.Objective. So it would be very feasible to make that the only method that works with ilpy, leaving all of the rest of the logic internal to motile, likely facilitating/easing development.Solver.solve()to use pulp, it would look like this:In my tests, the pulp solver did come up with the same final objective value for every test run in motile, but it yielded with different values for each variables... So I might need some help understanding expectations in the results. (@cmalinmayor, perhaps you could help me evaluate that?)
Summary
So, the thing I'm proposing we consider is this:
ilpy.Expressionandilpy.Variable, both of which were pure python objects I added in Add expression objects, convertible to LinearConstraints ilpy#9 that could just as easily bemotile.Expressionandmotile.Variable.ilpyin thesolve()method. Which would make it easier to experiment with other backends like pulp.I can open an example PR for that if you like. @funkey @cmalinmayor
this might be particularly useful in the context of the napari plugin you're building, as that may require your full package (and all of its dependencies) to be on conda-forge if you want it to be easily installable (and currently, we can't build ilpy on conda-forge because it demands gurobi be present at build time, and that's not on conda-froge)