Skip to content

Commit 9dbd195

Browse files
Gv/polishing001 (#422)
* Update core.py * Update DTAT399A_backend.core.ipynb * Update DTDV411_style.ipynb * Update core.py * Update test_core.py * Update DTDV401_overview.ipynb * Update DTDV401_overview.ipynb * Update DTAT306_properties.ipynb * Update properties.py * Update scatterers.py * Update units.py
1 parent a1ffaf9 commit 9dbd195

File tree

9 files changed

+186
-171
lines changed

9 files changed

+186
-171
lines changed

deeptrack/backend/core.py

Lines changed: 66 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
"""Core data structures for DeepTrack2.
22
3-
This module defines the foundational data structures used throughout
4-
DeepTrack2 for constructing, managing, and evaluating computational graphs
5-
with flexible data storage and dependency management.
3+
This module defines the foundational data structures used throughout DeepTrack2
4+
for constructing, managing, and evaluating computational graphs with flexible
5+
data storage and dependency management.
66
77
Key Features
88
------------
99
- **Hierarchical Data Management**
1010
11-
Provides validated, hierarchical data containers (`DeepTrackDataObject`
12-
and `DeepTrackDataDict`) for storing data and managing complex, nested
13-
data structures. Supports dependency tracking and flexible indexing.
11+
Provides validated, hierarchical data containers (`DeepTrackDataObject` and
12+
`DeepTrackDataDict`) for storing data and managing complex, nested data
13+
structures. Supports dependency tracking and flexible indexing.
1414
1515
- **Computation Graphs with Lazy Evaluation**
1616
17-
Implements the `DeepTrackNode` class, the core abstraction for nodes in
18-
a computational graph. Supports lazy evaluation, caching, dependency
17+
Implements the `DeepTrackNode` class, the core abstraction for nodes in a
18+
computational graph. Supports lazy evaluation, caching, dependency
1919
tracking, and operator overloading for intuitive composition of complex
2020
computational pipelines.
2121
@@ -40,28 +40,20 @@
4040
4141
- `DeepTrackNode`: Node in a computation graph with operator overloading.
4242
43-
Represents a node in a computation graph, capable of storing and
44-
computing values based on dependencies, with full support for lazy
45-
evaluation, dependency tracking, and operator overloading.
43+
Represents a node in a computation graph, capable of storing and computing
44+
values based on dependencies, with full support for lazy evaluation,
45+
dependency tracking, and operator overloading.
4646
4747
Functions:
4848
49-
- `_equivalent(a, b)`
49+
- `_equivalent(a, b) -> bool`
5050
51-
def _equivalent(a: Any, b: Any) -> bool
51+
Determines whether two objects should be considered equivalent, according
52+
to DeepTrack2's internal rules (identity, empty lists, etc).
5253
53-
Determines whether two objects should be considered equivalent,
54-
according to DeepTrack2's internal rules (identity, empty lists, etc).
54+
- `_create_node_with_operator(op, a, b) -> DeepTrackNode`
5555
56-
- `_create_node_with_operator(op, a, b)`
57-
58-
def _create_node_with_operator(
59-
op: Callable,
60-
a: Any,
61-
b: Any,
62-
) -> DeepTrackNode
63-
64-
Internal helper to create a new computation node by applying a
56+
Internal helper function to create a new computation node by applying a
6557
specified operator to two operands, establishing correct graph
6658
relationships and supporting operator overloading.
6759
@@ -120,9 +112,9 @@ def _create_node_with_operator(
120112
from __future__ import annotations
121113

122114
from collections.abc import ItemsView, KeysView, ValuesView
123-
import operator # Operator overloading for computation nodes.
124-
from weakref import WeakSet # Manages relationships between nodes without
125-
# creating circular dependencies.
115+
import operator # Operator overloading for computation nodes
116+
from weakref import WeakSet # To manage relationships between nodes without
117+
# creating circular dependencies
126118
from typing import Any, Callable, Iterator
127119

128120
from deeptrack.utils import get_kwarg_names
@@ -183,23 +175,29 @@ class DeepTrackDataObject:
183175
>>> import deeptrack as dt
184176
185177
Create a `DeepTrackDataObject`:
178+
186179
>>> data_obj = dt.DeepTrackDataObject()
187180
>>> data_obj
181+
DeepTrackDataObject(data=None, valid=False)
188182
189183
Store a value in this container:
184+
190185
>>> data_obj.store(42)
191186
>>> data_obj
192187
DeepTrackDataObject(data=42, valid=True)
193188
194189
Access the currently stored value:
190+
195191
>>> data_obj.current_value()
196192
42
197193
198194
Check if the stored data is valid:
195+
199196
>>> data_obj.is_valid()
200197
True
201198
202199
Invalidate the stored data:
200+
203201
>>> data_obj.invalidate()
204202
>>> data_obj
205203
DeepTrackDataObject(data=42, valid=False)
@@ -208,6 +206,7 @@ class DeepTrackDataObject:
208206
False
209207
210208
Validate the data to restore its valid status:
209+
211210
>>> data_obj.validate()
212211
>>> data_obj
213212
DeepTrackDataObject(data=42, valid=True)
@@ -284,8 +283,7 @@ def __repr__(self: DeepTrackDataObject) -> str:
284283
"""Return the string representation of the object.
285284
286285
Provides a concise representation of the data object, including the
287-
stored data and its validity flag. It is useful for debugging and
288-
logging purposes.
286+
stored data and its validity flag. Useful for debugging and logging.
289287
290288
Returns
291289
-------
@@ -307,15 +305,13 @@ class DeepTrackDataDict:
307305
`DeepTrackDataDict` can store multiple `DeepTrackDataObject` instances,
308306
each associated with a unique tuple of integers (its `_ID`).
309307
310-
**Use of _IDs**
311-
312308
The default `_ID` is an empty tuple, `_ID = ()`.
313309
314-
Once the first entry is created, all `_ID`s must match the set key length.
310+
Once the first entry is created, all `_ID`s must match the set key-length.
315311
316312
When retrieving the data associated to an `_ID`:
317-
- If an `_ID` longer than the set key length is requested, it is trimmed.
318-
- If an `_ID` shorter than the set key length is requested, a dictionary
313+
- If an `_ID` longer than the set key-length is requested, it is trimmed.
314+
- If an `_ID` shorter than the set key-length is requested, a dictionary
319315
slice containing all matching entries is returned.
320316
321317
NOTE: The `_ID`s are specifically used in the `Repeat` feature to allow it
@@ -325,8 +321,8 @@ class DeepTrackDataDict:
325321
----------
326322
keylength: int or None
327323
Read-only property exposing the internal variable with the length of
328-
the `_ID`s set when the first entry is created. If `None`, no entries
329-
have been created, and any `_ID` length is valid.
324+
the `_ID`s set when the first entry is created. If `None`, no entry has
325+
been created, and any `_ID` length is valid.
330326
dict: dict[tuple[int, ...], DeepTrackDataObject] or {}
331327
Read-only property exposing the internal dictionary of stored data,
332328
`_dict`. This is a dictionary mapping tuples of integers (`_ID`s) to
@@ -341,7 +337,7 @@ class DeepTrackDataDict:
341337
`validate() -> None`
342338
Mark all stored data objects as valid.
343339
`valid_index(_ID) -> bool`
344-
Check if the given _ID is valid for the current configuration.
340+
Check if the given `_ID` is valid for the current configuration.
345341
`__getitem__(_ID) -> DeepTrackDataObject or dict[_ID, DeepTrackDataObject]`
346342
Retrieve data associated with the `_ID`. Can return a
347343
`DeepTrackDataObject`, or a dict of `DeepTrackDataObject`s if `_ID` is
@@ -366,11 +362,13 @@ class DeepTrackDataDict:
366362
>>> import deeptrack as dt
367363
368364
Create a structure to store multiple, indexed instances of data:
365+
369366
>>> data_dict = dt.DeepTrackDataDict()
370367
>>> data_dict
371368
DeepTrackDataDict(0 entries, keylength=None)
372369
373370
Create the entries:
371+
374372
>>> data_dict.create_index((0, 0))
375373
>>> data_dict.create_index((0, 1))
376374
>>> data_dict.create_index((1, 0))
@@ -379,6 +377,7 @@ class DeepTrackDataDict:
379377
DeepTrackDataDict(4 entries, keylength=2)
380378
381379
Store the values associated with each `_ID`:
380+
382381
>>> data_dict[(0, 0)].store("Data at (0, 0)")
383382
>>> data_dict[(0, 1)].store("Data at (0, 1)")
384383
>>> data_dict[(1, 0)].store("Data at (1, 0)")
@@ -387,24 +386,29 @@ class DeepTrackDataDict:
387386
DeepTrackDataDict(4 entries, keylength=2)
388387
389388
Retrieve values based on their `_ID`s:
389+
390390
>>> data_dict[(0, 0)]
391391
DeepTrackDataObject(data='Data at (0, 0)', valid=True)
392392
393393
>>> data_dict[(0, 0)].current_value()
394394
'Data at (0, 0)'
395395
396396
>>> data_dict[(1, 1)]
397+
397398
DeepTrackDataObject(data='Data at (1, 1)', valid=True)
398399
399400
>>> data_dict[(1, 1)].current_value()
401+
400402
'Data at (1, 1)'
401403
402404
If requesting a shorter `_ID`, it returns all matching nested entries:
405+
403406
>>> data_dict[(0,)]
404407
{(0, 0): DeepTrackDataObject(data='Data at (0, 0)', valid=True),
405408
(0, 1): DeepTrackDataObject(data='Data at (0, 1)', valid=True)}
406409
407410
Validate and invalidate all entries at once:
411+
408412
>>> data_dict.invalidate()
409413
>>> data_dict[(0, 0)].is_valid()
410414
False
@@ -420,6 +424,7 @@ class DeepTrackDataDict:
420424
True
421425
422426
Invalidate and validate a single entry:
427+
423428
>>> data_dict[(0, 1)].invalidate()
424429
>>> data_dict[(0, 1)].is_valid()
425430
False
@@ -429,19 +434,21 @@ class DeepTrackDataDict:
429434
True
430435
431436
Check if a given `_ID` exists:
437+
432438
>>> (1, 0) in data_dict
433439
True
434440
435441
>>> (2, 2) in data_dict
436442
False
437443
438444
Iterate over all entries:
445+
439446
>>> for key, value in data_dict.items():
440447
... print(key, value.current_value())
441-
(0, 0) DeepTrackDataObject(data='Data at (0, 0)', valid=True)
442-
(0, 1) DeepTrackDataObject(data='Data at (0, 1)', valid=True)
443-
(1, 0) DeepTrackDataObject(data='Data at (1, 0)', valid=True)
444-
(1, 1) DeepTrackDataObject(data='Data at (1, 1)', valid=True)
448+
(0, 0) Data at (0, 0)
449+
(0, 1) Data at (0, 1)
450+
(1, 0) Data at (1, 0)
451+
(1, 1) Data at (1, 1)
445452
446453
>>> for key in data_dict.keys():
447454
... print(key)
@@ -458,6 +465,7 @@ class DeepTrackDataDict:
458465
DeepTrackDataObject(data='Data at (1, 1)', valid=True)
459466
460467
Check if an `_ID` is valid according to current keylength:
468+
461469
>>> data_dict.valid_index((0, 1))
462470
True
463471
@@ -848,7 +856,7 @@ class DeepTrackNode:
848856
garbage collection of nodes that are no longer used.
849857
dependencies: WeakSet[DeepTrackNode]
850858
Read-only property exposing the internal weak set `_dependencies`
851-
containign the nodes on which this node depends (its parents).
859+
containing the nodes on which this node depends (its parents).
852860
This is a weakref.WeakSet, for efficient memory management.
853861
_action: Callable[..., Any]
854862
The function or lambda-function to compute the node value.
@@ -940,6 +948,7 @@ class DeepTrackNode:
940948
>>> from deeptrack.backend.core import DeepTrackNode
941949
942950
Create three `DeepTrackNode` objects, as parent, child, and grandchild:
951+
943952
>>> parent = DeepTrackNode(
944953
... node_name="parent",
945954
... action=lambda: 10,
@@ -956,32 +965,37 @@ class DeepTrackNode:
956965
>>> child.add_child(grandchild)
957966
958967
Check all children of `parent` (includes `parent` itself):
968+
959969
>>> for node in parent.recurse_children():
960970
... print(node)
961971
DeepTrackNode(name='parent', len=0, action=<lambda>)
962972
DeepTrackNode(name='child', len=0, action=<lambda>)
963973
DeepTrackNode(name='grandchild', len=0, action=<lambda>)
964974
965975
Print the children tree:
976+
966977
>>> parent.print_children_tree()
967978
- DeepTrackNode 'parent' at 0x334202650
968979
- DeepTrackNode 'child' at 0x334201cf0
969980
- DeepTrackNode 'grandchild' at 0x334201ea0
970981
971982
Check all dependencies of `grandchild` (includes `grandchild` itself):
983+
972984
>>> for node in grandchild.recurse_dependencies():
973985
... print(node)
974986
DeepTrackNode(name='grandchild', len=0, action=<lambda>)
975987
DeepTrackNode(name='child', len=0, action=<lambda>)
976988
DeepTrackNode(name='parent', len=0, action=<lambda>)
977989
978990
Print the dependency tree:
991+
979992
>>> grandchild.print_dependencies_tree()
980993
- DeepTrackNode 'grandchild' at 0x334201ea0
981994
- DeepTrackNode 'child' at 0x334201cf0
982995
- DeepTrackNode 'parent' at 0x334202650
983996
984997
Store and retrieve data for specific _IDs:
998+
985999
>>> parent.store(15, _ID=(0,))
9861000
>>> parent.store(20, _ID=(1,))
9871001
>>> parent.current_value((0,))
@@ -990,6 +1004,7 @@ class DeepTrackNode:
9901004
20
9911005
9921006
Compute and retrieve the value for the child and grandchild node:
1007+
9931008
>>> child(_ID=(0,))
9941009
30
9951010
>>> child(_ID=(1,))
@@ -1000,6 +1015,7 @@ class DeepTrackNode:
10001015
120
10011016
10021017
Validation and invalidation:
1018+
10031019
>>> parent.is_valid((0,))
10041020
True
10051021
>>> child.is_valid((0,))
@@ -1024,6 +1040,7 @@ class DeepTrackNode:
10241040
False
10251041
10261042
Setting a value and automatic invalidation:
1043+
10271044
>>> parent.current_value((0,))
10281045
15
10291046
>>> grandchild((0,)) # Computes and stores the value in grandchild
@@ -1038,6 +1055,7 @@ class DeepTrackNode:
10381055
252
10391056
10401057
Resetting all data in the dependency tree (recomputation required):
1058+
10411059
>>> grandchild.update()
10421060
>>> grandchild()
10431061
60
@@ -1047,6 +1065,7 @@ class DeepTrackNode:
10471065
60
10481066
10491067
Operator overloading—arithmetic and comparison:
1068+
10501069
>>> node_a = DeepTrackNode(lambda: 5)
10511070
>>> node_b = DeepTrackNode(lambda: 3)
10521071
@@ -1079,26 +1098,31 @@ class DeepTrackNode:
10791098
True
10801099
10811100
Indexing into computed data:
1101+
10821102
>>> vector_node = DeepTrackNode(lambda: [10, 20, 30])
10831103
>>> first_element = vector_node[0]
10841104
>>> first_element()
10851105
10
10861106
10871107
Accessing a value before computing it raises an error:
1108+
10881109
>>> new_node = DeepTrackNode(lambda: 123)
10891110
>>> new_node.is_valid((42,))
10901111
False
1112+
10911113
>>> new_node.current_value((42,))
10921114
KeyError: 'Attempting to index an empty dict.'
10931115
10941116
Working with nested _ID slicing:
1117+
10951118
>>> parent = DeepTrackNode(lambda: 5)
10961119
>>> child = DeepTrackNode(lambda _ID=None: parent(_ID[:1]) + _ID[1])
10971120
>>> parent.add_child(child)
10981121
>>> child((0, 3)) # Equivalent to parent((0,)) + 3
10991122
8
11001123
11011124
Citations for a node and its dependencies:
1125+
11021126
>>> parent.get_citations() # Set of citation strings
11031127
{...}
11041128

0 commit comments

Comments
 (0)