tnfr 4.5.2__py3-none-any.whl → 8.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tnfr might be problematic. Click here for more details.
- tnfr/__init__.py +334 -50
- tnfr/__init__.pyi +33 -0
- tnfr/_compat.py +10 -0
- tnfr/_generated_version.py +34 -0
- tnfr/_version.py +49 -0
- tnfr/_version.pyi +7 -0
- tnfr/alias.py +214 -37
- tnfr/alias.pyi +108 -0
- tnfr/backends/__init__.py +354 -0
- tnfr/backends/jax_backend.py +173 -0
- tnfr/backends/numpy_backend.py +238 -0
- tnfr/backends/optimized_numpy.py +420 -0
- tnfr/backends/torch_backend.py +408 -0
- tnfr/cache.py +149 -556
- tnfr/cache.pyi +13 -0
- tnfr/cli/__init__.py +51 -16
- tnfr/cli/__init__.pyi +26 -0
- tnfr/cli/arguments.py +344 -32
- tnfr/cli/arguments.pyi +29 -0
- tnfr/cli/execution.py +676 -50
- tnfr/cli/execution.pyi +70 -0
- tnfr/cli/interactive_validator.py +614 -0
- tnfr/cli/utils.py +18 -3
- tnfr/cli/utils.pyi +7 -0
- tnfr/cli/validate.py +236 -0
- tnfr/compat/__init__.py +85 -0
- tnfr/compat/dataclass.py +136 -0
- tnfr/compat/jsonschema_stub.py +61 -0
- tnfr/compat/matplotlib_stub.py +73 -0
- tnfr/compat/numpy_stub.py +155 -0
- tnfr/config/__init__.py +224 -0
- tnfr/config/__init__.pyi +10 -0
- tnfr/{constants_glyphs.py → config/constants.py} +26 -20
- tnfr/config/constants.pyi +12 -0
- tnfr/config/defaults.py +54 -0
- tnfr/{constants/core.py → config/defaults_core.py} +59 -6
- tnfr/config/defaults_init.py +33 -0
- tnfr/config/defaults_metric.py +104 -0
- tnfr/config/feature_flags.py +81 -0
- tnfr/config/feature_flags.pyi +16 -0
- tnfr/config/glyph_constants.py +31 -0
- tnfr/config/init.py +77 -0
- tnfr/config/init.pyi +8 -0
- tnfr/config/operator_names.py +254 -0
- tnfr/config/operator_names.pyi +36 -0
- tnfr/config/physics_derivation.py +354 -0
- tnfr/config/presets.py +83 -0
- tnfr/config/presets.pyi +7 -0
- tnfr/config/security.py +927 -0
- tnfr/config/thresholds.py +114 -0
- tnfr/config/tnfr_config.py +498 -0
- tnfr/constants/__init__.py +51 -133
- tnfr/constants/__init__.pyi +92 -0
- tnfr/constants/aliases.py +33 -0
- tnfr/constants/aliases.pyi +27 -0
- tnfr/constants/init.py +3 -1
- tnfr/constants/init.pyi +12 -0
- tnfr/constants/metric.py +9 -15
- tnfr/constants/metric.pyi +19 -0
- tnfr/core/__init__.py +33 -0
- tnfr/core/container.py +226 -0
- tnfr/core/default_implementations.py +329 -0
- tnfr/core/interfaces.py +279 -0
- tnfr/dynamics/__init__.py +213 -633
- tnfr/dynamics/__init__.pyi +83 -0
- tnfr/dynamics/adaptation.py +267 -0
- tnfr/dynamics/adaptation.pyi +7 -0
- tnfr/dynamics/adaptive_sequences.py +189 -0
- tnfr/dynamics/adaptive_sequences.pyi +14 -0
- tnfr/dynamics/aliases.py +23 -0
- tnfr/dynamics/aliases.pyi +19 -0
- tnfr/dynamics/bifurcation.py +232 -0
- tnfr/dynamics/canonical.py +229 -0
- tnfr/dynamics/canonical.pyi +48 -0
- tnfr/dynamics/coordination.py +385 -0
- tnfr/dynamics/coordination.pyi +25 -0
- tnfr/dynamics/dnfr.py +2699 -398
- tnfr/dynamics/dnfr.pyi +26 -0
- tnfr/dynamics/dynamic_limits.py +225 -0
- tnfr/dynamics/feedback.py +252 -0
- tnfr/dynamics/feedback.pyi +24 -0
- tnfr/dynamics/fused_dnfr.py +454 -0
- tnfr/dynamics/homeostasis.py +157 -0
- tnfr/dynamics/homeostasis.pyi +14 -0
- tnfr/dynamics/integrators.py +496 -102
- tnfr/dynamics/integrators.pyi +36 -0
- tnfr/dynamics/learning.py +310 -0
- tnfr/dynamics/learning.pyi +33 -0
- tnfr/dynamics/metabolism.py +254 -0
- tnfr/dynamics/nbody.py +796 -0
- tnfr/dynamics/nbody_tnfr.py +783 -0
- tnfr/dynamics/propagation.py +326 -0
- tnfr/dynamics/runtime.py +908 -0
- tnfr/dynamics/runtime.pyi +77 -0
- tnfr/dynamics/sampling.py +10 -5
- tnfr/dynamics/sampling.pyi +7 -0
- tnfr/dynamics/selectors.py +711 -0
- tnfr/dynamics/selectors.pyi +85 -0
- tnfr/dynamics/structural_clip.py +207 -0
- tnfr/errors/__init__.py +37 -0
- tnfr/errors/contextual.py +492 -0
- tnfr/execution.py +77 -55
- tnfr/execution.pyi +45 -0
- tnfr/extensions/__init__.py +205 -0
- tnfr/extensions/__init__.pyi +18 -0
- tnfr/extensions/base.py +173 -0
- tnfr/extensions/base.pyi +35 -0
- tnfr/extensions/business/__init__.py +71 -0
- tnfr/extensions/business/__init__.pyi +11 -0
- tnfr/extensions/business/cookbook.py +88 -0
- tnfr/extensions/business/cookbook.pyi +8 -0
- tnfr/extensions/business/health_analyzers.py +202 -0
- tnfr/extensions/business/health_analyzers.pyi +9 -0
- tnfr/extensions/business/patterns.py +183 -0
- tnfr/extensions/business/patterns.pyi +8 -0
- tnfr/extensions/medical/__init__.py +73 -0
- tnfr/extensions/medical/__init__.pyi +11 -0
- tnfr/extensions/medical/cookbook.py +88 -0
- tnfr/extensions/medical/cookbook.pyi +8 -0
- tnfr/extensions/medical/health_analyzers.py +181 -0
- tnfr/extensions/medical/health_analyzers.pyi +9 -0
- tnfr/extensions/medical/patterns.py +163 -0
- tnfr/extensions/medical/patterns.pyi +8 -0
- tnfr/flatten.py +29 -50
- tnfr/flatten.pyi +21 -0
- tnfr/gamma.py +66 -53
- tnfr/gamma.pyi +36 -0
- tnfr/glyph_history.py +144 -57
- tnfr/glyph_history.pyi +35 -0
- tnfr/glyph_runtime.py +19 -0
- tnfr/glyph_runtime.pyi +8 -0
- tnfr/immutable.py +70 -30
- tnfr/immutable.pyi +36 -0
- tnfr/initialization.py +22 -16
- tnfr/initialization.pyi +65 -0
- tnfr/io.py +5 -241
- tnfr/io.pyi +13 -0
- tnfr/locking.pyi +7 -0
- tnfr/mathematics/__init__.py +79 -0
- tnfr/mathematics/backend.py +453 -0
- tnfr/mathematics/backend.pyi +99 -0
- tnfr/mathematics/dynamics.py +408 -0
- tnfr/mathematics/dynamics.pyi +90 -0
- tnfr/mathematics/epi.py +391 -0
- tnfr/mathematics/epi.pyi +65 -0
- tnfr/mathematics/generators.py +242 -0
- tnfr/mathematics/generators.pyi +29 -0
- tnfr/mathematics/metrics.py +119 -0
- tnfr/mathematics/metrics.pyi +16 -0
- tnfr/mathematics/operators.py +239 -0
- tnfr/mathematics/operators.pyi +59 -0
- tnfr/mathematics/operators_factory.py +124 -0
- tnfr/mathematics/operators_factory.pyi +11 -0
- tnfr/mathematics/projection.py +87 -0
- tnfr/mathematics/projection.pyi +33 -0
- tnfr/mathematics/runtime.py +182 -0
- tnfr/mathematics/runtime.pyi +64 -0
- tnfr/mathematics/spaces.py +256 -0
- tnfr/mathematics/spaces.pyi +83 -0
- tnfr/mathematics/transforms.py +305 -0
- tnfr/mathematics/transforms.pyi +62 -0
- tnfr/metrics/__init__.py +47 -9
- tnfr/metrics/__init__.pyi +20 -0
- tnfr/metrics/buffer_cache.py +163 -0
- tnfr/metrics/buffer_cache.pyi +24 -0
- tnfr/metrics/cache_utils.py +214 -0
- tnfr/metrics/coherence.py +1510 -330
- tnfr/metrics/coherence.pyi +129 -0
- tnfr/metrics/common.py +23 -16
- tnfr/metrics/common.pyi +35 -0
- tnfr/metrics/core.py +251 -36
- tnfr/metrics/core.pyi +13 -0
- tnfr/metrics/diagnosis.py +709 -110
- tnfr/metrics/diagnosis.pyi +86 -0
- tnfr/metrics/emergence.py +245 -0
- tnfr/metrics/export.py +60 -18
- tnfr/metrics/export.pyi +7 -0
- tnfr/metrics/glyph_timing.py +233 -43
- tnfr/metrics/glyph_timing.pyi +81 -0
- tnfr/metrics/learning_metrics.py +280 -0
- tnfr/metrics/learning_metrics.pyi +21 -0
- tnfr/metrics/phase_coherence.py +351 -0
- tnfr/metrics/phase_compatibility.py +349 -0
- tnfr/metrics/reporting.py +63 -28
- tnfr/metrics/reporting.pyi +25 -0
- tnfr/metrics/sense_index.py +1126 -43
- tnfr/metrics/sense_index.pyi +9 -0
- tnfr/metrics/trig.py +215 -23
- tnfr/metrics/trig.pyi +13 -0
- tnfr/metrics/trig_cache.py +148 -24
- tnfr/metrics/trig_cache.pyi +10 -0
- tnfr/multiscale/__init__.py +32 -0
- tnfr/multiscale/hierarchical.py +517 -0
- tnfr/node.py +646 -140
- tnfr/node.pyi +139 -0
- tnfr/observers.py +160 -45
- tnfr/observers.pyi +31 -0
- tnfr/ontosim.py +23 -19
- tnfr/ontosim.pyi +28 -0
- tnfr/operators/__init__.py +1358 -106
- tnfr/operators/__init__.pyi +31 -0
- tnfr/operators/algebra.py +277 -0
- tnfr/operators/canonical_patterns.py +420 -0
- tnfr/operators/cascade.py +267 -0
- tnfr/operators/cycle_detection.py +358 -0
- tnfr/operators/definitions.py +4108 -0
- tnfr/operators/definitions.pyi +78 -0
- tnfr/operators/grammar.py +1164 -0
- tnfr/operators/grammar.pyi +140 -0
- tnfr/operators/hamiltonian.py +710 -0
- tnfr/operators/health_analyzer.py +809 -0
- tnfr/operators/jitter.py +107 -38
- tnfr/operators/jitter.pyi +11 -0
- tnfr/operators/lifecycle.py +314 -0
- tnfr/operators/metabolism.py +618 -0
- tnfr/operators/metrics.py +2138 -0
- tnfr/operators/network_analysis/__init__.py +27 -0
- tnfr/operators/network_analysis/source_detection.py +186 -0
- tnfr/operators/nodal_equation.py +395 -0
- tnfr/operators/pattern_detection.py +660 -0
- tnfr/operators/patterns.py +669 -0
- tnfr/operators/postconditions/__init__.py +38 -0
- tnfr/operators/postconditions/mutation.py +236 -0
- tnfr/operators/preconditions/__init__.py +1226 -0
- tnfr/operators/preconditions/coherence.py +305 -0
- tnfr/operators/preconditions/dissonance.py +236 -0
- tnfr/operators/preconditions/emission.py +128 -0
- tnfr/operators/preconditions/mutation.py +580 -0
- tnfr/operators/preconditions/reception.py +125 -0
- tnfr/operators/preconditions/resonance.py +364 -0
- tnfr/operators/registry.py +74 -0
- tnfr/operators/registry.pyi +9 -0
- tnfr/operators/remesh.py +1415 -91
- tnfr/operators/remesh.pyi +26 -0
- tnfr/operators/structural_units.py +268 -0
- tnfr/operators/unified_grammar.py +105 -0
- tnfr/parallel/__init__.py +54 -0
- tnfr/parallel/auto_scaler.py +234 -0
- tnfr/parallel/distributed.py +384 -0
- tnfr/parallel/engine.py +238 -0
- tnfr/parallel/gpu_engine.py +420 -0
- tnfr/parallel/monitoring.py +248 -0
- tnfr/parallel/partitioner.py +459 -0
- tnfr/py.typed +0 -0
- tnfr/recipes/__init__.py +22 -0
- tnfr/recipes/cookbook.py +743 -0
- tnfr/rng.py +75 -151
- tnfr/rng.pyi +26 -0
- tnfr/schemas/__init__.py +8 -0
- tnfr/schemas/grammar.json +94 -0
- tnfr/sdk/__init__.py +107 -0
- tnfr/sdk/__init__.pyi +19 -0
- tnfr/sdk/adaptive_system.py +173 -0
- tnfr/sdk/adaptive_system.pyi +21 -0
- tnfr/sdk/builders.py +370 -0
- tnfr/sdk/builders.pyi +51 -0
- tnfr/sdk/fluent.py +1121 -0
- tnfr/sdk/fluent.pyi +74 -0
- tnfr/sdk/templates.py +342 -0
- tnfr/sdk/templates.pyi +41 -0
- tnfr/sdk/utils.py +341 -0
- tnfr/secure_config.py +46 -0
- tnfr/security/__init__.py +70 -0
- tnfr/security/database.py +514 -0
- tnfr/security/subprocess.py +503 -0
- tnfr/security/validation.py +290 -0
- tnfr/selector.py +59 -22
- tnfr/selector.pyi +19 -0
- tnfr/sense.py +92 -67
- tnfr/sense.pyi +23 -0
- tnfr/services/__init__.py +17 -0
- tnfr/services/orchestrator.py +325 -0
- tnfr/sparse/__init__.py +39 -0
- tnfr/sparse/representations.py +492 -0
- tnfr/structural.py +639 -263
- tnfr/structural.pyi +83 -0
- tnfr/telemetry/__init__.py +35 -0
- tnfr/telemetry/cache_metrics.py +226 -0
- tnfr/telemetry/cache_metrics.pyi +64 -0
- tnfr/telemetry/nu_f.py +422 -0
- tnfr/telemetry/nu_f.pyi +108 -0
- tnfr/telemetry/verbosity.py +36 -0
- tnfr/telemetry/verbosity.pyi +15 -0
- tnfr/tokens.py +2 -4
- tnfr/tokens.pyi +36 -0
- tnfr/tools/__init__.py +20 -0
- tnfr/tools/domain_templates.py +478 -0
- tnfr/tools/sequence_generator.py +846 -0
- tnfr/topology/__init__.py +13 -0
- tnfr/topology/asymmetry.py +151 -0
- tnfr/trace.py +300 -126
- tnfr/trace.pyi +42 -0
- tnfr/tutorials/__init__.py +38 -0
- tnfr/tutorials/autonomous_evolution.py +285 -0
- tnfr/tutorials/interactive.py +1576 -0
- tnfr/tutorials/structural_metabolism.py +238 -0
- tnfr/types.py +743 -12
- tnfr/types.pyi +357 -0
- tnfr/units.py +68 -0
- tnfr/units.pyi +13 -0
- tnfr/utils/__init__.py +282 -0
- tnfr/utils/__init__.pyi +215 -0
- tnfr/utils/cache.py +4223 -0
- tnfr/utils/cache.pyi +470 -0
- tnfr/{callback_utils.py → utils/callbacks.py} +26 -39
- tnfr/utils/callbacks.pyi +49 -0
- tnfr/utils/chunks.py +108 -0
- tnfr/utils/chunks.pyi +22 -0
- tnfr/utils/data.py +428 -0
- tnfr/utils/data.pyi +74 -0
- tnfr/utils/graph.py +85 -0
- tnfr/utils/graph.pyi +10 -0
- tnfr/utils/init.py +821 -0
- tnfr/utils/init.pyi +80 -0
- tnfr/utils/io.py +559 -0
- tnfr/utils/io.pyi +66 -0
- tnfr/{helpers → utils}/numeric.py +51 -24
- tnfr/utils/numeric.pyi +21 -0
- tnfr/validation/__init__.py +257 -0
- tnfr/validation/__init__.pyi +85 -0
- tnfr/validation/compatibility.py +460 -0
- tnfr/validation/compatibility.pyi +6 -0
- tnfr/validation/config.py +73 -0
- tnfr/validation/graph.py +139 -0
- tnfr/validation/graph.pyi +18 -0
- tnfr/validation/input_validation.py +755 -0
- tnfr/validation/invariants.py +712 -0
- tnfr/validation/rules.py +253 -0
- tnfr/validation/rules.pyi +44 -0
- tnfr/validation/runtime.py +279 -0
- tnfr/validation/runtime.pyi +28 -0
- tnfr/validation/sequence_validator.py +162 -0
- tnfr/validation/soft_filters.py +170 -0
- tnfr/validation/soft_filters.pyi +32 -0
- tnfr/validation/spectral.py +164 -0
- tnfr/validation/spectral.pyi +42 -0
- tnfr/validation/validator.py +1266 -0
- tnfr/validation/window.py +39 -0
- tnfr/validation/window.pyi +1 -0
- tnfr/visualization/__init__.py +98 -0
- tnfr/visualization/cascade_viz.py +256 -0
- tnfr/visualization/hierarchy.py +284 -0
- tnfr/visualization/sequence_plotter.py +784 -0
- tnfr/viz/__init__.py +60 -0
- tnfr/viz/matplotlib.py +278 -0
- tnfr/viz/matplotlib.pyi +35 -0
- tnfr-8.5.0.dist-info/METADATA +573 -0
- tnfr-8.5.0.dist-info/RECORD +353 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/entry_points.txt +1 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/licenses/LICENSE.md +1 -1
- tnfr/collections_utils.py +0 -300
- tnfr/config.py +0 -32
- tnfr/grammar.py +0 -344
- tnfr/graph_utils.py +0 -84
- tnfr/helpers/__init__.py +0 -71
- tnfr/import_utils.py +0 -228
- tnfr/json_utils.py +0 -162
- tnfr/logging_utils.py +0 -116
- tnfr/presets.py +0 -60
- tnfr/validators.py +0 -84
- tnfr/value_utils.py +0 -59
- tnfr-4.5.2.dist-info/METADATA +0 -379
- tnfr-4.5.2.dist-info/RECORD +0 -67
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/WHEEL +0 -0
- {tnfr-4.5.2.dist-info → tnfr-8.5.0.dist-info}/top_level.txt +0 -0
tnfr/operators/jitter.py
CHANGED
|
@@ -1,24 +1,31 @@
|
|
|
1
|
+
"""Jitter operators for reproducible phase perturbations."""
|
|
2
|
+
|
|
1
3
|
from __future__ import annotations
|
|
2
|
-
from typing import Any, TYPE_CHECKING
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
import threading
|
|
6
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
7
|
|
|
6
|
-
from ..
|
|
8
|
+
from ..rng import base_seed, cache_enabled
|
|
9
|
+
from ..rng import clear_rng_cache as _clear_rng_cache
|
|
7
10
|
from ..rng import (
|
|
8
|
-
ScopedCounterCache,
|
|
9
11
|
make_rng,
|
|
10
|
-
base_seed,
|
|
11
|
-
cache_enabled,
|
|
12
|
-
clear_rng_cache as _clear_rng_cache,
|
|
13
12
|
seed_hash,
|
|
14
13
|
)
|
|
15
|
-
from ..
|
|
14
|
+
from ..types import NodeId, TNFRGraph
|
|
15
|
+
from ..utils import (
|
|
16
|
+
CacheManager,
|
|
17
|
+
InstrumentedLRUCache,
|
|
18
|
+
ScopedCounterCache,
|
|
19
|
+
build_cache_manager,
|
|
20
|
+
ensure_node_offset_map,
|
|
21
|
+
get_nodenx,
|
|
22
|
+
)
|
|
16
23
|
|
|
17
24
|
if TYPE_CHECKING: # pragma: no cover - type checking only
|
|
18
|
-
from ..node import
|
|
25
|
+
from ..node import NodeProtocol
|
|
19
26
|
|
|
20
27
|
# Guarded by the cache lock to ensure thread-safe access. ``seq`` stores
|
|
21
|
-
# per-scope jitter sequence counters in an LRU cache bounded to avoid
|
|
28
|
+
# per-scope jitter sequence counters in an instrumented LRU cache bounded to avoid
|
|
22
29
|
# unbounded memory usage.
|
|
23
30
|
_JITTER_MAX_ENTRIES = 1024
|
|
24
31
|
|
|
@@ -26,18 +33,53 @@ _JITTER_MAX_ENTRIES = 1024
|
|
|
26
33
|
class JitterCache:
|
|
27
34
|
"""Container for jitter-related caches."""
|
|
28
35
|
|
|
29
|
-
def __init__(
|
|
30
|
-
self
|
|
31
|
-
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
max_entries: int = _JITTER_MAX_ENTRIES,
|
|
39
|
+
*,
|
|
40
|
+
manager: CacheManager | None = None,
|
|
41
|
+
) -> None:
|
|
42
|
+
self._manager = manager or build_cache_manager()
|
|
43
|
+
if not self._manager.has_override("scoped_counter:jitter"):
|
|
44
|
+
self._manager.configure(
|
|
45
|
+
overrides={"scoped_counter:jitter": int(max_entries)}
|
|
46
|
+
)
|
|
47
|
+
self._sequence = ScopedCounterCache(
|
|
48
|
+
"jitter",
|
|
49
|
+
max_entries=None,
|
|
50
|
+
manager=self._manager,
|
|
51
|
+
default_max_entries=int(max_entries),
|
|
52
|
+
)
|
|
53
|
+
self._settings_key = "jitter_settings"
|
|
54
|
+
self._manager.register(
|
|
55
|
+
self._settings_key,
|
|
56
|
+
lambda: {"max_entries": self._sequence.max_entries},
|
|
57
|
+
reset=self._reset_settings,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def _reset_settings(self, settings: dict[str, Any] | None) -> dict[str, Any]:
|
|
61
|
+
return {"max_entries": self._sequence.max_entries}
|
|
62
|
+
|
|
63
|
+
def _refresh_settings(self) -> None:
|
|
64
|
+
self._manager.update(
|
|
65
|
+
self._settings_key,
|
|
66
|
+
lambda _: {"max_entries": self._sequence.max_entries},
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def manager(self) -> CacheManager:
|
|
71
|
+
"""Expose the cache manager backing this cache."""
|
|
72
|
+
|
|
73
|
+
return self._manager
|
|
32
74
|
|
|
33
75
|
@property
|
|
34
|
-
def seq(self) ->
|
|
35
|
-
"""Expose the sequence cache for tests and diagnostics."""
|
|
76
|
+
def seq(self) -> InstrumentedLRUCache[tuple[int, int], int]:
|
|
77
|
+
"""Expose the instrumented sequence cache for tests and diagnostics."""
|
|
36
78
|
|
|
37
79
|
return self._sequence.cache
|
|
38
80
|
|
|
39
81
|
@property
|
|
40
|
-
def lock(self):
|
|
82
|
+
def lock(self) -> threading.Lock | threading.RLock:
|
|
41
83
|
"""Return the lock protecting the sequence cache."""
|
|
42
84
|
|
|
43
85
|
return self._sequence.lock
|
|
@@ -53,21 +95,26 @@ class JitterCache:
|
|
|
53
95
|
"""Set the maximum number of cached jitter sequences."""
|
|
54
96
|
|
|
55
97
|
self._sequence.configure(max_entries=int(value))
|
|
56
|
-
self.
|
|
98
|
+
self._refresh_settings()
|
|
57
99
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
100
|
+
@property
|
|
101
|
+
def settings(self) -> dict[str, Any]:
|
|
102
|
+
"""Return jitter cache settings stored on the manager."""
|
|
103
|
+
|
|
104
|
+
return cast(dict[str, Any], self._manager.get(self._settings_key))
|
|
105
|
+
|
|
106
|
+
def setup(self, force: bool = False, max_entries: int | None = None) -> None:
|
|
61
107
|
"""Ensure jitter cache matches the configured size."""
|
|
62
108
|
|
|
63
109
|
self._sequence.configure(force=force, max_entries=max_entries)
|
|
64
|
-
self.
|
|
110
|
+
self._refresh_settings()
|
|
65
111
|
|
|
66
112
|
def clear(self) -> None:
|
|
67
113
|
"""Clear cached RNGs and jitter state."""
|
|
68
114
|
|
|
69
115
|
_clear_rng_cache()
|
|
70
116
|
self._sequence.clear()
|
|
117
|
+
self._manager.clear(self._settings_key)
|
|
71
118
|
|
|
72
119
|
def bump(self, key: tuple[int, int]) -> int:
|
|
73
120
|
"""Return current jitter sequence counter for ``key`` and increment it."""
|
|
@@ -78,40 +125,57 @@ class JitterCache:
|
|
|
78
125
|
class JitterCacheManager:
|
|
79
126
|
"""Manager exposing the jitter cache without global reassignment."""
|
|
80
127
|
|
|
81
|
-
def __init__(
|
|
82
|
-
self
|
|
128
|
+
def __init__(
|
|
129
|
+
self,
|
|
130
|
+
cache: JitterCache | None = None,
|
|
131
|
+
*,
|
|
132
|
+
manager: CacheManager | None = None,
|
|
133
|
+
) -> None:
|
|
134
|
+
if cache is not None:
|
|
135
|
+
self.cache = cache
|
|
136
|
+
self._manager = cache.manager
|
|
137
|
+
else:
|
|
138
|
+
self._manager = manager or build_cache_manager()
|
|
139
|
+
self.cache = JitterCache(manager=self._manager)
|
|
83
140
|
|
|
84
141
|
# Convenience passthrough properties
|
|
85
142
|
@property
|
|
86
|
-
def seq(self) ->
|
|
143
|
+
def seq(self) -> InstrumentedLRUCache[tuple[int, int], int]:
|
|
144
|
+
"""Expose the underlying instrumented jitter sequence cache."""
|
|
145
|
+
|
|
87
146
|
return self.cache.seq
|
|
88
147
|
|
|
89
148
|
@property
|
|
90
149
|
def settings(self) -> dict[str, Any]:
|
|
150
|
+
"""Return persisted jitter cache configuration."""
|
|
151
|
+
|
|
91
152
|
return self.cache.settings
|
|
92
153
|
|
|
93
154
|
@property
|
|
94
|
-
def lock(self):
|
|
155
|
+
def lock(self) -> threading.Lock | threading.RLock:
|
|
156
|
+
"""Return the lock associated with the jitter cache."""
|
|
157
|
+
|
|
95
158
|
return self.cache.lock
|
|
96
159
|
|
|
97
160
|
@property
|
|
98
161
|
def max_entries(self) -> int:
|
|
99
162
|
"""Return the maximum number of cached jitter entries."""
|
|
163
|
+
|
|
100
164
|
return self.cache.max_entries
|
|
101
165
|
|
|
102
166
|
@max_entries.setter
|
|
103
167
|
def max_entries(self, value: int) -> None:
|
|
104
168
|
"""Set the maximum number of cached jitter entries."""
|
|
169
|
+
|
|
105
170
|
self.cache.max_entries = value
|
|
106
171
|
|
|
107
|
-
def setup(
|
|
108
|
-
self, force: bool = False, max_entries: int | None = None
|
|
109
|
-
) -> None:
|
|
172
|
+
def setup(self, force: bool = False, max_entries: int | None = None) -> None:
|
|
110
173
|
"""Ensure jitter cache matches the configured size.
|
|
111
174
|
|
|
112
175
|
``max_entries`` may be provided to explicitly resize the cache.
|
|
113
176
|
When omitted the existing ``cache.max_entries`` is preserved.
|
|
114
177
|
"""
|
|
178
|
+
|
|
115
179
|
if max_entries is not None:
|
|
116
180
|
self.cache.setup(force=True, max_entries=max_entries)
|
|
117
181
|
else:
|
|
@@ -119,6 +183,7 @@ class JitterCacheManager:
|
|
|
119
183
|
|
|
120
184
|
def clear(self) -> None:
|
|
121
185
|
"""Clear cached RNGs and jitter state."""
|
|
186
|
+
|
|
122
187
|
self.cache.clear()
|
|
123
188
|
|
|
124
189
|
def bump(self, key: tuple[int, int]) -> int:
|
|
@@ -148,27 +213,31 @@ def reset_jitter_manager() -> None:
|
|
|
148
213
|
_JITTER_MANAGER = None
|
|
149
214
|
|
|
150
215
|
|
|
151
|
-
def _node_offset(G, n) -> int:
|
|
216
|
+
def _node_offset(G: TNFRGraph, n: NodeId) -> int:
|
|
152
217
|
"""Deterministic node index used for jitter seeds."""
|
|
153
218
|
mapping = ensure_node_offset_map(G)
|
|
154
219
|
return int(mapping.get(n, 0))
|
|
155
220
|
|
|
156
221
|
|
|
157
|
-
def _resolve_jitter_seed(node:
|
|
158
|
-
|
|
159
|
-
if
|
|
160
|
-
raise ImportError("
|
|
161
|
-
if isinstance(node,
|
|
162
|
-
|
|
222
|
+
def _resolve_jitter_seed(node: NodeProtocol) -> tuple[int, int]:
|
|
223
|
+
node_nx_type = get_nodenx()
|
|
224
|
+
if node_nx_type is None:
|
|
225
|
+
raise ImportError("NodeNX is unavailable")
|
|
226
|
+
if isinstance(node, node_nx_type):
|
|
227
|
+
graph = cast(TNFRGraph, getattr(node, "G"))
|
|
228
|
+
node_id = cast(NodeId, getattr(node, "n"))
|
|
229
|
+
return _node_offset(graph, node_id), id(graph)
|
|
163
230
|
uid = getattr(node, "_noise_uid", None)
|
|
164
231
|
if uid is None:
|
|
165
232
|
uid = id(node)
|
|
166
233
|
setattr(node, "_noise_uid", uid)
|
|
167
|
-
|
|
234
|
+
graph = cast(TNFRGraph | None, getattr(node, "G", None))
|
|
235
|
+
scope = graph if graph is not None else node
|
|
236
|
+
return int(uid), id(scope)
|
|
168
237
|
|
|
169
238
|
|
|
170
239
|
def random_jitter(
|
|
171
|
-
node:
|
|
240
|
+
node: NodeProtocol,
|
|
172
241
|
amplitude: float,
|
|
173
242
|
) -> float:
|
|
174
243
|
"""Return deterministic noise in ``[-amplitude, amplitude]`` for ``node``.
|
|
@@ -184,7 +253,7 @@ def random_jitter(
|
|
|
184
253
|
seed_root = base_seed(node.G)
|
|
185
254
|
seed_key, scope_id = _resolve_jitter_seed(node)
|
|
186
255
|
|
|
187
|
-
cache_key = (seed_root, scope_id)
|
|
256
|
+
cache_key = (seed_root, scope_id, seed_key)
|
|
188
257
|
seq = 0
|
|
189
258
|
if cache_enabled(node.G):
|
|
190
259
|
manager = get_jitter_manager()
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""Node lifecycle management for TNFR canonical theory.
|
|
2
|
+
|
|
3
|
+
According to TNFR theory (El pulso que nos atraviesa, p.44), nodes follow
|
|
4
|
+
a canonical lifecycle:
|
|
5
|
+
|
|
6
|
+
1. Activation - Node emerges through sufficient reorganization
|
|
7
|
+
2. Stabilization - Finds coherent phase and form
|
|
8
|
+
3. Propagation - Reorganizes its network environment
|
|
9
|
+
4. Mutation - Transforms through dissonance
|
|
10
|
+
5. Collapse - Loses phase/frequency and dissolves
|
|
11
|
+
|
|
12
|
+
This module provides lifecycle state tracking and transition validation.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from enum import Enum
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from ..types import NodeId, TNFRGraph
|
|
22
|
+
|
|
23
|
+
from ..alias import get_attr
|
|
24
|
+
from ..constants.aliases import ALIAS_EPI, ALIAS_VF, ALIAS_DNFR, ALIAS_THETA
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"LifecycleState",
|
|
28
|
+
"CollapseReason",
|
|
29
|
+
"get_lifecycle_state",
|
|
30
|
+
"check_collapse_conditions",
|
|
31
|
+
"should_collapse",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# Default thresholds for lifecycle state determination
|
|
35
|
+
DEFAULT_MIN_PHASE_COUPLING = 0.1 # Minimum phase coupling before decoupling collapse
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class LifecycleState(Enum):
|
|
39
|
+
"""Canonical TNFR node lifecycle states.
|
|
40
|
+
|
|
41
|
+
These states correspond to the fundamental phases of node existence
|
|
42
|
+
in the Resonant Fractal Nature paradigm.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
DORMANT = "dormant"
|
|
46
|
+
"""Node exists but has minimal structural frequency (νf < activation_threshold)."""
|
|
47
|
+
|
|
48
|
+
ACTIVATION = "activation"
|
|
49
|
+
"""Node is emerging with increasing νf and ΔNFR."""
|
|
50
|
+
|
|
51
|
+
STABILIZATION = "stabilization"
|
|
52
|
+
"""Node is finding coherent form (high C(t), decreasing |ΔNFR|)."""
|
|
53
|
+
|
|
54
|
+
PROPAGATION = "propagation"
|
|
55
|
+
"""Node is reorganizing its environment (high phase coupling)."""
|
|
56
|
+
|
|
57
|
+
MUTATION = "mutation"
|
|
58
|
+
"""Node is undergoing phase transformation (high |ΔNFR|, phase shifts)."""
|
|
59
|
+
|
|
60
|
+
COLLAPSING = "collapsing"
|
|
61
|
+
"""Node is losing coherence and approaching dissolution."""
|
|
62
|
+
|
|
63
|
+
COLLAPSED = "collapsed"
|
|
64
|
+
"""Node has dissolved (νf → 0 or extreme dissonance)."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class CollapseReason(Enum):
|
|
68
|
+
"""Canonical reasons for node collapse in TNFR.
|
|
69
|
+
|
|
70
|
+
These correspond to the fundamental ways structural coherence can fail.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
FREQUENCY_FAILURE = "frequency_failure"
|
|
74
|
+
"""Structural frequency dropped below collapse threshold (νf → 0)."""
|
|
75
|
+
|
|
76
|
+
EXTREME_DISSONANCE = "extreme_dissonance"
|
|
77
|
+
"""ΔNFR magnitude exceeded bifurcation threshold."""
|
|
78
|
+
|
|
79
|
+
NETWORK_DECOUPLING = "network_decoupling"
|
|
80
|
+
"""Phase coherence with network dropped below coupling threshold."""
|
|
81
|
+
|
|
82
|
+
EPI_DISSOLUTION = "epi_dissolution"
|
|
83
|
+
"""Primary Information Structure lost coherence (EPI → 0)."""
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _get_node_attr(
|
|
87
|
+
G: TNFRGraph, node: NodeId, aliases: tuple[str, ...], default: float = 0.0
|
|
88
|
+
) -> float:
|
|
89
|
+
"""Get node attribute using alias fallback."""
|
|
90
|
+
return float(get_attr(G.nodes[node], aliases, default))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def get_lifecycle_state(
|
|
94
|
+
G: TNFRGraph,
|
|
95
|
+
node: NodeId,
|
|
96
|
+
*,
|
|
97
|
+
config: dict[str, Any] | None = None,
|
|
98
|
+
) -> LifecycleState:
|
|
99
|
+
"""Determine current lifecycle state of a node.
|
|
100
|
+
|
|
101
|
+
Analyzes node's structural parameters (νf, ΔNFR, EPI, θ) to determine
|
|
102
|
+
its position in the canonical TNFR lifecycle.
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
G : TNFRGraph
|
|
107
|
+
Graph containing the node
|
|
108
|
+
node : NodeId
|
|
109
|
+
Node to analyze
|
|
110
|
+
config : dict, optional
|
|
111
|
+
Configuration overrides for thresholds:
|
|
112
|
+
- activation_threshold: Min νf for activation (default: 0.1)
|
|
113
|
+
- collapse_threshold: Min νf to avoid collapse (default: 0.01)
|
|
114
|
+
- bifurcation_threshold: Max |ΔNFR| before bifurcation (default: 10.0)
|
|
115
|
+
- stabilization_dnfr: Max |ΔNFR| for stabilization (default: 1.0)
|
|
116
|
+
- stabilization_coherence: Min coherence for stabilization (default: 0.8)
|
|
117
|
+
- propagation_coupling: Min phase coupling for propagation (default: 0.7)
|
|
118
|
+
- mutation_dnfr: Min |ΔNFR| for mutation state (default: 5.0)
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
LifecycleState
|
|
123
|
+
Current lifecycle state
|
|
124
|
+
|
|
125
|
+
Notes
|
|
126
|
+
-----
|
|
127
|
+
Collapse conditions are checked first. Among active states, the state
|
|
128
|
+
with the strongest indicators is returned (e.g., high |ΔNFR| → mutation
|
|
129
|
+
takes precedence over stabilization).
|
|
130
|
+
|
|
131
|
+
Examples
|
|
132
|
+
--------
|
|
133
|
+
>>> from tnfr.structural import create_nfr
|
|
134
|
+
>>> G, node = create_nfr("test", epi=0.5, vf=1.0)
|
|
135
|
+
>>> G.nodes[node]["ΔNFR"] = 0.5
|
|
136
|
+
>>> state = get_lifecycle_state(G, node)
|
|
137
|
+
>>> state.value
|
|
138
|
+
'activation'
|
|
139
|
+
"""
|
|
140
|
+
if config is None:
|
|
141
|
+
config = {}
|
|
142
|
+
|
|
143
|
+
# Get thresholds from config or graph or defaults
|
|
144
|
+
def _get_threshold(key: str, default: float) -> float:
|
|
145
|
+
return float(config.get(key, G.graph.get(key.upper(), default)))
|
|
146
|
+
|
|
147
|
+
activation_threshold = _get_threshold("activation_threshold", 0.1)
|
|
148
|
+
collapse_threshold = _get_threshold("collapse_threshold", 0.01)
|
|
149
|
+
bifurcation_threshold = _get_threshold("bifurcation_threshold", 10.0)
|
|
150
|
+
stabilization_dnfr = _get_threshold("stabilization_dnfr", 1.0)
|
|
151
|
+
stabilization_coherence = _get_threshold("stabilization_coherence", 0.8)
|
|
152
|
+
propagation_coupling = _get_threshold("propagation_coupling", 0.7)
|
|
153
|
+
mutation_dnfr = _get_threshold("mutation_dnfr", 5.0)
|
|
154
|
+
|
|
155
|
+
# Get node structural parameters
|
|
156
|
+
vf = _get_node_attr(G, node, ALIAS_VF)
|
|
157
|
+
dnfr = _get_node_attr(G, node, ALIAS_DNFR)
|
|
158
|
+
epi = _get_node_attr(G, node, ALIAS_EPI)
|
|
159
|
+
theta = _get_node_attr(G, node, ALIAS_THETA)
|
|
160
|
+
|
|
161
|
+
# Check for collapse conditions first
|
|
162
|
+
if vf < collapse_threshold:
|
|
163
|
+
return LifecycleState.COLLAPSING
|
|
164
|
+
|
|
165
|
+
if abs(dnfr) > bifurcation_threshold:
|
|
166
|
+
return LifecycleState.COLLAPSING
|
|
167
|
+
|
|
168
|
+
# Compute phase coupling (simplified - could use full network coupling)
|
|
169
|
+
neighbors = list(G.neighbors(node))
|
|
170
|
+
if neighbors:
|
|
171
|
+
import math
|
|
172
|
+
|
|
173
|
+
neighbor_phases = [_get_node_attr(G, n, ALIAS_THETA) for n in neighbors]
|
|
174
|
+
mean_neighbor_phase = sum(neighbor_phases) / len(neighbor_phases)
|
|
175
|
+
phase_diff = abs(theta - mean_neighbor_phase)
|
|
176
|
+
# Normalize to [0, 1] where 1 is perfect alignment
|
|
177
|
+
phase_coupling = 1.0 - min(phase_diff, math.pi) / math.pi
|
|
178
|
+
else:
|
|
179
|
+
phase_coupling = 0.0
|
|
180
|
+
|
|
181
|
+
# Check for decoupling collapse
|
|
182
|
+
if neighbors and phase_coupling < DEFAULT_MIN_PHASE_COUPLING:
|
|
183
|
+
return LifecycleState.COLLAPSING
|
|
184
|
+
|
|
185
|
+
# Check active states (priority: mutation > propagation > stabilization > activation)
|
|
186
|
+
|
|
187
|
+
# Mutation: High dissonance with sufficient frequency
|
|
188
|
+
if abs(dnfr) > mutation_dnfr and vf > activation_threshold:
|
|
189
|
+
return LifecycleState.MUTATION
|
|
190
|
+
|
|
191
|
+
# Propagation: Strong network coupling
|
|
192
|
+
if phase_coupling > propagation_coupling and vf > activation_threshold:
|
|
193
|
+
return LifecycleState.PROPAGATION
|
|
194
|
+
|
|
195
|
+
# Stabilization: High coherence, low dissonance
|
|
196
|
+
# Note: C(t) computation would require full graph state, using EPI as proxy
|
|
197
|
+
if abs(dnfr) < stabilization_dnfr and epi > stabilization_coherence:
|
|
198
|
+
return LifecycleState.STABILIZATION
|
|
199
|
+
|
|
200
|
+
# Activation: Above activation threshold but not yet stabilized
|
|
201
|
+
if vf >= activation_threshold:
|
|
202
|
+
return LifecycleState.ACTIVATION
|
|
203
|
+
|
|
204
|
+
# Dormant: Below activation threshold but above collapse
|
|
205
|
+
return LifecycleState.DORMANT
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def check_collapse_conditions(
|
|
209
|
+
G: TNFRGraph,
|
|
210
|
+
node: NodeId,
|
|
211
|
+
*,
|
|
212
|
+
config: dict[str, Any] | None = None,
|
|
213
|
+
) -> tuple[bool, CollapseReason | None]:
|
|
214
|
+
"""Check if node meets any collapse conditions.
|
|
215
|
+
|
|
216
|
+
Parameters
|
|
217
|
+
----------
|
|
218
|
+
G : TNFRGraph
|
|
219
|
+
Graph containing the node
|
|
220
|
+
node : NodeId
|
|
221
|
+
Node to check
|
|
222
|
+
config : dict, optional
|
|
223
|
+
Configuration overrides for collapse thresholds
|
|
224
|
+
|
|
225
|
+
Returns
|
|
226
|
+
-------
|
|
227
|
+
should_collapse : bool
|
|
228
|
+
True if node should collapse
|
|
229
|
+
reason : CollapseReason | None
|
|
230
|
+
Reason for collapse, or None if not collapsing
|
|
231
|
+
|
|
232
|
+
Notes
|
|
233
|
+
-----
|
|
234
|
+
Multiple collapse conditions may be met simultaneously. This function
|
|
235
|
+
returns the first detected condition in priority order:
|
|
236
|
+
1. Frequency failure (most fundamental)
|
|
237
|
+
2. Extreme dissonance (structural instability)
|
|
238
|
+
3. Network decoupling (loss of resonance)
|
|
239
|
+
4. EPI dissolution (form loss)
|
|
240
|
+
"""
|
|
241
|
+
if config is None:
|
|
242
|
+
config = {}
|
|
243
|
+
|
|
244
|
+
def _get_threshold(key: str, default: float) -> float:
|
|
245
|
+
return float(config.get(key, G.graph.get(key.upper(), default)))
|
|
246
|
+
|
|
247
|
+
collapse_threshold = _get_threshold("collapse_threshold", 0.01)
|
|
248
|
+
bifurcation_threshold = _get_threshold("bifurcation_threshold", 10.0)
|
|
249
|
+
min_coupling = _get_threshold("min_phase_coupling", 0.1)
|
|
250
|
+
min_epi = _get_threshold("min_epi", 0.01)
|
|
251
|
+
|
|
252
|
+
# Get node parameters
|
|
253
|
+
vf = _get_node_attr(G, node, ALIAS_VF)
|
|
254
|
+
dnfr = _get_node_attr(G, node, ALIAS_DNFR)
|
|
255
|
+
epi = _get_node_attr(G, node, ALIAS_EPI)
|
|
256
|
+
theta = _get_node_attr(G, node, ALIAS_THETA)
|
|
257
|
+
|
|
258
|
+
# Check frequency failure (most fundamental)
|
|
259
|
+
if vf < collapse_threshold:
|
|
260
|
+
return (True, CollapseReason.FREQUENCY_FAILURE)
|
|
261
|
+
|
|
262
|
+
# Check extreme dissonance
|
|
263
|
+
if abs(dnfr) > bifurcation_threshold:
|
|
264
|
+
return (True, CollapseReason.EXTREME_DISSONANCE)
|
|
265
|
+
|
|
266
|
+
# Check network decoupling
|
|
267
|
+
neighbors = list(G.neighbors(node))
|
|
268
|
+
if neighbors:
|
|
269
|
+
import math
|
|
270
|
+
|
|
271
|
+
neighbor_phases = [_get_node_attr(G, n, ALIAS_THETA) for n in neighbors]
|
|
272
|
+
mean_neighbor_phase = sum(neighbor_phases) / len(neighbor_phases)
|
|
273
|
+
phase_diff = abs(theta - mean_neighbor_phase)
|
|
274
|
+
phase_coupling = 1.0 - min(phase_diff, math.pi) / math.pi
|
|
275
|
+
|
|
276
|
+
if phase_coupling < min_coupling:
|
|
277
|
+
return (True, CollapseReason.NETWORK_DECOUPLING)
|
|
278
|
+
|
|
279
|
+
# Check EPI dissolution
|
|
280
|
+
if epi < min_epi:
|
|
281
|
+
return (True, CollapseReason.EPI_DISSOLUTION)
|
|
282
|
+
|
|
283
|
+
return (False, None)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def should_collapse(
|
|
287
|
+
G: TNFRGraph,
|
|
288
|
+
node: NodeId,
|
|
289
|
+
*,
|
|
290
|
+
config: dict[str, Any] | None = None,
|
|
291
|
+
) -> bool:
|
|
292
|
+
"""Check if node should collapse (simplified interface).
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
G : TNFRGraph
|
|
297
|
+
Graph containing the node
|
|
298
|
+
node : NodeId
|
|
299
|
+
Node to check
|
|
300
|
+
config : dict, optional
|
|
301
|
+
Configuration overrides
|
|
302
|
+
|
|
303
|
+
Returns
|
|
304
|
+
-------
|
|
305
|
+
bool
|
|
306
|
+
True if node meets collapse conditions
|
|
307
|
+
|
|
308
|
+
See Also
|
|
309
|
+
--------
|
|
310
|
+
check_collapse_conditions : Full collapse check with reason
|
|
311
|
+
get_lifecycle_state : Complete lifecycle state determination
|
|
312
|
+
"""
|
|
313
|
+
should_collapse_flag, _ = check_collapse_conditions(G, node, config=config)
|
|
314
|
+
return should_collapse_flag
|