Skip to content

Commit

Permalink
update ilpy API to v0.3.0 (#43)
Browse files Browse the repository at this point in the history
* update ilpy API

* remove conditional
  • Loading branch information
tlambert03 committed May 3, 2023
1 parent a7cb077 commit 5f42466
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 40 deletions.
8 changes: 4 additions & 4 deletions docs/source/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Adding Constraints
New constraints are introduced by subclassing :class:`Constraint
<motile.constraints.Constraint>` and implementing the :func:`instantiate
<motile.constraints.Constraint.instantiate>` method. This method should return
a list of ``ilpy.LinearConstraint``.
a list of ``ilpy.Constraint``.

Imagine we know precisely that we want to track at most :math:`k` objects, but
we don't know beforehand which of the many objects in the track graph those
Expand All @@ -128,7 +128,7 @@ This can be done with a constraint as follows:

appear_indicators = solver.get_variables(NodeAppear)

constraint = ilpy.LinearConstraint()
constraint = ilpy.Constraint()
for appear_indicator, index in appear_indicators.items():
constraint.set_coefficient(index, 1.0)
constraint.set_relation(ilpy.Relation.LessEqual)
Expand Down Expand Up @@ -301,15 +301,15 @@ The complete variable declaration looks like this:
out_edge_index = edge_indicators[out_edge]

# edge pair indicator = 1 <=> in edge = 1 and out edge = 1
constraint = ilpy.LinearConstraint()
constraint = ilpy.Constraint()
constraint.set_coefficient(pair_index, 2)
constraint.set_coefficient(in_edge_index, -1)
constraint.set_coefficient(out_edge_index, -1)
constraint.set_relation(ilpy.Relation.LessEqual)
constraint.set_value(0)
constraints.append(constraint)

constraint = ilpy.LinearConstraint()
constraint = ilpy.Constraint()
constraint.set_coefficient(pair_index, -1)
constraint.set_coefficient(in_edge_index, 1)
constraint.set_coefficient(out_edge_index, 1)
Expand Down
4 changes: 2 additions & 2 deletions motile/constraints/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class Constraint(ABC):
@abstractmethod
def instantiate(self, solver: Solver) -> Iterable[ilpy.LinearConstraint]:
def instantiate(self, solver: Solver) -> Iterable[ilpy.Constraint]:
"""Create and return specific linear constraints for the given solver.
Args:
Expand All @@ -21,5 +21,5 @@ def instantiate(self, solver: Solver) -> Iterable[ilpy.LinearConstraint]:
Returns:
An iterable of :class:`ilpy.LinearConstraint`.
An iterable of :class:`ilpy.Constraint`.
"""
4 changes: 2 additions & 2 deletions motile/constraints/max_children.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ class MaxChildren(Constraint):
def __init__(self, max_children: int) -> None:
self.max_children = max_children

def instantiate(self, solver: Solver) -> Iterable[ilpy.LinearConstraint]:
def instantiate(self, solver: Solver) -> Iterable[ilpy.Constraint]:
edge_indicators = solver.get_variables(EdgeSelected)

for node in solver.graph.nodes:
constraint = ilpy.LinearConstraint()
constraint = ilpy.Constraint()

# all outgoing edges
for edge in solver.graph.next_edges[node]:
Expand Down
4 changes: 2 additions & 2 deletions motile/constraints/max_parents.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ class MaxParents(Constraint):
def __init__(self, max_parents: int) -> None:
self.max_parents = max_parents

def instantiate(self, solver: Solver) -> Iterable[ilpy.LinearConstraint]:
def instantiate(self, solver: Solver) -> Iterable[ilpy.Constraint]:
edge_indicators = solver.get_variables(EdgeSelected)

for node in solver.graph.nodes:
constraint = ilpy.LinearConstraint()
constraint = ilpy.Constraint()

# all incoming edges
for edge in solver.graph.prev_edges[node]:
Expand Down
6 changes: 3 additions & 3 deletions motile/constraints/pin.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Pin(Constraint):
def __init__(self, attribute: str) -> None:
self.attribute = attribute

def instantiate(self, solver: Solver) -> list[ilpy.LinearConstraint]:
def instantiate(self, solver: Solver) -> list[ilpy.Constraint]:
node_indicators = solver.get_variables(NodeSelected)
edge_indicators = solver.get_variables(EdgeSelected)

Expand All @@ -57,8 +57,8 @@ def instantiate(self, solver: Solver) -> list[ilpy.LinearConstraint]:
if self.attribute in attributes and not attributes[self.attribute]
]

must_select_constraint = ilpy.LinearConstraint()
must_not_select_constraint = ilpy.LinearConstraint()
must_select_constraint = ilpy.Constraint()
must_not_select_constraint = ilpy.Constraint()

for index in must_select:
must_select_constraint.set_coefficient(index, 1)
Expand Down
4 changes: 2 additions & 2 deletions motile/constraints/select_edge_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class SelectEdgeNodes(Constraint):
This constraint will be added by default to any :class:`Solver` instance.
"""

def instantiate(self, solver: Solver) -> Iterable[ilpy.LinearConstraint]:
def instantiate(self, solver: Solver) -> Iterable[ilpy.Constraint]:
node_indicators = solver.get_variables(NodeSelected)
edge_indicators = solver.get_variables(EdgeSelected)

Expand All @@ -34,7 +34,7 @@ def instantiate(self, solver: Solver) -> Iterable[ilpy.LinearConstraint]:
ind_e = edge_indicators[edge]
nodes_ind = [node_indicators[node] for node in nodes]

constraint = ilpy.LinearConstraint()
constraint = ilpy.Constraint()
constraint.set_coefficient(ind_e, len(nodes_ind))
for node_ind in nodes_ind:
constraint.set_coefficient(node_ind, -1)
Expand Down
20 changes: 7 additions & 13 deletions motile/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from .ssvm import fit_weights

logger = logging.getLogger(__name__)
ILPY_V03 = ilpy.__version__.split(".")[:2] >= ["0", "3"]

if TYPE_CHECKING:
from motile.costs import Costs
Expand Down Expand Up @@ -48,9 +47,9 @@ def __init__(
self._weights_changed = True
self.features = Features()

self.ilp_solver: ilpy.LinearSolver | None = None
self.objective: ilpy.LinearObjective | None = None
self.constraints = ilpy.LinearConstraints()
self.ilp_solver: ilpy.Solver | None = None
self.objective: ilpy.Objective | None = None
self.constraints = ilpy.Constraints()

self.num_variables: int = 0
self._costs = np.zeros((0,), dtype=np.float32)
Expand Down Expand Up @@ -131,13 +130,13 @@ def solve(self, timeout: float = 0.0, num_threads: int = 1) -> ilpy.Solution:
vector.
"""

self.objective = ilpy.LinearObjective(self.num_variables)
self.objective = ilpy.Objective(self.num_variables)
for i, c in enumerate(self.costs):
logger.debug("Setting cost of var %d to %.3f", i, c)
self.objective.set_coefficient(i, c)

# TODO: support other variable types
self.ilp_solver = ilpy.LinearSolver(
self.ilp_solver = ilpy.Solver(
self.num_variables,
ilpy.VariableType.Binary,
variable_types=self.variable_types,
Expand All @@ -153,13 +152,8 @@ def solve(self, timeout: float = 0.0, num_threads: int = 1) -> ilpy.Solution:

self.ilp_solver.set_verbose(False)

solution = self.ilp_solver.solve()

if ILPY_V03:
self.solution, message = solution, solution.get_status()
else:
self.solution, message = solution
if message:
self.solution = self.ilp_solver.solve()
if message := self.solution.get_status():
logger.info("ILP solver returned with: %s", message)

return self.solution
Expand Down
8 changes: 4 additions & 4 deletions motile/variables/node_appear.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def instantiate(solver: Solver) -> Collection[NodeId]:
return solver.graph.nodes

@staticmethod
def instantiate_constraints(solver: Solver) -> Iterable[ilpy.LinearConstraint]:
def instantiate_constraints(solver: Solver) -> Iterable[ilpy.Constraint]:
appear_indicators = solver.get_variables(NodeAppear)
node_indicators = solver.get_variables(NodeSelected)
edge_indicators = solver.get_variables(EdgeSelected)
Expand All @@ -51,7 +51,7 @@ def instantiate_constraints(solver: Solver) -> Iterable[ilpy.LinearConstraint]:
if num_prev_edges == 0:
# special case: no incoming edges, appear indicator is equal to
# selection indicator
constraint = ilpy.LinearConstraint()
constraint = ilpy.Constraint()
constraint.set_coefficient(node_indicators[node], 1.0)
constraint.set_coefficient(appear_indicators[node], -1.0)
constraint.set_relation(ilpy.Relation.Equal)
Expand All @@ -72,8 +72,8 @@ def instantiate_constraints(solver: Solver) -> Iterable[ilpy.LinearConstraint]:
# (1) s - appear <= num_prev - 1
# (2) s - appear * num_prev >= 0

constraint1 = ilpy.LinearConstraint()
constraint2 = ilpy.LinearConstraint()
constraint1 = ilpy.Constraint()
constraint2 = ilpy.Constraint()

# set s for both constraints:

Expand Down
6 changes: 3 additions & 3 deletions motile/variables/node_split.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def instantiate(solver: Solver) -> Collection[NodeId]:
return solver.graph.nodes

@staticmethod
def instantiate_constraints(solver: Solver) -> Iterable[ilpy.LinearConstraint]:
def instantiate_constraints(solver: Solver) -> Iterable[ilpy.Constraint]:
split_indicators = solver.get_variables(NodeSplit)
edge_indicators = solver.get_variables(EdgeSelected)

Expand All @@ -53,8 +53,8 @@ def instantiate_constraints(solver: Solver) -> Iterable[ilpy.LinearConstraint]:
# (1) 2 * split - sum(next_selected) <= 0
# (2) (num_next - 1) * split - sum(next_selected) >= -1

constraint1 = ilpy.LinearConstraint()
constraint2 = ilpy.LinearConstraint()
constraint1 = ilpy.Constraint()
constraint2 = ilpy.Constraint()

constraint1.set_coefficient(split_indicators[node], 2.0)
constraint2.set_coefficient(split_indicators[node], len(next_edges) - 1.0)
Expand Down
4 changes: 2 additions & 2 deletions motile/variables/variable.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def instantiate(solver):
pass

@staticmethod
def instantiate_constraints(solver: Solver) -> Iterable[ilpy.LinearConstraint]:
def instantiate_constraints(solver: Solver) -> Iterable[ilpy.Constraint]:
"""Add linear constraints to the solver to ensure that these variables
are coupled to other variables of the solver.
Expand All @@ -100,7 +100,7 @@ def instantiate_constraints(solver: Solver) -> Iterable[ilpy.LinearConstraint]:
Returns:
A iterable of :class:`ilpy.LinearConstraint`. See
A iterable of :class:`ilpy.Constraint`. See
:class:`motile.constraints.Constraint` for how to create linear
constraints.
"""
Expand Down
15 changes: 12 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ authors = [
{ name = 'Florian Jug', email = 'florian.jug@fht.org' },
]
dynamic = ["version"]
dependencies = ['networkx', 'ilpy', 'numpy', 'structsvm']
dependencies = ['networkx', 'ilpy>=0.3.0', 'numpy', 'structsvm']

[project.optional-dependencies]
dev = ["pre-commit", "pytest", "pytest-cov", "ruff", "twine", "build"]
Expand Down Expand Up @@ -50,6 +50,15 @@ select = [
"RUF", # ruff specific rules
]

# https://docs.pytest.org/en/6.2.x/customize.html
[tool.pytest.ini_options]
minversion = "6.0"
testpaths = ["tests"]
filterwarnings = [
"error:::motile",
"ignore:ilpy.*:DeprecationWarning:structsvm",
]

# https://coverage.readthedocs.io/en/6.4/config.html
[tool.coverage.report]
exclude_lines = [
Expand All @@ -61,8 +70,8 @@ exclude_lines = [

[tool.mypy]
files = "motile"
strict = true # feel free to relax this if it's annoying
allow_untyped_defs = true # TODO: can eventually fill out typing and remove this
strict = true # feel free to relax this if it's annoying
allow_untyped_defs = true # TODO: can eventually fill out typing and remove this
# allow_untyped_calls = true # TODO: can eventually fill out typing and remove this
disallow_any_generics = false
ignore_missing_imports = true

0 comments on commit 5f42466

Please sign in to comment.