TNFR Logo
TheoryLearnSoftwareResearch

On this page

TNFR

Resonant Fractal Nature Theory — a mathematical framework for coherent patterns on graph-coupled networks.

About
  • Project history
  • Editorial policy
  • Contact
Resources
  • GitHub
  • PyPI
  • DOI · Zenodo
Legal
  • MIT License
  • Citation
© 2026 TNFR project — MIT licensed.DOI 10.5281/zenodo.17602860
docs
grammar
PHYSICS_VERIFICATION.md
API_CONTRACTS.mdCANONICAL_OZ_SEQUENCES.mdEMPIRICAL_CONFRONTATION_EEG.mdREADME.mdSTRUCTURAL_FIELDS_TETRAD.mdSTRUCTURAL_INTERFACE_THEORY.md
theory
APPLIED_STRUCTURAL_ANALYSIS.mdCATALOG_TYPE_HYGIENE_PROGRAMME.mdDISSIPATIVE_AND_OPEN_SYSTEMS.mdEMERGENT_ONTOLOGY.mdEXTENDED_FIELDS_AND_DERIVED_QUANTITIES.mdFUNDAMENTAL_THEORY.mdGAUGE_SYMMETRY_AND_UNIFICATION.mdGLOSSARY.mdMATHEMATICAL_DYNAMICS_BASIS.mdMINIMAL_STRUCTURAL_DEGREES.mdNUCLEUS_A_PRIME_LADDER_ATLAS.mdNUCLEUS_B_EQUIVARIANCE_OBSTRUCTIONS.mdPHYSICAL_REGIME_CORRESPONDENCES.mdREADME.mdREMESH_INFINITY_DERIVATION.mdSTRUCTURAL_CONSERVATION_THEOREM.mdSTRUCTURAL_OPERATORS.mdSTRUCTURAL_STABILITY_AND_DYNAMICS.mdTNFR_BSD_RESEARCH_NOTES.mdTNFR_HODGE_RESEARCH_NOTES.mdTNFR_NAVIER_STOKES_RESEARCH_NOTES.mdTNFR_NUMBER_THEORY.mdTNFR_P_VS_NP_RESEARCH_NOTES.mdTNFR_RIEMANN_RESEARCH_NOTES.mdTNFR_VARIATIONAL_PRINCIPLE.mdTNFR_YANG_MILLS_RESEARCH_NOTES.mdTNFR.pdfUNIFIED_GRAMMAR_RULES.md
factorization-lab
analysis
analyze_patterns.pycertificate_manifest.py
benchmarks
benchmark_analysis.pybenchmark_expansion_suite.pyfull_spectrum_factorization.pypaley_gap_extended.pypaley_gap_smoke.pytest_benchmark_suite.py
demos
experiment_contexts
exp_0b1663cd19b7.jsonexp_0bf0054b7474.jsonexp_75a4c8ca616a.jsonexp_848ee0fd1857.jsonexp_f6fe00562193.jsonexp_fdf3da424e1e.json
failure_telemetry_batch.pyfeedback_integration_demo.pyintegration_demo_snapshots.dbseed_management_integration_demo.pysnapshot_integration_demo.pytrajectory_143.jsontrajectory_77.jsontrajectory_89.jsontrajectory_91.jsontrajectory_97.json
docs
FACTORING_PLAYBOOK.mdFALSE_POSITIVE_TEST_SUITE.mdOPERATOR_CERTIFICATES.mdROADMAP.mdSPECTRAL_ROUTE.md
experiment_contexts
exp_cebe1d9e7d8e.json
notebooks
spectral_history.ipynb
scripts
run_false_positive_tests.py
tests
run_false_positive_test_suite.pytest_cli.pytest_false_positive_methodology.pytest_false_positive_verifier.pytest_feedback_integration.pytest_partitioning.pytest_seed_management.pytest_self_opt_support.pytest_snapshot_system.pytest_spectral_paley.pytest_verification_robustness.py
tnfr_factorization
__init__.pyapi.pycli.pyfailure_telemetry.pyfeedback_adapter.pyfeedback_integration.pypartitioning.pyself_opt_support.pyspectral_paley.py
demo_snapshots.dbLICENSE_SNAPSHOT.mdPACKAGE_SUMMARY.mdREADME.mdseed_management.pysnapshot_system.pytest_certificate_hashing.pytest_installation.pyverification_trajectory_77.json
benchmarks
analyze_tetrad_universality.pyb0star_alpha_canonical_product_graphs.pybenchmark_optimization_tracks.pybenchmark_utils.pyboundary_vibration.pybridge_primes_riemann.pychiral_involution.pycli_utils.pycoherence_projector_sense_index.pycommutant_bridge.pycomposition_arithmetic.pyconfinement_zones_test.pyconservation_law_validation.pydirected_paley_bridge.pyemergent_arithmetic_pulse.pyemergent_atom_dynamics.pyemergent_atomic_shells.pyemergent_base_dimension.pyemergent_dimension_dynamics.pyemergent_fractal_pulse.pyemergent_fractal_simplex_dimension.pyemergent_integers_symmetry.pyemergent_musical_nfr.pyemergent_nfr_geometry.pyemergent_nfr_where.pyemergent_rationals.pyemergent_rhythm.pyemergent_screening.pyemergent_shell_cardinals.pyemergent_shell_ordering.pyemergent_simplex_dimension.pyemergent_substrate_symmetry.pyequivariance_wall.pyexternal_phase_gate_validation.pyfield_methods_battery.pygolden_residue_remesh_bridge.pyintegrated_force_regime_study.pyinverse_spectrum_to_symmetry.pyk_phi_safety_demo.pykuramoto_farey_bridge.pymissing_piece_bridge.pymultichannel_interface_benchmark.pynavier_stokes_recipe_bridge.pynodal_propagator_residue_bridge.pyns_moment_hierarchy_cascade.pyoperational_irreducibility.pypaley_bridge.pyphase_curvature_investigation.pyphase_wall.pyphi_s_confinement_investigation.pyprimes_as_consequence.pypulse_phase_coherence_budget.pyREADME.mdremesh_infinity_riemann_baseline.pyremesh_infinity_riemann_composed.pyremesh_infinity_riemann_modified_graph.pyremesh_infinity_riemann_operator.pyremesh_infinity_riemann_spectral_basis.pyremesh_infinity_riemann_spectral_robustness.pyremesh_infinity_riemann_spectral.pyresidue_phase_vs_riemann.pystructural_interface_benchmark.pytemporal_interface_benchmark.pytetrad_results_aggregate.pyu2_destabilization_irreversibility.pyuniversality_clusters.pyxi_c_fast_experiment.py
primality-test
benchmarks
comprehensive_benchmark.py
docs
ADVANCED_INTEGRATION.mdmathematical_foundation.mdperformance_analysis.md
examples
advanced_examples.pybasic_usage.py
tnfr_primality
__init__.py__main__.pyadvanced_cli.pyadvanced_core.pycli.pyconstants.pycore.pyoptimized.py
MANIFEST.inPACKAGE_SUMMARY.mdREADME.mdRELEASE_NOTES_v1.0.mdsetup.pytest_installation.py
tests
core_physics
__init__.pytest_conservation_laws.pytest_delta_nfr_computation_paths.pytest_delta_nfr.pytest_dispersion_coherence_sign_invariance.pytest_emergent_constants_guard.pytest_lyapunov_operators.pytest_nodal_equation.pytest_structural_triad.py
data
replay_manifests
sample_run
_manifest_summary.json_manifest.json_partition_files.txt.gz
self_opt_validation
seed_alpha
paley.json
seed_beta
integration.json
seed_gamma
unknown.json
self_optimization
test_run
partitioned
test_run
test_run_p0.jsontest_run_p1.json
_manifest_summary.json_manifest.json
engines
test_pattern_discovery_manifest.pytest_self_optimization_engine.py
mathematics
__init__.pytest_autodiff.pytest_backends.pytest_dissipative_dynamics.pytest_epi.pytest_factory_patterns.pytest_metrics.pytest_navier_stokes_refounded.pytest_number_theory_canonical.pytest_operators.pytest_residue_networks.pytest_riemann_nodal_pulse.pytest_riemann_pulse_coherence.pytest_spaces.pytest_transforms.pytest_validator.py
operators
test_canonical_operators_modern.pytest_grammar_canon.pytest_grammar_canonical_consistency.pytest_grammar_dynamics.pytest_operator_contracts.pytest_operator_strategies.py
parallel
test_fractal_partition_manifest.py
physics
test_conservation_gauge_unification.pytest_dissipative_conservation.pytest_emergent_chemistry.pytest_field_cache_invalidation.pytest_gauge.pytest_phase_transition.pytest_signatures.pytest_spectral_conservation.pytest_structural_diffusion.pytest_structural_integrity.pytest_symplectic_substrate.pytest_tetrad_bounds.pytest_variational.pytest_yang_mills_closure.pytest_yang_mills_derivability.pytest_yang_mills_scaling.pytest_yang_mills_structural_gap.pytest_yang_mills_u6_sweep.py
scripts
test_run_self_opt_validation.pytest_run_self_optimization.py
sdk
__init__.pytest_simple_advanced.py
__init__.pyconftest.pyREADME.mdtest_breast_cancer_phase_gate_demo.pytest_classical_mechanics.pytest_distributed_fft.pytest_external_phase_gate_validation.pytest_factorization_entrypoint.pytest_multichannel_interface.pytest_nodal_optimizer.pytest_phase_gate_api.pytest_replay_register_manifest.pytest_signal_confrontation.pytest_structural_interface_api.pytest_structural_interface_baselines.pytest_structural_interface_benchmark.pytest_temporal_interface.pytest_vectorized_coherence_length_regression.pytest_wine_quality_phase_gate_demo.pyutils.py
examples
01_foundations
01_hello_world.py02_musical_resonance.py03_network_formation.py04_operator_sequences.py05_coherence_evolution.py06_network_topologies.py07_phase_transitions.py08_emergent_phenomena.py09_visualization_suite.py10_simplified_sdk_showcase.py
02_physics_regimes
11_classical_limit_comparison.py115_operator_contract_audit.py12_classical_mechanics_demo.py13_quantum_mechanics_demo.py14_uncertainty_and_interference.py15_train_crossing_demo.py17_conservation_law_demo.py26_gauge_structure_demo.py27_variational_principle_demo.py28_dissipative_systems_demo.py29_lyapunov_stability_demo.py30_self_optimization_demo.py31_mathematical_constants_basis.py33_complex_field_unification.py34_conservation_protocol_suite.py35_tetrad_irreducibility.py36_grammar_violation_detector.py37_operator_tetrad_synergy.py38_grammar_energy_landscape.py39_nodal_equation_decomposition.py
03_riemann_zeta
157_nodal_pulse_phase_attack.py41_von_mangoldt_zeta_demo.py42_riemann_zeros_as_resonances.py43_prime_ladder_hamiltonian_demo.py44_weil_explicit_formula_demo.py45_li_keiper_demo.py46_weil_tnfr_positivity_demo.py47_alpha_sweep_demo.py48_admissible_family_sweep_demo.py49_nodeaware_gauge_sweep_demo.py50_uniform_coercivity_demo.py51_adaptive_coercivity_demo.py52_paley_gap_coercivity_demo.py53_lyapunov_spectral_positivity_demo.py54_hilbert_polya_demo.py55_structural_zero_density_demo.py56_spectral_emergence_demo.py57_admissible_rescaling_demo.py58_oscillatory_correction_demo.py
04_riemann_L_twisted
59_dirichlet_l_function_demo.py60_dirichlet_l_continuation_demo.py61_dirichlet_l_hamiltonian_demo.py62_dirichlet_weil_explicit_formula_demo.py63_dirichlet_li_keiper_demo.py64_twisted_weil_positivity_demo.py65_twisted_alpha_sweep_demo.py66_twisted_admissible_family_sweep_demo.py67_twisted_nodeaware_gauge_sweep_demo.py68_twisted_hermite_family_demo.py69_twisted_coercivity_uniform_demo.py70_twisted_paley_gap_coercivity_demo.py71_twisted_lyapunov_spectral_demo.py72_twisted_hilbert_polya_demo.py73_twisted_structural_zero_density_demo.py74_twisted_spectral_emergence_demo.py75_twisted_admissible_rescaling_demo.py76_twisted_oscillatory_correction_demo.py
05_type_hygiene
77_remesh_infinity_residue_split_demo.py78_nuf_type_signature_demo.py79_epi_type_signature_demo.py80_phi_type_signature_demo.py81_dnfr_type_signature_demo.py82_remesh_window_type_signature_demo.py83_delta_phi_max_type_signature_demo.py84_coupling_weights_type_signature_demo.py85_tetrad_closure_signature_demo.py86_currents_closure_signature_demo.py87_aggregates_closure_signature_demo.py88_urules_consistency_signature_demo.py89_operator_catalog_discipline_signature_demo.py
06_navier_stokes
158_navier_stokes_two_face_refounded.py
07_number_theory
100_prime_families_orbits.py101_numbers_as_coupled_network.py102_nodal_flow_primes_equilibria.py116_nuf_emergent_prime_visibility.py146_primality_grammatical_inertness.py147_numbers_as_free_monoid_words.py148_capacity_arm_carries_von_mangoldt.py149_p14_is_the_capacity_arm_operator.py153_structural_frequency_rank_cyclotomy.py40_arithmetic_number_theory.py94_generative_number_construction.py95_primes_from_spectral_waves.py96_spectral_vibration_of_coherence.py97_goldbach_additive_multiplicative.pyemergent_chemistry_particles_demo.py
08_emergent_geometry
103_emergent_substrate_meets_riemann.py106_per_node_polarization_geometry.py107_orthogonal_structure_emergent_geometry.py108_emergent_field_generating_structure.py112_structure_predicts_coherence_flow.py113_overdamped_projection_bridge.py114_substrate_conserved_quantities.py117_emergent_geometry_residue_graph.py118_emergent_vs_classical_operator.py119_phase_sector_directed_residue.py120_symmetry_wall_substrate_vs_spectrum.py121_canonical_symmetry_break_negative.py122_factorization_phase_sector.py123_symmetry_sector_decomposition.py124_emergent_metric_fractal_consistency.py125_node_is_the_emergent_substrate.py126_two_layers_base_fiber.py127_base_is_emergent_not_imposed.py128_base_substrate_coemergence.py129_spectral_gap_base_fiber_clock.py130_operators_break_substrate_charges.py131_coemergent_loop_convergence.py132_geometric_phase_holonomy.py133_psi_topological_defects.py134_spectral_dimension_heat_kernel.py135_arrow_of_time_h_theorem.py136_heat_kernel_coefficients.py137_synchronization_transition.py138_structure_frequency_synchronization.py139_grammar_formal_language.py140_grammar_automaton.py141_grammar_rule_decomposition.py142_grammar_operator_quotient.py143_glyphic_function_sublanguage.py144_branching_combinator.py145_syntactic_monoid_starfree.py150_emergent_grammatical_pattern_parry.py151_grammar_in_emergent_geometry.py152_operator_contract_tetrahedron.py154_conductor_annotated_qr_spectrum.py155_ontological_position_of_numbers.py156_emergence_directness_law.py98_emergent_symplectic_substrate.py99_structural_diffusion.pyunified_fields_showcase.py
09_millennium
109_p_vs_np_coherence_synthesis.py110_bsd_rank_structural_pressure.py111_hodge_discrete_and_honest_gap.py
10_applications
159_empirical_confrontation_pipeline.py90_phase_gate_monitor_demo.py91_breast_cancer_phase_gate_demo.py92_wine_quality_phase_gate_demo.py93_structural_interface_demo.pypytorch_cuda_demo.py
README.md
scripts
replay
__init__.pyregister_manifest.py
__init__.pyREADME.mdrebuild_failure_manifest.pyrun_reproducible_benchmarks.pyrun_self_opt_validation.pyrun_self_optimization.pytnfr_is_prime.pyvalidate_conservation_law.pyverify_internal_references.py
src
core
__init__.pyevaluation.py
tnfr
backends
__init__.pyjax_backend.pynumpy_backend.pyoptimized_numpy.pyREADME.mdtorch_backend.py
cli
__init__.py__init__.pyiarguments.pyarguments.pyiexecution.pyexecution.pyiinteractive_validator.pyREADME.mdutils.pyutils.pyi
compat
__init__.pydataclass.pyjsonschema_stub.pymatplotlib_stub.pynumpy_stub.pyREADME.md
config
__init__.py__init__.pyiconstants.pyconstants.pyidefaults_core.pydefaults_init.pydefaults_metric.pydefaults.pyfeature_flags.pyfeature_flags.pyiglyph_constants.pyoperator_names.pyoperator_names.pyiphysics_derivation.pyprecision_modes.pypresets.pypresets.pyiREADME.mdsecurity.pythresholds.pytnfr_config.py
constants
__init__.py__init__.pyialiases.pyaliases.pyicanonical.pymetric.pymetric.pyioperational.py
core
__init__.pycontainer.pydefault_implementations.pyexceptions.pyinterfaces.pyREADME.md
dynamics
__init__.py__init__.pyiadaptation.pyadaptation.pyiadaptive_sequences.pyadaptive_sequences.pyiadelic.pyadvanced_cache_optimizer.pyadvanced_fft_arithmetic.pyaliases.pyaliases.pyibifurcation.pycache_aware_fft_engine.pycanonical.pycanonical.pyicomputational_hub.pycoordination.pycoordination.pyidistributed_fft.pydnfr.pydnfr.pyidynamic_limits.pyemergent_centralization.pyemergent_integration_engine.pyfeedback.pyfeedback.pyifft_backend.pyfft_cache_coordinator.pyfft_dispatchers.pyfft_engine.pyfft_workers.pyfused_dnfr.pyhomeostasis.pyhomeostasis.pyiintegrators.pyintegrators.pyilearning.pylearning.pyimetabolism.pymulti_modal_cache.pynbody_tnfr.pynbody.pynodal_optimizer.pyoptimization_orchestrator.pypropagation.pyREADME.mdruntime.pyruntime.pyisampling.pysampling.pyiselectors.pyselectors.pyiself_optimizing_engine.pyspectral_structural_fusion.pystructural_cache.pystructural_clip.pysymplectic.pyunified_backend.pyunified_mathematical_cache_orchestrator.py
engines
computation
__init__.pyfft_engine.pyunified_fft_engine.pyunified_gpu_system.py
constants
__init__.pycanonical.pyoperational.py
integration
__init__.pyemergent_integration.py
pattern_discovery
__init__.pymathematical_patterns.pymulti_modal_cache.py
self_optimization
__init__.pyengine.py
__init__.pyREADME.md
errors
__init__.pycontextual.py
factorization
__init__.py
flatten
README.md
gamma
README.md
glyph_history
README.md
glyph_runtime
README.md
immutable
README.md
initialization
README.md
io
README.md
math
__init__.pyfields_symbolic.pygrammar_validators.pyoptimizer.pyREADME.mdsymbolic.py
mathematics
__init__.pybackend.pybackend.pyidynamics.pydynamics.pyiepi.pyepi.pyigenerators.pygenerators.pyiliouville.pymetrics.pymetrics.pyinumber_theory.pyoperators_factory.pyoperators_factory.pyioperators.pyoperators.pyioptimized_primality.pyprojection.pyprojection.pyiREADME.mdruntime.pyruntime.pyispaces.pyspaces.pyispectral.pytransforms.pytransforms.pyiunified_cache.pyunified_numerical.pyzeta.py
metrics
__init__.py__init__.pyibuffer_cache.pybuffer_cache.pyicache_utils.pycoherence.pycoherence.pyicommon.pycommon.pyicore.pycore.pyidiagnosis.pydiagnosis.pyiemergence.pyexport.pyexport.pyiglyph_timing.pyglyph_timing.pyilearning_metrics.pylearning_metrics.pyilocal_coherence.pyphase_coherence.pyphase_compatibility.pyREADME.mdreporting.pyreporting.pyisense_index.pysense_index.pyitelemetry.pytetrad.pytrig_cache.pytrig_cache.pyitrig.pytrig.pyi
multiscale
__init__.pyhierarchical.pyREADME.md
navier_stokes
__init__.pyconservative_face.pyoperator.py
node
README.md
observers
README.md
operators
network_analysis
__init__.pysource_detection.py
postconditions
__init__.pymutation.py
preconditions
__init__.pycoherence.pydissonance.pyemission.pymutation.pyreception.pyresonance.py
strategies
__init__.pydefaults.pygpu_strategies.pystrategy.py
__init__.py__init__.pyialgebra.pycanonical_patterns.pycascade.pycoherence.pycontraction.pycoupling.pycycle_detection.pydefinitions_base.pydefinitions.pydefinitions.pyidissonance.pyemission.pyexpansion.pygrammar_application.pygrammar_canon.pygrammar_context.pygrammar_core.pygrammar_dynamics.pygrammar_error_factory.pygrammar_memoization.pygrammar_patterns.pygrammar_telemetry.pygrammar_types.pygrammar_u6.pygrammar_validate.pygrammar.pygrammar.pyihamiltonian.pyhealth_analyzer.pyintrospection.pyjitter.pyjitter.pyilifecycle.pymetabolism.pymetrics_basic.pymetrics_core.pymetrics_network.pymetrics_structural.pymetrics_u6.pymetrics.pymutation.pynodal_equation.pyoperator_contracts.pypattern_detection.pypatterns.pyREADME.mdreception.pyrecursivity.pyregistry.pyregistry.pyiremesh.pyremesh.pyiresonance.pyself_organization.pysilence.pystructural_units.pytransition.py
parallel
__init__.pyauto_scaler.pydistributed.pyengine.pymonitoring.pypartitioner.pyREADME.md
performance
guardrails.py
physics
__init__.py_helpers.pycalibration.pycanonical.pycell.pyclassical_mechanics.pyconservation_gauge_unification.pyconservation.pydissipative_conservation.pyemergent_chemistry.pyemergent_particles.pyextended.pyfields.pygauge.pyintegrity.pyinteractions.pylife.pylyapunov.pypatterns.pyphase_transition.pyquantum_mechanics.pyREADME.mdsignatures.pyspectral_conservation.pyspectral_metrics.pystructural_diffusion.pysymplectic_substrate.pytelemetry.pyunified.pyvariational.pyvectorized_ops.py
primality
__init__.py
recipes
__init__.pycookbook.pyREADME.md
riemann
__init__.pyadmissible_family_sweep.pyadmissible_rescaling.pyaggregates_closure_signature.pyalpha_sweep.pyanalytic_continuation_dirichlet.pyanalytic_continuation.pycoercivity_uniform.pycoupling_weights_type_signature.pycurrents_closure_signature.pydelta_phi_max_type_signature.pydirichlet_l.pydnfr_type_signature.pyepi_type_signature.pyhilbert_polya.pyli_keiper.pylyapunov_spectral_positivity.pynodal_pulse.pynodeaware_gauge_sweep.pynuf_type_signature.pyoperator_catalog_discipline_signature.pyoperator.pyoscillatory_correction.pypaley_gap_coercivity.pyphi_type_signature.pyprime_ladder_hamiltonian.pypulse_coherence.pyremesh_infinity_residue_split.pyremesh_window_type_signature.pyspectral_emergence.pystructural_zero_density.pytelemetry.pytetrad_closure_signature.pytwisted_admissible_family_sweep.pytwisted_admissible_rescaling.pytwisted_alpha_sweep.pytwisted_coercivity_uniform.pytwisted_hermite_family.pytwisted_hilbert_polya.pytwisted_li_keiper.pytwisted_lyapunov_spectral_positivity.pytwisted_nodeaware_gauge_sweep.pytwisted_oscillatory_correction.pytwisted_paley_gap_coercivity.pytwisted_prime_ladder_hamiltonian.pytwisted_spectral_emergence.pytwisted_structural_zero_density.pytwisted_weil_explicit_formula.pytwisted_weil_positivity.pyurules_consistency_signature.pyvon_mangoldt.pyweil_explicit_formula.pyweil_positivity.py
schemas
__init__.pygrammar.jsonREADME.md
sdk
__init__.py__init__.pyiadaptive_system.pyadaptive_system.pyibuilders.pybuilders.pyifluent.pyfluent.pyiREADME.mdself_opt.pysimple.pytemplates.pytemplates.pyiutils.py
security
__init__.pycrypto.pydatabase.pyREADME.mdsubprocess.pyvalidation.py
sequencing
__init__.pypatterns.pyREADME.md
services
__init__.pyorchestrator.pyREADME.md
sparse
__init__.pyREADME.mdrepresentations.py
structural
README.md
telemetry
__init__.pycache_metrics.pycache_metrics.pyiconstants.pynu_f.pynu_f.pyiREADME.mdunified_telemetry_system.pyverbosity.pyverbosity.pyi
tools
__init__.pydomain_templates.pyREADME.mdsequence_generator.pytnfr_is_prime_cli_optimized.pytnfr_is_prime_cli.py
topology
__init__.pyasymmetry.pyREADME.md
utils
cache_layers.pycache.pycache.pyicallbacks.pycallbacks.pyichunks.pychunks.pyidata.pydata.pyifast_diameter.pygraph.pygraph.pyiinit.pyinit.pyiio.pyio.pyinumeric.pynumeric.pyiREADME.mdtopology.pyunified_cache.py
validation
__init__.py__init__.pyiaggregator.pybase.pycompatibility.pycompatibility.pyiconfig.pygraph.pygraph.pyihealth.pyinput_validation.pyinterface_baselines.pyinvariants.pymultichannel_interface.pyphase_gate.pyREADME.mdrules.pyrules.pyiruntime.pyruntime.pyisequence_validator.pysignal_confrontation.pysoft_filters.pysoft_filters.pyispectral.pyspectral.pyistructural_interface.pytemporal_interface.pyunified_validation_system.pyvalidator.pywindow.pywindow.pyi
visualization
__init__.pycascade_viz.pyhierarchy.pyREADME.mdsequence_plotter.py
yang_mills
__init__.pyclosure.pyderivability.pyscaling.pystructural_gap.pyu6_sweep.py
__init__.py__init__.pyi_compat.py_version.py_version.pyialias.pyalias.pyibackend_config.pycache.pycache.pyiexecution.pyexecution.pyiflatten.pyflatten.pyigamma.pygamma.pyiglyph_history.pyglyph_history.pyiglyph_runtime.pyglyph_runtime.pyiimmutable.pyimmutable.pyiinitialization.pyinitialization.pyiio.pyio.pyilocking.pylocking.pyinode.pynode.pyiobservers.pyobservers.pyiontosim.pyontosim.pyipy.typedrng.pyrng.pyisecure_config.pyselector.pyselector.pyisense.pysense.pyistructural.pystructural.pyitokens.pytokens.pyitrace.pytrace.pyitypes.pytypes.pyiunits.pyunits.pyi
tetrad_evaluator.py
.pre-commit-config.yaml.semgrep.yaml.zenodo.jsonARCHITECTURE.mdbandit.yamlCHANGELOG.mdCITATION.cffCONTRIBUTING.mdEMERGENT_CANON_AUDIT.mdEMERGENT_DERIVATION_PLAN.mdLICENSE.mdMakefileMANIFEST.inpyproject.tomlpyrightconfig.jsonPYTORCH_CUDA_INTEGRATION.mdREADME.mdSECURITY.mdTESTING.mdTNFR_Website_Content_Brief.md
FILE: src/tnfr/metrics/diagnosis.py

diagnosis.py

Diagnostic metrics.

Source Code

python
"""Diagnostic metrics."""

from __future__ import annotations

import math
from collections import deque
from collections.abc import Mapping, MutableMapping, Sequence
from concurrent.futures import ProcessPoolExecutor
from dataclasses import dataclass
from functools import partial
from operator import ge, le
from statistics import StatisticsError, fmean
from typing import Any, Callable, Iterable, cast

from ..alias import get_attr
from ..constants import (
    STATE_DISSONANT,
    STATE_STABLE,
    STATE_TRANSITION,
    VF_KEY,
    get_param,
    normalise_state_token,
)
from ..constants.aliases import ALIAS_DNFR, ALIAS_EPI, ALIAS_SI, ALIAS_VF
from ..glyph_history import append_metric, ensure_history
from ..mathematics.unified_numerical import np
from ..types import (
    DiagnosisNodeData,
    DiagnosisPayload,
    DiagnosisPayloadChunk,
    DiagnosisResult,
    DiagnosisResultList,
    DiagnosisSharedState,
    NodeId,
    TNFRGraph,
)
from ..utils import (
    CallbackEvent,
    callback_manager,
    clamp01,
    resolve_chunk_size,
    similarity_abs,
)
from .coherence import CoherenceMatrixPayload, coherence_matrix, local_phase_sync
from .common import _coerce_jobs, compute_dnfr_accel_max, min_max_range, normalize_dnfr
from .trig_cache import compute_theta_trig, get_trig_cache

# ---------------------------------------------------------------------------
# Dissonance detection thresholds
# ---------------------------------------------------------------------------
_DISSONANCE_START_DNFR = 0.5
_DISSONANCE_START_PHASE_SYNC = 0.4
_DISSONANCE_END_DNFR = 0.2
_DISSONANCE_END_PHASE_SYNC = 0.7

CoherenceSeries = Sequence[CoherenceMatrixPayload | None]
CoherenceHistory = Mapping[str, CoherenceSeries]


def _coherence_matrix_to_numpy(
    weight_matrix: Any,
    size: int,
) -> Any:
    """Convert stored coherence weights into a dense NumPy array."""

    if weight_matrix is None or np is None or size <= 0:
        return None

    ndarray_type: Any = getattr(np, "ndarray", tuple())
    if ndarray_type and isinstance(weight_matrix, ndarray_type):
        matrix = weight_matrix.astype(float, copy=True)
    elif isinstance(weight_matrix, (list, tuple)):
        weight_seq = list(weight_matrix)
        if not weight_seq:
            matrix = np.zeros((size, size), dtype=float)
        else:
            first = weight_seq[0]
            if isinstance(first, (list, tuple)) and len(first) == size:
                matrix = np.array(weight_seq, dtype=float)
            elif (
                isinstance(first, (list, tuple))
                and len(first) == 3
                and not isinstance(first[0], (list, tuple))
            ):
                matrix = np.zeros((size, size), dtype=float)
                for i, j, weight in weight_seq:
                    matrix[int(i), int(j)] = float(weight)
            else:
                return None
    else:
        return None

    if matrix.shape != (size, size):
        return None
    np.fill_diagonal(matrix, 0.0)
    return matrix


def _weighted_phase_sync_vectorized(
    matrix: Any,
    cos_vals: Any,
    sin_vals: Any,
) -> Any:
    """Vectorised computation of weighted local phase synchrony."""

    denom = np.sum(matrix, axis=1)
    if np.all(denom == 0.0):
        return np.zeros_like(denom, dtype=float)
    real = matrix @ cos_vals
    imag = matrix @ sin_vals
    magnitude = np.hypot(real, imag)
    safe_denom = np.where(denom == 0.0, 1.0, denom)
    return magnitude / safe_denom


def _unweighted_phase_sync_vectorized(
    nodes: Sequence[Any],
    neighbors_map: Mapping[Any, tuple[Any, ...]],
    cos_arr: Any,
    sin_arr: Any,
    index_map: Mapping[Any, int],
) -> list[float]:
    """Compute unweighted phase synchrony using NumPy helpers."""

    results: list[float] = []
    for node in nodes:
        neighbors = neighbors_map.get(node, ())
        if not neighbors:
            results.append(0.0)
            continue
        indices = [index_map[nb] for nb in neighbors if nb in index_map]
        if not indices:
            results.append(0.0)
            continue
        cos_vals = np.take(cos_arr, indices)
        sin_vals = np.take(sin_arr, indices)
        real = np.sum(cos_vals)
        imag = np.sum(sin_vals)
        denom = float(len(indices))
        if denom == 0.0:
            results.append(0.0)
        else:
            results.append(float(np.hypot(real, imag) / denom))
    return results


def _neighbor_means_vectorized(
    nodes: Sequence[Any],
    neighbors_map: Mapping[Any, tuple[Any, ...]],
    epi_arr: Any,
    index_map: Mapping[Any, int],
) -> list[float | None]:
    """Vectorized helper to compute neighbour EPI means."""

    results: list[float | None] = []
    for node in nodes:
        neighbors = neighbors_map.get(node, ())
        if not neighbors:
            results.append(None)
            continue
        indices = [index_map[nb] for nb in neighbors if nb in index_map]
        if not indices:
            results.append(None)
            continue
        values = np.take(epi_arr, indices)
        results.append(float(np.mean(values)))
    return results


@dataclass(frozen=True)
class RLocalWorkerArgs:
    """Typed payload passed to :func:`_rlocal_worker`."""

    chunk: Sequence[Any]
    coherence_nodes: Sequence[Any]
    weight_matrix: Any
    weight_index: Mapping[Any, int]
    neighbors_map: Mapping[Any, tuple[Any, ...]]
    cos_map: Mapping[Any, float]
    sin_map: Mapping[Any, float]


@dataclass(frozen=True)
class NeighborMeanWorkerArgs:
    """Typed payload passed to :func:`_neighbor_mean_worker`."""

    chunk: Sequence[Any]
    neighbors_map: Mapping[Any, tuple[Any, ...]]
    epi_map: Mapping[Any, float]


def _rlocal_worker(args: RLocalWorkerArgs) -> list[float]:
    """Worker used to compute ``R_local`` in Python fallbacks."""

    results: list[float] = []
    for node in args.chunk:
        if args.coherence_nodes and args.weight_matrix is not None:
            idx = args.weight_index.get(node)
            if idx is None:
                rloc = 0.0
            else:
                rloc = _weighted_phase_sync_from_matrix(
                    idx,
                    node,
                    args.coherence_nodes,
                    args.weight_matrix,
                    args.cos_map,
                    args.sin_map,
                )
        else:
            rloc = _local_phase_sync_unweighted(
                args.neighbors_map.get(node, ()),
                args.cos_map,
                args.sin_map,
            )
        results.append(float(rloc))
    return results


def _neighbor_mean_worker(args: NeighborMeanWorkerArgs) -> list[float | None]:
    """Worker used to compute neighbour EPI means in Python mode."""

    results: list[float | None] = []
    for node in args.chunk:
        neighbors = args.neighbors_map.get(node, ())
        if not neighbors:
            results.append(None)
            continue
        try:
            results.append(fmean(args.epi_map[nb] for nb in neighbors))
        except StatisticsError:
            results.append(None)
    return results


def _weighted_phase_sync_from_matrix(
    node_index: int,
    node: Any,
    nodes_order: Sequence[Any],
    matrix: Any,
    cos_map: Mapping[Any, float],
    sin_map: Mapping[Any, float],
) -> float:
    """Compute weighted phase synchrony using a cached matrix."""

    if matrix is None or not nodes_order:
        return 0.0

    num = 0.0 + 0.0j
    den = 0.0

    if isinstance(matrix, list) and matrix and isinstance(matrix[0], list):
        row = matrix[node_index]
        for weight, neighbor in zip(row, nodes_order):
            if neighbor == node:
                continue
            w = float(weight)
            if w == 0.0:
                continue
            cos_j = cos_map.get(neighbor)
            sin_j = sin_map.get(neighbor)
            if cos_j is None or sin_j is None:
                continue
            den += w
            num += w * complex(cos_j, sin_j)
    else:
        for ii, jj, weight in matrix:
            if ii != node_index:
                continue
            neighbor = nodes_order[jj]
            if neighbor == node:
                continue
            w = float(weight)
            if w == 0.0:
                continue
            cos_j = cos_map.get(neighbor)
            sin_j = sin_map.get(neighbor)
            if cos_j is None or sin_j is None:
                continue
            den += w
            num += w * complex(cos_j, sin_j)

    return abs(num / den) if den else 0.0


def _local_phase_sync_unweighted(
    neighbors: Iterable[Any],
    cos_map: Mapping[Any, float],
    sin_map: Mapping[Any, float],
) -> float:
    """Fallback unweighted phase synchrony based on neighbours."""

    num = 0.0 + 0.0j
    den = 0.0
    for neighbor in neighbors:
        cos_j = cos_map.get(neighbor)
        sin_j = sin_map.get(neighbor)
        if cos_j is None or sin_j is None:
            continue
        num += complex(cos_j, sin_j)
        den += 1.0
    return abs(num / den) if den else 0.0


def _state_from_thresholds(
    Rloc: float,
    dnfr_n: float,
    cfg: Mapping[str, Any],
) -> str:
    stb = cfg.get("stable", {"Rloc_hi": 0.8, "dnfr_lo": 0.2, "persist": 3})
    dsr = cfg.get("dissonance", {"Rloc_lo": 0.4, "dnfr_hi": 0.5, "persist": 3})

    stable_checks = {
        "Rloc": (Rloc, float(stb["Rloc_hi"]), ge),
        "dnfr": (dnfr_n, float(stb["dnfr_lo"]), le),
    }
    if all(comp(val, thr) for val, thr, comp in stable_checks.values()):
        return STATE_STABLE

    dissonant_checks = {
        "Rloc": (Rloc, float(dsr["Rloc_lo"]), le),
        "dnfr": (dnfr_n, float(dsr["dnfr_hi"]), ge),
    }
    if all(comp(val, thr) for val, thr, comp in dissonant_checks.values()):
        return STATE_DISSONANT

    return STATE_TRANSITION


def _recommendation(state: str, cfg: Mapping[str, Any]) -> list[Any]:
    adv = cfg.get("advice", {})
    canonical_state = normalise_state_token(state)
    return list(adv.get(canonical_state, []))


def _get_last_weights(
    G: TNFRGraph,
    hist: CoherenceHistory,
) -> tuple[CoherenceMatrixPayload | None, CoherenceMatrixPayload | None]:
    """Return last Wi and Wm matrices from history."""
    CfgW = get_param(G, "COHERENCE")
    Wkey = CfgW.get("Wi_history_key", "W_i")
    Wm_key = CfgW.get("history_key", "W_sparse")
    Wi_series = hist.get(Wkey, [])
    Wm_series = hist.get(Wm_key, [])
    Wi_last = Wi_series[-1] if Wi_series else None
    Wm_last = Wm_series[-1] if Wm_series else None
    return Wi_last, Wm_last


def _node_diagnostics(
    node_data: DiagnosisNodeData,
    shared: DiagnosisSharedState,
) -> DiagnosisResult:
    """Compute diagnostic payload for a single node."""

    dcfg = shared["dcfg"]
    compute_symmetry = shared["compute_symmetry"]
    epi_min = shared["epi_min"]
    epi_max = shared["epi_max"]

    node = node_data["node"]
    Si = clamp01(float(node_data["Si"]))
    EPI = float(node_data["EPI"])
    vf = float(node_data["VF"])
    dnfr_n = clamp01(float(node_data["dnfr_norm"]))
    Rloc = float(node_data["R_local"])

    if compute_symmetry:
        epi_bar = node_data.get("neighbor_epi_mean")
        symm = (
            1.0 if epi_bar is None else similarity_abs(EPI, epi_bar, epi_min, epi_max)
        )
    else:
        symm = None

    state = _state_from_thresholds(Rloc, dnfr_n, dcfg)
    canonical_state = normalise_state_token(state)

    alerts = []
    if canonical_state == STATE_DISSONANT and dnfr_n >= shared["dissonance_hi"]:
        alerts.append("high structural tension")

    advice = _recommendation(canonical_state, dcfg)

    payload: DiagnosisPayload = {
        "node": node,
        "Si": Si,
        "EPI": EPI,
        VF_KEY: vf,
        "dnfr_norm": dnfr_n,
        "W_i": node_data.get("W_i"),
        "R_local": Rloc,
        "symmetry": symm,
        "state": canonical_state,
        "advice": advice,
        "alerts": alerts,
    }

    return node, payload


def _diagnosis_worker_chunk(
    chunk: DiagnosisPayloadChunk,
    shared: DiagnosisSharedState,
) -> DiagnosisResultList:
    """Evaluate diagnostics for a chunk of nodes."""

    return [_node_diagnostics(item, shared) for item in chunk]


def _diagnosis_step(
    G: TNFRGraph,
    ctx: DiagnosisSharedState | None = None,
    *,
    n_jobs: int | None = None,
) -> None:
    del ctx

    if n_jobs is None:
        n_jobs = _coerce_jobs(G.graph.get("DIAGNOSIS_N_JOBS"))
    else:
        n_jobs = _coerce_jobs(n_jobs)

    dcfg = get_param(G, "DIAGNOSIS")
    if not dcfg.get("enabled", True):
        return

    hist = ensure_history(G)
    coherence_hist = cast(CoherenceHistory, hist)
    key = dcfg.get("history_key", "nodal_diag")

    existing_diag_history = hist.get(key, [])
    if isinstance(existing_diag_history, deque):
        snapshots = list(existing_diag_history)
    elif isinstance(existing_diag_history, list):
        snapshots = existing_diag_history
    else:
        snapshots = []

    for snapshot in snapshots:
        if not isinstance(snapshot, Mapping):
            continue
        for node, payload in snapshot.items():
            if not isinstance(payload, Mapping):
                continue
            state_value = payload.get("state")
            if not isinstance(state_value, str):
                continue
            canonical = normalise_state_token(state_value)
            if canonical == state_value:
                continue
            if isinstance(payload, MutableMapping):
                payload["state"] = canonical
            elif isinstance(snapshot, MutableMapping):
                new_payload = dict(payload)
                new_payload["state"] = canonical
                snapshot[node] = new_payload

    norms = compute_dnfr_accel_max(G)
    G.graph["_sel_norms"] = norms
    dnfr_max = float(norms.get("dnfr_max", 1.0)) or 1.0

    nodes_data: list[tuple[NodeId, dict[str, Any]]] = list(G.nodes(data=True))
    nodes: list[NodeId] = [n for n, _ in nodes_data]

    Wi_last, Wm_last = _get_last_weights(G, coherence_hist)

    supports_vector = bool(
        np is not None
        and all(
            hasattr(np, attr)
            for attr in (
                "fromiter",
                "clip",
                "abs",
                "maximum",
                "minimum",
                "array",
                "zeros",
                "zeros_like",
                "sum",
                "hypot",
                "where",
                "take",
                "mean",
                "fill_diagonal",
                "all",
            )
        )
    )

    if not nodes:
        append_metric(hist, key, {})
        return

    rloc_values: list[float]

    if supports_vector:
        epi_arr = np.fromiter(
            (cast(float, get_attr(nd, ALIAS_EPI, 0.0)) for _, nd in nodes_data),
            dtype=float,
            count=len(nodes_data),
        )
        epi_min = float(np.min(epi_arr))
        epi_max = float(np.max(epi_arr))
        epi_vals = epi_arr.tolist()

        si_arr = np.clip(
            np.fromiter(
                (cast(float, get_attr(nd, ALIAS_SI, 0.0)) for _, nd in nodes_data),
                dtype=float,
                count=len(nodes_data),
            ),
            0.0,
            1.0,
        )
        si_vals = si_arr.tolist()

        vf_arr = np.fromiter(
            (cast(float, get_attr(nd, ALIAS_VF, 0.0)) for _, nd in nodes_data),
            dtype=float,
            count=len(nodes_data),
        )
        vf_vals = vf_arr.tolist()

        if dnfr_max > 0:
            dnfr_arr = np.clip(
                np.fromiter(
                    (
                        abs(cast(float, get_attr(nd, ALIAS_DNFR, 0.0)))
                        for _, nd in nodes_data
                    ),
                    dtype=float,
                    count=len(nodes_data),
                )
                / dnfr_max,
                0.0,
                1.0,
            )
            dnfr_norms = dnfr_arr.tolist()
        else:
            dnfr_norms = [0.0] * len(nodes)
    else:
        epi_vals = [cast(float, get_attr(nd, ALIAS_EPI, 0.0)) for _, nd in nodes_data]
        epi_min, epi_max = min_max_range(epi_vals, default=(0.0, 1.0))
        si_vals = [clamp01(get_attr(nd, ALIAS_SI, 0.0)) for _, nd in nodes_data]
        vf_vals = [cast(float, get_attr(nd, ALIAS_VF, 0.0)) for _, nd in nodes_data]
        dnfr_norms = [
            normalize_dnfr(nd, dnfr_max) if dnfr_max > 0 else 0.0
            for _, nd in nodes_data
        ]

    epi_map = {node: epi_vals[idx] for idx, node in enumerate(nodes)}

    trig_cache = get_trig_cache(G)
    trig_local = compute_theta_trig(nodes_data)
    cos_map = dict(trig_cache.cos)
    sin_map = dict(trig_cache.sin)
    cos_map.update(trig_local.cos)
    sin_map.update(trig_local.sin)

    neighbors_map = {n: tuple(G.neighbors(n)) for n in nodes}

    if Wm_last is None:
        coherence_nodes, weight_matrix = coherence_matrix(G)
        if coherence_nodes is None:
            coherence_nodes = []
            weight_matrix = None
    else:
        coherence_nodes = list(nodes)
        weight_matrix = Wm_last

    coherence_nodes = list(coherence_nodes)
    weight_index = {node: idx for idx, node in enumerate(coherence_nodes)}

    node_index_map: dict[Any, int] | None = None

    if supports_vector:
        size = len(coherence_nodes)
        matrix_np = _coherence_matrix_to_numpy(weight_matrix, size) if size else None
        if matrix_np is not None and size:
            cos_weight = np.fromiter(
                (float(cos_map.get(node, 0.0)) for node in coherence_nodes),
                dtype=float,
                count=size,
            )
            sin_weight = np.fromiter(
                (float(sin_map.get(node, 0.0)) for node in coherence_nodes),
                dtype=float,
                count=size,
            )
            weighted_sync = _weighted_phase_sync_vectorized(
                matrix_np,
                cos_weight,
                sin_weight,
            )
            rloc_map = {
                coherence_nodes[idx]: float(weighted_sync[idx]) for idx in range(size)
            }
        else:
            rloc_map = {}

        node_index_map = {node: idx for idx, node in enumerate(nodes)}
        if not rloc_map:
            cos_arr = np.fromiter(
                (float(cos_map.get(node, 0.0)) for node in nodes),
                dtype=float,
                count=len(nodes),
            )
            sin_arr = np.fromiter(
                (float(sin_map.get(node, 0.0)) for node in nodes),
                dtype=float,
                count=len(nodes),
            )
            rloc_values = _unweighted_phase_sync_vectorized(
                nodes,
                neighbors_map,
                cos_arr,
                sin_arr,
                node_index_map,
            )
        else:
            rloc_values = [rloc_map.get(node, 0.0) for node in nodes]
    else:
        if n_jobs and n_jobs > 1 and len(nodes) > 1:
            approx_chunk = math.ceil(len(nodes) / n_jobs) if n_jobs else None
            chunk_size = resolve_chunk_size(
                approx_chunk,
                len(nodes),
                minimum=1,
            )
            rloc_values = []
            with ProcessPoolExecutor(max_workers=n_jobs) as executor:
                futures = [
                    executor.submit(
                        _rlocal_worker,
                        RLocalWorkerArgs(
                            chunk=nodes[idx : idx + chunk_size],
                            coherence_nodes=coherence_nodes,
                            weight_matrix=weight_matrix,
                            weight_index=weight_index,
                            neighbors_map=neighbors_map,
                            cos_map=cos_map,
                            sin_map=sin_map,
                        ),
                    )
                    for idx in range(0, len(nodes), chunk_size)
                ]
                for fut in futures:
                    rloc_values.extend(fut.result())
        else:
            rloc_values = _rlocal_worker(
                RLocalWorkerArgs(
                    chunk=nodes,
                    coherence_nodes=coherence_nodes,
                    weight_matrix=weight_matrix,
                    weight_index=weight_index,
                    neighbors_map=neighbors_map,
                    cos_map=cos_map,
                    sin_map=sin_map,
                )
            )

    if isinstance(Wi_last, (list, tuple)) and Wi_last:
        wi_values = [
            Wi_last[i] if i < len(Wi_last) else None for i in range(len(nodes))
        ]
    else:
        wi_values = [None] * len(nodes)

    compute_symmetry = bool(dcfg.get("compute_symmetry", True))
    neighbor_means: list[float | None]
    if compute_symmetry:
        if supports_vector and node_index_map is not None and len(nodes):
            neighbor_means = _neighbor_means_vectorized(
                nodes,
                neighbors_map,
                epi_arr,
                node_index_map,
            )
        elif n_jobs and n_jobs > 1 and len(nodes) > 1:
            approx_chunk = math.ceil(len(nodes) / n_jobs) if n_jobs else None
            chunk_size = resolve_chunk_size(
                approx_chunk,
                len(nodes),
                minimum=1,
            )
            neighbor_means = cast(list[float | None], [])
            with ProcessPoolExecutor(max_workers=n_jobs) as executor:
                submit = cast(Callable[..., Any], executor.submit)
                futures = [
                    submit(
                        cast(
                            Callable[[NeighborMeanWorkerArgs], list[float | None]],
                            _neighbor_mean_worker,
                        ),
                        NeighborMeanWorkerArgs(
                            chunk=nodes[idx : idx + chunk_size],
                            neighbors_map=neighbors_map,
                            epi_map=epi_map,
                        ),
                    )
                    for idx in range(0, len(nodes), chunk_size)
                ]
                for fut in futures:
                    neighbor_means.extend(cast(list[float | None], fut.result()))
        else:
            neighbor_means = _neighbor_mean_worker(
                NeighborMeanWorkerArgs(
                    chunk=nodes,
                    neighbors_map=neighbors_map,
                    epi_map=epi_map,
                )
            )
    else:
        neighbor_means = [None] * len(nodes)

    node_payload: DiagnosisPayloadChunk = []
    for idx, node in enumerate(nodes):
        node_payload.append(
            {
                "node": node,
                "Si": si_vals[idx],
                "EPI": epi_vals[idx],
                "VF": vf_vals[idx],
                "dnfr_norm": dnfr_norms[idx],
                "R_local": rloc_values[idx],
                "W_i": wi_values[idx],
                "neighbor_epi_mean": neighbor_means[idx],
            }
        )

    shared = {
        "dcfg": dcfg,
        "compute_symmetry": compute_symmetry,
        "epi_min": float(epi_min),
        "epi_max": float(epi_max),
        "dissonance_hi": float(dcfg.get("dissonance", {}).get("dnfr_hi", 0.5)),
    }

    if n_jobs and n_jobs > 1 and len(node_payload) > 1:
        approx_chunk = math.ceil(len(node_payload) / n_jobs) if n_jobs else None
        chunk_size = resolve_chunk_size(
            approx_chunk,
            len(node_payload),
            minimum=1,
        )
        diag_pairs: DiagnosisResultList = []
        with ProcessPoolExecutor(max_workers=n_jobs) as executor:
            submit = cast(Callable[..., Any], executor.submit)
            futures = [
                submit(
                    cast(
                        Callable[
                            [list[dict[str, Any]], dict[str, Any]],
                            list[tuple[Any, dict[str, Any]]],
                        ],
                        _diagnosis_worker_chunk,
                    ),
                    node_payload[idx : idx + chunk_size],
                    shared,
                )
                for idx in range(0, len(node_payload), chunk_size)
            ]
            for fut in futures:
                diag_pairs.extend(cast(DiagnosisResultList, fut.result()))
    else:
        diag_pairs = [_node_diagnostics(item, shared) for item in node_payload]

    diag_map = dict(diag_pairs)
    diag: dict[NodeId, DiagnosisPayload] = {
        node: diag_map.get(node, {}) for node in nodes
    }

    append_metric(hist, key, diag)


def dissonance_events(G: TNFRGraph, ctx: DiagnosisSharedState | None = None) -> None:
    """Emit per-node structural dissonance start/end events.

    Events are recorded as ``"dissonance_start"`` and ``"dissonance_end"``.
    """

    del ctx

    hist = ensure_history(G)
    # Dissonance events are recorded in ``history['events']``
    norms = G.graph.get("_sel_norms", {})
    dnfr_max = float(norms.get("dnfr_max", 1.0)) or 1.0
    step_idx = len(hist.get("C_steps", []))
    nodes: list[NodeId] = list(G.nodes())
    for n in nodes:
        nd = G.nodes[n]
        dn = normalize_dnfr(nd, dnfr_max)
        Rloc = local_phase_sync(G, n)
        st = bool(nd.get("_disr_state", False))
        if (
            (not st)
            and dn >= _DISSONANCE_START_DNFR
            and Rloc <= _DISSONANCE_START_PHASE_SYNC
        ):
            nd["_disr_state"] = True
            append_metric(
                hist,
                "events",
                ("dissonance_start", {"node": n, "step": step_idx}),
            )
        elif st and dn <= _DISSONANCE_END_DNFR and Rloc >= _DISSONANCE_END_PHASE_SYNC:
            nd["_disr_state"] = False
            append_metric(
                hist,
                "events",
                ("dissonance_end", {"node": n, "step": step_idx}),
            )


def register_diagnosis_callbacks(G: TNFRGraph) -> None:
    """Attach diagnosis observers (Si/dissonance tracking) to ``G``."""

    raw_jobs = G.graph.get("DIAGNOSIS_N_JOBS")
    n_jobs = _coerce_jobs(raw_jobs)

    callback_manager.register_callback(
        G,
        event=CallbackEvent.AFTER_STEP.value,
        func=partial(_diagnosis_step, n_jobs=n_jobs),
        name="diagnosis_step",
    )
    callback_manager.register_callback(
        G,
        event=CallbackEvent.AFTER_STEP.value,
        func=dissonance_events,
        name="dissonance_events",
    )