paramrf 0.35.3__tar.gz → 0.35.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.
Files changed (238) hide show
  1. {paramrf-0.35.3 → paramrf-0.35.4}/AGENTS.md +14 -1
  2. {paramrf-0.35.3 → paramrf-0.35.4}/CLAUDE.md +14 -1
  3. {paramrf-0.35.3 → paramrf-0.35.4}/CONTEXT.md +25 -0
  4. {paramrf-0.35.3 → paramrf-0.35.4}/PKG-INFO +1 -1
  5. paramrf-0.35.4/docs/adr/0004-profiled-lines.md +214 -0
  6. {paramrf-0.35.3 → paramrf-0.35.4}/docs/api/index.rst +1 -0
  7. {paramrf-0.35.3 → paramrf-0.35.4}/paramrf.egg-info/PKG-INFO +1 -1
  8. {paramrf-0.35.3 → paramrf-0.35.4}/paramrf.egg-info/SOURCES.txt +5 -0
  9. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/__init__.py +2 -0
  10. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/__init__.py +11 -1
  11. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/__init__.py +6 -0
  12. paramrf-0.35.4/pmrf/models/components/lines/profiles.py +341 -0
  13. paramrf-0.35.4/pmrf/models/composite/interconnected/cascade.py +390 -0
  14. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/parameters.py +166 -19
  15. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/rf/__init__.py +7 -0
  16. paramrf-0.35.4/pmrf/rf/cascade.py +208 -0
  17. {paramrf-0.35.3 → paramrf-0.35.4}/pyproject.toml +1 -1
  18. paramrf-0.35.4/tests/test_models/test_profiles.py +357 -0
  19. paramrf-0.35.4/tests/test_models/test_repeated_cascade.py +274 -0
  20. paramrf-0.35.4/tests/test_structural_updates.py +607 -0
  21. paramrf-0.35.3/pmrf/models/composite/interconnected/cascade.py +0 -250
  22. paramrf-0.35.3/tests/test_structural_updates.py +0 -306
  23. {paramrf-0.35.3 → paramrf-0.35.4}/.github/workflows/docs.yml +0 -0
  24. {paramrf-0.35.3 → paramrf-0.35.4}/.github/workflows/draft-pdf.yml +0 -0
  25. {paramrf-0.35.3 → paramrf-0.35.4}/.github/workflows/publish.yml +0 -0
  26. {paramrf-0.35.3 → paramrf-0.35.4}/.github/workflows/tests.yml +0 -0
  27. {paramrf-0.35.3 → paramrf-0.35.4}/.gitignore +0 -0
  28. {paramrf-0.35.3 → paramrf-0.35.4}/CHANGELOG.md +0 -0
  29. {paramrf-0.35.3 → paramrf-0.35.4}/CITATION.cff +0 -0
  30. {paramrf-0.35.3 → paramrf-0.35.4}/CONTRIBUTING.md +0 -0
  31. {paramrf-0.35.3 → paramrf-0.35.4}/LICENSE +0 -0
  32. {paramrf-0.35.3 → paramrf-0.35.4}/NOTICE +0 -0
  33. {paramrf-0.35.3 → paramrf-0.35.4}/README.rst +0 -0
  34. {paramrf-0.35.3 → paramrf-0.35.4}/assets/logo.png +0 -0
  35. {paramrf-0.35.3 → paramrf-0.35.4}/docs/Makefile +0 -0
  36. {paramrf-0.35.3 → paramrf-0.35.4}/docs/_static/custom.css +0 -0
  37. {paramrf-0.35.3 → paramrf-0.35.4}/docs/_templates/autosummary/class.rst +0 -0
  38. {paramrf-0.35.3 → paramrf-0.35.4}/docs/_templates/autosummary/function.rst +0 -0
  39. {paramrf-0.35.3 → paramrf-0.35.4}/docs/_templates/autosummary/module.rst +0 -0
  40. {paramrf-0.35.3 → paramrf-0.35.4}/docs/adr/0001-line-modelling-architecture.md +0 -0
  41. {paramrf-0.35.3 → paramrf-0.35.4}/docs/adr/0002-parameter-api.md +0 -0
  42. {paramrf-0.35.3 → paramrf-0.35.4}/docs/adr/0003-derived-models.md +0 -0
  43. {paramrf-0.35.3 → paramrf-0.35.4}/docs/agents/domain.md +0 -0
  44. {paramrf-0.35.3 → paramrf-0.35.4}/docs/agents/issue-tracker.md +0 -0
  45. {paramrf-0.35.3 → paramrf-0.35.4}/docs/agents/triage-labels.md +0 -0
  46. {paramrf-0.35.3 → paramrf-0.35.4}/docs/conf.py +0 -0
  47. {paramrf-0.35.3 → paramrf-0.35.4}/docs/core_concepts/core_primitives.rst +0 -0
  48. {paramrf-0.35.3 → paramrf-0.35.4}/docs/core_concepts/index.rst +0 -0
  49. {paramrf-0.35.3 → paramrf-0.35.4}/docs/core_concepts/jax_overview.rst +0 -0
  50. {paramrf-0.35.3 → paramrf-0.35.4}/docs/core_concepts/optimization_and_inference.rst +0 -0
  51. {paramrf-0.35.3 → paramrf-0.35.4}/docs/core_concepts/parameter_names.rst +0 -0
  52. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/cascading_and_terminating.rst +0 -0
  53. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/circuit_clc.png +0 -0
  54. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/circuit_models.rst +0 -0
  55. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/custom_models.rst +0 -0
  56. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/derivatives_and_sweeps.rst +0 -0
  57. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/index.rst +0 -0
  58. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/model_optimization.rst +0 -0
  59. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/multiple_models_one_parameter_set.rst +0 -0
  60. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/parameter_naming_and_model_manipulation.rst +0 -0
  61. {paramrf-0.35.3 → paramrf-0.35.4}/docs/examples/shared_substrates.rst +0 -0
  62. {paramrf-0.35.3 → paramrf-0.35.4}/docs/index.rst +0 -0
  63. {paramrf-0.35.3 → paramrf-0.35.4}/docs/license.rst +0 -0
  64. {paramrf-0.35.3 → paramrf-0.35.4}/docs/make.bat +0 -0
  65. {paramrf-0.35.3 → paramrf-0.35.4}/docs/research/conductor-loss-alternatives.md +0 -0
  66. {paramrf-0.35.3 → paramrf-0.35.4}/docs/research/holloway1994.pdf +0 -0
  67. {paramrf-0.35.3 → paramrf-0.35.4}/docs/research/microstrip-loss-conventions.md +0 -0
  68. {paramrf-0.35.3 → paramrf-0.35.4}/docs/research/precision-coax-microstrip-10-500mhz.md +0 -0
  69. {paramrf-0.35.3 → paramrf-0.35.4}/docs/skrf_comparison/index.rst +0 -0
  70. {paramrf-0.35.3 → paramrf-0.35.4}/docs/skrf_comparison/overview.rst +0 -0
  71. {paramrf-0.35.3 → paramrf-0.35.4}/docs/skrf_comparison/performance.rst +0 -0
  72. {paramrf-0.35.3 → paramrf-0.35.4}/docs/tutorials/1_cable_fitting.ipynb +0 -0
  73. {paramrf-0.35.3 → paramrf-0.35.4}/docs/tutorials/2_chip_inductor_fitting.ipynb +0 -0
  74. {paramrf-0.35.3 → paramrf-0.35.4}/docs/tutorials/data/CBN-1.5FT-SMSM.s2p +0 -0
  75. {paramrf-0.35.3 → paramrf-0.35.4}/docs/tutorials/data/on-chip-inductor.s2p +0 -0
  76. {paramrf-0.35.3 → paramrf-0.35.4}/docs/tutorials/index.rst +0 -0
  77. {paramrf-0.35.3 → paramrf-0.35.4}/paper/paper.bib +0 -0
  78. {paramrf-0.35.3 → paramrf-0.35.4}/paper/paper.md +0 -0
  79. {paramrf-0.35.3 → paramrf-0.35.4}/paper/rlc.png +0 -0
  80. {paramrf-0.35.3 → paramrf-0.35.4}/paramrf.egg-info/dependency_links.txt +0 -0
  81. {paramrf-0.35.3 → paramrf-0.35.4}/paramrf.egg-info/requires.txt +0 -0
  82. {paramrf-0.35.3 → paramrf-0.35.4}/paramrf.egg-info/top_level.txt +0 -0
  83. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/_solver_view.py +0 -0
  84. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/bijectors.py +0 -0
  85. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/constraints.py +0 -0
  86. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/covariance_kernels.py +0 -0
  87. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/discrepancy_models.py +0 -0
  88. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/distributions.py +0 -0
  89. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/evaluators.py +0 -0
  90. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/fitting/__init__.py +0 -0
  91. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/fitting/minimize.py +0 -0
  92. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/fitting/result.py +0 -0
  93. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/fitting/routers.py +0 -0
  94. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/fitting/sample.py +0 -0
  95. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/fitting/targets.py +0 -0
  96. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/frequency.py +0 -0
  97. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/infer/__init__.py +0 -0
  98. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/infer/base.py +0 -0
  99. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/infer/result.py +0 -0
  100. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/infer/sample.py +0 -0
  101. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/infer/solvers/__init__.py +0 -0
  102. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/infer/solvers/blackjax.py +0 -0
  103. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/infer/solvers/polychord.py +0 -0
  104. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/likelihoods.py +0 -0
  105. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/losses.py +0 -0
  106. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/materials/__init__.py +0 -0
  107. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/materials/conductor.py +0 -0
  108. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/materials/dielectric.py +0 -0
  109. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/materials/properties.py +0 -0
  110. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/materials/roughness.py +0 -0
  111. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/materials/substrate.py +0 -0
  112. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/materials/surface_impedance.py +0 -0
  113. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/math/__init__.py +0 -0
  114. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/math/aggregations.py +0 -0
  115. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/math/bessel.py +0 -0
  116. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/math/conversions.py +0 -0
  117. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/math/losses.py +0 -0
  118. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/math/misc.py +0 -0
  119. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/__init__.py +0 -0
  120. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/base.py +0 -0
  121. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/bridge.py +0 -0
  122. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/callable.py +0 -0
  123. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/delegated.py +0 -0
  124. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/derived.py +0 -0
  125. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/static.py +0 -0
  126. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/adapters/wrapped.py +0 -0
  127. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/base.py +0 -0
  128. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/__init__.py +0 -0
  129. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/ideal.py +0 -0
  130. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/base.py +0 -0
  131. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/coaxial.py +0 -0
  132. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/empirical.py +0 -0
  133. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/ideal.py +0 -0
  134. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/microstrip.py +0 -0
  135. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/nodal.py +0 -0
  136. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/nonuniform.py +0 -0
  137. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/planar.py +0 -0
  138. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lines/stripline.py +0 -0
  139. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/lumped.py +0 -0
  140. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/components/sections.py +0 -0
  141. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/__init__.py +0 -0
  142. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/__init__.py +0 -0
  143. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/circuit/__init__.py +0 -0
  144. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/circuit/base.py +0 -0
  145. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/circuit/circuit.py +0 -0
  146. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/circuit/solvers/__init__.py +0 -0
  147. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/circuit/solvers/nodal.py +0 -0
  148. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/circuit/solvers/scattering.py +0 -0
  149. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/interconnected/terminated.py +0 -0
  150. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/nodal.py +0 -0
  151. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/topological.py +0 -0
  152. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/composite/transformed.py +0 -0
  153. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/surrogates/__init__.py +0 -0
  154. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/surrogates/expansion.py +0 -0
  155. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/models/surrogates/rational.py +0 -0
  156. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/modules/__init__.py +0 -0
  157. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/modules/base.py +0 -0
  158. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/modules/wrapped.py +0 -0
  159. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/network_collection.py +0 -0
  160. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/noise_models.py +0 -0
  161. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/__init__.py +0 -0
  162. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/base.py +0 -0
  163. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/minimize.py +0 -0
  164. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/result.py +0 -0
  165. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/solvers/__init__.py +0 -0
  166. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/solvers/jaxopt.py +0 -0
  167. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/solvers/optimistix.py +0 -0
  168. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/optimize/solvers/scipy.py +0 -0
  169. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/problems.py +0 -0
  170. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/rf/conversions.py +0 -0
  171. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/rf/mna.py +0 -0
  172. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/serialization.py +0 -0
  173. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/terms.py +0 -0
  174. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/types.py +0 -0
  175. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/__init__.py +0 -0
  176. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/array.py +0 -0
  177. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/debug.py +0 -0
  178. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/network.py +0 -0
  179. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/random.py +0 -0
  180. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/rf.py +0 -0
  181. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/transforms.py +0 -0
  182. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/tree.py +0 -0
  183. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/utils/type.py +0 -0
  184. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/viz/__init__.py +0 -0
  185. {paramrf-0.35.3 → paramrf-0.35.4}/pmrf/viz/plots.py +0 -0
  186. {paramrf-0.35.3 → paramrf-0.35.4}/scripts/install-test-deps.sh +0 -0
  187. {paramrf-0.35.3 → paramrf-0.35.4}/setup.cfg +0 -0
  188. {paramrf-0.35.3 → paramrf-0.35.4}/tests/__init__.py +0 -0
  189. {paramrf-0.35.3 → paramrf-0.35.4}/tests/_dependency_checks.py +0 -0
  190. {paramrf-0.35.3 → paramrf-0.35.4}/tests/_jit.py +0 -0
  191. {paramrf-0.35.3 → paramrf-0.35.4}/tests/conftest.py +0 -0
  192. {paramrf-0.35.3 → paramrf-0.35.4}/tests/data/10m_cable.s2p +0 -0
  193. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_autodiff.py +0 -0
  194. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_conversions.py +0 -0
  195. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_covariance_kernels.py +0 -0
  196. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_dc_limits.py +0 -0
  197. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_evaluators.py +0 -0
  198. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_fitting_minimize.py +0 -0
  199. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_fitting_routers.py +0 -0
  200. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_fitting_sample.py +0 -0
  201. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_fitting_targets.py +0 -0
  202. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_frequency.py +0 -0
  203. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_infer_base.py +0 -0
  204. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_infer_sample.py +0 -0
  205. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_map_priors.py +0 -0
  206. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_materials/test_conductor.py +0 -0
  207. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_materials/test_dielectric.py +0 -0
  208. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_materials/test_serialization.py +0 -0
  209. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_materials/test_substrate.py +0 -0
  210. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_materials/test_surface_impedance.py +0 -0
  211. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_math/test_bessel.py +0 -0
  212. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_model.py +0 -0
  213. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_adapters.py +0 -0
  214. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_circuit_nodal.py +0 -0
  215. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_circuit_port_order.py +0 -0
  216. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_circuit_scattering.py +0 -0
  217. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_current_distribution.py +0 -0
  218. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_derived.py +0 -0
  219. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_interconnected.py +0 -0
  220. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_lines.py +0 -0
  221. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_lines_skrf_matrix.py +0 -0
  222. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_lumped.py +0 -0
  223. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_nodal.py +0 -0
  224. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_sections.py +0 -0
  225. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_transformed.py +0 -0
  226. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_models/test_transformers.py +0 -0
  227. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_module.py +0 -0
  228. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_naming.py +0 -0
  229. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_optimize_base.py +0 -0
  230. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_optimize_minimize.py +0 -0
  231. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_parameters.py +0 -0
  232. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_parameters_by_name.py +0 -0
  233. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_raw_space_solving.py +0 -0
  234. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_serialization.py +0 -0
  235. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_terms.py +0 -0
  236. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_transforms.py +0 -0
  237. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_utils/test_compress.py +0 -0
  238. {paramrf-0.35.3 → paramrf-0.35.4}/tests/test_utils/test_tree.py +0 -0
@@ -8,7 +8,20 @@ dataclasses that are also JAX PyTrees. Built on `jax`, `equinox`, and
8
8
  ## Breaking changes are acceptable
9
9
 
10
10
  The library is pre-1.0 and moves quickly. Do not add backwards-compatibility shims,
11
- deprecation aliases, or legacy code paths unless explicitly asked. Prefer the clean design.
11
+ deprecation aliases, or legacy code paths unless explicitly asked. Prefer the clean design,
12
+ within the seam the ticket names.
13
+
14
+ ## Seams
15
+
16
+ A ticket names one seam, either a file, a set of files or a folder. If a cross-seam change is needed, implmentation should be stopped and decision left to the human.
17
+
18
+ `docs/adr/` and `CONTEXT.md` change in their own commit, by the reviewer. An
19
+ implementation reaching for a word absent from the ticket, the ADR and `CONTEXT.md` has
20
+ reached a decision it does not have: ask.
21
+
22
+ Open every PR body with `## Cross-seam`: each file touched outside the ticket's seam,
23
+ with the ADR or issue line sanctioning it, or `DECISION NEEDED`. Write `none` when there
24
+ are none.
12
25
 
13
26
  ## Commands
14
27
 
@@ -8,7 +8,20 @@ dataclasses that are also JAX PyTrees. Built on `jax`, `equinox`, and
8
8
  ## Breaking changes are acceptable
9
9
 
10
10
  The library is pre-1.0 and moves quickly. Do not add backwards-compatibility shims,
11
- deprecation aliases, or legacy code paths unless explicitly asked. Prefer the clean design.
11
+ deprecation aliases, or legacy code paths unless explicitly asked. Prefer the clean design,
12
+ within the seam the ticket names.
13
+
14
+ ## Seams
15
+
16
+ A ticket names one seam, either a file, a set of files or a folder. If a cross-seam change is needed, implmentation should be stopped and decision left to the human.
17
+
18
+ `docs/adr/` and `CONTEXT.md` change in their own commit, by the reviewer. An
19
+ implementation reaching for a word absent from the ticket, the ADR and `CONTEXT.md` has
20
+ reached a decision it does not have: ask.
21
+
22
+ Open every PR body with `## Cross-seam`: each file touched outside the ticket's seam,
23
+ with the ADR or issue line sanctioning it, or `DECISION NEEDED`. Write `none` when there
24
+ are none.
12
25
 
13
26
  ## Commands
14
27
 
@@ -54,6 +54,31 @@ dataclass field replace, not an update.
54
54
 
55
55
  *Avoid:* "set values", "with values"; "update" for an optimiser step.
56
56
 
57
+ ### Tie and resolve
58
+
59
+ `prf.tie` derives one part of a tree from another: the **target** is removed from
60
+ the parameters and recomputed as `fn(source)` whenever the tree is resolved, so
61
+ it follows the source through `prf.update`, optimisation and sampling. Target
62
+ and source are selected by name, on either side, and a name that picks a
63
+ sub-model ties the parameters beneath it, pairing by suffix.
64
+
65
+ Two operations read a tied tree back, and they are not the same thing.
66
+
67
+ - **Resolve** (`prf.resolve`): structural. Ties are applied and every wrapper
68
+ that only describes structure is discharged; parameters stay parameters. A
69
+ container resolves to its own shape, so a `dict` of components comes back a
70
+ `dict`, still parameterised.
71
+ - **Evaluation** (`prf.unwrap`): every parameter becomes its physical value.
72
+ The result is numbers, not priors, and nothing rebuilt from it is
73
+ parameterised. A joint prior (`prf.modules.Probabilistic`) is discharged the
74
+ same way and for the same reason, so resolve leaves it standing.
75
+
76
+ Resolve is the one ordinary modelling code wants; `prf.unwrap` is a low-level
77
+ evaluation primitive. A tie's target resolves to a plain value either way — it
78
+ is derived, so it has no prior of its own.
79
+
80
+ *Avoid:* "unwrap" for reading a tied container; "apply the ties" for evaluation.
81
+
57
82
  ### Derived model
58
83
 
59
84
  A model computed from a **base** model and **new parameters** by a function,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paramrf
3
- Version: 0.35.3
3
+ Version: 0.35.4
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
@@ -0,0 +1,214 @@
1
+ # ADR-0004: Profiled lines vary parameters along a line through target-agnostic profiles
2
+
3
+ Status: accepted (2026-09)
4
+
5
+ ## Context
6
+
7
+ ParamRF's line models (ADR-0001) are uniform: a `MicrostripLine` has one width,
8
+ one substrate, one conductor, and its `zc_and_gammaL` holds everywhere along it.
9
+ Real structures are not. A taper's width varies along z, a cable's permittivity
10
+ drifts with a moisture gradient, a deposited conductor's conductivity varies with
11
+ position. The quantity that varies is always an existing parameter of an existing
12
+ line, and it must keep taking part in fitting: its shape is described by a few
13
+ new parameters with their own priors, and those are what the user fits.
14
+
15
+ The repository already held a commented-out `ProfiledLine` from an earlier API:
16
+ a `line_fn` plus parallel `profile_fns` (static callables) and `profile_params`
17
+ dicts, a constructor that sniffed keyword types to decide what was a profile, and
18
+ a `method='stepped'|'riccati'` switch whose Riccati branch raised
19
+ `NotImplementedError`. It predates the parameter API (ADR-0002) and derived
20
+ models (ADR-0003).
21
+
22
+ Two further pressures shaped this. First (#45), a cascade of ten microstrip lines
23
+ with independent widths is built by a Python list comprehension, which traces
24
+ once per member and gives every member its own name set — the same batching
25
+ problem a profiled line has, at a different level. Second, users do not know what
26
+ section count a given taper needs for an accurate answer, and an under-resolved
27
+ line is not merely inaccurate: an optimiser will exploit it.
28
+
29
+ ## Decision
30
+
31
+ `pmrf.models.ProfiledLine` takes a uniform line and a mapping from parameter
32
+ targets to **profiles**, and evaluates as a cascade of `n` uniform sections whose
33
+ parameters are sampled at the section midpoints.
34
+
35
+ ### A profile is a shape, not a line
36
+
37
+ `AbstractProfile` is a `pmrf.Module` — parameter-aware, named, validated, but not
38
+ a `Model`, exactly as materials and the line strategy objects are. It declares one
39
+ method, `evaluate(t)`, on normalised `t`, documented as elementwise so the
40
+ container passes the whole midpoint array in one call. Its coefficients are
41
+ ordinary `prf.param` fields carrying physical units and their own constraints, so
42
+ `ExponentialProfile(start=2e-3, end=8e-3)` and
43
+ `ExponentialProfile(start=Bounded(...), end=Bounded(...))` both work with no
44
+ profile-specific machinery.
45
+
46
+ A profile knows nothing about the line, the parameter, or the family. This is the
47
+ central constraint: an exponential taper must apply to microstrip width, stripline
48
+ width or coaxial inner diameter without being rewritten. `evaluate` returns a
49
+ plain physical value, in the units of whatever it is attached to.
50
+
51
+ `t = 0` is at port 1 and `t = 1` at port 2, fixed on `AbstractProfile` and
52
+ restated on `ProfiledLine`. Reversing a taper swaps coefficients through
53
+ `prf.update`; there is no orientation flag, because a flag that silently mirrors
54
+ geometry inside a cascade is a debugging hazard.
55
+
56
+ The library ships `LinearProfile`, `ExponentialProfile` and `KlopfensteinProfile`.
57
+ Klopfenstein is the linearised-Riccati *design* solution: it defines a shape worth
58
+ simulating and is not a reference the simulator is checked against.
59
+
60
+ ### Targets are dotted paths; the base tree is untouched
61
+
62
+ `ProfiledLine` holds the base line and a mapping from a **dotted path** naming a
63
+ `Param` on that base to the profile driving it. The base tree is left exactly as
64
+ built. Profiles are not substituted into it, and not declared as model fields.
65
+
66
+ Targets are exact dotted paths only — `w`, `substrate.dielectric.ep_r` — not
67
+ globs, sequences or callables. A glob matching three parameters would silently
68
+ create three independent profiles that happen to share a shape object, which is
69
+ never what the user meant.
70
+
71
+ `length` is not profilable and is rejected. It is the base's own parameter and it
72
+ is the **total** length; `ProfiledLine` divides by `n` internally and the user
73
+ never sees a per-section length. `ProfiledLine` has no `length` field, exposing
74
+ the base's as a read-only forwarding property.
75
+
76
+ A profiled target's value on the base is discarded, and the parameter is removed
77
+ from the profiled line's parameter set, replaced by the profile's coefficients.
78
+ Passing a value explicitly for a target that is also profiled raises: silently
79
+ discarded input is how an afternoon is lost. A field default that is discarded
80
+ does not raise, because the user did not type it.
81
+
82
+ Coefficients are named under the target: `w.start`, `w.end`,
83
+ `substrate.dielectric.ep_r.start` — the name the parameter would have had, with
84
+ the coefficient below it. `ProfiledLine` names them itself rather than letting
85
+ the mapping's keys be named as dict keys, so a non-identifier path does not leak
86
+ a bracket form into parameter names. Globs such as `'w.*'` and `'*.start'` then
87
+ both do the obvious thing, and the container's internal field layout never
88
+ appears in a parameter name.
89
+
90
+ ### Two ways in, one representation
91
+
92
+ The base may be given as a constructed line or as a class. A class is constructed
93
+ from the plain keywords, with a small reserved set (`n`, `extrapolate`, `name`,
94
+ `metadata`) always belonging to the container and a clear error on collision. A
95
+ user usually thinks "I want to profile a microstrip line", not "I want to profile
96
+ this particular line", and the class form lets them say so without constructing a
97
+ line whose width they are about to throw away. A base built this way is
98
+ deliberately unnamed, so parameter paths flatten to the container root.
99
+
100
+ Whichever form is used, one representation is stored: a base model plus the
101
+ target-to-profile mapping.
102
+
103
+ ### Evaluation: midpoint sections, extrapolated
104
+
105
+ `ProfiledLine` is a `TransmissionLine` and an `AbstractBuilder`. `build` returns a
106
+ `pmrf.models.RepeatedCascade`, so every representation delegates for free and no
107
+ line-specific evaluation code is written twice.
108
+
109
+ `RepeatedCascade` is public in its own right: one model, a mapping from dotted
110
+ names to arrays whose leading axis is the repeat axis, and everything else shared.
111
+ `n` is static by construction, from that leading axis. It is what `ProfiledLine`
112
+ produces after evaluating profiles at the midpoints, and it is also the answer to
113
+ #45's cascade of independently-parametrised lines. The reduction itself moves into
114
+ `pmrf.rf` and is shared with `Cascade`: the ABCD domain by default, reduced with a
115
+ sequential `lax.scan`.
116
+
117
+ Sampling each profile at the section midpoints and building exact hyperbolic ABCD
118
+ sections is the exponential midpoint rule, an order-2 Magnus integrator. It is
119
+ time-symmetric, so its error expands in **even** powers of `h`, and Richardson
120
+ extrapolation of `T(n)` and `T(2n)` as `(4·T(2n) − T(n))/3` is **O(h⁴)**, not
121
+ O(h³). Extrapolation is on by default (`extrapolate=True`), costing `3n` section
122
+ builds, all vmapped. `n` defaults to 64, which holds the per-section electrical
123
+ length inside the guard below well past any realistic taper. `extrapolate=False`
124
+ is the escape for users who want the plain midpoint result.
125
+
126
+ Extrapolation is applied to the complex ABCD entries, and the per-frequency error
127
+ estimate is exposed.
128
+
129
+ ### The guard is on by default and hard-errors
130
+
131
+ Two conditions are checked at evaluation: the Richardson error estimate against a
132
+ tolerance, and per-section electrical length against `βh ≲ 0.2 rad`. Both are
133
+ traced, so both use `eqx.error_if`, as the degenerate-DC guards in the line base
134
+ already do. A static `check=True` field turns them off.
135
+
136
+ The error estimate is taken relative to a matrix norm, never to `|S11|`:
137
+ normalising by a reflection coefficient makes the guard fire spuriously at every
138
+ reflection null.
139
+
140
+ Erroring rather than warning is deliberate. Silently wrong S-parameters feeding a
141
+ likelihood is the worst outcome available, and an under-resolved taper is exactly
142
+ the kind of numerical slack an optimiser finds and exploits. The accepted cost is
143
+ that a fit whose trial point wanders past the guard dies mid-run rather than being
144
+ penalised; `check=False` is the lever.
145
+
146
+ ### Introspection
147
+
148
+ `ProfiledLine.at(t)` returns the base model with the profiled targets substituted
149
+ at that `t` — the same substitution the evaluator performs at the midpoints,
150
+ exposed for one position. It composes: `line.at(0.5).zc_and_gammaL(f)`, and `t`
151
+ as an array vmaps to give the whole profile at once.
152
+
153
+ ## Rejected options
154
+
155
+ - **Profiles declared as model fields** (`w: Param | Profile`). A `prf.param`
156
+ field runs an `as_param` converter, so a profile node is coerced or rejected;
157
+ and where a foreign node is stored untouched (ADR-0003), the field's constraint
158
+ and scale deliberately do not apply. A `MicrostripLine` whose width validation
159
+ is silently bypassed by an intermediate shape object is worse than no feature.
160
+ - **Substituting the profile into the base tree in place**, replacing the `Param`
161
+ at the target. Naming would fall out for free, but it is the same validation
162
+ bypass at a different entry point.
163
+ - **A section function returning a concrete line** (`lambda t: MicrostripLine(w=...)`).
164
+ It couples the taper definition to the line family: an exponential taper for
165
+ microstrip width would have to be rewritten for stripline width and again for
166
+ coaxial diameter. This is what forced profiles to be plain `f(t) -> value`.
167
+ - **Authoring through `prf.update`** — construct a line, then update targets with
168
+ profile nodes. Unintuitive as the primary interface, and it reintroduces
169
+ in-tree substitution.
170
+ - **A profile as a `Model`.** It has no ports and no S-parameters. `pmrf.Module`
171
+ is the base for parameter-aware objects that are not models.
172
+ - **`ProfiledLine` as an `AbstractUniformLine`**, with `zc_and_gammaL` returning
173
+ the midpoint value. A characteristic impedance that is silently the value at
174
+ one position invites exactly the misuse the guard exists to prevent.
175
+ - **Relative profiles** (`w(t) = w_base × profile(t)`). It keeps the base value
176
+ meaningful, but requires a reference value at every target and makes a
177
+ dimensionless profile of permittivity mean something odd. Profiles are absolute.
178
+ - **A per-section `length`.** The base's `length` already carries a name, a prior
179
+ and possibly a joint lab prior with the geometry; dividing it internally keeps
180
+ all three and keeps a values dict from an earlier fit applicable.
181
+ - **`BatchedCascade` as the name.** "Batch" in ParamRF already means the
182
+ parameter batch dimension (`prf.batch_axes`, `prf.sweep`), and a
183
+ `RepeatedCascade` can itself be batched in that sense.
184
+ - **A `method` field and the Riccati path.** The old Riccati branch never ran. A
185
+ field with one legal value is an API promise bought before it is needed, and a
186
+ higher-order integrator would change `n` and `extrapolate` semantics too, so it
187
+ deserves its own design rather than a pre-cut slot.
188
+ - **Automatic `n` selection.** `n` must be static under `jit` — it is a vmap axis
189
+ and a scan length — while `Frequency.f` is a dynamic array leaf, so no rule that
190
+ reads the frequency band can run inside `jit`. A manual `refine()` for power
191
+ users is deferred to its own ticket.
192
+ - **Arbitrary-shape profiles** (polynomial, spline). They raise the smoothness
193
+ question the extrapolation depends on and belong in a later ticket.
194
+
195
+ ## Consequences
196
+
197
+ - The even-power error expansion, and therefore the O(h⁴) claim, requires the
198
+ profile to be C² in `t`. This is documented on `AbstractProfile`. A deliberate
199
+ kink is expressed by cascading two `ProfiledLine`s with `**`, not by a
200
+ discontinuous profile.
201
+ - Periodic uniform sections have an artificial Bragg stopband at `βh = π`. The
202
+ `βh ≲ 0.2` guard keeps evaluation far from it, but the mechanism is why the
203
+ guard is always on rather than advisory.
204
+ - Richardson measures the *discretisation* error only. It is blind to the model
205
+ error floor — mode conversion, radiation, quasi-TEM breakdown — so a small
206
+ estimate is not a statement that the answer is physically right.
207
+ - The convergence-order test is the slow one in the suite: it measures O(h²)
208
+ without extrapolation and O(h⁴) with it. Validation otherwise uses the
209
+ exponential taper's closed form, a diffrax RK reference, and exactness at
210
+ `n = 1`.
211
+ - `Cascade` and `RepeatedCascade` share one reduction in `pmrf.rf`; changes to the
212
+ cascade numerics now affect both.
213
+ - The commented-out `nonuniform.py` is deleted rather than ported. Per `AGENTS.md`
214
+ there is no back-compatibility obligation.
@@ -31,6 +31,7 @@ Working with Parameters
31
31
  pmrf.log_prior
32
32
  pmrf.update
33
33
  pmrf.tie
34
+ pmrf.resolve
34
35
  pmrf.derived
35
36
 
36
37
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: paramrf
3
- Version: 0.35.3
3
+ Version: 0.35.4
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
@@ -26,6 +26,7 @@ docs/_templates/autosummary/module.rst
26
26
  docs/adr/0001-line-modelling-architecture.md
27
27
  docs/adr/0002-parameter-api.md
28
28
  docs/adr/0003-derived-models.md
29
+ docs/adr/0004-profiled-lines.md
29
30
  docs/agents/domain.md
30
31
  docs/agents/issue-tracker.md
31
32
  docs/agents/triage-labels.md
@@ -132,6 +133,7 @@ pmrf/models/components/lines/microstrip.py
132
133
  pmrf/models/components/lines/nodal.py
133
134
  pmrf/models/components/lines/nonuniform.py
134
135
  pmrf/models/components/lines/planar.py
136
+ pmrf/models/components/lines/profiles.py
135
137
  pmrf/models/components/lines/stripline.py
136
138
  pmrf/models/composite/__init__.py
137
139
  pmrf/models/composite/nodal.py
@@ -161,6 +163,7 @@ pmrf/optimize/solvers/jaxopt.py
161
163
  pmrf/optimize/solvers/optimistix.py
162
164
  pmrf/optimize/solvers/scipy.py
163
165
  pmrf/rf/__init__.py
166
+ pmrf/rf/cascade.py
164
167
  pmrf/rf/conversions.py
165
168
  pmrf/rf/mna.py
166
169
  pmrf/utils/__init__.py
@@ -222,6 +225,8 @@ tests/test_models/test_lines.py
222
225
  tests/test_models/test_lines_skrf_matrix.py
223
226
  tests/test_models/test_lumped.py
224
227
  tests/test_models/test_nodal.py
228
+ tests/test_models/test_profiles.py
229
+ tests/test_models/test_repeated_cascade.py
225
230
  tests/test_models/test_sections.py
226
231
  tests/test_models/test_transformed.py
227
232
  tests/test_models/test_transformers.py
@@ -69,6 +69,7 @@ from pmrf.parameters import (
69
69
  log_prior as log_prior,
70
70
  update as update,
71
71
  tie as tie,
72
+ resolve as resolve,
72
73
  )
73
74
 
74
75
  from pmrf.serialization import (
@@ -160,6 +161,7 @@ __all__ = [
160
161
  "log_prior",
161
162
  "update",
162
163
  "tie",
164
+ "resolve",
163
165
  "unwrap",
164
166
  "unwrap_self",
165
167
  "derivative",
@@ -70,6 +70,13 @@ from pmrf.models.components.lines.base import (
70
70
  ImmittanceResult as ImmittanceResult,
71
71
  )
72
72
 
73
+ from pmrf.models.components.lines.profiles import (
74
+ AbstractProfile as AbstractProfile,
75
+ LinearProfile as LinearProfile,
76
+ ExponentialProfile as ExponentialProfile,
77
+ KlopfensteinProfile as KlopfensteinProfile,
78
+ )
79
+
73
80
  from pmrf.models.components.lines.nodal import (
74
81
  FloatingLine as FloatingLine,
75
82
  )
@@ -170,7 +177,10 @@ from pmrf.models.composite.interconnected.circuit.solvers.nodal import (
170
177
  )
171
178
 
172
179
  from pmrf.models.composite.interconnected.circuit.circuit import Circuit as Circuit
173
- from pmrf.models.composite.interconnected.cascade import Cascade as Cascade
180
+ from pmrf.models.composite.interconnected.cascade import (
181
+ Cascade as Cascade,
182
+ RepeatedCascade as RepeatedCascade,
183
+ )
174
184
  from pmrf.models.composite.interconnected.terminated import Terminated as Terminated
175
185
 
176
186
 
@@ -18,3 +18,9 @@ from pmrf.models.components.lines.stripline import (
18
18
  StriplineCrossSection as StriplineCrossSection,
19
19
  CohnCurrentDistribution as CohnCurrentDistribution,
20
20
  )
21
+ from pmrf.models.components.lines.profiles import (
22
+ AbstractProfile as AbstractProfile,
23
+ LinearProfile as LinearProfile,
24
+ ExponentialProfile as ExponentialProfile,
25
+ KlopfensteinProfile as KlopfensteinProfile,
26
+ )