hypertopos 0.3.0__tar.gz → 0.3.1__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.
Files changed (206) hide show
  1. {hypertopos-0.3.0 → hypertopos-0.3.1}/CHANGELOG.md +23 -0
  2. {hypertopos-0.3.0 → hypertopos-0.3.1}/PKG-INFO +1 -1
  3. {hypertopos-0.3.0 → hypertopos-0.3.1}/ROADMAP.md +10 -4
  4. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/api-reference.md +4 -4
  5. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/concepts.md +10 -0
  6. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/configuration.md +1 -0
  7. hypertopos-0.3.1/hypertopos/builder/_scatter.py +72 -0
  8. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/builder/_stats.py +25 -1
  9. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/builder/builder.py +531 -255
  10. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/builder/derived.py +23 -19
  11. hypertopos-0.3.1/hypertopos/engine/fdr.py +89 -0
  12. hypertopos-0.3.1/hypertopos/engine/selection.py +102 -0
  13. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/navigation/navigator.py +222 -5
  14. {hypertopos-0.3.0 → hypertopos-0.3.1}/pyproject.toml +1 -1
  15. hypertopos-0.3.1/tests/test_builder_chunking.py +93 -0
  16. hypertopos-0.3.1/tests/test_builder_contagion.py +99 -0
  17. hypertopos-0.3.1/tests/test_builder_rolling_z.py +83 -0
  18. hypertopos-0.3.1/tests/test_builder_scatter.py +177 -0
  19. hypertopos-0.3.1/tests/test_fdr.py +128 -0
  20. hypertopos-0.3.1/tests/test_navigator_fdr_select.py +540 -0
  21. hypertopos-0.3.1/tests/test_selection.py +131 -0
  22. {hypertopos-0.3.0 → hypertopos-0.3.1}/.github/workflows/publish-pypi.yml +0 -0
  23. {hypertopos-0.3.0 → hypertopos-0.3.1}/.gitignore +0 -0
  24. {hypertopos-0.3.0 → hypertopos-0.3.1}/LICENSE.md +0 -0
  25. {hypertopos-0.3.0 → hypertopos-0.3.1}/README.md +0 -0
  26. {hypertopos-0.3.0 → hypertopos-0.3.1}/SECURITY.md +0 -0
  27. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/README.md +0 -0
  28. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/architecture.md +0 -0
  29. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/LICENSE +0 -0
  30. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/README.md +0 -0
  31. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/README.md +0 -0
  32. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/SUMMARY.md +0 -0
  33. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
  34. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
  35. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
  36. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
  37. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r1/0.1.0-settings.md +0 -0
  38. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
  39. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
  40. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
  41. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
  42. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r2/0.1.0-settings.md +0 -0
  43. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
  44. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
  45. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
  46. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
  47. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r3/0.1.0-settings.md +0 -0
  48. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-report.md +0 -0
  49. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-a-scoring.md +0 -0
  50. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-report.md +0 -0
  51. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-exp-b-scoring.md +0 -0
  52. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r4/0.1.0-settings.md +0 -0
  53. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-report.md +0 -0
  54. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-a-scoring.md +0 -0
  55. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-report.md +0 -0
  56. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-exp-b-scoring.md +0 -0
  57. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r5/0.1.0-settings.md +0 -0
  58. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-report.md +0 -0
  59. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-a-scoring.md +0 -0
  60. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-report.md +0 -0
  61. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-exp-b-scoring.md +0 -0
  62. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/results/0.1.0/r6/0.1.0-settings.md +0 -0
  63. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_accounts.py +0 -0
  64. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_cpty_banks.py +0 -0
  65. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_districts.py +0 -0
  66. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_loan_accounts.py +0 -0
  67. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_operations.py +0 -0
  68. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_orders.py +0 -0
  69. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_transactions.py +0 -0
  70. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/prepare_tx_types.py +0 -0
  71. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/berka/sphere/sphere.yaml +0 -0
  72. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/README.md +0 -0
  73. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/_layer2_cross_eval.py +0 -0
  74. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/_layer3_cross_eval.py +0 -0
  75. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_accounts.py +0 -0
  76. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_currencies.py +0 -0
  77. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_payment_formats.py +0 -0
  78. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/hi_small_sphere/prepare_transactions.py +0 -0
  79. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/hi_small_sphere/sphere.yaml +0 -0
  80. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/layer1_passive_scan.py +0 -0
  81. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/layer2_xgboost_sweep.py +0 -0
  82. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/layer3_typology_filter.py +0 -0
  83. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/li_small_sphere/prepare_accounts.py +0 -0
  84. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/li_small_sphere/prepare_currencies.py +0 -0
  85. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/li_small_sphere/prepare_payment_formats.py +0 -0
  86. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/li_small_sphere/prepare_transactions.py +0 -0
  87. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/li_small_sphere/sphere.yaml +0 -0
  88. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/ibm-aml/passive-scan-calibration.md +0 -0
  89. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/README.md +0 -0
  90. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/SUMMARY.md +0 -0
  91. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-report.md +0 -0
  92. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-a-scoring.md +0 -0
  93. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-report.md +0 -0
  94. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-b-scoring.md +0 -0
  95. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-exp-c-report.md +0 -0
  96. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r1/0.1.0-settings.md +0 -0
  97. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-report.md +0 -0
  98. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-a-scoring.md +0 -0
  99. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-report.md +0 -0
  100. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-b-scoring.md +0 -0
  101. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-exp-c-report.md +0 -0
  102. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r2/0.1.0-settings.md +0 -0
  103. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-report.md +0 -0
  104. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-a-scoring.md +0 -0
  105. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-report.md +0 -0
  106. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-b-scoring.md +0 -0
  107. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-exp-c-report.md +0 -0
  108. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/results/0.1.0/r3/0.1.0-settings.md +0 -0
  109. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/sphere/prepare_trips.py +0 -0
  110. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmark/nyc-taxi/sphere/sphere.yaml +0 -0
  111. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/benchmarks.md +0 -0
  112. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/data-format.md +0 -0
  113. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/build-pipeline.svg +0 -0
  114. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/core-objects.svg +0 -0
  115. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/cutting-plane.svg +0 -0
  116. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/directory-layout.svg +0 -0
  117. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/hypertopos-overview.svg +0 -0
  118. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/model-lifecycle.svg +0 -0
  119. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/navigation-primitives.svg +0 -0
  120. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/images/three-scales.svg +0 -0
  121. {hypertopos-0.3.0 → hypertopos-0.3.1}/docs/quickstart.md +0 -0
  122. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/__init__.py +0 -0
  123. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/builder/__init__.py +0 -0
  124. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/builder/_writer.py +0 -0
  125. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/builder/mapping.py +0 -0
  126. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/cli/__init__.py +0 -0
  127. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/cli/__main__.py +0 -0
  128. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/cli/build.py +0 -0
  129. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/cli/info.py +0 -0
  130. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/cli/main.py +0 -0
  131. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/cli/schema.py +0 -0
  132. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/cli/sources.py +0 -0
  133. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/__init__.py +0 -0
  134. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/aggregation.py +0 -0
  135. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/calibration.py +0 -0
  136. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/chains.py +0 -0
  137. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/composition.py +0 -0
  138. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/forecast.py +0 -0
  139. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/geometry.py +0 -0
  140. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/invalidation.py +0 -0
  141. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/investigation.py +0 -0
  142. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/engine/lance_sql_agg.py +0 -0
  143. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/model/__init__.py +0 -0
  144. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/model/manifest.py +0 -0
  145. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/model/objects.py +0 -0
  146. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/model/sphere.py +0 -0
  147. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/navigation/__init__.py +0 -0
  148. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/navigation/scanner.py +0 -0
  149. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/sphere.py +0 -0
  150. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/storage/__init__.py +0 -0
  151. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/storage/_schemas.py +0 -0
  152. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/storage/cache.py +0 -0
  153. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/storage/reader.py +0 -0
  154. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/storage/writer.py +0 -0
  155. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/utils/__init__.py +0 -0
  156. {hypertopos-0.3.0 → hypertopos-0.3.1}/hypertopos/utils/arrow.py +0 -0
  157. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/.gitkeep +0 -0
  158. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/conftest.py +0 -0
  159. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/fixtures/generate_fixtures.py +0 -0
  160. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_aggregation_engine.py +0 -0
  161. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_arrow_utils.py +0 -0
  162. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_builder.py +0 -0
  163. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_builder_alias.py +0 -0
  164. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_builder_derived.py +0 -0
  165. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_builder_edges.py +0 -0
  166. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_cache.py +0 -0
  167. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_calibration.py +0 -0
  168. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_chains.py +0 -0
  169. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_cli.py +0 -0
  170. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_cli_alias.py +0 -0
  171. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_cli_sources.py +0 -0
  172. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_compaction.py +0 -0
  173. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_composition.py +0 -0
  174. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_continuous_event_dims.py +0 -0
  175. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_data_quality_prop.py +0 -0
  176. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_detection.py +0 -0
  177. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_edge_table.py +0 -0
  178. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_engine.py +0 -0
  179. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_forecast.py +0 -0
  180. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_fts.py +0 -0
  181. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_geometric_path.py +0 -0
  182. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_geometry_stats.py +0 -0
  183. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_hybrid_search.py +0 -0
  184. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_incremental.py +0 -0
  185. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_invalidation.py +0 -0
  186. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_investigation.py +0 -0
  187. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_lance_sql_agg.py +0 -0
  188. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_mapping.py +0 -0
  189. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_model.py +0 -0
  190. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_mvcc_session.py +0 -0
  191. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_navigation.py +0 -0
  192. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_navigator_alerts.py +0 -0
  193. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_observability.py +0 -0
  194. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_precision_stack.py +0 -0
  195. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_reputation.py +0 -0
  196. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_row_id_cache.py +0 -0
  197. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_scanner.py +0 -0
  198. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_sphere.py +0 -0
  199. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_sphere_overview_perf.py +0 -0
  200. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_stats_guard.py +0 -0
  201. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_storage.py +0 -0
  202. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_streaming_reads.py +0 -0
  203. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_temporal_centroids.py +0 -0
  204. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_trajectory_ann.py +0 -0
  205. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_witness_cohort.py +0 -0
  206. {hypertopos-0.3.0 → hypertopos-0.3.1}/tests/test_witness_set.py +0 -0
@@ -5,6 +5,29 @@ 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
+ ## [0.3.1] — 2026-04-12
9
+
10
+ ### Added
11
+
12
+ - 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.
13
+ - 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.
14
+ - `engine/fdr.py` module: `benjamini_hochberg`, `empirical_p_values_from_rank`, `q_values_from_p_values` — pure NumPy, no state, no I/O.
15
+ - `engine/selection.py` module: `lazy_greedy_facility_location`, `compute_similarity_matrix` — pure NumPy, Nemhauser-Wolsey-Fisher optimality guarantee.
16
+
17
+ ### Changed
18
+
19
+ - 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.
20
+ - 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.
21
+ - 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).
22
+ - Build pipeline: temporal Arrow assembly uses single numpy reshape instead of per-bucket table construction loop.
23
+ - 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.
24
+ - 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.
25
+
26
+ ### Fixed
27
+
28
+ - `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.
29
+ - `π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.
30
+
8
31
  ## [0.3.0] — 2026-04-12
9
32
 
10
33
  > **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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hypertopos
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Understand the structure of your data — without training machine learning models
5
5
  Project-URL: Homepage, https://github.com/hypertopos/hypertopos-py
6
6
  Project-URL: Documentation, https://github.com/hypertopos/hypertopos-py/tree/main/docs
@@ -2,7 +2,13 @@
2
2
 
3
3
  > Planned direction. Priorities may change based on feedback.
4
4
 
5
- ## Current: 0.3.0
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
 
@@ -38,19 +44,19 @@ Two production-quality improvements on top of the 0.3.0 storage layer:
38
44
 
39
45
  - **FDR control on anomaly queries** — Benjamini–Hochberg `fdr_alpha` parameter + per-row `q_value` column on `find_anomalies` and friends. Replaces unbounded "top-K by `delta_norm`" with a set whose false-discovery rate is bounded by α.
40
46
  - **Diverse-coverage anomaly summarisation** — `select="diverse"` option using lazy-greedy submodular maximisation, plus a `representativeness` column ("this 1 anomaly stands in for 38 cases").
41
- - **Temporal build optimization** — pre-aggregate event counts before the temporal loop, eliminating per-window event table rescans.
47
+ - **Build pipeline optimization** — vectorized scatter (Arrow `pc.index_in` + numpy), Welford rolling z-score, Arrow-native contagion stats, adaptive memory chunking for temporal build, pre-sorted graph dims bucketing.
42
48
 
43
49
  ---
44
50
 
45
51
  ## Plan: 0.3.2
46
52
 
47
- README and docs narrative restructure.
53
+ TBD.
48
54
 
49
55
  ---
50
56
 
51
57
  ## Plan: 0.4.0
52
58
 
53
- TBD — picked after 0.3.2 lands.
59
+ TBD.
54
60
 
55
61
  ---
56
62
 
@@ -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]` |
@@ -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
  ![Three Scales](images/three-scales.svg)
@@ -871,6 +871,7 @@ hypertopos build --config sphere.yaml --force --verbose
871
871
  The builder parallelizes where possible:
872
872
  - **Points write** — parallel across lines (ThreadPoolExecutor, up to 4 workers)
873
873
  - **Geometry build** — parallel across patterns (one thread per pattern)
874
+ - **Temporal build** — parallel across patterns; adaptive memory chunking when the shape tensor exceeds available RAM (auto-detected via platform API, 4 GB fallback)
874
875
 
875
876
  ### Build timing
876
877
 
@@ -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()))