paramrf 0.34.4__tar.gz → 0.35.2__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.
Files changed (235) hide show
  1. paramrf-0.35.2/.github/workflows/tests.yml +34 -0
  2. paramrf-0.35.2/CHANGELOG.md +126 -0
  3. paramrf-0.35.2/CITATION.cff +14 -0
  4. {paramrf-0.34.4 → paramrf-0.35.2}/CONTEXT.md +60 -5
  5. {paramrf-0.34.4 → paramrf-0.35.2}/CONTRIBUTING.md +6 -13
  6. {paramrf-0.34.4/paramrf.egg-info → paramrf-0.35.2}/PKG-INFO +3 -3
  7. {paramrf-0.34.4 → paramrf-0.35.2}/README.rst +1 -1
  8. paramrf-0.35.2/docs/adr/0002-parameter-api.md +255 -0
  9. {paramrf-0.34.4 → paramrf-0.35.2}/docs/api/index.rst +13 -0
  10. {paramrf-0.34.4 → paramrf-0.35.2}/docs/core_concepts/core_primitives.rst +1 -1
  11. {paramrf-0.34.4 → paramrf-0.35.2}/docs/core_concepts/index.rst +1 -0
  12. {paramrf-0.34.4 → paramrf-0.35.2}/docs/core_concepts/jax_overview.rst +2 -2
  13. paramrf-0.35.2/docs/core_concepts/parameter_names.rst +48 -0
  14. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/custom_models.rst +2 -2
  15. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/derivatives_and_sweeps.rst +28 -16
  16. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/model_optimization.rst +1 -1
  17. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/multiple_models_one_parameter_set.rst +2 -2
  18. paramrf-0.35.2/docs/examples/parameter_naming_and_model_manipulation.rst +151 -0
  19. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/shared_substrates.rst +2 -2
  20. {paramrf-0.34.4 → paramrf-0.35.2}/docs/tutorials/1_cable_fitting.ipynb +6 -13
  21. {paramrf-0.34.4 → paramrf-0.35.2}/docs/tutorials/2_chip_inductor_fitting.ipynb +1 -1
  22. {paramrf-0.34.4 → paramrf-0.35.2}/paper/paper.bib +1 -1
  23. {paramrf-0.34.4 → paramrf-0.35.2/paramrf.egg-info}/PKG-INFO +3 -3
  24. {paramrf-0.34.4 → paramrf-0.35.2}/paramrf.egg-info/SOURCES.txt +15 -2
  25. {paramrf-0.34.4 → paramrf-0.35.2}/paramrf.egg-info/requires.txt +1 -1
  26. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/__init__.py +11 -3
  27. paramrf-0.35.2/pmrf/_solver_view.py +125 -0
  28. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/evaluators.py +5 -17
  29. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/infer/base.py +71 -95
  30. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/materials/substrate.py +7 -1
  31. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/adapters/delegated.py +4 -0
  32. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/adapters/static.py +86 -58
  33. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/base.py +20 -72
  34. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/nonuniform.py +1 -1
  35. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/circuit/circuit.py +56 -12
  36. paramrf-0.35.2/pmrf/modules/base.py +134 -0
  37. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/modules/wrapped.py +1 -18
  38. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/base.py +35 -69
  39. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/minimize.py +3 -4
  40. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/solvers/jaxopt.py +3 -3
  41. paramrf-0.35.2/pmrf/parameters.py +1618 -0
  42. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/problems.py +12 -6
  43. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/serialization.py +77 -29
  44. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/__init__.py +1 -0
  45. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/transforms.py +53 -14
  46. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/tree.py +159 -56
  47. {paramrf-0.34.4 → paramrf-0.35.2}/pyproject.toml +2 -2
  48. paramrf-0.35.2/scripts/install-test-deps.sh +22 -0
  49. paramrf-0.35.2/tests/_jit.py +27 -0
  50. paramrf-0.35.2/tests/conftest.py +19 -0
  51. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_dc_limits.py +2 -1
  52. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_evaluators.py +1 -1
  53. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_fitting_routers.py +4 -2
  54. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_fitting_targets.py +11 -7
  55. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_map_priors.py +12 -6
  56. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_materials/test_substrate.py +3 -2
  57. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_model.py +28 -5
  58. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_adapters.py +106 -2
  59. paramrf-0.35.2/tests/test_models/test_circuit_port_order.py +168 -0
  60. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_module.py +14 -9
  61. paramrf-0.35.2/tests/test_naming.py +421 -0
  62. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_optimize_base.py +16 -20
  63. paramrf-0.35.2/tests/test_parameters.py +203 -0
  64. paramrf-0.35.2/tests/test_parameters_by_name.py +364 -0
  65. paramrf-0.35.2/tests/test_raw_space_solving.py +318 -0
  66. paramrf-0.35.2/tests/test_serialization.py +73 -0
  67. paramrf-0.35.2/tests/test_structural_updates.py +235 -0
  68. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_transforms.py +96 -0
  69. paramrf-0.35.2/tests/test_utils/test_tree.py +43 -0
  70. paramrf-0.34.4/.github/workflows/tests.yml +0 -44
  71. paramrf-0.34.4/CHANGELOG.md +0 -11
  72. paramrf-0.34.4/docs/examples/parameter_naming_and_model_manipulation.rst +0 -82
  73. paramrf-0.34.4/pmrf/modules/base.py +0 -231
  74. paramrf-0.34.4/pmrf/parameters.py +0 -945
  75. paramrf-0.34.4/pmrf/utils/optix.py +0 -313
  76. paramrf-0.34.4/tests/test_naming.py +0 -146
  77. {paramrf-0.34.4 → paramrf-0.35.2}/.github/workflows/docs.yml +0 -0
  78. {paramrf-0.34.4 → paramrf-0.35.2}/.github/workflows/draft-pdf.yml +0 -0
  79. {paramrf-0.34.4 → paramrf-0.35.2}/.github/workflows/publish.yml +0 -0
  80. {paramrf-0.34.4 → paramrf-0.35.2}/.gitignore +0 -0
  81. {paramrf-0.34.4 → paramrf-0.35.2}/AGENTS.md +0 -0
  82. {paramrf-0.34.4 → paramrf-0.35.2}/CLAUDE.md +0 -0
  83. {paramrf-0.34.4 → paramrf-0.35.2}/LICENSE +0 -0
  84. {paramrf-0.34.4 → paramrf-0.35.2}/NOTICE +0 -0
  85. {paramrf-0.34.4 → paramrf-0.35.2}/assets/logo.png +0 -0
  86. {paramrf-0.34.4 → paramrf-0.35.2}/docs/Makefile +0 -0
  87. {paramrf-0.34.4 → paramrf-0.35.2}/docs/_static/custom.css +0 -0
  88. {paramrf-0.34.4 → paramrf-0.35.2}/docs/_templates/autosummary/class.rst +0 -0
  89. {paramrf-0.34.4 → paramrf-0.35.2}/docs/_templates/autosummary/function.rst +0 -0
  90. {paramrf-0.34.4 → paramrf-0.35.2}/docs/_templates/autosummary/module.rst +0 -0
  91. {paramrf-0.34.4 → paramrf-0.35.2}/docs/adr/0001-line-modelling-architecture.md +0 -0
  92. {paramrf-0.34.4 → paramrf-0.35.2}/docs/agents/domain.md +0 -0
  93. {paramrf-0.34.4 → paramrf-0.35.2}/docs/agents/issue-tracker.md +0 -0
  94. {paramrf-0.34.4 → paramrf-0.35.2}/docs/agents/triage-labels.md +0 -0
  95. {paramrf-0.34.4 → paramrf-0.35.2}/docs/conf.py +0 -0
  96. {paramrf-0.34.4 → paramrf-0.35.2}/docs/core_concepts/optimization_and_inference.rst +0 -0
  97. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/cascading_and_terminating.rst +0 -0
  98. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/circuit_clc.png +0 -0
  99. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/circuit_models.rst +0 -0
  100. {paramrf-0.34.4 → paramrf-0.35.2}/docs/examples/index.rst +1 -1
  101. {paramrf-0.34.4 → paramrf-0.35.2}/docs/index.rst +0 -0
  102. {paramrf-0.34.4 → paramrf-0.35.2}/docs/license.rst +0 -0
  103. {paramrf-0.34.4 → paramrf-0.35.2}/docs/make.bat +0 -0
  104. {paramrf-0.34.4 → paramrf-0.35.2}/docs/research/conductor-loss-alternatives.md +0 -0
  105. {paramrf-0.34.4 → paramrf-0.35.2}/docs/research/holloway1994.pdf +0 -0
  106. {paramrf-0.34.4 → paramrf-0.35.2}/docs/research/microstrip-loss-conventions.md +0 -0
  107. {paramrf-0.34.4 → paramrf-0.35.2}/docs/research/precision-coax-microstrip-10-500mhz.md +0 -0
  108. {paramrf-0.34.4 → paramrf-0.35.2}/docs/skrf_comparison/index.rst +0 -0
  109. {paramrf-0.34.4 → paramrf-0.35.2}/docs/skrf_comparison/overview.rst +0 -0
  110. {paramrf-0.34.4 → paramrf-0.35.2}/docs/skrf_comparison/performance.rst +0 -0
  111. {paramrf-0.34.4 → paramrf-0.35.2}/docs/tutorials/data/CBN-1.5FT-SMSM.s2p +0 -0
  112. {paramrf-0.34.4 → paramrf-0.35.2}/docs/tutorials/data/on-chip-inductor.s2p +0 -0
  113. {paramrf-0.34.4 → paramrf-0.35.2}/docs/tutorials/index.rst +0 -0
  114. {paramrf-0.34.4 → paramrf-0.35.2}/paper/paper.md +0 -0
  115. {paramrf-0.34.4 → paramrf-0.35.2}/paper/rlc.png +0 -0
  116. {paramrf-0.34.4 → paramrf-0.35.2}/paramrf.egg-info/dependency_links.txt +0 -0
  117. {paramrf-0.34.4 → paramrf-0.35.2}/paramrf.egg-info/top_level.txt +0 -0
  118. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/bijectors.py +0 -0
  119. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/constraints.py +0 -0
  120. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/covariance_kernels.py +0 -0
  121. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/discrepancy_models.py +0 -0
  122. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/distributions.py +0 -0
  123. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/fitting/__init__.py +0 -0
  124. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/fitting/minimize.py +0 -0
  125. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/fitting/result.py +0 -0
  126. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/fitting/routers.py +0 -0
  127. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/fitting/sample.py +0 -0
  128. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/fitting/targets.py +0 -0
  129. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/frequency.py +0 -0
  130. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/infer/__init__.py +0 -0
  131. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/infer/result.py +0 -0
  132. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/infer/sample.py +0 -0
  133. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/infer/solvers/__init__.py +0 -0
  134. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/infer/solvers/blackjax.py +0 -0
  135. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/infer/solvers/polychord.py +0 -0
  136. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/likelihoods.py +0 -0
  137. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/losses.py +0 -0
  138. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/materials/__init__.py +0 -0
  139. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/materials/conductor.py +0 -0
  140. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/materials/dielectric.py +0 -0
  141. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/materials/properties.py +0 -0
  142. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/materials/roughness.py +0 -0
  143. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/materials/surface_impedance.py +0 -0
  144. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/math/__init__.py +0 -0
  145. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/math/aggregations.py +0 -0
  146. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/math/bessel.py +0 -0
  147. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/math/conversions.py +0 -0
  148. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/math/losses.py +0 -0
  149. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/math/misc.py +0 -0
  150. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/__init__.py +0 -0
  151. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/adapters/__init__.py +0 -0
  152. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/adapters/base.py +0 -0
  153. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/adapters/bridge.py +0 -0
  154. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/adapters/callable.py +0 -0
  155. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/adapters/wrapped.py +0 -0
  156. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/__init__.py +0 -0
  157. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/ideal.py +0 -0
  158. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/__init__.py +0 -0
  159. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/base.py +0 -0
  160. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/coaxial.py +0 -0
  161. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/empirical.py +0 -0
  162. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/ideal.py +0 -0
  163. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/microstrip.py +0 -0
  164. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/nodal.py +0 -0
  165. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/planar.py +0 -0
  166. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lines/stripline.py +0 -0
  167. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/lumped.py +0 -0
  168. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/components/sections.py +0 -0
  169. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/__init__.py +0 -0
  170. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/__init__.py +0 -0
  171. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/cascade.py +0 -0
  172. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/circuit/__init__.py +0 -0
  173. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/circuit/base.py +0 -0
  174. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/circuit/solvers/__init__.py +0 -0
  175. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/circuit/solvers/nodal.py +0 -0
  176. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/circuit/solvers/scattering.py +0 -0
  177. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/interconnected/terminated.py +0 -0
  178. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/nodal.py +0 -0
  179. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/topological.py +0 -0
  180. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/composite/transformed.py +0 -0
  181. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/surrogates/__init__.py +0 -0
  182. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/surrogates/expansion.py +0 -0
  183. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/models/surrogates/rational.py +0 -0
  184. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/modules/__init__.py +0 -0
  185. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/network_collection.py +0 -0
  186. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/noise_models.py +0 -0
  187. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/__init__.py +0 -0
  188. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/result.py +0 -0
  189. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/solvers/__init__.py +0 -0
  190. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/solvers/optimistix.py +0 -0
  191. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/optimize/solvers/scipy.py +0 -0
  192. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/rf/__init__.py +0 -0
  193. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/rf/conversions.py +0 -0
  194. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/rf/mna.py +0 -0
  195. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/terms.py +0 -0
  196. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/types.py +0 -0
  197. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/array.py +0 -0
  198. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/debug.py +0 -0
  199. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/network.py +0 -0
  200. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/random.py +0 -0
  201. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/rf.py +0 -0
  202. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/utils/type.py +0 -0
  203. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/viz/__init__.py +0 -0
  204. {paramrf-0.34.4 → paramrf-0.35.2}/pmrf/viz/plots.py +0 -0
  205. {paramrf-0.34.4 → paramrf-0.35.2}/setup.cfg +0 -0
  206. {paramrf-0.34.4 → paramrf-0.35.2}/tests/__init__.py +0 -0
  207. {paramrf-0.34.4 → paramrf-0.35.2}/tests/_dependency_checks.py +0 -0
  208. {paramrf-0.34.4 → paramrf-0.35.2}/tests/data/10m_cable.s2p +0 -0
  209. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_autodiff.py +0 -0
  210. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_conversions.py +0 -0
  211. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_covariance_kernels.py +0 -0
  212. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_fitting_minimize.py +0 -0
  213. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_fitting_sample.py +0 -0
  214. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_frequency.py +0 -0
  215. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_infer_base.py +0 -0
  216. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_infer_sample.py +0 -0
  217. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_materials/test_conductor.py +0 -0
  218. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_materials/test_dielectric.py +0 -0
  219. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_materials/test_serialization.py +0 -0
  220. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_materials/test_surface_impedance.py +0 -0
  221. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_math/test_bessel.py +0 -0
  222. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_circuit_nodal.py +0 -0
  223. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_circuit_scattering.py +0 -0
  224. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_current_distribution.py +0 -0
  225. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_interconnected.py +0 -0
  226. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_lines.py +0 -0
  227. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_lines_skrf_matrix.py +0 -0
  228. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_lumped.py +0 -0
  229. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_nodal.py +0 -0
  230. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_sections.py +0 -0
  231. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_transformed.py +0 -0
  232. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_models/test_transformers.py +0 -0
  233. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_optimize_minimize.py +0 -0
  234. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_terms.py +0 -0
  235. {paramrf-0.34.4 → paramrf-0.35.2}/tests/test_utils/test_compress.py +0 -0
@@ -0,0 +1,34 @@
1
+ name: Run Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - name: Checkout repo
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Install Compilers and MPI
18
+ run: |
19
+ sudo apt-get update
20
+ sudo apt-get install -y gfortran libopenmpi-dev openmpi-bin
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: '3.11'
26
+
27
+ - name: Install dependencies and smoke test package import
28
+ run: scripts/install-test-deps.sh
29
+
30
+ - name: Run Pytest
31
+ run: |
32
+ python -m pytest -rs
33
+ env:
34
+ PMRF_TESTS_NO_SKIPS: "1"
@@ -0,0 +1,126 @@
1
+ # Changelog
2
+
3
+ ## 0.35.0
4
+
5
+ Parameter names, values and serialisation (#138), and the parameter API of
6
+ ADR-0002 (#146). Downstream users pinning an exact version: every breaking
7
+ change below ships in **0.35.0**.
8
+
9
+ Name-keyed operations are now free functions rather than methods, values are
10
+ read and written in the units they were declared in, and changing a value no
11
+ longer recompiles a model's RF methods.
12
+
13
+ ### Breaking changes (0.35.0)
14
+
15
+ - **Parameter API (#146):** name-keyed operations are free functions, so they
16
+ also work on a collection such as `(model, noise_model)`. Removed:
17
+ `Module.named_params`, `values`, `with_values`, `with_free`, `with_fixed`,
18
+ `map` and `at`; `Module.tied` and `Model.tied`; `Param.as_fixed`, `as_free`
19
+ and `at`; and the public `Param.wrap`. Use `prf.params`, `prf.param_values`,
20
+ `prf.update` and `prf.tie`. There are no aliases or deprecation shims.
21
+ - **Parameter API (#146):** `Module` has no public methods. `Model` keeps its RF
22
+ methods, which are now fixed by a checked-in allowlist.
23
+ - **Values (#146):** `Param.value`, `prf.param_values` and `prf.update` use the
24
+ **declared** value, the number as written in the units the parameter's scale
25
+ declares, so `prf.update(m, {'C': 3.0})` on a pF parameter sets 3 pF, not 3 F.
26
+ This reverses the physical-value behaviour `prf.replace(p, value=...)` had
27
+ earlier in this release.
28
+ - **Values (#146):** a scale on a value overrides the field's scale instead of
29
+ multiplying with it, so a `Param` with `scale=1e-9` passed into a pF field is
30
+ nF. `Param.scale` is `None` when the parameter declares none.
31
+ - **Values (#146):** `Param.unscaled_value` is removed; the three spaces are
32
+ `Param.value` (declared), `physical_value` and `raw_value`.
33
+ - **Values (#146):** the `Param` field holding the Parax variable is renamed
34
+ from `raw_value` to `variable`, and the bijector properties are now
35
+ `raw_to_declared_bijector` and `declared_to_physical_bijector`.
36
+ - **Values (#146):** a model's `repr` shows declared values, not scaled ones.
37
+ - **Inference (#146):** the minimiser and samplers run on raw-space values, over
38
+ a name-keyed dict rather than a flat vector. `pmrf.utils.optix`'s lens is
39
+ internal; nothing public returns a `Lens`.
40
+ - **Inference (#146):** `prf.log_prior` is a density in the space asked for, so
41
+ a raw-space prior carries the constraint's Jacobian and scale terms. Saved
42
+ results that stored physical values need re-reading in declared space.
43
+ - **Inference (#151):** `run_minimizer`'s `use_bounds` argument is removed. A
44
+ bounded solver is given infinite raw bounds, because raw space is the whole
45
+ real line and each parameter's bijector keeps its constraints.
46
+ - **Inference (#151):** a parameter starting exactly on one of its bounds
47
+ raises for the minimiser and for joint and split samplers: its raw value is
48
+ infinite and cannot move. Hypercube samplers work in declared space and
49
+ accept it.
50
+ - **Values (#146):** `prf.derivative` differentiates each parameter with
51
+ respect to its declared value, so the derivative for a pF parameter is per pF;
52
+ `space='physical'` gives the old per-SI-unit result. Each result keeps its
53
+ argument's structure, wrappers included, instead of the unwrapped one. A fixed
54
+ parameter gets its sensitivity rather than zero, and a tie's source includes
55
+ the path through the tie.
56
+ - **Names (#133):** parameter names no longer include tied targets; a tie's
57
+ target is derived, not stored.
58
+ - **Names (#133):** wrapper path parts (`Tied`, `Probabilistic`, `Wrapped`) are
59
+ dropped from parameter names.
60
+ - **Names (#133):** a name collision raises.
61
+ - **Names (#133):** `prf.unfreeze` also unfreezes frozen parameters inside the
62
+ value.
63
+ - **Names (#133):** string dict keys that are valid identifiers give dotted
64
+ names (`components.cable.length`) instead of the bracket form.
65
+ - **Names (#146):** a named model held in a dict drops its key, and top-level
66
+ dict keys do not add a namespace layer.
67
+ - **Values (#134):** the `Param` constructor rejects a raw value together with
68
+ `distribution` or `constraint`.
69
+ - **Save format (#136):** prf files carry a header
70
+ (`{"format": "prf", "schema_version": 1, "paramrf_version": ..., "tree": ...}`).
71
+ `prf.load` rejects files without it, or with a `schema_version`
72
+ other than 1, so files saved by earlier versions
73
+ cannot be loaded.
74
+ - **Save format (#136):** `prf.save` writes every field, defaults included.
75
+
76
+ ### Added
77
+
78
+ - `prf.params` and `prf.param_values` read a model's parameters by name, and
79
+ `prf.log_prior` scores them, each over a selector and one of the three value
80
+ spaces (#146).
81
+ - `prf.update`, one verb for changing a model: values by name, a value or fixed
82
+ state over a selector, a new sub-model, or a function of the old part (#146).
83
+ - `prf.tie` as a free function (#146).
84
+ - One name resolver behind every name-based operation; names resolve through
85
+ frozen parameters and after evaluating a `Touchstone`-backed model (#133).
86
+ - `prf.replace` works on `Param`, keeping prior, constraint, fixed state, scale,
87
+ name and metadata (#134).
88
+ - `pmrf.serialization.SCHEMA_VERSION` and `FORMAT` constants, and a clearer
89
+ `ImportError` when a saved class cannot be imported (#136).
90
+
91
+ ### Fixed
92
+
93
+ - Changing a parameter's value no longer recompiles a model's RF methods. The
94
+ value forms of `prf.update` keep the tree structure and every leaf's dtype,
95
+ shape and weak type (#130, #146).
96
+ - Compiling `model.s(freq)` for a Touchstone-backed model no longer takes
97
+ minutes: interpolation is vectorised over port pairs rather than looped. At
98
+ 50 ports with linear interpolation, the first call drops from 70 s to 0.4 s;
99
+ 100 ports no longer times out. S-parameter data stays static, which keeps
100
+ per-call evaluation fast (#130, #152).
101
+ - `pmrf.__version__` is now set; it was looked up under the wrong
102
+ distribution name (#136).
103
+ - The `Model.build` deprecation warning is emitted once per class, and now
104
+ points to plain functions for composites with no parameters of their own
105
+ (#137).
106
+ - `pmrf.__all__` no longer lists `Topology` and `Initvar`, which do not exist
107
+ (#146).
108
+
109
+ ### Documentation
110
+
111
+ - New "Working with parameter names" page in core concepts: how names are
112
+ formed, the three value spaces, selectors, the forms of `prf.update`, ties,
113
+ and what recompiles. The "Parameter naming and model manipulation" example
114
+ walks through the same operations on the new API (#146).
115
+ - `Module` and `Substrate` explain that passing the same instance to two
116
+ sibling fields gives independent parameters, and how to share one (#137).
117
+
118
+ ## Unreleased
119
+
120
+ - Complete Tesche coaxial conductor physics across the low-frequency regime,
121
+ and pass evaluated material properties to pure coaxial formulations.
122
+ - Correct microstrip results by enabling Kirschning--Jansen modal dispersion by
123
+ default. Set `dispersion=None` on `MicrostripLine` to retain the quasi-static
124
+ pipeline explicitly.
125
+ - Add the Hammerstad--Jensen microstrip formulation and selectable complex
126
+ (ADS-like) and real (QUCS-like) permittivity conventions.
@@ -0,0 +1,14 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it as below."
3
+ type: software
4
+ title: "ParamRF: A Modern Framework for Parametric Radio Frequency Modeling"
5
+ authors:
6
+ - family-names: "Allen"
7
+ given-names: "Gary"
8
+ orcid: "https://orcid.org/0009-0005-6572-598X"
9
+ - family-names: "De Villiers"
10
+ given-names: "Dirk"
11
+ orcid: "https://orcid.org/0000-0003-1273-5365"
12
+ repository-code: "https://github.com/gvcallen/paramrf"
13
+ license: "Apache-2.0"
14
+ version: "0.34.4"
@@ -1,8 +1,62 @@
1
1
  # ParamRF domain context
2
2
 
3
- Vocabulary for the transmission-line modelling layer. Terms here are the ones
4
- the code uses; prefer them over synonyms. Each entry points at the class that
5
- owns the maths rather than repeating it.
3
+ Vocabulary for ParamRF's domain layers. Terms here are the ones the code uses;
4
+ prefer them over synonyms. Each entry points at the class that owns the maths
5
+ rather than repeating it.
6
+
7
+ ## Parameters
8
+
9
+ Vocabulary from ADR-0002.
10
+
11
+ ### Param
12
+
13
+ A named, possibly bounded or probabilistic value in a model: `pmrf.Param`,
14
+ wrapping a Parax **variable** (the field `variable`). A **parameter name** is
15
+ the dotted path #133's resolver gives it (`feed_coax.dielectric.ep_r`), the key
16
+ for saved results, ties and selectors.
17
+
18
+ ### Space
19
+
20
+ Where a parameter's number lives. Every surface defaults to **declared**.
21
+
22
+ - **raw**: the latent, unbounded array optimisers and samplers move through.
23
+ - **declared**: the number as written, in the units the parameter's scale
24
+ declares (2.0 for 2 pF). Construction, bounds, priors and `Param.value` are
25
+ in declared space.
26
+ - **physical**: the scaled, SI value (2e-12).
27
+
28
+ *Avoid:* "unconstrained space" (reads as a parameter without bounds; that is
29
+ `prf.Unconstrained`), "unscaled" and "constrained" for declared space, "unit
30
+ space" (the unit hypercube).
31
+
32
+ ### Scale
33
+
34
+ The units a value is written in: physical = declared × scale. An explicit scale
35
+ on a value overrides a field's default; scales never multiply.
36
+
37
+ ### Fixed and frozen
38
+
39
+ - **Fixed**: a parameter state. The parameter keeps its name and prior and is
40
+ excluded from optimisation. Toggled with `prf.update(..., fixed=)`.
41
+ - **Frozen**: an opaque subtree (`prf.freeze`), for constant data. Name-based
42
+ operations never act on it.
43
+
44
+ ### Update
45
+
46
+ `prf.update` returns a copy of a model with the parts a **selector** picks
47
+ replaced. A selector is a parameter name, a glob over names, a sequence of
48
+ names, or a callable. A **validated update** (a name → value mapping,
49
+ `value=`, `fixed=`) goes through each parameter's constructor; a **structural
50
+ update** (a new node, `fn=`) bypasses validation. `prf.replace` is the plain
51
+ dataclass field replace, not an update.
52
+
53
+ *Avoid:* "set values", "with values"; "update" for an optimiser step.
54
+
55
+ ### Parameter values
56
+
57
+ `prf.param_values`: a name-keyed dict of arrays in one space, the form values
58
+ take when crossing ParamRF's boundary. Not flattened; a 1-D vector exists only
59
+ inside adapters that need one.
6
60
 
7
61
  ## Line modelling
8
62
 
@@ -73,6 +127,7 @@ paper with no ParamRF objects in sight.
73
127
 
74
128
  ## Decisions
75
129
 
76
- `docs/adr/` records the decisions behind this layering. Read
130
+ `docs/adr/` records the decisions behind these layers. Read
77
131
  `docs/adr/0001-line-modelling-architecture.md` before changing a strategy
78
- interface or a default.
132
+ interface or a default, and `docs/adr/0002-parameter-api.md` before adding a
133
+ method, a public function or a value space.
@@ -8,25 +8,18 @@ ParamRF builds on top of JAX and Equinox's functional style. If you are coming f
8
8
 
9
9
  1. **Fork and Clone:** Fork the repository on GitHub and clone it locally.
10
10
  2. **Virtual Environment:** Set up a virtual environment using Python 3.11+.
11
- 3. **Install Dependencies:** Install the package in editable mode along with the test and documentation dependencies:
11
+ 3. **Install Dependencies:** Install the package in editable mode with the same test environment CI uses, including the pinned `distreqx` fork, PolyChord and blackjax. PolyChord needs Fortran and MPI compilers (like `mpifort` and `mpicxx`):
12
12
 
13
- pip install -e .[tests,docs]
14
-
15
- 4. **External Inference Dependencies (Optional):** If you plan on working on the Bayesian inference module (`pmrf.infer`), you may need to install our custom `distreqx` fork and PolyChord. Note that PolyChord requires C++ and Fortran compilers (like `mpicxx` and `mpifort`) to be installed on your system:
16
-
17
- ```bash
18
- pip install git+https://github.com/gvcallen/distreqx.git
19
- ```
20
-
21
- ```bash
22
- pip install git+https://github.com/PolyChord/PolyChordLite.git
23
- ```
13
+ scripts/install-test-deps.sh
14
+ pip install -e .[docs]
24
15
 
25
16
  ## Building docs/running tests
26
17
 
27
18
  We use `pytest` for all unit testing. Simply run:
28
19
 
29
- pytest
20
+ pytest -rs
21
+
22
+ Tests for optional backends skip when a backend is missing. CI sets `PMRF_TESTS_NO_SKIPS=1`, which turns any skip into a failure, so run with it locally too before opening a PR.
30
23
 
31
24
  When writing new tests, especially for fitting and inference routines, try to use synthetic, in-memory S-parameter data (identity fits) rather than committing `.s2p` files to the repository. This keeps the test suite fast and the repository size small.
32
25
 
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paramrf
3
- Version: 0.34.4
3
+ Version: 0.35.2
4
4
  Summary: Parametric radio frequency modeling
5
5
  Author-email: Gary Allen <gvcallen@gmail.com>
6
6
  Project-URL: homepage, https://github.com/gvcallen/paramrf
7
7
  Description-Content-Type: text/x-rst
8
8
  License-File: LICENSE
9
9
  License-File: NOTICE
10
- Requires-Dist: parax
10
+ Requires-Dist: parax>=0.10.8
11
11
  Requires-Dist: threadpoolctl
12
12
  Requires-Dist: eqxpress
13
13
  Requires-Dist: jax
@@ -113,7 +113,7 @@ The code below demonstrate how to define and optimize an RLC model to satisfy a
113
113
  model.plot_s_db(plot_freq, m=0, n=0, label='initial')
114
114
  result.model.plot_s_db(plot_freq, m=0, n=0, label='optimized')
115
115
 
116
- print(result.model.named_params())
116
+ print(prf.param_values(result.model))
117
117
 
118
118
  Next steps
119
119
  ----------
@@ -69,7 +69,7 @@ The code below demonstrate how to define and optimize an RLC model to satisfy a
69
69
  model.plot_s_db(plot_freq, m=0, n=0, label='initial')
70
70
  result.model.plot_s_db(plot_freq, m=0, n=0, label='optimized')
71
71
 
72
- print(result.model.named_params())
72
+ print(prf.param_values(result.model))
73
73
 
74
74
  Next steps
75
75
  ----------
@@ -0,0 +1,255 @@
1
+ # ADR-0002: Parameter API: free functions, one update verb, three value spaces
2
+
3
+ Status: accepted (2026-09)
4
+
5
+ ## Context
6
+
7
+ 0.35.0 (#138) made parameter names the key for saved results, ties, free sets
8
+ and external tooling, and added name-keyed operations as `Module` methods:
9
+ `values`, `with_values`, `with_free` and `with_fixed`, next to the existing
10
+ `named_params`, `at`, `map` and `tied`. An older ParamRF had `with_*` methods
11
+ and deprecated them because they inflated the API and cut against a move to a
12
+ functional style. There was no rule for which operations get a method, so the
13
+ surface grew case by case.
14
+
15
+ Four problems forced a decision:
16
+
17
+ - **Joint trees.** Fitting and inference increasingly work on collections such
18
+ as `(model, noise_model)`. A method on `Module` cannot reach them, so every
19
+ method-only operation needs a function as well, and the API grows two
20
+ spellings.
21
+ - **Scale.** Construction takes the declared value (`prf.param(scale=1e-12)`
22
+ then `RC(1.0, 2.0)` stores 2 pF), but `Param.value`, `values()`,
23
+ `with_values()` and `prf.replace(p, value=...)` use the physical, scaled
24
+ value. `with_values({'C': 3.0})` on that model silently sets 3 F. A `Param`
25
+ with its own scale passed into a scaled field multiplies the two scales
26
+ (`1e-9 × 1e-12`).
27
+ - **Recompilation (#130).** RF methods are wrapped in `eqx.filter_jit`, so any
28
+ change to a model's static structure recompiles. Rebuilding a parameter with
29
+ `model.at(name).set(prf.Unconstrained(v))`, as the docs showed, changes its
30
+ scale and constraint and recompiles; for a large circuit that is a minute per
31
+ change. `_replace_raw_value` also flips `weak_type` on the value leaf, so
32
+ `with_values` and `prf.replace` recompile once even though the treedef is
33
+ unchanged.
34
+ - **Flattening (#135, #142).** A proposed `prf.flatten` returned a
35
+ `FlatParams` object over a 1-D vector. Flattening that prominent is unusual
36
+ in the JAX ecosystem.
37
+
38
+ Precedent from other JAX libraries: Equinox's `Module` has no public methods;
39
+ Flax NNX deprecated `Module.iter_modules` and `iter_children` in favour of free
40
+ functions; Penzai keeps a single `.select()` gateway; NumPyro's
41
+ `initialize_model` exposes a potential function over a **name-keyed dict** of
42
+ unconstrained values and ravels only inside samplers. scikit-rf, which most
43
+ ParamRF users know, puts RF operations on `Network` as methods.
44
+
45
+ ## Decisions
46
+
47
+ ### 1. A method belongs to the class that gives it meaning
48
+
49
+ If an operation would make equal sense on a plain collection of models and
50
+ parameters, such as a `(model, noise_model)` tuple, it is a free function and
51
+ never a method. There are no exceptions.
52
+
53
+ - **`Model`** keeps RF methods: `s`, `a`, `z`, `y`, `mna`, `nports`,
54
+ `port_tuples`, `primary_matrix`, `build`, `expand`, `cascaded`, `flipped`,
55
+ `renumbered`, `terminated`, `**`, `@`, `to_skrf` and `export_touchstone`.
56
+ `tied` leaves; tying is parameterisation, not circuit algebra.
57
+ - **`Module`** has no public methods (decision 2).
58
+ - **`Param`** keeps read-only properties that describe it (`value`, `bounds`,
59
+ `distribution`, ...). `as_fixed`, `as_free` and `at` are removed; `wrap`
60
+ becomes private.
61
+
62
+ A test asserts that `Module` has no public methods and that `Model`'s public
63
+ methods equal a checked-in allowlist, so adding one is a reviewed decision. The
64
+ methods `__init_subclass__` generates (`s_db`, `s_mn_mag`, ...) and the plotting
65
+ names `__getattr__` serves are RF and allowed; the test skips them by pattern
66
+ and says so. Replacing them is out of scope.
67
+
68
+ This matches what scikit-rf users expect, where RF operations are methods, and
69
+ what JAX users expect, where tree operations are functions.
70
+
71
+ ### 2. `Module` is a contract, not a toolbox
72
+
73
+ Without methods, `Module` still does work nothing else does. Its `name` is what
74
+ makes a named module collapse the path to its left into a namespace, which name
75
+ resolution depends on. `pmrf.modules.validate` rejects raw float JAX arrays in
76
+ its fields, which are ambiguous between free and fixed parameters. It carries
77
+ `metadata`, gives the unwrapped `repr`, and is the base of `Model`, losses,
78
+ likelihoods, kernels, materials and evaluators. Subclassing it says the object
79
+ takes part in naming and validation. Its docstring says so.
80
+
81
+ ### 3. One verb for changing a model: `prf.update`
82
+
83
+ `prf.update` returns a copy of a model in which the parts a selector picks are
84
+ replaced. Exactly one form says what with:
85
+
86
+ ```python
87
+ prf.update(model, {'L1.L': 3.0, 'C1.C': 2.0}) # parameter values by name
88
+ prf.update(model, 'L1.L', value=3.0) # parameter fields on a selection
89
+ prf.update(model, 'cable.*', fixed=True) # fixed state
90
+ prf.update(model, 'cascade[1]', Short()) # a new sub-model or node
91
+ prf.update(model, 'load.*', fn=lambda p: ...) # a function of the old part
92
+ prf.update(model, v, space='raw') # write-back from an optimiser or sampler
93
+ prf.update(param, value=3.0) # selector omitted: the root itself
94
+ ```
95
+
96
+ It replaces `with_values`, `with_free`, `with_fixed`, `Module.map`,
97
+ `Module.at`, `Param.as_fixed`, `Param.as_free` and `Param.wrap`.
98
+
99
+ - **Selectors** are parameter names, `fnmatch` globs over them, sequences of
100
+ names, or callables, resolved by the #133 resolver.
101
+ - **Two tiers, set by the form.** The mapping, `value=` and `fixed=` forms go
102
+ through each parameter's constructor: they validate bounds and keep the prior,
103
+ constraint, scale, name and metadata. The node and `fn=` forms are
104
+ structural: they bypass converters and validation, and the docstring says so.
105
+ - **The mapping form** is recognised only as the second positional argument
106
+ with every key a string. Its values may be arrays or `Param` objects. Any
107
+ other second argument is a selector, and a form mismatch raises an error that
108
+ lists the forms.
109
+ - **`fixed=`** is additive. `fixed=False` frees a parameter even if it was
110
+ created fixed, and parameters the selector does not match are untouched.
111
+ "Only these free" is `update(update(m, '*', fixed=True), names, fixed=False)`.
112
+ - **Value forms keep the jit cache key.** The mapping, `value=` and `space=`
113
+ forms never change the treedef, or any leaf's dtype, shape or `weak_type`
114
+ (decision 10). Changing `fixed=`, or any structural form, changes the model's
115
+ structure, and recompiling is expected.
116
+ - **Not an optimiser step.** In fitting, "updates" also means Optax gradient
117
+ steps; the docstring says `update` is neither.
118
+
119
+ `prf.replace` stays as the plain `dataclasses.replace`: fields of one object,
120
+ unvalidated, able to break a type. Its docstring points to `update` for
121
+ anything name-based.
122
+
123
+ `prf.tie(model, target, source, fn=identity)` stays a separate verb. A tie is
124
+ not a replacement: its target is recomputed from its source every time the
125
+ model is unwrapped.
126
+
127
+ ### 4. Reading: `prf.params` and `prf.param_values`
128
+
129
+ ```python
130
+ prf.params(model, where='*', *, free_only=False) # dict[str, Param]
131
+ prf.param_values(model, where='*', *, free_only=False, space='declared') # dict[str, Array]
132
+ prf.log_prior(model, *, space='declared') # scalar
133
+ ```
134
+
135
+ A user thinks "the parameters of my model", so `params` returns `Param`
136
+ objects, whose `repr` shows value, bounds and prior. `param_values` is what
137
+ `update` accepts and what optimisers use; a bare `values` was rejected as too
138
+ vague at top level, where it could mean S-parameter data. `named_params`'
139
+ `full_params` and `namespace_separator` are dropped.
140
+
141
+ The documented identity, which is also the regression test of decision 10:
142
+
143
+ ```python
144
+ prf.update(m, prf.param_values(m, space=s), space=s) # same structure, same cache key
145
+ ```
146
+
147
+ All functions are defined in `pmrf.parameters` and re-exported at top level.
148
+
149
+ ### 5. Three value spaces: `raw`, `declared`, `physical`
150
+
151
+ | Space | Meaning | Example (2 pF, `scale=1e-12`) |
152
+ |---|---|---|
153
+ | `raw` | the latent, unbounded array an optimiser or sampler moves through | constraint bijector inverse of 2.0 |
154
+ | `declared` | the number as written, in the units the parameter's scale declares | `2.0` |
155
+ | `physical` | the scaled, SI value | `2e-12` |
156
+
157
+ `declared` is the default everywhere, and every user-facing surface agrees with
158
+ construction: `Param(value=...)`, bounds, priors, `repr`, `Param.value`,
159
+ `param_values` and `update`. Distributions and bounds are authored in declared
160
+ space.
161
+
162
+ `Param` gets one property per space: `value` (declared), `physical_value` and
163
+ `raw_value` (the latent array). `unscaled_value` is removed. The field
164
+ currently named `raw_value`, which holds the Parax variable, is renamed
165
+ `variable`. The bijector properties become `raw_to_declared_bijector` and
166
+ `declared_to_physical_bijector`; "constrained" and "unscaled" are no longer
167
+ used as space names.
168
+
169
+ `log_prior` supports all three spaces: declared is the density as written,
170
+ physical adds −log|scale| per parameter, and raw adds the constraint bijector's
171
+ log|det J| as well. Each docstring states its measure.
172
+
173
+ **Why `raw`, not `unconstrained`.** ParamRF's users are mostly RF engineers,
174
+ who read "unconstrained" as "a parameter without bounds", which is what
175
+ `prf.Unconstrained` creates. The code already used "raw" for this reason.
176
+ **Why not `unit`.** In fitting, "unit space" is the unit hypercube nested
177
+ samplers work in, which ParamRF's hypercube sampler maps to.
178
+ **Why not `unscaled`, `normalized`, `per_unit` or `engineering`.** `declared`
179
+ says what the value is rather than what has not happened to it; `normalized`
180
+ means z = Z/Z₀ in RF and [0, 1] in optimisation; `per_unit` is power-systems
181
+ jargon; "engineering units" means the scaled value in instrumentation.
182
+
183
+ ### 6. Scale is the units a value is written in
184
+
185
+ A field's scale is a default unit. An explicit scale on the value overrides it;
186
+ the two are never multiplied. The factories and `as_param` take `scale=None`,
187
+ meaning "inherit the field's", so `prf.Unconstrained(2.0)` passed into a pF
188
+ field is 2 pF, and `prf.Unconstrained(2.0, scale=1e-9)` is 2 nF.
189
+
190
+ This reverses 0.35.0's `prf.replace(p, value=...)`, which took the physical
191
+ value; `update(p, value=...)` takes the declared one.
192
+
193
+ ### 7. Fixed and frozen are different things
194
+
195
+ - **Fixed** is a parameter state. The parameter is still a `Param`, still named
196
+ and still carries its prior; it is excluded from optimisation.
197
+ `update(..., fixed=)` toggles it.
198
+ - **Frozen** (`prf.freeze`, `prf.unfreeze`) makes a subtree opaque. It is for
199
+ constant data, such as `field(converter=prf.freeze)`, and for hiding whole
200
+ subtrees.
201
+
202
+ The name-based operations act on fixed, never on frozen.
203
+
204
+ ### 8. No public flattening
205
+
206
+ Parameter values cross ParamRF's boundary as name-keyed dicts, which are
207
+ pytrees that Optax, Optimistix, BlackJAX and `jax.grad` take directly.
208
+ Raveling to a 1-D vector stays inside the adapters that need it, such as the
209
+ SciPy solver (`pmrf/optimize/solvers/scipy.py`) and non-JAX samplers.
210
+ `prf.flatten` and `FlatParams` (#142) are not added. A public ravel helper, with
211
+ one name per array element, can be added when a non-JAX consumer needs it; it
212
+ would sit on top of `param_values`, not replace it.
213
+
214
+ The substance of #142 is kept: the raw-space log prior with the Jacobian and
215
+ scale terms, name alignment, and a single implementation behind the minimiser
216
+ and samplers.
217
+
218
+ ### 9. Public names describe the domain, not the data structure
219
+
220
+ Public functions have no `tree_` prefix; most users do not know what a pytree
221
+ is and should not need to. `tree_*` names are for private helpers. Docs say "a
222
+ model, or any collection of models and parameters", and reserve "pytree" for
223
+ an advanced page.
224
+
225
+ ### 10. Recompilation is prevented by keeping structure, not by changing the cache
226
+
227
+ The automatic `eqx.filter_jit` on RF methods and its cache key stay as they
228
+ are. Dropping the automatic jit would make `model.s(freq)` slow for the users
229
+ decision 1 keeps methods for; removing `name` and `metadata` from the treedef
230
+ would complicate name resolution for a rare case.
231
+
232
+ Instead, the structure-preserving forms of `update` guarantee an unchanged
233
+ cache key, and a test enforces it for every such form: treedef, dtype, shape and
234
+ `weak_type` of every leaf. The docs say plainly that changing a parameter's
235
+ value never recompiles and rebuilding it does.
236
+
237
+ ## Consequences
238
+
239
+ - **Breaking, in one minor release.** Removed: `Module.named_params`, `values`,
240
+ `with_values`, `with_free`, `with_fixed`, `map`, `at`, `tied`; `Model.tied`;
241
+ `Param.as_fixed`, `as_free`, `at`, `unscaled_value`; public `Param.wrap`.
242
+ Renamed: the `Param.raw_value` field to `variable`, and the bijector
243
+ properties. Changed: `Param.value` returns the declared value; scales
244
+ override instead of multiplying. No aliases or deprecation shims.
245
+ - `pmrf.utils.optix`'s lens becomes internal or is deleted; nothing public
246
+ returns a `Lens`.
247
+ - Joint-tree names keep #133's rules. A named model inside a dict drops its key,
248
+ and a collision raises; top-level dict keys do not become a namespace layer.
249
+ - Saved results keyed by name keep working; results that stored physical
250
+ values need re-reading in declared space.
251
+ - Deferred to their own issues: reducing the top-level and submodule surface
252
+ (including `__all__` listing `Topology` and `Initvar`, which do not exist);
253
+ compile duration for large S-parameter blocks (#130, the per-port
254
+ interpolation loop); the generated `s_db`-style methods; flat vectors for
255
+ non-JAX tools.
@@ -20,6 +20,19 @@ Core Primitives
20
20
  pmrf.Random
21
21
 
22
22
 
23
+ Working with Parameters
24
+ -----------------------
25
+
26
+ .. autosummary::
27
+ :toctree: generated/
28
+
29
+ pmrf.params
30
+ pmrf.param_values
31
+ pmrf.log_prior
32
+ pmrf.update
33
+ pmrf.tie
34
+
35
+
23
36
  Main Modules
24
37
  ------------
25
38
 
@@ -9,7 +9,7 @@ The Model
9
9
 
10
10
  Under the hood, ParamRF uses the `Equinox <https://docs.kidger.site/equinox/api/module/module/>`_ library to interoperate with JAX. This means that :class:`pmrf.Model` is an `Equinox Module <https://docs.kidger.site/equinox/api/module/module/>`_, a `JAX PyTree <https://docs.jax.dev/en/latest/pytrees.html>`_ and a Python `dataclass <https://docs.python.org/3/library/dataclasses.html>`_. If these concepts are completely foreign to you, do not worry. The practical consequences of this are:
11
11
 
12
- * **Models are immutable**. Models cannot be mutated (``model.x = 5.0`` will throw an error). Rather, they represent "pure functions" which store their parameters alongside. This means that models should *never* contain any fields that can be derived from other fields (state should be "pure"), and should also not reference other objects in the model (using the same parameter object twice will result in two separate parameters!). Instead, :meth:`pmrf.Model.at` can be used to manipulate a model. Although this approach may feel new to some, the end result is improved optimization performance, differentiation capabilities, and added flexibility.
12
+ * **Models are immutable**. Models cannot be mutated (``model.x = 5.0`` will throw an error). Rather, they represent "pure functions" which store their parameters alongside. This means that models should *never* contain any fields that can be derived from other fields (state should be "pure"), and should also not reference other objects in the model (using the same parameter object twice will result in two separate parameters!). Instead, :func:`pmrf.update` returns a copy of a model with the parts you select replaced; see :doc:`parameter_names`. Although this approach may feel new to some, the end result is improved optimization performance and differentiation capabilities.
13
13
  * **Models are lazy**. Since models only store their parameters (and not their S-matrix or frequency), they do not perform any computation at initialization. Rather, their response is only evaluated when one of their methods (e.g. :meth:`pmrf.Model.s`) is passed a :class:`~pmrf.Frequency` object *as input*. This is in contrast to other, purely object-oriented libraries (such as :mod:`scikit-rf`).
14
14
  * **Models are JAX-native**. Models are simply JAX `PyTrees <https://docs.jax.dev/en/latest/pytrees.html>`_, meaning they can be passed around in any JAX context. This means that they can be compiled *just-in-time* (JIT) for enhanced performance and simulation on other platforms (GPUs, TPUs etc) and also used for many advanced JAX features (such as for vectorization via :func:`jax.vmap` and differentiation via :func:`jax.jacfwd`). See the :doc:`jax_overview` section for more details.
15
15
 
@@ -7,5 +7,6 @@ This chapter provides an in-depth description of the most important concepts in
7
7
  :maxdepth: 2
8
8
 
9
9
  core_primitives
10
+ parameter_names
10
11
  optimization_and_inference
11
12
  jax_overview
@@ -11,7 +11,7 @@ Standard Python is interpreted, which can introduce significant overhead. JAX by
11
11
 
12
12
  When a JIT-compiled function is called, it is not immediately executed. Instead, JAX "traces" the function's execution using abstract values. This trace builds a static computation graph in an intermediate representation called **XLA** (Accelerated Linear Algebra). The XLA compiler then optimizes this graph by merging operations, reducing memory allocation, and compiling it to a specific hardware architecture (CPU, GPU, or TPU).
13
13
 
14
- In ParamRF, all models and evaluators are designed to be JIT-compatible. Because JAX relies on this static tracing mechanism, functions passed to the JIT compiler must be *pure* (stateless and lacking side-effects). This functional paradigm is the underlying reason why ParamRF models are immutable; rather than modifying a model's attributes in-place, methods like :meth:`pmrf.Model.at` should be used.
14
+ In ParamRF, all models and evaluators are designed to be JIT-compatible. Because JAX relies on this static tracing mechanism, functions passed to the JIT compiler must be *pure* (stateless and lacking side-effects). This functional paradigm is the underlying reason why ParamRF models are immutable; rather than modifying a model's attributes in-place, :func:`pmrf.update` returns a modified copy.
15
15
 
16
16
  Differentiability and Autodifferentiation
17
17
  -----------------------------------------
@@ -64,7 +64,7 @@ This allows you to easily create and evaluate entire batches of models simultane
64
64
 
65
65
  Parax and Unwrapping
66
66
  --------------------
67
- ParamRF builds on top of the JAX library `Parax <https://github.com/gvcallen/parax>`_ for parameters and constraints. Parax allows parameters (and entire models) to be manipulated in powerful ways, such as being fixed, scaled, constrained, or tied together. To accomplish this, Parax makes use of a concept known as *unwrapping*. To initialize a *wrapper*, the relevant parameter or object is *wrapped* in the desired class (for example, a "scale" wrapper). Then, to apply the wrapper, the object is *unwrapped*. This mechanism is what allows parameters to be tied together using :meth:`pmrf.Model.tied`, or for parameters to remain bounded, *even* when using an unbounded optimization algorithm.
67
+ ParamRF builds on top of the JAX library `Parax <https://github.com/gvcallen/parax>`_ for parameters and constraints. Parax allows parameters (and entire models) to be fixed, scaled, constrained, or tied together. To accomplish this, Parax makes use of a concept known as *unwrapping*. To initialize a *wrapper*, the relevant parameter or object is *wrapped* in the desired class (for example, a "scale" wrapper). Then, to apply the wrapper, the object is *unwrapped*. This mechanism is what allows parameters to be tied together using :func:`pmrf.tie`, or for parameters to remain bounded, *even* when using an unbounded optimization algorithm.
68
68
 
69
69
  In ParamRF, unwrapping can be done manually using :func:`pmrf.unwrap` (which is an alias to :func:`parax.unwrap`). It is also automatically applied in ParamRF for RF response methods such as :meth:`pmrf.Model.s` via the :func:`pmrf.unwrap_self` annotation, as well as internally during optimization and inference. However, if you are using non-standard methods on your models and simply want to evaluate them (for example in a Jupyter notebook), then you should manually unwrap using :func:`pmrf.unwrap`, or annotate your method using :func:`pmrf.unwrap_self` where relevant.
70
70