When using the matrix-friendly Python API, you may receive the following error:
RuntimeWarning: Chained matrix multiplications of MVars is inefficient, collect numeric terms first when building expressions
This warning indicates that the matrix-friendly API modeling code may be slower than necessary due to the order in which arithmetic operations are carried out. The resulting model is still correct, but you may be able to get better performance by re-arranging the mathematical expressions in your code. This article describes two cases where simple re-arrangements can be performed to produce more efficient code.
Case 1
The following code will raise a runtime performance warning in gurobipy version 11. In this case, we have a 2D MVar and 2D data arrays:
A = np.random.random((50, 50))
B = np.random.random((50, 50))
X = model.addMVar((50, 50))
expr = X @ A @ B # raises RuntimeWarning
The performance issue occurs because Python evaluates this expression from left to right. This means gurobipy must first create a temporary MLinExpr to capture the result of X @ A, then a second MLinExpr to multiply out the result. Tracking this intermediate object is expensive. The additional work can be avoided by running the following code instead:
expr = X @ (A @ B)
Here we override Python's normal operator order to gather together the numeric terms. Since the numeric result A @ B can be computed efficiently by numpy, without interacting with gurobipy modeling objects, only one variable-data interaction occurs and the resulting code is more efficient.
Case 2
The following code will raise a runtime performance warning in gurobipy version 11. In this particular case, both data matrices A and B are 1D row and column vectors respectively, while the MVar X is 2D.
A = np.random.random((1, 50))
B = np.random.random((40, 1))
X = model.addMVar((50, 40))
expr = A @ X @ B # raises RuntimeWarning
The performance issue occurs for similar reasons as example 1 (two expensive computations on modeling objects are required), but it is not immediately clear how to gather together the numeric terms. In this case, the performance issue can be avoided by running the following code instead:
expr = np.kron(A, B.T) @ X.reshape(-1)
The Kronecker product computes the required coefficients term-wise, and aligns the resulting arrays in one dimension to produce the same result more efficiently.
Disabling the warning
It is safe to disable this warning if the performance impact is not severe in your application. For example, the warning may be triggered in cases where the arrays involved are small enough that the overhead is not noticeable, or you may want to disable it in interactive demo code. In either case, you can use Python's built-in warning filters to either disable all such warnings in your environment, or to temporarily suppress warnings at the specific location in your code where this occurs.
Comments
0 comments
Article is closed for comments.