diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e63edb73..efc3d5f45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added ### Fixed ### Changed +- Speed up `constant * Expr` via C-level API ### Removed ## 6.1.0 - 2026.01.31 diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index f782a46da..d8ba55c8c 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -289,22 +289,24 @@ cdef class Expr: cdef PyObject *v2_ptr = NULL cdef PyObject *old_v_ptr = NULL cdef Term child - cdef double prod_v + cdef double coef if _is_number(other): - f = float(other) - return Expr({v:f*c for v,c in self.terms.items()}) + coef = float(other) + while PyDict_Next(self.terms, &pos1, &k1_ptr, &v1_ptr): + res[k1_ptr] = (v1_ptr) * coef + return Expr(res) elif isinstance(other, Expr): while PyDict_Next(self.terms, &pos1, &k1_ptr, &v1_ptr): pos2 = 0 while PyDict_Next(other.terms, &pos2, &k2_ptr, &v2_ptr): child = (k1_ptr) * (k2_ptr) - prod_v = ((v1_ptr)) * ((v2_ptr)) + coef = ((v1_ptr)) * ((v2_ptr)) if (old_v_ptr := PyDict_GetItem(res, child)) != NULL: - res[child] = (old_v_ptr) + prod_v + res[child] = (old_v_ptr) + coef else: - res[child] = prod_v + res[child] = coef return Expr(res) elif isinstance(other, GenExpr): diff --git a/tests/test_expr.py b/tests/test_expr.py index a4e739b76..855f47cff 100644 --- a/tests/test_expr.py +++ b/tests/test_expr.py @@ -224,6 +224,11 @@ def test_mul(): x = m.addVar(name="x") y = m.addVar(name="y") + # test Expr * number + assert str((x + y) * 2.0) == "Expr({Term(x): 2.0, Term(y): 2.0})" + assert str(2.0 * (x + y)) == "Expr({Term(x): 2.0, Term(y): 2.0})" + + # test Expr * Expr assert str(Expr({CONST: 1.0}) * x) == "Expr({Term(x): 1.0})" assert str(y * Expr({CONST: -1.0})) == "Expr({Term(y): -1.0})" assert str((x - x) * y) == "Expr({Term(x, y): 0.0})"