hypertopos 0.7.1__tar.gz → 0.7.2__tar.gz
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.
- {hypertopos-0.7.1 → hypertopos-0.7.2}/CHANGELOG.md +16 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/LICENSE.md +1 -1
- {hypertopos-0.7.1 → hypertopos-0.7.2}/PKG-INFO +1 -1
- {hypertopos-0.7.1 → hypertopos-0.7.2}/README.md +2 -1
- {hypertopos-0.7.1 → hypertopos-0.7.2}/SECURITY.md +1 -1
- hypertopos-0.7.2/docs/patent-implementation-map.md +123 -0
- hypertopos-0.7.2/docs/refutations.md +71 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/__init__.py +1 -1
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/chains.py +60 -1
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/counterfactual.py +44 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/geometry.py +42 -2
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/model/objects.py +7 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/navigation/navigator.py +558 -13
- {hypertopos-0.7.1 → hypertopos-0.7.2}/pyproject.toml +1 -1
- hypertopos-0.7.2/tests/test_audit_label_alignment.py +245 -0
- hypertopos-0.7.2/tests/test_chain_edge_potentials.py +156 -0
- hypertopos-0.7.2/tests/test_counterfactual_frozen_trajectory.py +278 -0
- hypertopos-0.7.2/tests/test_kind_tag_mismatch_warning.py +277 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_navigation.py +9 -3
- hypertopos-0.7.2/tests/test_signed_confidence_ranking.py +401 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/.github/workflows/publish-pypi.yml +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/.gitignore +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/ROADMAP.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/README.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/api-reference.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/architecture.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/LICENSE +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/README.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/README.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/SUMMARY.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_accounts.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_cpty_banks.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_districts.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_loan_accounts.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_operations.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_orders.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_transactions.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/prepare_tx_types.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/berka/sphere/sphere.yaml +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/README.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/_layer2_cross_eval.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/_layer3_cross_eval.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_accounts.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_currencies.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_payment_formats.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_transactions.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/hi_small_sphere/sphere.yaml +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/layer1_passive_scan.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/layer2_xgboost_sweep.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/layer3_typology_filter.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_accounts.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_currencies.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_payment_formats.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_transactions.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/li_small_sphere/sphere.yaml +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/ibm-aml/passive-scan-calibration.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/README.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/SUMMARY.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-settings.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/sphere/prepare_trips.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmark/nyc-taxi/sphere/sphere.yaml +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/benchmarks.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/concepts.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/configuration.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/data-format.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/external-chains-as-anchor-line.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/build-pipeline.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/core-objects.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/cutting-plane.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/directory-layout.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/hypertopos-overview.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/model-lifecycle.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/navigation-primitives.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/images/three-scales.svg +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/introduction.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/docs/quickstart.md +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/__init__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/_bootstrap.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/_bregman.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/_scatter.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/_stats.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/_theta_sensitivity.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/_writer.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/builder.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/conformance.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/conformance_mapping.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/derived.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/dim_blocks.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/mapping.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/builder/temporal_bucket.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/cli/__init__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/cli/__main__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/cli/build.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/cli/info.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/cli/main.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/cli/schema.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/cli/sources.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/__init__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/adjacency.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/aggregation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/calibration.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/calibration_label_aware.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/composition.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/conformance.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/density_gaps.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/diagnostics.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/dim_audit.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/edge_features.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/explanation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/fdr.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/forecast.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/graph_algorithms.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/heredity.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/hop_predicate.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/invalidation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/investigation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/lance_sql_agg.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/p_value_calibration.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/selection.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/structuring.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/engine/topology.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/model/__init__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/model/manifest.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/model/sphere.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/navigation/__init__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/navigation/scanner.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/sphere.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/__init__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/_lance_versions.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/_schemas.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/_tag_timestamps.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/cache.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/calibration_history.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/exceptions.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/reader.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/topology_cache.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/storage/writer.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/utils/__init__.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/hypertopos/utils/arrow.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/.gitkeep +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/conftest.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/fixtures/generate_fixtures.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/fixtures/synthetic_chain_sphere.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_adjacency.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_adjacency_vectorized.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_aggregation_engine.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_anomaly_propagation_in_chain.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_arrow_utils.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_bootstrap.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_bregman.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_alias.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_chunking.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_contagion.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_derived.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_edges.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_fdr_hierarchy.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_fdr_hierarchy_inject.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_fdr_yaml_end_to_end.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_rolling_z.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_scatter.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_temporal_bucket.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_builder_temporal_perf.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_cache.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_calibration.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_calibration_drift.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_calibration_history.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_calibration_history_builder.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_calibration_history_reader.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_calibration_label_aware.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_chain_coherent_anomaly.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_chain_coherent_synthetic.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_chain_drift_trajectory.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_chain_investigation_summary.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_chain_typology_and_extend.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_chain_witness_intersection.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_chains.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_cli.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_cli_alias.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_cli_label_aware_flag.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_cli_sources.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_compaction.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_composition.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_conformance.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_conformance_mapping.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_conftest_magicmock_restore.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_continuous_event_dims.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_counterfactual.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_data_quality_prop.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_decompose_drift.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_delta_dim_aggregation_consumers.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_delta_norm_signed.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_density_gaps_engine.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_detection.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_diagnostics.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_dim_audit.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_dim_blocks.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_dim_quality_warnings.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_distinct_neighbor_count.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_dim_aggregations.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_dim_aggregations_builder.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_dim_aggregations_chain.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_dim_aggregations_engine.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_dim_aggregations_roundtrip.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_dimensions_builder.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_dimensions_yaml.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_features.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_features_reader.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_potential.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_edge_table.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_engine.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_fdr.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_fdr_multi_resolution.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_fdr_per_dim.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_find_calibration_influencers.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_find_density_gaps.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_find_diverse_explanations.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_find_lead_lag.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_find_lead_lag_significance.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_find_motif_by_hops.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_forecast.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_fts.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_generate_sar_rationale.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_geometric_path.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_geometry_stats.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_gradient_alignment.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_graph_algorithms.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_graph_geometry_tension.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_heredity.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_hop_predicate_engine.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_hub_safety_caps.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_hybrid_search.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_incremental.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_invalidation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_investigate_chain.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_investigate_entity.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_investigation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_kind_detect.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_label_audit_schema.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_lance_sql_agg.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_m1_1_builder_wiring.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_mapping.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_model.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_motif.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_mvcc_session.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_native_mvcc_geometry.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_navigator_alerts.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_navigator_fdr_resolution.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_navigator_fdr_select.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_neighbors_window.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_observability.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_p_value_calibration.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_pattern_fdr_hierarchy.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_precision_stack.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_profile_motif_perf_smoke.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_reliability_flags.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_reputation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_root_cause_trace.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_row_id_cache.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_scanner.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_schema_chain_eda_validation.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_selection.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_simulate_dimension_change.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_sphere.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_sphere_format_version.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_sphere_overview_perf.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_sphere_validation_cheap_tier.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_stats_guard.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_storage.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_streaming_reads.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_structuring_engine.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_tag_timestamps.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_temporal_centroids.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_theta_sensitivity.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_topology.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_topology_anomalies.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_topology_cache.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_topology_trajectory_anomalies.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_trajectory_ann.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_witness_cohort.py +0 -0
- {hypertopos-0.7.1 → hypertopos-0.7.2}/tests/test_witness_set.py +0 -0
|
@@ -7,6 +7,22 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.2] — 2026-05-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `GDSNavigator.π5_attract_anomaly` / `find_anomalies` accept `rank_by="signed_confidence"` — composes `delta_norm_signed`, the Fisher LDA alignment, and the reliability flags into one confidence-weighted score: `score = delta_norm_signed × |lda_alignment| × (1 − reliability_penalty)` where `reliability_penalty = 0.5 × single_dim_driven + 0.5 × low_confidence_bucket`. Survivors carry `signed_confidence_score`, `lda_alignment`, `reliability_penalty` per polygon. Pattern without `label_aware_calibration` raises `GDSNavigationError` — no silent fallback to `delta_norm` ranking.
|
|
14
|
+
- New per-dim `kind_mismatch` warning class on `sphere_overview.dim_quality_warnings`. Fires when a `kind='gaussian'` dim shows `|direction_component| < 0.05` (LDA assigns near-zero weight) AND `cohens_d_pos_neg >= 0.3` (raw class stats separate) — the dim's variance is captured by another dim's Fisher axis, suggesting kind re-declaration or split. Composes with label-aware calibration; suppressed for dims already flagged `negative_space`. Pattern-level prerequisite: `Pattern.label_aware_calibration` must be non-None.
|
|
15
|
+
- `GDSNavigator.audit_label_alignment(pattern_id, *, top_n=10)` — reports Fisher LDA direction's discrimination power as AUROC of `delta_norm_signed` against the binary label column declared in `sphere.yaml`'s `label_audit:` block. Returns `{pattern_id, auroc, n_pos, n_neg, top_dims}` where `top_dims` carries the `top_n` dims sorted by `|direction_component|` desc (most label-discriminating axes first). Returns fallback shape with `auroc: null` and `label_aware_available: False` on patterns built without `label_audit:`.
|
|
16
|
+
- `Chain.to_dict()` carries an additional `edge_potentials: list[float | null]` field — one Euclidean distance `||delta(keys[i]) - delta(keys[i+1])||` per consecutive-pair hop, computed against a caller-supplied anchor pattern delta lookup. `null` on hops with missing polygon, mismatched delta shapes, or non-finite distance (NaN / inf strict-JSON sanitised). Length equals `len(keys) - 1` (empty list for single-entity chains). Surfaces high-distance behavioural jumps inside multi-hop chains for downstream chain investigation primitives.
|
|
17
|
+
- `GDSNavigator.π3_dive_solid` accepts `counterfactual_frozen_population: bool = False` — when `True`, each `SolidSlice` on the returned solid carries an additional `delta_norm_frozen_pop` field reporting the per-slice L2 norm recomputed against the FIRST slice's raw shape as the entity-relative reference epoch (sigma stays at the current pattern's diagonal). Answers "is this entity's apparent normalization a real shift, or just population drift around a stationary entity?" — a stationary entity yields `delta_norm_frozen_pop = 0` across all slices. Default `False` preserves the existing return shape (`delta_norm_frozen_pop = None`).
|
|
18
|
+
- `engine.counterfactual.recompute_delta_norm_against_frozen(shape, mu_frozen, sigma)` — pure-NumPy helper used by the frozen-trajectory path. Sigma-dead dims contribute zero (same convention as the per-edge counterfactual primitives).
|
|
19
|
+
- `SolidSlice.delta_norm_frozen_pop: float | None` — new optional field on the model dataclass populated by the frozen-trajectory path; `None` for the default build path.
|
|
20
|
+
- `docs/refutations.md` — record of tested-and-closed hypotheses: pattern-level σ-estimator swap, per-dim heuristic σ-swap, lazy-chain sampled calibration, Bregman-based `is_anomaly` flag, plus a methodology refutation on cycle-compression. Each entry states the hypothesis, dataset, numeric verdict, root cause, and closure rule.
|
|
21
|
+
- `docs/patent-implementation-map.md` — descriptive map of nine architectural-claim clusters (per-entity geometric position, counterfactual closure, multi-resolution FDR, persistent-homology cycle persistence, declarative compliance, HMP detector composition, chain-coherent investigation, reliability triage, build-time dim quality auditing) to their shipped surfaces. No internal tracker IDs.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- README adds CI test workflow badge linking to the public-mirror Actions run.
|
|
25
|
+
|
|
10
26
|
## [0.7.1] — 2026-05-20
|
|
11
27
|
|
|
12
28
|
### Added
|
|
@@ -26,7 +26,7 @@ For purposes of this License:
|
|
|
26
26
|
- **Internal use** within an organization is not a competitive offering. Your
|
|
27
27
|
organization includes entities under common control.
|
|
28
28
|
|
|
29
|
-
**Change Date:** 2029-05-
|
|
29
|
+
**Change Date:** 2029-05-21
|
|
30
30
|
|
|
31
31
|
**Change License:** Apache License, Version 2.0
|
|
32
32
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hypertopos
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Understand the structure of your data — without training machine learning models
|
|
5
5
|
Project-URL: Homepage, https://github.com/hypertopos/hypertopos-py
|
|
6
6
|
Project-URL: Documentation, https://github.com/hypertopos/hypertopos-py/tree/main/docs
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
> A behavioral feature layer for graph and temporal data — turning behavior into coordinates, trajectories, and explanations.
|
|
4
4
|
|
|
5
|
+
[](https://github.com/hypertopos/hypertopos-py/actions/workflows/test.yml)
|
|
5
6
|
[](https://www.python.org/downloads/)
|
|
6
7
|
[](LICENSE.md)
|
|
7
8
|
[](https://doi.org/10.5281/zenodo.19482069)
|
|
8
9
|
[](https://arrow.apache.org/docs/python/)
|
|
9
10
|
[](https://github.com/lance-format/lance)
|
|
10
11
|
[](https://modelcontextprotocol.io)
|
|
11
|
-
[](pyproject.toml)
|
|
12
13
|
|
|
13
14
|
hypertopos is not a database, and not a machine learning model. It is a layer that turns relational data into a coordinate system where every entity gets a position derived from its relationships and the population around it.
|
|
14
15
|
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Patent implementation map
|
|
2
|
+
|
|
3
|
+
This document maps the architectural claims behind hypertopos to the shipped implementation surfaces in the current release.
|
|
4
|
+
|
|
5
|
+
Each cluster names a load-bearing idea, the navigator or MCP surface that ships it, and the method one-line. The point is to make it easy for a reviewer or external auditor to walk from "this is what the project claims to do" to "this is the function that does it and the test that verifies it".
|
|
6
|
+
|
|
7
|
+
There are no claim numbers (`IC-XX`, `DC-XX.Y`) in this document — those are internal tracker IDs. Each cluster is named by its descriptive function.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Per-entity geometric position derived from a population
|
|
12
|
+
|
|
13
|
+
**What it is.** Every entity carries a position in a multi-dimensional space whose axes are not raw column values but population-relative statistics — z-scores against the population mean, ratios against population percentiles, signed projections onto label-discriminating axes. The position is computed once at build time and stored as a Lance column.
|
|
14
|
+
|
|
15
|
+
**Surfaces.** `Pattern.mu_diag`, `Pattern.sigma_diag`, `Pattern.theta_diag` (population-level statistics persisted on every pattern). `Pattern.label_aware_calibration` (per-class moments + Fisher LDA direction, opt-in via `sphere.yaml`'s `label_audit:` block). `delta_norm` and `delta_norm_signed` Lance columns on every geometry table.
|
|
16
|
+
|
|
17
|
+
**Method.** Welford one-pass for mu / sigma. Quantile sketches for theta. Linear discriminant analysis with diagonal-covariance approximation for label-aware direction. All computations are deterministic and reproducible from the source data plus `sphere.yaml`.
|
|
18
|
+
|
|
19
|
+
**Tests.** `tests/test_calibration.py`, `tests/test_calibration_label_aware.py`, `tests/test_m1_1_builder_wiring.py`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Counterfactual closure — what changes if X is removed
|
|
24
|
+
|
|
25
|
+
**What it is.** For an anomalous entity, ask "would it still be anomalous if this edge, this counterparty, or this dim were removed?" — and answer with a closed-form recomputation of `delta_norm` against the same population statistics, no model retraining.
|
|
26
|
+
|
|
27
|
+
**Surfaces.** `GDSNavigator.simulate_edge_removal`, `simulate_counterparty_removal`, `select_minimal_joint_edge_removal`, `simulate_dimension_change`. Each surfaces as an MCP tool with the same name.
|
|
28
|
+
|
|
29
|
+
**Method.** Closed-form exp-family delta recomputation. For each candidate removal, recompute the entity's polygon contribution to `delta_norm` as `mu_diag - new_centroid`, take the ratio against `sigma_diag`, project against `theta_diag` percentile — same calibration, different inputs. Greedy selection for the minimal joint set.
|
|
30
|
+
|
|
31
|
+
**Tests.** `tests/test_counterfactual_*.py`.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Multi-resolution false-discovery-rate control
|
|
36
|
+
|
|
37
|
+
**What it is.** Standard Benjamini-Hochberg FDR on a per-entity anomaly score answers one question — "is this entity anomalous?" — but ignores that entities live in nested cells (region × time × kind). Multi-resolution FDR aggregates per-cell p-values up a hierarchy with Tippett min-p and reports `q`-values at every level, surviving the more conservative join.
|
|
38
|
+
|
|
39
|
+
**Surfaces.** `Pattern.fdr_hierarchy` and `Pattern.fdr_temporal_hierarchy` (declarative schema in `sphere.yaml`). `find_anomalies(fdr_resolution, fdr_temporal_resolution)` MCP tool returns `cell_q_spatial` and `cell_q_temporal` per survivor. `engine.fdr.fdr_multi_resolution` (pure-math primitive).
|
|
40
|
+
|
|
41
|
+
**Method.** Per-cell Fisher exact 2×2 on an anomaly indicator vs background. BH or Storey correction within each cell. Tippett min-p aggregation up each declared level. Independent per-axis adjustment for spatial vs temporal hierarchies.
|
|
42
|
+
|
|
43
|
+
**Tests.** `tests/test_fdr_*.py`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Persistent-homology cycle persistence at the entity level
|
|
48
|
+
|
|
49
|
+
**What it is.** Treat each entity's k-nearest-neighbour ball in the polygon space as a small point cloud and ask whether that cloud carries persistent topological cycles. An entity with high `h1_max_persistence` sits inside a non-trivial topological structure — a ring, a hub, a cluster boundary — that single-entity z-scores cannot see.
|
|
50
|
+
|
|
51
|
+
**Surfaces.** `find_topological_anomalies` MCP tool. `engine.topology` module (Vietoris-Rips persistent homology backed by `ripser` when the optional `[topology]` extra is installed). `storage.topology_cache` for memoised per-entity persistence values.
|
|
52
|
+
|
|
53
|
+
**Method.** Per-entity k-NN ball (k ≈ 50 by default), PCA reduction to 10 dims, Vietoris-Rips H_1 persistence computed with `ripser`. Score = max `h1` death-birth interval. Cached because the computation is expensive; invalidated when calibration epochs bump.
|
|
54
|
+
|
|
55
|
+
**Tests.** `tests/test_topology_*.py`.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Declarative compliance rules as a first-class build-time predicate language
|
|
60
|
+
|
|
61
|
+
**What it is.** Compliance officers write rules in YAML using a small declarative AST (`and / or / not` over `==, !=, <, <=, >, >=, in`); the builder evaluates them at build time and materialises a violations sidecar Lance dataset; a runtime MCP tool reads the sidecar with rule-id and severity pushdown. Separate from `delta_norm` anomaly flags — an entity can be one, the other, or both.
|
|
62
|
+
|
|
63
|
+
**Surfaces.** `Pattern.conformance_rules` (model field). `builder.conformance_mapping.parse_conformance_rules` (YAML loader with column-existence validation). `find_conformance_violations` MCP tool.
|
|
64
|
+
|
|
65
|
+
**Method.** Vectorised PyArrow expression evaluation. Severity literals `low < medium < high < critical`. Rule-id stamping. Sidecar Lance dataset alongside the geometry table.
|
|
66
|
+
|
|
67
|
+
**Tests.** `tests/test_conformance_mapping.py`, `tests/test_find_conformance_violations_mcp.py`.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Detector composition via harmonic-mean p-value
|
|
72
|
+
|
|
73
|
+
**What it is.** Five independent detectors — `delta_norm`, `neighbor_contamination`, `segment_shift`, `trajectory_continuous`, `density_gap` — score each entity. A correct composition treats these as multiple hypothesis tests on the same null and combines them via the Wilson harmonic-mean p-value (HMP), not via score averaging. The combined `hmp` is rank-stable across detector subsets and exact under the null.
|
|
74
|
+
|
|
75
|
+
**Surfaces.** `combine_anomaly_pvalues`, `classify_detector_consensus`, `composite_risk`, `composite_risk_batch` MCP tools. `engine.composition.harmonic_mean_p` (pure-math primitive). `engine.p_value_calibration` (per-detector score-to-p-value adapters).
|
|
76
|
+
|
|
77
|
+
**Method.** Per-detector calibrated p-values. Wilson HMP combiner. Null-simulation lookup for the `L`-test threshold at level `alpha`. Per-detector weighting optional.
|
|
78
|
+
|
|
79
|
+
**Tests.** `tests/test_composition_*.py`, `tests/test_detector_consensus_*.py`.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## Chain-coherent investigation primitives
|
|
84
|
+
|
|
85
|
+
**What it is.** A chain is a multi-hop path through an event graph (`from_account → to_account` over time). Investigating a chain means asking whether the chain itself carries anomaly signal (`investigate_chain`), whether its members move together in regime space (`chain_drift_trajectory`), whether their explanatory witness sets intersect (`chain_witness_intersection`), and whether the chain's typology matches a known pattern (`classify_chain_typology`). The chain becomes a first-class investigation entity, not just a list of vertices.
|
|
86
|
+
|
|
87
|
+
**Surfaces.** `chain_lines:` YAML block. `extract_chains` build pass. `chain_pattern` derived geometry. `find_chains_with_coherent_anomaly`, `chain_witness_intersection`, `chain_drift_trajectory`, `classify_chain_typology`, `extend_chain`, `investigate_chain`, `generate_sar_rationale` MCP tools.
|
|
88
|
+
|
|
89
|
+
**Method.** Seeded BFS over the event graph with multi-criteria seed selection (`fan_out`, `cross_bank`, `multi_currency`, `pass_through`). Chain feature extraction at build time (`hop_count`, `is_cyclic`, `time_span_hours`, `amount_decay`, etc.). Same pattern-level calibration applied to chains as to entities, so `delta_norm` carries the same population-relative semantics.
|
|
90
|
+
|
|
91
|
+
**Tests.** `tests/test_chain_*.py`.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Reliability triage on every flagged polygon
|
|
96
|
+
|
|
97
|
+
**What it is.** A polygon flagged anomalous can be flagged because one dim dominates the score (an artefact of that dim's calibration, not a real anomaly) or because the bootstrap confidence on `is_anomaly` is fragile (the flag would flip if the population were resampled). Both surfaces are reported as boolean flags alongside the score, so an investigator can corroborate before opening a case.
|
|
98
|
+
|
|
99
|
+
**Surfaces.** `reliability_flags` field on `find_anomalies`, `explain_anomaly`, `composite_risk`, `combine_anomaly_pvalues`, `investigate_entity`. Shape `{single_dim_driven, dominant_dim, dominant_dim_share, low_confidence_bucket, confidence, flags}`.
|
|
100
|
+
|
|
101
|
+
**Method.** `single_dim_driven` fires when one dim's attribution share exceeds 70 %. `low_confidence_bucket` fires when bootstrap-resampled `anomaly_confidence` falls below 0.5. Both computed at flag time, no extra MCP roundtrip.
|
|
102
|
+
|
|
103
|
+
**Tests.** `tests/test_reliability_flags.py`.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Build-time dim quality auditing — silent calibration failure modes surfaced
|
|
108
|
+
|
|
109
|
+
**What it is.** Five build-time auditors catch the silent failure modes that break z-score / `delta_norm` semantics: dead dims (zero variance), sparse dims (mostly zero), dominant-dim mass (one dim accounts for ≥70 % of tail variance), negative-space dims (gaussian-declared but empirically zero-modal), non-normal dims (gaussian-declared but Shapiro-Wilk / KS `p < 0.01`), and per-pattern heteroscedasticity (Brown-Forsythe Levene `p < 0.01` on the grouping variable).
|
|
110
|
+
|
|
111
|
+
**Surfaces.** `sphere_overview.dim_quality_warnings[]` block. `engine.diagnostics` and `engine.dim_audit` modules.
|
|
112
|
+
|
|
113
|
+
**Method.** Build-time statistical tests on the calibrated geometry table. Each auditor returns a structured warning with `dim_label`, `reason`, `advice`, `evidence_value`, `threshold` — enough for a downstream consumer to either raise a calibration ticket or apply the recommended transform.
|
|
114
|
+
|
|
115
|
+
**Tests.** `tests/test_dim_quality_warnings.py`, `tests/test_diagnostics.py`.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Notes
|
|
120
|
+
|
|
121
|
+
This document is a living artefact. As new architectural surfaces ship, they are added here. As surfaces are deprecated or refactored, the entries update with the cycle they shipped in.
|
|
122
|
+
|
|
123
|
+
Internal tracker IDs (`IC-XX`, `DC-XX.Y`, `OPF-XX`, `IDEA-XXX`) are kept out of this document by project rule — they are private bookkeeping and would look like dead hyperlinks to anyone reading from the public mirror. Each cluster is named by what it does, not by which ticket tracks it.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Refutations
|
|
2
|
+
|
|
3
|
+
This document records hypotheses that were tested, found wrong, and closed. Each entry follows the discipline: state the hypothesis, name the dataset, give the numeric evidence, and link the commit or PR that closed the line of work.
|
|
4
|
+
|
|
5
|
+
The point is not to celebrate failures. It is to leave a record so the same idea is not re-investigated six months later under a different name, and so a reviewer can see that the project takes negative results seriously.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Pattern-level σ-estimator swap (refuted 0.6.6, PR #435)
|
|
10
|
+
|
|
11
|
+
**Hypothesis.** Replacing the population standard deviation σ used in `delta_norm` calibration with a robust estimator (MAD, IQR, Qn) would lift downstream anomaly detection by being less sensitive to outlier leak.
|
|
12
|
+
|
|
13
|
+
**What was tested.** Four σ estimators (`std`, `mad`, `iqr`, `qn`) plugged into `Pattern.sigma_diag` calibration. Evaluation on a money-laundering account-level pattern with binary `is_laundering` labels.
|
|
14
|
+
|
|
15
|
+
**Verdict.** AUROC and top-K precision differences across the four estimators sat inside the bootstrap noise band (±0.005 on AUROC). MAD and IQR did not deliver any robustness lift on this distribution. The "robust σ" line of work was closed.
|
|
16
|
+
|
|
17
|
+
**Why it failed.** Account-level features are already log-transformed or rank-normalised upstream of calibration. Outliers no longer drive σ when the input is approximately normal post-transform. The σ-swap intervention had no surface to act on.
|
|
18
|
+
|
|
19
|
+
**Citation.** PR #435 retrospective + `MEMORY.md` entry `project_population_robust_sigma_refuted`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Per-dim heuristic σ-estimator swap (refuted 0.6.6, PR #436)
|
|
24
|
+
|
|
25
|
+
**Hypothesis.** σ-swap might work if applied per-dim rather than population-wide — pick the estimator per column based on a heuristic (skewness, kurtosis, sparsity).
|
|
26
|
+
|
|
27
|
+
**What was tested.** Per-dim heuristic dispatcher selecting `std` / `mad` / `iqr` based on column statistics. Same evaluation setup as PR #435.
|
|
28
|
+
|
|
29
|
+
**Verdict.** Same outcome — ΔAUROC < 0.005, inside bootstrap noise. The heuristic dispatcher added complexity without measurable lift.
|
|
30
|
+
|
|
31
|
+
**Why it failed.** Same root cause as PR #435 — when upstream features are normalised, σ-estimator choice does not materially change the calibration. The dispatcher was solving a problem that did not exist on this data shape.
|
|
32
|
+
|
|
33
|
+
**Closure rule.** The entire "calibration robustness via σ-estimator swap" theme is closed for the project unless a new dataset surfaces with un-normalised, heavy-tailed inputs where the assumption is testable from scratch. The closure rule is enforced via the session memory `project_population_robust_sigma_refuted` — re-opening requires a different dataset or methodology.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Lazy-chain geometry sampled calibration (refuted 0.5.x, retired 2026-04-19)
|
|
38
|
+
|
|
39
|
+
**Hypothesis.** Computing chain-pattern calibration on a sampled subset of chains rather than the full population would scale linearly with sphere size and preserve detection quality.
|
|
40
|
+
|
|
41
|
+
**What was tested.** Sampled-calibration variant against full-calibration baseline on the money-laundering chain pattern. Compared `delta_norm` rankings and downstream chain-level anomaly recall.
|
|
42
|
+
|
|
43
|
+
**Verdict.** Sampled calibration produced a sub-null `delta_norm` distribution — calibration estimates were biased low because the sampled tail underrepresented the true variance. Bias roughly 24 % on the most affected dims. Chain recall on labelled bad chains dropped materially.
|
|
44
|
+
|
|
45
|
+
**Why it failed.** Chains are a long-tail distribution by structure. Uniform sampling under-weights long chains, which carry the discriminating signal. A weighted sampler would have helped in theory but the engineering cost outweighed the expected gain.
|
|
46
|
+
|
|
47
|
+
**Closure.** The lazy-chain-geometry document carries an in-doc retired banner dated 2026-04-19. The work was superseded by treating chains as first-class geometric entities (the `chain_lines:` YAML block and the chain investigation primitives shipped in 0.6.x and 0.7.0).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Bregman-based `is_anomaly` flag (refuted 2026-04-16)
|
|
52
|
+
|
|
53
|
+
**Hypothesis.** Replacing the Mahalanobis-distance-based anomaly flag with a Bregman-divergence-based flag would lift top-K precision on money-laundering account-level detection by giving exp-family-correct distance semantics for non-gaussian dims (counts, ratios).
|
|
54
|
+
|
|
55
|
+
**What was tested.** Bregman-flag variant evaluated against Mahalanobis baseline at K ∈ {50, 100, 200, 500}, top-K precision and recall on labelled laundering accounts.
|
|
56
|
+
|
|
57
|
+
**Verdict.** ΔF1 ≤ +0.005 across all K values — inside bootstrap noise. The Bregman variant did not deliver measurable lift on this data shape.
|
|
58
|
+
|
|
59
|
+
**Why it failed.** The dimension-kind tags in production are mostly `gaussian` or `mahalanobis` with a few `bernoulli` flags. Bregman's correctness advantage only matters when a substantial fraction of dims are explicitly `poisson` or `bernoulli` and carry top-of-rank load. On the tested pattern, no such dim dominated.
|
|
60
|
+
|
|
61
|
+
**Closure.** The `is_anomaly` flag stays Mahalanobis-based. Bregman remains a per-dim choice via `dimension_kinds: poisson` / `bernoulli` declarations but is not promoted to the global flag computation. Tracked as a closed direction in session memory.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Process refutation — emergent cycle-compression (0.7.0 retrospective)
|
|
66
|
+
|
|
67
|
+
This one is not algorithmic. The 0.7.0 cycle landed five PRs in a 24-hour window, four of which had been originally planned as 0.7.1 milestones. The result was a tag that shipped on the planned date but with a release-prep state that drifted across packages.
|
|
68
|
+
|
|
69
|
+
**Lesson.** Items that emerge during a cycle and feel ready to ship should not skip cycle boundaries unless the truth-in-CHANGELOG cost of leaving them is concrete. The hard cycle-window rule is enforced going forward: feature PRs land outside the seven-day pre-tag window; the release-prep PR is the literal last PR before the tag; emerged-during-execution items defer to the next cycle by default.
|
|
70
|
+
|
|
71
|
+
**Why this entry exists.** Methodology refutations are as load-bearing as algorithmic ones — they keep the cycle cadence honest. The 0.7.1 cycle deliberately sized smaller to test the rule under real conditions.
|
|
@@ -10,6 +10,7 @@ Used for: money laundering chain detection, supply chain path analysis.
|
|
|
10
10
|
from __future__ import annotations
|
|
11
11
|
|
|
12
12
|
import contextlib
|
|
13
|
+
import math
|
|
13
14
|
from collections import defaultdict
|
|
14
15
|
from dataclasses import dataclass
|
|
15
16
|
from datetime import datetime
|
|
@@ -173,7 +174,23 @@ class Chain:
|
|
|
173
174
|
for i in range(len(self.amounts) - 1)
|
|
174
175
|
)
|
|
175
176
|
|
|
176
|
-
def to_dict(
|
|
177
|
+
def to_dict(
|
|
178
|
+
self,
|
|
179
|
+
*,
|
|
180
|
+
delta_by_key: dict[str, np.ndarray] | None = None,
|
|
181
|
+
) -> dict:
|
|
182
|
+
"""Serialise the chain to a JSON-safe dict.
|
|
183
|
+
|
|
184
|
+
``delta_by_key`` is an optional pre-fetched mapping from entity key
|
|
185
|
+
to that entity's polygon delta vector against the chain's anchor
|
|
186
|
+
pattern. When supplied, the output gains an ``edge_potentials``
|
|
187
|
+
field — one Euclidean distance ``||delta(keys[i]) - delta(keys[i+1])||``
|
|
188
|
+
per consecutive-pair hop. Element is ``None`` when either endpoint
|
|
189
|
+
lacks a delta entry, the vectors have mismatched or zero length,
|
|
190
|
+
or the resulting distance is non-finite (NaN / inf — strict-JSON
|
|
191
|
+
sanitised). When ``delta_by_key`` is None, ``edge_potentials`` is
|
|
192
|
+
a list of ``None`` of the appropriate length.
|
|
193
|
+
"""
|
|
177
194
|
amt_std = float(np.std(self.amounts)) if self.amounts else 0.0
|
|
178
195
|
amt_mean = float(np.mean(self.amounts)) if self.amounts else 0.0
|
|
179
196
|
return {
|
|
@@ -196,9 +213,51 @@ class Chain:
|
|
|
196
213
|
"amount_monotone_decreasing": (
|
|
197
214
|
self.amount_monotone_decreasing()
|
|
198
215
|
),
|
|
216
|
+
"edge_potentials": _compute_edge_potentials(
|
|
217
|
+
self.keys, delta_by_key,
|
|
218
|
+
),
|
|
199
219
|
}
|
|
200
220
|
|
|
201
221
|
|
|
222
|
+
def _compute_edge_potentials(
|
|
223
|
+
keys: list[str],
|
|
224
|
+
delta_by_key: dict[str, np.ndarray] | None,
|
|
225
|
+
) -> list[float | None]:
|
|
226
|
+
"""Euclidean distance ``||delta(keys[i]) - delta(keys[i+1])||`` per hop.
|
|
227
|
+
|
|
228
|
+
Length is ``max(0, len(keys) - 1)``. Element is ``None`` when:
|
|
229
|
+
- ``delta_by_key`` is None (the caller did not pre-fetch deltas);
|
|
230
|
+
- either endpoint's key is missing from ``delta_by_key``;
|
|
231
|
+
- the two delta vectors have mismatched or zero length;
|
|
232
|
+
- the computed distance is not finite (NaN / inf).
|
|
233
|
+
|
|
234
|
+
NaN / inf protection keeps the result strict-JSON-safe: any non-finite
|
|
235
|
+
distance is replaced by ``None`` before serialisation.
|
|
236
|
+
"""
|
|
237
|
+
n_hops = max(0, len(keys) - 1)
|
|
238
|
+
if n_hops == 0:
|
|
239
|
+
return []
|
|
240
|
+
if delta_by_key is None:
|
|
241
|
+
return [None] * n_hops
|
|
242
|
+
|
|
243
|
+
out: list[float | None] = []
|
|
244
|
+
for i in range(n_hops):
|
|
245
|
+
a = delta_by_key.get(keys[i])
|
|
246
|
+
b = delta_by_key.get(keys[i + 1])
|
|
247
|
+
if a is None or b is None:
|
|
248
|
+
out.append(None)
|
|
249
|
+
continue
|
|
250
|
+
if a.shape != b.shape or a.size == 0:
|
|
251
|
+
out.append(None)
|
|
252
|
+
continue
|
|
253
|
+
d = float(np.linalg.norm(a - b))
|
|
254
|
+
if not math.isfinite(d):
|
|
255
|
+
out.append(None)
|
|
256
|
+
continue
|
|
257
|
+
out.append(d)
|
|
258
|
+
return out
|
|
259
|
+
|
|
260
|
+
|
|
202
261
|
def extract_chains(
|
|
203
262
|
from_keys: list[str],
|
|
204
263
|
to_keys: list[str],
|
|
@@ -26,6 +26,7 @@ __all__ = [
|
|
|
26
26
|
"compute_per_edge_source_value_pvalues",
|
|
27
27
|
"simulate_joint_edge_removal",
|
|
28
28
|
"select_minimal_joint_removal",
|
|
29
|
+
"recompute_delta_norm_against_frozen",
|
|
29
30
|
]
|
|
30
31
|
|
|
31
32
|
|
|
@@ -823,3 +824,46 @@ def select_minimal_joint_removal(
|
|
|
823
824
|
"target_reached": target_reached,
|
|
824
825
|
"k_max_reached": k_max_reached,
|
|
825
826
|
}
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def recompute_delta_norm_against_frozen(
|
|
830
|
+
*,
|
|
831
|
+
shape: np.ndarray,
|
|
832
|
+
mu_frozen: np.ndarray,
|
|
833
|
+
sigma: np.ndarray,
|
|
834
|
+
) -> float:
|
|
835
|
+
"""L2 norm of ``shape`` normalised against a frozen reference ``mu_frozen``.
|
|
836
|
+
|
|
837
|
+
Frozen-population trajectory primitive used by
|
|
838
|
+
``GDSEngine.build_solid(counterfactual_frozen_population=True)``: for each
|
|
839
|
+
solid slice, recompute ``delta_norm`` using the FIRST slice's raw shape
|
|
840
|
+
(``mu_frozen``) as the population reference and the current pattern's
|
|
841
|
+
diagonal sigma as the scale. Sigma-dead dims (``sigma < 1e-10``) contribute
|
|
842
|
+
zero — same convention as the per-edge counterfactual primitives.
|
|
843
|
+
|
|
844
|
+
Args:
|
|
845
|
+
shape: per-slice raw shape vector, shape (D,).
|
|
846
|
+
mu_frozen: frozen reference mean (e.g. slice-0's raw shape), shape (D,).
|
|
847
|
+
sigma: current pattern's diagonal sigma, shape (D,).
|
|
848
|
+
|
|
849
|
+
Returns:
|
|
850
|
+
Float L2 norm. Returns ``0.0`` when ``shape == mu_frozen`` (stationary
|
|
851
|
+
entity in a drifting population) — the canonical signal that the
|
|
852
|
+
entity's apparent normalisation came from population drift, not
|
|
853
|
+
entity movement.
|
|
854
|
+
"""
|
|
855
|
+
if shape.shape != mu_frozen.shape or shape.shape != sigma.shape:
|
|
856
|
+
raise ValueError(
|
|
857
|
+
f"shape / mu_frozen / sigma must have matching length; got "
|
|
858
|
+
f"{shape.shape} / {mu_frozen.shape} / {sigma.shape}",
|
|
859
|
+
)
|
|
860
|
+
shape = shape.astype(np.float64, copy=False)
|
|
861
|
+
mu_frozen = mu_frozen.astype(np.float64, copy=False)
|
|
862
|
+
sigma = sigma.astype(np.float64, copy=False)
|
|
863
|
+
sigma_dead_mask = sigma < _SIGMA_ZERO_FLOOR
|
|
864
|
+
sigma_safe = np.where(sigma_dead_mask, 1.0, sigma)
|
|
865
|
+
delta = np.where(sigma_dead_mask, 0.0, (shape - mu_frozen) / sigma_safe)
|
|
866
|
+
val = float(np.linalg.norm(delta))
|
|
867
|
+
if not np.isfinite(val):
|
|
868
|
+
return 0.0
|
|
869
|
+
return val
|
|
@@ -201,6 +201,7 @@ class GDSEngine:
|
|
|
201
201
|
manifest: Manifest,
|
|
202
202
|
filters: dict[str, str | list[str]] | None = None,
|
|
203
203
|
timestamp: datetime | None = None,
|
|
204
|
+
counterfactual_frozen_population: bool = False,
|
|
204
205
|
) -> Solid:
|
|
205
206
|
base = self.build_polygon(primary_key, pattern_id, manifest)
|
|
206
207
|
table = self._storage.read_temporal(
|
|
@@ -216,6 +217,11 @@ class GDSEngine:
|
|
|
216
217
|
pattern = sphere.patterns[pattern_id]
|
|
217
218
|
sigma = np.maximum(pattern.sigma_diag, self.SIGMA_EPSILON)
|
|
218
219
|
slices: list[SolidSlice] = []
|
|
220
|
+
# When counterfactual_frozen_population is requested, capture each
|
|
221
|
+
# slice's raw shape_snapshot so the frozen-trajectory recompute can
|
|
222
|
+
# run after the loop using the chronologically-first slice's shape
|
|
223
|
+
# as the entity-relative reference.
|
|
224
|
+
raw_shapes: list[np.ndarray] = []
|
|
219
225
|
for i in range(table.num_rows):
|
|
220
226
|
row = {col: table[col][i].as_py() for col in table.schema.names}
|
|
221
227
|
shape = np.array(row["shape_snapshot"], dtype=np.float32)
|
|
@@ -241,11 +247,45 @@ class GDSEngine:
|
|
|
241
247
|
changed_line_id=row.get("changed_line_id"),
|
|
242
248
|
added_edge=None,
|
|
243
249
|
))
|
|
244
|
-
|
|
250
|
+
if counterfactual_frozen_population:
|
|
251
|
+
raw_shapes.append(shape)
|
|
252
|
+
# Pair raw_shapes with their slice and sort jointly so the
|
|
253
|
+
# chronologically-first slice's raw shape becomes the frozen
|
|
254
|
+
# reference even when temporal rows arrive out of order.
|
|
255
|
+
if counterfactual_frozen_population and slices:
|
|
256
|
+
paired = sorted(
|
|
257
|
+
zip(slices, raw_shapes, strict=True),
|
|
258
|
+
key=lambda p: p[0].timestamp,
|
|
259
|
+
)
|
|
260
|
+
slices = [p[0] for p in paired]
|
|
261
|
+
raw_shapes = [p[1] for p in paired]
|
|
262
|
+
else:
|
|
263
|
+
slices.sort(key=lambda s: s.timestamp)
|
|
245
264
|
if timestamp is not None:
|
|
246
265
|
if timestamp.tzinfo is None:
|
|
247
266
|
timestamp = timestamp.replace(tzinfo=UTC)
|
|
248
|
-
|
|
267
|
+
if counterfactual_frozen_population:
|
|
268
|
+
surviving = [
|
|
269
|
+
(s, r) for s, r in zip(slices, raw_shapes, strict=True)
|
|
270
|
+
if s.timestamp <= timestamp
|
|
271
|
+
]
|
|
272
|
+
slices = [p[0] for p in surviving]
|
|
273
|
+
raw_shapes = [p[1] for p in surviving]
|
|
274
|
+
else:
|
|
275
|
+
slices = [s for s in slices if s.timestamp <= timestamp]
|
|
276
|
+
if counterfactual_frozen_population and slices:
|
|
277
|
+
from hypertopos.engine.counterfactual import (
|
|
278
|
+
recompute_delta_norm_against_frozen,
|
|
279
|
+
)
|
|
280
|
+
mu_frozen = raw_shapes[0]
|
|
281
|
+
_w = mu_frozen.shape[-1]
|
|
282
|
+
sigma_frozen = sigma[:_w]
|
|
283
|
+
for slc, raw in zip(slices, raw_shapes, strict=True):
|
|
284
|
+
slc.delta_norm_frozen_pop = recompute_delta_norm_against_frozen(
|
|
285
|
+
shape=raw,
|
|
286
|
+
mu_frozen=mu_frozen,
|
|
287
|
+
sigma=sigma_frozen,
|
|
288
|
+
)
|
|
249
289
|
return Solid(primary_key=primary_key, pattern_id=pattern_id,
|
|
250
290
|
base_polygon=base, slices=slices)
|
|
251
291
|
|
|
@@ -87,6 +87,13 @@ class SolidSlice:
|
|
|
87
87
|
changed_property: str | None
|
|
88
88
|
changed_line_id: str | None
|
|
89
89
|
added_edge: Edge | None
|
|
90
|
+
# Counterfactual frozen-population delta_norm. Populated only when the
|
|
91
|
+
# owning solid was built with ``counterfactual_frozen_population=True`` —
|
|
92
|
+
# in that case each slice's value is the L2 norm of the per-slice shape
|
|
93
|
+
# normalised against the FIRST slice's raw shape (treated as the
|
|
94
|
+
# entity-relative reference epoch) instead of the current pattern mu.
|
|
95
|
+
# ``None`` for the default path preserves the existing serialised shape.
|
|
96
|
+
delta_norm_frozen_pop: float | None = None
|
|
90
97
|
|
|
91
98
|
def prop_column_states(self, pattern: Pattern) -> dict[str, bool]:
|
|
92
99
|
"""Inverse z-score: recover boolean property states from delta_snapshot."""
|