Speeding up problem set-up
See original GitHub issueIssue Description
This is more a suggestion than an actual bug.
I’ve been using python-mip and cvxpy on the same MI problems, and even though their actual problem-solving performances are usually the same (or even slightly better for python-mip), the problem set-up (defining the objective and the constraints) is much too slow with python-mip.
For instance, let’s consider the following two implementations:
A : np.ndarray of shape (2000, 3069)
d : np.ndarray of shape (3069,)
# python-mip
# the following block takes 11 seconds to run
model = mip.Model("disorders")
x = model.add_var_tensor(shape=(len(d),), name="x", var_type=mip.BINARY)
model.objective = mip.minimize(x.T @ d)
model += (A @ x == 1)
# this line takes 0.13s to run
status = model.optimize()
# cvxpy
# this blocks takes 0.14s
x = cp.Variable(shape=len(d), boolean=True)
obj = cvxpy.Minimize(d.T @ x)
constraints = [A @ x == 1]
prob = cvxpy.Problem(obj, constraints)
optimal = prob.solve()
It’s quite a shame that python-mip is limited by such a non-central part of MIP-solving, that is, setting up the problem. I used CProfile to figure out what’s taking so much time when building the problem, and it seems that the repeated instance checks when multiplying a variable with a scalar are the culprits.
Maybe you could exploit the structured nature of LinExprTensor
to have only one typecheck when multiplying then with arrays… or something like this.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:2
- Comments:6 (4 by maintainers)
Thanks I’ll have a play with that later.
Yeah I’ve found that instance checking is a bit of a bottleneck for me too. In my problems model construction time is the vast majority of the work so I’m really looking for ways to cut it down short of moving to pypy/julia. cylp looks good but its internal postfix notation makes me extremely uncomfortable, and it doesn’t seem to be able to handle constraints constructed from linear expressions (and it has very spartan documentation)
performing operations on variables in python-mip results in a linear expression which is a constant float plus a dictionary map from a variable object to the variable object’s coefficient in the linear expression (plus sense for use in constraints). Operations between two linear expressions result in merging the dictionaries.
The dictionary is, essentially, a DOK sparse vector of coefficients for the linear expression. I think the linear expressions could be sped up considerably by implementing them as sparse matrices:
Because it’s all just array sum and multiplication there shouldn’t need to be any type checking (perhaps make it optional).
Would be challenging to shoehorn it into python-mip at this point, and vectorising it down to the level of adding constraints in the underlying cbc/gurobi solvers might be tricky.
I’m thinking of building this as a proof of concept with scipy sparse matrices. If I can get that working nicely I might try and bring some of the learnings into python-mip.
That didn’t really work, too much overhead in the sparse matrices