paramrf 0.36.4__tar.gz → 0.36.5__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.
- {paramrf-0.36.4 → paramrf-0.36.5}/CONTEXT.md +84 -1
- {paramrf-0.36.4/paramrf.egg-info → paramrf-0.36.5}/PKG-INFO +2 -2
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/adr/0005-priors-by-name.md +32 -3
- paramrf-0.36.5/docs/examples/discrepancy_prediction.rst +77 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/index.rst +2 -0
- paramrf-0.36.5/docs/examples/port_discrepancy.py +273 -0
- paramrf-0.36.5/docs/examples/port_discrepancy.rst +81 -0
- {paramrf-0.36.4 → paramrf-0.36.5/paramrf.egg-info}/PKG-INFO +2 -2
- {paramrf-0.36.4 → paramrf-0.36.5}/paramrf.egg-info/SOURCES.txt +9 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/paramrf.egg-info/requires.txt +1 -1
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/covariance_kernels.py +52 -19
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/discrepancy_models.py +136 -21
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/evaluators.py +246 -9
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/infer/base.py +5 -4
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/likelihoods.py +34 -40
- paramrf-0.36.5/pmrf/linearization.py +198 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/__init__.py +5 -0
- paramrf-0.36.5/pmrf/models/port_discrepancy.py +175 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/modules/wrapped.py +11 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/parameters.py +149 -68
- {paramrf-0.36.4 → paramrf-0.36.5}/pyproject.toml +2 -2
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_covariance_kernels.py +20 -0
- paramrf-0.36.5/tests/test_discrepancy_models.py +248 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_evaluators.py +230 -14
- paramrf-0.36.5/tests/test_linearization.py +417 -0
- paramrf-0.36.5/tests/test_models/test_port_discrepancy.py +144 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_parameters_joint_prior.py +329 -8
- paramrf-0.36.5/tests/test_port_discrepancy_example.py +56 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/.github/workflows/docs.yml +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/.github/workflows/draft-pdf.yml +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/.github/workflows/publish.yml +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/.github/workflows/tests.yml +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/.gitignore +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/AGENTS.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/CHANGELOG.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/CITATION.cff +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/CLAUDE.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/CONTRIBUTING.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/LICENSE +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/NOTICE +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/README.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/assets/logo.png +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/Makefile +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/_static/custom.css +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/_templates/autosummary/class.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/_templates/autosummary/function.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/_templates/autosummary/module.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/adr/0001-line-modelling-architecture.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/adr/0002-parameter-api.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/adr/0003-derived-models.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/adr/0004-profiled-lines.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/adr/0006-reference-impedance.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/adr/0007-edge-values.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/agents/domain.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/agents/issue-tracker.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/agents/triage-labels.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/api/index.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/conf.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/core_concepts/core_primitives.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/core_concepts/index.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/core_concepts/jax_overview.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/core_concepts/optimization_and_inference.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/core_concepts/parameter_names.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/cascading_and_terminating.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/circuit_clc.png +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/circuit_models.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/custom_models.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/derivatives_and_sweeps.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/model_optimization.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/multiple_models_one_parameter_set.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/parameter_naming_and_model_manipulation.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/examples/shared_substrates.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/index.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/license.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/make.bat +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/research/conductor-loss-alternatives.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/research/holloway1994.pdf +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/research/microstrip-loss-conventions.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/research/precision-coax-microstrip-10-500mhz.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/skrf_comparison/index.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/skrf_comparison/overview.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/skrf_comparison/performance.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/tutorials/1_cable_fitting.ipynb +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/tutorials/2_chip_inductor_fitting.ipynb +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/tutorials/data/CBN-1.5FT-SMSM.s2p +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/tutorials/data/on-chip-inductor.s2p +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/docs/tutorials/index.rst +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/paper/paper.bib +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/paper/paper.md +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/paper/rlc.png +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/paramrf.egg-info/dependency_links.txt +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/paramrf.egg-info/top_level.txt +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/_solver_view.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/bijectors.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/constraints.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/distributions.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/fitting/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/fitting/minimize.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/fitting/result.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/fitting/routers.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/fitting/sample.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/fitting/targets.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/frequency.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/infer/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/infer/result.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/infer/sample.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/infer/solvers/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/infer/solvers/blackjax.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/infer/solvers/polychord.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/losses.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/materials/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/materials/conductor.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/materials/dielectric.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/materials/properties.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/materials/roughness.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/materials/substrate.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/materials/surface_impedance.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/math/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/math/aggregations.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/math/bessel.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/math/conversions.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/math/losses.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/math/misc.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/bridge.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/callable.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/delegated.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/derived.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/static.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/adapters/wrapped.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/ideal.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/coaxial.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/empirical.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/ideal.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/microstrip.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/nodal.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/nonuniform.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/planar.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/profiles.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lines/stripline.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/lumped.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/components/sections.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/cascade.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/circuit/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/circuit/base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/circuit/circuit.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/circuit/solvers/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/circuit/solvers/nodal.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/circuit/solvers/scattering.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/interconnected/terminated.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/nodal.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/topological.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/composite/transformed.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/surrogates/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/surrogates/expansion.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/models/surrogates/rational.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/modules/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/modules/base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/network_collection.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/noise_models.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/minimize.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/result.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/solvers/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/solvers/jaxopt.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/solvers/optimistix.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/optimize/solvers/scipy.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/problems.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/rf/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/rf/cascade.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/rf/conversions.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/rf/mna.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/serialization.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/terms.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/types.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/array.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/debug.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/network.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/random.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/rf.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/transforms.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/tree.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/utils/type.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/viz/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/pmrf/viz/plots.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/scripts/benchmark_gp_marginal_likelihood.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/scripts/install-test-deps.sh +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/setup.cfg +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/__init__.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/_jit.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/conftest.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/data/10m_cable.s2p +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_autodiff.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_box_space_solving.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_conversions.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_dc_limits.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_fitting_minimize.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_fitting_routers.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_fitting_sample.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_fitting_targets.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_frequency.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_infer_base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_infer_sample.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_map_priors.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_materials/test_conductor.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_materials/test_dielectric.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_materials/test_serialization.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_materials/test_substrate.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_materials/test_surface_impedance.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_math/test_bessel.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_model.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_adapters.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_cascade.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_circuit_native_z0.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_circuit_nodal.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_circuit_port_order.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_circuit_scattering.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_circuit_shorted_ports.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_circuit_skrf_port_names.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_circuit_z0.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_current_distribution.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_derived.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_edge_values.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_interconnected.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_lines.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_lines_skrf_matrix.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_lumped.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_nodal.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_profiles.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_rational.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_repeated_cascade.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_sections.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_transformed.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_models/test_transformers.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_module.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_naming.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_optimize_base.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_optimize_minimize.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_parameters.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_parameters_by_name.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_parameters_prior.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_parameters_validity.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_raw_space_solving.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_serialization.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_structural_updates.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_terms.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_transforms.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_utils/test_compress.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_utils/test_debug.py +0 -0
- {paramrf-0.36.4 → paramrf-0.36.5}/tests/test_utils/test_tree.py +0 -0
|
@@ -98,7 +98,9 @@ is derived, so it has no prior of its own.
|
|
|
98
98
|
|
|
99
99
|
`prf.prior` attaches a prior to the parameters a selector picks (ADR-0005). A
|
|
100
100
|
scalar distribution gives each of them its own prior. A distribution whose
|
|
101
|
-
event size equals the
|
|
101
|
+
event size equals the parameters' total size (their number, when they are
|
|
102
|
+
scalars; an array parameter counts each of its values) is a **joint prior** over
|
|
103
|
+
them: a
|
|
102
104
|
wrapper (`prf.modules.Probabilistic`) around the unchanged tree, so every
|
|
103
105
|
parameter keeps its name. A joint prior replaces its parameters' own priors,
|
|
104
106
|
and its `space` says which space its distribution is over. Raw space for its
|
|
@@ -170,6 +172,87 @@ Modifies conductor behaviour rather than line state — it scales a surface
|
|
|
170
172
|
impedance — so it belongs to the material, not the geometry: it is a field of
|
|
171
173
|
`RoughConductor` in `pmrf.materials.conductor`.
|
|
172
174
|
|
|
175
|
+
## Discrepancy modelling
|
|
176
|
+
|
|
177
|
+
A fit in Kennedy–O'Hagan form treats an observation as $\tilde h = h(\theta) +
|
|
178
|
+
\delta + \varepsilon$: the model prediction, a **discrepancy** and measurement
|
|
179
|
+
noise. `MarginalLogLikelihood` scores it; see `pmrf.discrepancy_models` and
|
|
180
|
+
`pmrf.likelihoods`.
|
|
181
|
+
|
|
182
|
+
### Event space and event block
|
|
183
|
+
|
|
184
|
+
The space in which probability is defined: the prediction after the event
|
|
185
|
+
transform, with frequency as the last (event) axis. Every other axis is a batch
|
|
186
|
+
axis. One entry of that batch, for example the real part of S21, is an **event
|
|
187
|
+
block**. Blocks are independent: a kernel chooses the covariance *within* each
|
|
188
|
+
block (`AutoCrossKernel` routes by block, `SharedIndependentKernel` shares
|
|
189
|
+
hyperparameters across blocks) and never couples two blocks.
|
|
190
|
+
|
|
191
|
+
*Avoid:* "output" or "task" for a block; "cross-covariance" for the kernel an
|
|
192
|
+
`AutoCrossKernel` gives a transmission block.
|
|
193
|
+
|
|
194
|
+
### Discrepancy
|
|
195
|
+
|
|
196
|
+
$\delta$, the systematic misfit between model and reality, as opposed to noise
|
|
197
|
+
$\varepsilon$. Either deterministic or a `GaussianProcess` over each event block.
|
|
198
|
+
It is on the observable unless it is a port discrepancy.
|
|
199
|
+
|
|
200
|
+
### Residual
|
|
201
|
+
|
|
202
|
+
The observation minus the model prediction, in event space: $r = \tilde h -
|
|
203
|
+
h(\theta)$. It contains both discrepancy and noise.
|
|
204
|
+
|
|
205
|
+
### Discrepancy prediction
|
|
206
|
+
|
|
207
|
+
The distribution of $\delta$ at new frequencies $x_B$, conditioned on residuals
|
|
208
|
+
at the fit frequencies $x_A$, per event block:
|
|
209
|
+
$\mu = K_{BA}(K_{AA}+\Sigma_n)^{-1} r$ and
|
|
210
|
+
$\Sigma = K_{BB} - K_{BA}(K_{AA}+\Sigma_n)^{-1}K_{AB}$. It excludes noise: it is
|
|
211
|
+
the discrepancy, not a predicted observation.
|
|
212
|
+
|
|
213
|
+
*Avoid:* "posterior predictive" (that includes noise); "discrepancy posterior"
|
|
214
|
+
(ambiguous with a posterior over hyperparameters).
|
|
215
|
+
|
|
216
|
+
### Linearisation
|
|
217
|
+
|
|
218
|
+
A Gauss–Newton approximation of a fit at its MAP, with the discrepancy's
|
|
219
|
+
hyperparameters held fixed: the Jacobian $J = -\partial r / \partial \theta$ of the
|
|
220
|
+
residual with respect to the model's free parameters, and the Fisher matrix
|
|
221
|
+
$F = J^\top \Sigma_D^{-1} J$ with $\Sigma_D = K + \Sigma_n$ per event block. With
|
|
222
|
+
the prior precision $\Sigma_0^{-1}$ it gives the linearised posterior covariance
|
|
223
|
+
$\Sigma_\text{post} = (F + \Sigma_0^{-1})^{-1}$, over parameter values in one space
|
|
224
|
+
(declared by default). Fisher matrices of independent fits add.
|
|
225
|
+
|
|
226
|
+
### Joint prediction
|
|
227
|
+
|
|
228
|
+
The Gaussian over the free parameters and the discrepancy at new frequencies
|
|
229
|
+
together, from a linearisation: unlike a discrepancy prediction, it carries the
|
|
230
|
+
parameters' uncertainty into $\delta$ and the cross-covariance between them. It is
|
|
231
|
+
the form in which one fit's posterior becomes the next fit's joint prior.
|
|
232
|
+
|
|
233
|
+
### Port discrepancy
|
|
234
|
+
|
|
235
|
+
A discrepancy embedded at a component's ports, on its S-parameters rather than on
|
|
236
|
+
an observable: $\check S_{ii} = S_{ii} + \delta_{ii}$ for reflection and
|
|
237
|
+
$\check S_{ij} = S_{ij} e^{\delta_{ij}}$ for transmission. A circuit containing
|
|
238
|
+
the corrected component carries $\delta$ to any observable or quantity of
|
|
239
|
+
interest, so it transfers between fits that observe the component differently.
|
|
240
|
+
Transmission is split into a symmetric and an antisymmetric block,
|
|
241
|
+
$\delta_{ij} = \delta^s_{ij} \pm \delta^a_{ij}$; a reciprocal component has no
|
|
242
|
+
antisymmetric block. $\delta$ is defined at a reference impedance and is learnt
|
|
243
|
+
at that impedance.
|
|
244
|
+
|
|
245
|
+
When the component is observed directly, in an event space of the same form
|
|
246
|
+
(additive for reflection, logarithmic for transmission), the port discrepancy *is*
|
|
247
|
+
the event-space discrepancy, and is marginalised there: this is the **reference
|
|
248
|
+
fit**. A later fit with the component inside a larger circuit holds $\delta$ as
|
|
249
|
+
explicit parameters, under the reference fit's joint prediction as their prior:
|
|
250
|
+
this is a **transfer fit**.
|
|
251
|
+
|
|
252
|
+
*Avoid:* "embedded discrepancy" or "embedded model error" (in the literature
|
|
253
|
+
these embed the error in the parameters); "stage 1" and "stage 2" (say reference
|
|
254
|
+
fit and transfer fit).
|
|
255
|
+
|
|
173
256
|
## Records at the boundaries
|
|
174
257
|
|
|
175
258
|
These frozen records are the seams that keep `Param`s and `Module`s out of the
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: paramrf
|
|
3
|
-
Version: 0.36.
|
|
3
|
+
Version: 0.36.5
|
|
4
4
|
Summary: Parametric radio frequency modeling
|
|
5
5
|
Author-email: Gary Allen <gvcallen@gmail.com>
|
|
6
6
|
Project-URL: homepage, https://github.com/gvcallen/paramrf
|
|
7
7
|
Description-Content-Type: text/x-rst
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
License-File: NOTICE
|
|
10
|
-
Requires-Dist: parax>=0.11.
|
|
10
|
+
Requires-Dist: parax>=0.11.6
|
|
11
11
|
Requires-Dist: threadpoolctl
|
|
12
12
|
Requires-Dist: eqxpress
|
|
13
13
|
Requires-Dist: jax
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# ADR-0005: Priors are attached by name, and a joint prior keeps its parameters
|
|
2
2
|
|
|
3
|
-
Status: accepted (2026-09)
|
|
3
|
+
Status: accepted (2026-09); amended (2026-10) to allow array-valued parameters
|
|
4
|
+
under a joint prior, and to compute a constant whitening log-determinant once.
|
|
4
5
|
|
|
5
6
|
## Context
|
|
6
7
|
|
|
@@ -43,8 +44,8 @@ The distribution's event size decides which:
|
|
|
43
44
|
`prf.Random`. Its raw space becomes the new prior's whitening, as for
|
|
44
45
|
`prf.Random`, and the prior is truncated to the parameter's existing bounds so
|
|
45
46
|
the bounds still hold.
|
|
46
|
-
- **Event size equal to the
|
|
47
|
-
over them.
|
|
47
|
+
- **Event size equal to the total size of the selected parameters**: a **joint
|
|
48
|
+
prior** over them. For scalar parameters the total size is their number.
|
|
48
49
|
- Anything else raises.
|
|
49
50
|
|
|
50
51
|
### A joint prior keeps its parameters
|
|
@@ -63,6 +64,22 @@ transparent to the name resolver, as `Tied` is. So:
|
|
|
63
64
|
would count the earlier fit's priors twice, since its posterior already
|
|
64
65
|
includes them.
|
|
65
66
|
|
|
67
|
+
### Array-valued parameters
|
|
68
|
+
|
|
69
|
+
A parameter under a joint prior may be an array, such as a port discrepancy's
|
|
70
|
+
values on a frequency grid. The distribution's vector is the selected parameters'
|
|
71
|
+
values, each flattened in C order, concatenated in name order. A parameter's own
|
|
72
|
+
shape is its declared shape: leading batch axes of a batched model stay batch
|
|
73
|
+
axes. Its raw value under the joint prior is its slice of the whitened vector,
|
|
74
|
+
reshaped to its shape, so it keeps one name however many values it holds.
|
|
75
|
+
Bounds are checked element by element, and fixing or tying the parameter raises,
|
|
76
|
+
as for a scalar.
|
|
77
|
+
|
|
78
|
+
The driving case is a port discrepancy (`CONTEXT.md`), whose values number
|
|
79
|
+
about $6 N_f$ for a reciprocal two-port, a few thousand. One parameter per value
|
|
80
|
+
would give it thousands of names and makes attaching and compiling the prior
|
|
81
|
+
slow (7 s and 52 s at 6000 scalars).
|
|
82
|
+
|
|
66
83
|
### Space
|
|
67
84
|
|
|
68
85
|
`space` states the space the distribution is over: `'raw'`, `'declared'` or
|
|
@@ -130,6 +147,14 @@ CDF.
|
|
|
130
147
|
- `prf.resolve` keeps a joint prior, since it is not a tie and does not change
|
|
131
148
|
what the parameters are. `prf.unwrap` drops it, as it drops every prior.
|
|
132
149
|
|
|
150
|
+
### A constant whitening log-determinant
|
|
151
|
+
|
|
152
|
+
Scoring in raw space adds the log-determinant of the whitening. When the
|
|
153
|
+
whitening's Jacobian is constant, as for a multivariate normal, that term is a
|
|
154
|
+
constant: it is computed once, when the prior is attached, and held by the joint
|
|
155
|
+
prior. Recomputing it as a dense Jacobian and its determinant on every call cost
|
|
156
|
+
15.7 s per evaluation at 6000 values.
|
|
157
|
+
|
|
133
158
|
## Rejected options
|
|
134
159
|
|
|
135
160
|
- **Polishing the collapsing `Probabilistic`** (keeping the names of the
|
|
@@ -148,6 +173,10 @@ CDF.
|
|
|
148
173
|
- **`Transformed.icdf` in distreqx.** It would not be an inverse CDF, and it would
|
|
149
174
|
put ParamRF's sampler logic in another repository.
|
|
150
175
|
|
|
176
|
+
- **A separate Gaussian-prior node for large arrays** (for a port discrepancy).
|
|
177
|
+
It would be a second way to attach a joint prior, which this ADR removed, and a
|
|
178
|
+
flow or Gaussian posterior over any array parameter needs the same thing.
|
|
179
|
+
|
|
151
180
|
## Consequences
|
|
152
181
|
|
|
153
182
|
- The old `Probabilistic` constructor (`target=`, `static=`, tuple targets) and
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Discrepancy Prediction
|
|
2
|
+
======================
|
|
3
|
+
|
|
4
|
+
A fit with a Gaussian-process discrepancy learns how the model differs from the data, not just the model parameters. That discrepancy can be predicted at frequencies the fit never saw, with its uncertainty. In this example we fit the S11 of a 10 m coaxial line with a GP discrepancy, then predict the discrepancy off the fit grid.
|
|
5
|
+
|
|
6
|
+
Fitting with a Discrepancy
|
|
7
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
8
|
+
|
|
9
|
+
The measured S11 is a coaxial line plus a slow ripple that the model cannot produce, and a little measurement noise. We fit the dielectric constant, with a :class:`~pmrf.discrepancy_models.GaussianProcess` to absorb the ripple. Its kernel's length scale is in the fit frequency's unit, here MHz:
|
|
10
|
+
|
|
11
|
+
.. plot::
|
|
12
|
+
:context: reset
|
|
13
|
+
:include-source:
|
|
14
|
+
:nofigs:
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
import pmrf as prf
|
|
18
|
+
from pmrf.models import CoaxialLine
|
|
19
|
+
from pmrf.materials import BulkConductor, ConstantDielectric
|
|
20
|
+
from pmrf.fitting import fit
|
|
21
|
+
from pmrf.likelihoods import GaussianLikelihood
|
|
22
|
+
from pmrf.discrepancy_models import GaussianProcess
|
|
23
|
+
from pmrf.covariance_kernels import Matern52Kernel
|
|
24
|
+
|
|
25
|
+
def coax(ep_r):
|
|
26
|
+
return CoaxialLine(
|
|
27
|
+
d_in=1.05e-3, d_out=3.2e-3,
|
|
28
|
+
dielectric=ConstantDielectric(ep_r=ep_r, tand=1e-3),
|
|
29
|
+
conductor=BulkConductor(sigma=1 / 1.7e-8),
|
|
30
|
+
length=10.0,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
freq = prf.Frequency(10, 500, 100, 'MHz')
|
|
34
|
+
ripple = 0.01 * np.exp(2j * np.pi * freq.f_scaled / 150.0)
|
|
35
|
+
rng = np.random.default_rng(0)
|
|
36
|
+
noise = 1e-3 * (rng.normal(size=freq.npoints) + 1j * rng.normal(size=freq.npoints))
|
|
37
|
+
s11 = np.asarray(coax(1.40).s(freq)[:, 0, 0]) + ripple + noise
|
|
38
|
+
|
|
39
|
+
gp = GaussianProcess(
|
|
40
|
+
Matern52Kernel(prf.Bounded(10.0, 200.0, value=50.0)) * prf.Bounded(1e-5, 1e-3, value=1e-4)
|
|
41
|
+
)
|
|
42
|
+
result = fit(
|
|
43
|
+
coax(prf.Bounded(1.38, 1.42, value=1.39)), s11, frequency=freq, features='s11',
|
|
44
|
+
likelihood=GaussianLikelihood(prf.Bounded(1e-7, 1e-4, value=1e-6)),
|
|
45
|
+
discrepancy=gp,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
Predicting the Discrepancy
|
|
49
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
50
|
+
|
|
51
|
+
The fitted :class:`~pmrf.evaluators.MarginalLogLikelihood`, with its optimized GP hyperparameters and noise, is the one term of the fit's objective. :meth:`~pmrf.evaluators.MarginalLogLikelihood.predict_discrepancy` conditions the GP on the fit's residuals and returns the distribution of the discrepancy at new frequencies. Here they extend past the fit band and are given in GHz; they are converted to the fit's unit first.
|
|
52
|
+
|
|
53
|
+
.. plot::
|
|
54
|
+
:context:
|
|
55
|
+
:include-source:
|
|
56
|
+
|
|
57
|
+
import matplotlib.pyplot as plt
|
|
58
|
+
|
|
59
|
+
(term,) = result.solution.objective
|
|
60
|
+
mll = term.evaluator.evaluator
|
|
61
|
+
|
|
62
|
+
new_freq = prf.Frequency(0.005, 0.6, 300, 'GHz')
|
|
63
|
+
delta = mll.predict_discrepancy(result.model, freq, new_freq)
|
|
64
|
+
|
|
65
|
+
mean = delta.mean()
|
|
66
|
+
std = np.sqrt(np.diagonal(delta.covariance(), axis1=-2, axis2=-1))
|
|
67
|
+
|
|
68
|
+
fig, axes = plt.subplots(2, 1, sharex=True)
|
|
69
|
+
for ax, m, s, r, part in zip(axes, mean, std, (ripple.real, ripple.imag), ('Re', 'Im')):
|
|
70
|
+
ax.fill_between(new_freq.f_scaled * 1e3, m - s, m + s, alpha=0.3, label=r'$\pm 1\sigma$')
|
|
71
|
+
ax.plot(new_freq.f_scaled * 1e3, m, label='predicted mean')
|
|
72
|
+
ax.plot(freq.f_scaled, r, 'k.', markersize=3, label='true ripple')
|
|
73
|
+
ax.set_ylabel(rf'{part} $\delta$')
|
|
74
|
+
axes[0].legend()
|
|
75
|
+
axes[1].set_xlabel('Frequency (MHz)')
|
|
76
|
+
|
|
77
|
+
The prediction is in event space: one block for the real part of S11 and one for the imaginary part, with frequency last. It is of the discrepancy alone, and excludes measurement noise. Inside the fit band it follows the ripple closely; beyond it, the mean relaxes towards zero and the uncertainty grows back to the GP's prior.
|
|
@@ -9,6 +9,8 @@ This chapter includes some basic examples to demonstrate ParamRF's core features
|
|
|
9
9
|
cascading_and_terminating
|
|
10
10
|
circuit_models
|
|
11
11
|
model_optimization
|
|
12
|
+
discrepancy_prediction
|
|
13
|
+
port_discrepancy
|
|
12
14
|
derivatives_and_sweeps
|
|
13
15
|
parameter_naming_and_model_manipulation
|
|
14
16
|
custom_models
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""Port discrepancy from a reference two-port to a reflection-only transfer fit.
|
|
2
|
+
|
|
3
|
+
Run with ``python docs/examples/port_discrepancy.py --n-frequency 1000``.
|
|
4
|
+
"""
|
|
5
|
+
import argparse
|
|
6
|
+
from time import perf_counter
|
|
7
|
+
|
|
8
|
+
import equinox as eqx
|
|
9
|
+
import jax
|
|
10
|
+
import jax.numpy as jnp
|
|
11
|
+
import numpy as np
|
|
12
|
+
from distreqx.bijectors import AbstractBijector
|
|
13
|
+
from distreqx.distributions import Normal
|
|
14
|
+
from scipy.optimize import minimize
|
|
15
|
+
|
|
16
|
+
import pmrf as prf
|
|
17
|
+
from pmrf.covariance_kernels import Matern52Kernel
|
|
18
|
+
from pmrf.discrepancy_models import GaussianProcess
|
|
19
|
+
from pmrf.evaluators import MarginalLogLikelihood
|
|
20
|
+
from pmrf.likelihoods import GaussianLikelihood
|
|
21
|
+
from pmrf.linearization import posterior_covariance
|
|
22
|
+
from pmrf.models import GridPortDiscrepancy, Load, PortCorrected, RLGCLine, SModel
|
|
23
|
+
|
|
24
|
+
jax.config.update('jax_enable_x64', True)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def line(resistance):
|
|
28
|
+
return RLGCLine(length=10.0, R=resistance, L=250e-9, C=100e-12, G=1e-6)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@prf.derived
|
|
32
|
+
def fitted_line(base, *, log_R):
|
|
33
|
+
return prf.replace(base, R=jnp.exp(log_R))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def truth(frequency):
|
|
37
|
+
f = frequency.f_scaled
|
|
38
|
+
# Skin-effect R and a small inductive dispersion absent from the fit model.
|
|
39
|
+
dispersive = RLGCLine(length=10.0, R=0.5 * jnp.sqrt(f / 250),
|
|
40
|
+
L=250e-9 * (1 + 0.0005 * jnp.log(f / 250)), C=100e-12, G=1e-6)
|
|
41
|
+
return SModel(dispersive.s(frequency), frequency, 50.0)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AbstractPortEventTransform(AbstractBijector, strict=True):
|
|
45
|
+
"""Example-local event transform conditioned on a predicted response."""
|
|
46
|
+
|
|
47
|
+
#: Prediction selecting the local unwrapped logarithm branch.
|
|
48
|
+
predicted: jax.Array
|
|
49
|
+
#: Log-transmission derivatives depend on the response.
|
|
50
|
+
_is_constant_jacobian: bool = eqx.field(static=True, init=False, default=False)
|
|
51
|
+
#: The log determinant depends on transmission magnitude.
|
|
52
|
+
_is_constant_log_det: bool = eqx.field(static=True, init=False, default=False)
|
|
53
|
+
|
|
54
|
+
def inverse_log_det_jacobian(self, event):
|
|
55
|
+
return -self.forward_log_det_jacobian(self.inverse(event))
|
|
56
|
+
|
|
57
|
+
def forward_and_log_det(self, s):
|
|
58
|
+
return self.forward(s), self.forward_log_det_jacobian(s)
|
|
59
|
+
|
|
60
|
+
def inverse_and_log_det(self, event):
|
|
61
|
+
return self.inverse(event), self.inverse_log_det_jacobian(event)
|
|
62
|
+
|
|
63
|
+
def same_as(self, other):
|
|
64
|
+
return self is other
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class PortEventTransform(AbstractPortEventTransform, strict=True):
|
|
68
|
+
r"""Invertible full two-port transform, with frequency last in event space.
|
|
69
|
+
|
|
70
|
+
**Mathematical Formulation**
|
|
71
|
+
|
|
72
|
+
$$h(\widetilde S; S)=\ln|\widetilde S| + i[\operatorname{unwrap}(\arg S)
|
|
73
|
+
+ \operatorname{unwrap}(\arg(\widetilde S/S))].$$
|
|
74
|
+
|
|
75
|
+
Reflections remain additive. Directional transmission logs become their
|
|
76
|
+
mean and half-difference. Each complex log contributes
|
|
77
|
+
$$-2\ln|\widetilde S|$$ to the forward log determinant, and the rotation
|
|
78
|
+
contributes $$-2\ln 2$$ per frequency.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def forward(self, s):
|
|
82
|
+
anchor = jnp.unwrap(jnp.angle(self.predicted), axis=0)
|
|
83
|
+
phase = anchor + jnp.unwrap(jnp.angle(s / self.predicted), axis=0)
|
|
84
|
+
logs = jnp.log(jnp.abs(s)) + 1j * phase
|
|
85
|
+
blocks = jnp.stack((s[:, 0, 0], s[:, 1, 1],
|
|
86
|
+
(logs[:, 1, 0] + logs[:, 0, 1]) / 2,
|
|
87
|
+
(logs[:, 1, 0] - logs[:, 0, 1]) / 2))
|
|
88
|
+
return jnp.stack((blocks.real, blocks.imag), axis=1)
|
|
89
|
+
|
|
90
|
+
def inverse(self, event):
|
|
91
|
+
blocks = event[:, 0] + 1j * event[:, 1]
|
|
92
|
+
return jnp.stack((jnp.stack((blocks[0], jnp.exp(blocks[2] - blocks[3])), axis=-1),
|
|
93
|
+
jnp.stack((jnp.exp(blocks[2] + blocks[3]), blocks[1]), axis=-1)), axis=-2)
|
|
94
|
+
|
|
95
|
+
def forward_log_det_jacobian(self, s):
|
|
96
|
+
# The mean/half-difference rotation has determinant 1/2 for each Re/Im pair.
|
|
97
|
+
return -2 * jnp.log(jnp.abs(s[:, 0, 1])) - 2 * jnp.log(jnp.abs(s[:, 1, 0])) - 2 * jnp.log(2.0)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ReciprocalEventTransform(AbstractPortEventTransform, strict=True):
|
|
101
|
+
r"""Transform the retained reflections and symmetric transmission only.
|
|
102
|
+
|
|
103
|
+
**Mathematical Formulation**
|
|
104
|
+
|
|
105
|
+
The symmetric transmission uses the anchored logarithm of
|
|
106
|
+
:class:`PortEventTransform`, with forward log determinant
|
|
107
|
+
$$-2\ln|\widetilde S^s|.$$ Reflections remain additive.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
def forward(self, s):
|
|
111
|
+
anchor = jnp.unwrap(jnp.angle(self.predicted[:, 2]))
|
|
112
|
+
log_t = jnp.log(jnp.abs(s[:, 2])) + 1j * (
|
|
113
|
+
anchor + jnp.unwrap(jnp.angle(s[:, 2] / self.predicted[:, 2])))
|
|
114
|
+
blocks = jnp.stack((s[:, 0], s[:, 1], log_t))
|
|
115
|
+
return jnp.stack((blocks.real, blocks.imag), axis=1)
|
|
116
|
+
|
|
117
|
+
def inverse(self, event):
|
|
118
|
+
blocks = event[:, 0] + 1j * event[:, 1]
|
|
119
|
+
return jnp.stack((blocks[0], blocks[1], jnp.exp(blocks[2])), axis=-1)
|
|
120
|
+
|
|
121
|
+
def forward_log_det_jacobian(self, s):
|
|
122
|
+
return -2 * jnp.log(jnp.abs(s[:, 2]))
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def reciprocal_predictor(model, frequency):
|
|
126
|
+
s = model.s(frequency)
|
|
127
|
+
return jnp.stack((s[:, 0, 0], s[:, 1, 1], s[:, 1, 0]), axis=-1)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def reference_fit(mll, model, frequency):
|
|
131
|
+
"""Fit the reference line's one log-resistance coordinate."""
|
|
132
|
+
def loss(vector):
|
|
133
|
+
candidate = prf.update(model, {'log_R': vector[0]})
|
|
134
|
+
return -mll(candidate, frequency) - prf.log_prior(candidate)
|
|
135
|
+
|
|
136
|
+
value_gradient = jax.jit(jax.value_and_grad(loss))
|
|
137
|
+
result = minimize(lambda x: tuple(np.asarray(v) for v in value_gradient(jnp.asarray(x))),
|
|
138
|
+
np.array([prf.values(model)['log_R']]), jac=True, method='L-BFGS-B',
|
|
139
|
+
bounds=[(np.log(0.05), np.log(2.0))],
|
|
140
|
+
options={'gtol': 1e-6, 'ftol': 1e-14, 'maxiter': 200})
|
|
141
|
+
if not np.isfinite(result.fun) or np.linalg.norm(result.jac, ord=np.inf) > 1e-3:
|
|
142
|
+
raise RuntimeError(f'MAP fit failed: {result.message}; gradient {result.jac}')
|
|
143
|
+
return prf.update(model, {'log_R': jnp.asarray(result.x[0])})
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def run(n_frequency=1000, n_transfer=None, mc_samples=10000):
|
|
147
|
+
"""Run the complete recipe, returning diagnostics and synchronized timings."""
|
|
148
|
+
n_transfer = n_frequency if n_transfer is None else n_transfer
|
|
149
|
+
timings = {}
|
|
150
|
+
before = perf_counter()
|
|
151
|
+
|
|
152
|
+
def finish(name, value):
|
|
153
|
+
nonlocal before
|
|
154
|
+
jax.block_until_ready(value)
|
|
155
|
+
now = perf_counter()
|
|
156
|
+
timings[name] = now - before
|
|
157
|
+
before = now
|
|
158
|
+
print(f'{name}: {timings[name]:.3f} s', flush=True)
|
|
159
|
+
|
|
160
|
+
frequency = prf.Frequency(10, 500, n_frequency, 'MHz')
|
|
161
|
+
transfer_frequency = prf.Frequency(10.1, 499.9, n_transfer, 'MHz')
|
|
162
|
+
true_s = truth(frequency).s(frequency)
|
|
163
|
+
rng = np.random.default_rng(265)
|
|
164
|
+
sigma = 0.002
|
|
165
|
+
observed = true_s + sigma * (rng.normal(size=true_s.shape) + 1j * rng.normal(size=true_s.shape))
|
|
166
|
+
initial = fitted_line(line(0.5), log_R=prf.Unconstrained(jnp.log(0.5)))
|
|
167
|
+
initial = prf.prior(initial, 'log_R', Normal(jnp.log(0.5), 0.02))
|
|
168
|
+
full_event = PortEventTransform(initial.s(frequency)).forward(observed)
|
|
169
|
+
# Discard only the antisymmetric block, then encode the symmetric log in S space.
|
|
170
|
+
retained = full_event[:3, 0] + 1j * full_event[:3, 1]
|
|
171
|
+
reciprocal_observed = jnp.stack((retained[0], retained[1], jnp.exp(retained[2])), axis=-1)
|
|
172
|
+
variance = jnp.ones((3, 2, n_frequency)) * sigma**2
|
|
173
|
+
variance = variance.at[2].set(sigma**2 / (2 * jnp.abs(reciprocal_observed[:, 2])**2))
|
|
174
|
+
reference = MarginalLogLikelihood(
|
|
175
|
+
predictor=reciprocal_predictor, observed=reciprocal_observed,
|
|
176
|
+
likelihood=GaussianLikelihood(variance),
|
|
177
|
+
discrepancy=GaussianProcess(Matern52Kernel(100.0) * 0.05**2, jitter=1e-8),
|
|
178
|
+
event_transform=ReciprocalEventTransform,
|
|
179
|
+
)
|
|
180
|
+
finish('synthetic data and event transform', reciprocal_observed)
|
|
181
|
+
fitted = reference_fit(reference, initial, frequency)
|
|
182
|
+
finish('reference MAP', fitted.s(frequency))
|
|
183
|
+
event = ReciprocalEventTransform(reciprocal_predictor(fitted, frequency))
|
|
184
|
+
log_residual = event.forward(reciprocal_observed)[2] - event.forward(reciprocal_predictor(fitted, frequency))[2]
|
|
185
|
+
additive_residual = true_s[:, 1, 0] - fitted.s(frequency)[:, 1, 0]
|
|
186
|
+
names, joint = reference.predict_joint(fitted, frequency, transfer_frequency)
|
|
187
|
+
finish('joint prediction', joint.covariance())
|
|
188
|
+
corrected = PortCorrected(fitted, GridPortDiscrepancy(
|
|
189
|
+
prf.Unconstrained(joint.mean()[1:].reshape(3, 2, n_transfer)),
|
|
190
|
+
transfer_frequency, ('11', '22', 's21')))
|
|
191
|
+
corrected = prf.prior(corrected, [*names, 'discrepancy.values'], joint)
|
|
192
|
+
finish('joint prior attachment', prf.log_prior(corrected))
|
|
193
|
+
# Keep the cable as the base: its parameter names and joint prior survive.
|
|
194
|
+
@prf.derived
|
|
195
|
+
def terminated(cable, *, load):
|
|
196
|
+
return cable.terminated(Load(gamma=(load - 50) / (load + 50)))
|
|
197
|
+
|
|
198
|
+
transfer = terminated(corrected, load=prf.Unconstrained(75.0))
|
|
199
|
+
true_reflection = truth(transfer_frequency).terminated(Load(gamma=(76.0 - 50) / (76.0 + 50))).s(transfer_frequency)[:, 0, 0]
|
|
200
|
+
transfer_observed = true_reflection + sigma * (
|
|
201
|
+
rng.normal(size=n_transfer) + 1j * rng.normal(size=n_transfer))
|
|
202
|
+
transfer_mll = MarginalLogLikelihood(
|
|
203
|
+
predictor=lambda m, f: m.s(f)[:, 0, 0], observed=transfer_observed,
|
|
204
|
+
likelihood=GaussianLikelihood(sigma**2),
|
|
205
|
+
)
|
|
206
|
+
# Whiten the cable's joint prior, leaving the new load in ohms.
|
|
207
|
+
mean = joint.mean()
|
|
208
|
+
factor = jnp.linalg.cholesky(joint.covariance())
|
|
209
|
+
|
|
210
|
+
def from_white(x):
|
|
211
|
+
cable_vector = mean + factor @ x[:-1]
|
|
212
|
+
return prf.update(transfer, {'log_R': cable_vector[0],
|
|
213
|
+
'discrepancy.values': cable_vector[1:].reshape(3, 2, n_transfer),
|
|
214
|
+
'load': x[-1]})
|
|
215
|
+
|
|
216
|
+
def loss_white(x):
|
|
217
|
+
return -transfer_mll(from_white(x), transfer_frequency) + jnp.sum(x[:-1]**2) / 2
|
|
218
|
+
|
|
219
|
+
value_gradient = jax.jit(jax.value_and_grad(loss_white))
|
|
220
|
+
solution = minimize(lambda x: tuple(np.asarray(v) for v in value_gradient(jnp.asarray(x))),
|
|
221
|
+
np.r_[np.zeros(len(mean)), 75.0], jac=True, method='L-BFGS-B',
|
|
222
|
+
options={'maxiter': 300, 'gtol': 1e-5, 'ftol': 1e-12})
|
|
223
|
+
if not solution.success:
|
|
224
|
+
raise RuntimeError(f'Transfer fit failed: {solution.message}')
|
|
225
|
+
transfer_map = from_white(jnp.asarray(solution.x))
|
|
226
|
+
finish('transfer MAP', transfer_map.s(transfer_frequency))
|
|
227
|
+
linearization = transfer_mll.linearize(transfer_map, transfer_frequency)
|
|
228
|
+
covariance = posterior_covariance([linearization], transfer_map)
|
|
229
|
+
finish('transfer linearisation and posterior', covariance)
|
|
230
|
+
|
|
231
|
+
# QoI belongs to the unterminated corrected cable; the fitted load does not enter it.
|
|
232
|
+
def qoi(model):
|
|
233
|
+
cable = model.operands[0]
|
|
234
|
+
return jnp.mean(jnp.abs(cable.s(transfer_frequency)[:, 1, 0])**2)
|
|
235
|
+
|
|
236
|
+
(gradient,) = prf.derivative(qoi, transfer_map)
|
|
237
|
+
gradient_values = prf.values(gradient)
|
|
238
|
+
J_q = jnp.concatenate([jnp.ravel(gradient_values[name]) for name in linearization.names])
|
|
239
|
+
q_variance = J_q @ covariance @ J_q
|
|
240
|
+
J = linearization.J.reshape(-1, covariance.shape[0])
|
|
241
|
+
# Independent real/imaginary reflection noise makes Sigma_D diagonal here.
|
|
242
|
+
G = covariance @ J.T / sigma**2
|
|
243
|
+
R = G @ J
|
|
244
|
+
P_q = J_q @ (jnp.eye(len(J_q)) - R)
|
|
245
|
+
G_q = J_q @ G
|
|
246
|
+
finish('QoI and gain diagnostics', (q_variance, P_q, G_q))
|
|
247
|
+
parameter_mean = jnp.concatenate([jnp.ravel(prf.values(transfer_map)[name]) for name in linearization.names])
|
|
248
|
+
posterior_factor = jnp.linalg.cholesky(covariance)
|
|
249
|
+
evaluate = jax.jit(jax.vmap(lambda x: qoi(prf.update(transfer_map, linearization.unflatten(x)))))
|
|
250
|
+
# Batch both sampling and evaluation: 10^4 by 6002 need not stay resident.
|
|
251
|
+
q_samples = []
|
|
252
|
+
for start in range(0, mc_samples, 100):
|
|
253
|
+
count = min(100, mc_samples - start)
|
|
254
|
+
draws = parameter_mean + jnp.asarray(rng.normal(size=(count, len(parameter_mean)))) @ posterior_factor.T
|
|
255
|
+
q_samples.append(np.asarray(evaluate(draws)))
|
|
256
|
+
mc_std = np.std(np.concatenate(q_samples), ddof=1)
|
|
257
|
+
delta_std = float(jnp.sqrt(q_variance))
|
|
258
|
+
finish('Monte Carlo push-forward', q_variance)
|
|
259
|
+
relative_std_error = abs(mc_std / delta_std - 1)
|
|
260
|
+
print(f'QoI {float(qoi(transfer_map)):.6f}; linear std {delta_std:.6g}; '
|
|
261
|
+
f'MC std {mc_std:.6g}; relative std error {relative_std_error:.2%}', flush=True)
|
|
262
|
+
return dict(timings=timings, relative_std_error=relative_std_error,
|
|
263
|
+
frequency=frequency, additive_residual=additive_residual,
|
|
264
|
+
log_residual=log_residual, P_q=P_q, G_q=G_q,
|
|
265
|
+
fitted_load=float(prf.values(transfer_map)['load']),
|
|
266
|
+
delta_std=delta_std, mc_std=mc_std)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
if __name__ == '__main__':
|
|
270
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
271
|
+
parser.add_argument('--n-frequency', type=int, default=1000)
|
|
272
|
+
args = parser.parse_args()
|
|
273
|
+
run(args.n_frequency)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
Port Discrepancy Transfer
|
|
2
|
+
===========================
|
|
3
|
+
|
|
4
|
+
A port discrepancy learned from a two-port reference fit can inform a reflection-only transfer fit. The joint prediction retains the correlation between the cable parameters and its discrepancy. Attach that prediction to :class:`~pmrf.models.PortCorrected` before terminating the cable; the correction then changes the reflection seen through the load as well as the cable's transmission.
|
|
5
|
+
|
|
6
|
+
The runnable :download:`example <port_discrepancy.py>` uses a synthetic 10 m line over 10–500 MHz. The truth has skin-effect resistance and a weak frequency dependence in its inductance. The fit uses constant RLGC, with resistance parameterised by an unconstrained ``log_R`` and a docs-local :func:`~pmrf.derived` constructor. This coordinate admits a Gaussian joint prior while keeping physical resistance positive. The reference fit has a 2% prior standard deviation on log-resistance, representing a separate resistance calibration. This limits the resistance–discrepancy confounding and keeps the subsequent local Gaussian approximation useful.
|
|
7
|
+
|
|
8
|
+
Log Transmission Event Space
|
|
9
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
10
|
+
|
|
11
|
+
Reflection errors are additive. A cable's transmission phase winds many times over the band, so an additive transmission residual oscillates on the inverse-delay scale. A log-ratio residual removes that winding. The example defines its own bijectors, using the ``AbstractBijector`` interface available in released distreqx. For predicted transmission $S$ and observed transmission $\widetilde S$, the transform uses
|
|
12
|
+
|
|
13
|
+
$$h(\widetilde S;S)=\ln|\widetilde S| + i\left[\operatorname{unwrap}(\arg S)+\operatorname{unwrap}\left(\arg\frac{\widetilde S}{S}\right)\right].$$
|
|
14
|
+
|
|
15
|
+
Subtracting $h(S;S)$ gives the unwrapped complex log ratio. Unwrap the ratio along frequency, rather than independently unwrapping the two transmissions. Their initial ratio phase selects the local branch; transmissions must be nonzero and the frequency spacing must resolve their phase. The forward log determinant is $-2\ln|\widetilde S|$ per complex transmission entry.
|
|
16
|
+
|
|
17
|
+
.. literalinclude:: port_discrepancy.py
|
|
18
|
+
:pyobject: PortEventTransform
|
|
19
|
+
|
|
20
|
+
The symmetric transmission block is the mean of the two directional logs; the antisymmetric block is their half-difference. This rotation adds the constant $-2\ln2$ to the log determinant per frequency. The full ``PortEventTransform`` is an invertible, example-local subclass of distreqx's ``AbstractBijector``. For this reciprocal model, the antisymmetric residual has no parameter sensitivity and is independent of the retained blocks in the first-order, equal-directional-noise approximation, so the fit discards it before constructing a reduced, invertible three-block transform. The discarded likelihood and rotation determinant are constants for the reference parameters.
|
|
21
|
+
|
|
22
|
+
.. literalinclude:: port_discrepancy.py
|
|
23
|
+
:pyobject: ReciprocalEventTransform
|
|
24
|
+
|
|
25
|
+
Each directional transmission has noise variance $\sigma^2/\lvert S\rvert^2$ in log space to first order. Averaging independent directional measurements halves that variance. The example fixes this approximation at the observed symmetric transmission, while each additive reflection retains variance $\sigma^2$. This is a small-noise approximation, with GP hyperparameters held fixed.
|
|
26
|
+
|
|
27
|
+
Reference Fit and Joint Handoff
|
|
28
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
29
|
+
|
|
30
|
+
The reference likelihood uses a block-independent Matérn 5/2 GP with discrepancy amplitude 0.05 and length scale 100 MHz. Its event layout is ``(block, real_or_imaginary, frequency)``, with blocks ``('11', '22', 's21')``. The fitted additive S21 residual oscillates while the log residual changes slowly:
|
|
31
|
+
|
|
32
|
+
.. plot::
|
|
33
|
+
:include-source:
|
|
34
|
+
|
|
35
|
+
import importlib.util
|
|
36
|
+
from pathlib import Path
|
|
37
|
+
import matplotlib.pyplot as plt
|
|
38
|
+
|
|
39
|
+
path = Path('port_discrepancy.py')
|
|
40
|
+
spec = importlib.util.spec_from_file_location('port_discrepancy_example', path)
|
|
41
|
+
example = importlib.util.module_from_spec(spec)
|
|
42
|
+
spec.loader.exec_module(example)
|
|
43
|
+
result = example.run(n_frequency=150, n_transfer=30)
|
|
44
|
+
frequency = result['frequency'].f_scaled
|
|
45
|
+
fig, axes = plt.subplots(2, 1, sharex=True)
|
|
46
|
+
axes[0].plot(frequency, result['additive_residual'].real)
|
|
47
|
+
axes[0].set_ylabel('Re additive S21 residual')
|
|
48
|
+
axes[1].plot(frequency, result['log_residual'][0])
|
|
49
|
+
axes[1].set_ylabel('Re log-ratio residual')
|
|
50
|
+
axes[1].set_xlabel('Frequency (MHz)')
|
|
51
|
+
|
|
52
|
+
:meth:`~pmrf.evaluators.MarginalLogLikelihood.predict_joint` returns a Gaussian over ``[log_R; discrepancy]``, with discrepancy flattened in that event order on the transfer grid. Its cross-covariance must survive the handoff:
|
|
53
|
+
|
|
54
|
+
.. code-block:: python
|
|
55
|
+
|
|
56
|
+
names, joint = reference.predict_joint(fitted, frequency, transfer_frequency)
|
|
57
|
+
corrected = PortCorrected(fitted, GridPortDiscrepancy(
|
|
58
|
+
prf.Unconstrained(joint.mean()[1:].reshape(3, 2, n_transfer)),
|
|
59
|
+
transfer_frequency, ('11', '22', 's21')))
|
|
60
|
+
corrected = prf.prior(corrected, [*names, 'discrepancy.values'], joint)
|
|
61
|
+
|
|
62
|
+
The transfer fit terminates this corrected cable in a load whose resistance is a new parameter. It observes S11 only. The example optimises the cable parameters in whitened joint-prior coordinates, then linearises the transfer likelihood in declared coordinates. The dense prior costs memory quadratically and dense posterior operations cost time cubically in the number of transfer points. The plot uses a smaller transfer grid; the standalone command exercises 1000 points in both grids and reports synchronized timings:
|
|
63
|
+
|
|
64
|
+
.. code-block:: bash
|
|
65
|
+
|
|
66
|
+
python docs/examples/port_discrepancy.py --n-frequency 1000
|
|
67
|
+
|
|
68
|
+
Quantity of Interest and Diagnostics
|
|
69
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
70
|
+
|
|
71
|
+
The quantity of interest $q$ is band-mean $|S_{21}|^2$ of the corrected, unterminated cable on the transfer grid. The fitted load informs the cable through the posterior, though $q$ has no direct load dependence. With transfer parameters $\vartheta$, :func:`~pmrf.derivative` supplies $J_q$, and :func:`~pmrf.linearization.posterior_covariance` supplies $\Sigma_\vartheta$:
|
|
72
|
+
|
|
73
|
+
$$\Sigma_q=J_q\Sigma_\vartheta J_q^\top.$$
|
|
74
|
+
|
|
75
|
+
The diagnostic recipe uses the transfer linearisation's event Jacobian $J$ and data covariance $\Sigma_D$:
|
|
76
|
+
|
|
77
|
+
$$G=\Sigma_\vartheta J^\top\Sigma_D^{-1},\qquad R=GJ,\qquad P_q=J_q(I-R),\qquad G_q=J_qG.$$
|
|
78
|
+
|
|
79
|
+
Here $\Sigma_D=\sigma^2 I$: the discrepancy is an explicit parameter with a joint prior, rather than another marginal GP in the transfer likelihood. ``P_q`` measures the QoI sensitivity left to the prior; ``G_q`` maps small reflection-data changes to QoI changes. Both are local diagnostics at the transfer MAP.
|
|
80
|
+
|
|
81
|
+
The script compares the propagated standard deviation with a nonlinear push-forward of 10,000 draws from $N(\hat\vartheta,\Sigma_\vartheta)$, evaluated in batches. The 5% standard-deviation criterion applies at amplitude 0.05; stronger log corrections can make the exponential nonlinearity too large for this linear approximation.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: paramrf
|
|
3
|
-
Version: 0.36.
|
|
3
|
+
Version: 0.36.5
|
|
4
4
|
Summary: Parametric radio frequency modeling
|
|
5
5
|
Author-email: Gary Allen <gvcallen@gmail.com>
|
|
6
6
|
Project-URL: homepage, https://github.com/gvcallen/paramrf
|
|
7
7
|
Description-Content-Type: text/x-rst
|
|
8
8
|
License-File: LICENSE
|
|
9
9
|
License-File: NOTICE
|
|
10
|
-
Requires-Dist: parax>=0.11.
|
|
10
|
+
Requires-Dist: parax>=0.11.6
|
|
11
11
|
Requires-Dist: threadpoolctl
|
|
12
12
|
Requires-Dist: eqxpress
|
|
13
13
|
Requires-Dist: jax
|