Skip to content

Commit

Permalink
Merge pull request #46 from tlambert03/docstyle
Browse files Browse the repository at this point in the history
Many updates to documentation, add docstyle linting
  • Loading branch information
tlambert03 authored Nov 2, 2023
2 parents 8830ca6 + 108c79e commit adf70fe
Show file tree
Hide file tree
Showing 35 changed files with 420 additions and 229 deletions.
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ ci:
autoupdate_commit_msg: "ci(pre-commit.ci): autoupdate"

repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.254
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.3
hooks:
- id: ruff
args: [--fix]
args: [--fix, --unsafe-fixes]

- repo: https://github.com/psf/black
rev: 23.1.0
rev: 23.10.1
hooks:
- id: black

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.1.1
rev: v1.6.1
hooks:
- id: mypy
files: "^motile/"
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ docs:
.PHONY: docs-watch
docs-watch:
pip install sphinx-autobuild
sphinx-autobuild docs/source docs/_build/html
sphinx-autobuild docs/source docs/_build/html --watch motile --watch docs/source --open-browser
61 changes: 60 additions & 1 deletion docs/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,36 @@ API Reference
.. automodule:: motile
:noindex:

.. admonition:: A note on ``NodeId`` and ``EdgeId`` types
:class: note, dropdown

The following types are used throughout the docs

- All objects in a graph (both ``Nodes`` and ``Edges``) are represented as
dictionaries mapping string attribute names to value. For example, a node
might be ``{ "id": 1, "x": 0.5, "y": 0.5, "t": 0 }``

``GraphObject: TypeAlias = Mapping[str, Any]``

- Node IDs may be integers, or a "meta-node" as a tuple of integers.

``NodeId: TypeAlias = Union[int, tuple[int, ...]]``

- Edges IDs are tuples of ``NodeId``.

``EdgeId: TypeAlias = tuple[NodeId, ...]``

- ``(0, 1)`` is an edge from node 0 to node 1.
- ``((0, 1), 2)`` is a hyperedge from nodes 0 and 1 to node 2 (i.e. a merge).
- ``((0,), (1, 2))`` is a hyperedge from node 0 to nodes 1 and 2 (i.e. a split).



Track Graph
-----------

.. autoclass:: TrackGraph
:members: get_frames, nodes_by_frame
:members:

Solver
------
Expand Down Expand Up @@ -78,6 +103,28 @@ EdgeDistance
^^^^^^^^^^^^
.. autoclass:: EdgeDistance

Features
--------

.. autoclass:: Features
:members:


Weights
-------

Weight
^^^^^^

.. autoclass:: Weight
:members:

Weights
^^^^^^^

.. autoclass:: Weights
:members:

Constraints
-----------

Expand All @@ -93,15 +140,27 @@ The following lists all constraints that are already implemented in ``motile``.
MaxChildren
^^^^^^^^^^^
.. autoclass:: MaxChildren
:show-inheritance:

MaxParents
^^^^^^^^^^
.. autoclass:: MaxParents
:show-inheritance:

ExpressionConstraint
^^^^^^^^^^^^^^^^^^^^
.. autoclass:: ExpressionConstraint
:show-inheritance:

Pin
^^^
.. autoclass:: Pin
:show-inheritance:

SelectEdgeNodes (internal use)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. autoclass:: SelectEdgeNodes
:show-inheritance:



12 changes: 12 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@
extensions = [
"jupyter_sphinx",
"sphinx.ext.autodoc",
"sphinx.ext.napoleon",
"sphinx_autodoc_typehints",
"sphinx.ext.githubpages",
"sphinx.ext.mathjax",
"sphinx_rtd_theme",
"sphinx_togglebutton",
"sphinxcontrib.jquery",
"sphinx.ext.intersphinx",
]

templates_path = ["_templates"]
Expand All @@ -52,3 +55,12 @@
togglebutton_hint_hide = ""

pygments_style = "lovelace"

# Napoleon settings
napoleon_google_docstring = True

intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
"networkx": ("https://networkx.org/documentation/stable/", None),
"numpy": ("https://numpy.org/doc/stable/", None),
}
2 changes: 1 addition & 1 deletion docs/source/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Do I have to use ``conda``?
---------------------------

Kinda. ``motile`` uses `ilpy <https://github.com/funkelab/ilpy>`_ to solve the
optimzation problem. Conda packages for ``ilpy`` are available for all major
optimization problem. Conda packages for ``ilpy`` are available for all major
platforms, linking against the conda packages for SCIP and Gurobi.

It is possible to not use ``conda``: If you have SCIP or Gurobi installed
Expand Down
2 changes: 1 addition & 1 deletion docs/source/learning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ to the solver.

In the example above, the :class:`motile.variables.EdgeSelected` variable
(which is the target of the cost :class:`motile.costs.EdgeSelection`), has the
follwing weights and features:
following weights and features:

.. math::
\vct{w}
Expand Down
2 changes: 1 addition & 1 deletion docs/source/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ Variables are instantiated and managed by the solver. All we have to do is to
ask the solver for the kind of variables we are interested in via
:func:`Solver.get_variables`. The returned value (e.g., ``node_selected``) is a
dictionary that maps *what* to *where*: in the case of node variables, the
dictionary keys are the nodes themselves (an integer) and the dictionay values
dictionary keys are the nodes themselves (an integer) and the dictionary values
are the indices in the ``solution`` vector where we can find the value of the
variable. Both node and edge indicators are binary variables (one if selected,
zero otherwise).
Expand Down
8 changes: 4 additions & 4 deletions motile/constraints/constraint.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@


class Constraint(ABC):
"""A base class for a constraint that can be added to a solver."""

@abstractmethod
def instantiate(
self, solver: Solver
) -> Iterable[ilpy.Constraint | ilpy.Expression]:
"""Create and return specific linear constraints for the given solver.
Args:
solver (:class:`Solver`):
The solver instance to create linear constraints for.
solver:
The :class:`~motile.Solver` instance to create linear constraints for.
Returns:
An iterable of :class:`ilpy.Constraint`.
"""
38 changes: 20 additions & 18 deletions motile/constraints/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@


class ExpressionConstraint(Constraint):
"""Enforces the selection of nodes/edges based on an expression evaluated
with the node/edge dict as a namespace.
"""Enforce selection of nodes/edges based on an expression.
The expression string is evaluated with the node/edge dict as a namespace.
This is a powerful general constraint that allows you to select nodes/edges based on
any combination of node/edge attributes. The `expression` string is evaluated for
Expand All @@ -29,33 +30,34 @@ class ExpressionConstraint(Constraint):
This takes advantaged of python's `eval` function, like this:
```python
my_expression = "some_attribute == True"
eval(my_expression, None, {"some_attribute": True}) # returns True (select)
eval(my_expression, None, {"some_attribute": False}) # returns False (exclude)
eval(my_expression, None, {}) # raises NameError (do nothing)
```
.. code-block:: python
my_expression = "some_attribute == True"
eval(my_expression, None, {"some_attribute": True}) # returns True (select)
eval(my_expression, None, {"some_attribute": False}) # returns False (exclude)
eval(my_expression, None, {}) # raises NameError (do nothing)
Args:
expression (string):
expression:
An expression to evaluate for each node/edge. The expression must
evaluate to a boolean value. The expression can use any names of
node/edge attributes as variables.
eval_nodes (bool):
eval_nodes:
Whether to evaluate the expression for nodes. By default, True.
eval_edges (bool):
eval_edges:
Whether to evaluate the expression for edges. By default, True.
Example:
If the nodes of a graph are:
>>> cells = [
... {"id": 0, "t": 0, "color": "red", "score": 1.0},
... {"id": 1, "t": 0, "color": "green", "score": 1.0},
... {"id": 2, "t": 1, "color": "blue", "score": 1.0},
... ]
If the nodes of a graph are:
cells = [
{"id": 0, "t": 0, "color": "red", "score": 1.0},
{"id": 1, "t": 0, "color": "green", "score": 1.0},
{"id": 2, "t": 1, "color": "blue", "score": 1.0},
]
Then the following constraint will select node 0:
Then the following constraint will select node 0:
>>> expr = "t == 0 and color != 'green'"
>>> solver.add_constraints(ExpressionConstraint(expr))
"""
Expand Down
10 changes: 5 additions & 5 deletions motile/constraints/max_children.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@


class MaxChildren(Constraint):
r"""Ensures that every selected node has no more than ``max_children``
selected edges to the next frame.
r"""Ensures that every selected node has no more than ``max_children``.
Where a "child" is a selected edges to the next frame.
Adds the following linear constraint for each node :math:`v`:
.. math::
\sum_{e \in \\text{out_edges}(v)} x_e \leq \\text{max_children}
\sum_{e \in \text{out_edges}(v)} x_e \leq \text{max_children}
Args:
max_children (int):
max_children:
The maximum number of children allowed.
"""

Expand Down
10 changes: 5 additions & 5 deletions motile/constraints/max_parents.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,18 @@


class MaxParents(Constraint):
r"""Ensures that every selected node has no more than ``max_parents``
selected edges to the previous frame.
r"""Ensures that every selected node has no more than ``max_parents``.
Where a "parent" is defined as an incoming selected edge from the previous frame.
Adds the following linear constraint for each node :math:`v`:
.. math::
\sum_{e \in \\text{in_edges}(v)} x_e \leq \\text{max_parents}
\sum_{e \in \text{in_edges}(v)} x_e \leq \text{max_parents}
Args:
max_parents (int):
max_parents:
The maximum number of parents allowed.
"""

Expand Down
6 changes: 2 additions & 4 deletions motile/constraints/pin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@


class Pin(ExpressionConstraint):
"""Enforces the selection of certain nodes and edges based on the value of
a given attribute.
"""Enforces the selection of nodes/edges based on truthiness of a given attribute.
Every node or edge that has the given attribute will be selected if the
attribute value is ``True`` (and not selected if the attribute value is
Expand All @@ -17,8 +16,7 @@ class Pin(ExpressionConstraint):
edges.
Args:
attribute (string):
attribute:
The name of the node/edge attribute to use.
"""

Expand Down
11 changes: 8 additions & 3 deletions motile/constraints/select_edge_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,21 @@


class SelectEdgeNodes(Constraint):
r"""Ensures that if an edge :math:`(u, v)` is selected, :math:`u` and
:math:`v` have to be selected as well.
r"""Ensures that if an edge is selected, its nodes are selected as well.
.. NOTE::
This class is for internal use.
If :math:`(u, v)` is selected, :math:`u` and :math:`v` have to be selected as well.
Adds the following linear constraint for each edge :math:`e = (u,v)`:
.. math::
2 x_e - x_u - x_v \leq 0
This constraint will be added by default to any :class:`Solver` instance.
This constraint will be added by default to any :class:`~motile.Solver` instance.
"""

def instantiate(self, solver: Solver) -> Iterable[Expression]:
Expand Down
5 changes: 2 additions & 3 deletions motile/costs/appear.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@


class Appear(Costs):
"""Costs for :class:`motile.variables.NodeAppear` variables.
"""Costs for :class:`~motile.variables.NodeAppear` variables.
Args:
constant (float):
constant:
A constant cost for each node that starts a track.
"""

Expand Down
12 changes: 7 additions & 5 deletions motile/costs/costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@


class Costs(ABC):
"""A base class for costs that can be added to a solver."""

@abstractmethod
def apply(self, solver: Solver) -> None:
"""Apply costs to the given solver. Use
:func:`motile.Solver.get_variables` and
"""Apply costs to the given solver.
Use :func:`motile.Solver.get_variables` and
:func:`motile.Solver.add_variable_cost`.
Args:
solver (:class:`Solver`):
The solver to create costs for.
solver:
The :class:`~motile.Solver` to create costs for.
"""
pass
Loading

0 comments on commit adf70fe

Please sign in to comment.