hypertopos 0.5.2__tar.gz → 0.6.0__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.5.2 → hypertopos-0.6.0}/CHANGELOG.md +228 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/LICENSE.md +1 -1
- {hypertopos-0.5.2 → hypertopos-0.6.0}/PKG-INFO +1 -1
- {hypertopos-0.5.2 → hypertopos-0.6.0}/README.md +1 -1
- {hypertopos-0.5.2 → hypertopos-0.6.0}/ROADMAP.md +16 -9
- {hypertopos-0.5.2 → hypertopos-0.6.0}/SECURITY.md +1 -1
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/api-reference.md +294 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/architecture.md +2 -2
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/concepts.md +122 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/configuration.md +1078 -1066
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/data-format.md +55 -0
- hypertopos-0.6.0/hypertopos/__init__.py +47 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/builder.py +469 -10
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/mapping.py +355 -243
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/cli/build.py +1001 -992
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/cli/schema.py +898 -895
- hypertopos-0.6.0/hypertopos/engine/density_gaps.py +150 -0
- hypertopos-0.6.0/hypertopos/engine/edge_features.py +189 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/geometry.py +604 -7
- hypertopos-0.6.0/hypertopos/engine/hop_predicate.py +285 -0
- hypertopos-0.6.0/hypertopos/engine/structuring.py +127 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/model/sphere.py +283 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/navigation/navigator.py +1722 -110
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/storage/_schemas.py +65 -52
- hypertopos-0.6.0/hypertopos/storage/calibration_history.py +206 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/storage/reader.py +143 -4
- {hypertopos-0.5.2 → hypertopos-0.6.0}/pyproject.toml +1 -1
- hypertopos-0.6.0/tests/test_calibration_drift.py +398 -0
- hypertopos-0.6.0/tests/test_calibration_history.py +345 -0
- hypertopos-0.6.0/tests/test_calibration_history_builder.py +841 -0
- hypertopos-0.6.0/tests/test_calibration_history_reader.py +271 -0
- hypertopos-0.6.0/tests/test_decompose_drift.py +564 -0
- hypertopos-0.6.0/tests/test_density_gaps_engine.py +111 -0
- hypertopos-0.6.0/tests/test_edge_dimensions_builder.py +145 -0
- hypertopos-0.6.0/tests/test_edge_dimensions_yaml.py +163 -0
- hypertopos-0.6.0/tests/test_edge_features.py +229 -0
- hypertopos-0.6.0/tests/test_edge_features_reader.py +55 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_edge_potential.py +7 -0
- hypertopos-0.6.0/tests/test_find_calibration_influencers.py +806 -0
- hypertopos-0.6.0/tests/test_find_density_gaps.py +105 -0
- hypertopos-0.6.0/tests/test_find_lead_lag.py +459 -0
- hypertopos-0.6.0/tests/test_find_lead_lag_significance.py +111 -0
- hypertopos-0.6.0/tests/test_find_motif_by_hops.py +130 -0
- hypertopos-0.6.0/tests/test_hop_predicate_engine.py +369 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_navigation.py +50 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_root_cause_trace.py +14 -0
- hypertopos-0.6.0/tests/test_structuring_engine.py +111 -0
- hypertopos-0.5.2/hypertopos/__init__.py +0 -19
- {hypertopos-0.5.2 → hypertopos-0.6.0}/.github/workflows/publish-pypi.yml +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/.gitignore +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/README.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/LICENSE +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/README.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/README.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/SUMMARY.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_accounts.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_cpty_banks.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_districts.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_loan_accounts.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_operations.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_orders.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_transactions.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/prepare_tx_types.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/berka/sphere/sphere.yaml +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/README.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/_layer2_cross_eval.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/_layer3_cross_eval.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_accounts.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_currencies.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_payment_formats.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_transactions.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/hi_small_sphere/sphere.yaml +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/layer1_passive_scan.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/layer2_xgboost_sweep.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/layer3_typology_filter.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/li_small_sphere/prepare_accounts.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/li_small_sphere/prepare_currencies.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/li_small_sphere/prepare_payment_formats.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/li_small_sphere/prepare_transactions.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/li_small_sphere/sphere.yaml +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/ibm-aml/passive-scan-calibration.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/README.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/SUMMARY.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-settings.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/sphere/prepare_trips.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmark/nyc-taxi/sphere/sphere.yaml +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/benchmarks.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/build-pipeline.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/core-objects.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/cutting-plane.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/directory-layout.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/hypertopos-overview.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/model-lifecycle.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/navigation-primitives.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/images/three-scales.svg +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/introduction.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/docs/quickstart.md +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/__init__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/_bootstrap.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/_bregman.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/_scatter.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/_stats.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/_writer.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/derived.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/builder/dim_blocks.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/cli/__init__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/cli/__main__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/cli/info.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/cli/main.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/cli/sources.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/__init__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/adjacency.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/aggregation.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/calibration.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/chains.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/composition.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/fdr.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/forecast.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/graph_algorithms.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/heredity.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/invalidation.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/investigation.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/lance_sql_agg.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/engine/selection.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/model/__init__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/model/manifest.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/model/objects.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/navigation/__init__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/navigation/scanner.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/sphere.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/storage/__init__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/storage/cache.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/storage/writer.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/utils/__init__.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/hypertopos/utils/arrow.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/.gitkeep +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/conftest.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/fixtures/generate_fixtures.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_adjacency.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_aggregation_engine.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_arrow_utils.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_bootstrap.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_bregman.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_alias.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_chunking.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_contagion.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_derived.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_edges.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_rolling_z.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_scatter.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_builder_temporal_perf.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_cache.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_calibration.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_chains.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_cli.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_cli_alias.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_cli_sources.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_compaction.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_composition.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_continuous_event_dims.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_data_quality_prop.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_detection.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_dim_blocks.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_edge_table.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_engine.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_fdr.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_forecast.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_fts.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_geometric_path.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_geometry_stats.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_gradient_alignment.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_graph_algorithms.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_heredity.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_hybrid_search.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_incremental.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_invalidation.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_investigation.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_kind_detect.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_lance_sql_agg.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_mapping.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_model.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_motif.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_mvcc_session.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_navigator_alerts.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_navigator_fdr_select.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_observability.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_precision_stack.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_profile_motif_perf_smoke.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_reputation.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_row_id_cache.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_scanner.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_selection.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_sphere.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_sphere_overview_perf.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_stats_guard.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_storage.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_streaming_reads.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_temporal_centroids.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_trajectory_ann.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_witness_cohort.py +0 -0
- {hypertopos-0.5.2 → hypertopos-0.6.0}/tests/test_witness_set.py +0 -0
|
@@ -7,6 +7,234 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.6.0] — 2026-04-30
|
|
11
|
+
|
|
12
|
+
### Theme
|
|
13
|
+
|
|
14
|
+
**Architecture-emergent analytics, properly enabled.** Calibration is
|
|
15
|
+
now versioned (multi-epoch retention, sphere format 2.4) and four
|
|
16
|
+
patent-aligned analytics ride on that versioning:
|
|
17
|
+
`compare_calibrations`, intrinsic / extrinsic drift decomposition,
|
|
18
|
+
hidden-influencer matrix, cross-pattern temporal lead-lag. Companion
|
|
19
|
+
features round out the release: edge-derived build-time dimensions,
|
|
20
|
+
joint density gap detection, and a declarative `HopPredicate` motif
|
|
21
|
+
API as power-user escape hatch from the closed-vocab `find_motif`
|
|
22
|
+
registry.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- `GDSNavigator.find_motif_by_hops(pattern_id, hops, *, seed_keys, max_results, score)`
|
|
26
|
+
— declarative motif API. Caller passes a list of `HopPredicate`s
|
|
27
|
+
describing per-hop constraints (`amount_min` / `amount_max` /
|
|
28
|
+
`time_delta_max_hours` / `direction` (`"forward"` / `"reverse"` /
|
|
29
|
+
`"any"`) / `edge_dim_predicates` referencing the per-edge sidecar from
|
|
30
|
+
the edge_dimensions YAML block) and the navigator walks the edge table
|
|
31
|
+
for matching chains of length 1..6. Power-user escape hatch from the
|
|
32
|
+
closed-vocab `find_motif` registry — composes cleanly with
|
|
33
|
+
`pair_edge_count`, `position_in_chain`, `time_since_pair_last_edge`,
|
|
34
|
+
`pair_amount_zscore`, `find_motif_structuring`. New `HopPredicate`
|
|
35
|
+
frozen dataclass in `hypertopos.model.sphere` (re-exported from the
|
|
36
|
+
package root). Seeded queries (`seed_keys` provided) build a scoped
|
|
37
|
+
`AdjacencyIndex` via Lance BTREE-pushdown reads expanding the BFS
|
|
38
|
+
frontier hop-by-hop — the visited subgraph alone is materialised, not
|
|
39
|
+
the full edge table. Unseeded full enumeration uses the cached global
|
|
40
|
+
`AdjacencyIndex` so repeat calls are O(1) per node lookup. Bounded
|
|
41
|
+
MVP — `amount_ratio_to_prev` and
|
|
42
|
+
`require_anomalous_entity` predicates plus k>6 land in a follow-up.
|
|
43
|
+
Scoring path uses the existing `_score_motif_from_edges` infrastructure
|
|
44
|
+
on anchor patterns; on event patterns the score is silently skipped
|
|
45
|
+
(anchor-companion scoring is the follow-up wiring); `score` defaults to
|
|
46
|
+
`False` since only event patterns are accepted today. Direction-aware
|
|
47
|
+
temporal monotonicity: `direction="forward"` enforces strict-increasing
|
|
48
|
+
timestamps, `direction="reverse"` enforces strict-decreasing
|
|
49
|
+
(causal-predecessor chain), `direction="any"` drops the monotonic
|
|
50
|
+
constraint and treats the time window as `|Δt|`. `hops[0].time_delta_max_hours`
|
|
51
|
+
must be `None` (rejected at validation — first hop has no previous
|
|
52
|
+
timestamp); `time_delta_max_hours` on subsequent hops must be positive.
|
|
53
|
+
Sidecar lookup is materialised lazily as `ek→idx` plus per-dim parallel
|
|
54
|
+
lists (rather than a 5M-row dict-of-dicts) — avoids the per-event
|
|
55
|
+
nested-dict allocation when `edge_dim_predicates` are used.
|
|
56
|
+
- `GDSNavigator.find_density_gaps(pattern_id, *, top_n, dim_pairs, bins, alpha, r_min, r_max, sample_size)`
|
|
57
|
+
— joint density gap detection via probability integral transform plus
|
|
58
|
+
independence null on dim pairs. For each pattern dim that survives the
|
|
59
|
+
usability check (≥30 finite values, σ > 0, more than 2 unique levels)
|
|
60
|
+
the empirical CDF transform produces a uniform marginal on `[0, 1]`.
|
|
61
|
+
Pairs whose Pearson `|r|` lands inside the configurable
|
|
62
|
+
`[r_min, r_max]` window become the test set. Per pair the `bins x bins`
|
|
63
|
+
joint histogram is compared against the uniform-independence
|
|
64
|
+
expectation `N / bins²` via per-cell chi² residual; only
|
|
65
|
+
under-populated cells are kept and Benjamini-Hochberg correction is
|
|
66
|
+
applied across all under-populated cells to control FDR. Each flagged
|
|
67
|
+
cell maps back to a named delta-space (z-score) range via
|
|
68
|
+
`ECDFEntry.inverse` — values live on geometry-delta units; raw
|
|
69
|
+
property unit mapping is deferred follow-up.
|
|
70
|
+
Returns a dict with `gaps` (sorted by `expected/observed` ratio
|
|
71
|
+
descending), `excluded_dims` reporting (with reason), and
|
|
72
|
+
`n_pairs_tested`. Anchor patterns only (event patterns are not the
|
|
73
|
+
primary use case); patterns with `< 100` entities raise. Geometry is
|
|
74
|
+
read with column projection narrowed to `["delta"]` and Lance-side
|
|
75
|
+
random sampling via `sample_size` (default `100,000`; pass `0` to
|
|
76
|
+
scan the full population). Delta matrix is built via
|
|
77
|
+
`combine_chunks().values.to_numpy()` reshape rather than
|
|
78
|
+
`to_pylist()` + `np.array`. New `engine.density_gaps` module exposes
|
|
79
|
+
`ECDFEntry`, `compute_density_gaps_for_pair`, `is_usable_for_gap`,
|
|
80
|
+
and `select_pairs_by_corr` as building blocks.
|
|
81
|
+
- Edge-derived dimensions on event patterns:
|
|
82
|
+
`engine.edge_features` exposes five build-time per-edge dim functions
|
|
83
|
+
(`pair_edge_count`, `position_in_chain` with default `min_position=5`
|
|
84
|
+
and parse-time reject below 3, `time_since_pair_last_edge`,
|
|
85
|
+
`pair_amount_zscore` LOW_VAR pairs only, `find_motif_structuring`)
|
|
86
|
+
plus an orchestrator `compute_all_edge_dims(edges, config)` that runs
|
|
87
|
+
every dim listed in a config dict and returns a single Arrow table
|
|
88
|
+
keyed by `event_key`. `EDGE_DIM_KINDS` table publishes the kind tag
|
|
89
|
+
(poisson / gaussian / bernoulli) for each dim so builder calibration
|
|
90
|
+
can pick the right normaliser. New `engine.structuring` module hosts
|
|
91
|
+
the structuring motif enumerator (single-seed +
|
|
92
|
+
all-seeds sweep returning the set of `event_key`s in any motif) for
|
|
93
|
+
the build-time `find_motif_structuring` dim; the navigator runtime
|
|
94
|
+
`_enumerate_structuring` path is unchanged. New
|
|
95
|
+
`EDGE_FEATURES_SCHEMA` constant in `storage._schemas` describes the
|
|
96
|
+
per-edge sidecar Lance dataset layout. New
|
|
97
|
+
`GDSReader.read_edge_features(pattern_id)` reads
|
|
98
|
+
`_gds_meta/edge_features/{pid}/data.lance` (empty table when sidecar
|
|
99
|
+
absent), forward-compatible with the
|
|
100
|
+
HopPredicate.edge_dim_predicates query API planned in a future
|
|
101
|
+
release. YAML surface for the new dims: `patterns.<pid>.edge_dimensions`
|
|
102
|
+
list of either bare dim names or single-key dicts with overrides;
|
|
103
|
+
parsed into `EdgeDimensionsConfig` and attached to `PatternMapping`.
|
|
104
|
+
Builder integration baked the per-event dim values into the event
|
|
105
|
+
polygon `shape_snapshot` (one extra dim per declared entry) and
|
|
106
|
+
extends `dimension_kinds` so downstream Bregman / theta-norm
|
|
107
|
+
calibration treats the new dims natively. Sidecar Lance dataset at
|
|
108
|
+
`_gds_meta/edge_features/{pid}/data.lance` is written alongside,
|
|
109
|
+
forward-compatible with the planned HopPredicate.edge_dim_predicates
|
|
110
|
+
query API. Anchor patterns and event patterns without edge_dimensions
|
|
111
|
+
are byte-identical to builds without the new YAML block.
|
|
112
|
+
- Multi-epoch calibration retention: builder writes per-pattern historical
|
|
113
|
+
fits to `_gds_meta/calibration_history/{pid}/v={N}.json` on every full
|
|
114
|
+
build; sphere format bumps to 2.4 with `pattern.calibration_epoch`,
|
|
115
|
+
`pattern.schema_hash`, and root `calibration_history_policy`. New reader
|
|
116
|
+
API: `read_calibration_fit`, `list_calibration_versions`,
|
|
117
|
+
`read_calibration_history_policy`. New types: `CalibrationFit`,
|
|
118
|
+
`CalibrationNotFoundError`. GC keeps the most-recent K epochs (default 5).
|
|
119
|
+
Schema drift wipes history; 2.3 spheres open read-only and migrate on
|
|
120
|
+
first 0.6.0 build.
|
|
121
|
+
- `GDSNavigator.compare_calibrations(pattern_id, v_from, v_to, top_n, verbose)`
|
|
122
|
+
— per-dimension μ/σ/θ drift between two calibration epochs, with auto-resolve
|
|
123
|
+
defaults and aggregate RMS scalar. New types: `CalibrationDriftReport`,
|
|
124
|
+
`DimensionDrift`. Diagnostic for inspecting calibration shifts after a
|
|
125
|
+
builder rebuild; reads via the multi-epoch retention API added earlier in
|
|
126
|
+
this release.
|
|
127
|
+
- `GDSNavigator.decompose_drift(entity_key, pattern_id, v_from, v_to, timestamp_from, timestamp_to, top_n, verbose)`
|
|
128
|
+
— per-entity intrinsic vs extrinsic decomposition of geometric drift between
|
|
129
|
+
two temporal slices viewed across two calibration epochs. Returns
|
|
130
|
+
`IntrinsicExtrinsicReport` with aggregate L2 displacements, sum-of-squares
|
|
131
|
+
`intrinsic_fraction` bounded `[0, 1]`, and ranked per-dimension breakdown.
|
|
132
|
+
New types: `IntrinsicExtrinsicReport`, `DimensionDecomposition`.
|
|
133
|
+
- `find_drifting_entities` per-entity dict gains 3 additive scalar fields:
|
|
134
|
+
`intrinsic_displacement`, `extrinsic_displacement`, `intrinsic_fraction`.
|
|
135
|
+
Auto-defaults to (oldest retained, current) calibration epochs;
|
|
136
|
+
resolves to `null` per-entity when decomposition isn't computable.
|
|
137
|
+
- `GDSNavigator.find_calibration_influencers(pattern_id, top_n, classify, high_threshold_pct, sample_size, verbose)`
|
|
138
|
+
— per-entity exact leave-one-out impact on coordinate system calibration with
|
|
139
|
+
4-cell classification matrix (hidden / distorter / standard_anomaly / normal).
|
|
140
|
+
Math uses exact leave-one-out via rolling Σs/Σs² (NOT first-order μ-only
|
|
141
|
+
approximation — that approximation makes the "hidden influencer" cell empty
|
|
142
|
+
by construction; corrected during brainstorm). New types:
|
|
143
|
+
`DimensionContribution`, `InfluenceEntry`, `InfluenceReport`. `verbose=True`
|
|
144
|
+
attaches `cascading_flip_count` per entry — count of OTHER entities
|
|
145
|
+
flipping is_anomaly after this entity's removal.
|
|
146
|
+
- `GDSNavigator.find_group_influence(pattern_id, groups)` — caller-
|
|
147
|
+
supplied leave-set-out impact with `reinforcing_factor = total_impact_set
|
|
148
|
+
/ Σ_individuals`. Reinforcing > 1 detects coordinated population-shift
|
|
149
|
+
attacks (collusion rings, duplicate-record contamination); < 1 detects
|
|
150
|
+
canceling sets. New type: `GroupInfluenceReport`.
|
|
151
|
+
- `find_anomalies` MCP per-entity dict gains 2 additive scalar fields:
|
|
152
|
+
`total_impact`, `classification`. Hooked at the MCP-layer polygon→dict
|
|
153
|
+
conversion via `_attach_influence_fields_to_anomaly_entries`; resolves to
|
|
154
|
+
`null` per-entity when pattern is event-type, N<2, or storage backend
|
|
155
|
+
lacks shape-reconstruction prerequisites.
|
|
156
|
+
- `GDSNavigator.find_lead_lag(pattern_a, pattern_b, *, timestamp_from, timestamp_to, cohort, min_epochs, max_lag, fdr_alpha, fdr_method, verbose, entity_key)`
|
|
157
|
+
— cross-pattern temporal lead-lag in population-relative coordinates.
|
|
158
|
+
Population-aggregated centroid drift cross-correlation (primary signal,
|
|
159
|
+
patent-line) plus mean step-magnitude volatility confirmation series with
|
|
160
|
+
`agreement` label. Per-dim D_A × D_B matrix with BH or Storey FDR over
|
|
161
|
+
Bonferroni-over-lag-adjusted p-values (`top_dim_pairs` ranked by ascending
|
|
162
|
+
q-value, full matrix in `per_dim_pairs` when `verbose=True`). Per-entity
|
|
163
|
+
drill-down via `entity_key` parameter. Time alignment by intersection of
|
|
164
|
+
pattern timestamp sets, hard floor `min_epochs=8`, default
|
|
165
|
+
`cohort="fixed"` (panel-clean centroid). Significance: peak Bonferroni
|
|
166
|
+
adjustment for the population peak (`max_corr_threshold` cut-off,
|
|
167
|
+
`bartlett_ci_95` reported alongside, `is_significant` boolean,
|
|
168
|
+
`degenerate_signal` flag set when either centroid drift series has zero
|
|
169
|
+
variance — agreement forced to `"divergent"`). Reliability label mirrors
|
|
170
|
+
`engine.forecast.reliability_label` convention (high N-1 ≥ 24, medium
|
|
171
|
+
≥ 12, else low). New types: `LeadLagReport`, `DimPairLeadLag`. Two-phase
|
|
172
|
+
Lance scan: a column-projected meta read picks the cohort and
|
|
173
|
+
pre-validates a 1 GB tensor budget against `count_geometry_rows` upper
|
|
174
|
+
bounds, so cross-pattern queries over disjoint entity_lines (e.g. AML
|
|
175
|
+
accounts vs account_pairs) raise immediately with an actionable error
|
|
176
|
+
pointing the agent at `cohort='fixed'` or `entity_key=<id>` instead of
|
|
177
|
+
allocating gigabytes. `read_temporal_batched` gains optional `columns=`
|
|
178
|
+
projection.
|
|
179
|
+
|
|
180
|
+
### Fixed
|
|
181
|
+
- `find_anomalies` polygon construction crashed with
|
|
182
|
+
`TypeError: float() argument must be a string or a real number, not 'NoneType'`
|
|
183
|
+
on rows whose `delta_rank_pct` / `bregman_divergence` / `anomaly_confidence`
|
|
184
|
+
column existed but stored a null Arrow value. The reconstruction code
|
|
185
|
+
used `float(row.get(field, 0.0))` guarded by `if field in row`, but
|
|
186
|
+
`row.get` returns `None` (not the default) for null cells, so the
|
|
187
|
+
default never fired. Replaced with explicit
|
|
188
|
+
`None if row.get(field) is None else float(row[field])` across both
|
|
189
|
+
`engine.geometry._reconstruct_polygons_from_geometry_table` and the
|
|
190
|
+
two find_anomalies branches in `navigation.navigator` (full-rank and
|
|
191
|
+
rank-by-property). No agent-visible API change.
|
|
192
|
+
- `aggregate_anomalies`: negative `ungrouped_anomalies` when
|
|
193
|
+
`read_points_batch` returns duplicate rows per entity across Lance
|
|
194
|
+
partitions. Added dedup via `seen_pks` set — each entity is now counted
|
|
195
|
+
in exactly one group regardless of partition layout. `ungrouped_anomalies`
|
|
196
|
+
is now guaranteed non-negative.
|
|
197
|
+
- `detect_neighbor_contamination`: replaced row-by-row `as_py()` loop
|
|
198
|
+
(O(N) PyArrow element access) with vectorised `to_pylist()` for the
|
|
199
|
+
initial geometry scan. Also vectorised the small batch-read in the
|
|
200
|
+
unknown-keys path.
|
|
201
|
+
- `detect_segment_shift`: removed internal `π12_attract_regime_change`
|
|
202
|
+
call used only to populate the non-essential `changepoint_date` output
|
|
203
|
+
field. Calling full changepoint detection (O(N×buckets)) for a single
|
|
204
|
+
optional output field dominated the tool's wall-clock time. Field
|
|
205
|
+
removed from output; callers needing changepoint context should call
|
|
206
|
+
`find_regime_changes` separately. Per-segment Python loop over the
|
|
207
|
+
points table also replaced with vectorised PyArrow ops — `pc.is_in`
|
|
208
|
+
for the anomalous-PK mask, `pc.count_distinct` for the high-
|
|
209
|
+
cardinality early exit, and
|
|
210
|
+
`group_by(...).aggregate([("_pk","count"),("is_anomaly","sum")])` for
|
|
211
|
+
the per-segment counts.
|
|
212
|
+
- `find_high_potential_edges` (`attract_edge_potential`) on event
|
|
213
|
+
patterns with large edge tables: replaced the
|
|
214
|
+
`AdjacencyIndex.from_lance()` rebuild — which read all five edge
|
|
215
|
+
columns and called `to_pylist()` over the full table — with a direct
|
|
216
|
+
two-column PyArrow `group_by(["from_key","to_key"])` aggregate.
|
|
217
|
+
Eliminates the per-call full-table materialisation that dominated
|
|
218
|
+
cost on multi-million-edge event patterns. Added an entity-type ratio
|
|
219
|
+
guard that fires BEFORE the groupby: read the cached
|
|
220
|
+
`_gds_meta/edge_stats/<pid>.json` (built at sphere-write time) for
|
|
221
|
+
`unique_from + unique_to` and divide by `pattern.population_size`
|
|
222
|
+
(already loaded from `sphere.json`). When the ratio is < 1 % the
|
|
223
|
+
edge endpoints belong to a different entity type than the host
|
|
224
|
+
geometry (e.g. zone IDs in a trip-edge pattern whose geometry holds
|
|
225
|
+
trips); the tool returns an empty ranking without opening either the
|
|
226
|
+
Lance edge table or the geometry dataset. New
|
|
227
|
+
`GDSReader.edge_stats_cached(pid)` helper that reads the build-time
|
|
228
|
+
JSON only — never falls back to the live full-table scan that
|
|
229
|
+
`edge_table_stats` performs as a recovery path — so primitives that
|
|
230
|
+
need a cheap guard cannot accidentally trigger a scan when the cache
|
|
231
|
+
is missing.
|
|
232
|
+
- `detect_trajectory_anomaly`: changed `sample_size` default from
|
|
233
|
+
`None` (full population scan) to `10,000`. Full-population temporal
|
|
234
|
+
streaming on large spheres caused multi-minute latency; the default
|
|
235
|
+
now caps at 10k entities. Pass `sample_size=0` to restore the full
|
|
236
|
+
scan.
|
|
237
|
+
|
|
10
238
|
## [0.5.2] — 2026-04-28
|
|
11
239
|
|
|
12
240
|
### 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-04-
|
|
29
|
+
**Change Date:** 2029-04-30
|
|
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.
|
|
3
|
+
Version: 0.6.0
|
|
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
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://arrow.apache.org/docs/python/)
|
|
9
9
|
[](https://github.com/lance-format/lance)
|
|
10
10
|
[](https://modelcontextprotocol.io)
|
|
11
|
-
[](pyproject.toml)
|
|
12
12
|
|
|
13
13
|
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
14
|
|
|
@@ -2,18 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
> Planned direction. Priorities may change based on feedback.
|
|
4
4
|
|
|
5
|
-
## Plan: 0.6.
|
|
5
|
+
## Plan: 0.6.1
|
|
6
6
|
|
|
7
|
-
Theme
|
|
7
|
+
Theme stays **architecture-emergent analytics, properly enabled** — 0.6.1 is a fast-follow patch that finishes the items deferred from the 0.6.0 MVP and lands the natural extensions of S1 / X1 once the 0.6.0 stress test surfaces real workloads.
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- Joint density gap detection — distribution-free "anomaly by absence" via probability integral transform + independence null.
|
|
9
|
+
- **Anchor-pattern aggregation of edge-derived dimensions** — extension of S1: aggregate the per-edge sidecar (`pair_edge_count`, `position_in_chain`, `time_since_pair_last_edge`, `pair_amount_zscore`, `find_motif_structuring`) up to the entity / pair / chain anchor patterns so account-level recall benefits from the same structural signals that lifted transaction-level recall in 0.6.0.
|
|
10
|
+
- **HopPredicate full PBL vectorisation + k>6** — replace the depth-first Python walk with the full Paranjape-Benson-Leskovec δ-temporal-motif algorithm; bumps the bound on hop count and unlocks the larger temporal windows where structuring chains nest inside each other.
|
|
11
|
+
- **`HopPredicate.amount_ratio_to_prev` and `require_anomalous_entity`** — the two predicate vocab items deferred from the 0.6.0 X1 MVP. `amount_ratio_to_prev` lets a hop assert "≤ 60% of the previous hop's amount" without baking absolute thresholds; `require_anomalous_entity` filters intermediates by their anchor-pattern anomaly score.
|
|
12
|
+
- **`find_motif_by_hops` anchor-companion scoring** — wire the existing `_score_motif_from_edges` infrastructure to event-pattern motif results via the anchor-companion lookup, so callers get a `score` field instead of the silent 0.6.0 skip.
|
|
13
|
+
- **Stress-test-driven fixes** — anything the 0.6.0 multi-sphere stress test surfaces (perf regressions, edge-case crashes, MCP serialisation gaps) lands here.
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
The 0.6.1 surface stays additive — no sphere format bump, no new tools beyond the deferred predicate fields. If any item turns out to need a format change, it slips to 0.7.0.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 0.6.0
|
|
20
|
+
|
|
21
|
+
Architecture-emergent analytics, properly enabled — calibration is now **versioned**, and the patent-aligned analytics ride on that versioning.
|
|
22
|
+
|
|
23
|
+
- **0.6.0** — Multi-epoch calibration retention in the builder (sphere format 2.4, back-compat for 2.3) makes calibration `(pattern_id, version)`-keyed; the previous epoch is retained alongside the active one so all subsequent analytics can compare epochs without rebuilding the sphere. `compare_calibrations(pattern_id, v1, v2)` reports per-dim μ/σ drift between any two retained epochs. Intrinsic / extrinsic drift decomposition splits an entity's temporal position change into entity-caused vs population-caused components and ships through `decompose_drift` plus an inline path on `find_drifting_entities`. Hidden-influencer matrix (`find_group_influence`) computes leave-one-out influence on the coordinate system to surface entities whose presence reshapes the population geometry without themselves being anomalous. Cross-pattern temporal lead-lag (`find_lead_lag`) cross-correlates per-entity displacement series across independently calibrated patterns and reports significant leaders / laggards with FDR control. Edge-derived dimensions on event patterns ship five build-time per-edge dim functions (`pair_edge_count`, `position_in_chain`, `time_since_pair_last_edge`, `pair_amount_zscore` LOW_VAR pairs only, `find_motif_structuring`) baked into the polygon shape via the `edge_dimensions:` YAML block; sidecar Lance dataset at `_gds_meta/edge_features/{pid}/data.lance` enables runtime per-edge predicate filtering. Joint density gap detection (`find_density_gaps`) produces distribution-free "anomaly by absence" via probability integral transform plus independence null on dim pairs, with BH-corrected χ² residuals on a 2-D histogram. Declarative motif API (`find_motif_by_hops`) ships as a power-user escape hatch from the closed-vocab `find_motif` registry — `HopPredicate` per-hop predicates (`amount_min` / `amount_max` / `time_delta_max_hours` / `direction` ∈ {forward, reverse, any} / `edge_dim_predicates`) walk chains of length 1..6 over the cached `AdjacencyIndex`, with direction-aware temporal monotonicity (forward = strict-↑, reverse = strict-↓ causal-predecessor, any = `|Δt|` window) and a lazy ek→idx + per-dim sidecar lookup that avoids the per-event nested-dict allocation.
|
|
17
24
|
|
|
18
25
|
---
|
|
19
26
|
|
|
@@ -245,6 +245,7 @@ class RelationSpec:
|
|
|
245
245
|
| `read_edges(pattern_id, from_keys=None, to_keys=None, timestamp_from=None, timestamp_to=None, columns=None)` | Read edge table with Lance BTREE-indexed push-down filters. Returns `pa.Table` |
|
|
246
246
|
| `has_edge_table(pattern_id)` | Check if an edge table exists for a pattern. Returns `bool` |
|
|
247
247
|
| `edge_table_stats(pattern_id)` | Quick statistics (row count, unique entities, timestamp/amount ranges). Returns `dict` or `None` |
|
|
248
|
+
| `edge_stats_cached(pattern_id)` | Read precomputed edge_stats JSON only — never falls back to a live scan. Returns `dict` or `None` if cache missing |
|
|
248
249
|
|
|
249
250
|
### Edge Table (GDSWriter)
|
|
250
251
|
|
|
@@ -254,6 +255,299 @@ class RelationSpec:
|
|
|
254
255
|
| `append_edges(pattern_id, new_edges)` | Append new edges to an existing Lance dataset (streaming build) |
|
|
255
256
|
| `create_edge_indexes(pattern_id)` | Build BTREE indexes on `from_key` and `to_key` after streaming writes |
|
|
256
257
|
|
|
258
|
+
### Edge Features Sidecar (GDSReader)
|
|
259
|
+
|
|
260
|
+
| Method | Description |
|
|
261
|
+
|--------|-------------|
|
|
262
|
+
| `read_edge_features(pattern_id)` | Read the per-edge derived dimension sidecar at `_gds_meta/edge_features/{pattern_id}/data.lance`. Returns an empty `pa.Table` conforming to `EDGE_FEATURES_SCHEMA` when no sidecar exists (the pattern did not declare an `edge_dimensions:` block). Forward-compatibility entry for a future HopPredicate query API; current navigator primitives read the dim values from the polygon `shape_snapshot` directly. |
|
|
263
|
+
|
|
264
|
+
### `GDSNavigator.find_motif_by_hops`
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
nav.find_motif_by_hops(
|
|
268
|
+
pattern_id: str,
|
|
269
|
+
hops: list[HopPredicate],
|
|
270
|
+
*,
|
|
271
|
+
seed_keys: list[str] | None = None,
|
|
272
|
+
max_results: int = 100,
|
|
273
|
+
score: bool = True,
|
|
274
|
+
) -> dict
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Declarative motif API — power-user escape hatch from the closed-vocab `find_motif` registry. Caller passes a list of `HopPredicate`s describing per-hop constraints (`amount_min`, `amount_max`, `time_delta_max_hours`, `direction` (`"forward"` / `"reverse"` / `"any"`), `edge_dim_predicates: dict[str, tuple[op, value]]`); the navigator walks the in-memory `AdjacencyIndex` for matching chains. `seed_keys=None` enumerates from all `from_key` nodes (capped at `max_results`). Returns `{pattern_id, n_results, motifs}` with each motif carrying `nodes`, `edges` (event_keys), `timestamps`, `amounts`, `dim_values_per_hop` (only when `edge_dim_predicates` were used), optional `score` (anchor patterns only) and `score_breakdown`. Raises on anchor pattern (event-only), unknown pattern, empty hops, k>6, `max_results<1`. Bounded MVP — `amount_ratio_to_prev` and `require_anomalous_entity` predicates plus k>6 land in a follow-up.
|
|
278
|
+
|
|
279
|
+
### `HopPredicate` (dataclass, frozen)
|
|
280
|
+
|
|
281
|
+
Fields: `amount_min`, `amount_max`, `time_delta_max_hours`, `direction` (Literal["forward", "reverse", "any"], default `"forward"`), `edge_dim_predicates` (`dict[str, tuple[str, float]]`, e.g. `{"pair_edge_count": (">=", 20.0)}`). Operators: `<`, `<=`, `>`, `>=`, `==`.
|
|
282
|
+
|
|
283
|
+
### Density Gaps Engine (`hypertopos.engine.density_gaps`)
|
|
284
|
+
|
|
285
|
+
| Symbol | Description |
|
|
286
|
+
|--------|-------------|
|
|
287
|
+
| `ECDFEntry(sorted_values)` | Frozen dataclass with `transform(x)` (raw → uniform `[0, 1]`) and `inverse(u)` (uniform → raw). Constructed via `ECDFEntry.from_values(x)` |
|
|
288
|
+
| `is_usable_for_gap(col)` | `(bool, reason)` admissibility check — rejects `too_sparse` (<30 finite), `degenerate` (σ≈0), `bernoulli_like` (≤2 unique) |
|
|
289
|
+
| `select_pairs_by_corr(corr, *, r_min, r_max, top_k)` | Pick top-`top_k` dim pairs with Pearson `|r| ∈ [r_min, r_max]`, sorted by `|r|` desc |
|
|
290
|
+
| `compute_density_gaps_for_pair(u_i, u_j, *, n, bins, alpha)` | Per-cell chi² residuals against uniform-independence expectation, Benjamini-Hochberg correction. Returns under-populated cells with `p_value`, `q_value`, `is_gap` |
|
|
291
|
+
|
|
292
|
+
### `GDSNavigator.find_density_gaps`
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
nav.find_density_gaps(
|
|
296
|
+
pattern_id: str,
|
|
297
|
+
*,
|
|
298
|
+
top_n: int = 10,
|
|
299
|
+
dim_pairs: list[tuple[str, str]] | None = None,
|
|
300
|
+
bins: int = 10,
|
|
301
|
+
alpha: float = 0.05,
|
|
302
|
+
r_min: float = 0.1,
|
|
303
|
+
r_max: float = 0.7,
|
|
304
|
+
) -> dict
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Returns dict with `pattern_id`, `n_entities`, `gaps` (each sorted by
|
|
308
|
+
`ratio` desc with `dim_i` / `dim_j` / `delta_range_i` / `delta_range_j`
|
|
309
|
+
(z-score space — geometry deltas, not raw property values) / `u_range_i`
|
|
310
|
+
/ `u_range_j` / `observed` / `expected` / `q_value` / `correlation`), `excluded_dims`, and `n_pairs_tested`. Raises
|
|
311
|
+
`GDSNavigationError` for unknown pattern, fewer than 100 entities,
|
|
312
|
+
invalid `alpha` / `bins` / `r_min`/`r_max` / `top_n`, or unknown dim
|
|
313
|
+
names in user-supplied `dim_pairs`.
|
|
314
|
+
|
|
315
|
+
### Edge Features Engine (`hypertopos.engine.edge_features`)
|
|
316
|
+
|
|
317
|
+
| Symbol | Description |
|
|
318
|
+
|--------|-------------|
|
|
319
|
+
| `EDGE_DIM_KINDS: dict[str, str]` | Per-dim Bregman kind tag (poisson / gaussian / bernoulli) keyed by dim name |
|
|
320
|
+
| `compute_pair_edge_count(edges)` | edges per `(from_key, to_key)` directed pair |
|
|
321
|
+
| `compute_position_in_chain(edges, *, min_position)` | depth in longest reverse-temporal chain ending at this edge; values below `min_position` zero out |
|
|
322
|
+
| `compute_time_since_pair_last_edge(edges, *, burst_seconds, dormant_seconds)` | seconds since previous edge in same pair; first edge → `dormant_seconds` |
|
|
323
|
+
| `compute_pair_amount_zscore(edges, *, cv_threshold, min_count)` | signed z-score of amount within LOW_VAR pairs |
|
|
324
|
+
| `compute_find_motif_structuring(edges, *, time_window_hours, amt1_min, amt2_max)` | 1.0 if edge participates in any A→B→C→D structuring motif |
|
|
325
|
+
| `compute_all_edge_dims(edges, config)` | orchestrator — runs each dim listed in config, returns Arrow table keyed by `event_key` |
|
|
326
|
+
|
|
327
|
+
### Structuring Engine (`hypertopos.engine.structuring`)
|
|
328
|
+
|
|
329
|
+
| Symbol | Description |
|
|
330
|
+
|--------|-------------|
|
|
331
|
+
| `enumerate_structuring_for_seed(seed, edges, *, time_window_sec, amt1_min, amt2_max, max_instances)` | Single-seed enumeration of A→B→C→D motifs anchored at `seed`; returns list of motif dicts |
|
|
332
|
+
| `enumerate_structuring_event_keys(edges, *, time_window_sec, amt1_min, amt2_max)` | All-seeds sweep — returns the set of `event_key`s participating in any motif. Build-time helper for `compute_find_motif_structuring` |
|
|
333
|
+
|
|
334
|
+
### Edge Dimensions YAML Parser (`hypertopos.builder.mapping`)
|
|
335
|
+
|
|
336
|
+
| Symbol | Description |
|
|
337
|
+
|--------|-------------|
|
|
338
|
+
| `EdgeDimensionsConfig(dims: dict[str, dict])` | Parsed `edge_dimensions:` block — frozen dataclass attached to `PatternMapping.edge_dimensions` |
|
|
339
|
+
| `parse_edge_dimensions(raw_list, *, pattern_type)` | Parse + validate the YAML list of dim entries (bare strings or single-key dicts). Raises `ValueError` on anchor pattern, `min_position < 3`, `cv_threshold` outside `(0, 1]`, `min_count < 2`, non-positive `amt1_min` / `amt2_max` / `time_window_hours`, negative `burst_seconds`, duplicate or unknown dim names, malformed entries |
|
|
340
|
+
|
|
341
|
+
### Calibration History (GDSReader)
|
|
342
|
+
|
|
343
|
+
| Method | Description |
|
|
344
|
+
|--------|-------------|
|
|
345
|
+
| `read_calibration_fit(pattern_id, version=None)` | Load one calibration epoch as a frozen `CalibrationFit` dataclass. `version=None` resolves to the pattern's current `calibration_epoch` from sphere.json. Raises `CalibrationNotFoundError` if the requested version does not exist on disk (trimmed by GC, or schema bump wiped history). For 2.3 spheres, `version=None` and `version=1` both reconstruct a `CalibrationFit` from the inline sphere.json fields; any `version >= 2` raises `CalibrationNotFoundError`. |
|
|
346
|
+
| `list_calibration_versions(pattern_id)` | Return all available calibration epochs for a pattern, ascending. On a 2.3 sphere returns `[1]`. On a 2.4 sphere returns the integers N present in `_gds_meta/calibration_history/{pattern_id}/v={N}.json`. |
|
|
347
|
+
| `read_calibration_history_policy()` | Read the `calibration_history_policy` from sphere.json. Defaults to `{"last_k": 5}` if absent. Raises `ValueError` if `last_k < 1`. |
|
|
348
|
+
|
|
349
|
+
### `CalibrationFit` (dataclass, frozen)
|
|
350
|
+
|
|
351
|
+
Fields: `pattern_id`, `calibration_epoch`, `schema_version`, `schema_hash`,
|
|
352
|
+
`mu`, `sigma_diag`, `theta`, `population_size`, `dimension_weights`,
|
|
353
|
+
`dimension_kinds`, `dim_percentiles`, `group_stats`, `gmm_components`,
|
|
354
|
+
`edge_max`, `computed_at`, `last_calibrated_at`.
|
|
355
|
+
|
|
356
|
+
### `CalibrationNotFoundError` (exception)
|
|
357
|
+
|
|
358
|
+
Extends `GDSError`. Raised by `read_calibration_fit` when the requested epoch
|
|
359
|
+
is not on disk.
|
|
360
|
+
|
|
361
|
+
### Calibration drift
|
|
362
|
+
|
|
363
|
+
#### `GDSNavigator.compare_calibrations(pattern_id, v_from=None, v_to=None, top_n=10, verbose=False) -> CalibrationDriftReport`
|
|
364
|
+
|
|
365
|
+
Per-dimension μ/σ/θ drift between two calibration epochs of the same pattern.
|
|
366
|
+
Auto-resolves: both `None` → second-to-last vs last; only `v_to=None` →
|
|
367
|
+
explicit `v_from` vs latest. Returns a `CalibrationDriftReport` with an
|
|
368
|
+
aggregate `overall_drift_rms` (RMS in σ units), ranked `top_drifted`
|
|
369
|
+
list, and optional full `per_dimension` breakdown when `verbose=True`.
|
|
370
|
+
|
|
371
|
+
Raises `ValueError` on `v_from == v_to`, single-epoch auto-resolve, or
|
|
372
|
+
schema_hash mismatch (cross-schema mu vectors are not dimensionally
|
|
373
|
+
comparable). `CalibrationNotFoundError` bubbles from missing versions.
|
|
374
|
+
|
|
375
|
+
#### `CalibrationDriftReport` (dataclass, frozen)
|
|
376
|
+
|
|
377
|
+
Fields: `pattern_id`, `v_from`, `v_to`, `schema_hash`,
|
|
378
|
+
`population_size_from`, `population_size_to`, `overall_drift_rms`,
|
|
379
|
+
`top_drifted: list[DimensionDrift]`, `per_dimension: list[DimensionDrift] | None`.
|
|
380
|
+
|
|
381
|
+
#### `DimensionDrift` (dataclass, frozen)
|
|
382
|
+
|
|
383
|
+
Fields: `dim_index`, `dim_kind`, `mu_from`, `mu_to`, `mu_delta`,
|
|
384
|
+
`mu_delta_normalized` (z-score: `(mu_to - mu_from) / sigma_from` with
|
|
385
|
+
`sigma_safe` guard for degenerate dims), `sigma_from`, `sigma_to`,
|
|
386
|
+
`sigma_delta`, `theta_from`, `theta_to`, `theta_delta`.
|
|
387
|
+
|
|
388
|
+
### Drift decomposition
|
|
389
|
+
|
|
390
|
+
#### `GDSNavigator.decompose_drift(entity_key, pattern_id, v_from=None, v_to=None, timestamp_from=None, timestamp_to=None, top_n=10, verbose=False) -> IntrinsicExtrinsicReport`
|
|
391
|
+
|
|
392
|
+
Decompose an entity's drift between two temporal slices into intrinsic
|
|
393
|
+
(entity-driven, σ_v1-normalised shape change) and extrinsic (population-
|
|
394
|
+
recalibration-driven, residual) components, viewed across two calibration
|
|
395
|
+
epochs. Auto-resolves: both version args `None` → oldest retained vs current;
|
|
396
|
+
both timestamp args `None` → first vs last temporal slice. Returns an
|
|
397
|
+
`IntrinsicExtrinsicReport` with aggregate L2 displacements, sum-of-squares
|
|
398
|
+
`intrinsic_fraction` in [0, 1], ranked `top_dimensions`, and optional full
|
|
399
|
+
`per_dimension` breakdown when `verbose=True`.
|
|
400
|
+
|
|
401
|
+
Raises `ValueError` on `<2` retained epochs, `v_from == v_to`, schema_hash
|
|
402
|
+
mismatch, `<2` slices in window, or event pattern. `CalibrationNotFoundError`
|
|
403
|
+
bubbles up from missing versions.
|
|
404
|
+
|
|
405
|
+
#### `IntrinsicExtrinsicReport` (dataclass, frozen)
|
|
406
|
+
|
|
407
|
+
Fields: `pattern_id`, `entity_key`, `v_from`, `v_to`, `schema_hash`,
|
|
408
|
+
`timestamp_from`, `timestamp_to`, `intrinsic_displacement`,
|
|
409
|
+
`extrinsic_displacement`, `total_displacement`, `intrinsic_fraction`,
|
|
410
|
+
`top_dimensions: list[DimensionDecomposition]`, `per_dimension: list[DimensionDecomposition] | None`.
|
|
411
|
+
|
|
412
|
+
#### `DimensionDecomposition` (dataclass, frozen)
|
|
413
|
+
|
|
414
|
+
Fields: `dim_index`, `dim_kind`, `dim_label`, `total` (delta_b - delta_a),
|
|
415
|
+
`intrinsic` ((s_b - s_a) / σ_v1), `extrinsic` (residual), `intrinsic_fraction`
|
|
416
|
+
(per-dim sum-of-squares ratio in [0, 1]).
|
|
417
|
+
|
|
418
|
+
### Influence analysis
|
|
419
|
+
|
|
420
|
+
#### `GDSNavigator.find_calibration_influencers(pattern_id, top_n=10, classify="hidden", high_threshold_pct=90.0, sample_size=None, verbose=False) -> InfluenceReport`
|
|
421
|
+
|
|
422
|
+
Detect entities with high influence on coordinate system calibration. Classifies
|
|
423
|
+
into the 4-cell influence × anomaly matrix (hidden / distorter /
|
|
424
|
+
standard_anomaly / normal); default `classify="hidden"` returns top_n entities
|
|
425
|
+
with high `total_impact` but low anomaly score (the patent's primary detection
|
|
426
|
+
cell — entities defining what 'normal' means without being detected as anomalous).
|
|
427
|
+
|
|
428
|
+
Math: exact leave-one-out via rolling Σs/Σs². For each entity E:
|
|
429
|
+
- `μ_without[i] = (Σs[i] - s_E[i]) / (N-1)`
|
|
430
|
+
- `σ²_without[i] = (Σs²[i] - s_E[i]²) / (N-1) - μ_without[i]²`
|
|
431
|
+
- `mu_impact = ‖(μ_full - μ_without) / σ_full_safe‖`
|
|
432
|
+
- `sigma_impact = ‖(σ_full - σ_without) / σ_full_safe‖`
|
|
433
|
+
- `total_impact = sqrt(mu_impact² + sigma_impact²)`
|
|
434
|
+
|
|
435
|
+
Classification: `high_impact = total_impact ≥ percentile(total_impact, high_threshold_pct)`;
|
|
436
|
+
`high_anomaly = ‖δ(E)‖ ≥ θ_norm`.
|
|
437
|
+
|
|
438
|
+
`verbose=True` attaches per-entry `cascading_flip_count` — count of
|
|
439
|
+
OTHER entities flipping `is_anomaly` after this entity's removal.
|
|
440
|
+
|
|
441
|
+
Raises `ValueError` on event pattern, `N<2`, `high_threshold_pct ∉ (0, 100)`,
|
|
442
|
+
invalid `classify`, or `top_n ∉ [1, 50]`.
|
|
443
|
+
|
|
444
|
+
#### `GDSNavigator.find_group_influence(pattern_id, groups) -> list[GroupInfluenceReport]`
|
|
445
|
+
|
|
446
|
+
Caller-supplied per-group leave-set-out impact. For each input
|
|
447
|
+
group, computes the set's collective μ/σ shift plus
|
|
448
|
+
`reinforcing_factor = total_impact_set / Σ_individuals`. Reinforcing > 1
|
|
449
|
+
indicates members pull together (coordinated injection or duplicate-record
|
|
450
|
+
contamination); < 1 indicates canceling (members offset each other).
|
|
451
|
+
|
|
452
|
+
Returns `list[GroupInfluenceReport]` (input order preserved).
|
|
453
|
+
|
|
454
|
+
Raises `ValueError` on event pattern, `N<3`, empty groups list, group with
|
|
455
|
+
`<2` members, group `≥ N`, missing entity_key, duplicate entity in group, or
|
|
456
|
+
undefined reinforcing factor (sum of individual impacts = 0).
|
|
457
|
+
|
|
458
|
+
#### Additive surface on `find_anomalies` MCP tool
|
|
459
|
+
|
|
460
|
+
Each per-entity polygon dict gains 2 scalar fields: `total_impact` (M4
|
|
461
|
+
leave-one-out scalar) and `classification` (`"hidden"` / `"distorter"` /
|
|
462
|
+
`"standard_anomaly"` / `"normal"`). Resolves to `null` per-entry when pattern
|
|
463
|
+
is event-type, `N<2`, or storage backend lacks shape-reconstruction
|
|
464
|
+
prerequisites — keeps batch response intact.
|
|
465
|
+
|
|
466
|
+
#### `InfluenceReport` (dataclass, frozen)
|
|
467
|
+
|
|
468
|
+
Fields: `pattern_id`, `pattern_version`, `population_size`, `high_threshold_pct`,
|
|
469
|
+
`total_impact_threshold`, `theta_norm`, `classify_filter`,
|
|
470
|
+
`cell_counts: dict[str, int]`, `entries: list[InfluenceEntry]`.
|
|
471
|
+
|
|
472
|
+
#### `InfluenceEntry` (dataclass, frozen)
|
|
473
|
+
|
|
474
|
+
Fields: `entity_key`, `mu_impact`, `sigma_impact`, `total_impact`, `delta_norm`,
|
|
475
|
+
`classification`, `top_dim_contributions: list[DimensionContribution]`,
|
|
476
|
+
`cascading_flip_count: int | None`.
|
|
477
|
+
|
|
478
|
+
#### `GroupInfluenceReport` (dataclass, frozen)
|
|
479
|
+
|
|
480
|
+
Fields: `pattern_id`, `pattern_version`, `group_index`, `member_count`,
|
|
481
|
+
`members: list[str]`, `mu_impact_set`, `sigma_impact_set`, `total_impact_set`,
|
|
482
|
+
`sum_individual_impacts`, `reinforcing_factor`,
|
|
483
|
+
`top_dim_contributions: list[DimensionContribution]`.
|
|
484
|
+
|
|
485
|
+
#### `DimensionContribution` (dataclass, frozen)
|
|
486
|
+
|
|
487
|
+
Fields: `dim_index`, `dim_kind`, `dim_label`, `mu_shift`, `sigma_shift`,
|
|
488
|
+
`contribution` (sqrt(mu_shift² + sigma_shift²)).
|
|
489
|
+
|
|
490
|
+
### Cross-pattern temporal lead-lag
|
|
491
|
+
|
|
492
|
+
#### `Navigator.find_lead_lag`
|
|
493
|
+
|
|
494
|
+
```python
|
|
495
|
+
nav.find_lead_lag(
|
|
496
|
+
pattern_a: str,
|
|
497
|
+
pattern_b: str,
|
|
498
|
+
*,
|
|
499
|
+
timestamp_from: str | None = None,
|
|
500
|
+
timestamp_to: str | None = None,
|
|
501
|
+
cohort: Literal["fixed", "all"] = "fixed",
|
|
502
|
+
min_epochs: int = 8,
|
|
503
|
+
max_lag: int | None = None,
|
|
504
|
+
fdr_alpha: float = 0.05,
|
|
505
|
+
fdr_method: Literal["bh", "storey"] = "storey",
|
|
506
|
+
verbose: bool = False,
|
|
507
|
+
entity_key: str | None = None,
|
|
508
|
+
) -> LeadLagReport
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Cross-pattern temporal lead-lag in population-relative coordinates. Both
|
|
512
|
+
patterns must be `pattern_type="anchor"` and (effectively) over the same
|
|
513
|
+
entity space — `cohort="fixed"` raises empty-cohort otherwise. Time alignment
|
|
514
|
+
uses the intersection of pattern timestamp sets; `min_epochs` is a hard
|
|
515
|
+
floor. Default `max_lag = (N - 1) // 4`.
|
|
516
|
+
|
|
517
|
+
Three nested answer levels:
|
|
518
|
+
|
|
519
|
+
1. **Population scalar.** `lag` and `correlation` from the cross-correlation
|
|
520
|
+
of differenced population centroid drift series. Bonferroni-adjusted peak
|
|
521
|
+
threshold (`max_corr_threshold` field) is the cut-off used by
|
|
522
|
+
`is_significant`.
|
|
523
|
+
2. **Per-dim D_A × D_B matrix.** `top_dim_pairs` (top 10 by ascending
|
|
524
|
+
q-value, ties broken by descending |corr|) with full sorted matrix in
|
|
525
|
+
`per_dim_pairs` when `verbose=True`. BH or Storey FDR applied to
|
|
526
|
+
Bonferroni-over-lag-adjusted p-values across all `D_A * D_B` pairs.
|
|
527
|
+
3. **Per-entity drill-down.** Pass `entity_key` to replace the population
|
|
528
|
+
centroid by that entity's own delta trajectory.
|
|
529
|
+
|
|
530
|
+
#### `LeadLagReport` (dataclass, frozen)
|
|
531
|
+
|
|
532
|
+
Fields: `pattern_a`, `pattern_b`, `entity_key: str | None`, `n_epochs_used`,
|
|
533
|
+
`n_dropped_a`, `n_dropped_b`, `cohort_size`, `cohort_dropped: int | None`,
|
|
534
|
+
`timestamp_from: datetime`, `timestamp_to: datetime`, `schema_hash_a`,
|
|
535
|
+
`schema_hash_b`, `lag`, `correlation`,
|
|
536
|
+
`centroid_drift_series_a/b: list[float]`, `lag_volatility`,
|
|
537
|
+
`correlation_volatility`, `volatility_series_a/b: list[float]`, `agreement`
|
|
538
|
+
(`"strong"` / `"weak"` / `"divergent"`), `bartlett_ci_95`,
|
|
539
|
+
`max_corr_threshold`, `is_significant`, `fdr_alpha`, `fdr_method`,
|
|
540
|
+
`n_dim_pairs`, `n_significant_pairs`, `top_dim_pairs: list[DimPairLeadLag]`,
|
|
541
|
+
`per_dim_pairs: list[DimPairLeadLag] | None`, `reliability`
|
|
542
|
+
(`"high"` / `"medium"` / `"low"`), `max_lag`,
|
|
543
|
+
`correlation_by_lag: list[float]`, `coverage_warning`, `degenerate_signal`.
|
|
544
|
+
|
|
545
|
+
#### `DimPairLeadLag` (dataclass, frozen)
|
|
546
|
+
|
|
547
|
+
Fields: `dim_index_a`, `dim_index_b`, `dim_label_a: str | None`,
|
|
548
|
+
`dim_label_b: str | None`, `lag`, `correlation`, `p_value`, `q_value`,
|
|
549
|
+
`is_significant`.
|
|
550
|
+
|
|
257
551
|
---
|
|
258
552
|
|
|
259
553
|
## Model Objects
|
|
@@ -113,7 +113,7 @@ For a denser top-down view, this stack is a useful shorthand:
|
|
|
113
113
|
|
|
114
114
|
```text
|
|
115
115
|
┌─────────────────────────────────────┐
|
|
116
|
-
│ MCP Server (hypertopos-mcp) │
|
|
116
|
+
│ MCP Server (hypertopos-mcp) │ 80 tools, smart detection mode
|
|
117
117
|
├─────────────────────────────────────┤
|
|
118
118
|
│ PassiveScanner │ multi-source batch screening
|
|
119
119
|
├─────────────────────────────────────┤
|
|
@@ -175,7 +175,7 @@ graph LR
|
|
|
175
175
|
ST --> E["Engine\ngeometry, delta,\nclustering, DTW"]
|
|
176
176
|
E --> N["Navigator\nπ1–π12, detect_*"]
|
|
177
177
|
N --> PS["PassiveScanner\nmulti-source\nbatch screening"]
|
|
178
|
-
N --> MCP["MCP Server\
|
|
178
|
+
N --> MCP["MCP Server\n72 tools\nstdio transport"]
|
|
179
179
|
MCP --> Agent["AI Agent"]
|
|
180
180
|
CT["CalibrationTracker\nonline drift\nWelford stats"] -.-> E
|
|
181
181
|
M["Manifest\nMVCC version pin"] -.-> ST
|