Skip to content

Refactor general expression conversion into per-class to_scip_expr - #1268

Open
Zeroto521 wants to merge 11 commits into
scipopt:masterfrom
Zeroto521:to-scipexpr
Open

Zeroto521 wants to merge 11 commits into
scipopt:masterfrom
Zeroto521:to-scipexpr

Conversation

@Zeroto521

@Zeroto521 Zeroto521 commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Replace the two-pass general expression conversion (expr_to_nodes / expr_to_array / value_to_array building an intermediate list[(type, indices)] node table and then walking it) with per-class to_scip_expr(self, scip) recursion on GenExpr and its subclasses.

# before
nodes = expr_to_nodes(expr)
for i, node in enumerate(nodes):
    t = node[0]
    if t is VarExpr:
        PY_SCIP_CALL(SCIPcreateExprVar(...))
    elif t is SumExpr:
        ...
    elif t is PowExpr:
        ...
    ...

# after
scip_expr = expr.to_scip_expr(self._scip)

Also hardens the exception paths so an error raised inside SCIPcreateExpr* no longer leaks already-built child SCIP_EXPR references.

Wrap the create/release sequence in SumExpr, ProdExpr, and the seven
unary sub-expressions in try/finally so partial failures do not leak
either the parent expression or already-built children. Apply the
same pattern in _createConsGenNonlinear so a failing
SCIPcreateConsNonlinear no longer leaks the built scip_expr.

While in the area, tighten local index/count types (Py_ssize_t for
container indexing, int for SCIP nchildren) in the new code and drop
the PyCons intermediate variable in _createConsGenNonlinear.
The n_ prefix matches neighboring counters (n, nvars, nbranchvars) and
makes it explicit that the variable is a count rather than a boolean
state. Switch kwargs[...] in _createConsGenNonlinear to double quotes
for consistency with the surrounding code.
Copilot AI balanced review requested due to automatic review settings October 12, 2026 09:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

The index i was left at len(children)-1 after the child loop, so the
constant component overwrote the last child's pointer and left the
final slot (which SCIPcreateExprSum reads because n=len+1) with
uninitialized memory. Initialize i to len(children) before writing
the constant.

Fixes the segfaults in test_nonlinear, test_recipe_nonlinear, and
test_matrix_variable when a GenExpr with nonzero constant is added
to a constraint or objective via _createConsGenNonlinear.
…asses

SumExpr, ProdExpr, PowExpr, and Constant had the ordering reversed from
the six UnaryExpr subclasses (Abs/Exp/Log/Sqrt/Sin/Cos) and from VarExpr,
which already put to_scip_expr first. Align them all so the new
to_scip_expr method appears above the pre-existing _evaluate in every
GenExpr class.
Abs/Exp/Log/Sqrt/Sin/Cos had __repr__ (and Abs's __abs__) sitting between
to_scip_expr and _evaluate. Move the dunder methods above to_scip_expr so
all 11 GenExpr subclasses follow the same layout: init/dunders, then
to_scip_expr, then _evaluate, then copy.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants