diff --git a/CHANGELOG.md b/CHANGELOG.md index efd8ed922..05dbcd0aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Added isPositive(), isNegative(), isFeasLE(), isFeasLT(), isFeasGE(), isFeasGT(), isHugeValue(), and tests - Added SCIP_LOCKTYPE, addVarLocksType(), getNLocksDown(), getNLocksUp(), getNLocksDownType(), getNLocksUpType(), and tests ### Fixed +- Raised an error when an expression is used when a variable is required ### Changed ### Removed diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 53bed228f..3b2ee7db7 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -1042,17 +1042,20 @@ cdef class Solution: # fast track for Variable cdef SCIP_Real coeff + cdef _VarArray wrapper if isinstance(expr, Variable): + wrapper = _VarArray(expr) self._checkStage("SCIPgetSolVal") - var = expr - return SCIPgetSolVal(self.scip, self.sol, var.scip_var) + return SCIPgetSolVal(self.scip, self.sol, wrapper.ptr[0]) return sum(self._evaluate(term)*coeff for term, coeff in expr.terms.items() if coeff != 0) def _evaluate(self, term): self._checkStage("SCIPgetSolVal") result = 1 - for var in term.vartuple: - result *= SCIPgetSolVal(self.scip, self.sol, ( var).scip_var) + cdef _VarArray wrapper + wrapper = _VarArray(term.vartuple) + for i in range(len(term.vartuple)): + result *= SCIPgetSolVal(self.scip, self.sol, wrapper.ptr[i]) return result def __setitem__(self, Variable var, value): @@ -2397,6 +2400,32 @@ cdef void relayErrorMessage(void *messagehdlr, FILE *file, const char *msg) noex fputs(msg, file) fflush(file) +cdef class _VarArray: + cdef SCIP_VAR** ptr + cdef int size + + def __cinit__(self, object vars): + if isinstance(vars, Variable): + self.size = 1 + self.ptr = malloc(sizeof(SCIP_VAR*)) + self.ptr[0] = (vars).scip_var + else: + if not isinstance(vars, (list, tuple)): + raise TypeError("Expected Variable or list of Variable, got %s." % type(vars)) + self.size = len(vars) + if self.size == 0: + self.ptr = NULL + else: + self.ptr = malloc(self.size * sizeof(SCIP_VAR*)) + for i, var in enumerate(vars): + if not isinstance(var, Variable): + raise TypeError("Expected Variable, got %s." % type(var)) + self.ptr[i] = (var).scip_var + + def __dealloc__(self): + if self.ptr != NULL: + free(self.ptr) + # - remove create(), includeDefaultPlugins(), createProbBasic() methods # - replace free() by "destructor" # - interface SCIPfreeProb() @@ -3420,6 +3449,7 @@ cdef class Model: cdef int nvars cdef SCIP_Real coef cdef int i + cdef _VarArray wrapper # turn the constant value into an Expr instance for further processing if not isinstance(expr, Expr): @@ -3444,8 +3474,8 @@ cdef class Model: # avoid CONST term of Expr if term != CONST: assert len(term) == 1 - var = term[0] - PY_SCIP_CALL(SCIPchgVarObj(self._scip, var.scip_var, coef)) + wrapper = _VarArray(term[0]) + PY_SCIP_CALL(SCIPchgVarObj(self._scip, wrapper.ptr[0], coef)) if sense == "minimize": self.setMinimize() @@ -5141,9 +5171,11 @@ cdef class Model: cdef SCIP_CONS* scip_cons cdef SCIP_Real coeff cdef int i + cdef _VarArray wrapper for i, (key, coeff) in enumerate(terms.items()): - vars_array[i] = (key[0]).scip_var + wrapper = _VarArray(key[0]) + vars_array[i] = wrapper.ptr[0] coeffs_array[i] = coeff PY_SCIP_CALL(SCIPcreateConsLinear( @@ -5180,6 +5212,7 @@ cdef class Model: cdef SCIP_CONS* scip_cons cdef SCIP_EXPR* prodexpr + cdef _VarArray wrapper PY_SCIP_CALL(SCIPcreateConsQuadraticNonlinear( self._scip, &scip_cons, str_conversion(kwargs['name']), 0, NULL, NULL, # linear @@ -5191,15 +5224,16 @@ cdef class Model: for v, c in terms.items(): if len(v) == 1: # linear - var = v[0] - PY_SCIP_CALL(SCIPaddLinearVarNonlinear(self._scip, scip_cons, var.scip_var, c)) + wrapper = _VarArray(v[0]) + PY_SCIP_CALL(SCIPaddLinearVarNonlinear(self._scip, scip_cons, wrapper.ptr[0], c)) else: # nonlinear assert len(v) == 2, 'term length must be 1 or 2 but it is %s' % len(v) varexprs = malloc(2 * sizeof(SCIP_EXPR*)) - var1, var2 = v[0], v[1] - PY_SCIP_CALL( SCIPcreateExprVar(self._scip, &varexprs[0], var1.scip_var, NULL, NULL) ) - PY_SCIP_CALL( SCIPcreateExprVar(self._scip, &varexprs[1], var2.scip_var, NULL, NULL) ) + wrapper = _VarArray(v[0]) + PY_SCIP_CALL( SCIPcreateExprVar(self._scip, &varexprs[0], wrapper.ptr[0], NULL, NULL) ) + wrapper = _VarArray(v[1]) + PY_SCIP_CALL( SCIPcreateExprVar(self._scip, &varexprs[1], wrapper.ptr[0], NULL, NULL) ) PY_SCIP_CALL( SCIPcreateExprProduct(self._scip, &prodexpr, 2, varexprs, 1.0, NULL, NULL) ) PY_SCIP_CALL( SCIPaddExprNonlinear(self._scip, scip_cons, prodexpr, c) ) @@ -5232,6 +5266,7 @@ cdef class Model: cdef SCIP_EXPR** varexprs cdef SCIP_EXPR** monomials cdef SCIP_CONS* scip_cons + cdef _VarArray wrapper cdef int* idxs cdef int i cdef int j @@ -5239,20 +5274,16 @@ cdef class Model: terms = cons.expr.terms # collect variables - variables = {var.ptr(): var for term in terms for var in term} - variables = list(variables.values()) - varindex = {var.ptr(): i for (i, var) in enumerate(variables)} + variables = {i: [var for var in term] for i, term in enumerate(terms)} # create monomials for terms monomials = malloc(len(terms) * sizeof(SCIP_EXPR*)) termcoefs = malloc(len(terms) * sizeof(SCIP_Real)) for i, (term, coef) in enumerate(terms.items()): - termvars = malloc(len(term) * sizeof(SCIP_VAR*)) - for j, var in enumerate(term): - termvars[j] = (var).scip_var - PY_SCIP_CALL( SCIPcreateExprMonomial(self._scip, &monomials[i], len(term), termvars, NULL, NULL, NULL) ) + wrapper = _VarArray(variables[i]) + + PY_SCIP_CALL( SCIPcreateExprMonomial(self._scip, &monomials[i], wrapper.size, wrapper.ptr, NULL, NULL, NULL) ) termcoefs[i] = coef - free(termvars) # create polynomial from monomials PY_SCIP_CALL( SCIPcreateExprSum(self._scip, &expr, len(terms), monomials, termcoefs, 0.0, NULL, NULL)) @@ -5303,6 +5334,7 @@ cdef class Model: cdef SCIP_EXPR** childrenexpr cdef SCIP_EXPR** scipexprs cdef SCIP_CONS* scip_cons + cdef _VarArray wrapper cdef int nchildren cdef int c cdef int i @@ -5323,18 +5355,15 @@ cdef class Model: for node in nodes: if node[0] == Operator.varidx: nvars += 1 - vars = malloc(nvars * sizeof(SCIP_VAR*)) - varpos = 0 scipexprs = malloc(len(nodes) * sizeof(SCIP_EXPR*)) for i,node in enumerate(nodes): opidx = node[0] if opidx == Operator.varidx: assert len(node[1]) == 1 pyvar = node[1][0] # for vars we store the actual var! - PY_SCIP_CALL( SCIPcreateExprVar(self._scip, &scipexprs[i], (pyvar).scip_var, NULL, NULL) ) - vars[varpos] = (pyvar).scip_var - varpos += 1 + wrapper = _VarArray(pyvar) + PY_SCIP_CALL( SCIPcreateExprVar(self._scip, &scipexprs[i], wrapper.ptr[0], NULL, NULL) ) continue if opidx == Operator.const: assert len(node[1]) == 1 @@ -5393,7 +5422,6 @@ cdef class Model: continue # default: raise NotImplementedError - assert varpos == nvars # create nonlinear constraint for the expression root PY_SCIP_CALL( SCIPcreateConsNonlinear( @@ -5418,7 +5446,6 @@ cdef class Model: # free more memory free(scipexprs) - free(vars) return PyCons @@ -6101,25 +6128,24 @@ cdef class Model: cdef int nvars = len(vars) cdef int i - cdef SCIP_VAR** vars_array = malloc(nvars * sizeof(SCIP_VAR*)) cdef SCIP_Longint* weights_array = malloc(nvars * sizeof(SCIP_Real)) cdef SCIP_CONS* scip_cons + cdef _VarArray wrapper assert nvars == len(weights), "Number of variables and weights must be the same." if name == '': name = 'c'+str(SCIPgetNConss(self._scip)+1) + wrapper = _VarArray(vars) for i in range(nvars): - vars_array[i] = (vars[i]).scip_var weights_array[i] = weights[i] PY_SCIP_CALL(SCIPcreateConsKnapsack( - self._scip, &scip_cons, str_conversion(name), nvars, vars_array, weights_array, + self._scip, &scip_cons, str_conversion(name), nvars, wrapper.ptr, weights_array, capacity, initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode)) - free(vars_array) free(weights_array) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) @@ -6173,6 +6199,7 @@ cdef class Model: cdef SCIP_CONS* scip_cons cdef int nvars cdef int i + cdef _VarArray wrapper PY_SCIP_CALL(SCIPcreateConsSOS1(self._scip, &scip_cons, str_conversion(name), 0, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode)) @@ -6180,15 +6207,14 @@ cdef class Model: if name == '': name = 'c'+str(SCIPgetNConss(self._scip)+1) + nvars = len(vars) + wrapper = _VarArray(vars) if weights is None: - for v in vars: - var = v - PY_SCIP_CALL(SCIPappendVarSOS1(self._scip, scip_cons, var.scip_var)) + for i in range(nvars): + PY_SCIP_CALL(SCIPappendVarSOS1(self._scip, scip_cons, wrapper.ptr[i])) else: - nvars = len(vars) for i in range(nvars): - var = vars[i] - PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, scip_cons, var.scip_var, weights[i])) + PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, scip_cons, wrapper.ptr[i], weights[i])) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) @@ -6238,6 +6264,7 @@ cdef class Model: cdef SCIP_CONS* scip_cons cdef int nvars cdef int i + cdef _VarArray wrapper PY_SCIP_CALL(SCIPcreateConsSOS2(self._scip, &scip_cons, str_conversion(name), 0, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode)) @@ -6245,15 +6272,14 @@ cdef class Model: if name == '': name = 'c'+str(SCIPgetNConss(self._scip)+1) + nvars = len(vars) + wrapper = _VarArray(vars) if weights is None: - for v in vars: - var = v - PY_SCIP_CALL(SCIPappendVarSOS2(self._scip, scip_cons, var.scip_var)) + for i in range(nvars): + PY_SCIP_CALL(SCIPappendVarSOS2(self._scip, scip_cons, wrapper.ptr[i])) else: - nvars = len(vars) for i in range(nvars): - var = vars[i] - PY_SCIP_CALL(SCIPaddVarSOS2(self._scip, scip_cons, var.scip_var, weights[i])) + PY_SCIP_CALL(SCIPaddVarSOS2(self._scip, scip_cons, wrapper.ptr[i], weights[i])) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) @@ -6301,14 +6327,15 @@ cdef class Model: """ cdef int nvars = len(vars) - cdef SCIP_VAR** _vars = malloc(nvars * sizeof(SCIP_VAR*)) + cdef SCIP_VAR** _vars cdef SCIP_VAR* _resvar + cdef _VarArray resvar_wrapper = _VarArray(resvar) + cdef _VarArray vars_wrapper = _VarArray(vars) cdef SCIP_CONS* scip_cons cdef int i - _resvar = (resvar).scip_var - for i, var in enumerate(vars): - _vars[i] = (var).scip_var + _resvar = resvar_wrapper.ptr[0] + _vars = vars_wrapper.ptr if name == '': name = 'c'+str(SCIPgetNConss(self._scip)+1) @@ -6320,8 +6347,6 @@ cdef class Model: pyCons = Constraint.create(scip_cons) PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons)) - free(_vars) - return pyCons def addConsOr(self, vars, resvar, name="", @@ -6366,14 +6391,15 @@ cdef class Model: """ cdef int nvars = len(vars) - cdef SCIP_VAR** _vars = malloc(nvars * sizeof(SCIP_VAR*)) + cdef SCIP_VAR** _vars cdef SCIP_VAR* _resvar + cdef _VarArray resvar_wrapper = _VarArray(resvar) + cdef _VarArray vars_wrapper = _VarArray(vars) cdef SCIP_CONS* scip_cons cdef int i - _resvar = (resvar).scip_var - for i, var in enumerate(vars): - _vars[i] = (var).scip_var + _resvar = resvar_wrapper.ptr[0] + _vars = vars_wrapper.ptr if name == '': name = 'c'+str(SCIPgetNConss(self._scip)+1) @@ -6385,8 +6411,6 @@ cdef class Model: pyCons = Constraint.create(scip_cons) PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons)) - free(_vars) - return pyCons def addConsXor(self, vars, rhsvar, name="", @@ -6430,14 +6454,15 @@ cdef class Model: The newly created XOR constraint """ + assert type(rhsvar) is type(bool()), "Provide BOOLEAN value as rhsvar, you gave %s." % type(rhsvar) + cdef int nvars = len(vars) - cdef SCIP_VAR** _vars = malloc(nvars * sizeof(SCIP_VAR*)) + cdef SCIP_VAR** _vars cdef SCIP_CONS* scip_cons cdef int i + cdef _VarArray wrapper = _VarArray(vars) - assert type(rhsvar) is type(bool()), "Provide BOOLEAN value as rhsvar, you gave %s." % type(rhsvar) - for i, var in enumerate(vars): - _vars[i] = (var).scip_var + _vars = wrapper.ptr if name == '': name = 'c'+str(SCIPgetNConss(self._scip)+1) @@ -6449,8 +6474,6 @@ cdef class Model: pyCons = Constraint.create(scip_cons) PY_SCIP_CALL(SCIPreleaseCons(self._scip, &scip_cons)) - free(_vars) - return pyCons def addConsCardinality(self, consvars, cardval, indvars=None, weights=None, name="", @@ -6504,6 +6527,9 @@ cdef class Model: cdef SCIP_VAR* indvar cdef SCIP_CONS* scip_cons cdef int i + cdef _VarArray v_wrapper + cdef _VarArray indvar_wrapper + cdef SCIP_VAR* scip_var PY_SCIP_CALL(SCIPcreateConsCardinality(self._scip, &scip_cons, str_conversion(name), 0, NULL, cardval, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode)) @@ -6515,16 +6541,19 @@ cdef class Model: if name == '': name = 'c'+str(SCIPgetNConss(self._scip)+1) + v_wrapper = _VarArray(consvars) for i, v in enumerate(consvars): - var = v + scip_var = v_wrapper.ptr[i] if indvars: - indvar = (indvars[i]).scip_var + indvar_wrapper = _VarArray(indvars[i]) + indvar = indvar_wrapper.ptr[0] else: indvar = NULL + if weights is None: - PY_SCIP_CALL(SCIPappendVarCardinality(self._scip, scip_cons, var.scip_var, indvar)) + PY_SCIP_CALL(SCIPappendVarCardinality(self._scip, scip_cons, scip_var, indvar)) else: - PY_SCIP_CALL(SCIPaddVarCardinality(self._scip, scip_cons, var.scip_var, indvar, weights[i])) + PY_SCIP_CALL(SCIPaddVarCardinality(self._scip, scip_cons, scip_var, indvar, weights[i])) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) pyCons = Constraint.create(scip_cons) @@ -6581,6 +6610,8 @@ cdef class Model: cdef SCIP_VAR* _binVar cdef SCIP_CONS* scip_cons cdef SCIP_Real coeff + cdef _VarArray wrapper + assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__ if cons._lhs is not None and cons._rhs is not None: @@ -6600,7 +6631,8 @@ cdef class Model: negate = True if binvar is not None: - _binVar = (binvar).scip_var + wrapper = _VarArray(binvar) + _binVar = wrapper.ptr[0] if not activeone: PY_SCIP_CALL(SCIPgetNegatedVar(self._scip, _binVar, &_binVar)) else: @@ -6611,10 +6643,10 @@ cdef class Model: terms = cons.expr.terms for key, coeff in terms.items(): - var = key[0] if negate: coeff = -coeff - PY_SCIP_CALL(SCIPaddVarIndicator(self._scip, scip_cons, var.scip_var, coeff)) + wrapper = _VarArray(key[0]) + PY_SCIP_CALL(SCIPaddVarIndicator(self._scip, scip_cons, wrapper.ptr[0], coeff)) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) pyCons = Constraint.create(scip_cons) @@ -8233,21 +8265,20 @@ cdef class Model: solution : Solution The corresponding solution in the main model """ - cdef SCIP_VAR** vars = malloc(self.getNVars() * sizeof(SCIP_VAR*)) + cdef SCIP_VAR** vars cdef SCIP_SOL* real_sol cdef SCIP_SOL* subscip_sol = sol.sol cdef SCIP_HEUR* _heur cdef SCIP_Bool success cdef int i + cdef _VarArray wrapper + + assert sub_model.getNVars(False) >= self.getNVars(True), "The sub_model must have at least as many variables as the main model." - for i, var in enumerate(sub_model.getVars()): - vars[i] = (var).scip_var - - name = str_conversion(heur.name) - _heur = SCIPfindHeur(self._scip, name) - PY_SCIP_CALL( SCIPtranslateSubSol(self._scip, sub_model._scip, subscip_sol, _heur, vars, &real_sol) ) + wrapper = _VarArray(sub_model.getVars(False)) + _heur = SCIPfindHeur(self._scip, str_conversion(heur.name)) + PY_SCIP_CALL( SCIPtranslateSubSol(self._scip, sub_model._scip, subscip_sol, _heur, wrapper.ptr, &real_sol) ) solution = Solution.create(self._scip, real_sol) - free(vars) return solution @@ -8758,12 +8789,13 @@ cdef class Model: cdef SCIP_NODE* downchild cdef SCIP_NODE* eqchild cdef SCIP_NODE* upchild + cdef _VarArray wrapper = _VarArray(variable) - PY_SCIP_CALL(SCIPbranchVar(self._scip, (variable).scip_var, &downchild, &eqchild, &upchild)) + PY_SCIP_CALL(SCIPbranchVar(self._scip, wrapper.ptr[0], &downchild, &eqchild, &upchild)) return Node.create(downchild), Node.create(eqchild), Node.create(upchild) - def branchVarVal(self, variable, value): + def branchVarVal(self, Variable variable, value): """ Branches on variable using a value which separates the domain of the variable. @@ -8787,8 +8819,9 @@ cdef class Model: cdef SCIP_NODE* downchild cdef SCIP_NODE* eqchild cdef SCIP_NODE* upchild + cdef _VarArray wrapper = _VarArray(variable) - PY_SCIP_CALL(SCIPbranchVarVal(self._scip, (variable).scip_var, value, &downchild, &eqchild, &upchild)) + PY_SCIP_CALL(SCIPbranchVarVal(self._scip, wrapper.ptr[0], value, &downchild, &eqchild, &upchild)) return Node.create(downchild), Node.create(eqchild), Node.create(upchild) @@ -9782,9 +9815,10 @@ cdef class Model: """ # no need to create a NULL solution wrapper in case we have a variable + cdef _VarArray wrapper if sol == None and isinstance(expr, Variable): - var = expr - return SCIPgetSolVal(self._scip, NULL, var.scip_var) + wrapper = _VarArray(expr) + return SCIPgetSolVal(self._scip, NULL, wrapper.ptr[0]) if sol == None: sol = Solution.create(self._scip, NULL) return sol[expr] @@ -10585,6 +10619,7 @@ cdef class Model: cdef SCIP_OBJSENSE objsense cdef SCIP_Real coef cdef int i + cdef _VarArray wrapper if sense == "minimize": objsense = SCIP_OBJSENSE_MINIMIZE @@ -10611,9 +10646,9 @@ cdef class Model: # avoid CONST term of Expr if term != CONST: assert len(term) == 1 - var = term[0] for i in range(nvars): - if vars[i] == var.scip_var: + wrapper = _VarArray(term[0]) + if vars[i] == wrapper.ptr[0]: _coeffs[i] = coef PY_SCIP_CALL(SCIPchgReoptObjective(self._scip, objsense, vars, &_coeffs[0], nvars)) diff --git a/tests/test_bipartite.py b/tests/test_bipartite.py index a25253524..d7c2788ca 100644 --- a/tests/test_bipartite.py +++ b/tests/test_bipartite.py @@ -40,8 +40,6 @@ def branchexeclp(self, allowaddcons): - - def create_model(): scip = Model() scip.setHeuristics(SCIP_PARAMSETTING.OFF) diff --git a/tests/test_branch_probing_lp.py b/tests/test_branch_probing_lp.py index b8bde8aa9..fe5959190 100644 --- a/tests/test_branch_probing_lp.py +++ b/tests/test_branch_probing_lp.py @@ -39,7 +39,6 @@ def branchexeclp(self, allowaddcons): return {"result": SCIP_RESULT.BRANCHED} - def test_branching(): m = Model() m.setHeuristics(SCIP_PARAMSETTING.OFF) @@ -65,7 +64,6 @@ def test_branching(): m.addCons(quicksum(v for v in more_vars[50::2]) <= (40 - i) * quicksum(v for v in more_vars[405::2])) - m.addCons(r1 >= x0) m.addCons(r2 >= -x0) m.addCons(y0 == r1 +r2) diff --git a/tests/test_cons.py b/tests/test_cons.py index a3c663ee0..63f7010e8 100644 --- a/tests/test_cons.py +++ b/tests/test_cons.py @@ -72,6 +72,23 @@ def test_cons_logical(): assert m.isEQ(m.getVal(result1), 1) assert m.isEQ(m.getVal(result2), 0) +def test_cons_logical_fail(): + m = Model() + x1 = m.addVar(vtype="B") + x2 = m.addVar(vtype="B") + x3 = m.addVar(vtype="B") + x4 = m.addVar(vtype="B") + result1 = m.addVar(vtype="B") + + m.addCons(x3 == 1 - x1) + m.addCons(x4 == 1 - x2) + + # result1 false + with pytest.raises(TypeError): + m.addConsOr([x1*x3, x2*x4], result1) + + m.optimize() + def test_SOScons(): m = Model() x = {} diff --git a/tests/test_model.py b/tests/test_model.py index 85f7f069d..ee95e8e42 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -288,13 +288,11 @@ def test_getStage(): x = m.addVar() m.addCons(x >= 1) - print(m.getStage()) assert m.getStage() == SCIP_STAGE.PROBLEM assert m.getStageName() == "PROBLEM" m.optimize() - print(m.getStage()) assert m.getStage() == SCIP_STAGE.SOLVED assert m.getStageName() == "SOLVED" diff --git a/tests/test_numerics.py b/tests/test_numerics.py index 964e6c216..622f4c03a 100644 --- a/tests/test_numerics.py +++ b/tests/test_numerics.py @@ -18,5 +18,3 @@ def test_numerical_checks(): assert not m.isFeasGT(1, 0.99999) assert m.isGT(1, 0.99999) - -test_numerical_checks()