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,405 @@
|
|
|
1
|
+
"""Automatic backend selection for optimal performance.
|
|
2
|
+
|
|
3
|
+
This module provides intelligent backend selection based on data characteristics,
|
|
4
|
+
available hardware, and performance requirements. Automatically chooses between
|
|
5
|
+
NumPy, Numba, GPU (CuPy), and distributed (Dask) backends.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from oscura.core.backend_selector import BackendSelector, select_backend
|
|
9
|
+
|
|
10
|
+
selector = BackendSelector()
|
|
11
|
+
backend = selector.select_for_fft(signal_size=10_000_000)
|
|
12
|
+
# Returns 'gpu' if available, else 'scipy'
|
|
13
|
+
|
|
14
|
+
Performance decision tree:
|
|
15
|
+
- Small data (<100K): NumPy/SciPy
|
|
16
|
+
- Medium data (100K-10M): Numba JIT
|
|
17
|
+
- Large data (>10M): GPU if available, else Numba
|
|
18
|
+
- Huge data (>1GB): Dask distributed
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
>>> from oscura.core.backend_selector import select_backend
|
|
22
|
+
>>> import numpy as np
|
|
23
|
+
>>>
|
|
24
|
+
>>> data = np.random.randn(50_000_000)
|
|
25
|
+
>>> backend = select_backend('fft', data_size=len(data))
|
|
26
|
+
>>> print(f"Selected backend: {backend}") # 'gpu' or 'scipy'
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from dataclasses import dataclass
|
|
32
|
+
from typing import Literal
|
|
33
|
+
|
|
34
|
+
import numpy as np
|
|
35
|
+
import psutil
|
|
36
|
+
|
|
37
|
+
# Check available backends
|
|
38
|
+
try:
|
|
39
|
+
from oscura.core.gpu_backend import gpu
|
|
40
|
+
|
|
41
|
+
HAS_GPU = gpu.gpu_available
|
|
42
|
+
except (ImportError, AttributeError):
|
|
43
|
+
HAS_GPU = False
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
import numba # type: ignore[import-untyped]
|
|
47
|
+
|
|
48
|
+
HAS_NUMBA = True
|
|
49
|
+
del numba
|
|
50
|
+
except ImportError:
|
|
51
|
+
HAS_NUMBA = False
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
import dask.array # type: ignore[import-not-found, import-untyped]
|
|
55
|
+
|
|
56
|
+
HAS_DASK = True
|
|
57
|
+
del dask # type: ignore[name-defined]
|
|
58
|
+
except ImportError:
|
|
59
|
+
HAS_DASK = False
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
import scipy.fft
|
|
63
|
+
|
|
64
|
+
HAS_SCIPY = True
|
|
65
|
+
del scipy
|
|
66
|
+
except ImportError:
|
|
67
|
+
HAS_SCIPY = False
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
BackendType = Literal["numpy", "scipy", "numba", "gpu", "dask"]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class BackendCapabilities:
|
|
75
|
+
"""Available backend capabilities on this system.
|
|
76
|
+
|
|
77
|
+
Attributes:
|
|
78
|
+
has_gpu: Whether GPU (CuPy) is available.
|
|
79
|
+
has_numba: Whether Numba JIT is available.
|
|
80
|
+
has_dask: Whether Dask distributed is available.
|
|
81
|
+
has_scipy: Whether SciPy is available.
|
|
82
|
+
cpu_count: Number of CPU cores.
|
|
83
|
+
total_memory_gb: Total system RAM in GB.
|
|
84
|
+
gpu_memory_gb: GPU memory in GB (0 if no GPU).
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
has_gpu: bool
|
|
88
|
+
has_numba: bool
|
|
89
|
+
has_dask: bool
|
|
90
|
+
has_scipy: bool
|
|
91
|
+
cpu_count: int
|
|
92
|
+
total_memory_gb: float
|
|
93
|
+
gpu_memory_gb: float
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_system_capabilities() -> BackendCapabilities:
|
|
97
|
+
"""Detect available backends and system resources.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
BackendCapabilities object with system information.
|
|
101
|
+
|
|
102
|
+
Example:
|
|
103
|
+
>>> caps = get_system_capabilities()
|
|
104
|
+
>>> if caps.has_gpu:
|
|
105
|
+
... print(f"GPU available with {caps.gpu_memory_gb:.1f} GB memory")
|
|
106
|
+
"""
|
|
107
|
+
# CPU and memory info
|
|
108
|
+
cpu_count = psutil.cpu_count(logical=False) or 1
|
|
109
|
+
total_memory = psutil.virtual_memory().total
|
|
110
|
+
total_memory_gb = total_memory / (1024**3)
|
|
111
|
+
|
|
112
|
+
# GPU memory
|
|
113
|
+
gpu_memory_gb = 0.0
|
|
114
|
+
if HAS_GPU:
|
|
115
|
+
try:
|
|
116
|
+
from oscura.core.gpu_backend import gpu
|
|
117
|
+
|
|
118
|
+
# Get GPU memory in bytes, convert to GB
|
|
119
|
+
gpu_memory_gb = gpu.get_memory_info()[1] / (1024**3) # type: ignore[attr-defined]
|
|
120
|
+
except Exception:
|
|
121
|
+
gpu_memory_gb = 0.0
|
|
122
|
+
|
|
123
|
+
return BackendCapabilities(
|
|
124
|
+
has_gpu=HAS_GPU,
|
|
125
|
+
has_numba=HAS_NUMBA,
|
|
126
|
+
has_dask=HAS_DASK,
|
|
127
|
+
has_scipy=HAS_SCIPY,
|
|
128
|
+
cpu_count=cpu_count,
|
|
129
|
+
total_memory_gb=total_memory_gb,
|
|
130
|
+
gpu_memory_gb=gpu_memory_gb,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class BackendSelector:
|
|
135
|
+
"""Intelligent backend selector for optimal performance.
|
|
136
|
+
|
|
137
|
+
This class analyzes data characteristics and system capabilities to
|
|
138
|
+
automatically select the best backend for each operation.
|
|
139
|
+
|
|
140
|
+
Example:
|
|
141
|
+
>>> selector = BackendSelector()
|
|
142
|
+
>>> # For FFT on 50M samples
|
|
143
|
+
>>> backend = selector.select_for_fft(50_000_000)
|
|
144
|
+
>>> # For edge detection with hysteresis
|
|
145
|
+
>>> backend = selector.select_for_edge_detection(1_000_000, has_hysteresis=True)
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def __init__(self) -> None:
|
|
149
|
+
"""Initialize backend selector with system capabilities."""
|
|
150
|
+
self.capabilities = get_system_capabilities()
|
|
151
|
+
|
|
152
|
+
def select_for_fft(
|
|
153
|
+
self,
|
|
154
|
+
data_size: int,
|
|
155
|
+
dtype: type = np.float64,
|
|
156
|
+
) -> BackendType:
|
|
157
|
+
"""Select optimal backend for FFT operations.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
data_size: Number of samples in signal.
|
|
161
|
+
dtype: Data type (affects memory usage).
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
BackendType: 'numpy', 'scipy', 'gpu', or 'dask'.
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
>>> selector = BackendSelector()
|
|
168
|
+
>>> backend = selector.select_for_fft(10_000_000)
|
|
169
|
+
"""
|
|
170
|
+
# Decision tree based on data size
|
|
171
|
+
if data_size > 100_000_000 and self.capabilities.has_dask:
|
|
172
|
+
# Huge data: use distributed
|
|
173
|
+
return "dask"
|
|
174
|
+
elif data_size > 10_000_000 and self.capabilities.has_gpu:
|
|
175
|
+
# Large data + GPU: use GPU
|
|
176
|
+
return "gpu"
|
|
177
|
+
elif self.capabilities.has_scipy:
|
|
178
|
+
# Use scipy.fft with workers (faster than numpy.fft)
|
|
179
|
+
return "scipy"
|
|
180
|
+
else:
|
|
181
|
+
# Fallback to numpy
|
|
182
|
+
return "numpy"
|
|
183
|
+
|
|
184
|
+
def select_for_edge_detection(
|
|
185
|
+
self,
|
|
186
|
+
data_size: int,
|
|
187
|
+
has_hysteresis: bool = False,
|
|
188
|
+
) -> BackendType:
|
|
189
|
+
"""Select optimal backend for edge detection.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
data_size: Number of samples in signal.
|
|
193
|
+
has_hysteresis: Whether hysteresis is used (affects vectorization).
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
BackendType: 'numpy', 'numba', or 'gpu'.
|
|
197
|
+
|
|
198
|
+
Example:
|
|
199
|
+
>>> selector = BackendSelector()
|
|
200
|
+
>>> backend = selector.select_for_edge_detection(5_000_000, has_hysteresis=True)
|
|
201
|
+
"""
|
|
202
|
+
if data_size > 10_000_000 and self.capabilities.has_gpu:
|
|
203
|
+
return "gpu"
|
|
204
|
+
elif has_hysteresis and self.capabilities.has_numba and data_size > 100_000:
|
|
205
|
+
return "numba"
|
|
206
|
+
elif has_hysteresis:
|
|
207
|
+
return "numpy" # Actually uses Python for hysteresis state machine
|
|
208
|
+
else:
|
|
209
|
+
return "numpy" # Vectorized without hysteresis
|
|
210
|
+
|
|
211
|
+
def select_for_correlation(
|
|
212
|
+
self,
|
|
213
|
+
signal1_size: int,
|
|
214
|
+
signal2_size: int,
|
|
215
|
+
mode: Literal["full", "valid", "same"] = "full",
|
|
216
|
+
) -> BackendType:
|
|
217
|
+
"""Select optimal backend for correlation.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
signal1_size: Size of first signal.
|
|
221
|
+
signal2_size: Size of second signal.
|
|
222
|
+
mode: Correlation mode.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Backend name.
|
|
226
|
+
|
|
227
|
+
Example:
|
|
228
|
+
>>> selector = BackendSelector()
|
|
229
|
+
>>> backend = selector.select_for_correlation(1_000_000, 10_000)
|
|
230
|
+
"""
|
|
231
|
+
total_size = signal1_size + signal2_size
|
|
232
|
+
output_size = self._estimate_correlation_output(signal1_size, signal2_size, mode)
|
|
233
|
+
|
|
234
|
+
# Estimate memory
|
|
235
|
+
total_memory_mb = (total_size + output_size) * 8 / (1024**2)
|
|
236
|
+
|
|
237
|
+
if total_memory_mb > self.capabilities.total_memory_gb * 1024 * 0.5:
|
|
238
|
+
# Would use >50% RAM: use chunked/streaming
|
|
239
|
+
return "dask" if self.capabilities.has_dask else "numpy"
|
|
240
|
+
elif signal1_size > 10_000_000 and self.capabilities.has_gpu:
|
|
241
|
+
return "gpu"
|
|
242
|
+
elif self.capabilities.has_scipy:
|
|
243
|
+
return "scipy"
|
|
244
|
+
else:
|
|
245
|
+
return "numpy"
|
|
246
|
+
|
|
247
|
+
def select_for_protocol_decode(
|
|
248
|
+
self,
|
|
249
|
+
data_size: int,
|
|
250
|
+
protocol: str,
|
|
251
|
+
) -> BackendType:
|
|
252
|
+
"""Select optimal backend for protocol decoding.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
data_size: Number of samples in signal.
|
|
256
|
+
protocol: Protocol name (e.g., 'uart', 'spi', 'i2c').
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
Backend name.
|
|
260
|
+
|
|
261
|
+
Example:
|
|
262
|
+
>>> selector = BackendSelector()
|
|
263
|
+
>>> backend = selector.select_for_protocol_decode(5_000_000, 'uart')
|
|
264
|
+
"""
|
|
265
|
+
# Protocol decoders use edge detection + state machines
|
|
266
|
+
# Large signals benefit from Numba-compiled state machines
|
|
267
|
+
if data_size > 1_000_000 and self.capabilities.has_numba:
|
|
268
|
+
return "numba"
|
|
269
|
+
else:
|
|
270
|
+
return "numpy"
|
|
271
|
+
|
|
272
|
+
def select_for_pattern_matching(
|
|
273
|
+
self,
|
|
274
|
+
data_size: int,
|
|
275
|
+
pattern_count: int,
|
|
276
|
+
approximate: bool = False,
|
|
277
|
+
) -> BackendType:
|
|
278
|
+
"""Select optimal backend for pattern matching.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
data_size: Size of data to search.
|
|
282
|
+
pattern_count: Number of patterns.
|
|
283
|
+
approximate: Whether approximate matching is acceptable.
|
|
284
|
+
|
|
285
|
+
Returns:
|
|
286
|
+
Backend name.
|
|
287
|
+
|
|
288
|
+
Example:
|
|
289
|
+
>>> selector = BackendSelector()
|
|
290
|
+
>>> backend = selector.select_for_pattern_matching(1_000_000, 100, approximate=True)
|
|
291
|
+
"""
|
|
292
|
+
# For approximate matching with many patterns, LSH is best
|
|
293
|
+
# Otherwise use standard string matching
|
|
294
|
+
if approximate and pattern_count > 10:
|
|
295
|
+
return "numpy" # LSH implementation in NumPy
|
|
296
|
+
elif data_size > 10_000_000:
|
|
297
|
+
return "numba"
|
|
298
|
+
else:
|
|
299
|
+
return "numpy"
|
|
300
|
+
|
|
301
|
+
def _estimate_correlation_output(
|
|
302
|
+
self,
|
|
303
|
+
size1: int,
|
|
304
|
+
size2: int,
|
|
305
|
+
mode: Literal["full", "valid", "same"],
|
|
306
|
+
) -> int:
|
|
307
|
+
"""Estimate output size of correlation.
|
|
308
|
+
|
|
309
|
+
Args:
|
|
310
|
+
size1: Size of first signal.
|
|
311
|
+
size2: Size of second signal.
|
|
312
|
+
mode: Correlation mode.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Estimated output size in samples.
|
|
316
|
+
"""
|
|
317
|
+
if mode == "full":
|
|
318
|
+
return size1 + size2 - 1
|
|
319
|
+
elif mode == "valid":
|
|
320
|
+
return max(size1, size2) - min(size1, size2) + 1
|
|
321
|
+
else: # same
|
|
322
|
+
return max(size1, size2)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
# Global selector instance
|
|
326
|
+
_global_selector: BackendSelector | None = None
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def get_global_selector() -> BackendSelector:
|
|
330
|
+
"""Get global backend selector instance (singleton).
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
Global BackendSelector instance.
|
|
334
|
+
|
|
335
|
+
Example:
|
|
336
|
+
>>> selector = get_global_selector()
|
|
337
|
+
>>> backend = selector.select_for_fft(1_000_000)
|
|
338
|
+
"""
|
|
339
|
+
global _global_selector
|
|
340
|
+
if _global_selector is None:
|
|
341
|
+
_global_selector = BackendSelector()
|
|
342
|
+
return _global_selector
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def select_backend(
|
|
346
|
+
operation: Literal[
|
|
347
|
+
"fft", "edge_detection", "correlation", "protocol_decode", "pattern_matching"
|
|
348
|
+
],
|
|
349
|
+
**kwargs: int | str | bool,
|
|
350
|
+
) -> BackendType:
|
|
351
|
+
"""Convenience function to select backend for an operation.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
operation: Type of operation.
|
|
355
|
+
**kwargs: Operation-specific parameters.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Selected backend name.
|
|
359
|
+
|
|
360
|
+
Example:
|
|
361
|
+
>>> backend = select_backend('fft', data_size=10_000_000)
|
|
362
|
+
>>> backend = select_backend('edge_detection', data_size=5_000_000, has_hysteresis=True)
|
|
363
|
+
>>> backend = select_backend('correlation', signal1_size=1_000_000, signal2_size=10_000)
|
|
364
|
+
"""
|
|
365
|
+
selector = get_global_selector()
|
|
366
|
+
|
|
367
|
+
if operation == "fft":
|
|
368
|
+
return selector.select_for_fft(
|
|
369
|
+
data_size=int(kwargs.get("data_size", 0)),
|
|
370
|
+
dtype=kwargs.get("dtype", np.float64), # type: ignore[arg-type]
|
|
371
|
+
)
|
|
372
|
+
elif operation == "edge_detection":
|
|
373
|
+
return selector.select_for_edge_detection(
|
|
374
|
+
data_size=int(kwargs.get("data_size", 0)),
|
|
375
|
+
has_hysteresis=bool(kwargs.get("has_hysteresis", False)),
|
|
376
|
+
)
|
|
377
|
+
elif operation == "correlation":
|
|
378
|
+
return selector.select_for_correlation(
|
|
379
|
+
signal1_size=int(kwargs.get("signal1_size", 0)),
|
|
380
|
+
signal2_size=int(kwargs.get("signal2_size", 0)),
|
|
381
|
+
mode=kwargs.get("mode", "full"), # type: ignore[arg-type]
|
|
382
|
+
)
|
|
383
|
+
elif operation == "protocol_decode":
|
|
384
|
+
return selector.select_for_protocol_decode(
|
|
385
|
+
data_size=int(kwargs.get("data_size", 0)),
|
|
386
|
+
protocol=str(kwargs.get("protocol", "")),
|
|
387
|
+
)
|
|
388
|
+
elif operation == "pattern_matching":
|
|
389
|
+
return selector.select_for_pattern_matching(
|
|
390
|
+
data_size=int(kwargs.get("data_size", 0)),
|
|
391
|
+
pattern_count=int(kwargs.get("pattern_count", 0)),
|
|
392
|
+
approximate=bool(kwargs.get("approximate", False)),
|
|
393
|
+
)
|
|
394
|
+
else:
|
|
395
|
+
return "numpy" # type: ignore[unreachable]
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
__all__ = [
|
|
399
|
+
"BackendCapabilities",
|
|
400
|
+
"BackendSelector",
|
|
401
|
+
"BackendType",
|
|
402
|
+
"get_global_selector",
|
|
403
|
+
"get_system_capabilities",
|
|
404
|
+
"select_backend",
|
|
405
|
+
]
|