@@ -2122,7 +2122,7 @@ cdef class Model:
21222122 return self ._addGenNonlinearCons(cons, ** kwargs)
21232123 else :
21242124 return self ._addNonlinearCons(cons, ** kwargs)
2125-
2125+
21262126 def addConss (self , conss , name = ' ' , initial = True , separate = True ,
21272127 enforce = True , check = True , propagate = True , local = False ,
21282128 modifiable = False , dynamic = False , removable = False ,
@@ -2188,6 +2188,106 @@ cdef class Model:
21882188
21892189 return constraints
21902190
2191+ def addConsDisjunction (self , conss , name = ' ' , initial = True ,
2192+ relaxcons = None , enforce = True , check = True ,
2193+ local = False , modifiable = False , dynamic = False ):
2194+
2195+ def ensure_iterable (elem , length ):
2196+ if isinstance (elem, Iterable):
2197+ return elem
2198+ else :
2199+ return list (repeat(elem, length))
2200+ assert isinstance (conss, Iterable), " Given constraint list is not iterable"
2201+
2202+ conss = list (conss)
2203+ n_conss = len (conss)
2204+ assert n_conss >= 2 , " Given constraint list contains fewer than 2 items!"
2205+
2206+ cdef SCIP_CONS* disj_cons
2207+
2208+ cdef SCIP_CONS* scip_cons
2209+
2210+ cdef SCIP_EXPR* scip_expr
2211+
2212+ PY_SCIP_CALL(SCIPcreateConsDisjunction(
2213+ self ._scip, & disj_cons, str_conversion(name), 0 , & scip_cons, NULL ,
2214+ initial, enforce, check, local, modifiable, dynamic
2215+ ))
2216+
2217+ # PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons))
2218+
2219+ for i, cons in enumerate (conss):
2220+ deg = cons.expr.degree()
2221+ assert isinstance (cons, ExprCons), " given constraint is not ExprCons but %s " % cons.__class__ .__name__
2222+
2223+ kwargs = dict (name = ' c' + str (SCIPgetNConss(self ._scip)+ 1 ),initial = True ,separate = True ,
2224+ enforce = True , check = True , propagate = True , local = False ,
2225+ modifiable = False , dynamic = False , removable = False ,
2226+ stickingatnode = False )
2227+
2228+ kwargs[' lhs' ] = - SCIPinfinity(self ._scip) if cons._lhs is None else cons._lhs
2229+ kwargs[' rhs' ] = SCIPinfinity(self ._scip) if cons._rhs is None else cons._rhs
2230+ terms = cons.expr.terms
2231+
2232+ if deg <= 1 :
2233+ nvars = len (terms.items())
2234+ vars_array = < SCIP_VAR** > malloc(nvars * sizeof(SCIP_VAR* ))
2235+ coeffs_array = < SCIP_Real* > malloc(nvars * sizeof(SCIP_Real))
2236+
2237+ for i, (key, coeff) in enumerate (terms.items()):
2238+ vars_array[i] = < SCIP_VAR* > (< Variable> key[0 ]).scip_var
2239+ coeffs_array[i] = < SCIP_Real> coeff
2240+
2241+ PY_SCIP_CALL(SCIPcreateConsLinear(
2242+ self ._scip, & scip_cons, str_conversion(kwargs[' name' ]), nvars, vars_array, coeffs_array,
2243+ kwargs[' lhs' ], kwargs[' rhs' ], kwargs[' initial' ],
2244+ kwargs[' separate' ], kwargs[' enforce' ], kwargs[' check' ],
2245+ kwargs[' propagate' ], kwargs[' local' ], kwargs[' modifiable' ],
2246+ kwargs[' dynamic' ], kwargs[' removable' ], kwargs[' stickingatnode' ]))
2247+ PY_SCIP_CALL(SCIPaddConsElemDisjunction(self ._scip, disj_cons, scip_cons))
2248+ PY_SCIP_CALL(SCIPreleaseCons(self ._scip, & scip_cons))
2249+ free(vars_array)
2250+ free(coeffs_array)
2251+ elif deg <= 2 :
2252+ PY_SCIP_CALL(SCIPcreateConsQuadraticNonlinear(
2253+ self ._scip, & scip_cons, str_conversion(kwargs[' name' ]),
2254+ 0 , NULL , NULL , # linear
2255+ 0 , NULL , NULL , NULL , # quadratc
2256+ kwargs[' lhs' ], kwargs[' rhs' ],
2257+ kwargs[' initial' ], kwargs[' separate' ], kwargs[' enforce' ],
2258+ kwargs[' check' ], kwargs[' propagate' ], kwargs[' local' ],
2259+ kwargs[' modifiable' ], kwargs[' dynamic' ], kwargs[' removable' ]))
2260+ for v, c in terms.items():
2261+ if len (v) == 1 : # linear
2262+ var = < Variable> v[0 ]
2263+ PY_SCIP_CALL(SCIPaddLinearVarNonlinear(self ._scip, scip_cons, var.scip_var, c))
2264+ else : # quadratic
2265+ assert len (v) == 2 , ' term length must be 1 or 2 but it is %s ' % len (v)
2266+
2267+ varexprs = < SCIP_EXPR** > malloc(2 * sizeof(SCIP_EXPR* ))
2268+ var1, var2 = < Variable> v[0 ], < Variable> v[1 ]
2269+ PY_SCIP_CALL( SCIPcreateExprVar(self ._scip, & varexprs[0 ], var1.scip_var, NULL , NULL ) )
2270+ PY_SCIP_CALL( SCIPcreateExprVar(self ._scip, & varexprs[1 ], var2.scip_var, NULL , NULL ) )
2271+ PY_SCIP_CALL( SCIPcreateExprProduct(self ._scip, & scip_expr, 2 , varexprs, 1.0 , NULL , NULL ) )
2272+
2273+ PY_SCIP_CALL( SCIPaddExprNonlinear(self ._scip, scip_cons, scip_expr, c) )
2274+
2275+ PY_SCIP_CALL(SCIPaddConsElemDisjunction(self ._scip, disj_cons, scip_cons))
2276+ PY_SCIP_CALL( SCIPreleaseExpr(self ._scip, & scip_expr) )
2277+ PY_SCIP_CALL(SCIPreleaseCons(self ._scip, & scip_cons))
2278+ PY_SCIP_CALL( SCIPreleaseExpr(self ._scip, & varexprs[1 ]) )
2279+ PY_SCIP_CALL( SCIPreleaseExpr(self ._scip, & varexprs[0 ]) )
2280+ free(varexprs)
2281+ else :
2282+ raise NotImplementedError (" Only Linear Expressions are currently supported" )
2283+
2284+
2285+ PY_SCIP_CALL(SCIPaddCons(self ._scip, disj_cons))
2286+ PyCons = Constraint.create(disj_cons)
2287+ PY_SCIP_CALL(SCIPreleaseCons(self ._scip, & disj_cons))
2288+ return PyCons
2289+
2290+
21912291 def getConsNVars (self , Constraint constraint ):
21922292 """
21932293 Gets number of variables in a constraint.
0 commit comments