Skip to content

Commit faa41eb

Browse files
Sebastian Hegersebheger
authored andcommitted
Update arithmetic of VAR entity and fixes attribute query
Arithmetics with VARs now returns LinExpr in majority of cases and fixes variables attribute query
1 parent 794b13a commit faa41eb

File tree

4 files changed

+613
-64
lines changed

4 files changed

+613
-64
lines changed

mip/cbc.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,6 +1358,9 @@ def var_get_name(self, idx: int) -> str:
13581358
def var_get_index(self, name: str) -> int:
13591359
return cbclib.Cbc_getColNameIndex(self._model, name.encode("utf-8"))
13601360

1361+
def var_get_branch_priority(self, var: "Var") -> numbers.Real:
1362+
return 0 # todo: modify branch priority in CBC
1363+
13611364
def constr_get_index(self, name: str) -> int:
13621365
return cbclib.Cbc_getRowNameIndex(self._model, name.encode("utf-8"))
13631366

@@ -1382,6 +1385,9 @@ def var_get_var_type(self, var: "Var") -> str:
13821385

13831386
return CONTINUOUS
13841387

1388+
def var_set_column(self, var: "Var", value: Column):
1389+
raise NotImplementedError("Cbc functionality currently unavailable")
1390+
13851391
def var_get_column(self, var: "Var") -> Column:
13861392
numnz = cbclib.Cbc_getColNz(self._model, var.idx)
13871393
if numnz == 0:
@@ -1393,7 +1399,7 @@ def var_get_column(self, var: "Var") -> Column:
13931399
ccoef = cbclib.Cbc_getColCoeffs(self._model, var.idx)
13941400

13951401
return Column(
1396-
[Constr(self.model, cidx[i]) for i in range(numnz)],
1402+
[self.model.constrs[cidx[i]] for i in range(numnz)],
13971403
[ccoef[i] for i in range(numnz)],
13981404
)
13991405

mip/entities.py

Lines changed: 61 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -601,14 +601,14 @@ class Var:
601601
"""Decision variable of the :class:`~mip.Model`. The creation of
602602
variables is performed calling the :meth:`~mip.Model.add_var`."""
603603

604-
__slots__ = ["__model", "idx"]
604+
__slots__ = ["_model", "_idx"]
605605

606606
def __init__(self, model: "mip.Model", idx: int):
607-
self.__model = model
608-
self.idx = idx
607+
self._model = model
608+
self._idx = idx
609609

610610
def __hash__(self) -> int:
611-
return self.idx
611+
return self._idx
612612

613613
def __add__(
614614
self, other: Union["mip.Var", LinExpr, numbers.Real]
@@ -634,44 +634,40 @@ def __sub__(
634634
) -> Union["mip.Var", LinExpr]:
635635
if isinstance(other, Var):
636636
return LinExpr([self, other], [1, -1])
637-
elif isinstance(other, LinExpr):
637+
if isinstance(other, LinExpr):
638638
return (-other).__add__(self)
639-
elif isinstance(other, numbers.Real):
639+
if isinstance(other, numbers.Real):
640640
if fabs(other) < mip.EPS:
641641
return self
642642
return LinExpr([self], [1], -other)
643-
else:
644-
raise TypeError("type {} not supported".format(type(other)))
643+
644+
raise TypeError("type {} not supported".format(type(other)))
645645

646646
def __rsub__(
647647
self, other: Union["mip.Var", LinExpr, numbers.Real]
648648
) -> Union["mip.Var", LinExpr]:
649649
if isinstance(other, Var):
650650
return LinExpr([self, other], [-1, 1])
651-
elif isinstance(other, LinExpr):
651+
if isinstance(other, LinExpr):
652652
return other.__sub__(self)
653-
elif isinstance(other, numbers.Real):
653+
if isinstance(other, numbers.Real):
654654
return LinExpr([self], [-1], other)
655-
else:
656-
raise TypeError("type {} not supported".format(type(other)))
657655

658-
def __mul__(self, other: numbers.Real) -> Union["mip.Var", numbers.Real, LinExpr]:
656+
raise TypeError("type {} not supported".format(type(other)))
657+
658+
def __mul__(self, other: numbers.Real) -> LinExpr:
659659
if not isinstance(other, numbers.Real):
660660
raise TypeError("Can not multiply with type {}".format(type(other)))
661-
if fabs(other) < mip.EPS:
662-
return other
663-
if fabs(other - 1) < mip.EPS:
664-
return self
665661
return LinExpr([self], [other])
666662

667-
def __rmul__(self, other: numbers.Real) -> Union["mip.Var", numbers.Real, LinExpr]:
663+
def __rmul__(self, other: numbers.Real) -> LinExpr:
668664
return self.__mul__(other)
669665

670-
def __truediv__(
671-
self, other: numbers.Real
672-
) -> Union["mip.Var", numbers.Real, LinExpr]:
666+
def __truediv__(self, other: numbers.Real) -> LinExpr:
673667
if not isinstance(other, numbers.Real):
674668
raise TypeError("Can not divide with type {}".format(type(other)))
669+
if isinstance(other, numbers.Real) and abs(other) < mip.EPS:
670+
raise ZeroDivisionError("Variable division by zero")
675671
return self.__mul__(1.0 / other)
676672

677673
def __neg__(self) -> LinExpr:
@@ -680,90 +676,84 @@ def __neg__(self) -> LinExpr:
680676
def __eq__(self, other) -> LinExpr:
681677
if isinstance(other, Var):
682678
return LinExpr([self, other], [1, -1], sense="=")
683-
elif isinstance(other, LinExpr):
684-
return other == self
685-
elif isinstance(other, numbers.Real):
686-
if other != 0:
687-
return LinExpr([self], [1], -1 * other, sense="=")
688-
return LinExpr([self], [1], sense="=")
689-
else:
690-
raise TypeError("type {} not supported".format(type(other)))
679+
if isinstance(other, LinExpr):
680+
return LinExpr([self], [1]) == other
681+
if isinstance(other, numbers.Real):
682+
return LinExpr([self], [1], -1 * other, sense="=")
683+
684+
raise TypeError("type {} not supported".format(type(other)))
691685

692686
def __le__(self, other: Union["mip.Var", LinExpr, numbers.Real]) -> LinExpr:
693687
if isinstance(other, Var):
694688
return LinExpr([self, other], [1, -1], sense="<")
695-
elif isinstance(other, LinExpr):
696-
return other >= self
697-
elif isinstance(other, numbers.Real):
698-
if other != 0:
699-
return LinExpr([self], [1], -1 * other, sense="<")
700-
return LinExpr([self], [1], sense="<")
701-
else:
702-
raise TypeError("type {} not supported".format(type(other)))
689+
if isinstance(other, LinExpr):
690+
return LinExpr([self], [1]) <= other
691+
if isinstance(other, numbers.Real):
692+
return LinExpr([self], [1], -1 * other, sense="<")
693+
694+
raise TypeError("type {} not supported".format(type(other)))
703695

704696
def __ge__(self, other: Union["mip.Var", LinExpr, numbers.Real]) -> LinExpr:
705697
if isinstance(other, Var):
706698
return LinExpr([self, other], [1, -1], sense=">")
707-
elif isinstance(other, LinExpr):
708-
return other <= self
709-
elif isinstance(other, numbers.Real):
710-
if other != 0:
711-
return LinExpr([self], [1], -1 * other, sense=">")
712-
return LinExpr([self], [1], sense=">")
713-
else:
714-
raise TypeError("type {} not supported".format(type(other)))
699+
if isinstance(other, LinExpr):
700+
return LinExpr([self], [1]) >= other
701+
if isinstance(other, numbers.Real):
702+
return LinExpr([self], [1], -1 * other, sense=">")
703+
704+
raise TypeError("type {} not supported".format(type(other)))
715705

716706
@property
717707
def name(self) -> str:
718708
"""Variable name."""
719-
return self.__model.solver.var_get_name(self.idx)
709+
return self._model.solver.var_get_name(self.idx)
720710

721711
def __str__(self) -> str:
722712
return self.name
723713

724714
@property
725715
def lb(self) -> numbers.Real:
726716
"""Variable lower bound."""
727-
return self.__model.solver.var_get_lb(self)
717+
return self._model.solver.var_get_lb(self)
728718

729719
@lb.setter
730720
def lb(self, value: numbers.Real):
731-
self.__model.solver.var_set_lb(self, value)
721+
self._model.solver.var_set_lb(self, value)
732722

733723
@property
734724
def ub(self) -> numbers.Real:
735725
"""Variable upper bound."""
736-
return self.__model.solver.var_get_ub(self)
726+
return self._model.solver.var_get_ub(self)
737727

738728
@ub.setter
739729
def ub(self, value: numbers.Real):
740-
self.__model.solver.var_set_ub(self, value)
730+
self._model.solver.var_set_ub(self, value)
741731

742732
@property
743733
def obj(self) -> numbers.Real:
744734
"""Coefficient of variable in the objective function."""
745-
return self.__model.solver.var_get_obj(self)
735+
return self._model.solver.var_get_obj(self)
746736

747737
@obj.setter
748738
def obj(self, value: numbers.Real):
749-
self.__model.solver.var_set_obj(self, value)
739+
self._model.solver.var_set_obj(self, value)
750740

751741
@property
752742
def branch_priority(self) -> numbers.Real:
753743
"""
754744
Variable's branching priority in the branch and bound process.
755745
Note: variables with higher priority are selected first. Default value is zero.
756746
"""
757-
return self.__model.solver.var_get_branch_priority(self)
747+
return self._model.solver.var_get_branch_priority(self)
758748

759749
@branch_priority.setter
760750
def branch_priority(self, value: numbers.Real):
761-
self.__model.solver.var_set_branch_priority(self, value)
751+
self._model.solver.var_set_branch_priority(self, value)
762752

763753
@property
764754
def var_type(self) -> str:
765755
"""Variable type, ('B') BINARY, ('C') CONTINUOUS and ('I') INTEGER."""
766-
return self.__model.solver.var_get_var_type(self)
756+
return self._model.solver.var_get_var_type(self)
767757

768758
@var_type.setter
769759
def var_type(self, value: str):
@@ -773,42 +763,42 @@ def var_type(self, value: str):
773763
(mip.BINARY, mip.CONTINUOUS, mip.INTEGER), value
774764
)
775765
)
776-
self.__model.solver.var_set_var_type(self, value)
766+
self._model.solver.var_set_var_type(self, value)
777767

778768
@property
779769
def column(self) -> Column:
780770
"""Variable coefficients in constraints.
781771
782772
:rtype: mip.Column
783773
"""
784-
return self.__model.solver.var_get_column(self)
774+
return self._model.solver.var_get_column(self)
785775

786776
@column.setter
787777
def column(self, value: Column):
788-
self.__model.solver.var_set_column(self, value)
778+
self._model.solver.var_set_column(self, value)
789779

790780
@property
791781
def rc(self) -> Optional[numbers.Real]:
792782
"""Reduced cost, only available after a linear programming model (only
793783
continuous variables) is optimized. Note that None is returned if no
794784
optimum solution is available"""
795785

796-
return self.__model.solver.var_get_rc(self)
786+
return self._model.solver.var_get_rc(self)
797787

798788
@property
799789
def x(self) -> Optional[numbers.Real]:
800790
"""Value of this variable in the solution. Note that None is returned
801791
if no solution is not available."""
802-
return self.__model.solver.var_get_x(self)
792+
return self._model.solver.var_get_x(self)
803793

804794
def xi(self, i: int) -> Optional[numbers.Real]:
805795
"""Value for this variable in the :math:`i`-th solution from the solution
806796
pool. Note that None is returned if the solution is not available."""
807-
if self.__model.status in [
797+
if self._model.status in [
808798
mip.OptimizationStatus.OPTIMAL,
809799
mip.OptimizationStatus.FEASIBLE,
810800
]:
811-
return self.__model.solver.var_get_xi(self, i)
801+
return self._model.solver.var_get_xi(self, i)
812802
return None
813803

814804
def __float__(self):
@@ -821,7 +811,15 @@ def model(self) -> "mip.Model":
821811
822812
:rtype: mip.Model
823813
"""
824-
return self.__model
814+
return self._model
815+
816+
@property
817+
def idx(self) -> int:
818+
"""Internal index of the variable to the model.
819+
820+
:rtype: int
821+
"""
822+
return self._idx
825823

826824

827825
class ConflictGraph:

mip/gurobi.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@
262262
GRBsetdblattrelement = grblib.GRBsetdblattrelement
263263
GRBsetintattr = grblib.GRBsetintattr
264264
GRBsetintattrelement = grblib.GRBsetintattrelement
265+
GRBgetintattrelement = grblib.GRBgetintattrelement
265266
GRBsetdblattr = grblib.GRBsetdblattr
266267
GRBgetintattr = grblib.GRBgetintattr
267268
GRBgetintparam = grblib.GRBgetintparam
@@ -1241,6 +1242,15 @@ def set_dbl_attr_element(self, name: str, index: int, value: float):
12411242
)
12421243
)
12431244

1245+
def get_int_attr_element(self, name: str, index: int) -> float:
1246+
res = ffi.new("int *")
1247+
error = GRBgetintattrelement(self._model, name.encode("utf-8"), index, res)
1248+
if error != 0:
1249+
raise ParameterNotAvailable(
1250+
"Error get grb double attr element {} index {}".format(name, index)
1251+
)
1252+
return res[0]
1253+
12441254
def set_int_attr_element(self, name: str, index: int, value: int):
12451255
error = GRBsetintattrelement(self._model, name.encode("utf-8"), index, value)
12461256
if error != 0:

0 commit comments

Comments
 (0)