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). This method
106 /// does not explicitly evaluate, simplify, or canonicalize expressions.
107 /// Unlike converting a `Parameter` with `Into`, it does not fold constants
108 /// into `Fixed` values.
109 ///
110 /// Fixed multiplication follows IEEE 754, including signed zero and
111 /// overflow; validation of the resulting value remains the caller's
112 /// responsibility.
113 ///
114 /// # Panics
115 ///
116 /// Panics if `factor` is not finite, for either variant.
117 pub fn scaled(&self, factor: f64) -> Self {
118 assert!(factor.is_finite(), "parameter scale factor must be finite");
119 match self {
120 Self::Fixed(value) => Self::Fixed(value * factor),
121 Self::Param(parameter) => Self::Param(parameter.clone() * factor),
122 }
123 }
124}
125
126impl PartialEq for ParameterValue {
127 /// Compares the construction form: two values are equal only when both
128 /// are symbolic with structurally equal expressions, or both are fixed
129 /// with the same value. A symbolic parameter and its evaluated fixed
130 /// value compare unequal even when numerically identical.
131 fn eq(&self, other: &Self) -> bool {
132 match (self, other) {
133 (Self::Param(lhs), Self::Param(rhs)) => lhs == rhs,
134 (Self::Fixed(lhs), Self::Fixed(rhs)) => lhs == rhs,
135 _ => false,
136 }
137 }
138}
139
140impl From<f64> for ParameterValue {
141 fn from(v: f64) -> Self {
142 Self::Fixed(v)
143 }
144}
145
146impl From<i64> for ParameterValue {
147 fn from(v: i64) -> Self {
148 Self::Fixed(v as f64)
149 }
150}
151
152impl From<Parameter> for ParameterValue {
153 /// Converts a `Parameter` into a `ParameterValue`.
154 ///
155 /// It attempts to eagerly evaluate the parameter if it represents a known constant
156 /// (like `Pi`, `E`, or a simple number) to optimize storage.
157 fn from(para: Parameter) -> Self {
158 if let Ok(p) = para.evaluate(&None) {
159 Self::Fixed(p)
160 } else {
161 Self::Param(para)
162 }
163 }
164}
165
166impl From<&Parameter> for ParameterValue {
167 fn from(para: &Parameter) -> Self {
168 if let Ok(p) = para.evaluate(&None) {
169 Self::Fixed(p)
170 } else {
171 Self::Param(para.clone())
172 }
173 }
174}
175
176impl From<ParameterValue> for Parameter {
177 fn from(para: ParameterValue) -> Parameter {
178 match para {
179 ParameterValue::Param(p) => p,
180 ParameterValue::Fixed(f) => Parameter::from(f),
181 }
182 }
183}
184
185impl From<&ParameterValue> for Parameter {
186 fn from(para: &ParameterValue) -> Parameter {
187 match para {
188 ParameterValue::Param(p) => p.clone(),
189 ParameterValue::Fixed(f) => Parameter::from(*f),
190 }
191 }
192}
193
194impl From<&str> for ParameterValue {
195 /// Converts a string literal into a symbolic parameter.
196 ///
197 /// ```rust
198 /// use cqlib_core::circuit::circuit_param::ParameterValue;
199 ///
200 /// let theta: ParameterValue = "theta".into();
201 /// ```
202 fn from(para: &str) -> Self {
203 Parameter::symbol(para).into()
204 }
205}
206
207#[cfg(test)]
208#[path = "circuit_param_test.rs"]
209mod circuit_param_test;