Skip to main content

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;