paramrf 0.37.2__tar.gz → 0.37.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {paramrf-0.37.2 → paramrf-0.37.4}/CONTEXT.md +6 -0
- {paramrf-0.37.2/paramrf.egg-info → paramrf-0.37.4}/PKG-INFO +1 -1
- {paramrf-0.37.2 → paramrf-0.37.4/paramrf.egg-info}/PKG-INFO +1 -1
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/empirical.py +20 -4
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/objectives/evaluators.py +40 -18
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/__init__.py +6 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/covariance_kernels.py +68 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/linearization.py +56 -17
- {paramrf-0.37.2 → paramrf-0.37.4}/pyproject.toml +1 -1
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_covariance_kernels.py +132 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_linearization.py +109 -2
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_lines.py +34 -2
- {paramrf-0.37.2 → paramrf-0.37.4}/.github/workflows/docs.yml +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/.github/workflows/draft-pdf.yml +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/.github/workflows/publish.yml +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/.github/workflows/tests.yml +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/.gitignore +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/AGENTS.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/CHANGELOG.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/CITATION.cff +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/CLAUDE.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/CONTRIBUTING.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/LICENSE +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/NOTICE +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/README.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/assets/logo.png +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/Makefile +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/_static/custom.css +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/_templates/autosummary/class.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/_templates/autosummary/function.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/_templates/autosummary/module.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/adr/0001-line-modelling-architecture.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/adr/0002-parameter-api.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/adr/0003-derived-models.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/adr/0004-profiled-lines.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/adr/0005-priors-by-name.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/adr/0006-reference-impedance.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/adr/0007-edge-values.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/agents/domain.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/agents/issue-tracker.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/agents/triage-labels.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/api/index.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/conf.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/core_concepts/core_primitives.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/core_concepts/index.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/core_concepts/jax_overview.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/core_concepts/optimization_and_inference.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/core_concepts/parameter_names.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/cascading_and_terminating.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/circuit_clc.png +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/circuit_models.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/custom_models.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/derivatives_and_sweeps.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/discrepancy_prediction.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/index.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/model_optimization.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/multiple_models_one_parameter_set.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/parameter_naming_and_model_manipulation.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/examples/shared_modules.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/index.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/license.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/make.bat +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/research/conductor-loss-alternatives.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/research/holloway1994.pdf +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/research/microstrip-loss-conventions.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/research/precision-coax-microstrip-10-500mhz.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/skrf_comparison/index.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/skrf_comparison/overview.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/skrf_comparison/performance.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/1_cable_fitting.ipynb +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/2_chip_inductor_fitting.ipynb +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/3_discrepancy_transfer.ipynb +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/data/CBN-1.5FT-SMSM.s2p +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/data/on-chip-inductor.s2p +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/index.rst +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/line_internal_discrepancy.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/docs/tutorials/port_discrepancy.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/paper/paper.bib +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/paper/paper.md +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/paper/rlc.png +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/paramrf.egg-info/SOURCES.txt +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/paramrf.egg-info/dependency_links.txt +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/paramrf.egg-info/requires.txt +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/paramrf.egg-info/top_level.txt +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/_solver_view.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/fitting/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/fitting/minimize.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/fitting/result.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/fitting/routers.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/fitting/sample.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/fitting/targets.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/frequency.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/infer/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/infer/base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/infer/result.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/infer/sample.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/infer/solvers/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/infer/solvers/blackjax.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/infer/solvers/polychord.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/materials/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/materials/conductor.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/materials/dielectric.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/materials/properties.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/materials/roughness.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/materials/substrate.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/materials/surface_impedance.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/math/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/math/aggregations.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/math/bessel.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/math/conversions.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/math/losses.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/math/misc.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/bridge.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/callable.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/delegated.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/derived.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/static.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/adapters/wrapped.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/ideal.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/coaxial.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/ideal.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/microstrip.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/nodal.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/nonuniform.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/planar.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/profiles.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lines/stripline.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/lumped.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/components/sections.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/cascade.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/circuit/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/circuit/base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/circuit/circuit.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/circuit/solvers/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/circuit/solvers/nodal.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/circuit/solvers/scattering.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/interconnected/terminated.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/nodal.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/topological.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/composite/transformed.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/line_discrepancy.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/port_discrepancy.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/surrogates/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/surrogates/expansion.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/models/surrogates/rational.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/modules/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/modules/base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/modules/wrapped.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/network_collection.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/objectives/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/objectives/losses.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/objectives/problems.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/objectives/terms.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/minimize.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/result.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/solvers/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/solvers/jaxopt.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/solvers/optimistix.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/optimize/solvers/scipy.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/parameters/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/parameters/constraints.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/rf/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/rf/cascade.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/rf/conversions.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/rf/mna.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/serialization.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/bijectors.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/discrepancy_models.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/distributions.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/likelihoods.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/stats/noise_models.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/types.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/array.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/debug.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/network.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/random.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/rf.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/transforms.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/tree.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/utils/type.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/viz/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/pmrf/viz/plots.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/scripts/benchmark_gp_marginal_likelihood.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/scripts/install-test-deps.sh +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/setup.cfg +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/__init__.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/_jit.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/conftest.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/data/10m_cable.s2p +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_autodiff.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_box_space_solving.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_conversions.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_dc_limits.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_discrepancy_models.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_evaluators.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_fitting_minimize.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_fitting_routers.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_fitting_sample.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_fitting_targets.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_frequency.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_grouped_exports.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_infer_base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_infer_sample.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_line_internal_discrepancy_example.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_map_priors.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_materials/test_conductor.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_materials/test_dielectric.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_materials/test_serialization.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_materials/test_substrate.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_materials/test_surface_impedance.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_math/test_bessel.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_model.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_adapters.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_cascade.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_circuit_native_z0.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_circuit_nodal.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_circuit_port_order.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_circuit_scattering.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_circuit_shorted_ports.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_circuit_skrf_port_names.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_circuit_z0.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_current_distribution.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_derived.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_edge_values.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_interconnected.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_line_discrepancy.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_lines_skrf_matrix.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_lumped.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_nodal.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_port_discrepancy.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_profiles.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_rational.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_repeated_cascade.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_sections.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_transformed.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_models/test_transformers.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_module.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_naming.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_optimize_base.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_optimize_minimize.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_parameters.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_parameters_by_name.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_parameters_joint_prior.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_parameters_prior.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_parameters_validity.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_port_discrepancy_example.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_raw_space_solving.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_serialization.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_structural_updates.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_terms.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_transforms.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_utils/test_compress.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_utils/test_debug.py +0 -0
- {paramrf-0.37.2 → paramrf-0.37.4}/tests/test_utils/test_tree.py +0 -0
|
@@ -223,6 +223,12 @@ the prior precision $\Sigma_0^{-1}$ it gives the linearised posterior covariance
|
|
|
223
223
|
$\Sigma_\text{post} = (F + \Sigma_0^{-1})^{-1}$, over parameter values in one space
|
|
224
224
|
(declared by default). Fisher matrices of independent fits add.
|
|
225
225
|
|
|
226
|
+
The **posterior covariance factor** $W$ is a square root of this covariance:
|
|
227
|
+
$\Sigma_\text{post}=WW^\top$. It follows the same free-parameter order and space as
|
|
228
|
+
the linearisation. For a stack $A$ of whitened Jacobians and a square root of the
|
|
229
|
+
prior precision, $A=USV^\top$ gives $W=VS^{-1}$. Joint prediction uses $W$ to
|
|
230
|
+
carry parameter uncertainty without first forming the Fisher matrix.
|
|
231
|
+
|
|
226
232
|
### Joint prediction
|
|
227
233
|
|
|
228
234
|
The Gaussian over the free parameters and the discrepancy at new frequencies
|
|
@@ -20,10 +20,14 @@ class PhysicalLine(AbstractImmittanceLine):
|
|
|
20
20
|
|
|
21
21
|
The per-unit-length parameters are
|
|
22
22
|
$$R = 2 z_n \alpha_c$$
|
|
23
|
-
$$L = \frac{z_n \sqrt{\varepsilon_r}}{c}$$
|
|
23
|
+
$$L = \frac{z_n \sqrt{\varepsilon_r}}{c} + \frac{R}{\omega}$$
|
|
24
24
|
$$G = \frac{2 \alpha_d}{z_n}$$
|
|
25
25
|
$$C = \frac{\sqrt{\varepsilon_r}}{z_n c}$$
|
|
26
26
|
|
|
27
|
+
The skin-effect term adds conductor reactance to the series impedance:
|
|
28
|
+
$$Z = R + j\omega z_n\sqrt{\varepsilon_r}/c + jR.$$
|
|
29
|
+
This contribution vanishes at DC.
|
|
30
|
+
|
|
27
31
|
Example
|
|
28
32
|
--------
|
|
29
33
|
.. code-block:: python
|
|
@@ -93,7 +97,11 @@ class PhysicalLine(AbstractImmittanceLine):
|
|
|
93
97
|
G = G_val * ones
|
|
94
98
|
C = C_val * ones
|
|
95
99
|
|
|
96
|
-
return ImmittanceResult
|
|
100
|
+
return ImmittanceResult(
|
|
101
|
+
Z=R + 1j * (freq.w * L + R),
|
|
102
|
+
Y=G + 1j * freq.w * C,
|
|
103
|
+
omega=freq.w,
|
|
104
|
+
)
|
|
97
105
|
|
|
98
106
|
|
|
99
107
|
class DatasheetLine(AbstractImmittanceLine):
|
|
@@ -108,10 +116,14 @@ class DatasheetLine(AbstractImmittanceLine):
|
|
|
108
116
|
|
|
109
117
|
and the per-unit-length parameters are
|
|
110
118
|
$$R = 2 z_n \alpha_c$$
|
|
111
|
-
$$L = \frac{z_n}{v_f c}$$
|
|
119
|
+
$$L = \frac{z_n}{v_f c} + \frac{R}{\omega}$$
|
|
112
120
|
$$G = \frac{2 \alpha_d}{z_n}$$
|
|
113
121
|
$$C = \frac{1}{z_n v_f c}$$
|
|
114
122
|
|
|
123
|
+
The skin-effect term adds conductor reactance to the series impedance:
|
|
124
|
+
$$Z = R + j\omega z_n/(v_f c) + jR.$$
|
|
125
|
+
This contribution vanishes at DC.
|
|
126
|
+
|
|
115
127
|
Example
|
|
116
128
|
--------
|
|
117
129
|
.. code-block:: python
|
|
@@ -186,5 +198,9 @@ class DatasheetLine(AbstractImmittanceLine):
|
|
|
186
198
|
L = (zn / (vf * c)) * jnp.ones_like(w)
|
|
187
199
|
C = (1.0 / (zn * vf * c)) * jnp.ones_like(w)
|
|
188
200
|
|
|
189
|
-
return ImmittanceResult
|
|
201
|
+
return ImmittanceResult(
|
|
202
|
+
Z=R + 1j * (w * L + R),
|
|
203
|
+
Y=G + 1j * w * C,
|
|
204
|
+
omega=w,
|
|
205
|
+
)
|
|
190
206
|
|
|
@@ -22,7 +22,7 @@ from pmrf.objectives.losses import HingeLoss, RMSELoss
|
|
|
22
22
|
from pmrf.stats.likelihoods import GaussianLikelihood
|
|
23
23
|
from pmrf.stats.covariance_kernels import cross_gram, gram
|
|
24
24
|
from pmrf.stats.discrepancy_models import GaussianProcess, _add_noise
|
|
25
|
-
from pmrf.stats.linearization import Linearization, _linearize,
|
|
25
|
+
from pmrf.stats.linearization import Linearization, _linearize, posterior_covariance_factor
|
|
26
26
|
from pmrf.modules.base import Module
|
|
27
27
|
from pmrf.parameters import Space, values as _values, update
|
|
28
28
|
from pmrf.utils import derivative, field, unwrap, unwrap_self
|
|
@@ -593,6 +593,8 @@ class MarginalLogLikelihood(AbstractEvaluator):
|
|
|
593
593
|
frequency: Frequency,
|
|
594
594
|
new_frequency: Frequency,
|
|
595
595
|
covariance: jnp.ndarray | None = None,
|
|
596
|
+
*,
|
|
597
|
+
covariance_factor: jnp.ndarray | None = None,
|
|
596
598
|
**kwargs,
|
|
597
599
|
) -> tuple[tuple[str, ...], dist.AbstractDistribution]:
|
|
598
600
|
r"""Predict the free parameters and discrepancy together as a joint prior.
|
|
@@ -600,13 +602,14 @@ class MarginalLogLikelihood(AbstractEvaluator):
|
|
|
600
602
|
**Mathematical Formulation**
|
|
601
603
|
|
|
602
604
|
At the fitted model, let $A = K_{*h}\Sigma_D^{-1}$ and let
|
|
603
|
-
|
|
605
|
+
$W W^\top = \Sigma_\text{post}$ factor the linearised parameter
|
|
606
|
+
covariance. Then
|
|
604
607
|
|
|
605
608
|
$$\hat\delta = A r, \qquad
|
|
606
609
|
\Sigma_\delta = K_{**} - A K_{*h}^\top
|
|
607
|
-
+
|
|
610
|
+
+ (AJW)(AJW)^\top$$
|
|
608
611
|
|
|
609
|
-
$$\Sigma_{\delta\theta} = -
|
|
612
|
+
$$\Sigma_{\delta\theta} = -(AJW)W^\top$$
|
|
610
613
|
|
|
611
614
|
The parameter term couples event blocks, giving one dense Gaussian over
|
|
612
615
|
$[\theta;\delta]$. Kernel hyperparameters are held fixed, as in
|
|
@@ -632,6 +635,12 @@ class MarginalLogLikelihood(AbstractEvaluator):
|
|
|
632
635
|
order. If omitted, computed from this fit and the model's prior with
|
|
633
636
|
:func:`pmrf.stats.linearization.posterior_covariance`. Supply a covariance
|
|
634
637
|
from combined linearisations when several fits inform the parameters.
|
|
638
|
+
covariance_factor : jnp.ndarray or None, default=None
|
|
639
|
+
A factor $W$ of the parameter covariance in declared space and
|
|
640
|
+
linearisation name order, with $W W^\top$ equal to the covariance.
|
|
641
|
+
Obtain one from :func:`pmrf.stats.linearization.posterior_covariance_factor`
|
|
642
|
+
when several fits inform the parameters. Cannot be combined with
|
|
643
|
+
covariance.
|
|
635
644
|
**kwargs
|
|
636
645
|
Passed to the predictor.
|
|
637
646
|
|
|
@@ -653,18 +662,29 @@ class MarginalLogLikelihood(AbstractEvaluator):
|
|
|
653
662
|
If the discrepancy is not a Gaussian process or the likelihood is not
|
|
654
663
|
Gaussian.
|
|
655
664
|
ValueError
|
|
656
|
-
If orthogonal discrepancy is enabled
|
|
657
|
-
|
|
665
|
+
If orthogonal discrepancy is enabled, an input has the wrong shape,
|
|
666
|
+
or both covariance arguments are supplied.
|
|
658
667
|
"""
|
|
659
668
|
if not isinstance(self.discrepancy, GaussianProcess):
|
|
660
669
|
raise TypeError("Joint prediction requires a `GaussianProcess` discrepancy.")
|
|
661
670
|
linearization = self.linearize(model, frequency, **kwargs)
|
|
662
671
|
p = linearization.J.shape[-1]
|
|
663
|
-
if covariance is None:
|
|
664
|
-
covariance
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
672
|
+
if covariance is not None and covariance_factor is not None:
|
|
673
|
+
raise ValueError("Supply either covariance or covariance_factor, not both.")
|
|
674
|
+
if covariance_factor is None:
|
|
675
|
+
if covariance is None:
|
|
676
|
+
covariance_factor = posterior_covariance_factor([linearization], model)
|
|
677
|
+
else:
|
|
678
|
+
covariance = jnp.asarray(covariance)
|
|
679
|
+
if covariance.shape != (p, p):
|
|
680
|
+
raise ValueError(f"Parameter covariance must have shape {(p, p)}.")
|
|
681
|
+
covariance_factor = jnp.linalg.cholesky(covariance)
|
|
682
|
+
covariance_factor = jnp.asarray(covariance_factor)
|
|
683
|
+
if covariance_factor.shape != (p, p):
|
|
684
|
+
raise ValueError(f"Parameter covariance factor must have shape {(p, p)}.")
|
|
685
|
+
# QR gives a triangular scale without first forming an ill-conditioned covariance.
|
|
686
|
+
_, R = jnp.linalg.qr(covariance_factor.T)
|
|
687
|
+
parameter_scale = R.T
|
|
668
688
|
|
|
669
689
|
batch_shape = linearization.residual.shape[:-1]
|
|
670
690
|
n = linearization.residual.shape[-1]
|
|
@@ -682,17 +702,19 @@ class MarginalLogLikelihood(AbstractEvaluator):
|
|
|
682
702
|
conditional = conditional - jnp.swapaxes(V, -1, -2) @ V
|
|
683
703
|
conditional = conditional.reshape((-1, n_new, n_new))
|
|
684
704
|
blocks = conditional.shape[0]
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
705
|
+
conditional_scale = jnp.linalg.cholesky(conditional)
|
|
706
|
+
conditional_scale = jnp.einsum(
|
|
707
|
+
'bij,bc->bicj', conditional_scale, jnp.eye(blocks)
|
|
708
|
+
).reshape((blocks * n_new, blocks * n_new))
|
|
688
709
|
AJ = (A @ linearization.J).reshape((-1, p))
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
710
|
+
joint_scale = jnp.block([
|
|
711
|
+
[parameter_scale, jnp.zeros((p, blocks * n_new))],
|
|
712
|
+
[-AJ @ parameter_scale, conditional_scale],
|
|
713
|
+
])
|
|
692
714
|
named_values = _values(model, free_only=True, space='declared')
|
|
693
715
|
theta = jnp.concatenate([jnp.ravel(named_values[name]) for name in linearization.names])
|
|
694
716
|
mean = jnp.concatenate([theta, mean_delta])
|
|
695
|
-
return linearization.names, dist.
|
|
717
|
+
return linearization.names, dist.MultivariateNormalTri(mean, joint_scale)
|
|
696
718
|
|
|
697
719
|
@unwrap_self
|
|
698
720
|
def linearize(
|
|
@@ -28,9 +28,11 @@ if TYPE_CHECKING:
|
|
|
28
28
|
AutoCrossKernel as AutoCrossKernel,
|
|
29
29
|
ConstantKernel as ConstantKernel,
|
|
30
30
|
CosineKernel as CosineKernel,
|
|
31
|
+
LogFrequencyKernel as LogFrequencyKernel,
|
|
31
32
|
Matern32Kernel as Matern32Kernel,
|
|
32
33
|
Matern52Kernel as Matern52Kernel,
|
|
33
34
|
PeriodicKernel as PeriodicKernel,
|
|
35
|
+
PowerEnvelopeKernel as PowerEnvelopeKernel,
|
|
34
36
|
ProductKernel as ProductKernel,
|
|
35
37
|
RBFKernel as RBFKernel,
|
|
36
38
|
SharedIndependentKernel as SharedIndependentKernel,
|
|
@@ -65,6 +67,7 @@ if TYPE_CHECKING:
|
|
|
65
67
|
from .linearization import (
|
|
66
68
|
Linearization as Linearization,
|
|
67
69
|
posterior_covariance as posterior_covariance,
|
|
70
|
+
posterior_covariance_factor as posterior_covariance_factor,
|
|
68
71
|
)
|
|
69
72
|
from .noise_models import (
|
|
70
73
|
AbstractNoiseModel as AbstractNoiseModel,
|
|
@@ -94,11 +97,13 @@ _EXPORTS = {
|
|
|
94
97
|
'Joint': 'distributions',
|
|
95
98
|
'Leafwise': 'bijectors',
|
|
96
99
|
'Linearization': 'linearization',
|
|
100
|
+
'LogFrequencyKernel': 'covariance_kernels',
|
|
97
101
|
'LogNormal': 'distributions',
|
|
98
102
|
'Matern32Kernel': 'covariance_kernels',
|
|
99
103
|
'Matern52Kernel': 'covariance_kernels',
|
|
100
104
|
'Normal': 'distributions',
|
|
101
105
|
'PeriodicKernel': 'covariance_kernels',
|
|
106
|
+
'PowerEnvelopeKernel': 'covariance_kernels',
|
|
102
107
|
'Permute': 'bijectors',
|
|
103
108
|
'ProductKernel': 'covariance_kernels',
|
|
104
109
|
'R2ToComplex': 'bijectors',
|
|
@@ -122,6 +127,7 @@ _EXPORTS = {
|
|
|
122
127
|
'cross_gram': 'covariance_kernels',
|
|
123
128
|
'gram': 'covariance_kernels',
|
|
124
129
|
'posterior_covariance': 'linearization',
|
|
130
|
+
'posterior_covariance_factor': 'linearization',
|
|
125
131
|
'truncate': 'distributions',
|
|
126
132
|
}
|
|
127
133
|
_MODULES = {'bijectors', 'discrepancy_models', 'linearization', 'likelihoods', 'noise_models', 'distributions', 'covariance_kernels'}
|
|
@@ -304,6 +304,74 @@ class CosineKernel(AbstractCovarianceKernel):
|
|
|
304
304
|
return jnp.cos(2 * jnp.pi * jnp.sum(x1 - x2) / self.period)
|
|
305
305
|
|
|
306
306
|
|
|
307
|
+
class LogFrequencyKernel(AbstractCovarianceKernel):
|
|
308
|
+
r"""
|
|
309
|
+
Evaluate a kernel on log frequency.
|
|
310
|
+
|
|
311
|
+
$$k(x_1, x_2) = k_\text{inner}(\ln x_1, \ln x_2)$$
|
|
312
|
+
|
|
313
|
+
A stationary inner kernel depends on the ratio $x_1/x_2$. For a
|
|
314
|
+
:class:`CosineKernel` with period $p$, one cycle spans a factor of $e^p$
|
|
315
|
+
in frequency. The warp needs no frequency unit: rescaling all inputs adds
|
|
316
|
+
the same constant to their logarithms, leaving stationary inner kernels
|
|
317
|
+
unchanged. Inputs must be strictly positive.
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
kernel : AbstractCovarianceKernel
|
|
322
|
+
The kernel evaluated on log frequency. Its batching is preserved.
|
|
323
|
+
"""
|
|
324
|
+
#: The inner kernel.
|
|
325
|
+
kernel: AbstractCovarianceKernel
|
|
326
|
+
|
|
327
|
+
def __call__(self, x1, x2, key=None):
|
|
328
|
+
x1, x2 = jnp.asarray(x1), jnp.asarray(x2)
|
|
329
|
+
x1 = eqx.error_if(x1, jnp.any(x1 <= 0),
|
|
330
|
+
"LogFrequencyKernel inputs must be strictly positive")
|
|
331
|
+
x2 = eqx.error_if(x2, jnp.any(x2 <= 0),
|
|
332
|
+
"LogFrequencyKernel inputs must be strictly positive")
|
|
333
|
+
return self.kernel(jnp.log(x1), jnp.log(x2), key=key)
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class PowerEnvelopeKernel(AbstractCovarianceKernel):
|
|
337
|
+
r"""
|
|
338
|
+
Power-law amplitude envelope for a one-dimensional frequency input.
|
|
339
|
+
|
|
340
|
+
$$k(x_1, x_2) = s(x_1)s(x_2), \qquad
|
|
341
|
+
s(x) = \left(\frac{x}{x_\text{ref}}\right)^{-\alpha}$$
|
|
342
|
+
|
|
343
|
+
This rank-one kernel is singular on its own. Multiply it by another
|
|
344
|
+
kernel to make that kernel's prior standard deviation scale as $s(x)$.
|
|
345
|
+
Inputs must be strictly positive and contain exactly one feature.
|
|
346
|
+
``reference`` must use the same units as the GP input (``frequency.f_scaled``
|
|
347
|
+
in the fitting path). A unit mismatch silently rescales the prior variance
|
|
348
|
+
by $(\text{unit ratio})^{-2\alpha}$.
|
|
349
|
+
|
|
350
|
+
Parameters
|
|
351
|
+
----------
|
|
352
|
+
reference : ArrayLike
|
|
353
|
+
Input at which the amplitude is one, in the same units as the GP input.
|
|
354
|
+
exponent : ArrayLike
|
|
355
|
+
Decay exponent. A shape ``(D,)`` gives ``D`` batched kernels.
|
|
356
|
+
"""
|
|
357
|
+
#: The reference input.
|
|
358
|
+
reference: ArrayLike
|
|
359
|
+
|
|
360
|
+
#: The decay exponent.
|
|
361
|
+
exponent: ArrayLike
|
|
362
|
+
|
|
363
|
+
def _amplitude(self, x):
|
|
364
|
+
x = jnp.asarray(x)
|
|
365
|
+
if x.size != 1:
|
|
366
|
+
raise ValueError("PowerEnvelopeKernel requires one-dimensional input points")
|
|
367
|
+
x = eqx.error_if(x, jnp.any(x <= 0),
|
|
368
|
+
"PowerEnvelopeKernel inputs must be strictly positive")
|
|
369
|
+
return (x.reshape(()) / self.reference) ** (-jnp.asarray(self.exponent))
|
|
370
|
+
|
|
371
|
+
def __call__(self, x1, x2, key=None):
|
|
372
|
+
return self._amplitude(x1) * self._amplitude(x2)
|
|
373
|
+
|
|
374
|
+
|
|
307
375
|
class WhiteNoiseKernel(AbstractCovarianceKernel):
|
|
308
376
|
"""
|
|
309
377
|
Kernel representing independent Gaussian noise.
|
|
@@ -122,34 +122,45 @@ def _linearize(
|
|
|
122
122
|
residual = residual_fn(model)
|
|
123
123
|
J = -jax.jacfwd(lambda x: residual_fn(rebuild(x)))(x0)
|
|
124
124
|
|
|
125
|
+
W = _whitened_jacobian(J, chol)
|
|
126
|
+
F = jnp.einsum('...nps,...nqs->pq', W, W)
|
|
127
|
+
return Linearization(J, residual, chol, F, names, shapes, space)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _whitened_jacobian(J: Array, chol: Array) -> Array:
|
|
131
|
+
"""Return $L^{-1}J$ grouped by shared covariance factors."""
|
|
132
|
+
|
|
125
133
|
# Event blocks sharing a factor are solved together, with the parameter axis
|
|
126
134
|
# carried as one more batch axis the factor is shared along.
|
|
127
135
|
n, p = J.shape[-2], J.shape[-1]
|
|
128
136
|
matrix_shape, R, _ = _group_by_matrix(chol.shape[:-2], jnp.moveaxis(J, -1, 0))
|
|
129
137
|
L = jnp.broadcast_to(chol, chol.shape[:-2] + (n, n)).reshape(matrix_shape + (n, n))
|
|
130
138
|
W = jsp.linalg.solve_triangular(L, R, lower=True)
|
|
131
|
-
|
|
132
|
-
F = jnp.einsum('...nps,...nqs->pq', W, W)
|
|
133
|
-
return Linearization(J, residual, chol, F, names, shapes, space)
|
|
139
|
+
return W.reshape(W.shape[:-1] + (p, -1))
|
|
134
140
|
|
|
135
141
|
|
|
136
|
-
def
|
|
142
|
+
def posterior_covariance_factor(
|
|
137
143
|
linearizations: Sequence[Linearization],
|
|
138
144
|
model: PyTree,
|
|
139
145
|
space: Space = 'declared',
|
|
140
146
|
) -> Array:
|
|
141
|
-
r"""
|
|
147
|
+
r"""A square root of the linearised posterior covariance of free parameters.
|
|
142
148
|
|
|
143
149
|
**Mathematical Formulation**
|
|
144
150
|
|
|
145
|
-
|
|
146
|
-
precision
|
|
151
|
+
Stack the whitened Jacobians of independent fits with a square root of the
|
|
152
|
+
prior precision, then use the singular values of that stack:
|
|
147
153
|
|
|
148
|
-
|
|
149
|
-
\Sigma_0^{-1}
|
|
154
|
+
$$A = \begin{bmatrix}L_1^{-1}J_1 \\ \cdots \\ L_k^{-1}J_k \\
|
|
155
|
+
\Sigma_0^{-1/2}\end{bmatrix}, \qquad
|
|
156
|
+
A = U S V^\top, \qquad W = V S^{-1}, \qquad
|
|
157
|
+
\Sigma_\text{post} = W W^\top,$$
|
|
150
158
|
|
|
151
|
-
|
|
152
|
-
|
|
159
|
+
where $\Sigma_0^{-1} = -\nabla^2\log p(\theta)$ in `space`. Its symmetric
|
|
160
|
+
eigendecomposition supplies the square root; negative eigenvalues are clipped to
|
|
161
|
+
zero. This local approximation is only meaningful where the prior is log-concave.
|
|
162
|
+
A parameter without a prior, or with a uniform prior inside its bounds, adds zero
|
|
163
|
+
prior precision.
|
|
153
164
|
|
|
154
165
|
Parameters
|
|
155
166
|
----------
|
|
@@ -164,8 +175,8 @@ def posterior_covariance(
|
|
|
164
175
|
Returns
|
|
165
176
|
-------
|
|
166
177
|
jax.Array
|
|
167
|
-
|
|
168
|
-
|
|
178
|
+
A factor ``W`` with shape ``(p, p)`` such that ``W @ W.T`` is the
|
|
179
|
+
covariance over free parameters in :attr:`~Linearization.names` order.
|
|
169
180
|
|
|
170
181
|
Raises
|
|
171
182
|
------
|
|
@@ -185,14 +196,42 @@ def posterior_covariance(
|
|
|
185
196
|
"A linearisation is over different free parameters from the model: "
|
|
186
197
|
f"{linearization.names} rather than {names}."
|
|
187
198
|
)
|
|
188
|
-
fisher = sum(linearization.F for linearization in linearizations)
|
|
189
199
|
prior_precision = -jax.hessian(lambda x: log_prior(rebuild(x), space=space))(x0)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
200
|
+
eigenvalues, eigenvectors = jnp.linalg.eigh(0.5 * (prior_precision + prior_precision.T))
|
|
201
|
+
prior_root = jnp.sqrt(jnp.maximum(eigenvalues, 0.0))[:, None] * eigenvectors.T
|
|
202
|
+
p = x0.size
|
|
203
|
+
factors = [
|
|
204
|
+
jnp.moveaxis(_whitened_jacobian(lin.J, lin.chol), -2, -1).reshape(-1, p)
|
|
205
|
+
for lin in linearizations
|
|
206
|
+
]
|
|
207
|
+
A = jnp.concatenate([*factors, prior_root], axis=0)
|
|
208
|
+
_, singular_values, vt = jnp.linalg.svd(A, full_matrices=False)
|
|
209
|
+
threshold = singular_values[0] * max(A.shape) * jnp.finfo(A.dtype).eps
|
|
210
|
+
singular_values = eqx.error_if(
|
|
211
|
+
singular_values, singular_values[-1] <= threshold,
|
|
212
|
+
"A parameter direction has neither data nor prior information.",
|
|
213
|
+
)
|
|
214
|
+
V = vt.T
|
|
215
|
+
return V / singular_values
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def posterior_covariance(
|
|
219
|
+
linearizations: Sequence[Linearization],
|
|
220
|
+
model: PyTree,
|
|
221
|
+
space: Space = 'declared',
|
|
222
|
+
) -> Array:
|
|
223
|
+
r"""The linearised posterior covariance of a model's free parameters.
|
|
224
|
+
|
|
225
|
+
See :func:`posterior_covariance_factor` for the factorisation and its
|
|
226
|
+
assumptions. The result has shape ``(p, p)`` in the linearisations'
|
|
227
|
+
:attr:`~Linearization.names` order.
|
|
228
|
+
"""
|
|
229
|
+
W = posterior_covariance_factor(linearizations, model, space)
|
|
230
|
+
return W @ W.T
|
|
193
231
|
|
|
194
232
|
|
|
195
233
|
__all__ = [
|
|
196
234
|
'Linearization',
|
|
197
235
|
'posterior_covariance',
|
|
236
|
+
'posterior_covariance_factor',
|
|
198
237
|
]
|
|
@@ -15,6 +15,8 @@ from pmrf.stats.covariance_kernels import (
|
|
|
15
15
|
Matern52Kernel,
|
|
16
16
|
ConstantKernel,
|
|
17
17
|
CosineKernel,
|
|
18
|
+
LogFrequencyKernel,
|
|
19
|
+
PowerEnvelopeKernel,
|
|
18
20
|
AutoCrossKernel,
|
|
19
21
|
SharedIndependentKernel,
|
|
20
22
|
)
|
|
@@ -38,6 +40,12 @@ def x_periods():
|
|
|
38
40
|
return jnp.linspace(0.0, 50.0, 200)
|
|
39
41
|
|
|
40
42
|
|
|
43
|
+
@pytest.fixture
|
|
44
|
+
def x_log():
|
|
45
|
+
assert jax.config.jax_enable_x64
|
|
46
|
+
return jnp.geomspace(10e6, 500e6, 300)
|
|
47
|
+
|
|
48
|
+
|
|
41
49
|
def _damped_cosine():
|
|
42
50
|
return CosineKernel(period=7.3) * Matern52Kernel(lengthscale=20.0) * 2.0
|
|
43
51
|
|
|
@@ -210,6 +218,130 @@ def test_cosine_kernel_under_jit_and_vmap(x_periods):
|
|
|
210
218
|
assert jnp.allclose(vmapped, CosineKernel(period=periods).gram(x_periods))
|
|
211
219
|
|
|
212
220
|
|
|
221
|
+
def test_log_frequency_kernel_has_ratio_period():
|
|
222
|
+
"""One cycle in log frequency spans a fixed frequency ratio."""
|
|
223
|
+
period = 1.7
|
|
224
|
+
start = jnp.array([10e6])
|
|
225
|
+
kernel = LogFrequencyKernel(CosineKernel(period))
|
|
226
|
+
assert jnp.allclose(kernel(start, start * jnp.exp(period)), 1.0, atol=1e-12)
|
|
227
|
+
assert jnp.allclose(kernel(start, start * jnp.exp(period / 2)), -1.0, atol=1e-12)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_log_frequency_kernel_unit_invariance_and_psd(x_log):
|
|
231
|
+
"""Changing frequency units leaves the damped covariance unchanged and PSD."""
|
|
232
|
+
kernel = LogFrequencyKernel(CosineKernel(1.7) * Matern52Kernel(1.5))
|
|
233
|
+
covariance = kernel.gram(x_log)
|
|
234
|
+
assert jnp.max(jnp.abs(covariance - kernel.gram(x_log / 1e6))) < 1e-12
|
|
235
|
+
eigenvalues = jnp.linalg.eigvalsh(covariance)
|
|
236
|
+
assert eigenvalues.min() >= -1e-8 * eigenvalues.max()
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def test_log_frequency_kernel_preserves_inner_batching(x_log):
|
|
240
|
+
"""A vector of inner periods gives one Gram matrix per period."""
|
|
241
|
+
periods = jnp.array([1.0, 2.0])
|
|
242
|
+
covariance = LogFrequencyKernel(CosineKernel(periods)).gram(x_log)
|
|
243
|
+
assert covariance.shape == (2, 300, 300)
|
|
244
|
+
for index, period in enumerate(periods):
|
|
245
|
+
expected = LogFrequencyKernel(CosineKernel(period)).gram(x_log)
|
|
246
|
+
assert jnp.allclose(covariance[index], expected)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@pytest.mark.parametrize("invalid", [0.0, -1.0])
|
|
250
|
+
@pytest.mark.parametrize("side", [0, 1])
|
|
251
|
+
def test_log_frequency_kernel_rejects_nonpositive_inputs(invalid, side):
|
|
252
|
+
"""Both arguments require positive frequencies, eagerly and under jit."""
|
|
253
|
+
kernel = LogFrequencyKernel(CosineKernel(1.7))
|
|
254
|
+
inputs = [jnp.array([10e6]), jnp.array([10e6])]
|
|
255
|
+
inputs[side] = jnp.array([invalid])
|
|
256
|
+
with pytest.raises(Exception, match="strictly positive"):
|
|
257
|
+
kernel(*inputs)
|
|
258
|
+
with pytest.raises(Exception, match="strictly positive"):
|
|
259
|
+
jax.jit(kernel)(*inputs)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_log_frequency_kernel_is_exported():
|
|
263
|
+
from pmrf.stats import LogFrequencyKernel as PublicLogFrequencyKernel
|
|
264
|
+
assert PublicLogFrequencyKernel is LogFrequencyKernel
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def test_power_envelope_kernel_value():
|
|
268
|
+
"""The covariance is the outer product of two power-law amplitudes."""
|
|
269
|
+
kernel = PowerEnvelopeKernel(10e6, 0.5)
|
|
270
|
+
assert jnp.allclose(kernel(40e6, 90e6), 1 / 6, rtol=1e-14)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _damped_log_periodic_envelope(exponent=0.65, period=1.7, lengthscale=1.5):
|
|
274
|
+
return (PowerEnvelopeKernel(10e6, exponent)
|
|
275
|
+
* LogFrequencyKernel(CosineKernel(period) * Matern52Kernel(lengthscale))
|
|
276
|
+
* 2.5e-3 ** 2)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def test_power_envelope_kernel_diagonal_and_psd(x_log):
|
|
280
|
+
"""The envelope sets the prior variance and preserves positive semidefiniteness."""
|
|
281
|
+
kernel = _damped_log_periodic_envelope()
|
|
282
|
+
for index in [0, 150, 299]:
|
|
283
|
+
expected = 2.5e-3 ** 2 * (x_log[index] / 10e6) ** -1.3
|
|
284
|
+
assert jnp.allclose(kernel(x_log[index], x_log[index]), expected, rtol=1e-10)
|
|
285
|
+
eigenvalues = jnp.linalg.eigvalsh(kernel.gram(x_log))
|
|
286
|
+
assert eigenvalues.min() >= -1e-8 * eigenvalues.max()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def test_power_envelope_kernel_batches_exponents(x_log):
|
|
290
|
+
"""A vector of exponents gives the corresponding scalar Gram matrices."""
|
|
291
|
+
exponents = jnp.array([0.5, 1.0])
|
|
292
|
+
covariance = PowerEnvelopeKernel(10e6, exponents).gram(x_log)
|
|
293
|
+
assert covariance.shape == (2, 300, 300)
|
|
294
|
+
for index, exponent in enumerate(exponents):
|
|
295
|
+
expected = PowerEnvelopeKernel(10e6, exponent).gram(x_log)
|
|
296
|
+
assert jnp.allclose(covariance[index], expected)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def test_power_envelope_kernel_composition_gradients_are_finite(x_log):
|
|
300
|
+
"""Envelope, period, lengthscale, and coincident input gradients stay finite."""
|
|
301
|
+
def covariance_sum(parameters):
|
|
302
|
+
return _damped_log_periodic_envelope(*parameters).gram(x_log).sum()
|
|
303
|
+
|
|
304
|
+
gradients = jax.grad(covariance_sum)(jnp.array([0.65, 1.7, 1.5]))
|
|
305
|
+
assert jnp.all(jnp.isfinite(gradients))
|
|
306
|
+
|
|
307
|
+
kernel = _damped_log_periodic_envelope()
|
|
308
|
+
input_gradient = jax.grad(lambda x1: kernel(x1, jnp.array([10e6])))(jnp.array([10e6]))
|
|
309
|
+
assert jnp.all(jnp.isfinite(input_gradient))
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_power_envelope_kernel_composition_under_jit(x_log):
|
|
313
|
+
"""Compilation preserves the composed covariance."""
|
|
314
|
+
kernel = _damped_log_periodic_envelope()
|
|
315
|
+
assert jnp.allclose(jax.jit(lambda x: kernel.gram(x))(x_log), kernel.gram(x_log))
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
@pytest.mark.parametrize("invalid", [0.0, -1.0])
|
|
319
|
+
@pytest.mark.parametrize("side", [0, 1])
|
|
320
|
+
def test_power_envelope_kernel_rejects_nonpositive_inputs(invalid, side):
|
|
321
|
+
"""Both arguments require positive frequencies, eagerly and under jit."""
|
|
322
|
+
kernel = PowerEnvelopeKernel(10e6, 0.65)
|
|
323
|
+
inputs = [jnp.array([10e6]), jnp.array([10e6])]
|
|
324
|
+
inputs[side] = jnp.array([invalid])
|
|
325
|
+
with pytest.raises(Exception, match="strictly positive"):
|
|
326
|
+
kernel(*inputs)
|
|
327
|
+
with pytest.raises(Exception, match="strictly positive"):
|
|
328
|
+
jax.jit(kernel)(*inputs)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def test_power_envelope_kernel_rejects_multidimensional_inputs():
|
|
332
|
+
"""A point with multiple features is rejected while tracing."""
|
|
333
|
+
kernel = PowerEnvelopeKernel(10e6, 0.65)
|
|
334
|
+
with pytest.raises(ValueError, match="one-dimensional"):
|
|
335
|
+
kernel(jnp.array([10e6, 20e6]), jnp.array([10e6]))
|
|
336
|
+
with pytest.raises(ValueError, match="one-dimensional"):
|
|
337
|
+
jax.jit(kernel)(jnp.array([10e6, 20e6]), jnp.array([10e6]))
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def test_power_envelope_kernel_is_exported():
|
|
341
|
+
from pmrf.stats import PowerEnvelopeKernel as PublicPowerEnvelopeKernel
|
|
342
|
+
assert PublicPowerEnvelopeKernel is PowerEnvelopeKernel
|
|
343
|
+
|
|
344
|
+
|
|
213
345
|
_AUTO = RBFKernel(lengthscale=0.3)
|
|
214
346
|
_CROSS = Matern32Kernel(lengthscale=2.0) * 0.5
|
|
215
347
|
|