oscura 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl

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 (465) hide show
  1. oscura/__init__.py +813 -8
  2. oscura/__main__.py +392 -0
  3. oscura/analyzers/__init__.py +37 -0
  4. oscura/analyzers/digital/__init__.py +177 -0
  5. oscura/analyzers/digital/bus.py +691 -0
  6. oscura/analyzers/digital/clock.py +805 -0
  7. oscura/analyzers/digital/correlation.py +720 -0
  8. oscura/analyzers/digital/edges.py +632 -0
  9. oscura/analyzers/digital/extraction.py +413 -0
  10. oscura/analyzers/digital/quality.py +878 -0
  11. oscura/analyzers/digital/signal_quality.py +877 -0
  12. oscura/analyzers/digital/thresholds.py +708 -0
  13. oscura/analyzers/digital/timing.py +1104 -0
  14. oscura/analyzers/eye/__init__.py +46 -0
  15. oscura/analyzers/eye/diagram.py +434 -0
  16. oscura/analyzers/eye/metrics.py +555 -0
  17. oscura/analyzers/jitter/__init__.py +83 -0
  18. oscura/analyzers/jitter/ber.py +333 -0
  19. oscura/analyzers/jitter/decomposition.py +759 -0
  20. oscura/analyzers/jitter/measurements.py +413 -0
  21. oscura/analyzers/jitter/spectrum.py +220 -0
  22. oscura/analyzers/measurements.py +40 -0
  23. oscura/analyzers/packet/__init__.py +171 -0
  24. oscura/analyzers/packet/daq.py +1077 -0
  25. oscura/analyzers/packet/metrics.py +437 -0
  26. oscura/analyzers/packet/parser.py +327 -0
  27. oscura/analyzers/packet/payload.py +2156 -0
  28. oscura/analyzers/packet/payload_analysis.py +1312 -0
  29. oscura/analyzers/packet/payload_extraction.py +236 -0
  30. oscura/analyzers/packet/payload_patterns.py +670 -0
  31. oscura/analyzers/packet/stream.py +359 -0
  32. oscura/analyzers/patterns/__init__.py +266 -0
  33. oscura/analyzers/patterns/clustering.py +1036 -0
  34. oscura/analyzers/patterns/discovery.py +539 -0
  35. oscura/analyzers/patterns/learning.py +797 -0
  36. oscura/analyzers/patterns/matching.py +1091 -0
  37. oscura/analyzers/patterns/periodic.py +650 -0
  38. oscura/analyzers/patterns/sequences.py +767 -0
  39. oscura/analyzers/power/__init__.py +116 -0
  40. oscura/analyzers/power/ac_power.py +391 -0
  41. oscura/analyzers/power/basic.py +383 -0
  42. oscura/analyzers/power/conduction.py +314 -0
  43. oscura/analyzers/power/efficiency.py +297 -0
  44. oscura/analyzers/power/ripple.py +356 -0
  45. oscura/analyzers/power/soa.py +372 -0
  46. oscura/analyzers/power/switching.py +479 -0
  47. oscura/analyzers/protocol/__init__.py +150 -0
  48. oscura/analyzers/protocols/__init__.py +150 -0
  49. oscura/analyzers/protocols/base.py +500 -0
  50. oscura/analyzers/protocols/can.py +620 -0
  51. oscura/analyzers/protocols/can_fd.py +448 -0
  52. oscura/analyzers/protocols/flexray.py +405 -0
  53. oscura/analyzers/protocols/hdlc.py +399 -0
  54. oscura/analyzers/protocols/i2c.py +368 -0
  55. oscura/analyzers/protocols/i2s.py +296 -0
  56. oscura/analyzers/protocols/jtag.py +393 -0
  57. oscura/analyzers/protocols/lin.py +445 -0
  58. oscura/analyzers/protocols/manchester.py +333 -0
  59. oscura/analyzers/protocols/onewire.py +501 -0
  60. oscura/analyzers/protocols/spi.py +334 -0
  61. oscura/analyzers/protocols/swd.py +325 -0
  62. oscura/analyzers/protocols/uart.py +393 -0
  63. oscura/analyzers/protocols/usb.py +495 -0
  64. oscura/analyzers/signal_integrity/__init__.py +63 -0
  65. oscura/analyzers/signal_integrity/embedding.py +294 -0
  66. oscura/analyzers/signal_integrity/equalization.py +370 -0
  67. oscura/analyzers/signal_integrity/sparams.py +484 -0
  68. oscura/analyzers/spectral/__init__.py +53 -0
  69. oscura/analyzers/spectral/chunked.py +273 -0
  70. oscura/analyzers/spectral/chunked_fft.py +571 -0
  71. oscura/analyzers/spectral/chunked_wavelet.py +391 -0
  72. oscura/analyzers/spectral/fft.py +92 -0
  73. oscura/analyzers/statistical/__init__.py +250 -0
  74. oscura/analyzers/statistical/checksum.py +923 -0
  75. oscura/analyzers/statistical/chunked_corr.py +228 -0
  76. oscura/analyzers/statistical/classification.py +778 -0
  77. oscura/analyzers/statistical/entropy.py +1113 -0
  78. oscura/analyzers/statistical/ngrams.py +614 -0
  79. oscura/analyzers/statistics/__init__.py +119 -0
  80. oscura/analyzers/statistics/advanced.py +885 -0
  81. oscura/analyzers/statistics/basic.py +263 -0
  82. oscura/analyzers/statistics/correlation.py +630 -0
  83. oscura/analyzers/statistics/distribution.py +298 -0
  84. oscura/analyzers/statistics/outliers.py +463 -0
  85. oscura/analyzers/statistics/streaming.py +93 -0
  86. oscura/analyzers/statistics/trend.py +520 -0
  87. oscura/analyzers/validation.py +598 -0
  88. oscura/analyzers/waveform/__init__.py +36 -0
  89. oscura/analyzers/waveform/measurements.py +943 -0
  90. oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
  91. oscura/analyzers/waveform/spectral.py +1689 -0
  92. oscura/analyzers/waveform/wavelets.py +298 -0
  93. oscura/api/__init__.py +62 -0
  94. oscura/api/dsl.py +538 -0
  95. oscura/api/fluent.py +571 -0
  96. oscura/api/operators.py +498 -0
  97. oscura/api/optimization.py +392 -0
  98. oscura/api/profiling.py +396 -0
  99. oscura/automotive/__init__.py +73 -0
  100. oscura/automotive/can/__init__.py +52 -0
  101. oscura/automotive/can/analysis.py +356 -0
  102. oscura/automotive/can/checksum.py +250 -0
  103. oscura/automotive/can/correlation.py +212 -0
  104. oscura/automotive/can/discovery.py +355 -0
  105. oscura/automotive/can/message_wrapper.py +375 -0
  106. oscura/automotive/can/models.py +385 -0
  107. oscura/automotive/can/patterns.py +381 -0
  108. oscura/automotive/can/session.py +452 -0
  109. oscura/automotive/can/state_machine.py +300 -0
  110. oscura/automotive/can/stimulus_response.py +461 -0
  111. oscura/automotive/dbc/__init__.py +15 -0
  112. oscura/automotive/dbc/generator.py +156 -0
  113. oscura/automotive/dbc/parser.py +146 -0
  114. oscura/automotive/dtc/__init__.py +30 -0
  115. oscura/automotive/dtc/database.py +3036 -0
  116. oscura/automotive/j1939/__init__.py +14 -0
  117. oscura/automotive/j1939/decoder.py +745 -0
  118. oscura/automotive/loaders/__init__.py +35 -0
  119. oscura/automotive/loaders/asc.py +98 -0
  120. oscura/automotive/loaders/blf.py +77 -0
  121. oscura/automotive/loaders/csv_can.py +136 -0
  122. oscura/automotive/loaders/dispatcher.py +136 -0
  123. oscura/automotive/loaders/mdf.py +331 -0
  124. oscura/automotive/loaders/pcap.py +132 -0
  125. oscura/automotive/obd/__init__.py +14 -0
  126. oscura/automotive/obd/decoder.py +707 -0
  127. oscura/automotive/uds/__init__.py +48 -0
  128. oscura/automotive/uds/decoder.py +265 -0
  129. oscura/automotive/uds/models.py +64 -0
  130. oscura/automotive/visualization.py +369 -0
  131. oscura/batch/__init__.py +55 -0
  132. oscura/batch/advanced.py +627 -0
  133. oscura/batch/aggregate.py +300 -0
  134. oscura/batch/analyze.py +139 -0
  135. oscura/batch/logging.py +487 -0
  136. oscura/batch/metrics.py +556 -0
  137. oscura/builders/__init__.py +41 -0
  138. oscura/builders/signal_builder.py +1131 -0
  139. oscura/cli/__init__.py +14 -0
  140. oscura/cli/batch.py +339 -0
  141. oscura/cli/characterize.py +273 -0
  142. oscura/cli/compare.py +775 -0
  143. oscura/cli/decode.py +551 -0
  144. oscura/cli/main.py +247 -0
  145. oscura/cli/shell.py +350 -0
  146. oscura/comparison/__init__.py +66 -0
  147. oscura/comparison/compare.py +397 -0
  148. oscura/comparison/golden.py +487 -0
  149. oscura/comparison/limits.py +391 -0
  150. oscura/comparison/mask.py +434 -0
  151. oscura/comparison/trace_diff.py +30 -0
  152. oscura/comparison/visualization.py +481 -0
  153. oscura/compliance/__init__.py +70 -0
  154. oscura/compliance/advanced.py +756 -0
  155. oscura/compliance/masks.py +363 -0
  156. oscura/compliance/reporting.py +483 -0
  157. oscura/compliance/testing.py +298 -0
  158. oscura/component/__init__.py +38 -0
  159. oscura/component/impedance.py +365 -0
  160. oscura/component/reactive.py +598 -0
  161. oscura/component/transmission_line.py +312 -0
  162. oscura/config/__init__.py +191 -0
  163. oscura/config/defaults.py +254 -0
  164. oscura/config/loader.py +348 -0
  165. oscura/config/memory.py +271 -0
  166. oscura/config/migration.py +458 -0
  167. oscura/config/pipeline.py +1077 -0
  168. oscura/config/preferences.py +530 -0
  169. oscura/config/protocol.py +875 -0
  170. oscura/config/schema.py +713 -0
  171. oscura/config/settings.py +420 -0
  172. oscura/config/thresholds.py +599 -0
  173. oscura/convenience.py +457 -0
  174. oscura/core/__init__.py +299 -0
  175. oscura/core/audit.py +457 -0
  176. oscura/core/backend_selector.py +405 -0
  177. oscura/core/cache.py +590 -0
  178. oscura/core/cancellation.py +439 -0
  179. oscura/core/confidence.py +225 -0
  180. oscura/core/config.py +506 -0
  181. oscura/core/correlation.py +216 -0
  182. oscura/core/cross_domain.py +422 -0
  183. oscura/core/debug.py +301 -0
  184. oscura/core/edge_cases.py +541 -0
  185. oscura/core/exceptions.py +535 -0
  186. oscura/core/gpu_backend.py +523 -0
  187. oscura/core/lazy.py +832 -0
  188. oscura/core/log_query.py +540 -0
  189. oscura/core/logging.py +931 -0
  190. oscura/core/logging_advanced.py +952 -0
  191. oscura/core/memoize.py +171 -0
  192. oscura/core/memory_check.py +274 -0
  193. oscura/core/memory_guard.py +290 -0
  194. oscura/core/memory_limits.py +336 -0
  195. oscura/core/memory_monitor.py +453 -0
  196. oscura/core/memory_progress.py +465 -0
  197. oscura/core/memory_warnings.py +315 -0
  198. oscura/core/numba_backend.py +362 -0
  199. oscura/core/performance.py +352 -0
  200. oscura/core/progress.py +524 -0
  201. oscura/core/provenance.py +358 -0
  202. oscura/core/results.py +331 -0
  203. oscura/core/types.py +504 -0
  204. oscura/core/uncertainty.py +383 -0
  205. oscura/discovery/__init__.py +52 -0
  206. oscura/discovery/anomaly_detector.py +672 -0
  207. oscura/discovery/auto_decoder.py +415 -0
  208. oscura/discovery/comparison.py +497 -0
  209. oscura/discovery/quality_validator.py +528 -0
  210. oscura/discovery/signal_detector.py +769 -0
  211. oscura/dsl/__init__.py +73 -0
  212. oscura/dsl/commands.py +246 -0
  213. oscura/dsl/interpreter.py +455 -0
  214. oscura/dsl/parser.py +689 -0
  215. oscura/dsl/repl.py +172 -0
  216. oscura/exceptions.py +59 -0
  217. oscura/exploratory/__init__.py +111 -0
  218. oscura/exploratory/error_recovery.py +642 -0
  219. oscura/exploratory/fuzzy.py +513 -0
  220. oscura/exploratory/fuzzy_advanced.py +786 -0
  221. oscura/exploratory/legacy.py +831 -0
  222. oscura/exploratory/parse.py +358 -0
  223. oscura/exploratory/recovery.py +275 -0
  224. oscura/exploratory/sync.py +382 -0
  225. oscura/exploratory/unknown.py +707 -0
  226. oscura/export/__init__.py +25 -0
  227. oscura/export/wireshark/README.md +265 -0
  228. oscura/export/wireshark/__init__.py +47 -0
  229. oscura/export/wireshark/generator.py +312 -0
  230. oscura/export/wireshark/lua_builder.py +159 -0
  231. oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
  232. oscura/export/wireshark/type_mapping.py +165 -0
  233. oscura/export/wireshark/validator.py +105 -0
  234. oscura/exporters/__init__.py +94 -0
  235. oscura/exporters/csv.py +303 -0
  236. oscura/exporters/exporters.py +44 -0
  237. oscura/exporters/hdf5.py +219 -0
  238. oscura/exporters/html_export.py +701 -0
  239. oscura/exporters/json_export.py +291 -0
  240. oscura/exporters/markdown_export.py +367 -0
  241. oscura/exporters/matlab_export.py +354 -0
  242. oscura/exporters/npz_export.py +219 -0
  243. oscura/exporters/spice_export.py +210 -0
  244. oscura/extensibility/__init__.py +131 -0
  245. oscura/extensibility/docs.py +752 -0
  246. oscura/extensibility/extensions.py +1125 -0
  247. oscura/extensibility/logging.py +259 -0
  248. oscura/extensibility/measurements.py +485 -0
  249. oscura/extensibility/plugins.py +414 -0
  250. oscura/extensibility/registry.py +346 -0
  251. oscura/extensibility/templates.py +913 -0
  252. oscura/extensibility/validation.py +651 -0
  253. oscura/filtering/__init__.py +89 -0
  254. oscura/filtering/base.py +563 -0
  255. oscura/filtering/convenience.py +564 -0
  256. oscura/filtering/design.py +725 -0
  257. oscura/filtering/filters.py +32 -0
  258. oscura/filtering/introspection.py +605 -0
  259. oscura/guidance/__init__.py +24 -0
  260. oscura/guidance/recommender.py +429 -0
  261. oscura/guidance/wizard.py +518 -0
  262. oscura/inference/__init__.py +251 -0
  263. oscura/inference/active_learning/README.md +153 -0
  264. oscura/inference/active_learning/__init__.py +38 -0
  265. oscura/inference/active_learning/lstar.py +257 -0
  266. oscura/inference/active_learning/observation_table.py +230 -0
  267. oscura/inference/active_learning/oracle.py +78 -0
  268. oscura/inference/active_learning/teachers/__init__.py +15 -0
  269. oscura/inference/active_learning/teachers/simulator.py +192 -0
  270. oscura/inference/adaptive_tuning.py +453 -0
  271. oscura/inference/alignment.py +653 -0
  272. oscura/inference/bayesian.py +943 -0
  273. oscura/inference/binary.py +1016 -0
  274. oscura/inference/crc_reverse.py +711 -0
  275. oscura/inference/logic.py +288 -0
  276. oscura/inference/message_format.py +1305 -0
  277. oscura/inference/protocol.py +417 -0
  278. oscura/inference/protocol_dsl.py +1084 -0
  279. oscura/inference/protocol_library.py +1230 -0
  280. oscura/inference/sequences.py +809 -0
  281. oscura/inference/signal_intelligence.py +1509 -0
  282. oscura/inference/spectral.py +215 -0
  283. oscura/inference/state_machine.py +634 -0
  284. oscura/inference/stream.py +918 -0
  285. oscura/integrations/__init__.py +59 -0
  286. oscura/integrations/llm.py +1827 -0
  287. oscura/jupyter/__init__.py +32 -0
  288. oscura/jupyter/display.py +268 -0
  289. oscura/jupyter/magic.py +334 -0
  290. oscura/loaders/__init__.py +526 -0
  291. oscura/loaders/binary.py +69 -0
  292. oscura/loaders/configurable.py +1255 -0
  293. oscura/loaders/csv.py +26 -0
  294. oscura/loaders/csv_loader.py +473 -0
  295. oscura/loaders/hdf5.py +9 -0
  296. oscura/loaders/hdf5_loader.py +510 -0
  297. oscura/loaders/lazy.py +370 -0
  298. oscura/loaders/mmap_loader.py +583 -0
  299. oscura/loaders/numpy_loader.py +436 -0
  300. oscura/loaders/pcap.py +432 -0
  301. oscura/loaders/preprocessing.py +368 -0
  302. oscura/loaders/rigol.py +287 -0
  303. oscura/loaders/sigrok.py +321 -0
  304. oscura/loaders/tdms.py +367 -0
  305. oscura/loaders/tektronix.py +711 -0
  306. oscura/loaders/validation.py +584 -0
  307. oscura/loaders/vcd.py +464 -0
  308. oscura/loaders/wav.py +233 -0
  309. oscura/math/__init__.py +45 -0
  310. oscura/math/arithmetic.py +824 -0
  311. oscura/math/interpolation.py +413 -0
  312. oscura/onboarding/__init__.py +39 -0
  313. oscura/onboarding/help.py +498 -0
  314. oscura/onboarding/tutorials.py +405 -0
  315. oscura/onboarding/wizard.py +466 -0
  316. oscura/optimization/__init__.py +19 -0
  317. oscura/optimization/parallel.py +440 -0
  318. oscura/optimization/search.py +532 -0
  319. oscura/pipeline/__init__.py +43 -0
  320. oscura/pipeline/base.py +338 -0
  321. oscura/pipeline/composition.py +242 -0
  322. oscura/pipeline/parallel.py +448 -0
  323. oscura/pipeline/pipeline.py +375 -0
  324. oscura/pipeline/reverse_engineering.py +1119 -0
  325. oscura/plugins/__init__.py +122 -0
  326. oscura/plugins/base.py +272 -0
  327. oscura/plugins/cli.py +497 -0
  328. oscura/plugins/discovery.py +411 -0
  329. oscura/plugins/isolation.py +418 -0
  330. oscura/plugins/lifecycle.py +959 -0
  331. oscura/plugins/manager.py +493 -0
  332. oscura/plugins/registry.py +421 -0
  333. oscura/plugins/versioning.py +372 -0
  334. oscura/py.typed +0 -0
  335. oscura/quality/__init__.py +65 -0
  336. oscura/quality/ensemble.py +740 -0
  337. oscura/quality/explainer.py +338 -0
  338. oscura/quality/scoring.py +616 -0
  339. oscura/quality/warnings.py +456 -0
  340. oscura/reporting/__init__.py +248 -0
  341. oscura/reporting/advanced.py +1234 -0
  342. oscura/reporting/analyze.py +448 -0
  343. oscura/reporting/argument_preparer.py +596 -0
  344. oscura/reporting/auto_report.py +507 -0
  345. oscura/reporting/batch.py +615 -0
  346. oscura/reporting/chart_selection.py +223 -0
  347. oscura/reporting/comparison.py +330 -0
  348. oscura/reporting/config.py +615 -0
  349. oscura/reporting/content/__init__.py +39 -0
  350. oscura/reporting/content/executive.py +127 -0
  351. oscura/reporting/content/filtering.py +191 -0
  352. oscura/reporting/content/minimal.py +257 -0
  353. oscura/reporting/content/verbosity.py +162 -0
  354. oscura/reporting/core.py +508 -0
  355. oscura/reporting/core_formats/__init__.py +17 -0
  356. oscura/reporting/core_formats/multi_format.py +210 -0
  357. oscura/reporting/engine.py +836 -0
  358. oscura/reporting/export.py +366 -0
  359. oscura/reporting/formatting/__init__.py +129 -0
  360. oscura/reporting/formatting/emphasis.py +81 -0
  361. oscura/reporting/formatting/numbers.py +403 -0
  362. oscura/reporting/formatting/standards.py +55 -0
  363. oscura/reporting/formatting.py +466 -0
  364. oscura/reporting/html.py +578 -0
  365. oscura/reporting/index.py +590 -0
  366. oscura/reporting/multichannel.py +296 -0
  367. oscura/reporting/output.py +379 -0
  368. oscura/reporting/pdf.py +373 -0
  369. oscura/reporting/plots.py +731 -0
  370. oscura/reporting/pptx_export.py +360 -0
  371. oscura/reporting/renderers/__init__.py +11 -0
  372. oscura/reporting/renderers/pdf.py +94 -0
  373. oscura/reporting/sections.py +471 -0
  374. oscura/reporting/standards.py +680 -0
  375. oscura/reporting/summary_generator.py +368 -0
  376. oscura/reporting/tables.py +397 -0
  377. oscura/reporting/template_system.py +724 -0
  378. oscura/reporting/templates/__init__.py +15 -0
  379. oscura/reporting/templates/definition.py +205 -0
  380. oscura/reporting/templates/index.html +649 -0
  381. oscura/reporting/templates/index.md +173 -0
  382. oscura/schemas/__init__.py +158 -0
  383. oscura/schemas/bus_configuration.json +322 -0
  384. oscura/schemas/device_mapping.json +182 -0
  385. oscura/schemas/packet_format.json +418 -0
  386. oscura/schemas/protocol_definition.json +363 -0
  387. oscura/search/__init__.py +16 -0
  388. oscura/search/anomaly.py +292 -0
  389. oscura/search/context.py +149 -0
  390. oscura/search/pattern.py +160 -0
  391. oscura/session/__init__.py +34 -0
  392. oscura/session/annotations.py +289 -0
  393. oscura/session/history.py +313 -0
  394. oscura/session/session.py +445 -0
  395. oscura/streaming/__init__.py +43 -0
  396. oscura/streaming/chunked.py +611 -0
  397. oscura/streaming/progressive.py +393 -0
  398. oscura/streaming/realtime.py +622 -0
  399. oscura/testing/__init__.py +54 -0
  400. oscura/testing/synthetic.py +808 -0
  401. oscura/triggering/__init__.py +68 -0
  402. oscura/triggering/base.py +229 -0
  403. oscura/triggering/edge.py +353 -0
  404. oscura/triggering/pattern.py +344 -0
  405. oscura/triggering/pulse.py +581 -0
  406. oscura/triggering/window.py +453 -0
  407. oscura/ui/__init__.py +48 -0
  408. oscura/ui/formatters.py +526 -0
  409. oscura/ui/progressive_display.py +340 -0
  410. oscura/utils/__init__.py +99 -0
  411. oscura/utils/autodetect.py +338 -0
  412. oscura/utils/buffer.py +389 -0
  413. oscura/utils/lazy.py +407 -0
  414. oscura/utils/lazy_imports.py +147 -0
  415. oscura/utils/memory.py +836 -0
  416. oscura/utils/memory_advanced.py +1326 -0
  417. oscura/utils/memory_extensions.py +465 -0
  418. oscura/utils/progressive.py +352 -0
  419. oscura/utils/windowing.py +362 -0
  420. oscura/visualization/__init__.py +321 -0
  421. oscura/visualization/accessibility.py +526 -0
  422. oscura/visualization/annotations.py +374 -0
  423. oscura/visualization/axis_scaling.py +305 -0
  424. oscura/visualization/colors.py +453 -0
  425. oscura/visualization/digital.py +337 -0
  426. oscura/visualization/eye.py +420 -0
  427. oscura/visualization/histogram.py +281 -0
  428. oscura/visualization/interactive.py +858 -0
  429. oscura/visualization/jitter.py +702 -0
  430. oscura/visualization/keyboard.py +394 -0
  431. oscura/visualization/layout.py +365 -0
  432. oscura/visualization/optimization.py +1028 -0
  433. oscura/visualization/palettes.py +446 -0
  434. oscura/visualization/plot.py +92 -0
  435. oscura/visualization/power.py +290 -0
  436. oscura/visualization/power_extended.py +626 -0
  437. oscura/visualization/presets.py +467 -0
  438. oscura/visualization/protocols.py +932 -0
  439. oscura/visualization/render.py +207 -0
  440. oscura/visualization/rendering.py +444 -0
  441. oscura/visualization/reverse_engineering.py +791 -0
  442. oscura/visualization/signal_integrity.py +808 -0
  443. oscura/visualization/specialized.py +553 -0
  444. oscura/visualization/spectral.py +811 -0
  445. oscura/visualization/styles.py +381 -0
  446. oscura/visualization/thumbnails.py +311 -0
  447. oscura/visualization/time_axis.py +351 -0
  448. oscura/visualization/waveform.py +367 -0
  449. oscura/workflow/__init__.py +13 -0
  450. oscura/workflow/dag.py +377 -0
  451. oscura/workflows/__init__.py +58 -0
  452. oscura/workflows/compliance.py +280 -0
  453. oscura/workflows/digital.py +272 -0
  454. oscura/workflows/multi_trace.py +502 -0
  455. oscura/workflows/power.py +178 -0
  456. oscura/workflows/protocol.py +492 -0
  457. oscura/workflows/reverse_engineering.py +639 -0
  458. oscura/workflows/signal_integrity.py +227 -0
  459. oscura-0.1.0.dist-info/METADATA +300 -0
  460. oscura-0.1.0.dist-info/RECORD +463 -0
  461. oscura-0.1.0.dist-info/entry_points.txt +2 -0
  462. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/licenses/LICENSE +1 -1
  463. oscura-0.0.1.dist-info/METADATA +0 -63
  464. oscura-0.0.1.dist-info/RECORD +0 -5
  465. {oscura-0.0.1.dist-info → oscura-0.1.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,251 @@
1
+ """Auto-inference and smart defaults for TraceKit.
2
+
3
+ This module provides automatic parameter detection and intelligent defaults
4
+ for common analysis tasks.
5
+
6
+ - RE-SEQ-002: Sequence Pattern Detection
7
+ - RE-SEQ-003: Request-Response Correlation
8
+ - RE-STR-001: UDP Stream Reconstruction
9
+ - RE-STR-002: TCP Stream Reassembly
10
+ - RE-STR-003: Message Framing and Segmentation
11
+ - RE-BIN-001: Magic Byte Detection
12
+ - RE-BIN-002: Structure Alignment Detection
13
+ - RE-BIN-003: Binary Parser DSL
14
+ - RE-DSL-003: Protocol Format Library
15
+ """
16
+
17
+ # Active Learning: L* algorithm for DFA inference
18
+ from oscura.inference.active_learning import (
19
+ LStarLearner,
20
+ ObservationTable,
21
+ Oracle,
22
+ SimulatorTeacher,
23
+ )
24
+ from oscura.inference.adaptive_tuning import (
25
+ AdaptiveParameterTuner,
26
+ TunedParameters,
27
+ get_adaptive_parameters,
28
+ )
29
+ from oscura.inference.alignment import (
30
+ AlignmentResult,
31
+ align_global,
32
+ align_local,
33
+ align_multiple,
34
+ compute_similarity,
35
+ find_conserved_regions,
36
+ find_variable_regions,
37
+ )
38
+ from oscura.inference.bayesian import (
39
+ BayesianInference,
40
+ Posterior,
41
+ Prior,
42
+ SequentialBayesian,
43
+ infer_with_uncertainty,
44
+ )
45
+
46
+ # RE-BIN-001, RE-BIN-002, RE-BIN-003: Binary Format Inference
47
+ from oscura.inference.binary import (
48
+ KNOWN_MAGIC_BYTES,
49
+ AlignmentDetector,
50
+ BinaryParserGenerator,
51
+ MagicByteDetector,
52
+ MagicByteResult,
53
+ ParserDefinition,
54
+ ParserField,
55
+ detect_alignment,
56
+ detect_magic_bytes,
57
+ find_all_magic_bytes,
58
+ generate_parser,
59
+ parser_to_python,
60
+ parser_to_yaml,
61
+ )
62
+ from oscura.inference.binary import (
63
+ AlignmentResult as BinaryAlignmentResult,
64
+ )
65
+ from oscura.inference.crc_reverse import (
66
+ STANDARD_CRCS,
67
+ CRCParameters,
68
+ CRCReverser,
69
+ verify_crc,
70
+ )
71
+ from oscura.inference.logic import detect_logic_family
72
+ from oscura.inference.message_format import (
73
+ InferredField,
74
+ MessageFormatInferrer,
75
+ MessageSchema,
76
+ detect_field_types,
77
+ find_dependencies,
78
+ infer_format,
79
+ )
80
+ from oscura.inference.protocol import detect_protocol
81
+ from oscura.inference.protocol_dsl import (
82
+ DecodedMessage,
83
+ FieldDefinition,
84
+ ProtocolDecoder,
85
+ ProtocolDefinition,
86
+ ProtocolEncoder,
87
+ decode_message,
88
+ load_protocol,
89
+ )
90
+
91
+ # RE-DSL-003: Protocol Format Library
92
+ from oscura.inference.protocol_library import (
93
+ ProtocolInfo,
94
+ ProtocolLibrary,
95
+ get_decoder,
96
+ get_library,
97
+ get_protocol,
98
+ list_protocols,
99
+ )
100
+
101
+ # RE-SEQ-002, RE-SEQ-003: Sequence Pattern Detection and Correlation
102
+ from oscura.inference.sequences import (
103
+ CommunicationFlow,
104
+ RequestResponseCorrelator,
105
+ RequestResponsePair,
106
+ SequencePattern,
107
+ SequencePatternDetector,
108
+ calculate_latency_stats,
109
+ correlate_requests,
110
+ detect_sequence_patterns,
111
+ find_message_dependencies,
112
+ )
113
+ from oscura.inference.signal_intelligence import (
114
+ AnalysisRecommendation,
115
+ assess_signal_quality,
116
+ check_measurement_suitability,
117
+ classify_signal,
118
+ get_optimal_domain_order,
119
+ recommend_analyses,
120
+ suggest_measurements,
121
+ )
122
+ from oscura.inference.spectral import auto_spectral_config
123
+ from oscura.inference.state_machine import (
124
+ FiniteAutomaton,
125
+ State,
126
+ StateMachineInferrer,
127
+ Transition,
128
+ infer_rpni,
129
+ minimize_dfa,
130
+ to_dot,
131
+ to_networkx,
132
+ )
133
+
134
+ # RE-STR-001, RE-STR-002, RE-STR-003: Stream Reassembly
135
+ from oscura.inference.stream import (
136
+ FramingResult,
137
+ MessageFrame,
138
+ MessageFramer,
139
+ ReassembledStream,
140
+ StreamSegment,
141
+ TCPStreamReassembler,
142
+ UDPStreamReassembler,
143
+ detect_message_framing,
144
+ extract_messages,
145
+ reassemble_tcp_stream,
146
+ reassemble_udp_stream,
147
+ )
148
+
149
+ __all__ = [
150
+ "KNOWN_MAGIC_BYTES",
151
+ "STANDARD_CRCS",
152
+ "AdaptiveParameterTuner",
153
+ # RE-BIN-001, RE-BIN-002, RE-BIN-003: Binary Format Inference
154
+ "AlignmentDetector",
155
+ "AlignmentResult",
156
+ "AnalysisRecommendation",
157
+ "BayesianInference",
158
+ "BinaryAlignmentResult",
159
+ "BinaryParserGenerator",
160
+ # CRC Reverse Engineering
161
+ "CRCParameters",
162
+ "CRCReverser",
163
+ # RE-SEQ-002, RE-SEQ-003: Sequence Patterns and Correlation
164
+ "CommunicationFlow",
165
+ "DecodedMessage",
166
+ "FieldDefinition",
167
+ "FiniteAutomaton",
168
+ # RE-STR-001, RE-STR-002, RE-STR-003: Stream Reassembly
169
+ "FramingResult",
170
+ "InferredField",
171
+ # Active Learning
172
+ "LStarLearner",
173
+ "MagicByteDetector",
174
+ "MagicByteResult",
175
+ "MessageFormatInferrer",
176
+ "MessageFrame",
177
+ "MessageFramer",
178
+ "MessageSchema",
179
+ "ObservationTable",
180
+ "Oracle",
181
+ "ParserDefinition",
182
+ "ParserField",
183
+ "Posterior",
184
+ "Prior",
185
+ "ProtocolDecoder",
186
+ "ProtocolDefinition",
187
+ "ProtocolEncoder",
188
+ # RE-DSL-003: Protocol Format Library
189
+ "ProtocolInfo",
190
+ "ProtocolLibrary",
191
+ "ReassembledStream",
192
+ "RequestResponseCorrelator",
193
+ "RequestResponsePair",
194
+ "SequencePattern",
195
+ "SequencePatternDetector",
196
+ "SequentialBayesian",
197
+ "SimulatorTeacher",
198
+ "State",
199
+ "StateMachineInferrer",
200
+ "StreamSegment",
201
+ "TCPStreamReassembler",
202
+ "Transition",
203
+ "TunedParameters",
204
+ "UDPStreamReassembler",
205
+ "align_global",
206
+ "align_local",
207
+ "align_multiple",
208
+ # Original exports
209
+ "assess_signal_quality",
210
+ "auto_spectral_config",
211
+ "calculate_latency_stats",
212
+ "check_measurement_suitability",
213
+ "classify_signal",
214
+ "compute_similarity",
215
+ "correlate_requests",
216
+ "decode_message",
217
+ "detect_alignment",
218
+ "detect_field_types",
219
+ "detect_logic_family",
220
+ "detect_magic_bytes",
221
+ "detect_message_framing",
222
+ "detect_protocol",
223
+ "detect_sequence_patterns",
224
+ "extract_messages",
225
+ "find_all_magic_bytes",
226
+ "find_conserved_regions",
227
+ "find_dependencies",
228
+ "find_message_dependencies",
229
+ "find_variable_regions",
230
+ "generate_parser",
231
+ "get_adaptive_parameters",
232
+ "get_decoder",
233
+ "get_library",
234
+ "get_optimal_domain_order",
235
+ "get_protocol",
236
+ "infer_format",
237
+ "infer_rpni",
238
+ "infer_with_uncertainty",
239
+ "list_protocols",
240
+ "load_protocol",
241
+ "minimize_dfa",
242
+ "parser_to_python",
243
+ "parser_to_yaml",
244
+ "reassemble_tcp_stream",
245
+ "reassemble_udp_stream",
246
+ "recommend_analyses",
247
+ "suggest_measurements",
248
+ "to_dot",
249
+ "to_networkx",
250
+ "verify_crc",
251
+ ]
@@ -0,0 +1,153 @@
1
+ # Active Learning for DFA Inference
2
+
3
+ This module implements **Angluin's L\* algorithm** for learning deterministic finite automata (DFAs) through active learning.
4
+
5
+ ## Overview
6
+
7
+ L*is an active learning algorithm that learns the minimal DFA for a regular language by querying an oracle. Unlike passive learning (RPNI), which learns from a fixed dataset, L* can ask questions to refine its hypothesis.
8
+
9
+ ### Key Features
10
+
11
+ - **Minimal DFA**: Guaranteed to produce the minimal DFA (fewest states)
12
+ - **Polynomial complexity**: O(|Q|²|Σ|) membership queries, O(|Q|) equivalence queries
13
+ - **No negative examples required**: Only needs access to oracle
14
+ - **Interactive learning**: Can learn from live systems, simulators, or models
15
+
16
+ ## Algorithm
17
+
18
+ The L\* algorithm maintains an **observation table** with:
19
+
20
+ - **S**: Set of prefixes (representative strings for states)
21
+ - **E**: Set of suffixes (experiments to distinguish states)
22
+ - **T**: Table mapping (prefix + suffix) to acceptance
23
+
24
+ The algorithm iterates:
25
+
26
+ 1. Fill observation table using membership queries
27
+ 2. Make table **closed** (every extension has equivalent in S)
28
+ 3. Make table **consistent** (equivalent rows have equivalent extensions)
29
+ 4. Construct hypothesis DFA from table
30
+ 5. Ask equivalence query
31
+ 6. If counterexample: refine table and repeat
32
+ 7. Return final hypothesis
33
+
34
+ ## Usage
35
+
36
+ ### Basic Example
37
+
38
+ ```python
39
+ from oscura.inference.active_learning import LStarLearner, SimulatorTeacher
40
+
41
+ # Create oracle from captured traces
42
+ traces = [
43
+ ["CONNECT", "ACK"],
44
+ ["CONNECT", "ACK", "DATA", "ACK"],
45
+ ["CONNECT", "ACK", "DISCONNECT"]
46
+ ]
47
+
48
+ teacher = SimulatorTeacher(traces)
49
+
50
+ # Learn DFA
51
+ learner = LStarLearner(teacher, verbose=True)
52
+ dfa = learner.learn()
53
+
54
+ print(f"Learned DFA with {len(dfa.states)} states")
55
+ print(f"Membership queries: {learner.membership_queries}")
56
+ print(f"Equivalence queries: {learner.equivalence_queries}")
57
+ ```
58
+
59
+ ### Custom Oracle
60
+
61
+ ```python
62
+ from oscura.inference.active_learning import Oracle, LStarLearner
63
+ from oscura.inference.state_machine import FiniteAutomaton
64
+
65
+ class MyOracle(Oracle):
66
+ def membership_query(self, word: tuple[str, ...]) -> bool:
67
+ # Implement your logic here
68
+ return len(word) % 2 == 0
69
+
70
+ def equivalence_query(self, hypothesis: FiniteAutomaton) -> tuple[str, ...] | None:
71
+ # Test hypothesis and return counterexample if wrong
72
+ test_words = [(), ("a",), ("a", "a"), ("a", "a", "a")]
73
+ for word in test_words:
74
+ if hypothesis.accepts(list(word)) != self.membership_query(word):
75
+ return word
76
+ return None
77
+
78
+ def get_alphabet(self) -> set[str]:
79
+ return {"a", "b"}
80
+
81
+ oracle = MyOracle()
82
+ learner = LStarLearner(oracle)
83
+ dfa = learner.learn()
84
+ ```
85
+
86
+ ## Oracles (Teachers)
87
+
88
+ ### SimulatorTeacher
89
+
90
+ Learn from captured protocol traces. Treats all prefixes of traces as valid.
91
+
92
+ ```python
93
+ from oscura.inference.active_learning import SimulatorTeacher
94
+
95
+ traces = [["a", "b", "c"], ["a", "b", "d"]]
96
+ teacher = SimulatorTeacher(traces)
97
+ ```
98
+
99
+ **Membership queries**: Returns True if word is a prefix of any trace
100
+ **Equivalence queries**: Tests hypothesis against all traces and their prefixes
101
+
102
+ ### Future Oracles
103
+
104
+ - **InteractiveTeacher**: Query live device/system
105
+ - **ModelTeacher**: Query formal specification or model
106
+
107
+ ## Observation Table
108
+
109
+ The observation table is the core data structure:
110
+
111
+ ```python
112
+ from oscura.inference.active_learning import ObservationTable
113
+
114
+ table = ObservationTable(alphabet={"a", "b"})
115
+
116
+ # Check properties
117
+ table.is_closed() # All extensions covered
118
+ table.is_consistent() # Equivalent rows have equivalent extensions
119
+
120
+ # Convert to DFA
121
+ dfa = table.to_dfa()
122
+ ```
123
+
124
+ ## Performance
125
+
126
+ For small protocols (5-10 states):
127
+
128
+ - Membership queries: 50-200
129
+ - Equivalence queries: 3-10
130
+ - Time: < 1 second
131
+
132
+ The algorithm is polynomial in the number of states and alphabet size.
133
+
134
+ ## Academic Reference
135
+
136
+ ```
137
+ Angluin, D. (1987). Learning regular sets from queries and counterexamples.
138
+ Information and Computation, 75(2), 87-106.
139
+ ```
140
+
141
+ ## Comparison with RPNI
142
+
143
+ |Feature|L\* (Active)|RPNI (Passive)|
144
+ |---|---|---|---|---|---|---|
145
+ |Learning type|Active (queries oracle)|Passive (fixed dataset)|
146
+ |Minimal DFA|Yes|No (may have extra states)|
147
+ |Negative examples|Not required|Optional|
148
+ |Live learning|Yes|No|
149
+ |Query complexity|O(|Q|²|Σ|)|N/A|## See Also
150
+
151
+ - `oscura.inference.state_machine`: RPNI passive learning
152
+ - `examples/lstar_demo.py`: Complete usage examples
153
+ - `tests/unit/inference/test_lstar.py`: Test cases including academic examples
@@ -0,0 +1,38 @@
1
+ """Active learning for protocol inference.
2
+
3
+ This module implements Angluin's L* algorithm for learning deterministic
4
+ finite automata (DFAs) through active learning. Unlike passive learning
5
+ (RPNI), active learning interacts with an oracle to learn the minimal DFA.
6
+
7
+ Key advantages of L* over passive learning:
8
+ - Learns minimal DFA (fewest states)
9
+ - Polynomial query complexity
10
+ - No negative examples required
11
+ - Interactive learning from live systems
12
+
13
+ References:
14
+ Angluin, D. (1987). Learning regular sets from queries and counterexamples.
15
+ Information and Computation, 75(2), 87-106.
16
+
17
+ Example:
18
+ >>> from oscura.inference.active_learning import LStarLearner, SimulatorTeacher
19
+ >>> # Create oracle from captured traces
20
+ >>> traces = [['A', 'B', 'C'], ['A', 'B', 'B', 'C']]
21
+ >>> oracle = SimulatorTeacher(traces)
22
+ >>> # Learn DFA
23
+ >>> learner = LStarLearner(oracle)
24
+ >>> dfa = learner.learn()
25
+ >>> print(f"Learned DFA with {len(dfa.states)} states")
26
+ """
27
+
28
+ from oscura.inference.active_learning.lstar import LStarLearner
29
+ from oscura.inference.active_learning.observation_table import ObservationTable
30
+ from oscura.inference.active_learning.oracle import Oracle
31
+ from oscura.inference.active_learning.teachers import SimulatorTeacher
32
+
33
+ __all__ = [
34
+ "LStarLearner",
35
+ "ObservationTable",
36
+ "Oracle",
37
+ "SimulatorTeacher",
38
+ ]
@@ -0,0 +1,257 @@
1
+ """Angluin's L* algorithm for DFA learning.
2
+
3
+ This module implements the L* algorithm for learning deterministic finite
4
+ automata through active learning. The algorithm interacts with an oracle
5
+ to learn the minimal DFA for the target language.
6
+
7
+ References:
8
+ Angluin, D. (1987). Learning regular sets from queries and counterexamples.
9
+ Information and Computation, 75(2), 87-106.
10
+
11
+ Algorithm Overview:
12
+ 1. Initialize observation table with S={ε}, E={ε}
13
+ 2. Fill table using membership queries
14
+ 3. While table not closed or not consistent:
15
+ - If not closed: add row to S
16
+ - If not consistent: add column to E
17
+ - Refill table
18
+ 4. Construct hypothesis DFA from table
19
+ 5. Ask equivalence query
20
+ 6. If counterexample exists: process it and goto step 3
21
+ 7. Return hypothesis
22
+
23
+ Complexity:
24
+ - O(|Q|²|Σ|) membership queries where |Q| is number of states, |Σ| is alphabet
25
+ - O(|Q|) equivalence queries
26
+ - Produces minimal DFA (fewest states)
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from oscura.inference.active_learning.observation_table import ObservationTable
32
+ from oscura.inference.active_learning.oracle import Oracle # noqa: TC001
33
+ from oscura.inference.state_machine import FiniteAutomaton # noqa: TC001
34
+
35
+
36
+ class LStarLearner:
37
+ """Angluin's L* algorithm for learning DFAs.
38
+
39
+ The L* algorithm learns the minimal DFA for a regular language by
40
+ interacting with an oracle through membership and equivalence queries.
41
+
42
+ Key properties:
43
+ - Guaranteed convergence to correct minimal DFA
44
+ - Polynomial query complexity
45
+ - No negative examples required
46
+
47
+ Attributes:
48
+ oracle: Oracle for answering queries
49
+ verbose: Print algorithm progress
50
+ membership_queries: Count of membership queries made
51
+ equivalence_queries: Count of equivalence queries made
52
+ """
53
+
54
+ def __init__(self, oracle: Oracle, verbose: bool = False):
55
+ """Initialize L* learner.
56
+
57
+ Args:
58
+ oracle: Oracle for answering membership and equivalence queries
59
+ verbose: Print algorithm progress if True
60
+ """
61
+ self.oracle = oracle
62
+ self.verbose = verbose
63
+ self.membership_queries = 0
64
+ self.equivalence_queries = 0
65
+
66
+ def learn(self, max_iterations: int = 1000) -> FiniteAutomaton:
67
+ """Run L* algorithm to learn the target DFA.
68
+
69
+ The algorithm proceeds as follows:
70
+ 1. Initialize observation table with S={ε}, E={ε}
71
+ 2. Fill table using membership queries
72
+ 3. Make table closed and consistent
73
+ 4. Construct hypothesis DFA
74
+ 5. Ask equivalence query
75
+ 6. If counterexample: update table and repeat from step 3
76
+ 7. Return final hypothesis
77
+
78
+ Args:
79
+ max_iterations: Maximum iterations to prevent infinite loops
80
+
81
+ Returns:
82
+ Learned minimal DFA
83
+
84
+ Raises:
85
+ ValueError: If algorithm doesn't converge within max_iterations
86
+ """
87
+ # Initialize observation table
88
+ alphabet = self.oracle.get_alphabet()
89
+ table = ObservationTable(alphabet=alphabet)
90
+
91
+ if self.verbose:
92
+ print(f"L* learning started with alphabet: {sorted(alphabet)}")
93
+
94
+ # Fill initial table
95
+ self._fill_table(table)
96
+
97
+ iteration = 0
98
+ while iteration < max_iterations:
99
+ iteration += 1
100
+
101
+ # Make table closed and consistent
102
+ changed = True
103
+ while changed:
104
+ changed = False
105
+
106
+ # Make closed
107
+ if self._make_closed(table):
108
+ changed = True
109
+ self._fill_table(table)
110
+
111
+ # Make consistent
112
+ if self._make_consistent(table):
113
+ changed = True
114
+ self._fill_table(table)
115
+
116
+ # Construct hypothesis
117
+ hypothesis = table.to_dfa()
118
+
119
+ if self.verbose:
120
+ print(f"Iteration {iteration}: Hypothesis has {len(hypothesis.states)} states")
121
+
122
+ # Equivalence query
123
+ self.equivalence_queries += 1
124
+ counterexample = self.oracle.equivalence_query(hypothesis)
125
+
126
+ if counterexample is None:
127
+ # Hypothesis is correct
128
+ if self.verbose:
129
+ print(f"Learning complete after {iteration} iterations")
130
+ print(f"Membership queries: {self.membership_queries}")
131
+ print(f"Equivalence queries: {self.equivalence_queries}")
132
+ return hypothesis
133
+
134
+ # Process counterexample
135
+ if self.verbose:
136
+ print(f"Counterexample found: {counterexample}")
137
+
138
+ self._process_counterexample(table, counterexample)
139
+ self._fill_table(table)
140
+
141
+ raise ValueError(f"L* did not converge after {max_iterations} iterations")
142
+
143
+ def _fill_table(self, table: ObservationTable) -> None:
144
+ """Fill table entries using membership queries.
145
+
146
+ Fills all missing entries in the observation table by querying
147
+ the oracle for membership of prefix+suffix combinations.
148
+
149
+ For every s in S union SA and every e in E, ensures T[s·e] is filled.
150
+
151
+ Args:
152
+ table: Observation table to fill
153
+ """
154
+ # Compute SA = S union (S · Sigma)
155
+ SA = set(table.S)
156
+ for s in table.S:
157
+ for a in table.alphabet:
158
+ SA.add(s + (a,))
159
+
160
+ # Fill all entries
161
+ for s in SA:
162
+ for e in table.E:
163
+ word = s + e
164
+ if word not in table.T:
165
+ self.membership_queries += 1
166
+ table.T[word] = self.oracle.membership_query(word)
167
+
168
+ def _make_closed(self, table: ObservationTable) -> bool:
169
+ """Make table closed.
170
+
171
+ If table is not closed, adds one row from SA to S to move toward closure.
172
+
173
+ A table is closed when every row in SA has an equivalent row in S.
174
+ If not closed, we add the counterexample string to S.
175
+
176
+ Args:
177
+ table: Observation table to make closed
178
+
179
+ Returns:
180
+ True if table was modified, False if already closed
181
+ """
182
+ counterexample = table.find_closing_counterexample()
183
+
184
+ if counterexample is None:
185
+ return False
186
+
187
+ # Add counterexample to S
188
+ table.S.add(counterexample)
189
+
190
+ if self.verbose:
191
+ print(f"Added to S (closing): {counterexample}")
192
+
193
+ return True
194
+
195
+ def _make_consistent(self, table: ObservationTable) -> bool:
196
+ """Make table consistent.
197
+
198
+ If table is not consistent, adds a distinguishing suffix to E.
199
+
200
+ A table is consistent when identical rows in S have identical
201
+ one-step extensions. If inconsistent, we find the distinguishing
202
+ suffix and add it to E.
203
+
204
+ Args:
205
+ table: Observation table to make consistent
206
+
207
+ Returns:
208
+ True if table was modified, False if already consistent
209
+ """
210
+ result = table.find_consistency_counterexample()
211
+
212
+ if result is None:
213
+ return False
214
+
215
+ s1, s2, a = result
216
+
217
+ # Find distinguishing suffix
218
+ # row(s1·a) ≠ row(s2·a), so there exists e ∈ E where they differ
219
+ for e in table.E:
220
+ s1ae = s1 + (a,) + e
221
+ s2ae = s2 + (a,) + e
222
+
223
+ if table.T.get(s1ae, False) != table.T.get(s2ae, False):
224
+ # Found distinguishing suffix: a·e
225
+ distinguishing_suffix = (a,) + e
226
+ table.E.add(distinguishing_suffix)
227
+
228
+ if self.verbose:
229
+ print(f"Added to E (consistency): {distinguishing_suffix}")
230
+
231
+ return True
232
+
233
+ # Should never reach here if find_consistency_counterexample worked correctly
234
+ return False
235
+
236
+ def _process_counterexample(
237
+ self, table: ObservationTable, counterexample: tuple[str, ...]
238
+ ) -> None:
239
+ """Process counterexample from equivalence query.
240
+
241
+ Adds all prefixes of the counterexample and the counterexample
242
+ itself to the observation table to refine the hypothesis.
243
+
244
+ This uses the "add all suffixes" strategy which is simple and
245
+ ensures progress.
246
+
247
+ Args:
248
+ table: Observation table to update
249
+ counterexample: Counterexample word from equivalence query
250
+ """
251
+ # Add all suffixes of counterexample to E
252
+ for i in range(len(counterexample) + 1):
253
+ suffix = counterexample[i:]
254
+ table.E.add(suffix)
255
+
256
+ if self.verbose:
257
+ print(f"Added {len(counterexample) + 1} suffixes to E from counterexample")