Parameter Validation and Aliases#
This example demonstrates how parameter names are resolved via aliasing rules (braces, underscores, and spaces) and how the library validates that all required numeric parameters are present before backend compilation.
What you’ll learn#
The aliasing behavior for parameter names and how to supply values in
params.How missing parameters produce informative
DSLValidationErrormessages.
Source#
1# flake8: noqa
2"""
3Parameter validation, alias rules, and backend dispatch.
4
5This example walks through:
6- Collecting required parameters from IR (Hamiltonian + collapse ops).
7- Resolving aliases like omega_{c} vs omega_c.
8- Seeing early validation errors before touching a backend.
9- Dispatching to different backends after validation.
10
11Call functions individually; nothing runs on import.
12"""
13
14from __future__ import annotations
15
16import sys
17from pathlib import Path
18from typing import Dict, Iterable, Tuple
19
20ROOT = Path(__file__).resolve().parents[1]
21if str(ROOT) not in sys.path:
22 sys.path.insert(0, str(ROOT))
23
24from latex_parser.backend_utils import (
25 collect_parameter_names,
26 param_aliases,
27 resolve_param,
28 validate_required_params,
29)
30from latex_parser.compile_core import compile_model_core
31from latex_parser.dsl import HilbertConfig, QubitSpec
32from latex_parser.ir import latex_to_ir
33
34
35def list_aliases(symbol: str) -> None:
36 """
37 Show the alias spellings we try for a given parameter name.
38 """
39 print("Aliases for", symbol, ":", param_aliases(symbol))
40
41
42def collect_required_params(
43 H_latex: str, c_ops_latex: Iterable[str] | None, cfg: HilbertConfig
44) -> Tuple[set[str], set[str]]:
45 """
46 Return (required_symbols, time_names) for a Hamiltonian/c_ops pair.
47 """
48 ir = latex_to_ir(H_latex, cfg, t_name="t")
49 time_names = {"t"}
50 required = collect_parameter_names(ir, cfg, time_names)
51 for c in c_ops_latex or []:
52 ir_c = latex_to_ir(c, cfg, t_name="t")
53 required |= collect_parameter_names(ir_c, cfg, time_names)
54 return required, time_names
55
56
57def demonstrate_validation() -> None:
58 """
59 Run validation for a simple driven qubit with collapse.
60 """
61 cfg = HilbertConfig(qubits=[QubitSpec(label="q", index=1)], bosons=[], customs=[])
62 H = r"\frac{\omega_0}{2} \sigma_{z,1} + A \cos(\omega t) \sigma_{x,1}"
63 c_ops = [r"\sqrt{\gamma} \sigma_{-,1}"]
64 required, time_names = collect_required_params(H, c_ops, cfg)
65 print("Required symbols:", sorted(required))
66 params_good = {"omega_0": 2.0, "A": 0.5, "omega": 1.0, "gamma": 0.1}
67 validate_required_params(required, params_good, time_names)
68 params_missing = {"omega_0": 2.0}
69 try:
70 validate_required_params(required, params_missing, time_names)
71 except Exception as exc: # noqa: BLE001 - user-facing demo
72 print("Expected failure:", exc)
73
74
75def resolve_examples() -> None:
76 """
77 Demonstrate resolve_param on multiple spellings.
78 """
79 params = {"omega_c": 1.0, "kappa": 0.1, "A": 0.5}
80 for name in ["omega_{c}", "omega c", "kappa", "A"]:
81 key, val = resolve_param(name, params)
82 print(f"{name!r} resolved to key={key!r}, val={val}")
83
84
85def dispatch_after_validation() -> None:
86 """
87 Show full validation + dispatch using compile_model_core.
88 """
89 cfg = HilbertConfig(qubits=[QubitSpec(label="q", index=1)], bosons=[], customs=[])
90 H = r"\delta \sigma_{x,1}"
91 params = {"delta": 0.4}
92 model = compile_model_core(
93 backend="qutip",
94 H_latex=H,
95 params=params,
96 config=cfg,
97 c_ops_latex=None,
98 t_name="t",
99 time_symbols=None,
100 )
101 print("Dispatched model type:", type(model))
102
103
104def advanced_alias_patterns() -> None:
105 """
106 Show aliases with braces, spaces, and asterisks.
107 """
108 cases = ["omega_{c}", "omega_{ c }", "omega*c", "omega c", "{omega_c}"]
109 for case in cases:
110 print(case, "->", param_aliases(case))
111
112
113def guard_against_operator_symbols() -> None:
114 """
115 Confirm operator symbols are not treated as params during collection.
116 """
117 cfg = HilbertConfig(qubits=[QubitSpec(label="q", index=1)], bosons=[], customs=[])
118 H = r"\omega \sigma_{z,1} + \sigma_{x,1}"
119 ir = latex_to_ir(H, cfg, t_name="t")
120 req = collect_parameter_names(ir, cfg, {"t"})
121 print("Parameters collected (should exclude operators):", req)
122
123
124def validate_with_multiple_time_symbols() -> None:
125 """
126 Collect params when multiple time-like symbols are in use.
127 """
128 cfg = HilbertConfig(qubits=[QubitSpec(label="q", index=1)], bosons=[], customs=[])
129 H = r"A \cos(\omega t) \sigma_{x,1} + B \sin(\nu s) \sigma_{x,1}"
130 ir = latex_to_ir(H, cfg, t_name="t", time_symbols=("s",))
131 req = collect_parameter_names(ir, cfg, {"t", "s"})
132 print("Required with extra time symbols:", sorted(req))
133
134
135def validate_collapse_time_dependence() -> None:
136 """
137 Show param collection for time-dependent collapse operators.
138 """
139 cfg = HilbertConfig(qubits=[QubitSpec(label="q", index=1)], bosons=[], customs=[])
140 c_ops = [r"\sqrt{\gamma(t)} \sigma_{-,1}", r"\sqrt{\kappa} \sigma_{x,1}"]
141 req = set()
142 for c in c_ops:
143 ir = latex_to_ir(c, cfg, t_name="t")
144 req |= collect_parameter_names(ir, cfg, {"t"})
145 print("Collapse-required params:", sorted(req))
146
147
148def compare_backends_after_validation() -> None:
149 """
150 Dispatch the same LaTeX to multiple backends after validation.
151 """
152 cfg = HilbertConfig(qubits=[QubitSpec(label="q", index=1)], bosons=[], customs=[])
153 H = r"\delta \sigma_{x,1}"
154 params = {"delta": 0.4}
155 for backend in ("qutip", "numpy"):
156 model = compile_model_core(
157 backend=backend,
158 H_latex=H,
159 params=params,
160 config=cfg,
161 c_ops_latex=None,
162 t_name="t",
163 time_symbols=None,
164 )
165 print(f"Backend {backend} produced type:", type(model))
166
167
168def resolve_bulk(names: Iterable[str], params: Dict[str, complex]) -> None:
169 """
170 Resolve a list of names against a parameter dict.
171 """
172 for name in names:
173 try:
174 key, val = resolve_param(name, params)
175 print(f"{name!r} -> {key!r}={val}")
176 except Exception as exc: # noqa: BLE001 - user-facing demo
177 print(f"{name!r} -> error {exc}")
178
179
180def resolve_demonstration_suite() -> None:
181 """
182 Suite of resolve_param calls covering edge cases.
183 """
184 params = {"omega_c": 1.0, "g": 0.5, "alpha": 0.3}
185 names = ["omega_{c}", "omega*c", "omega c", "omega{c}", "g", "beta"]
186 resolve_bulk(names, params)
187
188
189def main() -> None:
190 list_aliases("omega_{c}")
191 demonstrate_validation()
192 resolve_examples()
193 dispatch_after_validation()
194 advanced_alias_patterns()
195 guard_against_operator_symbols()
196 validate_with_multiple_time_symbols()
197 validate_collapse_time_dependence()
198 compare_backends_after_validation()
199 resolve_demonstration_suite()
200 print("End of parameter validation tour.")
201
202
203# Notes for developers:
204# - The registry-based dispatch in compile_core already performs the
205# required-parameter collection shown above; this file exposes the same
206# helpers for learning and debugging.
207# - To plug in your own backend, register it via `register_backend` and the
208# same validation will run automatically.
209
210
211if __name__ == "__main__":
212 main()
Run#
python examples/example_param_validation_and_aliases.py
Notes#
Use this example when you encounter parameter lookup failures; it shows the candidate names the library will try.