napari_track_edit.motile.backend.solve ====================================== .. py:module:: napari_track_edit.motile.backend.solve Attributes ---------- .. autoapisummary:: napari_track_edit.motile.backend.solve.logger napari_track_edit.motile.backend.solve.PIN_ATTR napari_track_edit.motile.backend.solve.PIN_UNSET napari_track_edit.motile.backend.solve.PIN_UNSELECTED napari_track_edit.motile.backend.solve.PIN_SELECTED napari_track_edit.motile.backend.solve._SKIP_ATTRS napari_track_edit.motile.backend.solve._SKIP_ATTRS Classes ------- .. autoapisummary:: napari_track_edit.motile.backend.solve.TernaryPin Functions --------- .. autoapisummary:: napari_track_edit.motile.backend.solve.graphview_to_motile_dicts napari_track_edit.motile.backend.solve.solve napari_track_edit.motile.backend.solve.build_candidate_graph napari_track_edit.motile.backend.solve._solve_full napari_track_edit.motile.backend.solve._solve_window napari_track_edit.motile.backend.solve._solve_single_window napari_track_edit.motile.backend.solve._solve_chunked napari_track_edit.motile.backend.solve._set_pinning_on_graph napari_track_edit.motile.backend.solve.construct_solver napari_track_edit.motile.backend.solve.get_solver_name Module Contents --------------- .. py:data:: logger .. py:data:: PIN_ATTR :value: 'pinned' .. py:data:: PIN_UNSET :value: -1 .. py:data:: PIN_UNSELECTED :value: 0 .. py:data:: PIN_SELECTED :value: 1 .. py:data:: _SKIP_ATTRS .. py:function:: graphview_to_motile_dicts(cand_graph: tracksdata.graph.GraphView) -> tuple[dict[int, dict], dict[tuple[int, int], dict]] Unpack a tracksdata ``GraphView`` into plain node/edge dicts for ``motile.TrackGraph``. ``GraphView`` is always backed by an in-memory ``rustworkx.PyDiGraph`` (regardless of the root graph's backend), so this walks that graph directly instead of going through tracksdata's polars-based ``node_attrs()``/``edge_attrs()``, which is dramatically slower for large candidate graphs. :param cand_graph: The candidate graph to unpack. Node and edge attribute dicts are reused by reference (not copied), matching how ``GraphView`` itself shares attribute storage with an in-memory root. :returns: A tuple ``(nodes, edges)`` matching the shape expected by ``motile.TrackGraph.add_node``/``add_edge``: - ``nodes``: mapping from node id to its attribute dict. - ``edges``: mapping from ``(source_id, target_id)`` to its attribute dict. .. py:function:: solve(solver_params: napari_track_edit.motile.backend.solver_params.SolverParams, input_data: numpy.ndarray, on_solver_update: collections.abc.Callable | None = None, scale: list | None = None, cand_graph: tracksdata.graph.BaseGraph | None = None) -> tracksdata.graph.BaseGraph Get a tracking solution for the given segmentation and parameters. Constructs a candidate graph from the segmentation (unless one is provided), a solver from the parameters, and then runs solving and returns a networkx graph with the solution. Most of this functionality is implemented in the motile toolbox. :param solver_params: The solver parameters to use when initializing the solver :type solver_params: SolverParams :param input_data: The input segmentation or points list to run tracking on. If 2D, assumed to be a list of points, otherwise a segmentation. :type input_data: np.ndarray :param on_solver_update: A function that is called whenever the motile solver emits an event. The function should take a dictionary of event data, and can be used to track progress of the solver. Defaults to None. :type on_solver_update: Callable, optional :param scale: The scale of the data in each dimension. :type scale: list, optional :param cand_graph: A pre-built candidate graph. If provided, skips candidate graph construction (except for single-window mode which always builds its own). Defaults to None. :type cand_graph: td.graph.BaseGraph, optional :returns: A solution graph where the ids of the nodes correspond to the time and ids of the passed in segmentation labels. See funtracks for exact implementation details. :rtype: td.graph.BaseGraph .. py:function:: build_candidate_graph(input_data: numpy.ndarray, solver_params: napari_track_edit.motile.backend.solver_params.SolverParams, scale: list | None = None, time_offset: int = 0) -> tracksdata.graph.BaseGraph Build the candidate graph from input data. .. py:function:: _solve_full(cand_graph: tracksdata.graph.BaseGraph, solver_params: napari_track_edit.motile.backend.solver_params.SolverParams, on_solver_update: collections.abc.Callable | None = None) -> tracksdata.graph.BaseGraph Solve the tracking problem on the full candidate graph at once. .. py:function:: _solve_window(window_subgraph: tracksdata.graph.GraphView, solver_params: napari_track_edit.motile.backend.solver_params.SolverParams, on_solver_update: collections.abc.Callable | None = None) -> tracksdata.graph.BaseGraph | None Solve a single window subgraph. This is the core solving logic shared by both single window mode and chunked solving. :param window_subgraph: The subgraph for this window. If any nodes or edges have the PIN_ATTR attribute set, a Pin constraint will be used. :param solver_params: The solver parameters. :param on_solver_update: Callback for solver progress updates. :returns: The solution graph for this window, or None if the window has no nodes. .. py:function:: _solve_single_window(input_data: numpy.ndarray, solver_params: napari_track_edit.motile.backend.solver_params.SolverParams, on_solver_update: collections.abc.Callable | None = None, scale: list | None = None) -> tracksdata.graph.BaseGraph Solve a single window for interactive parameter testing. Builds the full candidate graph, filters it to the window frames, and solves. Node times are naturally correct (no adjustment needed). :param input_data: The full input segmentation or points list. :param solver_params: The solver parameters including window_size and single_window_start. :param on_solver_update: Callback for solver progress updates. :param scale: The scale of the data in each dimension. :returns: The solution graph for the requested window. :raises ValueError: If single_window_start is beyond the data range. .. py:function:: _solve_chunked(cand_graph: tracksdata.graph.BaseGraph, solver_params: napari_track_edit.motile.backend.solver_params.SolverParams, on_solver_update: collections.abc.Callable | None = None) -> tracksdata.graph.BaseGraph Solve the tracking problem in chunks using a sliding window approach. This function solves the tracking problem in windows of `window_size` frames, with `overlap_size` frames of overlap between consecutive windows. The overlap region from the previous window is pinned (fixed) when solving the next window to maintain consistency across windows. :param cand_graph: The full candidate graph with all nodes and edges. :param solver_params: The solver parameters including window_size and overlap_size. :param on_solver_update: Callback for solver progress updates. :returns: The combined solution graph from all windows. .. py:function:: _set_pinning_on_graph(cand_graph: tracksdata.graph.BaseGraph, solution_graph: tracksdata.graph.BaseGraph, overlap_start: int, overlap_end: int) -> None Set PIN_ATTR on candidate graph nodes/edges in the overlap region. For all nodes and edges in the overlap region [overlap_start, overlap_end), sets PIN_ATTR to PIN_SELECTED if selected in the solution, PIN_UNSELECTED if not selected. Everything outside the overlap region stays PIN_UNSET. :param cand_graph: The full candidate graph to modify in place. :param solution_graph: The solution graph from the current window. :param overlap_start: Start frame of overlap region (inclusive). :param overlap_end: End frame of overlap region (exclusive). .. py:data:: _SKIP_ATTRS .. py:class:: TernaryPin(attribute: str) Bases: :py:obj:`motile.constraints.constraint.Constraint` Like motile's Pin, but treats PIN_UNSET as unconstrained. motile.constraints.Pin evaluates `{attribute} == True` for every node/edge and only skips ones where the attribute is entirely absent (NameError). Since tracksdata can't store nulls, our PIN_ATTR is always present once the schema key exists, so Pin would force-unselect every node/edge that hasn't actually been decided yet. This constraint instead only pins nodes/edges whose attribute value is PIN_SELECTED or PIN_UNSELECTED, leaving PIN_UNSET ones free for the solver to decide. .. py:attribute:: attribute .. py:method:: instantiate(solver: motile.Solver) -> list[ilpy.Constraint] Create and return specific linear constraints for the given solver. :param solver: The :class:`~motile.Solver` instance to create linear constraints for. :returns: An iterable of :class:`ilpy.Constraint`. .. py:function:: construct_solver(cand_graph: tracksdata.graph.GraphView, solver_params: napari_track_edit.motile.backend.solver_params.SolverParams) -> motile.Solver Construct a motile solver with the parameters specified in the solver params object. :param cand_graph: The candidate graph to use in the solver :type cand_graph: td.graph.GraphView :param solver_params: The costs and constraints to use in the solver :type solver_params: SolverParams :returns: A motile solver with the specified graph, costs, and constraints. :rtype: Solver .. py:function:: get_solver_name() -> str Return the name of the ILP solver backend that will be used. Attempts Gurobi first; falls back to SCIP.