Skip to content

Commit ca6fc75

Browse files
Add method to add multiple constraints at once
1 parent 77050b5 commit ca6fc75

File tree

2 files changed

+69
-0
lines changed

2 files changed

+69
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44
### Added
5+
- add convenience function `Model.addConss()` to add multiple constraints at once
56
### Fixed
67
### Changed
78
### Removed

src/pyscipopt/scip.pyx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ from cpython.pycapsule cimport PyCapsule_New, PyCapsule_IsValid, PyCapsule_GetPo
1212
from libc.stdlib cimport malloc, free
1313
from libc.stdio cimport fdopen
1414

15+
from collections.abc import Iterable
16+
from itertools import repeat
17+
1518
include "expr.pxi"
1619
include "lp.pxi"
1720
include "benders.pxi"
@@ -2019,6 +2022,71 @@ cdef class Model:
20192022
else:
20202023
return self._addNonlinearCons(cons, **kwargs)
20212024

2025+
def addConss(self, conss, name='', initial=True, separate=True,
2026+
enforce=True, check=True, propagate=True, local=False,
2027+
modifiable=False, dynamic=False, removable=False,
2028+
stickingatnode=False):
2029+
"""Adds multiple linear or quadratic constraints.
2030+
2031+
Each of the constraints is added to the model using Model.addCons().
2032+
2033+
For all parameters, except @p conss, this method behaves differently depending on the type of the passed argument:
2034+
1. If the value is iterable, it must be of the same length as @p conss. For each constraint, Model.addCons() will be called with the value at the corresponding index.
2035+
2. Else, the (default) value will be applied to all of the constraints.
2036+
2037+
:param conss An iterable of constraint objects. Any iterable will be converted into a list before further processing.
2038+
:param name: the names of the constraints, generic name if empty (Default value = ''). If a single string is passed, it will be suffixed by an underscore and the enumerated index of the constraint (starting with 0).
2039+
:param initial: should the LP relaxation of constraints be in the initial LP? (Default value = True)
2040+
:param separate: should the constraints be separated during LP processing? (Default value = True)
2041+
:param enforce: should the constraints be enforced during node processing? (Default value = True)
2042+
:param check: should the constraints be checked for feasibility? (Default value = True)
2043+
:param propagate: should the constraints be propagated during node processing? (Default value = True)
2044+
:param local: are the constraints only valid locally? (Default value = False)
2045+
:param modifiable: are the constraints modifiable (subject to column generation)? (Default value = False)
2046+
:param dynamic: are the constraints subject to aging? (Default value = False)
2047+
:param removable: should the relaxation be removed from the LP due to aging or cleanup? (Default value = False)
2048+
:param stickingatnode: should the constraints always be kept at the node where it was added, even if it may be @oved to a more global node? (Default value = False)
2049+
:return A list of added @ref scip#Constraint "Constraint" objects.
2050+
2051+
:see addCons()
2052+
"""
2053+
def ensure_iterable(elem, length):
2054+
if isinstance(elem, Iterable):
2055+
return elem
2056+
else:
2057+
return list(repeat(elem, length))
2058+
2059+
assert isinstance(conss, Iterable), "Given constraint list is not iterable."
2060+
2061+
conss = list(conss)
2062+
n_conss = len(conss)
2063+
2064+
if isinstance(name, str):
2065+
if name == "":
2066+
name = ["" for idx in range(n_conss)]
2067+
else:
2068+
name = ["%s_%s" % (name, idx) for idx in range(n_conss)]
2069+
initial = ensure_iterable(initial, n_conss)
2070+
separate = ensure_iterable(separate, n_conss)
2071+
enforce = ensure_iterable(enforce, n_conss)
2072+
check = ensure_iterable(check, n_conss)
2073+
propagate = ensure_iterable(propagate, n_conss)
2074+
local = ensure_iterable(local, n_conss)
2075+
modifiable = ensure_iterable(modifiable, n_conss)
2076+
dynamic = ensure_iterable(dynamic, n_conss)
2077+
removable = ensure_iterable(removable, n_conss)
2078+
stickingatnode = ensure_iterable(stickingatnode, n_conss)
2079+
2080+
constraints = []
2081+
for i, cons in enumerate(conss):
2082+
constraints.append(
2083+
self.addCons(cons, name[i], initial[i], separate[i], enforce[i],
2084+
check[i], propagate[i], local[i], modifiable[i],
2085+
dynamic[i], removable[i], stickingatnode[i])
2086+
)
2087+
2088+
return constraints
2089+
20222090
def _addLinCons(self, ExprCons lincons, **kwargs):
20232091
assert isinstance(lincons, ExprCons), "given constraint is not ExprCons but %s" % lincons.__class__.__name__
20242092

0 commit comments

Comments
 (0)