oscura 0.0.1__py3-none-any.whl → 0.1.1__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.
- oscura/__init__.py +813 -8
- oscura/__main__.py +392 -0
- oscura/analyzers/__init__.py +37 -0
- oscura/analyzers/digital/__init__.py +177 -0
- oscura/analyzers/digital/bus.py +691 -0
- oscura/analyzers/digital/clock.py +805 -0
- oscura/analyzers/digital/correlation.py +720 -0
- oscura/analyzers/digital/edges.py +632 -0
- oscura/analyzers/digital/extraction.py +413 -0
- oscura/analyzers/digital/quality.py +878 -0
- oscura/analyzers/digital/signal_quality.py +877 -0
- oscura/analyzers/digital/thresholds.py +708 -0
- oscura/analyzers/digital/timing.py +1104 -0
- oscura/analyzers/eye/__init__.py +46 -0
- oscura/analyzers/eye/diagram.py +434 -0
- oscura/analyzers/eye/metrics.py +555 -0
- oscura/analyzers/jitter/__init__.py +83 -0
- oscura/analyzers/jitter/ber.py +333 -0
- oscura/analyzers/jitter/decomposition.py +759 -0
- oscura/analyzers/jitter/measurements.py +413 -0
- oscura/analyzers/jitter/spectrum.py +220 -0
- oscura/analyzers/measurements.py +40 -0
- oscura/analyzers/packet/__init__.py +171 -0
- oscura/analyzers/packet/daq.py +1077 -0
- oscura/analyzers/packet/metrics.py +437 -0
- oscura/analyzers/packet/parser.py +327 -0
- oscura/analyzers/packet/payload.py +2156 -0
- oscura/analyzers/packet/payload_analysis.py +1312 -0
- oscura/analyzers/packet/payload_extraction.py +236 -0
- oscura/analyzers/packet/payload_patterns.py +670 -0
- oscura/analyzers/packet/stream.py +359 -0
- oscura/analyzers/patterns/__init__.py +266 -0
- oscura/analyzers/patterns/clustering.py +1036 -0
- oscura/analyzers/patterns/discovery.py +539 -0
- oscura/analyzers/patterns/learning.py +797 -0
- oscura/analyzers/patterns/matching.py +1091 -0
- oscura/analyzers/patterns/periodic.py +650 -0
- oscura/analyzers/patterns/sequences.py +767 -0
- oscura/analyzers/power/__init__.py +116 -0
- oscura/analyzers/power/ac_power.py +391 -0
- oscura/analyzers/power/basic.py +383 -0
- oscura/analyzers/power/conduction.py +314 -0
- oscura/analyzers/power/efficiency.py +297 -0
- oscura/analyzers/power/ripple.py +356 -0
- oscura/analyzers/power/soa.py +372 -0
- oscura/analyzers/power/switching.py +479 -0
- oscura/analyzers/protocol/__init__.py +150 -0
- oscura/analyzers/protocols/__init__.py +150 -0
- oscura/analyzers/protocols/base.py +500 -0
- oscura/analyzers/protocols/can.py +620 -0
- oscura/analyzers/protocols/can_fd.py +448 -0
- oscura/analyzers/protocols/flexray.py +405 -0
- oscura/analyzers/protocols/hdlc.py +399 -0
- oscura/analyzers/protocols/i2c.py +368 -0
- oscura/analyzers/protocols/i2s.py +296 -0
- oscura/analyzers/protocols/jtag.py +393 -0
- oscura/analyzers/protocols/lin.py +445 -0
- oscura/analyzers/protocols/manchester.py +333 -0
- oscura/analyzers/protocols/onewire.py +501 -0
- oscura/analyzers/protocols/spi.py +334 -0
- oscura/analyzers/protocols/swd.py +325 -0
- oscura/analyzers/protocols/uart.py +393 -0
- oscura/analyzers/protocols/usb.py +495 -0
- oscura/analyzers/signal_integrity/__init__.py +63 -0
- oscura/analyzers/signal_integrity/embedding.py +294 -0
- oscura/analyzers/signal_integrity/equalization.py +370 -0
- oscura/analyzers/signal_integrity/sparams.py +484 -0
- oscura/analyzers/spectral/__init__.py +53 -0
- oscura/analyzers/spectral/chunked.py +273 -0
- oscura/analyzers/spectral/chunked_fft.py +571 -0
- oscura/analyzers/spectral/chunked_wavelet.py +391 -0
- oscura/analyzers/spectral/fft.py +92 -0
- oscura/analyzers/statistical/__init__.py +250 -0
- oscura/analyzers/statistical/checksum.py +923 -0
- oscura/analyzers/statistical/chunked_corr.py +228 -0
- oscura/analyzers/statistical/classification.py +778 -0
- oscura/analyzers/statistical/entropy.py +1113 -0
- oscura/analyzers/statistical/ngrams.py +614 -0
- oscura/analyzers/statistics/__init__.py +119 -0
- oscura/analyzers/statistics/advanced.py +885 -0
- oscura/analyzers/statistics/basic.py +263 -0
- oscura/analyzers/statistics/correlation.py +630 -0
- oscura/analyzers/statistics/distribution.py +298 -0
- oscura/analyzers/statistics/outliers.py +463 -0
- oscura/analyzers/statistics/streaming.py +93 -0
- oscura/analyzers/statistics/trend.py +520 -0
- oscura/analyzers/validation.py +598 -0
- oscura/analyzers/waveform/__init__.py +36 -0
- oscura/analyzers/waveform/measurements.py +943 -0
- oscura/analyzers/waveform/measurements_with_uncertainty.py +371 -0
- oscura/analyzers/waveform/spectral.py +1689 -0
- oscura/analyzers/waveform/wavelets.py +298 -0
- oscura/api/__init__.py +62 -0
- oscura/api/dsl.py +538 -0
- oscura/api/fluent.py +571 -0
- oscura/api/operators.py +498 -0
- oscura/api/optimization.py +392 -0
- oscura/api/profiling.py +396 -0
- oscura/automotive/__init__.py +73 -0
- oscura/automotive/can/__init__.py +52 -0
- oscura/automotive/can/analysis.py +356 -0
- oscura/automotive/can/checksum.py +250 -0
- oscura/automotive/can/correlation.py +212 -0
- oscura/automotive/can/discovery.py +355 -0
- oscura/automotive/can/message_wrapper.py +375 -0
- oscura/automotive/can/models.py +385 -0
- oscura/automotive/can/patterns.py +381 -0
- oscura/automotive/can/session.py +452 -0
- oscura/automotive/can/state_machine.py +300 -0
- oscura/automotive/can/stimulus_response.py +461 -0
- oscura/automotive/dbc/__init__.py +15 -0
- oscura/automotive/dbc/generator.py +156 -0
- oscura/automotive/dbc/parser.py +146 -0
- oscura/automotive/dtc/__init__.py +30 -0
- oscura/automotive/dtc/database.py +3036 -0
- oscura/automotive/j1939/__init__.py +14 -0
- oscura/automotive/j1939/decoder.py +745 -0
- oscura/automotive/loaders/__init__.py +35 -0
- oscura/automotive/loaders/asc.py +98 -0
- oscura/automotive/loaders/blf.py +77 -0
- oscura/automotive/loaders/csv_can.py +136 -0
- oscura/automotive/loaders/dispatcher.py +136 -0
- oscura/automotive/loaders/mdf.py +331 -0
- oscura/automotive/loaders/pcap.py +132 -0
- oscura/automotive/obd/__init__.py +14 -0
- oscura/automotive/obd/decoder.py +707 -0
- oscura/automotive/uds/__init__.py +48 -0
- oscura/automotive/uds/decoder.py +265 -0
- oscura/automotive/uds/models.py +64 -0
- oscura/automotive/visualization.py +369 -0
- oscura/batch/__init__.py +55 -0
- oscura/batch/advanced.py +627 -0
- oscura/batch/aggregate.py +300 -0
- oscura/batch/analyze.py +139 -0
- oscura/batch/logging.py +487 -0
- oscura/batch/metrics.py +556 -0
- oscura/builders/__init__.py +41 -0
- oscura/builders/signal_builder.py +1131 -0
- oscura/cli/__init__.py +14 -0
- oscura/cli/batch.py +339 -0
- oscura/cli/characterize.py +273 -0
- oscura/cli/compare.py +775 -0
- oscura/cli/decode.py +551 -0
- oscura/cli/main.py +247 -0
- oscura/cli/shell.py +350 -0
- oscura/comparison/__init__.py +66 -0
- oscura/comparison/compare.py +397 -0
- oscura/comparison/golden.py +487 -0
- oscura/comparison/limits.py +391 -0
- oscura/comparison/mask.py +434 -0
- oscura/comparison/trace_diff.py +30 -0
- oscura/comparison/visualization.py +481 -0
- oscura/compliance/__init__.py +70 -0
- oscura/compliance/advanced.py +756 -0
- oscura/compliance/masks.py +363 -0
- oscura/compliance/reporting.py +483 -0
- oscura/compliance/testing.py +298 -0
- oscura/component/__init__.py +38 -0
- oscura/component/impedance.py +365 -0
- oscura/component/reactive.py +598 -0
- oscura/component/transmission_line.py +312 -0
- oscura/config/__init__.py +191 -0
- oscura/config/defaults.py +254 -0
- oscura/config/loader.py +348 -0
- oscura/config/memory.py +271 -0
- oscura/config/migration.py +458 -0
- oscura/config/pipeline.py +1077 -0
- oscura/config/preferences.py +530 -0
- oscura/config/protocol.py +875 -0
- oscura/config/schema.py +713 -0
- oscura/config/settings.py +420 -0
- oscura/config/thresholds.py +599 -0
- oscura/convenience.py +457 -0
- oscura/core/__init__.py +299 -0
- oscura/core/audit.py +457 -0
- oscura/core/backend_selector.py +405 -0
- oscura/core/cache.py +590 -0
- oscura/core/cancellation.py +439 -0
- oscura/core/confidence.py +225 -0
- oscura/core/config.py +506 -0
- oscura/core/correlation.py +216 -0
- oscura/core/cross_domain.py +422 -0
- oscura/core/debug.py +301 -0
- oscura/core/edge_cases.py +541 -0
- oscura/core/exceptions.py +535 -0
- oscura/core/gpu_backend.py +523 -0
- oscura/core/lazy.py +832 -0
- oscura/core/log_query.py +540 -0
- oscura/core/logging.py +931 -0
- oscura/core/logging_advanced.py +952 -0
- oscura/core/memoize.py +171 -0
- oscura/core/memory_check.py +274 -0
- oscura/core/memory_guard.py +290 -0
- oscura/core/memory_limits.py +336 -0
- oscura/core/memory_monitor.py +453 -0
- oscura/core/memory_progress.py +465 -0
- oscura/core/memory_warnings.py +315 -0
- oscura/core/numba_backend.py +362 -0
- oscura/core/performance.py +352 -0
- oscura/core/progress.py +524 -0
- oscura/core/provenance.py +358 -0
- oscura/core/results.py +331 -0
- oscura/core/types.py +504 -0
- oscura/core/uncertainty.py +383 -0
- oscura/discovery/__init__.py +52 -0
- oscura/discovery/anomaly_detector.py +672 -0
- oscura/discovery/auto_decoder.py +415 -0
- oscura/discovery/comparison.py +497 -0
- oscura/discovery/quality_validator.py +528 -0
- oscura/discovery/signal_detector.py +769 -0
- oscura/dsl/__init__.py +73 -0
- oscura/dsl/commands.py +246 -0
- oscura/dsl/interpreter.py +455 -0
- oscura/dsl/parser.py +689 -0
- oscura/dsl/repl.py +172 -0
- oscura/exceptions.py +59 -0
- oscura/exploratory/__init__.py +111 -0
- oscura/exploratory/error_recovery.py +642 -0
- oscura/exploratory/fuzzy.py +513 -0
- oscura/exploratory/fuzzy_advanced.py +786 -0
- oscura/exploratory/legacy.py +831 -0
- oscura/exploratory/parse.py +358 -0
- oscura/exploratory/recovery.py +275 -0
- oscura/exploratory/sync.py +382 -0
- oscura/exploratory/unknown.py +707 -0
- oscura/export/__init__.py +25 -0
- oscura/export/wireshark/README.md +265 -0
- oscura/export/wireshark/__init__.py +47 -0
- oscura/export/wireshark/generator.py +312 -0
- oscura/export/wireshark/lua_builder.py +159 -0
- oscura/export/wireshark/templates/dissector.lua.j2 +92 -0
- oscura/export/wireshark/type_mapping.py +165 -0
- oscura/export/wireshark/validator.py +105 -0
- oscura/exporters/__init__.py +94 -0
- oscura/exporters/csv.py +303 -0
- oscura/exporters/exporters.py +44 -0
- oscura/exporters/hdf5.py +219 -0
- oscura/exporters/html_export.py +701 -0
- oscura/exporters/json_export.py +291 -0
- oscura/exporters/markdown_export.py +367 -0
- oscura/exporters/matlab_export.py +354 -0
- oscura/exporters/npz_export.py +219 -0
- oscura/exporters/spice_export.py +210 -0
- oscura/extensibility/__init__.py +131 -0
- oscura/extensibility/docs.py +752 -0
- oscura/extensibility/extensions.py +1125 -0
- oscura/extensibility/logging.py +259 -0
- oscura/extensibility/measurements.py +485 -0
- oscura/extensibility/plugins.py +414 -0
- oscura/extensibility/registry.py +346 -0
- oscura/extensibility/templates.py +913 -0
- oscura/extensibility/validation.py +651 -0
- oscura/filtering/__init__.py +89 -0
- oscura/filtering/base.py +563 -0
- oscura/filtering/convenience.py +564 -0
- oscura/filtering/design.py +725 -0
- oscura/filtering/filters.py +32 -0
- oscura/filtering/introspection.py +605 -0
- oscura/guidance/__init__.py +24 -0
- oscura/guidance/recommender.py +429 -0
- oscura/guidance/wizard.py +518 -0
- oscura/inference/__init__.py +251 -0
- oscura/inference/active_learning/README.md +153 -0
- oscura/inference/active_learning/__init__.py +38 -0
- oscura/inference/active_learning/lstar.py +257 -0
- oscura/inference/active_learning/observation_table.py +230 -0
- oscura/inference/active_learning/oracle.py +78 -0
- oscura/inference/active_learning/teachers/__init__.py +15 -0
- oscura/inference/active_learning/teachers/simulator.py +192 -0
- oscura/inference/adaptive_tuning.py +453 -0
- oscura/inference/alignment.py +653 -0
- oscura/inference/bayesian.py +943 -0
- oscura/inference/binary.py +1016 -0
- oscura/inference/crc_reverse.py +711 -0
- oscura/inference/logic.py +288 -0
- oscura/inference/message_format.py +1305 -0
- oscura/inference/protocol.py +417 -0
- oscura/inference/protocol_dsl.py +1084 -0
- oscura/inference/protocol_library.py +1230 -0
- oscura/inference/sequences.py +809 -0
- oscura/inference/signal_intelligence.py +1509 -0
- oscura/inference/spectral.py +215 -0
- oscura/inference/state_machine.py +634 -0
- oscura/inference/stream.py +918 -0
- oscura/integrations/__init__.py +59 -0
- oscura/integrations/llm.py +1827 -0
- oscura/jupyter/__init__.py +32 -0
- oscura/jupyter/display.py +268 -0
- oscura/jupyter/magic.py +334 -0
- oscura/loaders/__init__.py +526 -0
- oscura/loaders/binary.py +69 -0
- oscura/loaders/configurable.py +1255 -0
- oscura/loaders/csv.py +26 -0
- oscura/loaders/csv_loader.py +473 -0
- oscura/loaders/hdf5.py +9 -0
- oscura/loaders/hdf5_loader.py +510 -0
- oscura/loaders/lazy.py +370 -0
- oscura/loaders/mmap_loader.py +583 -0
- oscura/loaders/numpy_loader.py +436 -0
- oscura/loaders/pcap.py +432 -0
- oscura/loaders/preprocessing.py +368 -0
- oscura/loaders/rigol.py +287 -0
- oscura/loaders/sigrok.py +321 -0
- oscura/loaders/tdms.py +367 -0
- oscura/loaders/tektronix.py +711 -0
- oscura/loaders/validation.py +584 -0
- oscura/loaders/vcd.py +464 -0
- oscura/loaders/wav.py +233 -0
- oscura/math/__init__.py +45 -0
- oscura/math/arithmetic.py +824 -0
- oscura/math/interpolation.py +413 -0
- oscura/onboarding/__init__.py +39 -0
- oscura/onboarding/help.py +498 -0
- oscura/onboarding/tutorials.py +405 -0
- oscura/onboarding/wizard.py +466 -0
- oscura/optimization/__init__.py +19 -0
- oscura/optimization/parallel.py +440 -0
- oscura/optimization/search.py +532 -0
- oscura/pipeline/__init__.py +43 -0
- oscura/pipeline/base.py +338 -0
- oscura/pipeline/composition.py +242 -0
- oscura/pipeline/parallel.py +448 -0
- oscura/pipeline/pipeline.py +375 -0
- oscura/pipeline/reverse_engineering.py +1119 -0
- oscura/plugins/__init__.py +122 -0
- oscura/plugins/base.py +272 -0
- oscura/plugins/cli.py +497 -0
- oscura/plugins/discovery.py +411 -0
- oscura/plugins/isolation.py +418 -0
- oscura/plugins/lifecycle.py +959 -0
- oscura/plugins/manager.py +493 -0
- oscura/plugins/registry.py +421 -0
- oscura/plugins/versioning.py +372 -0
- oscura/py.typed +0 -0
- oscura/quality/__init__.py +65 -0
- oscura/quality/ensemble.py +740 -0
- oscura/quality/explainer.py +338 -0
- oscura/quality/scoring.py +616 -0
- oscura/quality/warnings.py +456 -0
- oscura/reporting/__init__.py +248 -0
- oscura/reporting/advanced.py +1234 -0
- oscura/reporting/analyze.py +448 -0
- oscura/reporting/argument_preparer.py +596 -0
- oscura/reporting/auto_report.py +507 -0
- oscura/reporting/batch.py +615 -0
- oscura/reporting/chart_selection.py +223 -0
- oscura/reporting/comparison.py +330 -0
- oscura/reporting/config.py +615 -0
- oscura/reporting/content/__init__.py +39 -0
- oscura/reporting/content/executive.py +127 -0
- oscura/reporting/content/filtering.py +191 -0
- oscura/reporting/content/minimal.py +257 -0
- oscura/reporting/content/verbosity.py +162 -0
- oscura/reporting/core.py +508 -0
- oscura/reporting/core_formats/__init__.py +17 -0
- oscura/reporting/core_formats/multi_format.py +210 -0
- oscura/reporting/engine.py +836 -0
- oscura/reporting/export.py +366 -0
- oscura/reporting/formatting/__init__.py +129 -0
- oscura/reporting/formatting/emphasis.py +81 -0
- oscura/reporting/formatting/numbers.py +403 -0
- oscura/reporting/formatting/standards.py +55 -0
- oscura/reporting/formatting.py +466 -0
- oscura/reporting/html.py +578 -0
- oscura/reporting/index.py +590 -0
- oscura/reporting/multichannel.py +296 -0
- oscura/reporting/output.py +379 -0
- oscura/reporting/pdf.py +373 -0
- oscura/reporting/plots.py +731 -0
- oscura/reporting/pptx_export.py +360 -0
- oscura/reporting/renderers/__init__.py +11 -0
- oscura/reporting/renderers/pdf.py +94 -0
- oscura/reporting/sections.py +471 -0
- oscura/reporting/standards.py +680 -0
- oscura/reporting/summary_generator.py +368 -0
- oscura/reporting/tables.py +397 -0
- oscura/reporting/template_system.py +724 -0
- oscura/reporting/templates/__init__.py +15 -0
- oscura/reporting/templates/definition.py +205 -0
- oscura/reporting/templates/index.html +649 -0
- oscura/reporting/templates/index.md +173 -0
- oscura/schemas/__init__.py +158 -0
- oscura/schemas/bus_configuration.json +322 -0
- oscura/schemas/device_mapping.json +182 -0
- oscura/schemas/packet_format.json +418 -0
- oscura/schemas/protocol_definition.json +363 -0
- oscura/search/__init__.py +16 -0
- oscura/search/anomaly.py +292 -0
- oscura/search/context.py +149 -0
- oscura/search/pattern.py +160 -0
- oscura/session/__init__.py +34 -0
- oscura/session/annotations.py +289 -0
- oscura/session/history.py +313 -0
- oscura/session/session.py +445 -0
- oscura/streaming/__init__.py +43 -0
- oscura/streaming/chunked.py +611 -0
- oscura/streaming/progressive.py +393 -0
- oscura/streaming/realtime.py +622 -0
- oscura/testing/__init__.py +54 -0
- oscura/testing/synthetic.py +808 -0
- oscura/triggering/__init__.py +68 -0
- oscura/triggering/base.py +229 -0
- oscura/triggering/edge.py +353 -0
- oscura/triggering/pattern.py +344 -0
- oscura/triggering/pulse.py +581 -0
- oscura/triggering/window.py +453 -0
- oscura/ui/__init__.py +48 -0
- oscura/ui/formatters.py +526 -0
- oscura/ui/progressive_display.py +340 -0
- oscura/utils/__init__.py +99 -0
- oscura/utils/autodetect.py +338 -0
- oscura/utils/buffer.py +389 -0
- oscura/utils/lazy.py +407 -0
- oscura/utils/lazy_imports.py +147 -0
- oscura/utils/memory.py +836 -0
- oscura/utils/memory_advanced.py +1326 -0
- oscura/utils/memory_extensions.py +465 -0
- oscura/utils/progressive.py +352 -0
- oscura/utils/windowing.py +362 -0
- oscura/visualization/__init__.py +321 -0
- oscura/visualization/accessibility.py +526 -0
- oscura/visualization/annotations.py +374 -0
- oscura/visualization/axis_scaling.py +305 -0
- oscura/visualization/colors.py +453 -0
- oscura/visualization/digital.py +337 -0
- oscura/visualization/eye.py +420 -0
- oscura/visualization/histogram.py +281 -0
- oscura/visualization/interactive.py +858 -0
- oscura/visualization/jitter.py +702 -0
- oscura/visualization/keyboard.py +394 -0
- oscura/visualization/layout.py +365 -0
- oscura/visualization/optimization.py +1028 -0
- oscura/visualization/palettes.py +446 -0
- oscura/visualization/plot.py +92 -0
- oscura/visualization/power.py +290 -0
- oscura/visualization/power_extended.py +626 -0
- oscura/visualization/presets.py +467 -0
- oscura/visualization/protocols.py +932 -0
- oscura/visualization/render.py +207 -0
- oscura/visualization/rendering.py +444 -0
- oscura/visualization/reverse_engineering.py +791 -0
- oscura/visualization/signal_integrity.py +808 -0
- oscura/visualization/specialized.py +553 -0
- oscura/visualization/spectral.py +811 -0
- oscura/visualization/styles.py +381 -0
- oscura/visualization/thumbnails.py +311 -0
- oscura/visualization/time_axis.py +351 -0
- oscura/visualization/waveform.py +367 -0
- oscura/workflow/__init__.py +13 -0
- oscura/workflow/dag.py +377 -0
- oscura/workflows/__init__.py +58 -0
- oscura/workflows/compliance.py +280 -0
- oscura/workflows/digital.py +272 -0
- oscura/workflows/multi_trace.py +502 -0
- oscura/workflows/power.py +178 -0
- oscura/workflows/protocol.py +492 -0
- oscura/workflows/reverse_engineering.py +639 -0
- oscura/workflows/signal_integrity.py +227 -0
- oscura-0.1.1.dist-info/METADATA +300 -0
- oscura-0.1.1.dist-info/RECORD +463 -0
- oscura-0.1.1.dist-info/entry_points.txt +2 -0
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/licenses/LICENSE +1 -1
- oscura-0.0.1.dist-info/METADATA +0 -63
- oscura-0.0.1.dist-info/RECORD +0 -5
- {oscura-0.0.1.dist-info → oscura-0.1.1.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""Auto-inference and smart defaults for Oscura.
|
|
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")
|