paramrf 0.36.6__tar.gz → 0.37.0__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.6 → paramrf-0.37.0}/CONTEXT.md +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/CONTRIBUTING.md +2 -2
- {paramrf-0.36.6/paramrf.egg-info → paramrf-0.37.0}/PKG-INFO +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/README.rst +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/api/index.rst +3 -11
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/core_concepts/core_primitives.rst +7 -7
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/core_concepts/optimization_and_inference.rst +3 -3
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/custom_models.rst +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/discrepancy_prediction.rst +5 -7
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/index.rst +2 -3
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/model_optimization.rst +3 -3
- paramrf-0.37.0/docs/examples/shared_modules.rst +30 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/skrf_comparison/performance.rst +1 -1
- paramrf-0.37.0/docs/tutorials/3_discrepancy_transfer.ipynb +317 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/tutorials/index.rst +2 -1
- paramrf-0.37.0/docs/tutorials/line_internal_discrepancy.py +148 -0
- {paramrf-0.36.6/docs/examples → paramrf-0.37.0/docs/tutorials}/port_discrepancy.py +3 -6
- {paramrf-0.36.6 → paramrf-0.37.0}/paper/paper.md +2 -1
- {paramrf-0.36.6 → paramrf-0.37.0/paramrf.egg-info}/PKG-INFO +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/paramrf.egg-info/SOURCES.txt +22 -16
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/__init__.py +6 -19
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/fitting/minimize.py +18 -18
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/fitting/result.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/fitting/routers.py +3 -3
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/fitting/sample.py +7 -7
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/fitting/targets.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/infer/result.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/infer/sample.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/materials/conductor.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/materials/dielectric.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/materials/roughness.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/materials/substrate.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/__init__.py +9 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/base.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/base.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/coaxial.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/empirical.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/ideal.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/microstrip.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/profiles.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/stripline.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lumped.py +1 -1
- paramrf-0.37.0/pmrf/models/line_discrepancy.py +296 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/port_discrepancy.py +14 -8
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/modules/wrapped.py +1 -1
- paramrf-0.37.0/pmrf/objectives/__init__.py +48 -0
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/objectives}/evaluators.py +25 -25
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/objectives}/problems.py +1 -1
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/objectives}/terms.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/minimize.py +3 -3
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/result.py +2 -2
- paramrf-0.36.6/pmrf/parameters.py → paramrf-0.37.0/pmrf/parameters/__init__.py +14 -14
- paramrf-0.37.0/pmrf/stats/__init__.py +74 -0
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/stats}/covariance_kernels.py +5 -5
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/stats}/discrepancy_models.py +7 -7
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/stats}/likelihoods.py +1 -1
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/stats}/linearization.py +4 -4
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/stats}/noise_models.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/viz/plots.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/pyproject.toml +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_box_space_solving.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_covariance_kernels.py +5 -5
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_discrepancy_models.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_evaluators.py +6 -6
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_fitting_minimize.py +6 -6
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_fitting_sample.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_fitting_targets.py +17 -17
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_infer_base.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_infer_sample.py +1 -1
- paramrf-0.37.0/tests/test_line_internal_discrepancy_example.py +38 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_linearization.py +5 -5
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_map_priors.py +3 -3
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_derived.py +2 -2
- paramrf-0.37.0/tests/test_models/test_line_discrepancy.py +275 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_naming.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_parameters.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_parameters_by_name.py +3 -3
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_parameters_joint_prior.py +7 -7
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_parameters_prior.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_parameters_validity.py +2 -2
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_port_discrepancy_example.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_raw_space_solving.py +3 -3
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_structural_updates.py +1 -1
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_terms.py +7 -7
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_transforms.py +5 -5
- paramrf-0.36.6/docs/examples/port_discrepancy.rst +0 -53
- paramrf-0.36.6/docs/examples/shared_substrates.rst +0 -50
- {paramrf-0.36.6 → paramrf-0.37.0}/.github/workflows/docs.yml +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/.github/workflows/draft-pdf.yml +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/.github/workflows/publish.yml +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/.github/workflows/tests.yml +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/.gitignore +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/AGENTS.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/CHANGELOG.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/CITATION.cff +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/CLAUDE.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/LICENSE +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/NOTICE +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/assets/logo.png +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/Makefile +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/_static/custom.css +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/_templates/autosummary/class.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/_templates/autosummary/function.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/_templates/autosummary/module.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/adr/0001-line-modelling-architecture.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/adr/0002-parameter-api.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/adr/0003-derived-models.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/adr/0004-profiled-lines.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/adr/0005-priors-by-name.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/adr/0006-reference-impedance.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/adr/0007-edge-values.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/agents/domain.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/agents/issue-tracker.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/agents/triage-labels.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/conf.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/core_concepts/index.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/core_concepts/jax_overview.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/core_concepts/parameter_names.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/cascading_and_terminating.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/circuit_clc.png +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/circuit_models.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/derivatives_and_sweeps.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/multiple_models_one_parameter_set.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/examples/parameter_naming_and_model_manipulation.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/index.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/license.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/make.bat +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/research/conductor-loss-alternatives.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/research/holloway1994.pdf +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/research/microstrip-loss-conventions.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/research/precision-coax-microstrip-10-500mhz.md +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/skrf_comparison/index.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/skrf_comparison/overview.rst +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/tutorials/1_cable_fitting.ipynb +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/tutorials/2_chip_inductor_fitting.ipynb +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/tutorials/data/CBN-1.5FT-SMSM.s2p +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/docs/tutorials/data/on-chip-inductor.s2p +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/paper/paper.bib +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/paper/rlc.png +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/paramrf.egg-info/dependency_links.txt +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/paramrf.egg-info/requires.txt +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/paramrf.egg-info/top_level.txt +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/_solver_view.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/fitting/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/frequency.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/infer/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/infer/base.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/infer/solvers/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/infer/solvers/blackjax.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/infer/solvers/polychord.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/materials/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/materials/properties.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/materials/surface_impedance.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/math/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/math/aggregations.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/math/bessel.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/math/conversions.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/math/losses.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/math/misc.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/base.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/bridge.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/callable.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/delegated.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/derived.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/static.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/adapters/wrapped.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/ideal.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/nodal.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/nonuniform.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/lines/planar.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/components/sections.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/cascade.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/circuit/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/circuit/base.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/circuit/circuit.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/circuit/solvers/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/circuit/solvers/nodal.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/circuit/solvers/scattering.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/interconnected/terminated.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/nodal.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/topological.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/composite/transformed.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/surrogates/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/surrogates/expansion.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/models/surrogates/rational.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/modules/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/modules/base.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/network_collection.py +0 -0
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/objectives}/losses.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/base.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/solvers/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/solvers/jaxopt.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/solvers/optimistix.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/optimize/solvers/scipy.py +0 -0
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/parameters}/constraints.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/rf/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/rf/cascade.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/rf/conversions.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/rf/mna.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/serialization.py +0 -0
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/stats}/bijectors.py +0 -0
- {paramrf-0.36.6/pmrf → paramrf-0.37.0/pmrf/stats}/distributions.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/types.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/array.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/debug.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/network.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/random.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/rf.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/transforms.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/tree.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/utils/type.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/pmrf/viz/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/scripts/benchmark_gp_marginal_likelihood.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/scripts/install-test-deps.sh +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/setup.cfg +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/__init__.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/_jit.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/conftest.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/data/10m_cable.s2p +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_autodiff.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_conversions.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_dc_limits.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_fitting_routers.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_frequency.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_materials/test_conductor.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_materials/test_dielectric.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_materials/test_serialization.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_materials/test_substrate.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_materials/test_surface_impedance.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_math/test_bessel.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_model.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_adapters.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_cascade.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_circuit_native_z0.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_circuit_nodal.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_circuit_port_order.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_circuit_scattering.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_circuit_shorted_ports.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_circuit_skrf_port_names.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_circuit_z0.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_current_distribution.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_edge_values.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_interconnected.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_lines.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_lines_skrf_matrix.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_lumped.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_nodal.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_port_discrepancy.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_profiles.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_rational.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_repeated_cascade.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_sections.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_transformed.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_models/test_transformers.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_module.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_optimize_base.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_optimize_minimize.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_serialization.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_utils/test_compress.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_utils/test_debug.py +0 -0
- {paramrf-0.36.6 → paramrf-0.37.0}/tests/test_utils/test_tree.py +0 -0
|
@@ -176,8 +176,8 @@ impedance — so it belongs to the material, not the geometry: it is a field of
|
|
|
176
176
|
|
|
177
177
|
A fit in Kennedy–O'Hagan form treats an observation as $\tilde h = h(\theta) +
|
|
178
178
|
\delta + \varepsilon$: the model prediction, a **discrepancy** and measurement
|
|
179
|
-
noise. `MarginalLogLikelihood` scores it; see
|
|
180
|
-
`pmrf.
|
|
179
|
+
noise. `pmrf.objectives.MarginalLogLikelihood` scores it; see
|
|
180
|
+
`pmrf.stats.GaussianProcess` and `pmrf.stats.GaussianLikelihood`.
|
|
181
181
|
|
|
182
182
|
### Event space and event block
|
|
183
183
|
|
|
@@ -32,13 +32,13 @@ For docs, we use sphinx. Simply run:
|
|
|
32
32
|
In general, ParamRF follows a tiered API in order to isolate the JAX and solver logic. It is useful to understand the module split (and perhaps have a basic grasp on JAX PyTrees and perhaps the `Equinox` library) before continuing. A brief overview on the different modules and some JAX "gotchas" is given below.
|
|
33
33
|
|
|
34
34
|
### The Module Split
|
|
35
|
-
Higher-level methods (fitting, optimization etc.) should be available to the user with minimal imports. However, these routines should build on top of "glue" helpers so that power users can pass additional arguments (such as custom evaluators in `pmrf.
|
|
35
|
+
Higher-level methods (fitting, optimization etc.) should be available to the user with minimal imports. However, these routines should build on top of "glue" helpers so that power users can pass additional arguments (such as custom evaluators in `pmrf.objectives`) to customize the algorithm.
|
|
36
36
|
|
|
37
37
|
* **Core Primitives** (`pmrf.Model`, `pmrf.Frequency`, `pmrf.Param`). These are the library's core primitives. They represent the foundational classes and functions that both users and library routines will interact with.
|
|
38
38
|
* **Lower-level Routines** (`pmrf.math`, `pmrf.rf`). These functions implement simple, standard formulae. They should operate directly on JAX arrays or very simple PyTree structures.
|
|
39
39
|
* **RF Models** (`pmrf.models`). These are the main, built-in RF models in the library. They have been separated into an deep organizational structure (e.g. lumped components, surrogates etc.). If a model's forward pass has many different possible implementations (for example, different models for a coaxial line, or different circuit solver algorithms) then a separate strategy class should be created alongside the model which implements some abstract interface (referred to as the "strategy" pattern, described below). See `pmrf.models.CoaxialLine` and `pmrf.models.Circuit` for simple and complex examples, respectively.
|
|
40
40
|
* **General Algorithms** (`pmrf.optimize` and `pmrf.infer`). These are a combination of functional entry points and wrappers around external libraries. They refer to parametric, model-agnostic algorithms. These modules should be structured in a tiered manner to decouple the Equinox/JAX logic from the RF/model logic. Refer to `pmrf.optimize.base` and `pmrf.optimize.minimize` for a good example.
|
|
41
|
-
* **Toolkits** (`pmrf.
|
|
41
|
+
* **Toolkits** (`pmrf.objectives`, `pmrf.stats`). These provide evaluators, losses, likelihoods, discrepancy models, noise models, and covariance kernels that users can mix and match.
|
|
42
42
|
* **Routers** (`pmrf.fitting`) This is an example of a high-level convenience API specifically for fitting models to measured data. Functions here, like `fit`, `fit_minimize` or `fit_sample`, essentially act as "routers", converting different data formats (e.g. `skrf.Network`) and user specs into code that other modules in the library can understand.
|
|
43
43
|
|
|
44
44
|
### Abstract Classes and the Strategy Pattern
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: paramrf
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.37.0
|
|
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
|
|
@@ -107,7 +107,7 @@ The code below demonstrate how to define and optimize an RLC model to satisfy a
|
|
|
107
107
|
C = prf.Bounded(0.0, 100.0, scale=1e-12, name='C')
|
|
108
108
|
|
|
109
109
|
model = Resistor(R) ** Inductor(L) ** Capacitor(C)
|
|
110
|
-
goal = prf.
|
|
110
|
+
goal = prf.objectives.Goal('s11_db', '<', -20)
|
|
111
111
|
passband = prf.Frequency(2, 5, 101, 'GHz')
|
|
112
112
|
|
|
113
113
|
result = prf.optimize.minimize(goal, model, passband, solver=prf.optimize.NelderMead())
|
|
@@ -60,7 +60,7 @@ The code below demonstrate how to define and optimize an RLC model to satisfy a
|
|
|
60
60
|
C = prf.Bounded(0.0, 100.0, scale=1e-12, name='C')
|
|
61
61
|
|
|
62
62
|
model = Resistor(R) ** Inductor(L) ** Capacitor(C)
|
|
63
|
-
goal = prf.
|
|
63
|
+
goal = prf.objectives.Goal('s11_db', '<', -20)
|
|
64
64
|
passband = prf.Frequency(2, 5, 101, 'GHz')
|
|
65
65
|
|
|
66
66
|
result = prf.optimize.minimize(goal, model, passband, solver=prf.optimize.NelderMead())
|
|
@@ -120,4 +120,4 @@ or using the BibTeX:
|
|
|
120
120
|
eprint = {2510.15881},
|
|
121
121
|
archivePrefix = {arXiv},
|
|
122
122
|
primaryClass = {cs.OH},
|
|
123
|
-
}
|
|
123
|
+
}
|
|
@@ -45,6 +45,9 @@ Main Modules
|
|
|
45
45
|
pmrf.models
|
|
46
46
|
pmrf.modules
|
|
47
47
|
pmrf.materials
|
|
48
|
+
pmrf.parameters
|
|
49
|
+
pmrf.stats
|
|
50
|
+
pmrf.objectives
|
|
48
51
|
pmrf.optimize
|
|
49
52
|
pmrf.infer
|
|
50
53
|
pmrf.fitting
|
|
@@ -56,18 +59,7 @@ Other Modules
|
|
|
56
59
|
:toctree: generated/
|
|
57
60
|
:recursive:
|
|
58
61
|
|
|
59
|
-
pmrf.constraints
|
|
60
|
-
pmrf.covariance_kernels
|
|
61
|
-
pmrf.discrepancy_models
|
|
62
|
-
pmrf.distributions
|
|
63
|
-
pmrf.evaluators
|
|
64
|
-
pmrf.problems
|
|
65
|
-
pmrf.terms
|
|
66
|
-
pmrf.likelihoods
|
|
67
|
-
pmrf.losses
|
|
68
62
|
pmrf.math
|
|
69
|
-
pmrf.noise_models
|
|
70
|
-
pmrf.parameters
|
|
71
63
|
pmrf.rf
|
|
72
64
|
pmrf.serialization
|
|
73
65
|
pmrf.viz
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Core Primitives
|
|
2
2
|
===============
|
|
3
3
|
|
|
4
|
-
The core primitives in ParamRF are the :class:`pmrf.Model`, :class:`pmrf.Frequency` and :class:`pmrf.Param` classes. Other classes, such as JAX arrays and :mod:`pmrf.
|
|
4
|
+
The core primitives in ParamRF are the :class:`pmrf.Model`, :class:`pmrf.Frequency` and :class:`pmrf.Param` classes. Other classes, such as JAX arrays and :mod:`pmrf.objectives`, help bring the rest of the library together.
|
|
5
5
|
|
|
6
6
|
The Model
|
|
7
7
|
~~~~~~~~~
|
|
@@ -26,16 +26,16 @@ Power users may want to have a look at the `Parax <https://github.com/gvcallen/p
|
|
|
26
26
|
The Evaluator and other classes
|
|
27
27
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
28
28
|
|
|
29
|
-
:class:`~pmrf.
|
|
29
|
+
:class:`~pmrf.objectives.AbstractEvaluator` is an interface for a callable class that can be used to "evaluate" a model over frequency. Its output is a :class:`jnp.ndarray` (tensor or scalar). Evaluators can be used for extraction of model features, encapsulating loss/error/likelihood functions, and more. For example, to create a goal-oriented objective function for an optimization, the :class:`~pmrf.objectives.Goal` evaluator can be used.
|
|
30
30
|
|
|
31
|
-
Evaluators are created automatically when fitting routines are called, such as :func:`~pmrf.optimize.minimize` or :func:`~pmrf.infer.sample`. For example, specifying ``'s21_db'`` as a fitting feature for :func:`~pmrf.fitting.fit` creates a :class:`~pmrf.
|
|
31
|
+
Evaluators are created automatically when fitting routines are called, such as :func:`~pmrf.optimize.minimize` or :func:`~pmrf.infer.sample`. For example, specifying ``'s21_db'`` as a fitting feature for :func:`~pmrf.fitting.fit` creates a :class:`~pmrf.objectives.Feature` evaluator, whilst specifying a loss or likelihood function creates a :class:`~pmrf.objectives.TargetLoss` or :class:`~pmrf.objectives.MarginalLogLikelihood` evaluator respectively.
|
|
32
32
|
|
|
33
33
|
Since an evaluator takes frequency *as input*, the same evaluator can be re-used over different sweeps. For example, an evaluator can be fitted over a coarse sweep of measured data, and then plotted over a much finer one.
|
|
34
34
|
|
|
35
|
-
When a model is solved, an evaluator is paired with the sweep it should be evaluated over using :class:`pmrf.
|
|
35
|
+
When a model is solved, an evaluator is paired with the sweep it should be evaluated over using :class:`pmrf.objectives.BoundEvaluator`, which optionally also accepts a relative weight. The result is a *term*, which is any callable that takes a model and returns a result. A :class:`pmrf.objectives.SummedTerms` problem is then simply a model together with the terms to be summed over it. Where an evaluator takes a model and a frequency, and a term takes a model, a :class:`pmrf.objectives.AbstractProblem` takes nothing further and returns the result directly.
|
|
36
36
|
|
|
37
|
-
Terms that do not require a frequency, such as a penalty on the model's parameters, can be written as a plain function of the model. The built-in terms inherit from :class:`pmrf.
|
|
37
|
+
Terms that do not require a frequency, such as a penalty on the model's parameters, can be written as a plain function of the model. The built-in terms inherit from :class:`pmrf.objectives.AbstractTerm`, but this is a convenience and not a requirement. In most cases these classes do not need to be created manually, as :func:`~pmrf.optimize.minimize` and :func:`~pmrf.infer.sample` create them automatically.
|
|
38
38
|
|
|
39
|
-
Priors are parameter metadata, and are stripped when a tree is unwrapped for a solver, so they cannot be read from inside an evaluator or a term. Instead, :class:`pmrf.
|
|
39
|
+
Priors are parameter metadata, and are stripped when a tree is unwrapped for a solver, so they cannot be read from inside an evaluator or a term. Instead, :class:`pmrf.objectives.PriorPenalized` extracts them once when it is constructed and evaluates them against the parameter values on each call. This is what separates a maximum a posteriori estimate from a maximum likelihood one, and it is selected automatically when `inference` is 'bayesian' in :func:`~pmrf.fitting.fit`.
|
|
40
40
|
|
|
41
|
-
Other
|
|
41
|
+
Other interfaces include :class:`~pmrf.objectives.AbstractLoss`, :class:`~pmrf.stats.AbstractLikelihood`, :class:`~pmrf.stats.AbstractDiscrepancyModel` and :class:`~pmrf.stats.AbstractNoiseModel`. See the :doc:`../tutorials/index` section for more examples.
|
|
@@ -17,11 +17,11 @@ An objective can also be paired with its own frequency sweep. This allows a sing
|
|
|
17
17
|
.. code-block:: python
|
|
18
18
|
|
|
19
19
|
result = minimize([
|
|
20
|
-
(prf.
|
|
21
|
-
(prf.
|
|
20
|
+
(prf.objectives.Goal('s11_db', '<', -20), prf.Frequency(50, 130, 101, 'MHz')),
|
|
21
|
+
(prf.objectives.Goal('s21_db', '>', -1), prf.Frequency(10, 500, 401, 'MHz')),
|
|
22
22
|
], model)
|
|
23
23
|
|
|
24
|
-
To weight the objectives against one another, the pairing can be created explicitly using :class:`pmrf.
|
|
24
|
+
To weight the objectives against one another, the pairing can be created explicitly using :class:`pmrf.objectives.BoundEvaluator`. Any callable that accepts only a model, such as a penalty on the parameters that requires no frequency, can also be summed in directly:
|
|
25
25
|
|
|
26
26
|
.. code-block:: python
|
|
27
27
|
|
|
@@ -54,7 +54,7 @@ The code below demonstrates this by extending the previous class, while constrai
|
|
|
54
54
|
:include-source:
|
|
55
55
|
|
|
56
56
|
# <previous imports>
|
|
57
|
-
from pmrf.constraints import Positive
|
|
57
|
+
from pmrf.parameters.constraints import Positive
|
|
58
58
|
|
|
59
59
|
class Capacitor(Capacitor):
|
|
60
60
|
C: prf.Param = prf.param(constraint=Positive(), as_free=True, scale=1e-12)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Discrepancy Prediction
|
|
2
2
|
======================
|
|
3
3
|
|
|
4
|
-
A
|
|
4
|
+
A fitted Gaussian-process discrepancy can be predicted at new frequencies, with its uncertainty. Here we fit the S11 of a coaxial line and predict its discrepancy beyond the fit band.
|
|
5
5
|
|
|
6
6
|
Fitting with a Discrepancy
|
|
7
7
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
We add a slow ripple and measurement noise to the line's S11, then fit the dielectric constant with a :class:`~pmrf.stats.GaussianProcess` for the discrepancy. The kernel length scale uses the fit frequency's unit, MHz:
|
|
10
10
|
|
|
11
11
|
.. plot::
|
|
12
12
|
:context: reset
|
|
@@ -18,9 +18,7 @@ The measured S11 is a coaxial line plus a slow ripple that the model cannot prod
|
|
|
18
18
|
from pmrf.models import CoaxialLine
|
|
19
19
|
from pmrf.materials import BulkConductor, ConstantDielectric
|
|
20
20
|
from pmrf.fitting import fit
|
|
21
|
-
from pmrf.
|
|
22
|
-
from pmrf.discrepancy_models import GaussianProcess
|
|
23
|
-
from pmrf.covariance_kernels import Matern52Kernel
|
|
21
|
+
from pmrf.stats import GaussianLikelihood, GaussianProcess, Matern52Kernel
|
|
24
22
|
|
|
25
23
|
def coax(ep_r):
|
|
26
24
|
return CoaxialLine(
|
|
@@ -48,7 +46,7 @@ The measured S11 is a coaxial line plus a slow ripple that the model cannot prod
|
|
|
48
46
|
Predicting the Discrepancy
|
|
49
47
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
50
48
|
|
|
51
|
-
|
|
49
|
+
:meth:`~pmrf.objectives.MarginalLogLikelihood.predict_discrepancy` uses the fitted model and residuals to predict the discrepancy. The new frequency grid extends beyond the fit band and uses GHz; the method handles the unit conversion.
|
|
52
50
|
|
|
53
51
|
.. plot::
|
|
54
52
|
:context:
|
|
@@ -74,4 +72,4 @@ The fitted :class:`~pmrf.evaluators.MarginalLogLikelihood`, with its optimized G
|
|
|
74
72
|
axes[0].legend()
|
|
75
73
|
axes[1].set_xlabel('Frequency (MHz)')
|
|
76
74
|
|
|
77
|
-
The prediction
|
|
75
|
+
The prediction has separate real and imaginary S11 blocks, with frequency last. It excludes measurement noise. Beyond the fit band, the mean approaches zero and uncertainty grows.
|
|
@@ -9,10 +9,9 @@ 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
|
|
14
12
|
derivatives_and_sweeps
|
|
15
13
|
parameter_naming_and_model_manipulation
|
|
16
14
|
custom_models
|
|
17
15
|
multiple_models_one_parameter_set
|
|
18
|
-
|
|
16
|
+
shared_modules
|
|
17
|
+
discrepancy_prediction
|
|
@@ -26,13 +26,13 @@ It is best practice to apply a scaling factor to our values in order to keep the
|
|
|
26
26
|
Running the Optimizer
|
|
27
27
|
~~~~~~~~~~~~~~~~~~~~~
|
|
28
28
|
|
|
29
|
-
Next, we define our design goals. In this case, we want to ensure good matching (low reflection) across our passband. We can use the :class:`~pmrf.
|
|
29
|
+
Next, we define our design goals. In this case, we want to ensure good matching (low reflection) across our passband. We can use the :class:`~pmrf.objectives.Goal` evaluator and pass it to the :func:`~pmrf.optimize.minimize` function alongside our frequency range:
|
|
30
30
|
|
|
31
31
|
.. plot::
|
|
32
32
|
:context:
|
|
33
33
|
:include-source:
|
|
34
34
|
|
|
35
|
-
from pmrf.
|
|
35
|
+
from pmrf.objectives import Goal
|
|
36
36
|
from pmrf.optimize import minimize, ScipyMinimize
|
|
37
37
|
|
|
38
38
|
match_goal = Goal('s11_db', '<', -20)
|
|
@@ -71,6 +71,6 @@ For more complex designs, the :func:`~pmrf.optimize.minimize` function can accep
|
|
|
71
71
|
(Goal('s21_db', '<', -40), stopband),
|
|
72
72
|
], lpf, solver=ScipyMinimize(method='L-BFGS-B'))
|
|
73
73
|
|
|
74
|
-
See the :doc:`/core_concepts/optimization_and_inference` page for how to weight such goals against one another. For even more advanced optimization, custom losses can be specified, either using the built-in losses in :mod:`~pmrf.
|
|
74
|
+
See the :doc:`/core_concepts/optimization_and_inference` page for how to weight such goals against one another. For even more advanced optimization, custom losses can be specified, either using the built-in losses in :mod:`~pmrf.objectives`; using a custom callable; or by creating a custom :class:`~pmrf.objectives.AbstractEvaluator`. The last example is the most powerful, allowing the specification of arbitrary, tunable hyper-parameters.
|
|
75
75
|
|
|
76
76
|
Note that ParamRF also provides convenience functions for fitting models directly to data in :func:`~pmrf.fitting`. See the tutorial for a detailed guide.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Sharing a Module Across Models
|
|
2
|
+
==============================
|
|
3
|
+
|
|
4
|
+
A model normally owns its modules and their parameters. To share one module across
|
|
5
|
+
several models, store it on an :class:`pmrf.models.AbstractBuilder` and pass it to
|
|
6
|
+
each model in ``build``. This board shares one substrate between two traces.
|
|
7
|
+
|
|
8
|
+
.. jupyter-execute::
|
|
9
|
+
|
|
10
|
+
import pmrf as prf
|
|
11
|
+
from pmrf.materials import ConstantDielectric, Substrate
|
|
12
|
+
from pmrf.models import AbstractBuilder, MicrostripLine
|
|
13
|
+
|
|
14
|
+
class Board(AbstractBuilder):
|
|
15
|
+
substrate: Substrate
|
|
16
|
+
w1: prf.Param
|
|
17
|
+
w2: prf.Param
|
|
18
|
+
|
|
19
|
+
def build(self):
|
|
20
|
+
return (
|
|
21
|
+
MicrostripLine(w=self.w1, substrate=self.substrate, length=0.1)
|
|
22
|
+
** MicrostripLine(w=self.w2, substrate=self.substrate, length=0.2)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
board = Board(
|
|
26
|
+
substrate=Substrate(h=1.6e-3, dielectric=ConstantDielectric(ep_r=4.3, tand=0.02)),
|
|
27
|
+
w1=1.0e-3,
|
|
28
|
+
w2=2.0e-3,
|
|
29
|
+
)
|
|
30
|
+
list(prf.params(board))
|
|
@@ -28,7 +28,7 @@ The script below builds the same circuit in ``scikit-rf`` and ParamRF once, then
|
|
|
28
28
|
GlobalMNACircuitSolver,
|
|
29
29
|
)
|
|
30
30
|
from pmrf.parameters import Bounded
|
|
31
|
-
from pmrf.
|
|
31
|
+
from pmrf.objectives import Goal
|
|
32
32
|
from pmrf.optimize import minimize as pmrf_minimize, ScipyMinimize
|
|
33
33
|
|
|
34
34
|
c = scipy.constants.c
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# Fitting and transferring discrepancy\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"A discrepancy describes the part of a measured response that a physical model misses. This tutorial fits a lossy coax with a small conductor error, predicts the correction with its uncertainty, and carries it to a new line length. It then shows how a port discrepancy can carry a joint parameter and correction prediction into a terminated circuit.\n",
|
|
10
|
+
"\n",
|
|
11
|
+
"The [discrepancy prediction example](../examples/discrepancy_prediction.rst) is the short introduction. The code here uses small synthetic data sets so every result can be checked against a known line."
|
|
12
|
+
],
|
|
13
|
+
"id": "discrepancy-01"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"cell_type": "markdown",
|
|
17
|
+
"metadata": {},
|
|
18
|
+
"source": [
|
|
19
|
+
"## A reference line and a shorter target\n",
|
|
20
|
+
"\n",
|
|
21
|
+
"The reference line is 30 cm long; the target is 15 cm. The nominal line has 10% excess conductor loss. A port correction belongs to the reference response, while an internal correction changes characteristic impedance, attenuation, and phase per unit length. That distinction matters when the line length changes."
|
|
22
|
+
],
|
|
23
|
+
"id": "discrepancy-02"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"cell_type": "code",
|
|
27
|
+
"execution_count": null,
|
|
28
|
+
"metadata": {},
|
|
29
|
+
"outputs": [],
|
|
30
|
+
"source": [
|
|
31
|
+
"from pathlib import Path\n",
|
|
32
|
+
"import sys\n",
|
|
33
|
+
"import jax\n",
|
|
34
|
+
"import jax.numpy as jnp\n",
|
|
35
|
+
"import matplotlib.pyplot as plt\n",
|
|
36
|
+
"import numpy as np\n",
|
|
37
|
+
"from scipy.optimize import minimize as scipy_minimize\n",
|
|
38
|
+
"import pmrf as prf\n",
|
|
39
|
+
"from pmrf.materials import BulkConductor, ConstantDielectric\n",
|
|
40
|
+
"from pmrf.models import (\n",
|
|
41
|
+
" CoaxialLine, GridLineDiscrepancy, GridPortDiscrepancy, LineCorrected,\n",
|
|
42
|
+
" Load, PortCorrected, line_internal_features, project_line_basis_joint,\n",
|
|
43
|
+
" reference_line_features,\n",
|
|
44
|
+
")\n",
|
|
45
|
+
"from pmrf.objectives import MarginalLogLikelihood\n",
|
|
46
|
+
"from pmrf.optimize import ScipyMinimize, minimize\n",
|
|
47
|
+
"from pmrf.stats import (\n",
|
|
48
|
+
" GaussianLikelihood, GaussianProcess, Matern52Kernel, RBFKernel, posterior_covariance,\n",
|
|
49
|
+
")\n",
|
|
50
|
+
"\n",
|
|
51
|
+
"tutorial_dir = Path.cwd()\n",
|
|
52
|
+
"if not (tutorial_dir / \"line_internal_discrepancy.py\").exists():\n",
|
|
53
|
+
" tutorial_dir = Path(\"docs/tutorials\")\n",
|
|
54
|
+
"sys.path.insert(0, str(tutorial_dir.resolve()))\n",
|
|
55
|
+
"from line_internal_discrepancy import (\n",
|
|
56
|
+
" MODEL_SIGMA, TRUTH_SIGMA, _basis, compare_transfer,\n",
|
|
57
|
+
" exact_values, frequency, line,\n",
|
|
58
|
+
")\n",
|
|
59
|
+
"from port_discrepancy import ReciprocalEventTransform, reciprocal_predictor\n",
|
|
60
|
+
"\n",
|
|
61
|
+
"f = frequency()\n",
|
|
62
|
+
"comparison = compare_transfer()\n",
|
|
63
|
+
"for name, errors in comparison.items():\n",
|
|
64
|
+
" print(f\"{name}: RMS S21 error = {errors['rms_s21']:.4g}, eta bias = {errors['eta_bias']:.4g}\")"
|
|
65
|
+
],
|
|
66
|
+
"id": "discrepancy-03"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"cell_type": "markdown",
|
|
70
|
+
"metadata": {},
|
|
71
|
+
"source": [
|
|
72
|
+
"The internal correction follows the shorter line's electrical length. The port correction uses the reference line's S-parameter error as if it belonged to the shorter line."
|
|
73
|
+
],
|
|
74
|
+
"id": "discrepancy-04"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"cell_type": "markdown",
|
|
78
|
+
"metadata": {},
|
|
79
|
+
"source": [
|
|
80
|
+
"## Learn an internal correction\n",
|
|
81
|
+
"\n",
|
|
82
|
+
"Here the reference measurement supplies characteristic impedance $Z_c$ and propagation constant $\\gamma$. `reference_line_features` converts them to the four log-ratio channels used by `GridLineDiscrepancy`: real and imaginary $\\log Z_c$, log attenuation, and log phase constant. The likelihood variance is specified in this transformed event space.\n",
|
|
83
|
+
"\n",
|
|
84
|
+
"The nominal model has a free conductor parameter. `predict_joint` returns one Gaussian over that parameter and the correction grid, preserving their covariance."
|
|
85
|
+
],
|
|
86
|
+
"id": "discrepancy-05"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"cell_type": "code",
|
|
90
|
+
"execution_count": null,
|
|
91
|
+
"metadata": {},
|
|
92
|
+
"outputs": [],
|
|
93
|
+
"source": [
|
|
94
|
+
"reference = line(0.30, TRUTH_SIGMA)\n",
|
|
95
|
+
"zc, gamma_length = reference.zc_and_gammaL(f)\n",
|
|
96
|
+
"observed = reference_line_features(zc, gamma_length / 0.30)\n",
|
|
97
|
+
"nominal = line(0.30, MODEL_SIGMA, free_sigma=True)\n",
|
|
98
|
+
"reference_likelihood = MarginalLogLikelihood(\n",
|
|
99
|
+
" predictor=line_internal_features,\n",
|
|
100
|
+
" observed=observed,\n",
|
|
101
|
+
" likelihood=GaussianLikelihood(1e-10),\n",
|
|
102
|
+
" discrepancy=GaussianProcess(RBFKernel(0.5) * 0.1**2, jitter=1e-12),\n",
|
|
103
|
+
")\n",
|
|
104
|
+
"names, joint = reference_likelihood.predict_joint(nominal, f, f)\n",
|
|
105
|
+
"print(\"Joint dimensions:\", joint.mean().shape[0], \"parameter names:\", names)"
|
|
106
|
+
],
|
|
107
|
+
"id": "discrepancy-06"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"cell_type": "markdown",
|
|
111
|
+
"metadata": {},
|
|
112
|
+
"source": [
|
|
113
|
+
"The same prediction can be attached to a shorter line. The joint prior keeps the conductor parameter correlated with the discrepancy values. `truncate='unnormalised'` respects the conductor's validity range for MAP work; it is unsuitable for sampling or evidence calculations."
|
|
114
|
+
],
|
|
115
|
+
"id": "discrepancy-07"
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"cell_type": "code",
|
|
119
|
+
"execution_count": null,
|
|
120
|
+
"metadata": {},
|
|
121
|
+
"outputs": [],
|
|
122
|
+
"source": [
|
|
123
|
+
"carrier = GridLineDiscrepancy.from_prediction(f, joint, joint=True)\n",
|
|
124
|
+
"short_line = LineCorrected(line(0.15, MODEL_SIGMA, free_sigma=True), carrier)\n",
|
|
125
|
+
"short_line = prf.prior(\n",
|
|
126
|
+
" short_line, [*names, \"discrepancy.values\"], joint,\n",
|
|
127
|
+
" truncate=\"unnormalised\",\n",
|
|
128
|
+
")\n",
|
|
129
|
+
"truth_short = line(0.15, TRUTH_SIGMA)\n",
|
|
130
|
+
"rms_s21 = np.sqrt(np.mean(np.abs(np.asarray(short_line.s(f) - truth_short.s(f))[:, 1, 0])**2))\n",
|
|
131
|
+
"print(\"Transferred RMS S21 error:\", float(rms_s21))"
|
|
132
|
+
],
|
|
133
|
+
"id": "discrepancy-08"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"cell_type": "markdown",
|
|
137
|
+
"metadata": {},
|
|
138
|
+
"source": [
|
|
139
|
+
"The joint mean contains the conductor parameter first, then four correction channels. Its diagonal gives marginal uncertainty in each channel; the off-diagonal entries still matter when the correction is used with the conductor parameter."
|
|
140
|
+
],
|
|
141
|
+
"id": "discrepancy-09"
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"cell_type": "code",
|
|
145
|
+
"execution_count": null,
|
|
146
|
+
"metadata": {},
|
|
147
|
+
"outputs": [],
|
|
148
|
+
"source": [
|
|
149
|
+
"mean = np.asarray(joint.mean())[1:].reshape(2, 2, f.npoints)\n",
|
|
150
|
+
"std = np.sqrt(np.diag(np.asarray(joint.covariance()))[1:]).reshape(2, 2, f.npoints)\n",
|
|
151
|
+
"truth = np.asarray(exact_values(f))\n",
|
|
152
|
+
"labels = (\"log Zc real\", \"log Zc imaginary\", \"log attenuation\", \"log phase\")\n",
|
|
153
|
+
"fig, axes = plt.subplots(2, 2, figsize=(10, 6), sharex=True)\n",
|
|
154
|
+
"for axis, label, predicted, sigma, actual in zip(axes.flat, labels, mean.reshape(4, -1), std.reshape(4, -1), truth.reshape(4, -1)):\n",
|
|
155
|
+
" axis.plot(f.f_scaled, actual, label=\"injected\")\n",
|
|
156
|
+
" axis.plot(f.f_scaled, predicted, label=\"predicted\")\n",
|
|
157
|
+
" axis.fill_between(f.f_scaled, predicted - 2*sigma, predicted + 2*sigma, alpha=0.2)\n",
|
|
158
|
+
" axis.set_title(label)\n",
|
|
159
|
+
"axes[0, 0].legend()\n",
|
|
160
|
+
"fig.tight_layout()"
|
|
161
|
+
],
|
|
162
|
+
"id": "discrepancy-10"
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"cell_type": "markdown",
|
|
166
|
+
"metadata": {},
|
|
167
|
+
"source": [
|
|
168
|
+
"## When only S-parameters are measured\n",
|
|
169
|
+
"\n",
|
|
170
|
+
"The preceding route uses $Z_c$ and $\\gamma$. If the reference only provides complex S-parameters, represent the internal correction with basis coefficients and fit them through the line response. The [supporting script](line_internal_discrepancy.py) defines the basis from an RBF spectral density; the fit and projection are shown below."
|
|
171
|
+
],
|
|
172
|
+
"id": "discrepancy-11"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"cell_type": "code",
|
|
176
|
+
"execution_count": null,
|
|
177
|
+
"metadata": {},
|
|
178
|
+
"outputs": [],
|
|
179
|
+
"source": [
|
|
180
|
+
"basis = _basis(f)\n",
|
|
181
|
+
"nominal_s = LineCorrected(line(0.30, MODEL_SIGMA), basis)\n",
|
|
182
|
+
"observed_s = line(0.30, TRUTH_SIGMA).s(f)\n",
|
|
183
|
+
"s_likelihood = MarginalLogLikelihood(\n",
|
|
184
|
+
" predictor=lambda model, grid: model.s(grid),\n",
|
|
185
|
+
" observed=observed_s,\n",
|
|
186
|
+
" likelihood=GaussianLikelihood(1e-8),\n",
|
|
187
|
+
")\n",
|
|
188
|
+
"objective = lambda model: -s_likelihood(model, f) - prf.log_prior(model)\n",
|
|
189
|
+
"fitted_s = minimize(objective, nominal_s, solver=ScipyMinimize(method=\"BFGS\"), max_iter=400).model\n",
|
|
190
|
+
"fitted_basis = prf.update(\n",
|
|
191
|
+
" basis, \"coefficients\", value=prf.values(fitted_s)[\"discrepancy.coefficients\"]\n",
|
|
192
|
+
")\n",
|
|
193
|
+
"linearization = s_likelihood.linearize(fitted_s, f)\n",
|
|
194
|
+
"covariance = posterior_covariance([linearization], fitted_s)\n",
|
|
195
|
+
"names_s, joint_s = project_line_basis_joint(\n",
|
|
196
|
+
" fitted_s, fitted_basis, linearization, covariance, f\n",
|
|
197
|
+
")\n",
|
|
198
|
+
"carrier_s = GridLineDiscrepancy.from_prediction(f, joint_s, joint=True)\n",
|
|
199
|
+
"short_s = LineCorrected(line(0.15, MODEL_SIGMA), carrier_s)\n",
|
|
200
|
+
"rms_s = np.sqrt(np.mean(np.abs(np.asarray(short_s.s(f) - truth_short.s(f))[:, 1, 0])**2))\n",
|
|
201
|
+
"mean_s = np.asarray(joint_s.mean()).reshape(2, 2, f.npoints)\n",
|
|
202
|
+
"std_s = np.sqrt(np.diag(np.asarray(joint_s.covariance()))).reshape(2, 2, f.npoints)\n",
|
|
203
|
+
"coverage = np.mean(np.abs(np.asarray(exact_values(f)) - mean_s) <= 2 * std_s)\n",
|
|
204
|
+
"print(\"Transferred RMS S21 error:\", float(rms_s))\n",
|
|
205
|
+
"print(\"Injected values within mean ± 2σ:\", float(coverage))"
|
|
206
|
+
],
|
|
207
|
+
"id": "discrepancy-12"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
"cell_type": "markdown",
|
|
211
|
+
"metadata": {},
|
|
212
|
+
"source": [
|
|
213
|
+
"## Port discrepancy in a later circuit\n",
|
|
214
|
+
"\n",
|
|
215
|
+
"A port correction applies when the fitted component stays the same and its surrounding circuit changes. Here the reference fit estimates a bounded coax length and a reciprocal port discrepancy. The [supporting script](port_discrepancy.py) defines the event transform used for symmetric transmission and contains independent covariance checks. We use that transform below, then carry the joint prediction into a terminated circuit."
|
|
216
|
+
],
|
|
217
|
+
"id": "discrepancy-13"
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"cell_type": "code",
|
|
221
|
+
"execution_count": null,
|
|
222
|
+
"metadata": {},
|
|
223
|
+
"outputs": [],
|
|
224
|
+
"source": [
|
|
225
|
+
"def cable(length):\n",
|
|
226
|
+
" return CoaxialLine(\n",
|
|
227
|
+
" length=length,\n",
|
|
228
|
+
" d_in=prf.Fixed(1.12e-3), d_out=prf.Fixed(3.2e-3),\n",
|
|
229
|
+
" dielectric=ConstantDielectric(ep_r=prf.Fixed(1.384), tand=prf.Fixed(0.001)),\n",
|
|
230
|
+
" conductor=BulkConductor(sigma=prf.Fixed(1 / 1.6e-8)),\n",
|
|
231
|
+
" )\n",
|
|
232
|
+
"\n",
|
|
233
|
+
"reference_f = prf.Frequency(100, 500, 12, \"MHz\")\n",
|
|
234
|
+
"transfer_f = prf.Frequency(120, 480, 6, \"MHz\")\n",
|
|
235
|
+
"true_cable = cable(prf.Fixed(0.1))\n",
|
|
236
|
+
"initial_cable = cable(prf.Bounded(0.05, 0.15, value=0.095))\n",
|
|
237
|
+
"reference_port = MarginalLogLikelihood(\n",
|
|
238
|
+
" predictor=reciprocal_predictor,\n",
|
|
239
|
+
" observed=reciprocal_predictor(true_cable, reference_f),\n",
|
|
240
|
+
" likelihood=GaussianLikelihood(jnp.full((3, 2, reference_f.npoints), 1e-8)),\n",
|
|
241
|
+
" discrepancy=GaussianProcess(Matern52Kernel(80.0) * 1e-6, jitter=1e-12),\n",
|
|
242
|
+
" event_transform=ReciprocalEventTransform,\n",
|
|
243
|
+
")\n",
|
|
244
|
+
"\n",
|
|
245
|
+
"def port_loss(length):\n",
|
|
246
|
+
" return -reference_port(prf.update(initial_cable, {\"length\": length}), reference_f)\n",
|
|
247
|
+
"\n",
|
|
248
|
+
"value_gradient = jax.jit(jax.value_and_grad(port_loss))\n",
|
|
249
|
+
"fit_result = scipy_minimize(\n",
|
|
250
|
+
" lambda x: tuple(np.asarray(v) for v in value_gradient(jnp.asarray(x))),\n",
|
|
251
|
+
" np.array([0.095]), jac=True, method=\"L-BFGS-B\", bounds=[(0.05, 0.15)],\n",
|
|
252
|
+
" options={\"maxiter\": 1000, \"gtol\": 1e-10, \"ftol\": 1e-14},\n",
|
|
253
|
+
")\n",
|
|
254
|
+
"assert fit_result.success\n",
|
|
255
|
+
"fitted_cable = prf.update(initial_cable, {\"length\": jnp.asarray(fit_result.x[0])})\n",
|
|
256
|
+
"port_names, port_joint = reference_port.predict_joint(fitted_cable, reference_f, transfer_f)\n",
|
|
257
|
+
"print(\"Fitted length:\", float(prf.values(fitted_cable)[\"length\"]))"
|
|
258
|
+
],
|
|
259
|
+
"id": "discrepancy-14"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"cell_type": "code",
|
|
263
|
+
"execution_count": null,
|
|
264
|
+
"metadata": {},
|
|
265
|
+
"outputs": [],
|
|
266
|
+
"source": [
|
|
267
|
+
"port_mean = port_joint.mean()[1:].reshape(3, 2, transfer_f.npoints)\n",
|
|
268
|
+
"corrected_cable = PortCorrected(\n",
|
|
269
|
+
" fitted_cable,\n",
|
|
270
|
+
" GridPortDiscrepancy(prf.Unconstrained(port_mean), transfer_f, (\"11\", \"22\", \"s21\")),\n",
|
|
271
|
+
")\n",
|
|
272
|
+
"corrected_cable = prf.prior(\n",
|
|
273
|
+
" corrected_cable, [*port_names, \"discrepancy.values\"], port_joint,\n",
|
|
274
|
+
" truncate=\"unnormalised\",\n",
|
|
275
|
+
")\n",
|
|
276
|
+
"load = Load(gamma=(75.0 - 50.0) / (75.0 + 50.0), z0=50.0)\n",
|
|
277
|
+
"transfer_circuit = corrected_cable.terminated(load)\n",
|
|
278
|
+
"observed_reflection = true_cable.terminated(load).s(transfer_f, z0=50.0)[:, 0, 0]\n",
|
|
279
|
+
"transfer_likelihood = MarginalLogLikelihood(\n",
|
|
280
|
+
" predictor=lambda model, grid: model.s(grid, z0=50.0)[:, 0, 0],\n",
|
|
281
|
+
" observed=observed_reflection,\n",
|
|
282
|
+
" likelihood=GaussianLikelihood(1e-6),\n",
|
|
283
|
+
")\n",
|
|
284
|
+
"transfer_linearization = transfer_likelihood.linearize(\n",
|
|
285
|
+
" transfer_circuit, transfer_f, space=\"declared\"\n",
|
|
286
|
+
")\n",
|
|
287
|
+
"transfer_covariance = posterior_covariance(\n",
|
|
288
|
+
" [transfer_linearization], transfer_circuit, space=\"declared\"\n",
|
|
289
|
+
")\n",
|
|
290
|
+
"print(\"Joint dimension:\", port_joint.mean().shape[0])\n",
|
|
291
|
+
"print(\"Transferred log prior:\", float(prf.log_prior(transfer_circuit, space=\"declared\")))\n",
|
|
292
|
+
"print(\"Posterior variance, first coordinate:\", float(transfer_covariance[0, 0]))"
|
|
293
|
+
],
|
|
294
|
+
"id": "discrepancy-15"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"cell_type": "markdown",
|
|
298
|
+
"metadata": {},
|
|
299
|
+
"source": [
|
|
300
|
+
"The carrier follows what stays physically common between fits: use an internal line correction across lengths, or a port correction when moving the same component into another circuit. The joint distribution retains parameter–discrepancy covariance in each later calculation. The [port script](port_discrepancy.py) compares the final covariance with a NumPy Kalman update and checks the transferred prior's precision."
|
|
301
|
+
],
|
|
302
|
+
"id": "discrepancy-16"
|
|
303
|
+
}
|
|
304
|
+
],
|
|
305
|
+
"metadata": {
|
|
306
|
+
"kernelspec": {
|
|
307
|
+
"display_name": "Python 3",
|
|
308
|
+
"language": "python",
|
|
309
|
+
"name": "python3"
|
|
310
|
+
},
|
|
311
|
+
"language_info": {
|
|
312
|
+
"name": "python"
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
"nbformat": 4,
|
|
316
|
+
"nbformat_minor": 5
|
|
317
|
+
}
|