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,392 @@
|
|
|
1
|
+
"""Parameter optimization for signal analysis.
|
|
2
|
+
|
|
3
|
+
This module provides parameter optimization utilities including
|
|
4
|
+
grid search, parameter space definition, and optimization result tracking.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import itertools
|
|
10
|
+
import logging
|
|
11
|
+
import time
|
|
12
|
+
from dataclasses import dataclass, field
|
|
13
|
+
from typing import TYPE_CHECKING, Any
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Callable, Iterator
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"GridSearch",
|
|
24
|
+
"OptimizationResult",
|
|
25
|
+
"ParameterSpace",
|
|
26
|
+
"optimize_parameters",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class ParameterSpace:
|
|
32
|
+
"""Definition of parameter search space.
|
|
33
|
+
|
|
34
|
+
Attributes:
|
|
35
|
+
name: Parameter name
|
|
36
|
+
values: List of values to try
|
|
37
|
+
low: Low bound (for continuous params)
|
|
38
|
+
high: High bound (for continuous params)
|
|
39
|
+
log_scale: Use logarithmic scale
|
|
40
|
+
num_samples: Number of samples for continuous
|
|
41
|
+
|
|
42
|
+
Example:
|
|
43
|
+
>>> # Discrete parameter
|
|
44
|
+
>>> window = ParameterSpace("window", values=["hann", "hamming", "blackman"])
|
|
45
|
+
>>> # Continuous parameter
|
|
46
|
+
>>> cutoff = ParameterSpace("cutoff", low=1e3, high=1e6, num_samples=10)
|
|
47
|
+
|
|
48
|
+
References:
|
|
49
|
+
API-014: Parameter Optimization
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
name: str
|
|
53
|
+
values: list[Any] | None = None
|
|
54
|
+
low: float | None = None
|
|
55
|
+
high: float | None = None
|
|
56
|
+
log_scale: bool = False
|
|
57
|
+
num_samples: int = 10
|
|
58
|
+
|
|
59
|
+
def __post_init__(self) -> None:
|
|
60
|
+
"""Generate values if continuous parameter."""
|
|
61
|
+
if self.values is None:
|
|
62
|
+
if self.low is not None and self.high is not None:
|
|
63
|
+
if self.log_scale:
|
|
64
|
+
self.values = list(
|
|
65
|
+
np.logspace(np.log10(self.low), np.log10(self.high), self.num_samples)
|
|
66
|
+
)
|
|
67
|
+
else:
|
|
68
|
+
self.values = list(np.linspace(self.low, self.high, self.num_samples))
|
|
69
|
+
else:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
f"Parameter {self.name}: must specify either values or (low, high) bounds"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
def __iter__(self) -> Iterator[Any]:
|
|
75
|
+
"""Iterate over parameter values."""
|
|
76
|
+
return iter(self.values or [])
|
|
77
|
+
|
|
78
|
+
def __len__(self) -> int:
|
|
79
|
+
"""Number of parameter values."""
|
|
80
|
+
return len(self.values or [])
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class OptimizationResult:
|
|
85
|
+
"""Result of parameter optimization.
|
|
86
|
+
|
|
87
|
+
Attributes:
|
|
88
|
+
best_params: Best parameter combination
|
|
89
|
+
best_score: Best objective score
|
|
90
|
+
all_results: All evaluated combinations
|
|
91
|
+
elapsed_time: Total optimization time
|
|
92
|
+
num_evaluations: Number of combinations evaluated
|
|
93
|
+
|
|
94
|
+
References:
|
|
95
|
+
API-014: Parameter Optimization
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
best_params: dict[str, Any]
|
|
99
|
+
best_score: float
|
|
100
|
+
all_results: list[tuple[dict[str, Any], float]] = field(default_factory=list)
|
|
101
|
+
elapsed_time: float = 0.0
|
|
102
|
+
num_evaluations: int = 0
|
|
103
|
+
|
|
104
|
+
def top_n(self, n: int = 5) -> list[tuple[dict[str, Any], float]]:
|
|
105
|
+
"""Get top N parameter combinations.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
n: Number of top results
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
List of (params, score) tuples
|
|
112
|
+
"""
|
|
113
|
+
sorted_results = sorted(self.all_results, key=lambda x: x[1], reverse=True)
|
|
114
|
+
return sorted_results[:n]
|
|
115
|
+
|
|
116
|
+
def to_dict(self) -> dict[str, Any]:
|
|
117
|
+
"""Convert to dictionary."""
|
|
118
|
+
return {
|
|
119
|
+
"best_params": self.best_params,
|
|
120
|
+
"best_score": self.best_score,
|
|
121
|
+
"num_evaluations": self.num_evaluations,
|
|
122
|
+
"elapsed_time": self.elapsed_time,
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class GridSearch:
|
|
127
|
+
"""Grid search optimization for parameter tuning.
|
|
128
|
+
|
|
129
|
+
Exhaustively searches all combinations of parameters to find
|
|
130
|
+
the best combination based on an objective function.
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
>>> def objective(params, data):
|
|
134
|
+
... result = analyze(data, **params)
|
|
135
|
+
... return result.snr
|
|
136
|
+
>>>
|
|
137
|
+
>>> search = GridSearch([
|
|
138
|
+
... ParameterSpace("nfft", values=[1024, 2048, 4096, 8192]),
|
|
139
|
+
... ParameterSpace("window", values=["hann", "hamming"]),
|
|
140
|
+
... ParameterSpace("overlap", low=0.25, high=0.75, num_samples=5)
|
|
141
|
+
... ])
|
|
142
|
+
>>>
|
|
143
|
+
>>> result = search.fit(objective, data)
|
|
144
|
+
>>> print(f"Best params: {result.best_params}")
|
|
145
|
+
|
|
146
|
+
References:
|
|
147
|
+
API-014: Parameter Optimization
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, param_spaces: list[ParameterSpace], verbose: bool = True):
|
|
151
|
+
"""Initialize grid search.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
param_spaces: List of parameter spaces
|
|
155
|
+
verbose: Print progress
|
|
156
|
+
"""
|
|
157
|
+
self.param_spaces = param_spaces
|
|
158
|
+
self.verbose = verbose
|
|
159
|
+
self._progress_callback: Callable[[int, int], None] | None = None
|
|
160
|
+
|
|
161
|
+
@property
|
|
162
|
+
def num_combinations(self) -> int:
|
|
163
|
+
"""Total number of parameter combinations."""
|
|
164
|
+
total = 1
|
|
165
|
+
for space in self.param_spaces:
|
|
166
|
+
total *= len(space)
|
|
167
|
+
return total
|
|
168
|
+
|
|
169
|
+
def on_progress(self, callback: Callable[[int, int], None]) -> GridSearch:
|
|
170
|
+
"""Set progress callback.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
callback: Function called with (current, total)
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Self (for chaining)
|
|
177
|
+
"""
|
|
178
|
+
self._progress_callback = callback
|
|
179
|
+
return self
|
|
180
|
+
|
|
181
|
+
def fit(
|
|
182
|
+
self,
|
|
183
|
+
objective: Callable[[dict[str, Any], Any], float],
|
|
184
|
+
data: Any,
|
|
185
|
+
*,
|
|
186
|
+
maximize: bool = True,
|
|
187
|
+
early_stop: float | None = None,
|
|
188
|
+
) -> OptimizationResult:
|
|
189
|
+
"""Run grid search optimization.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
objective: Objective function (params, data) -> score
|
|
193
|
+
data: Data to pass to objective
|
|
194
|
+
maximize: If True, maximize score; if False, minimize
|
|
195
|
+
early_stop: Stop if score reaches this threshold
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
Optimization result
|
|
199
|
+
"""
|
|
200
|
+
start_time = time.time()
|
|
201
|
+
all_results: list[tuple[dict[str, Any], float]] = []
|
|
202
|
+
best_params: dict[str, Any] = {}
|
|
203
|
+
best_score = float("-inf") if maximize else float("inf")
|
|
204
|
+
|
|
205
|
+
# Generate all combinations
|
|
206
|
+
param_names = [s.name for s in self.param_spaces]
|
|
207
|
+
param_values = [list(s) for s in self.param_spaces]
|
|
208
|
+
|
|
209
|
+
total = self.num_combinations
|
|
210
|
+
if self.verbose:
|
|
211
|
+
logger.info(f"Grid search: {total} combinations")
|
|
212
|
+
|
|
213
|
+
for i, values in enumerate(itertools.product(*param_values)):
|
|
214
|
+
params = dict(zip(param_names, values, strict=False))
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
score = objective(params, data)
|
|
218
|
+
except Exception as e:
|
|
219
|
+
logger.warning(f"Objective failed for {params}: {e}")
|
|
220
|
+
score = float("-inf") if maximize else float("inf")
|
|
221
|
+
|
|
222
|
+
all_results.append((params, score))
|
|
223
|
+
|
|
224
|
+
# Update best
|
|
225
|
+
if maximize:
|
|
226
|
+
if score > best_score:
|
|
227
|
+
best_score = score
|
|
228
|
+
best_params = params.copy()
|
|
229
|
+
elif score < best_score:
|
|
230
|
+
best_score = score
|
|
231
|
+
best_params = params.copy()
|
|
232
|
+
|
|
233
|
+
# Progress
|
|
234
|
+
if self._progress_callback:
|
|
235
|
+
self._progress_callback(i + 1, total)
|
|
236
|
+
|
|
237
|
+
# Early stopping
|
|
238
|
+
if early_stop is not None and (
|
|
239
|
+
(maximize and score >= early_stop) or (not maximize and score <= early_stop)
|
|
240
|
+
):
|
|
241
|
+
if self.verbose:
|
|
242
|
+
logger.info(f"Early stop at {i + 1}/{total}")
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
elapsed = time.time() - start_time
|
|
246
|
+
|
|
247
|
+
if self.verbose:
|
|
248
|
+
logger.info(f"Completed: best_score={best_score:.4f}, time={elapsed:.2f}s")
|
|
249
|
+
|
|
250
|
+
return OptimizationResult(
|
|
251
|
+
best_params=best_params,
|
|
252
|
+
best_score=best_score,
|
|
253
|
+
all_results=all_results,
|
|
254
|
+
elapsed_time=elapsed,
|
|
255
|
+
num_evaluations=len(all_results),
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class RandomSearch:
|
|
260
|
+
"""Random search optimization.
|
|
261
|
+
|
|
262
|
+
Samples random combinations from parameter space.
|
|
263
|
+
|
|
264
|
+
References:
|
|
265
|
+
API-014: Parameter Optimization
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
def __init__(
|
|
269
|
+
self,
|
|
270
|
+
param_spaces: list[ParameterSpace],
|
|
271
|
+
n_iterations: int = 100,
|
|
272
|
+
random_state: int | None = None,
|
|
273
|
+
):
|
|
274
|
+
"""Initialize random search.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
param_spaces: Parameter spaces
|
|
278
|
+
n_iterations: Number of random samples
|
|
279
|
+
random_state: Random seed
|
|
280
|
+
"""
|
|
281
|
+
self.param_spaces = param_spaces
|
|
282
|
+
self.n_iterations = n_iterations
|
|
283
|
+
self.random_state = random_state
|
|
284
|
+
|
|
285
|
+
def fit(
|
|
286
|
+
self,
|
|
287
|
+
objective: Callable[[dict[str, Any], Any], float],
|
|
288
|
+
data: Any,
|
|
289
|
+
*,
|
|
290
|
+
maximize: bool = True,
|
|
291
|
+
) -> OptimizationResult:
|
|
292
|
+
"""Run random search.
|
|
293
|
+
|
|
294
|
+
Args:
|
|
295
|
+
objective: Objective function
|
|
296
|
+
data: Data for objective
|
|
297
|
+
maximize: Maximize or minimize
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
Optimization result
|
|
301
|
+
"""
|
|
302
|
+
rng = np.random.default_rng(self.random_state)
|
|
303
|
+
start_time = time.time()
|
|
304
|
+
all_results: list[tuple[dict[str, Any], float]] = []
|
|
305
|
+
best_params: dict[str, Any] = {}
|
|
306
|
+
best_score = float("-inf") if maximize else float("inf")
|
|
307
|
+
|
|
308
|
+
for _ in range(self.n_iterations):
|
|
309
|
+
# Sample random parameters
|
|
310
|
+
params = {}
|
|
311
|
+
for space in self.param_spaces:
|
|
312
|
+
if space.values:
|
|
313
|
+
params[space.name] = rng.choice(space.values)
|
|
314
|
+
|
|
315
|
+
try:
|
|
316
|
+
score = objective(params, data)
|
|
317
|
+
except Exception:
|
|
318
|
+
score = float("-inf") if maximize else float("inf")
|
|
319
|
+
|
|
320
|
+
all_results.append((params, score))
|
|
321
|
+
|
|
322
|
+
if maximize:
|
|
323
|
+
if score > best_score:
|
|
324
|
+
best_score = score
|
|
325
|
+
best_params = params.copy()
|
|
326
|
+
elif score < best_score:
|
|
327
|
+
best_score = score
|
|
328
|
+
best_params = params.copy()
|
|
329
|
+
|
|
330
|
+
return OptimizationResult(
|
|
331
|
+
best_params=best_params,
|
|
332
|
+
best_score=best_score,
|
|
333
|
+
all_results=all_results,
|
|
334
|
+
elapsed_time=time.time() - start_time,
|
|
335
|
+
num_evaluations=len(all_results),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def optimize_parameters(
|
|
340
|
+
objective: Callable[[dict[str, Any], Any], float],
|
|
341
|
+
data: Any,
|
|
342
|
+
param_spaces: list[ParameterSpace] | dict[str, list[Any]],
|
|
343
|
+
*,
|
|
344
|
+
method: str = "grid",
|
|
345
|
+
maximize: bool = True,
|
|
346
|
+
**kwargs: Any,
|
|
347
|
+
) -> OptimizationResult:
|
|
348
|
+
"""Optimize parameters for objective function.
|
|
349
|
+
|
|
350
|
+
Convenience function for parameter optimization.
|
|
351
|
+
|
|
352
|
+
Args:
|
|
353
|
+
objective: Objective function (params, data) -> score
|
|
354
|
+
data: Data to pass to objective
|
|
355
|
+
param_spaces: Parameter spaces (list or dict)
|
|
356
|
+
method: Optimization method ("grid", "random")
|
|
357
|
+
maximize: Maximize or minimize
|
|
358
|
+
**kwargs: Additional arguments for optimizer
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
Optimization result
|
|
362
|
+
|
|
363
|
+
Raises:
|
|
364
|
+
ValueError: If method is not one of the supported types.
|
|
365
|
+
|
|
366
|
+
Example:
|
|
367
|
+
>>> result = optimize_parameters(
|
|
368
|
+
... objective=lambda p, d: analyze(d, **p).snr,
|
|
369
|
+
... data=trace,
|
|
370
|
+
... param_spaces={
|
|
371
|
+
... "nfft": [1024, 2048, 4096],
|
|
372
|
+
... "window": ["hann", "hamming"]
|
|
373
|
+
... }
|
|
374
|
+
... )
|
|
375
|
+
|
|
376
|
+
References:
|
|
377
|
+
API-014: Parameter Optimization
|
|
378
|
+
"""
|
|
379
|
+
# Convert dict to ParameterSpace list
|
|
380
|
+
if isinstance(param_spaces, dict):
|
|
381
|
+
param_spaces = [
|
|
382
|
+
ParameterSpace(name, values=values) for name, values in param_spaces.items()
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
if method == "grid":
|
|
386
|
+
optimizer = GridSearch(param_spaces, **kwargs)
|
|
387
|
+
elif method == "random":
|
|
388
|
+
optimizer = RandomSearch(param_spaces, **kwargs) # type: ignore[assignment]
|
|
389
|
+
else:
|
|
390
|
+
raise ValueError(f"Unknown optimization method: {method}")
|
|
391
|
+
|
|
392
|
+
return optimizer.fit(objective, data, maximize=maximize)
|