hypertopos 0.3.0__tar.gz → 0.3.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.3.0 → hypertopos-0.3.2}/CHANGELOG.md +43 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/LICENSE.md +1 -1
- {hypertopos-0.3.0 → hypertopos-0.3.2}/PKG-INFO +1 -1
- hypertopos-0.3.2/README.md +69 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/ROADMAP.md +25 -9
- {hypertopos-0.3.0 → hypertopos-0.3.2}/SECURITY.md +2 -1
- hypertopos-0.3.2/docs/README.md +22 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/api-reference.md +5 -4
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/concepts.md +10 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/configuration.md +35 -0
- hypertopos-0.3.2/docs/introduction.md +57 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/quickstart.md +5 -1
- hypertopos-0.3.2/hypertopos/builder/_scatter.py +72 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/builder/_stats.py +25 -1
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/builder/_writer.py +2 -2
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/builder/builder.py +919 -324
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/builder/derived.py +291 -20
- hypertopos-0.3.2/hypertopos/builder/dim_blocks.py +110 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/cli/build.py +9 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/cli/schema.py +45 -0
- hypertopos-0.3.2/hypertopos/engine/fdr.py +89 -0
- hypertopos-0.3.2/hypertopos/engine/heredity.py +109 -0
- hypertopos-0.3.2/hypertopos/engine/selection.py +102 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/navigation/navigator.py +332 -5
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/storage/writer.py +7 -5
- {hypertopos-0.3.0 → hypertopos-0.3.2}/pyproject.toml +1 -1
- hypertopos-0.3.2/tests/test_builder_chunking.py +93 -0
- hypertopos-0.3.2/tests/test_builder_contagion.py +99 -0
- hypertopos-0.3.2/tests/test_builder_rolling_z.py +83 -0
- hypertopos-0.3.2/tests/test_builder_scatter.py +177 -0
- hypertopos-0.3.2/tests/test_builder_temporal_perf.py +309 -0
- hypertopos-0.3.2/tests/test_dim_blocks.py +324 -0
- hypertopos-0.3.2/tests/test_fdr.py +128 -0
- hypertopos-0.3.2/tests/test_heredity.py +320 -0
- hypertopos-0.3.2/tests/test_navigator_fdr_select.py +540 -0
- hypertopos-0.3.2/tests/test_selection.py +131 -0
- hypertopos-0.3.0/README.md +0 -121
- hypertopos-0.3.0/docs/README.md +0 -38
- {hypertopos-0.3.0 → hypertopos-0.3.2}/.github/workflows/publish-pypi.yml +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/.gitignore +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/architecture.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/LICENSE +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/README.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/README.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/SUMMARY.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_accounts.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_cpty_banks.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_districts.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_loan_accounts.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_operations.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_orders.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_transactions.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/prepare_tx_types.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/berka/sphere/sphere.yaml +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/README.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/_layer2_cross_eval.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/_layer3_cross_eval.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_accounts.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_currencies.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_payment_formats.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_transactions.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/hi_small_sphere/sphere.yaml +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/layer1_passive_scan.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/layer2_xgboost_sweep.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/layer3_typology_filter.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_accounts.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_currencies.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_payment_formats.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/li_small_sphere/prepare_transactions.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/li_small_sphere/sphere.yaml +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/ibm-aml/passive-scan-calibration.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/README.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/SUMMARY.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-c-report.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-settings.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/sphere/prepare_trips.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmark/nyc-taxi/sphere/sphere.yaml +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/benchmarks.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/data-format.md +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/build-pipeline.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/core-objects.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/cutting-plane.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/directory-layout.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/hypertopos-overview.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/model-lifecycle.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/navigation-primitives.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/docs/images/three-scales.svg +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/builder/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/builder/mapping.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/cli/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/cli/__main__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/cli/info.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/cli/main.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/cli/sources.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/aggregation.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/calibration.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/chains.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/composition.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/forecast.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/geometry.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/invalidation.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/investigation.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/engine/lance_sql_agg.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/model/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/model/manifest.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/model/objects.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/model/sphere.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/navigation/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/navigation/scanner.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/sphere.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/storage/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/storage/_schemas.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/storage/cache.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/storage/reader.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/utils/__init__.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/hypertopos/utils/arrow.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/.gitkeep +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/conftest.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/fixtures/generate_fixtures.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_aggregation_engine.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_arrow_utils.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_builder.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_builder_alias.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_builder_derived.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_builder_edges.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_cache.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_calibration.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_chains.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_cli.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_cli_alias.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_cli_sources.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_compaction.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_composition.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_continuous_event_dims.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_data_quality_prop.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_detection.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_edge_table.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_engine.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_forecast.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_fts.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_geometric_path.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_geometry_stats.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_hybrid_search.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_incremental.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_invalidation.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_investigation.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_lance_sql_agg.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_mapping.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_model.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_mvcc_session.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_navigation.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_navigator_alerts.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_observability.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_precision_stack.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_reputation.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_row_id_cache.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_scanner.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_sphere.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_sphere_overview_perf.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_stats_guard.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_storage.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_streaming_reads.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_temporal_centroids.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_trajectory_ann.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_witness_cohort.py +0 -0
- {hypertopos-0.3.0 → hypertopos-0.3.2}/tests/test_witness_set.py +0 -0
|
@@ -5,6 +5,49 @@ All notable changes to hypertopos will be documented in this file.
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.3.2] — 2026-04-13
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Generalized dimension blocks (g/t/s). `geo_properties`, `metric_properties`, and `semantic_dim` optional fields on pattern config. Geographic and metric blocks use empirical mu/sigma normalization; semantic block applies SVD-based PCA dimensionality reduction. Dimension names are prefixed (`g:lat`, `t:balance`, `s:pc0`) for identification in `explain_anomaly` output. All optional — existing configs unaffected.
|
|
15
|
+
- `find_novel_entities(pattern_id)` navigator method. Ranks entities by geometric deviation from neighbor-expected position using edge table adjacency. High novelty_score = entity doesn't behave like its neighborhood. Requires a pattern with an edge table.
|
|
16
|
+
- `engine/heredity.py` module: `compute_expected_delta`, `compute_novelty_score`, `compute_novelty_decomposition` — pure NumPy scoring functions for geometric heredity.
|
|
17
|
+
- `builder/dim_blocks.py` module: `normalize_metric_block`, `normalize_geo_block`, `normalize_semantic_block` — normalization helpers for generalized dimension blocks.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Temporal build: `reciprocity` and `counterpart_overlap` graph features replaced per-window Arrow fallback with NumPy integer-encoded set operations. One-time entity encoding via `pc.index_in`, then per-bucket `np.unique` + `np.intersect1d` + `np.bincount`. Eliminates Arrow table construction and hash joins per temporal window.
|
|
22
|
+
- Temporal build: `in_degree`/`out_degree` use single-pass Arrow `group_by([key, bucket]).aggregate([count_distinct])` across all buckets. Eliminates O(n_buckets) per-window iterations.
|
|
23
|
+
- Temporal build: chunked path pre-computes derived-dim groupby tables and graph feature tensors once before the entity chunk loop. Eliminates redundant full event-table scans per chunk.
|
|
24
|
+
- Geometry build: per-dimension BTREE scalar indices (`delta_dim_0` through `delta_dim_N`) replaced with Lance zone-map filtering. Saves sequential index builds proportional to dimension count.
|
|
25
|
+
- Geometry build: streaming path keeps `is_anomaly` in memory instead of re-reading from Lance after finalization.
|
|
26
|
+
- Lance compaction `target_rows_per_fragment` raised from 1M to 4M for temporal, geometry, and geometry chunk finalization — fewer fragments to merge during compaction.
|
|
27
|
+
|
|
28
|
+
## [0.3.1] — 2026-04-12
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Benjamini-Hochberg FDR control on attract primitives. `fdr_alpha` parameter on `pi5_attract_anomaly`, `pi6_attract_boundary`, `pi7_attract_hub`, and `pi9_attract_drift` applies BH multiple-testing correction to empirical-null p-values derived from delta rank percentile. Entities with `q_value > alpha` are removed before the top-N step. Guarantees `E[FDR] <= alpha`. Each retained entity carries a `q_value` — the minimum alpha at which it would still be retained. Off by default; legacy behavior preserved when omitted.
|
|
33
|
+
- Diverse selection via submodular facility location. `select="diverse"` on the same four primitives replaces top-N ranking with a lazy-greedy maximisation of the facility location objective over pairwise cosine similarity of delta vectors. Achieves `(1 - 1/e)` approximation to the optimal K-subset. Each selected entity reports a `representativeness` count — how many population members are nearest to it among the selected set. Covers the geometric space of the result set instead of clustering around a single extreme region.
|
|
34
|
+
- `engine/fdr.py` module: `benjamini_hochberg`, `empirical_p_values_from_rank`, `q_values_from_p_values` — pure NumPy, no state, no I/O.
|
|
35
|
+
- `engine/selection.py` module: `lazy_greedy_facility_location`, `compute_similarity_matrix` — pure NumPy, Nemhauser-Wolsey-Fisher optimality guarantee.
|
|
36
|
+
|
|
37
|
+
### Changed
|
|
38
|
+
|
|
39
|
+
- Build pipeline: derived dimension scatter loops replaced with Arrow `pc.index_in` + numpy fancy indexing. Eliminates per-row Python iteration in both static (`compute_derived_batch`) and temporal (`_precompute_shape_tensor`) build paths.
|
|
40
|
+
- Build pipeline: contagion stats computation (`_build_contagion_stats`) rewritten from Python loops over edge table to Arrow group_by + join. Eliminates O(E+N) Python iterations.
|
|
41
|
+
- Build pipeline: rolling z-score (`_compute_max_rolling_z`) uses Welford online algorithm. Reduces time complexity from O(n_buckets² × N × D) to O(n_buckets × N × D) and memory from O(N × n_buckets × D) to O(N × D).
|
|
42
|
+
- Build pipeline: temporal Arrow assembly uses single numpy reshape instead of per-bucket table construction loop.
|
|
43
|
+
- Build pipeline: graph dims temporal computation uses pre-sorted event table with `searchsorted` for O(1) per-bucket slicing instead of O(E) scan per bucket.
|
|
44
|
+
- Build pipeline: `build_temporal` adaptively chunks entities when the shape tensor would exceed available RAM. Auto-detects memory via platform APIs, falls back to 4 GB budget. Single-pass path (zero overhead) when tensor fits.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- `empirical_p_values_from_rank` used a `1/N` floor where N was the input array length. When the input was a small `top_n` slice (e.g. 5 entities), the floor clipped all p-values to 0.2, causing BH to reject everything at `alpha=0.05`. Now uses a fixed `1e-10` floor — the rank percentile is already computed against the full population, so the floor only needs to prevent exact zero for numerical stability.
|
|
49
|
+
- `π7_attract_hub` and `π9_attract_drift` FDR computed p-values as `(N-i)/N` where N was the `top_n` result count (e.g. 10), making the best possible p-value `1/top_n` — mathematically unable to pass BH at any `alpha < 1/top_n`. Now uses population-level rank percentiles: hubs use the empirical CDF from the full scores array, drift uses the rank position relative to the total pre-truncation population.
|
|
50
|
+
|
|
8
51
|
## [0.3.0] — 2026-04-12
|
|
9
52
|
|
|
10
53
|
> **Theme:** Lance 3.x perf upgrade. Aggregate engine moves from a persistent subprocess worker plus an optional external DataFusion package to the Lance scanner's built-in DataFusion executor via `LanceDataset.sql(...)`. The build-time contagion stats table replaces the runtime edge-table replay in the graph contagion scanner. Format 2.2 is the new write default.
|
|
@@ -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-13
|
|
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.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# hypertopos
|
|
2
|
+
|
|
3
|
+
> Understand the structure of your data — without training machine learning models.
|
|
4
|
+
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](LICENSE.md)
|
|
7
|
+
[](https://doi.org/10.5281/zenodo.19482069)
|
|
8
|
+
[](https://arrow.apache.org/docs/python/)
|
|
9
|
+
[](https://github.com/lance-format/lance)
|
|
10
|
+
[](https://modelcontextprotocol.io)
|
|
11
|
+
[](pyproject.toml)
|
|
12
|
+
|
|
13
|
+
hypertopos transforms relational data into a geometric space where every entity gets a coordinate derived from its relationships. Distance from the population center reveals anomalies. Proximity between entities reveals similarity. Movement over time reveals drift. No training, no labels — geometry is computed from the data.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install hypertopos
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## What you can do with it
|
|
22
|
+
|
|
23
|
+
- Detect anomalies without training ML models or labeling data
|
|
24
|
+
- Discover clusters and structural archetypes
|
|
25
|
+
- Track behavioral drift over time
|
|
26
|
+
- Compare populations to find what differentiates them
|
|
27
|
+
- Explore datasets with AI agents that navigate rather than query
|
|
28
|
+
|
|
29
|
+
## How it works
|
|
30
|
+
|
|
31
|
+
You describe your data in YAML — entity types, sources, relationships. hypertopos computes population statistics and produces a sphere: pre-computed geometry stored in Apache Arrow format.
|
|
32
|
+
|
|
33
|
+
Agents (or Python code) open the sphere and navigate it using twelve primitives that cover movement, clustering, anomaly detection, population comparison, and temporal analysis. Each step is stateful — where you are determines what you see next.
|
|
34
|
+
|
|
35
|
+
For the full picture: [Introduction](docs/introduction.md) · [Core Concepts](docs/concepts.md) · [Quick Start](docs/quickstart.md)
|
|
36
|
+
|
|
37
|
+
## Benchmarks
|
|
38
|
+
|
|
39
|
+
Validated on three domains with the same engine, zero domain rules, zero labels:
|
|
40
|
+
|
|
41
|
+
| Domain | Dataset | Key result |
|
|
42
|
+
|--------|---------|------------|
|
|
43
|
+
| Banking | Berka (Czech, real data) | 85.5% recall on loan defaults |
|
|
44
|
+
| AML | IBM AML (synthetic) | 80.4% recall, zero labels |
|
|
45
|
+
| Transport | NYC Yellow Taxi (7.6M trips) | 8/8 anomaly categories detected |
|
|
46
|
+
|
|
47
|
+
Benchmark scripts and data preparation are included. Results are reproducible. Numbers are from the pre-0.1.0 validation run and have not been re-evaluated against recent releases.
|
|
48
|
+
|
|
49
|
+
Full results: [Benchmarks](docs/benchmarks.md)
|
|
50
|
+
|
|
51
|
+
## Documentation
|
|
52
|
+
|
|
53
|
+
| | |
|
|
54
|
+
|---|---|
|
|
55
|
+
| [Introduction](docs/introduction.md) | The idea and where it stands |
|
|
56
|
+
| [Quick Start](docs/quickstart.md) | Install, build, navigate |
|
|
57
|
+
| [Core Concepts](docs/concepts.md) | Mathematical foundation |
|
|
58
|
+
| [Configuration](docs/configuration.md) | Sphere builder YAML reference |
|
|
59
|
+
| [API Reference](docs/api-reference.md) | Python API |
|
|
60
|
+
| [Data Format](docs/data-format.md) | On-disk storage format |
|
|
61
|
+
| [Architecture](docs/architecture.md) | Package layers and design |
|
|
62
|
+
|
|
63
|
+
## Status
|
|
64
|
+
|
|
65
|
+
Research-stage project. Working code, reproducible benchmarks, active development. API may change.
|
|
66
|
+
|
|
67
|
+
## License
|
|
68
|
+
|
|
69
|
+
[Business Source License 1.1](LICENSE.md). Free for internal use, development, testing, and research. See LICENSE.md for details.
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
> Planned direction. Priorities may change based on feedback.
|
|
4
4
|
|
|
5
|
-
## Current: 0.3.
|
|
5
|
+
## Current: 0.3.1
|
|
6
|
+
|
|
7
|
+
FDR control, diverse anomaly summarisation, and build pipeline optimization — Benjamini-Hochberg multiple-testing correction, submodular facility location, vectorized build with adaptive memory chunking. See CHANGELOG.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 0.3.0
|
|
6
12
|
|
|
7
13
|
Lance 3.x perf upgrade — aggregate engine rewritten around Lance SQL, precomputed contagion stats, format 2.2, contagion anchor resolution fix, edge table auto-detect fix. See CHANGELOG.
|
|
8
14
|
|
|
@@ -32,25 +38,35 @@ First public release — full GDS stack, π1–π12, builder, MCP server, valida
|
|
|
32
38
|
|
|
33
39
|
---
|
|
34
40
|
|
|
35
|
-
##
|
|
41
|
+
## 0.3.2
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
Builder intelligence — smarter geometry, faster builds.
|
|
38
44
|
|
|
39
|
-
- **
|
|
40
|
-
- **
|
|
41
|
-
- **
|
|
45
|
+
- **Temporal build performance** — NumPy graph features, chunked pre-computation, Lance compact tuning. Combined speedup across benchmark spheres.
|
|
46
|
+
- **Geometry build performance** — per-dimension BTREE indices replaced with zone-map filtering, streaming re-read eliminated.
|
|
47
|
+
- **Generalized (d+m+g+t+s) dimension blocks** — geographic, metric, and semantic dimension support in the builder beyond the current anchor/event model.
|
|
48
|
+
- **Geometric heredity for cold-start entities** — `find_novel_entities` scores entities by deviation from neighbor-expected geometry using edge table adjacency.
|
|
42
49
|
|
|
43
50
|
---
|
|
44
51
|
|
|
45
|
-
## Plan: 0.3.
|
|
52
|
+
## Plan: 0.3.3
|
|
53
|
+
|
|
54
|
+
Agent navigation policy — skill-level changes to make agents effective navigators of geometric space.
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
- **Graph confirmation flow** — add witness_cohort + contagion confirmation step to investigator and fraud-investigator skills after explain_anomaly.
|
|
57
|
+
- **Investigation memory** — instruct agents to track checked entities, dead ends, and promising leads in conversation state.
|
|
58
|
+
- **Failure mode guards** — depth limits, anomaly strength thresholds, and force-switch after N consecutive calls on same entity.
|
|
59
|
+
- **Decision scoring** — priority queue heuristic (anomaly_strength + graph_support + temporal_signal + novelty) for iterative re-ranking of investigation targets.
|
|
48
60
|
|
|
49
61
|
---
|
|
50
62
|
|
|
51
63
|
## Plan: 0.4.0
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
Edge-derived geometry — the edge table becomes a first-class geometric citizen.
|
|
66
|
+
|
|
67
|
+
- **Edge-derived dimensions on event patterns** — degree, flow, velocity computed at build time as geometric dimensions. Edges feed geometry.
|
|
68
|
+
- **`find_motif` — structural pattern matching on edge table** — per-hop predicates, new navigation primitive for subgraph patterns invisible to flat queries.
|
|
69
|
+
- **Geometric edge potential** — relationship anomaly scoring via endpoint geometric distance. Geometry feeds edge scoring.
|
|
54
70
|
|
|
55
71
|
---
|
|
56
72
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Security
|
|
2
2
|
|
|
3
|
-
## Current version: 0.3.
|
|
3
|
+
## Current version: 0.3.2
|
|
4
4
|
|
|
5
5
|
hypertopos runs locally. No network services, no auth layer, no multi-tenancy.
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@ hypertopos runs locally. No network services, no auth layer, no multi-tenancy.
|
|
|
9
9
|
- **Pickle files** — chain cache (`.cache/chains_*.pkl`) uses pickle. Don't load cache files from untrusted sources.
|
|
10
10
|
- **Sphere paths** — `HyperSphere.open(path)` reads from the local filesystem. Don't point it at user-controlled paths without validation.
|
|
11
11
|
- **MCP server** — communicates over stdio. Not designed for network exposure.
|
|
12
|
+
- **SQL injection protection** — `lance_sql_agg` validates user-controlled SQL inputs. Filter values are escaped (doubles single quotes, rejects backslash and ASCII control chars). Column identifiers are validated against `^[A-Za-z_][A-Za-z0-9_]*$`.
|
|
12
13
|
|
|
13
14
|
## Reporting
|
|
14
15
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# hypertopos Documentation
|
|
2
|
+
|
|
3
|
+
hypertopos turns relational data into navigable geometric space — a coordinate system where structure, anomalies, and change become directly observable.
|
|
4
|
+
|
|
5
|
+
**New here?** Read the [Introduction](introduction.md) for the idea, then [Quick Start](quickstart.md) to try it.
|
|
6
|
+
|
|
7
|
+
**Want depth?** [Core Concepts](concepts.md) covers the mathematical foundation, objects, and vocabulary.
|
|
8
|
+
|
|
9
|
+
**Building something?** Jump to the reference docs below.
|
|
10
|
+
|
|
11
|
+
## Documents
|
|
12
|
+
|
|
13
|
+
| Document | What it covers |
|
|
14
|
+
|----------|----------------|
|
|
15
|
+
| [Introduction](introduction.md) | The idea, how it works, where it stands |
|
|
16
|
+
| [Quick Start](quickstart.md) | Install, build a sphere, connect an agent |
|
|
17
|
+
| [Core Concepts](concepts.md) | Mathematical foundation, objects, population model |
|
|
18
|
+
| [Architecture](architecture.md) | Package layers, runtime flow, design decisions |
|
|
19
|
+
| [Configuration](configuration.md) | Sphere builder YAML reference |
|
|
20
|
+
| [API Reference](api-reference.md) | Python API — classes, methods, errors |
|
|
21
|
+
| [Data Format](data-format.md) | On-disk layout, Arrow schemas, sphere.json |
|
|
22
|
+
| [Benchmarks](benchmarks.md) | Validation results — banking, AML, transportation |
|
|
@@ -103,9 +103,9 @@ with sphere.session("agent-1") as session:
|
|
|
103
103
|
|
|
104
104
|
| Primitive | Method | What it does | Returns |
|
|
105
105
|
|-----------|--------|--------------|---------|
|
|
106
|
-
| π5 | `π5_attract_anomaly(pattern_id, radius, top_n)` | Find most anomalous polygons | `(list[Polygon], int, list, dict)` |
|
|
107
|
-
| π6 | `π6_attract_boundary(alias_id, pattern_id, direction, top_n)` | Find entities nearest to alias cutting plane | `list[(Polygon, float)]` |
|
|
108
|
-
| π7 | `π7_attract_hub(pattern_id, top_n, line_id_filter)` | Find entities with highest connectivity | `list[(str, int, float)]` |
|
|
106
|
+
| π5 | `π5_attract_anomaly(pattern_id, radius, top_n, fdr_alpha, fdr_method, select)` | Find most anomalous polygons | `(list[Polygon], int, list, dict)` |
|
|
107
|
+
| π6 | `π6_attract_boundary(alias_id, pattern_id, direction, top_n, fdr_alpha, fdr_method, select)` | Find entities nearest to alias cutting plane | `list[(Polygon, float)]` |
|
|
108
|
+
| π7 | `π7_attract_hub(pattern_id, top_n, line_id_filter, fdr_alpha, fdr_method, select)` | Find entities with highest connectivity | `list[(str, int, float)]` |
|
|
109
109
|
| π7+ | `π7_attract_hub_and_stats(pattern_id, top_n, line_id_filter)` | Hub ranking + population hub score statistics in one scan | `(list, dict)` |
|
|
110
110
|
| π8 | `π8_attract_cluster(pattern_id, n_clusters, top_n, sample_size)` | Discover geometric archetypes via k-means++ | `list[dict]` |
|
|
111
111
|
|
|
@@ -113,7 +113,7 @@ with sphere.session("agent-1") as session:
|
|
|
113
113
|
|
|
114
114
|
| Primitive | Method | What it does | Returns |
|
|
115
115
|
|-----------|--------|--------------|---------|
|
|
116
|
-
| π9 | `π9_attract_drift(pattern_id, top_n, sample_size)` | Find entities with highest temporal drift | `list[dict]` |
|
|
116
|
+
| π9 | `π9_attract_drift(pattern_id, top_n, sample_size, fdr_alpha, fdr_method, select)` | Find entities with highest temporal drift | `list[dict]` |
|
|
117
117
|
| π10 | `π10_attract_trajectory(primary_key, pattern_id, top_n)` | Find entities with similar temporal trajectory | `list[dict]` |
|
|
118
118
|
| π11 | `π11_attract_population_compare(pattern_id, window_a_from, window_a_to, window_b_from, window_b_to)` | Compare population geometry between two time windows | `dict` |
|
|
119
119
|
| π12 | `π12_attract_regime_change(pattern_id, timestamp_from, timestamp_to)` | Detect population geometry regime shifts | `list[dict]` |
|
|
@@ -133,6 +133,7 @@ with sphere.session("agent-1") as session:
|
|
|
133
133
|
| `cluster_bridges(pattern_id, n_clusters=5, top_n_bridges=10)` | Find entities bridging geometric clusters via edge table. Runs π8 clustering then identifies cross-cluster edges and bridge entities |
|
|
134
134
|
| `anomalous_edges(from_key, to_key, pattern_id, top_n=10)` | Find edges between two entities enriched with event-level geometry (delta_norm, is_anomaly). Unlike path tools which score entities (anchor), this scores individual transactions (event geometry) |
|
|
135
135
|
| `find_witness_cohort(primary_key, pattern_id, top_n=10, *, config=None, edge_pattern_id=None)` | Rank entities that share the target's witness signature. Investigative peer ranking — NOT edge forecasting. Combines four signals: `exp(-distance/theta)` delta similarity, witness Jaccard overlap, trajectory cosine alignment (optional), and graded anomaly bonus from `delta_rank_pct`. Excludes entities already connected via BTREE edge lookup — this is the function's main contribution over plain ANN. Configure via `WitnessCohortConfig(weights=WitnessCohortWeights(...), candidate_pool, min_witness_overlap, min_score, use_trajectory, bidirectional_check, timestamp_cutoff)`. Returns `WitnessCohortResult` with ranked `CohortMember` items, per-component scores, exclusion counts, and reproducibility metadata |
|
|
136
|
+
| `find_novel_entities(pattern_id, top_n=10, sample_size=5000)` | Rank entities by geometric deviation from neighbor-expected position using edge table adjacency. High `novelty_score` = entity doesn't behave like its neighborhood. Requires a pattern with an edge table. Returns list of dicts with `primary_key`, `novelty_score`, and per-dimension decomposition |
|
|
136
137
|
|
|
137
138
|
### Analysis and Detection
|
|
138
139
|
|
|
@@ -58,6 +58,16 @@ where μ = E[s] is the population mean, σ = max(std(s), ε) the clamped standar
|
|
|
58
58
|
- **Cross-sphere comparison** *(planned)* — dimensionless scalar metrics from independently calibrated coordinate spaces enable comparison across separate data systems
|
|
59
59
|
- **What-if analysis** *(planned)* — hypothetical edge changes produce a modified coordinate vector showing the geometric effect
|
|
60
60
|
|
|
61
|
+
## Statistical Guarantees
|
|
62
|
+
|
|
63
|
+
Two opt-in post-processing stages can be applied to any attract primitive that scans a population (pi5, pi6, pi7, pi9). Both are off by default — omitting them preserves the legacy behavior exactly.
|
|
64
|
+
|
|
65
|
+
**FDR control.** When `fdr_alpha` is set, the result set passes through a Benjamini-Hochberg (BH) procedure. Each entity's empirical-null p-value is derived from its population rank percentile: `p = 1 - rank_pct / 100`, where `rank_pct` is the entity's position in the full population sorted by the primitive's ranking metric (delta norm for pi5/pi6, hub score for pi7, displacement for pi9). The BH procedure guarantees `E[FDR] <= alpha` under independence or positive regression dependency (PRDS). Every retained entity carries a q-value — the minimum alpha at which it would still be retained. Entities with `q_value > alpha` are removed before the top-N / selection step.
|
|
66
|
+
|
|
67
|
+
**Diverse selection.** When `select="diverse"`, the top-N step is replaced by a lazy-greedy facility location algorithm that maximizes a monotone submodular objective over pairwise cosine similarity of delta vectors. The greedy selection achieves a `(1 - 1/e)` approximation to the optimal K-subset. Each selected entity reports a representativeness count — how many population members are closest to it among the selected set. The result is K entities that cover the geometric space of the result set rather than clustering around a single extreme region.
|
|
68
|
+
|
|
69
|
+
Both stages compose: `fdr_alpha=0.05, select="diverse"` first filters to FDR-controlled discoveries, then picks the K most geometrically diverse among them.
|
|
70
|
+
|
|
61
71
|
## Three Scales
|
|
62
72
|
|
|
63
73
|

|
|
@@ -394,6 +394,40 @@ patterns:
|
|
|
394
394
|
|
|
395
395
|
To skip edge table emission entirely (e.g. for faster iterative builds), use the `--no-edges` CLI flag.
|
|
396
396
|
|
|
397
|
+
### Generalized Dimension Blocks
|
|
398
|
+
|
|
399
|
+
Patterns optionally accept three additional dimension block fields that inject geographic, metric, or semantic columns from the entity line into the delta vector. All three are optional — existing configs are unaffected.
|
|
400
|
+
|
|
401
|
+
```yaml
|
|
402
|
+
patterns:
|
|
403
|
+
account_pattern:
|
|
404
|
+
type: anchor
|
|
405
|
+
entity_line: accounts
|
|
406
|
+
# ... existing fields (derived_dimensions, relations, etc.) ...
|
|
407
|
+
|
|
408
|
+
# Generalized dimension blocks (optional)
|
|
409
|
+
geo_properties: # list of geographic column names from entity line
|
|
410
|
+
- latitude
|
|
411
|
+
- longitude
|
|
412
|
+
metric_properties: # list of numeric column names from entity line
|
|
413
|
+
- balance
|
|
414
|
+
- credit_limit
|
|
415
|
+
semantic_dim: # embedding dimensionality reduction
|
|
416
|
+
columns:
|
|
417
|
+
- description_embedding
|
|
418
|
+
n_components: 8 # target PCA dimensions
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
| Field | Type | Default | Description |
|
|
422
|
+
|-------|------|---------|-------------|
|
|
423
|
+
| `geo_properties` | list | `null` | Geographic columns. Normalized with empirical mu/sigma. Dimensions prefixed `g:` (e.g. `g:lat`). |
|
|
424
|
+
| `metric_properties` | list | `null` | Numeric columns. Normalized with empirical mu/sigma. Dimensions prefixed `t:` (e.g. `t:balance`). |
|
|
425
|
+
| `semantic_dim` | dict | `null` | Embedding columns reduced via SVD-based PCA. Dimensions prefixed `s:` (e.g. `s:pc0`). |
|
|
426
|
+
| `semantic_dim.columns` | list | — | **Required** (within `semantic_dim`). List of embedding column names. |
|
|
427
|
+
| `semantic_dim.n_components` | int | — | **Required** (within `semantic_dim`). Target number of PCA dimensions. |
|
|
428
|
+
|
|
429
|
+
Dimension prefixes (`g:`, `t:`, `s:`) appear in `explain_anomaly` output, making it clear which block a dimension belongs to.
|
|
430
|
+
|
|
397
431
|
### Multiple patterns on the same entity line
|
|
398
432
|
|
|
399
433
|
You can define multiple patterns for the same anchor line with different calibration:
|
|
@@ -871,6 +905,7 @@ hypertopos build --config sphere.yaml --force --verbose
|
|
|
871
905
|
The builder parallelizes where possible:
|
|
872
906
|
- **Points write** — parallel across lines (ThreadPoolExecutor, up to 4 workers)
|
|
873
907
|
- **Geometry build** — parallel across patterns (one thread per pattern)
|
|
908
|
+
- **Temporal build** — parallel across patterns; adaptive memory chunking when the shape tensor exceeds available RAM (auto-detected via platform API, 4 GB fallback)
|
|
874
909
|
|
|
875
910
|
### Build timing
|
|
876
911
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Introduction
|
|
2
|
+
|
|
3
|
+
## The problem
|
|
4
|
+
|
|
5
|
+
Relational databases store entities and their relationships. SQL retrieves specific records that match filters. That works when you know what you're looking for.
|
|
6
|
+
|
|
7
|
+
But some questions aren't filters:
|
|
8
|
+
|
|
9
|
+
- Which entities are behaving differently from the rest of the population?
|
|
10
|
+
- How is that difference changing over time?
|
|
11
|
+
- What structural patterns exist that nobody thought to query for?
|
|
12
|
+
|
|
13
|
+
These are exploration questions. They require context — not just a row, but how that row relates to every other row in the system. SQL doesn't naturally express this.
|
|
14
|
+
|
|
15
|
+
## The idea
|
|
16
|
+
|
|
17
|
+
hypertopos maps every entity in a relational dataset into a shared geometric space.
|
|
18
|
+
|
|
19
|
+
The coordinate of each entity is derived from its typed relationships — who it connects to, through which channels, how often, which properties are filled or missing. This produces a position vector that captures the entity's structural identity relative to the population.
|
|
20
|
+
|
|
21
|
+
From there, geometric operations become analytical operations:
|
|
22
|
+
|
|
23
|
+
- **Distance from center** — anomaly detection
|
|
24
|
+
- **Proximity to other entities** — similarity and clustering
|
|
25
|
+
- **Movement over time** — drift tracking
|
|
26
|
+
- **Group differences** — population comparison
|
|
27
|
+
|
|
28
|
+
No models are trained. No labels are required. The geometry is computed directly from the relational structure of the data.
|
|
29
|
+
|
|
30
|
+
## How it works
|
|
31
|
+
|
|
32
|
+
The process has two phases:
|
|
33
|
+
|
|
34
|
+
**Build.** You describe your data sources and their relationships in a YAML configuration. hypertopos reads the data, computes population statistics (mean, standard deviation, empirical thresholds per dimension), and produces a sphere — a directory of pre-computed geometry stored in Apache Arrow format.
|
|
35
|
+
|
|
36
|
+
**Navigate.** An AI agent (or Python code) opens the sphere and moves through it. Twelve navigation primitives cover the common exploratory operations: walking along entity collections, jumping across relationships, finding clusters, detecting outliers, comparing groups, and tracking temporal change. Each step is stateful — where you are determines what you see next.
|
|
37
|
+
|
|
38
|
+
The sphere is designed for on-demand loading. Opening it reads a few kilobytes of metadata. Everything else is loaded as navigation requires it.
|
|
39
|
+
|
|
40
|
+
## What you can do with it
|
|
41
|
+
|
|
42
|
+
- Detect anomalies without training ML models or labeling data
|
|
43
|
+
- Discover clusters and structural archetypes in a population
|
|
44
|
+
- Track how entities drift from their historical behavior
|
|
45
|
+
- Compare groups to find which dimensions differentiate them
|
|
46
|
+
- Explore unknown datasets with AI agents that navigate rather than query
|
|
47
|
+
- Build investigation workflows that chain geometric operations
|
|
48
|
+
|
|
49
|
+
## Where this is now
|
|
50
|
+
|
|
51
|
+
hypertopos is a research-stage project with working code and reproducible benchmarks.
|
|
52
|
+
|
|
53
|
+
It has been validated on three domains — banking (Berka), anti-money laundering (IBM AML), and transportation (NYC Taxi) — using the same engine with zero domain-specific rules. Benchmark scripts and data preparation are included in the repository.
|
|
54
|
+
|
|
55
|
+
The library is under active development. The API may change. Contributions and feedback are welcome.
|
|
56
|
+
|
|
57
|
+
For the mathematical foundation, see [Core Concepts](concepts.md). To try it, see [Quick Start](quickstart.md).
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# Quick Start
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> From zero to a navigable geometric space.
|
|
4
|
+
|
|
5
|
+
This guide walks you through building a sphere from a relational dataset and connecting it to an AI agent. You will define entity types and their relationships in YAML, compute population-relative coordinates for every entity, and produce a sphere that agents can explore.
|
|
6
|
+
|
|
7
|
+
For the idea behind this, see [Introduction](introduction.md).
|
|
4
8
|
|
|
5
9
|
## Install
|
|
6
10
|
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Copyright (C) 2026 Karol Kędzia
|
|
2
|
+
# Licensed under the Business Source License 1.1 (the "License");
|
|
3
|
+
# you may not use this file except in compliance with the License.
|
|
4
|
+
# See LICENSE.md in the repository root for full terms.
|
|
5
|
+
"""Vectorized Arrow→tensor scatter utilities.
|
|
6
|
+
|
|
7
|
+
Replaces Python-level row iteration with Arrow pc.index_in + numpy
|
|
8
|
+
fancy indexing. Used by both static (compute_derived_batch) and
|
|
9
|
+
temporal (_precompute_shape_tensor) build paths.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pyarrow as pa
|
|
15
|
+
import pyarrow.compute as pc
|
|
16
|
+
from numpy.typing import NDArray
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def vectorized_scatter(
|
|
20
|
+
tensor: NDArray[np.float32],
|
|
21
|
+
dim_idx: int,
|
|
22
|
+
edge_max: float,
|
|
23
|
+
anchor_keys_arr: pa.Array,
|
|
24
|
+
grouped_fk_col: pa.Array,
|
|
25
|
+
grouped_bucket_col: pa.Array,
|
|
26
|
+
grouped_values_col: pa.Array,
|
|
27
|
+
) -> None:
|
|
28
|
+
if len(grouped_fk_col) == 0:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
entity_idx_arr = pc.index_in(grouped_fk_col, anchor_keys_arr)
|
|
32
|
+
valid = pc.and_(pc.is_valid(entity_idx_arr), pc.is_valid(grouped_values_col))
|
|
33
|
+
|
|
34
|
+
entity_indices = entity_idx_arr.filter(valid).to_numpy(
|
|
35
|
+
zero_copy_only=False,
|
|
36
|
+
).astype(np.intp)
|
|
37
|
+
bucket_indices = grouped_bucket_col.filter(valid).to_numpy(
|
|
38
|
+
zero_copy_only=False,
|
|
39
|
+
).astype(np.intp)
|
|
40
|
+
values = grouped_values_col.filter(valid).to_numpy(
|
|
41
|
+
zero_copy_only=False,
|
|
42
|
+
).astype(np.float64)
|
|
43
|
+
|
|
44
|
+
nan_ok = ~np.isnan(values)
|
|
45
|
+
entity_indices = entity_indices[nan_ok]
|
|
46
|
+
bucket_indices = bucket_indices[nan_ok]
|
|
47
|
+
values = np.clip(values[nan_ok], 0.0, edge_max) / edge_max
|
|
48
|
+
|
|
49
|
+
tensor[entity_indices, bucket_indices, dim_idx] = values.astype(np.float32)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def vectorized_scatter_1d(
|
|
53
|
+
result: NDArray[np.float64],
|
|
54
|
+
anchor_keys_arr: pa.Array,
|
|
55
|
+
grouped_fk_col: pa.Array,
|
|
56
|
+
grouped_values_col: pa.Array,
|
|
57
|
+
) -> None:
|
|
58
|
+
if len(grouped_fk_col) == 0:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
entity_idx_arr = pc.index_in(grouped_fk_col, anchor_keys_arr)
|
|
62
|
+
valid = pc.and_(pc.is_valid(entity_idx_arr), pc.is_valid(grouped_values_col))
|
|
63
|
+
|
|
64
|
+
entity_indices = entity_idx_arr.filter(valid).to_numpy(
|
|
65
|
+
zero_copy_only=False,
|
|
66
|
+
).astype(np.intp)
|
|
67
|
+
values = grouped_values_col.filter(valid).to_numpy(
|
|
68
|
+
zero_copy_only=False,
|
|
69
|
+
).astype(np.float64)
|
|
70
|
+
|
|
71
|
+
nan_ok = ~np.isnan(values)
|
|
72
|
+
result[entity_indices[nan_ok]] = values[nan_ok]
|
|
@@ -116,6 +116,7 @@ def compute_stats_grouped(
|
|
|
116
116
|
shape_vectors: np.ndarray,
|
|
117
117
|
group_ids: np.ndarray,
|
|
118
118
|
anomaly_percentile: float = 95.0,
|
|
119
|
+
prop_dim_start: int | None = None,
|
|
119
120
|
) -> tuple[
|
|
120
121
|
dict[str, tuple[np.ndarray, np.ndarray, np.ndarray, int]],
|
|
121
122
|
np.ndarray,
|
|
@@ -123,6 +124,10 @@ def compute_stats_grouped(
|
|
|
123
124
|
]:
|
|
124
125
|
"""Compute per-group population statistics.
|
|
125
126
|
|
|
127
|
+
prop_dim_start: if set, apply SIGMA_EPS_PROP floor to sigma[prop_dim_start:]
|
|
128
|
+
for each group before computing deltas. Eliminates the need for a separate
|
|
129
|
+
re-run pass in the caller.
|
|
130
|
+
|
|
126
131
|
Returns:
|
|
127
132
|
group_results: {group_name: (mu, sigma, theta, population_size)}
|
|
128
133
|
deltas: (N, D) float32 — z-scored against per-group mu/sigma
|
|
@@ -140,9 +145,12 @@ def compute_stats_grouped(
|
|
|
140
145
|
group_shape = shape_vectors[mask]
|
|
141
146
|
|
|
142
147
|
if len(group_shape) < 2:
|
|
143
|
-
# Too few entities for meaningful stats — use global-like single point
|
|
144
148
|
mu_g = group_shape.mean(axis=0).astype(np.float32)
|
|
145
149
|
sigma_g = np.full(D, SIGMA_EPS, dtype=np.float32)
|
|
150
|
+
if prop_dim_start is not None:
|
|
151
|
+
sigma_g[prop_dim_start:] = np.maximum(
|
|
152
|
+
sigma_g[prop_dim_start:], SIGMA_EPS_PROP,
|
|
153
|
+
)
|
|
146
154
|
theta_g = np.zeros(D, dtype=np.float32)
|
|
147
155
|
deltas[mask] = ((group_shape - mu_g) / sigma_g).astype(np.float32)
|
|
148
156
|
_masked = deltas[mask]
|
|
@@ -155,6 +163,22 @@ def compute_stats_grouped(
|
|
|
155
163
|
mu_g, sigma_g, theta_g, group_deltas, group_norms, _ = compute_stats(
|
|
156
164
|
group_shape, anomaly_percentile,
|
|
157
165
|
)
|
|
166
|
+
|
|
167
|
+
if prop_dim_start is not None:
|
|
168
|
+
sigma_g[prop_dim_start:] = np.maximum(
|
|
169
|
+
sigma_g[prop_dim_start:], SIGMA_EPS_PROP,
|
|
170
|
+
)
|
|
171
|
+
group_deltas = ((group_shape - mu_g) / sigma_g).astype(np.float32)
|
|
172
|
+
group_norms = np.sqrt(
|
|
173
|
+
np.einsum('ij,ij->i', group_deltas, group_deltas),
|
|
174
|
+
).astype(np.float32)
|
|
175
|
+
g_theta_scalar = (
|
|
176
|
+
float(np.percentile(group_norms, anomaly_percentile))
|
|
177
|
+
if len(group_norms) > 1 else 0.0
|
|
178
|
+
)
|
|
179
|
+
g_comp = g_theta_scalar / np.sqrt(D) if D > 0 else 0.0
|
|
180
|
+
theta_g = np.full(D, g_comp, dtype=np.float32)
|
|
181
|
+
|
|
158
182
|
deltas[mask] = group_deltas
|
|
159
183
|
delta_norms[mask] = group_norms
|
|
160
184
|
group_results[gid_str] = (mu_g, sigma_g, theta_g, int(mask.sum()))
|
|
@@ -179,9 +179,9 @@ def finalize_geometry_chunks(
|
|
|
179
179
|
return
|
|
180
180
|
|
|
181
181
|
# Compact fragments from multiple appends into fewer files.
|
|
182
|
-
# target_rows_per_fragment=
|
|
182
|
+
# target_rows_per_fragment=4M merges small chunks into large fragments
|
|
183
183
|
# for better sequential scan performance (fewer random I/O ops).
|
|
184
|
-
ds.optimize.compact_files(target_rows_per_fragment=
|
|
184
|
+
ds.optimize.compact_files(target_rows_per_fragment=4_194_304)
|
|
185
185
|
ds = _lance.dataset(str(lance_path)) # re-open after compaction
|
|
186
186
|
|
|
187
187
|
# Detect list_size for index building
|