beqforge 0.3.2__tar.gz → 0.3.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {beqforge-0.3.2 → beqforge-0.3.4}/.github/workflows/build-device-check.yml +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/.github/workflows/build-executable.yml +2 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/.github/workflows/tests.yml +3 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/CLAUDE.md +2 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/PKG-INFO +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/TODO.md +3 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/beq_common/__init__.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/AGENTS.md +9 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/accept.py +7 -5
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/charts.py +7 -7
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/design.py +3 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/designer.py +10 -8
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/explain.py +4 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/extraction.py +88 -12
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/filters.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/harness.py +4 -4
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/impact.py +117 -27
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/impact.toml +91 -4
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/pipeline.py +266 -64
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/record.py +12 -4
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/verify.py +150 -27
- {beqforge-0.3.2 → beqforge-0.3.4}/negative_corpus.json +66 -66
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/bass-ceiling.md +6 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/design-version.md +2 -0
- beqforge-0.3.4/plans/designer-latency.md +175 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/done-baseline-evidence.md +13 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/library-review-improvements.md +61 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/redesign-scope.md +51 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/test-portability-and-runtime.md +14 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/fixtures/design_golden.json +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_accept.py +4 -5
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_cache.py +2 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_channel_evidence.py +2 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_corpus.py +32 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_design.py +4 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_designer.py +24 -19
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_device_rate.py +16 -5
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_evidence.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_evidence_excess.py +1 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_exclusions.py +11 -5
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_extraction.py +67 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_filters.py +10 -10
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_guard.py +1 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_impact.py +93 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_jacobian.py +3 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_pipeline.py +175 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_playback.py +160 -5
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_playback_headroom.py +118 -4
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_publication.py +6 -7
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_record.py +6 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_reference.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_references.py +132 -17
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_strategy_config.py +7 -8
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_target_fit.py +1 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_timings.py +25 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_extract_layout.py +13 -14
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/design_beq.py +6 -6
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/designer_server.py +11 -11
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/e10_cases.py +2 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/e10_run.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/a1_sweep.py +3 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/compare_verdicts.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/inject_variants.py +4 -4
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p14_compare.py +11 -9
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p14_run_pipeline.py +6 -5
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p18_compare_structure.py +3 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p18_greedy.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p18_seeded.py +1 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p20_smooth_drift.py +23 -9
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/probe.py +9 -6
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/refit_probe.py +204 -52
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/request_timing.py +3 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/score_injected.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/t8_floor_rules.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/extract.py +4 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/impact.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/negative_corpus.py +4 -4
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/render_ledger.py +6 -3
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/replay.py +2 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/smoke_test_exe.py +6 -6
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/summarise.py +4 -5
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/validate_evidence.py +6 -4
- {beqforge-0.3.2 → beqforge-0.3.4}/uv.lock +38 -38
- {beqforge-0.3.2 → beqforge-0.3.4}/.dockerignore +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.gitattributes +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.github/dependabot.yml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.github/workflows/build-designer-image.yml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.github/workflows/dependabot-auto-merge.yaml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.gitignore +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.idea/.gitignore +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.idea/beqforge.iml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.idea/editor.xml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.idea/jsonSchemas.xml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.idea/misc.xml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.idea/modules.xml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/.idea/vcs.xml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/AGENTS.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/LICENCE.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/README.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beq_common/AGENTS.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beq_common/biquad.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beq_common/provenance.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beq_common/publication.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beq_common/types.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/__init__.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/adjustments.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/beqd.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/biquad.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/cache.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/cli.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/dc_offset.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/diagnose.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/e10.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/fit_kernel.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/identify.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/material.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/reference.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge/rolloff.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge-device-check.spec +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge.spec +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/AGENTS.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/__init__.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/analyse.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/audio.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/catalogue.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/cli.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/coefficients.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/engines.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/evidence.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/manifest.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/measurement.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/profiles.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/qualification.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/report.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/transactions.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqforge_device_check/verify.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/AGENTS.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/__init__.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/cache.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/cli.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/core.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/data/seed-manifest.json +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/data/seed.json.gz +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/beqoptimiser/parameters.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/device-check-examples/minidsp-2x4hd-usb-loopback.json +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/device-check-examples/simulation.json +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/device-check.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser-report/README.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser-report/assets/aggregate-48000.png +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser-report/assets/aggregate-96000.png +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser-report/assets/example-1.png +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser-report/assets/example-2.png +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser-report/entry-results.json.gz +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser-report/statistics.json +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/docs/optimiser.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/evidence_validation.json +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/packaging/designer/Dockerfile +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/packaging/designer/beqforge-designer.cmd +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/packaging/designer/smoke.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/packaging/device-check/NOTICE.txt +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/E10-validation-protocol.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/F2-catalogue-filter-optimisation.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/F2-device-precision-validation.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/F2-package-consolidation.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/R2a-server-stage-cache.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/contract-1.5-adjustments.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/default-record-and-log-locations.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/done-design-and-pipeline.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/e10-titles-v1.json +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/fit-kernel.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/fit-weighting.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/library-v5-review.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/parameter-space-optimisation.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/playback-contract.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/research-design-decisions.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/texture-yardstick-sub-band.md +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/plans/texture-yardstick-sub-band.patch +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/pyproject.toml +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/__init__.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/conftest.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/fixtures/optimiser_seed_entry.json +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_cli.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_adjustments.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_cache_stores.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_charts.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_dc_offset.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_designer_server.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_diagnose.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_explain.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_frozen_cache.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_golden.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_harness.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_headroom.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_hum.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_identify.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_parallel.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_record_fingerprint.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_ripple_band.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_rolloff.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_design_verify.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_designer_image.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_designer_server_startup.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_analyse.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_catalogue.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_catalogue_predict.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_cli.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_engines.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_evidence.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_examples.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_manifest.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_measurement.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_qualification.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_transactions.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_device_check_verify.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_e10.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_fit_kernel.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_optimiser.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_optimiser_cache.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_optimiser_parameters.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_optimiser_report.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tests/test_package_boundaries.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/__init__.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/build_optimiser_seed.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/compare_records.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p14_jacobian.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p14_surrogate.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p18_compare_greedy.py +2 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p18_compare_seeded.py +2 -2
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/p18_structure.py +1 -1
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/experiments/parameter_benchmark.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/ledger_template.html +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/optimiser_catalogue_report.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/prepare_device_check_helper.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/seed_optimiser_cache.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/smoke_test_device_check.py +0 -0
- {beqforge-0.3.2 → beqforge-0.3.4}/tools/smoke_test_install.py +0 -0
|
@@ -112,7 +112,7 @@ jobs:
|
|
|
112
112
|
contents: read
|
|
113
113
|
steps:
|
|
114
114
|
- uses: actions/checkout@v7
|
|
115
|
-
- uses: astral-sh/setup-uv@v10.3.
|
|
115
|
+
- uses: astral-sh/setup-uv@v10.3.3
|
|
116
116
|
with:
|
|
117
117
|
enable-cache: true
|
|
118
118
|
- name: Build wheel and source distribution
|
|
@@ -201,7 +201,7 @@ jobs:
|
|
|
201
201
|
- uses: actions/checkout@v7
|
|
202
202
|
|
|
203
203
|
- name: Install uv
|
|
204
|
-
uses: astral-sh/setup-uv@v10.3.
|
|
204
|
+
uses: astral-sh/setup-uv@v10.3.3
|
|
205
205
|
with:
|
|
206
206
|
enable-cache: true
|
|
207
207
|
|
|
@@ -67,7 +67,7 @@ jobs:
|
|
|
67
67
|
run: choco install ffmpeg -y
|
|
68
68
|
|
|
69
69
|
- name: Install uv
|
|
70
|
-
uses: astral-sh/setup-uv@v10.3.
|
|
70
|
+
uses: astral-sh/setup-uv@v10.3.3
|
|
71
71
|
with:
|
|
72
72
|
enable-cache: true
|
|
73
73
|
|
|
@@ -100,7 +100,7 @@ jobs:
|
|
|
100
100
|
- uses: actions/checkout@v7
|
|
101
101
|
|
|
102
102
|
- name: Install uv
|
|
103
|
-
uses: astral-sh/setup-uv@v10.3.
|
|
103
|
+
uses: astral-sh/setup-uv@v10.3.3
|
|
104
104
|
with:
|
|
105
105
|
enable-cache: true
|
|
106
106
|
|
|
@@ -131,7 +131,7 @@ jobs:
|
|
|
131
131
|
profile: [base, designer, optimiser, device-check, all]
|
|
132
132
|
steps:
|
|
133
133
|
- uses: actions/checkout@v7
|
|
134
|
-
- uses: astral-sh/setup-uv@v10.3.
|
|
134
|
+
- uses: astral-sh/setup-uv@v10.3.3
|
|
135
135
|
with:
|
|
136
136
|
enable-cache: true
|
|
137
137
|
- name: Build the single distribution
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
protocol for anything that can change a decision, and the outcome written into
|
|
13
13
|
`TODO.md` and the completed evidence under `plans/`. Don't substitute a full nine-title rerun, and don't skip it.
|
|
14
14
|
* Don't run `uv run python tools/design_beq.py` casually against real material — a run is
|
|
15
|
-
40-80 s a title. Wrap any timed run in `systemd-inhibit` (see AGENTS.md's "
|
|
16
|
-
|
|
15
|
+
40-80 s a title. Wrap any timed run in `systemd-inhibit` (see AGENTS.md's "Long-running jobs and
|
|
16
|
+
timing") and use the background-task workflow rather than a foreground `sleep` loop.
|
|
17
17
|
* The stage cache (`beqforge/cache.py`) is on by default and keyed per stage; `--fresh` forces a
|
|
18
18
|
recompute, `--no-cache` uses neither. Don't assume a change took effect without one of these if
|
|
19
19
|
you're re-running against the same material.
|
|
@@ -47,16 +47,16 @@ ID alone (AGENTS.md says where each lives), and the stale contract versions are
|
|
|
47
47
|
| 20 | Protective corner (former TODO 9) | parked | The corner is already derived from boost cap and implied order, floored by `lowest_frequency_hz`. No case requires an independent override or different fallback alignment; reopen with one. |
|
|
48
48
|
| 21 | Device configuration on the wire | parked | The current contract deliberately omits device preference; server flags already configure realisation. Reopen if one server must serve different device formats/rates per request; then agree an upstream extension. Hardware validity remains F2. |
|
|
49
49
|
| 22 | Catalogue comparison (former TODO 8) | parked | A disagreement detector has no demonstrated accuracy benefit. Reopen with an independent discrepancy to investigate; authored filters remain comparisons, never calibration targets. |
|
|
50
|
-
| 23 | Performance | fit kernel released in 0.3.0 (design version 8); 0.3.1 fixes the designer image run as another user | [The measurement and outcome](plans/fit-kernel.md#outcome). The fit's search now scores each DE generation at once in the linear domain (`beqforge/fit_kernel.py`; numba, numpy fallback, bit-identical). Real runs on the 11 titles: 703 to 283 s wall, 1905 to 373 CPU-s; the `fit` stage 10x, so a title is 28 s and 37 CPU-s. A re-roll: refit probe no worse on balance, no baseline title's winner or verdict changed in real runs, protocol and corpus unchanged where gated. **Left:** the fit re-rolls near the top of the band as freely as at 10 Hz (row 37: weighting the search works, held back behind row 36); measure a library sample for the impact basis and replace it in a later 0.3 release (0.3.0's is mostly injected variants); CI's Windows and macOS executables with numba.
|
|
50
|
+
| 23 | Performance | fit kernel released in 0.3.0 (design version 8); 0.3.1 fixes the designer image run as another user | [The measurement and outcome](plans/fit-kernel.md#outcome). The fit's search now scores each DE generation at once in the linear domain (`beqforge/fit_kernel.py`; numba, numpy fallback, bit-identical). Real runs on the 11 titles: 703 to 283 s wall, 1905 to 373 CPU-s; the `fit` stage 10x, so a title is 28 s and 37 CPU-s. A re-roll: refit probe no worse on balance, no baseline title's winner or verdict changed in real runs, protocol and corpus unchanged where gated. **Left:** the fit re-rolls near the top of the band as freely as at 10 Hz (row 37: weighting the search works, held back behind row 36); measure a library sample for the impact basis and replace it in a later 0.3 release (0.3.0's is mostly injected variants); CI's Windows and macOS executables with numba. **Repeated work removed** (not yet released; no design version bump, no answer changed): seven exact-preserving changes, one commit each, each with probe `--tol 0` unchanged and real-run records identical — the headroom peak in one-second blocks, the sub feed and its unfiltered spectra built once a run, one filtered waveform per measured candidate, a counterfactual cap's restoration reused when its boosts repeat, one flat deficit a channel, and the bootstrap's percentile read off draw counts (bit-identical; the review's prototype differed by 2e-15 dB only through the layout `std` read). Caught Stealing 54.7 to 27.5 s cold and 28.9 to 15.2 s warm in the pipeline, Black Bag 22.1 to 13.9 and 12.3 to 8.1; see [the outcome](plans/designer-latency.md). Follow-up (the mix's flat deficit measured twice a run not five times, the record's chart transfer built once a candidate: Caught Stealing warm 22.3 to 20.6 s wall, Black Bag 14.2 to 12.3, records identical). Still parked: the ~7 s a title outside the pipeline that remains (npz decompression 1.5 s, imports 0.8 s, record 0.6 s, chart curves 4 s; measure on the server path), an own DE loop. GPU offload rejected. Missed-fit search belongs to F3/F4. |
|
|
51
51
|
| 24 | Chart peak bias | parked | Deliberate catalogue-compatible per-bin maximum; it affects presentation only. Reopen if a reader needs an alternative percentile view; keep it distinct from the 95th-percentile evidence envelope. |
|
|
52
52
|
| 26 | Texture yardstick retune (library review) | blocked on E10 | The cubic (row 2) keeps the 200 Hz band only because the sub-band cap admitted one synthetic negative, `broadband/5`: chance unevenness in noise bursts, at a 2.6 dB boost. With 45 gated cases a single case moves the gate's upper bound from 0.079 to 0.118. Carrie is still declined (0.85x about a cubic, 1.33x with the cap). The reopening trigger (the library review) is met and the first measurement is done ([research item 4](plans/library-review-improvements.md#4-the-texture-retune-row-26)): the trend degree does nothing; ending the band at 80 Hz clears five of the seven review declines but lets five more gated negatives past this rule and costs three filtered cases their passband. Its full corpus run lets in exactly `broadband/5` (a false acceptance at a 2.5 dB boost; gate 0/45 to 1/45, upper bound 0.118, FAIL) and moves nothing else, `filtered` included ([the run](plans/library-review-improvements.md#4-the-texture-retune-row-26)). So the open question is the boost-weighted gate, and E10 (row 3) before adopting any retune. Retune against its declines: the band edge, the trend degree, and whether the gate weights a false acceptance by its boost. The library holds no known negative, so it can expose wrong declines but not measure false acceptances. Any retune still needs the corpus or a fresh E10 set for that, ideally with negatives built from real programme texture (row 5). See [the outcome](plans/texture-yardstick-sub-band.md#outcome-17-october-2026). |
|
|
53
53
|
| 28 | Fit sensitivity to arithmetic | released in 0.2.10 (design version 6) | Identification now converges to 1e-12 and the fit gets its target and ceiling quantised to 0.01 dB. The refit probe gives the same filters natively and as an emulated older CPU (0 of 11 titles moved; 28 Years Later's parametric moved before). Protocol false acceptances unchanged; corpus gate pass, `filtered` 7/9 (was 6/9). The 11 titles' corrected curves stay within 1.3 dB, though three swap between near-tied winners ([outcome](plans/library-review-improvements.md#built-a-reproducible-fit-input-22-october-2026-design_version-6)). CI's verdict on the golden: `counterfactual` matches on Linux, Windows and macOS and is back in it; `parametric` matches on the x86 runners that disagreed in 0.2.6, but macOS arm64 publishes the same filters with figures 0.06 dB apart, so it stays out ([verdict](plans/library-review-improvements.md#built-a-reproducible-fit-input-22-october-2026-design_version-6)). Left: find where arm64's parametric fit parts from x86's (needs an arm64 machine: compare the identified target, the quantised fit input and the fitted cascade unrounded) and either close the gap or accept it and say so in the golden's docstring; until then an Apple-silicon caller can see parametric figures a few hundredths of a dB from an x86 one. From design version 8 (row 23's kernel) `counterfactual` on the golden's rolloff title also publishes one step away on arm64 (36.25 Hz against 36.24, figures within tolerance), so the golden skips that case on arm64 too. Ship before the version 5 redesign (row 31) so the library is redesigned once. |
|
|
54
54
|
| 29 | Library review improvements | fixes released; research recorded | From beqdesigner's review of all 279 declines in its full-library run ([the proposal](plans/library-review-improvements.md), with evidence, ids and run records per item). Released in 0.2.9 as design version 5 ([outcomes](plans/library-review-improvements.md#outcomes)): fix 1 (the unmeasured floor wording), fix 2 (the fit scored on the published parameters; The Quiet Girl now designs, Bugonia now declines on coverage below 8.1 Hz), fix 3 (mains-hum notches filled), fix 4 (the fit bounded by the over-lift bar; Crazy Heart's flatten passes), O2 (an infrasonic plateau refused as the reference) and O1 (a stepping DC offset reported). Research, measured and not built ([research](plans/library-review-improvements.md#research-after-the-fixes)), now tracked elsewhere: 1b (textured passband) has no contained fix and is a plateau recalibration for E10 (row 3); 4 is row 26; 5 (per-title scene margin, which also covers item 7) is an experiment for the protocol, corpus and E10; 6 (a sub-band reference) rescues one plausible title and is not worth building on this evidence; item 8's machine dependence is row 28. Not followed up: item 3's tilt band. Fix 2 rescued The Quiet Girl, and The Smurfs and Despicable Me 3 already design here, but Blood Simple and Synecdoche, New York haven't been rerun; check them in row 31. |
|
|
55
55
|
| 30 | Contract 1.5: design adjustments | released in 0.2.9; one gap parked | beqdesigner can ask for a different design per title: ignore a band, a floor, a boost cap, a tilted goal, a note. `beqforge/adjustments.py` answers each; default answers unchanged (golden; probe `--tol 0`, all 11 titles). See [the outcome](plans/contract-1.5-adjustments.md#outcome). Only gap: a note's text is not written into the run record (the record holds the adjusted params, so the design replays). Reopen if a review needs the note from the record. |
|
|
56
56
|
| 31 | Review the version 5 library redesign | open: handoff received 23 October 2026; group 1 released in 0.2.10 (design version 6) | Outcome and evidence: [the v5 review](plans/library-v5-review.md). beqdesigner's v5 rerun (job `ef4265fa`, 1,083 titles, about 1,000 designed and 69 declined; 18 titles v4 designed now decline, about half wrongly) is triaged into four groups in `/media/beq/triage/handoff-v5.md` (queue ids, v5 records and earlier runs per title). 1 (built): O2 refused a plateau starting just under 10 Hz on the knee of a real rolloff (A Quiet Place: Day One, Elysium, Pan); it now keeps such a plateau when it reaches 20 Hz and the shortfall beneath exceeds the passband's ripple, and all three design. Probe, protocol and corpus unchanged. 2: a whole candidate rejected for missing one check by a fraction: cliff by under 3 dB/oct (Heartburn, Back to the Future, Guardians of the Galaxy, The Commitments), tilt (Guardians, The Exorcist, Alien Resurrection, Philadelphia), level (Ray), F9 residual just below the content edge (Drive, Amy). The library has no negatives, so a tolerance it suggests still goes through the protocol and E10, never the catalogue. 3: every candidate corrected only down to above the content edge (Return to Seoul, Spirited Away, The Jungle Book, Bugonia). 4: known, rows 5/6 (steep filter; `judge_from_content_edge` is a server flag, not on the wire), F5 (Eastern Promises, Fame, Home Alone) and row 26. Earlier scope of this row: 0.2.9 reports design version 5, so beqdesigner's out-of-date filter flags every title designed before it. Review the redesign's declines and changed designs from their run records, as the first review was (row 29): confirm the fixes' titles moved as measured, rerun the open cases (row 29's tilt titles, the near-texture watch cases Black Bag, Bugonia and Incredible Hulk), and collect real-title evidence for rows 26 and 29's research. It can show wrong declines but not false acceptances (the library has no known negatives), so a retune it suggests still goes through E10. Do row 28 first if both machines will design. Records are kept by default from 0.2.10 (row 27, done). |
|
|
57
|
-
| 32 | Fit optimum selection | part built (design version
|
|
57
|
+
| 32 | Fit optimum selection | part built (design version 10, not released); ties now broken by strategy order; verdict flips left | Found by row 28: reproducible is not stable. Changing the fit's input by a few thousandths of a dB changes winners. **Built (25 October 2026, version 7):** the fitter returns every fit within its 0.5 dB residual bar of its answer, and the pipeline keeps the first the judge passes, fewest sections first; an alternative must lift by more than the goal tolerance to replace the answer ([outcome](plans/library-review-improvements.md#row-32-choosing-among-near-equal-fits-25-october-2026-design_version-7)). **Measured again (2 November 2026):** `refit_probe.py`'s perturbation stacked per worker and read cached proposals made under another offset, so every earlier flip count was indicative only; fixed, and `rejudge` now judges again from kept fits in seconds. On 32 titles (the 11 baseline titles and the 21 variants that had ever moved) at three offsets, version 9 flipped 14 of 93 winners (28 with `--content-edge`), and real titles flip too: 28 Years Later and Send Help move between parametric and flatten, under 0.1 dB apart, because the tie went to the fewest sections and a near-equal fit's section count re-rolls. Searching the outranked fit again from other seeds (A) and per-octave departure (C) changed little and are not adopted. **Built (version 10):** a flatness tie goes to the strategy listed first in `STRATEGIES`: 8 flips (18), every default-mode ranking flip gone. Probe: the two real titles go to flatten, the same answer within 0.6 dB; protocol seed 101 improves one recovery, 947 unchanged; corpus gate unchanged. On the v8 library 80 of 1,069 winners change (70 of 762 all passing, 10 of 271 with a failure). **Left:** verdict flips, a candidate on the edge of a shape check passing under one offset and failing under the next (8 of the 8 left, 11 of 18 with `--content-edge`). Next: a margin around those checks. See [the outcome](plans/library-review-improvements.md#a-stable-tie-break-2-november-2026-design_version-10) and [the finding](plans/library-review-improvements.md#built-a-reproducible-fit-input-22-october-2026-design_version-6). |
|
|
58
58
|
| 33 | Noise lift beyond the evidence (found on a pumping floor) | open: the evidence bound was measured and not adopted; next, validate the floor detection | E10's first runs lifted `nonstationary_floor` 27-33 dB, but that recipe swung about 37 dB inside every event: event-locked content, matching real stopband leakage on contrast and coherence, so not a failure under the content principle ([v5](plans/E10-validation-protocol.md#the-pumping-floor-v5-28-october-2026) reports it as `event_locked_floor` and counts realistic 2-6 dB `tracking_floor` and `breathing_floor`). On seeds 1-12 under [v6](plans/E10-validation-protocol.md#the-skirt-and-the-cases-it-leaves-v6-28-october-2026) (noise lift counted beyond half an octave under the truth band), 4 of 72 realistic-floor rows lift noise. The two large ones are the judge passing the candidate that fits worst: on `breathing_floor/2` four three-section fits within 1.1-2.1 dB of target fail the cliff check, and a single shelf 6.4 dB off target and 5.5 dB over the evidence ceiling passes and is selected; on `breathing_floor/5` (gentle) a single section 9.0 dB over the ceiling at an unsupported bin is selected over a passing three-section fit 1.9 dB over. Nothing refuses a design for exceeding its evidence: the over-lift bar is measured from the goal, and `evidence_excess` is reported only. **Bounding lift by the evidence, not adopted (29 October 2026):** the fitter bound at the licence plus 1.5 dB (A), a judge check on any excess over 1.5 dB (B), and that check limited to excesses at least half an octave wide (C) were each built on branch `experiment-evidence-bound`, uncommitted. C cleared E10's counted noise lifts, but applied to the v8 library's stored `evidence_excess` it refuses the accepted design for 96 of 1,033 titles, and 14 of those would decline. Inspection showed that most of these are a smooth fit against a ragged licence on content that tracks the programme (Any Given Sunday, To Die For). The large lifts it catches below a diagnosed floor (Wait Until Dark, White Heat) sit inside a licence that allows 20-25 dB there, and C catches them only by an edge excess. **Lift below the diagnosed floor:** 247 of the 304 designed titles with measured bins under `noise_floor_hz` lift more than 3 dB there, and 151 more than 10 dB. Almost all are flatten, holding its lift flat, among them Moana (37 dB under 8.4 Hz) and White Heat (29 dB under 40 Hz). These are not counts of noise lift. `noise_floor_hz` is the mix tracking boundary, the first band whose envelope fails to track the passband, and failing that is not proof of noise. The contrast licence often disagrees with it (Wait Until Dark: 20 dB licensed below it). **On E10's truth** ([evidence](plans/E10-validation-protocol.md#is-the-tracking-boundary-a-noise-detector-29-october-2026)), the boundary sits 0.24-0.42 octave (median) below the true noise boundary on every floor family, up to 1.12 octave, and never above it: it calls the noise just above it content. It also misses half of `finite_stopband`'s exposed cases, and calls LFE-only content noise at 15-27 Hz on 3 of 12 `partial_channels`. **On the library**, an independent test calibrated on E10 (does the band's level scale with programme loudness, and is it steady) finds content under the boundary on 151 of 300 floored titles, among them Moana, Creed III and Juror #2, the largest lifts below it. Only 10 look like steady noise. The other 139, mostly older, neither scale nor stay steady, which these statistics cannot resolve. So the boundary is not fit to gate a rule, and its content-as-noise error also holds restorations flat that the content supports. Measured within 240 s stretches, only 27 of those 139 become steady noise; 85 stay bursty within minutes without scaling with the plateau, which is either content independent of the plateau or transient noise (splices, handling), and E10 has no transient noise to calibrate against. **Next:** listen to Wait Until Dark, Marnie and GoodFellas (bursty, not scaling) against Mary Poppins and The Exorcist (steady noise). If bursty and non-scaling proves to be noise, add a transient-noise family to E10. Then improve the floor detection itself, before deciding how a restoration should behave below a floor shown to be noise: hold flat, cap at the local target as the counterfactuals do, or return to 0 dB. [Evidence](plans/E10-validation-protocol.md#bounding-lift-by-the-evidence-three-experiments-none-adopted-29-october-2026). Related: rows 32, 36, E4/E9. |
|
|
59
|
-
| 34 | Redesign scope | built and released in 0.2.12 (contract 1.6's `POST /impact`); beqdesigner's step 2 live check passed, step 4 verification next | The pipeline is a catalogue of 37 named features (`beqforge/impact.py`), each owning its code and reading from a run record how a design relied on it. Each answer-changing design version declares in `beqforge/impact.toml` which features it changed and how much that moved; `tools/impact.py check` refuses changed code no feature owns and undeclared answer-reaching changes (regression step 5). `POST /impact` scores up to 500 records (15 s over the share), and records from before 0.2.11 are identified by the release that wrote them, so the current library can be scored. Version 7's fit-choice degree was measured on the library on one machine: 18 of 34 post-fit declines moved, 7 of 15 designs with a failing candidate, 2 of 15 with all passing, 0 of 8 analysis declines. Against the library: 34 likely, 1,015 possible, 236 unaffected, 70 unknown (pre-design-version builds). It doesn't meet the plan's bar for this change (likely holds 18 of the sample's 27 movers): a change that re-rolls the fit can't be narrowed by outcome class. The ranking still holds: the top 30% hold about two thirds of the expected movers. Records now carry each verdict's margins and each fit `_choose_fit` had, so a threshold change is scored exactly (`will_move` or `unaffected`), and `unknown` (`no_figure`) only for older records or a losing candidate that would start passing; this changes no answer (rejudge probe 11 of 11 at `--tol 0`, refit probe 0 of 54 moved). Responses carry each change's `basis` and `unknown_reason` (agreed with beqdesigner). `impact.MOVED`'s cascade line is now 20/f dB, capped at 2 dB at 10 Hz and below, not a flat 1 dB. Version 7's designed classes were recounted on beqdesigner's 105-title sample: 16 of 35 with a failing candidate, 22 of 60 all passing. **Left:** beqdesigner's step 2 live check and step 4 verification sample (105 titles), and the check's from-the-results direction. See [the outcome](plans/redesign-scope.md#outcome). |
|
|
59
|
+
| 34 | Redesign scope | built and released in 0.2.12 (contract 1.6's `POST /impact`); beqdesigner's step 2 live check passed, step 4 verification next | The pipeline is a catalogue of 37 named features (`beqforge/impact.py`), each owning its code and reading from a run record how a design relied on it. Each answer-changing design version declares in `beqforge/impact.toml` which features it changed and how much that moved; `tools/impact.py check` refuses changed code no feature owns and undeclared answer-reaching changes (regression step 5). `POST /impact` scores up to 500 records (15 s over the share), and records from before 0.2.11 are identified by the release that wrote them, so the current library can be scored. Version 7's fit-choice degree was measured on the library on one machine: 18 of 34 post-fit declines moved, 7 of 15 designs with a failing candidate, 2 of 15 with all passing, 0 of 8 analysis declines. Against the library: 34 likely, 1,015 possible, 236 unaffected, 70 unknown (pre-design-version builds). It doesn't meet the plan's bar for this change (likely holds 18 of the sample's 27 movers): a change that re-rolls the fit can't be narrowed by outcome class. The ranking still holds: the top 30% hold about two thirds of the expected movers. Records now carry each verdict's margins and each fit `_choose_fit` had, so a threshold change is scored exactly (`will_move` or `unaffected`), and `unknown` (`no_figure`) only for older records or a losing candidate that would start passing; this changes no answer (rejudge probe 11 of 11 at `--tol 0`, refit probe 0 of 54 moved). Responses carry each change's `basis` and `unknown_reason` (agreed with beqdesigner). `impact.MOVED`'s cascade line is now 20/f dB, capped at 2 dB at 10 Hz and below, not a flat 1 dB. Version 7's designed classes were recounted on beqdesigner's 105-title sample: 16 of 35 with a failing candidate, 22 of 60 all passing. **Version 9's scores (1 November 2026):** the ceiling's basis came from a sample chosen for lifting above 60 Hz, and its two touches were combined as independent although they were one measurement, so every one of the v8 library's 1,033 designs scored `likely`. Each touch can now be confined above a frequency (`above_hz`, `basis_below`). A record is scored by whether its band, target or cascade reaches above 60 Hz, and the largest share among a change's touches stands for the change. That gives 365 `likely` and 684 `possible` (0.29, row 32's re-roll); expected movers fall from 999 to 532. No answer changes (probe 11 of 11 at `--tol 0`). See [the outcome](plans/redesign-scope.md#scoring-a-change-by-what-it-reaches-1-november-2026). **Left:** beqdesigner's step 2 live check and step 4 verification sample (105 titles), and the check's from-the-results direction. See [the outcome](plans/redesign-scope.md#outcome). |
|
|
60
60
|
| 35 | Review the version 6 library redesign | open: handoff received 24 October 2026; the 500 and record overwriting fixed in 0.2.11; the 14 new declines traced to row 28, 13 of them designing again on row 32 (version 7, unreleased) | beqdesigner's v6 rerun (job `e6a70f7e`, 1,287 titles): 1,016 designed and 270 declined. A Quiet Place: Day One, Elysium and Pan now design (row 31's O2 fix). 17 titles declined on v5 now design, and 14 designed on v5 now decline. Handoff: `/media/beq/triage/handoff-v6.md`; ledger `/media/beq/triage/ledger.jsonl`, stage `E`. **Fixed:** A Quiet Place Part II got a 500 every time, because `TargetFit.within_tolerance` came back as 1.0000000000000002. Zero-weight bins that miss the tolerance change how numpy pairs up a sum, so the in-tolerance sum could round above the full one. It is now met / (met + missed), which can't exceed 1; this code predates row 28. The figure is reporting only, so no design version: the title's filters are unchanged (low shelf 18.25 Hz +24.6 dB, peak 28.38 Hz −3.5 dB), and its response is now valid. Probe 11 of 11 unchanged at `--tol 0`; suite passes. **The 14 new declines (analysed 24 October 2026):** all 14 were rerun on one machine, at v0.2.9, at `f6dca94` (row 28 alone) and at HEAD, from requests identical to the library's (same digest). All 14 design on 0.2.9 and decline at `f6dca94`; HEAD agrees with `f6dca94`. So row 28 alone is the cause, and processor variance accounts for none of them. In every case the target is identical (0.000 dB apart) and the fit lands on a different cascade: row 28's 0.01 dB quantisation of the fit's input re-rolls the fitter's choice among near-equal optima (row 32). Except for TINA, the new fits are within 1 dB of the old by the fitter's own measure, worst-case error (Kung Fu Panda 4 0.4-0.9 dB worse, You've Got Mail 0.9 dB better), yet a judge check that depends on the error's shape flips: cliff (Platoon, The Manchurian Candidate, You've Got Mail, How to Marry a Millionaire), tilt (On Golden Pond, Christine, Twilight's counterfactual), ripple (Arrival, Kung Fu Panda 4), turnover (Magnolia, Zoolander), extent (Twilight's flatten) or slot (Rocky, Souleymane's Story). In TINA's case the fit itself is worse: 0.2.9's three-section fits drifted 2.1-3.0 dB on publication rounding, against a 3.0 limit, with their lowest sections about 4 quantisation steps from the device's DC limit. The new fits most likely drifted just past it, and the exposure rule fell back to one shelf fitting 4-6 dB off. No threshold should move on this evidence: the declines are as arbitrary as the 0.2.9 acceptances were, and the fix is row 32's, choosing among near-equal fits by what the judge checks rather than by which optimum the arithmetic lands on. **Slot check:** Rocky's and Souleymane's Story's new fits carry sections under 1 dB that the pruner keeps, because dropping them costs 1.02 and 1.12 dB of worst-case error against its 1 dB allowance. Their 0.2.9 two-section fits reached the same error without them. `designer-interface.md` §4 allows a rejection only for a reason internal to the evidence, which a redundant section is not, so the stated reason is wrong whatever the outcome. The outcome itself is doubtful too: both designs are 0.2-2.6 dB sections at 71-353 Hz, mostly above an 80 Hz crossover, not low-end restoration. The library's flip lists are not ground truth for row 34 by themselves (see [the proposal](plans/redesign-scope.md#validation-before-relying-on-it)): its v5 records were overwritten by v6's, because a record was named by its request's audio digest. **Fixed (0.2.11):** a record is now named by digests of the audio and of the rest of the request (coverage, bass management, adjustments, settings) and by the design version, and holds `design_version` and `design_settings` itself. A response's `run_record` (1.6, agreed in principle with beqdesigner) names it relative to the shared root. No answer changes. |
|
|
61
61
|
| 36 | Is a design worth publishing at all? | open | The judge checks that a design does what its target asks, never whether the target is worth asking for. Only the target stage refuses a correction within the goal tolerance (1.5 dB), so anything that reaches the judge by another route can pass as a trivial design: row 32's search over near-equal fits accepted a single 0.98 dB shelf on the band-exclusion test until `pipeline._lifts_beyond_goal` gated alternatives. Rocky's and Souleymane's Story's v6 designs (row 35: 0.2-2.6 dB sections at 71-353 Hz, mostly above the crossover) are the real-title form of it. Decide whether the judge should refuse a design whose low-end lift stays within the goal tolerance, and with what reason under `designer-interface.md` §4. A decision change: protocol, corpus and the library's small designs. |
|
|
62
62
|
| 37 | A frequency-dependent fit tolerance | variant C works; held back behind row 36, not in 0.3.0 | The fit scores the worst absolute dB error the same at every frequency, so near-equal fits are interchangeable to it wherever their error sits. Version 8's re-roll (row 23) moved 28 Years Later by 1.13 dB at 33 Hz and Obsession by 1.09 dB at 29 Hz. `impact.MOVED` now uses 20/f dB, capped at 2 dB at 10 Hz. Weighting the fit's bars as well as its search failed: the fitter either spent sections on fractions of a dB above 50 Hz (A), or the near-equal window, 2 dB wide at 10 Hz, let single shelves up to 3.6 dB off win on section count (B). **C**, weighting only the search (f/40 Hz, 0.25 to 1), works: over the 31 winners, median worst error at 30-40 Hz falls from 1.11 to 0.63 dB and at 5-10 Hz rises from 1.05 to 1.65. No real winner changes, protocol and corpus gate are unchanged, and corpus recovery goes from 1.37 to 0.85 dB RMS. It is held back because the band-exclusion test now designs a 1 dB nothing where version 8 declined, which is row 36's hole. **Next:** C together with a row 36 rule (refuse the fitter's answer, not only its alternatives, when its lift stays within the goal tolerance), validated as one decision change. Code on branch `experiment-fit-weighting-search`. See [the evidence](plans/fit-weighting.md). |
|
|
@@ -151,6 +151,14 @@ something that decides anything is committed. The synthetic protocol and corpus
|
|
|
151
151
|
a decision-changing fitter change; run the two protocol seeds in parallel. `probe.py snapshot
|
|
152
152
|
--content-edge --records DIR` likewise rejudges any scratch record set with the option on.
|
|
153
153
|
|
|
154
|
+
A change to fit selection or the judge alone does not need refitting: take "before" with
|
|
155
|
+
`snapshot --state <scratch>/state`, and `refit_probe.py rejudge ... --state <scratch>/state`
|
|
156
|
+
judges and chooses again from the kept fits in seconds (`REFIT_REJUDGE_FRESH=1` when the judge
|
|
157
|
+
itself changed, about seven minutes over the steep variants). In a worktree, run the tool with
|
|
158
|
+
the main checkout's `.venv/bin/python`: `uv run` there builds an environment without the
|
|
159
|
+
extras. Stability under another machine's arithmetic (TODO 32) is `REFIT_PERTURB_DB`; see the
|
|
160
|
+
tool's docstring.
|
|
161
|
+
|
|
154
162
|
**3. Act on what moved:**
|
|
155
163
|
|
|
156
164
|
* **Nothing** → the change reaches no decision on this material. The test suite is still
|
|
@@ -532,7 +540,7 @@ least `min_judge_octaves` above the floor — no fixed top (historical review T6
|
|
|
532
540
|
|
|
533
541
|
* A run is ~40-80 s a title (down from ~100-490 s, 7.18x, every accepted filter's verdict
|
|
534
542
|
preserved); the test suite is ~2 minutes. Run both in the background regardless — see
|
|
535
|
-
"
|
|
543
|
+
the root AGENTS.md's "Long-running jobs and timing". Measured 2026-10-25 on 0.2.12 (warm cache, 10 designing
|
|
536
544
|
titles, 70 s and 190 CPU-s a title on average): `fit` is 41% of wall time and 70% of CPU,
|
|
537
545
|
`parametric`'s own fit inside its target stage another ~10% and ~12%, the rest of target
|
|
538
546
|
construction ~12%, judging 17% (verify, headroom, sub feed) and ~13 s a title falls outside
|
|
@@ -24,11 +24,11 @@ import numpy as np
|
|
|
24
24
|
from beqforge import DESIGN_GRID, BiquadSpec
|
|
25
25
|
from beqforge.filters import (
|
|
26
26
|
Realisation,
|
|
27
|
-
publication_filters,
|
|
28
|
-
unstable_sections,
|
|
29
27
|
biquad_sos,
|
|
30
28
|
drift_distribution,
|
|
31
29
|
magnitude_db,
|
|
30
|
+
publication_filters,
|
|
31
|
+
unstable_sections,
|
|
32
32
|
)
|
|
33
33
|
from beqforge.verify import Correction
|
|
34
34
|
|
|
@@ -603,9 +603,11 @@ def assess(
|
|
|
603
603
|
return Verdict(
|
|
604
604
|
passed=False,
|
|
605
605
|
failures=[
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
606
|
+
(
|
|
607
|
+
f"only {octaves:.2f} octaves left to judge "
|
|
608
|
+
f"({correction.band_hz[0]:.1f}-{correction.band_hz[1]:.1f} Hz); a slope read "
|
|
609
|
+
f"over less than {params.min_judge_octaves:g} is set by where the band stops"
|
|
610
|
+
)
|
|
609
611
|
],
|
|
610
612
|
notes=[],
|
|
611
613
|
worst_gradient_before=math.nan,
|
|
@@ -34,13 +34,13 @@ import matplotlib
|
|
|
34
34
|
|
|
35
35
|
matplotlib.use("Agg") # written to file, never shown; plt.show() blocks a headless run
|
|
36
36
|
|
|
37
|
-
import matplotlib.pyplot as plt
|
|
38
|
-
import numpy as np
|
|
37
|
+
import matplotlib.pyplot as plt
|
|
38
|
+
import numpy as np
|
|
39
39
|
|
|
40
|
-
from beqforge import BiquadSpec
|
|
41
|
-
from beqforge.filters import Realisation, unstable_sections
|
|
42
|
-
from beqforge.
|
|
43
|
-
from beqforge.
|
|
40
|
+
from beqforge import BiquadSpec
|
|
41
|
+
from beqforge.filters import Realisation, unstable_sections
|
|
42
|
+
from beqforge.material import Material
|
|
43
|
+
from beqforge.verify import device_waveform
|
|
44
44
|
|
|
45
45
|
logger = logging.getLogger(__name__)
|
|
46
46
|
|
|
@@ -146,7 +146,7 @@ def programme_levels_db(
|
|
|
146
146
|
|
|
147
147
|
index = int(power.sum(axis=1).argmax()) if frame_index is None else frame_index
|
|
148
148
|
anchor = int(coarse[index])
|
|
149
|
-
half =
|
|
149
|
+
half = round(LOUDEST_SPAN_S * fs / 2.0)
|
|
150
150
|
fine = np.arange(
|
|
151
151
|
max(0, anchor - half),
|
|
152
152
|
min(len(samples) - NPERSEG, anchor + half) + 1,
|
|
@@ -15,7 +15,7 @@ Two routes, matching the contract's `method`:
|
|
|
15
15
|
|
|
16
16
|
import logging
|
|
17
17
|
from collections.abc import Callable
|
|
18
|
-
from dataclasses import dataclass
|
|
18
|
+
from dataclasses import dataclass, field
|
|
19
19
|
from typing import Literal
|
|
20
20
|
|
|
21
21
|
import numpy as np
|
|
@@ -118,7 +118,7 @@ class DesignParams:
|
|
|
118
118
|
least often selected. `PipelineParams.fit_seeds` sets this now, and the
|
|
119
119
|
reasoning there applies here unchanged."""
|
|
120
120
|
|
|
121
|
-
realisation: Realisation | None = Realisation
|
|
121
|
+
realisation: Realisation | None = field(default_factory=Realisation)
|
|
122
122
|
"""How the cascade will actually be realised, scored during the fit.
|
|
123
123
|
|
|
124
124
|
On by default. Fitting in float64 alone accepts cascades built from large opposing
|
|
@@ -321,7 +321,7 @@ def _unpriced_fitted_target(
|
|
|
321
321
|
|
|
322
322
|
|
|
323
323
|
def _nearest_even_order(order: float) -> int:
|
|
324
|
-
return max(2, 2 *
|
|
324
|
+
return max(2, 2 * round(order / 2.0))
|
|
325
325
|
|
|
326
326
|
|
|
327
327
|
PROTECTIVE_ALIGNMENT = Alignment.BUTTERWORTH
|
|
@@ -77,7 +77,7 @@ logger = logging.getLogger(__name__)
|
|
|
77
77
|
CONTRACT_VERSION = "1.6"
|
|
78
78
|
"""The version this module implements. Responses echo the request's own version instead."""
|
|
79
79
|
|
|
80
|
-
DESIGN_VERSION =
|
|
80
|
+
DESIGN_VERSION = 10
|
|
81
81
|
"""Which answer this code gives (1.4): bumped by any change that can alter `candidates`,
|
|
82
82
|
`rejected`, a decline or a 1.3 fit figure for an unchanged request and unchanged settings.
|
|
83
83
|
|
|
@@ -105,7 +105,9 @@ the first the judge passes, fewest sections first, instead of whichever the arit
|
|
|
105
105
|
domain (TODO 23), which re-rolls the fit among near-equal optima as any change to its
|
|
106
106
|
arithmetic does. 0.3.2 is 9: it stops bass EQ at a 60 Hz ceiling: no target asks for lift
|
|
107
107
|
above it, the judged band's top is capped there, and a cascade may add no more than the level
|
|
108
|
-
tolerance beyond it (TODO 39).
|
|
108
|
+
tolerance beyond it (TODO 39). 10 breaks a flatness tie between candidates by the strategy's
|
|
109
|
+
place in `STRATEGIES`, not by section count (TODO 32). `plans/design-version.md` has each one's
|
|
110
|
+
build."""
|
|
109
111
|
|
|
110
112
|
Coverage = Literal["complete_programme", "excerpt"]
|
|
111
113
|
ChannelScope = Literal["all_channels", "lfe_only", "mixed"]
|
|
@@ -966,14 +968,14 @@ def _validate_candidate(
|
|
|
966
968
|
for i, spec in enumerate(candidate.filters):
|
|
967
969
|
_validate_biquad_spec(spec, i, index, where)
|
|
968
970
|
_validate_fit(candidate, f"{where}[{index}]")
|
|
969
|
-
if candidate.commentary is not None
|
|
970
|
-
|
|
971
|
+
if candidate.commentary is not None and (
|
|
972
|
+
not isinstance(candidate.commentary, dict)
|
|
973
|
+
or not all(
|
|
971
974
|
isinstance(k, str) and isinstance(v, str)
|
|
972
975
|
for k, v in candidate.commentary.items()
|
|
973
|
-
)
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
)
|
|
976
|
+
)
|
|
977
|
+
):
|
|
978
|
+
raise ContractViolation(f"{where}[{index}].commentary must be a dict[str, str]")
|
|
977
979
|
|
|
978
980
|
|
|
979
981
|
def _validate_fit(candidate: DesignCandidate, prefix: str) -> None:
|
|
@@ -150,8 +150,10 @@ def correction(candidate: Candidate, report: Report | None = None) -> str:
|
|
|
150
150
|
for f in candidate.filters
|
|
151
151
|
)
|
|
152
152
|
parts = [
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
(
|
|
154
|
+
f"{candidate.label}: {len(candidate.filters)} section(s) — {sections}; "
|
|
155
|
+
f"peak boost {candidate.mv_adjust_db:+.1f} dB"
|
|
156
|
+
)
|
|
155
157
|
]
|
|
156
158
|
recovered = candidate.verdict.recovered_fraction
|
|
157
159
|
if math.isfinite(recovered):
|
|
@@ -168,8 +168,15 @@ def _block_bootstrap_se(
|
|
|
168
168
|
their own isolated instant (wide) — a flat constant cannot see that difference.
|
|
169
169
|
|
|
170
170
|
Vectorised across every bin in `bins` at once: the resampled *frame indices* are the same
|
|
171
|
-
for every bin in a given replicate, so one `(n_boot, n_frames)` index matrix
|
|
172
|
-
|
|
171
|
+
for every bin in a given replicate, so one `(n_boot, n_frames)` index matrix serves every
|
|
172
|
+
bin.
|
|
173
|
+
|
|
174
|
+
A replicate is a multiset of the masked frames, so its percentile needs only how often
|
|
175
|
+
each frame was drawn and the frames in order of value (`_bootstrap_percentiles`), not the
|
|
176
|
+
resampled values gathered and partitioned per replicate. That was 90% of `extract`, and
|
|
177
|
+
the LFE of a two-hour title (12 s) set a cold run's wall time; this takes about a sixth of
|
|
178
|
+
it and returns the same bits, which `tests/test_design_extraction.py` pins against the
|
|
179
|
+
gather.
|
|
173
180
|
"""
|
|
174
181
|
runs = _runs(mask)
|
|
175
182
|
if len(runs) < 2 or n_boot < 2:
|
|
@@ -195,21 +202,90 @@ def _block_bootstrap_se(
|
|
|
195
202
|
if filled >= n_frames:
|
|
196
203
|
break
|
|
197
204
|
batch *= 2
|
|
198
|
-
|
|
199
|
-
#
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
percentiles = _bootstrap_percentiles(power, bins, mask, replicate_idx, percentile)
|
|
206
|
+
# C order, so `std` sums each bin's replicates in the order the gather's result did
|
|
207
|
+
percentiles_db = 10.0 * np.log10(np.ascontiguousarray(percentiles) + 1e-300)
|
|
208
|
+
return np.std(percentiles_db, axis=1)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
_TOP_SHARE = 4.0
|
|
212
|
+
"""How far down the values a replicate's percentile is sought, in multiples of its rank.
|
|
213
|
+
|
|
214
|
+
The 95th percentile of `n` frames is about the `0.05 n`-th largest, and a replicate draws each
|
|
215
|
+
frame about once, so the frames ranked within four times that hold it in all but freak draws.
|
|
216
|
+
A bin where some replicate needs more is sorted in full, so this sets only the speed."""
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _bootstrap_percentiles(
|
|
220
|
+
power: np.ndarray,
|
|
221
|
+
bins: np.ndarray,
|
|
222
|
+
mask: np.ndarray,
|
|
223
|
+
replicate_idx: np.ndarray,
|
|
224
|
+
percentile: float,
|
|
225
|
+
) -> np.ndarray:
|
|
226
|
+
"""Each replicate's `percentile` of each bin's power, `(bins, replicates)`.
|
|
227
|
+
|
|
228
|
+
`np.percentile`'s linear method over each replicate, without forming the replicate: the
|
|
229
|
+
order statistics it interpolates are read off the masked frames in descending order of
|
|
230
|
+
value, by how many draws each one carries, and interpolated as `np.percentile` does it
|
|
231
|
+
(`numpy.lib._function_base_impl._lerp`). The same values, the same arithmetic, so the same
|
|
232
|
+
bits. Non-finite power, which `np.percentile` treats its own way, takes the gather.
|
|
233
|
+
|
|
234
|
+
Chunked over bins to bound memory (`_BOOTSTRAP_ELEMENTS`): the counts gathered in sorted
|
|
235
|
+
order are replicates x bins x the frames searched.
|
|
236
|
+
"""
|
|
237
|
+
n_boot, n_frames = replicate_idx.shape
|
|
238
|
+
frames = np.flatnonzero(mask)
|
|
239
|
+
position = np.zeros(mask.shape[0], dtype=np.int64)
|
|
240
|
+
position[frames] = np.arange(n_frames)
|
|
241
|
+
counts = np.zeros((n_boot, n_frames), dtype=np.int64)
|
|
242
|
+
np.add.at(counts, (np.arange(n_boot)[:, None], position[replicate_idx]), 1)
|
|
243
|
+
virtual = (n_frames - 1) * (percentile / 100.0)
|
|
244
|
+
lower = math.floor(virtual)
|
|
245
|
+
upper = n_frames - 1 if virtual >= n_frames - 1 else lower + 1
|
|
246
|
+
lower = min(lower, upper)
|
|
247
|
+
gamma = virtual - lower
|
|
248
|
+
# descending, the ascending order statistic j is where the draws passed exceed n - 1 - j
|
|
249
|
+
need_lower, need_upper = n_frames - 1 - lower, n_frames - 1 - upper
|
|
250
|
+
searched = min(n_frames, math.ceil(_TOP_SHARE * (need_lower + 1)) + 16)
|
|
251
|
+
chunk = max(1, _BOOTSTRAP_ELEMENTS // max(n_boot * searched, 1))
|
|
252
|
+
out = np.empty((bins.shape[0], n_boot))
|
|
253
|
+
for start in range(0, bins.shape[0], chunk):
|
|
254
|
+
taken = bins[start : start + chunk]
|
|
255
|
+
values = power[taken][:, frames]
|
|
256
|
+
order = np.argsort(-values, axis=1, kind="stable")[:, :searched]
|
|
257
|
+
passed = np.cumsum(counts[:, order], axis=2)
|
|
258
|
+
if not np.isfinite(values).all() or not (passed[:, :, -1] > need_lower).all():
|
|
259
|
+
out[start : start + taken.size] = _gathered_percentiles(
|
|
260
|
+
power, taken, replicate_idx, percentile
|
|
261
|
+
)
|
|
262
|
+
continue
|
|
263
|
+
top = np.take_along_axis(values, order, axis=1)
|
|
264
|
+
low = np.take_along_axis(top, (passed > need_lower).argmax(axis=2).T, axis=1)
|
|
265
|
+
high = np.take_along_axis(top, (passed > need_upper).argmax(axis=2).T, axis=1)
|
|
266
|
+
diff = high - low
|
|
267
|
+
out[start : start + taken.size] = (
|
|
268
|
+
high - diff * (1 - gamma) if gamma >= 0.5 else low + diff * gamma
|
|
269
|
+
)
|
|
270
|
+
return out
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _gathered_percentiles(
|
|
274
|
+
power: np.ndarray, bins: np.ndarray, replicate_idx: np.ndarray, percentile: float
|
|
275
|
+
) -> np.ndarray:
|
|
276
|
+
"""`_bootstrap_percentiles` the direct way: gather every replicate, `np.percentile` it.
|
|
277
|
+
|
|
278
|
+
Chunked over bins, because the gather is proportional to bins x replicates x frames:
|
|
279
|
+
every bin of a two-hour title at 200 replicates is some 400 MB in one array.
|
|
280
|
+
"""
|
|
281
|
+
n_boot, n_frames = replicate_idx.shape
|
|
203
282
|
flat_idx = replicate_idx.reshape(-1)
|
|
204
283
|
chunk = max(1, _BOOTSTRAP_ELEMENTS // max(n_boot * n_frames, 1))
|
|
205
|
-
out = np.empty(bins.shape[0])
|
|
284
|
+
out = np.empty((bins.shape[0], n_boot))
|
|
206
285
|
for start in range(0, bins.shape[0], chunk):
|
|
207
286
|
taken = bins[start : start + chunk]
|
|
208
287
|
resampled = power[taken][:, flat_idx].reshape(taken.size, n_boot, n_frames)
|
|
209
|
-
|
|
210
|
-
np.percentile(resampled, percentile, axis=2) + 1e-300
|
|
211
|
-
)
|
|
212
|
-
out[start : start + chunk] = np.std(percentiles_db, axis=1)
|
|
288
|
+
out[start : start + taken.size] = np.percentile(resampled, percentile, axis=2)
|
|
213
289
|
return out
|
|
214
290
|
|
|
215
291
|
|
|
@@ -860,7 +860,7 @@ def _escalate(
|
|
|
860
860
|
pending = [state for state in states if state.answer is None]
|
|
861
861
|
if not pending:
|
|
862
862
|
break
|
|
863
|
-
tasks: list[
|
|
863
|
+
tasks: list[FitTask] = []
|
|
864
864
|
owners: list[tuple[_Escalation, int]] = []
|
|
865
865
|
for sections in group:
|
|
866
866
|
for state in pending:
|
|
@@ -20,8 +20,8 @@ from dataclasses import dataclass
|
|
|
20
20
|
import numpy as np
|
|
21
21
|
from scipy import signal
|
|
22
22
|
|
|
23
|
-
from beqforge.biquad import PeakingEQ
|
|
24
23
|
from beqforge import Alignment, HighPass
|
|
24
|
+
from beqforge.biquad import PeakingEQ
|
|
25
25
|
from beqforge.filters import high_pass_sos
|
|
26
26
|
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
@@ -97,13 +97,13 @@ def synthesise(profile: SyntheticProfile, fs: float, seed: int = 0) -> np.ndarra
|
|
|
97
97
|
can be reproduced from its profile and seed alone.
|
|
98
98
|
"""
|
|
99
99
|
rng = np.random.default_rng(seed)
|
|
100
|
-
n =
|
|
100
|
+
n = round(profile.duration_s * fs)
|
|
101
101
|
out = rng.standard_normal(n) * 10.0 ** (profile.floor_db / 20.0)
|
|
102
102
|
|
|
103
|
-
count = max(1,
|
|
103
|
+
count = max(1, round(profile.duration_s * profile.event_rate_hz))
|
|
104
104
|
starts = rng.integers(0, n, size=count)
|
|
105
105
|
levels = 10.0 ** (-rng.uniform(0.0, profile.event_level_spread_db, count) / 20.0)
|
|
106
|
-
decay_len =
|
|
106
|
+
decay_len = round(profile.event_decay_s * 6.0 * fs)
|
|
107
107
|
envelope = np.exp(-np.arange(decay_len) / (profile.event_decay_s * fs))
|
|
108
108
|
nyquist = fs / 2.0
|
|
109
109
|
for start, level in zip(starts, levels, strict=True):
|