Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
a2736c0
Update core.py
giovannivolpe Nov 6, 2025
e756381
Update features.py
giovannivolpe Nov 6, 2025
93cec54
Update test_features.py
giovannivolpe Nov 6, 2025
ff3d08c
Update features.py
giovannivolpe Nov 6, 2025
92027ca
Update test_features.py
giovannivolpe Nov 6, 2025
f611657
Update features.py
giovannivolpe Nov 8, 2025
3773dee
Update test_features.py
giovannivolpe Nov 8, 2025
52fb60b
Update features.py
giovannivolpe Nov 8, 2025
5796b1a
Update test_features.py
giovannivolpe Nov 8, 2025
2fd6f0e
Update features.py
giovannivolpe Nov 9, 2025
184a8be
Update test_features.py
giovannivolpe Nov 10, 2025
6982c5c
Update test_dlcc.py
giovannivolpe Nov 10, 2025
f357e3d
Update test_dlcc.py
giovannivolpe Nov 10, 2025
5f2b04d
Update test_features.py
giovannivolpe Nov 10, 2025
09f740e
module docstring
giovannivolpe Dec 21, 2025
2ddfd1d
module docstring + headings
giovannivolpe Dec 21, 2025
f1822b8
StructuralFeature + Chain + test_Chain
giovannivolpe Dec 29, 2025
6ab2b21
DummyFeature + test
giovannivolpe Dec 31, 2025
6b4009d
Value + test
giovannivolpe Dec 31, 2025
429bde8
Update features.py
giovannivolpe Dec 31, 2025
b1bbd1a
Stack + test
giovannivolpe Dec 31, 2025
62e674d
Arguments + test
giovannivolpe Dec 31, 2025
f09bf8c
Update test_dlcc.py
giovannivolpe Dec 31, 2025
ffb8227
Probability + test
giovannivolpe Dec 31, 2025
6bedb74
Update test_dlcc.py
giovannivolpe Dec 31, 2025
c92c5d8
Update test_dlcc.py
giovannivolpe Dec 31, 2025
a5ee4bd
Repeat + test
giovannivolpe Dec 31, 2025
abe2151
Update features.py
giovannivolpe Dec 31, 2025
06894f8
Bind++
giovannivolpe Dec 31, 2025
89f51b3
ConditionalSetFeature + ConditionalSetProperty ++
giovannivolpe Dec 31, 2025
f36f069
Lambda++
giovannivolpe Dec 31, 2025
47e7822
Merge++
giovannivolpe Dec 31, 2025
87beac6
OneOf + OneOfDict++
giovannivolpe Dec 31, 2025
ed76e3f
Update test_dlcc.py
giovannivolpe Dec 31, 2025
979f41e
Update test_dlcc.py
giovannivolpe Dec 31, 2025
167e8ae
Update test_dlcc.py
giovannivolpe Dec 31, 2025
d6504b0
LoadImage
giovannivolpe Jan 1, 2026
fff7dfd
AsType
giovannivolpe Jan 1, 2026
c5570d9
Upscale + NonOverlapping
giovannivolpe Jan 2, 2026
8b691b2
Store .. TakeProperties
giovannivolpe Jan 2, 2026
477c73b
remove NDArray
giovannivolpe Jan 2, 2026
9894b74
Update features.py
giovannivolpe Jan 2, 2026
f1eca0f
Update features.py
giovannivolpe Jan 4, 2026
98f252d
Update features.py
giovannivolpe Jan 4, 2026
580815d
u
giovannivolpe Jan 4, 2026
b76361f
Update features.py
giovannivolpe Jan 7, 2026
d8f0f01
Update properties.py
giovannivolpe Jan 7, 2026
36f4c5c
Update test_features.py
giovannivolpe Jan 7, 2026
2e71aec
Delete test_image.py
giovannivolpe Jan 7, 2026
541e7a6
Update features.py
giovannivolpe Jan 7, 2026
52669bc
core.py final checks
giovannivolpe Jan 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 64 additions & 42 deletions deeptrack/backend/core.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
"""Core data structures for DeepTrack2.

This module defines the foundational data structures used throughout DeepTrack2
for constructing, managing, and evaluating computational graphs with flexible
data storage and dependency management.
This module defines the data structures used throughout DeepTrack2 to
construct, manage, and evaluate computational graphs with flexible data storage
and dependency management.

Key Features
------------
- **Hierarchical Data Management**

Provides validated, hierarchical data containers (`DeepTrackDataObject` and
`DeepTrackDataDict`) for storing data and managing complex, nested data
`DeepTrackDataDict`) to store data and manage complex, nested data
structures. Supports dependency tracking and flexible indexing.

- **Computation Graphs with Lazy Evaluation**
Expand Down Expand Up @@ -41,8 +41,8 @@
- `DeepTrackNode`: Node in a computation graph with operator overloading.

Represents a node in a computation graph, capable of storing and computing
values based on dependencies, with full support for lazy evaluation,
dependency tracking, and operator overloading.
values based on dependencies, with support for lazy evaluation, dependency
tracking, and operator overloading.

Functions:

Expand Down Expand Up @@ -116,6 +116,7 @@
from weakref import WeakSet # To manage relationships between nodes without
# creating circular dependencies
from typing import Any, Callable, Iterator
import warnings

from deeptrack.utils import get_kwarg_names

Expand Down Expand Up @@ -146,7 +147,7 @@ class DeepTrackDataObject:
"""Basic data container for DeepTrack2.

`DeepTrackDataObject` is a simple data container to store some data and
track its validity.
to track its validity.

Attributes
----------
Expand Down Expand Up @@ -310,9 +311,9 @@ class DeepTrackDataDict:
Once the first entry is created, all `_ID`s must match the set key-length.

When retrieving the data associated to an `_ID`:
- If an `_ID` longer than the set key-length is requested, it is trimmed.
- If an `_ID` shorter than the set key-length is requested, a dictionary
slice containing all matching entries is returned.
- If an `_ID` longer than the set key-length is requested, it is trimmed.
- If an `_ID` shorter than the set key-length is requested, a dictionary
slice containing all matching entries is returned.

NOTE: The `_ID`s are specifically used in the `Repeat` feature to allow it
to return different values without changing the input.
Expand Down Expand Up @@ -340,10 +341,10 @@ class DeepTrackDataDict:
Check if the given `_ID` is valid for the current configuration.
`__getitem__(_ID) -> DeepTrackDataObject or dict[_ID, DeepTrackDataObject]`
Retrieve data associated with the `_ID`. Can return a
`DeepTrackDataObject`, or a dict of `DeepTrackDataObject`s if `_ID` is
shorter than `keylength`.
`DeepTrackDataObject`, or a dictionary of `DeepTrackDataObject`s if
`_ID` is shorter than `keylength`.
`__contains__(_ID) -> bool`
Check whether the given `_ID` exists in the dictionary.
Return whether the given `_ID` exists in the dictionary.
`__len__() -> int`
Return the number of stored entries.
`__iter__() -> Iterator`
Expand Down Expand Up @@ -500,7 +501,7 @@ def invalidate(self: DeepTrackDataDict) -> None:
Calls `invalidate()` on every `DeepTrackDataObject` in the dictionary.

NOTE: Currently, it invalidates the data objects stored at all `_ID`s.
TODO: Add optional argument `_ID: tuple[int, ...] ()` and permit
TODO: Add optional argument `_ID: tuple[int, ...] = ()` to permit
invalidation of only specific `_ID`s.

"""
Expand All @@ -514,7 +515,7 @@ def validate(self: DeepTrackDataDict) -> None:
Calls `validate()` on every `DeepTrackDataObject` in the dictionary.

NOTE: Currently, it validates the data objects stored at all `_ID`s.
TODO: Add optional argument `_ID: tuple[int, ...] ()` and permit
TODO: Add optional argument `_ID: tuple[int, ...] = ()` to permit
validation of only specific `_ID`s.

"""
Expand Down Expand Up @@ -563,7 +564,7 @@ def valid_index(
f"Got a tuple of types: {[type(i).__name__ for i in _ID]}."
)

# If keylength has not yet been set, all indexes are valid.
# If keylength has not been set yet, all indexes are valid.
if self._keylength is None:
return True

Expand All @@ -584,7 +585,8 @@ def create_index(
Each newly created index is associated with a new
`DeepTrackDataObject`.

If `_ID` is already in `dict`, no new entry is created.
If `_ID` is already in `dict`, no new entry is created and a warning is
issued.

If `keylength` is `None`, it is set to the length of `_ID`. Once
established, all subsequently created `_ID`s must have this same
Expand All @@ -608,11 +610,16 @@ def create_index(
# Check if the given _ID is valid.
# (Also: Ensure _ID is a tuple of integers.)
assert self.valid_index(_ID), (
f"{_ID} is not a valid index for current dictionary configuration."
f"{_ID} is not a valid index for {self}."
)

# If `_ID` already exists, do nothing.
# If `_ID` already exists, issue a warning and skip creation.
if _ID in self._dict:
warnings.warn(
f"Index {_ID!r} already exists in {self}. "
"No new entry was created.",
UserWarning
)
return

# Create a new DeepTrackDataObject for this _ID.
Expand Down Expand Up @@ -837,7 +844,7 @@ class DeepTrackNode:
----------
action: Callable or Any, optional
Action to compute this node's value. If not provided, uses a no-op
action (lambda: None).
action (`lambda: None`).
node_name: str or None, optional
Optional name assigned to the node. Defaults to `None`.
**kwargs: Any
Expand All @@ -846,28 +853,28 @@ class DeepTrackNode:
Attributes
----------
node_name: str or None
Optional name assigned to the node. Defaults to `None`.
Name assigned to the node. Defaults to `None`.
data: DeepTrackDataDict
Dictionary-like object for storing data, indexed by tuples of integers.
children: WeakSet[DeepTrackNode]
Read-only property exposing the internal weak set `_children`
Read-only property exposing the internal weak set `._children`
containing the nodes that depend on this node (its children).
This is a weakref.WeakSet, so references are weak and do not prevent
This is a `weakref.WeakSet`, so references are weak and do not prevent
garbage collection of nodes that are no longer used.
dependencies: WeakSet[DeepTrackNode]
Read-only property exposing the internal weak set `_dependencies`
containing the nodes on which this node depends (its parents).
This is a weakref.WeakSet, for efficient memory management.
Read-only property exposing the internal weak set `._dependencies`
containing the nodes on which this node depends (its ancestors).
This is a `weakref.WeakSet`, for efficient memory management.
_action: Callable[..., Any]
The function or lambda-function to compute the node value.
_accepts_ID: bool
Whether `action` accepts an input _ID.
Whether `action` accepts an input `_ID`.
_all_children: WeakSet[DeepTrackNode]
All nodes in the subtree rooted at the node, including the node itself.
This is a weakref.WeakSet, for efficient memory management.
This is a `weakref.WeakSet`, for efficient memory management.
_all_dependencies: WeakSet[DeepTrackNode]
All the dependencies for this node, including the node itself.
This is a weakref.WeakSet, for efficient memory management.
This is a `weakref.WeakSet`, for efficient memory management.
_citations: list[str]
Citations associated with this node.

Expand Down Expand Up @@ -899,11 +906,11 @@ class DeepTrackNode:
current value, the node is invalidated to ensure dependencies are
recomputed.
`print_children_tree(indent) -> None`
Print a tree of all child nodes (recursively) for debugging.
Print a tree of all child nodes (recursively) for inspection.
`recurse_children() -> set[DeepTrackNode]`
Return all child nodes in the dependency tree rooted at this node.
`print_dependencies_tree(indent) -> None`
Print a tree of all parent nodes (recursively) for debugging.
Print a tree of all parent nodes (recursively) for inspection.
`recurse_dependencies() -> Iterator[DeepTrackNode]`
Yield all nodes that this node depends on, traversing dependencies.
`get_citations() -> set[str]`
Expand Down Expand Up @@ -945,7 +952,7 @@ class DeepTrackNode:

Examples
--------
>>> from deeptrack.backend.core import DeepTrackNode
>>> from deeptrack import DeepTrackNode

Create three `DeepTrackNode` objects, as parent, child, and grandchild:

Expand Down Expand Up @@ -1123,13 +1130,14 @@ class DeepTrackNode:

Citations for a node and its dependencies:

>>> parent.get_citations() # Set of citation strings
>>> parent.get_citations() # Get of citation strings
{...}

"""

node_name: str | None
data: DeepTrackDataDict

_children: WeakSet[DeepTrackNode]
_dependencies: WeakSet[DeepTrackNode]
_all_children: WeakSet[DeepTrackNode]
Expand Down Expand Up @@ -1189,9 +1197,9 @@ def __init__(
----------
action: Callable or Any, optional
Action to compute this node's value. If not provided, uses a no-op
action (lambda: None).
action (`lambda: None`).
node_name: str or None, optional
Optional name for the node. Defaults to `None`.
Name for the node. Defaults to `None`.
**kwargs: Any
Additional arguments for subclasses or extended functionality.

Expand All @@ -1218,11 +1226,11 @@ def __init__(
self._accepts_ID = "_ID" in get_kwarg_names(self.action)

# Keep track of all children, including this node.
self._all_children = WeakSet() #TODO ***BM*** Ok WeakSet from set?
self._all_children = WeakSet()
self._all_children.add(self)

# Keep track of all dependencies, including this node.
self._all_dependencies = WeakSet() #TODO ***BM*** Ok this addition?
self._all_dependencies = WeakSet()
self._all_dependencies.add(self)

def add_child(
Expand Down Expand Up @@ -1253,7 +1261,7 @@ def add_child(

"""

# Check for cycle: if `self` is already in `child`'s dependency tree
# Check for cycle: if `self` is already in `child`'s children tree
if self in child.recurse_children():
raise ValueError(
f"Adding {child.node_name} as child to {self.node_name} "
Expand Down Expand Up @@ -1305,6 +1313,12 @@ def add_dependency(
self: DeepTrackNode
Return the current node for chaining.

Raises
------
ValueError
If adding this parent would introduce a cycle in the dependency
graph.

"""

parent.add_child(self)
Expand All @@ -1324,7 +1338,7 @@ def store(
The data to be stored.
_ID: tuple[int, ...], optional
The index for this data. If `_ID` does not exist, it creates it.
Defaults to (), indicating a root-level entry.
Defaults to `()`, indicating a root-level entry.

Returns
-------
Expand All @@ -1334,7 +1348,8 @@ def store(
"""

# Create the index if necessary
self.data.create_index(_ID)
if _ID not in self.data:
self.data.create_index(_ID)

# Then store data in it
self.data[_ID].store(data)
Expand Down Expand Up @@ -1407,6 +1422,13 @@ def invalidate(

"""

if _ID:
warnings.warn(
"The `_ID` argument to `.invalidate()` is currently ignored. "
"Passing a non-empty `_ID` will invalidate the full dataset.",
UserWarning,
)

# Invalidate data for all children of this node.
for child in self.recurse_children():
child.data.invalidate()
Expand Down Expand Up @@ -1470,7 +1492,7 @@ def set_value(
value: Any
The value to store.
_ID: tuple[int, ...], optional
The `_ID` at which to store the value.
The `_ID` at which to store the value. Defsaults to `()`.

Returns
-------
Expand Down Expand Up @@ -1705,7 +1727,7 @@ def current_value(
self: DeepTrackNode,
_ID: tuple[int, ...] = (),
) -> Any:
"""Retrieve the currently stored value at _ID.
"""Retrieve the value currently stored at _ID.

Parameters
----------
Expand Down
Loading
Loading