Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

## 6.2.0 - 2026.04.23
### Added
- Added `solveProbingLPWithPricing()` and test
- `Expr` and `GenExpr` support NumPy unary functions (`np.sin`, `np.cos`, `np.sqrt`, `np.exp`, `np.log`, `np.absolute`, `np.negative`)
- `Expr` and `GenExpr` support NumPy binary functions (`np.add`, `np.subtract`, `np.multiply`, `np.divide`, `np.true_divide`, `np.power`, `np.less_equal`, `np.greater_equal`, `np.equal`)
- Added `getBase()` and `setBase()` methods to `LP` class for getting/setting basis status
Expand Down
1 change: 1 addition & 0 deletions src/pyscipopt/scip.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ cdef extern from "scip/scip.h":
SCIP_RETCODE SCIPchgVarUbProbing(SCIP* scip, SCIP_VAR* var, SCIP_Real newbound)
SCIP_RETCODE SCIPchgVarLbProbing(SCIP* scip, SCIP_VAR* var, SCIP_Real newbound)
SCIP_RETCODE SCIPsolveProbingLP(SCIP* scip, int itlim, SCIP_Bool* lperror, SCIP_Bool* cutoff)
SCIP_RETCODE SCIPsolveProbingLPWithPricing(SCIP* scip, SCIP_Bool pretendroot, SCIP_Bool displayinfo, int maxpricerounds, SCIP_Bool* lperror, SCIP_Bool* cutoff)
SCIP_RETCODE SCIPendProbing(SCIP* scip)
SCIP_RETCODE SCIPfixVarProbing(SCIP* scip, SCIP_VAR* var, SCIP_Real fixedval)
SCIP_Bool SCIPisObjChangedProbing(SCIP* scip)
Expand Down
29 changes: 29 additions & 0 deletions src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -10413,6 +10413,35 @@ cdef class Model:
PY_SCIP_CALL( SCIPsolveProbingLP(self._scip, itlim, &lperror, &cutoff) )
return lperror, cutoff

def solveProbingLPWithPricing(self, pretendroot=False, displayinfo=False, maxpricerounds=-1):
"""
Solves the LP at the current probing node (cannot be applied at preprocessing stage) and applies pricing
until the LP is solved to optimality; no separation is applied.

Parameters
----------
pretendroot : bool
should the pricers be called as if we are at the root node? (Default value = False)
displayinfo : bool
should info lines be displayed after each pricing round? (Default value = False)
maxpricerounds : int
maximal number of pricing rounds (-1: no limit); a finite limit means that the LP might not be
solved to optimality! (Default value = -1)

Returns
-------
lperror : bool
whether an unresolved LP error occurred
cutoff : bool
whether the probing LP was infeasible or the objective limit was reached

"""
cdef SCIP_Bool lperror
cdef SCIP_Bool cutoff

PY_SCIP_CALL( SCIPsolveProbingLPWithPricing(self._scip, pretendroot, displayinfo, maxpricerounds, &lperror, &cutoff) )
return lperror, cutoff

def applyCutsProbing(self):
"""
Applies the cuts in the separation storage to the LP and clears the storage afterwards;
Expand Down
6 changes: 6 additions & 0 deletions src/pyscipopt/scip.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1555,6 +1555,12 @@ class Model:
def solveConcurrent(self) -> Incomplete: ...
def solveDiveLP(self, itlim: Incomplete = ...) -> Incomplete: ...
def solveProbingLP(self, itlim: Incomplete = ...) -> Incomplete: ...
def solveProbingLPWithPricing(
self,
pretendroot: Incomplete = ...,
displayinfo: Incomplete = ...,
maxpricerounds: Incomplete = ...,
) -> Incomplete: ...
def sortAndCons(self, and_cons: Incomplete) -> Incomplete: ...
def startDive(self) -> Incomplete: ...
def startProbing(self) -> Incomplete: ...
Expand Down
51 changes: 50 additions & 1 deletion tests/test_branch_probing_lp.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,53 @@ def test_branching():
print("t", m.getVal(t))

assert my_branchrule.was_called_val
assert my_branchrule.was_called_int
assert my_branchrule.was_called_int


class ProbingPricingBranching(Branchrule):

def __init__(self, model):
self.model = model
self.was_called = False

def branchexeclp(self, allowaddcons):
if self.was_called:
return {"result": SCIP_RESULT.DIDNOTRUN}

self.model.startProbing()
self.model.constructLP()
lperror, cutoff = self.model.solveProbingLPWithPricing(
pretendroot=True, displayinfo=False, maxpricerounds=0
)
assert isinstance(lperror, bool)
assert isinstance(cutoff, bool)
assert not lperror

lperror2, cutoff2 = self.model.solveProbingLPWithPricing()
assert isinstance(lperror2, bool)
assert isinstance(cutoff2, bool)

self.model.endProbing()
Comment thread
Joao-Dionisio marked this conversation as resolved.
self.was_called = True
return {"result": SCIP_RESULT.DIDNOTRUN}


def test_solve_probing_lp_with_pricing():
m = Model()
m.setHeuristics(SCIP_PARAMSETTING.OFF)
m.setSeparating(SCIP_PARAMSETTING.OFF)
m.setIntParam("presolving/maxrounds", 0)
m.setLongintParam("limits/nodes", 1)

y1 = m.addVar(vtype="B")
y2 = m.addVar(vtype="B")
y3 = m.addVar(vtype="B")
m.addCons(2 * y1 + 3 * y2 + y3 <= 4)
m.setObjective(5 * y1 + 4 * y2 + 3 * y3, sense="maximize")

rule = ProbingPricingBranching(m)
m.includeBranchrule(rule, "probing-pricing-test", "exercise solveProbingLPWithPricing",
priority=10000000, maxdepth=3, maxbounddist=1)
m.optimize()

assert rule.was_called
Loading