cqlib_core/circuit/circuit_param.rs
1// This code is part of Cqlib.
2//
3// (C) Copyright China Telecom Quantum Group 2026
4//
5// This code is licensed under the Apache License, Version 2.0. You may
6// obtain a copy of this license in the LICENSE.txt file in the root directory
7// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8//
9// Any modifications or derivative works of this code must retain this
10// copyright notice, and modified files need to carry a notice indicating
11// that they have been altered from the originals.
12
13//! # Circuit Parameters Module
14//!
15//! This module defines the types used to represent parameters within a circuit and its operations.
16//! It bridges the gap between high-level symbolic expressions ([`Parameter`]) and low-level
17//! storage ([`CircuitParam`]).
18//!
19//! ## Two Representations
20//!
21//! | Type | IR layer | Parameter form | Where used |
22//! |------|----------|---------------|------------|
23//! | [`ParameterValue`] | Construction (pre-insertion) | Symbolic or fixed | [`ValueOperation`](super::ValueOperation), gate builders |
24//! | [`CircuitParam`] | Storage (post-insertion) | Index into parameter table or fixed | [`Operation`](super::Operation), circuit internals |
25//!
26//! [`ParameterValue`] is the user-facing type accepted by gate convenience methods
27//! (`Circuit::rx`, `Circuit::u`, etc.) and value-level IR builders. Fixed `f64`
28//! values and symbolic [`Parameter`] objects are both accepted interchangeably.
29//!
30//! On insertion via [`Circuit::append`](super::circuit_impl::Circuit::append),
31//! symbolic parameters are interned into the circuit's parameter table and
32//! replaced with [`CircuitParam::Index`]. Fixed parameters are validated to be
33//! finite and stored directly as [`CircuitParam::Fixed`].
34
35use super::parameter::Parameter;
36
37/// Represents a resolved parameter stored efficiently within a [`Circuit`](crate::circuit::Circuit).
38///
39/// This enum is designed for memory efficiency in the circuit's operation list.
40/// Instead of storing full symbolic expressions in every operation, we either store a raw `f64`
41/// (for fixed values) or an index into the circuit's centralized parameter table.
42#[derive(Debug, Clone)]
43pub enum CircuitParam {
44 /// An index pointing to the `parameters` list in the parent [`Circuit`](crate::circuit::Circuit).
45 /// This represents a symbolic or shared parameter.
46 Index(u32),
47 /// A concrete, fixed floating-point value.
48 Fixed(f64),
49}
50
51impl PartialEq for CircuitParam {
52 /// Compares the circuit-local storage form: two parameters are equal only
53 /// when both are fixed with the same value, or both are indices into the
54 /// same parameter-table position. Indices from different circuits compare
55 /// by table position, not by the parameter they resolve to.
56 fn eq(&self, other: &Self) -> bool {
57 match (self, other) {
58 (Self::Index(lhs), Self::Index(rhs)) => lhs == rhs,
59 (Self::Fixed(lhs), Self::Fixed(rhs)) => lhs == rhs,
60 _ => false,
61 }
62 }
63}
64
65impl From<f64> for CircuitParam {
66 fn from(v: f64) -> Self {
67 Self::Fixed(v)
68 }
69}
70
71/// A flexible input type for specifying parameters when constructing circuit operations.
72///
73/// This enum allows users to pass either raw numbers or symbolic `Parameter` objects
74/// to gate builders (like `rx`, `ry`, `u`).
75///
76/// # Examples
77///
78/// ```rust
79/// use cqlib_core::circuit::circuit_param::ParameterValue;
80/// use cqlib_core::circuit::Parameter;
81///
82/// // Create from float
83/// let val1: ParameterValue = 1.5.into();
84///
85/// // Create from integer
86/// let val2: ParameterValue = 42.into();
87///
88/// // Create from symbolic parameter
89/// let param = Parameter::symbol("theta");
90/// let val3: ParameterValue = param.into();
91/// ```
92#[derive(Debug, Clone)]
93pub enum ParameterValue {
94 /// A symbolic parameter (which may contain variables like "theta").
95 Param(Parameter),
96 /// A fixed floating-point value.
97 Fixed(f64),
98}
99
100impl ParameterValue {
101 /// Returns a scaled value, preserving the `Fixed` or `Param` variant.
102 ///
103 /// Fixed values use floating-point multiplication. Symbolic values use
104 /// expression multiplication and remain `Param`, even if the resulting
105 /// expression is constant (including multiplication by zero). In affine
106 /// expressions with unresolved symbols, coefficients are preserved without
107 /// tolerance pruning.
108 /// This method does not explicitly evaluate or canonicalize expressions.
109 /// Unlike converting a `Parameter` with `Into`, it does not fold constants
110 /// into `Fixed` values.
111 ///
112 /// Fixed multiplication follows IEEE 754, including signed zero and
113 /// overflow; validation of the resulting value remains the caller's
114 /// responsibility.
115 ///
116 /// # Panics
117 ///
118 /// Panics if `factor` is not finite, for either variant.
119 pub fn scaled(&self, factor: f64) -> Self {
120 assert!(factor.is_finite(), "parameter scale factor must be finite");
121 match self {
122 Self::Fixed(value) => Self::Fixed(value * factor),
123 Self::Param(parameter) => Self::Param(parameter.clone() * factor),
124 }
125 }
126}
127
128impl PartialEq for ParameterValue {
129 /// Compares the construction form: two values are equal only when both
130 /// are symbolic with structurally equal expressions, or both are fixed
131 /// with the same value. A symbolic parameter and its evaluated fixed
132 /// value compare unequal even when numerically identical.
133 fn eq(&self, other: &Self) -> bool {
134 match (self, other) {
135 (Self::Param(lhs), Self::Param(rhs)) => lhs == rhs,
136 (Self::Fixed(lhs), Self::Fixed(rhs)) => lhs == rhs,
137 _ => false,
138 }
139 }
140}
141
142impl From<f64> for ParameterValue {
143 fn from(v: f64) -> Self {
144 Self::Fixed(v)
145 }
146}
147
148impl From<i64> for ParameterValue {
149 fn from(v: i64) -> Self {
150 Self::Fixed(v as f64)
151 }
152}
153
154impl From<Parameter> for ParameterValue {
155 /// Converts a `Parameter` into a `ParameterValue`.
156 ///
157 /// It attempts to eagerly evaluate the parameter if it represents a known constant
158 /// (like `Pi`, `E`, or a simple number) to optimize storage.
159 fn from(para: Parameter) -> Self {
160 if let Ok(p) = para.evaluate(&None) {
161 Self::Fixed(p)
162 } else {
163 Self::Param(para)
164 }
165 }
166}
167
168impl From<&Parameter> for ParameterValue {
169 fn from(para: &Parameter) -> Self {
170 if let Ok(p) = para.evaluate(&None) {
171 Self::Fixed(p)
172 } else {
173 Self::Param(para.clone())
174 }
175 }
176}
177
178impl From<ParameterValue> for Parameter {
179 fn from(para: ParameterValue) -> Parameter {
180 match para {
181 ParameterValue::Param(p) => p,
182 ParameterValue::Fixed(f) => Parameter::from(f),
183 }
184 }
185}
186
187impl From<&ParameterValue> for Parameter {
188 fn from(para: &ParameterValue) -> Parameter {
189 match para {
190 ParameterValue::Param(p) => p.clone(),
191 ParameterValue::Fixed(f) => Parameter::from(*f),
192 }
193 }
194}
195
196impl From<&str> for ParameterValue {
197 /// Converts a string literal into a symbolic parameter.
198 ///
199 /// ```rust
200 /// use cqlib_core::circuit::circuit_param::ParameterValue;
201 ///
202 /// let theta: ParameterValue = "theta".into();
203 /// ```
204 fn from(para: &str) -> Self {
205 Parameter::symbol(para).into()
206 }
207}
208
209#[cfg(test)]
210#[path = "circuit_param_test.rs"]
211mod circuit_param_test;