1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
// This code is part of Cqlib.
//
// (C) Copyright China Telecom Quantum Group 2026
//
// This code is licensed under the Apache License, Version 2.0. You may
// obtain a copy of this license in the LICENSE.txt file in the root directory
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
//
// Any modifications or derivative works of this code must retain this
// copyright notice, and modified files need to carry a notice indicating
// that they have been altered from the originals.
//! Symbolic matrix representations for quantum gates and circuits.
//!
//! This module provides a symbolic counterpart of
//! [`circuit_to_matrix()`](fn@crate::circuit::circuit_to_matrix).
//! Instead of immediately evaluating gate parameters to concrete `f64` values,
//! it keeps unresolved parameters as [`Parameter`](crate::circuit::Parameter) expression trees inside a
//! dense symbolic matrix. The resulting matrix can later be evaluated under
//! different parameter bindings without rebuilding the circuit-level unitary.
//!
//! # Purpose
//!
//! The symbolic matrix layer is primarily intended for **small quantum circuits**
//! and compiler-internal analysis tasks, especially:
//!
//! - preserving symbolic parameters in parametric gates such as `RX(theta)`,
//! `RZ(phi)`, `U(theta, phi, lambda)`, and circuit-defined custom gates;
//! - comparing small rewrite patterns and replacement circuits;
//! - validating decomposition rules and peephole optimizations;
//! - debugging matrix conventions, qubit ordering, and global phase behavior;
//! - building reusable symbolic matrices for
//! [`CircuitGate`](crate::circuit::CircuitGate) and circuit-backed
//! [`UnitaryGate`](crate::circuit::gate::UnitaryGate) definitions.
//!
//! This module is **not** a large-scale symbolic simulator. A full dense
//! unitary matrix for `n` qubits contains `4^n` symbolic entries, and each entry
//! may itself contain a growing symbolic expression tree. In practice, this
//! backend should be used for small subcircuits, rule validation, and custom
//! gate definitions rather than full deep circuits.
//!
//! # Core types
//!
//! - [`SymbolicComplex`] stores one complex symbolic value as two independent
//! [`Parameter`](crate::circuit::Parameter) expressions: one for the real part and one for the imaginary
//! part.
//! - [`SymbolicMatrix`] is a dense `Array2<SymbolicComplex>` with the same
//! storage layout and matrix convention as the numerical circuit-matrix API.
//!
//! # Matrix construction
//!
//! [`circuit_to_symbolic_matrix`] walks through the circuit operations in order
//! and applies each instruction to an initially symbolic identity matrix.
//!
//! The implementation mirrors the numerical matrix path:
//!
//! - fixed standard gates may use the numeric fast path and are lifted into
//! symbolic values only at the matrix-element boundary;
//! - parametric standard gates are constructed directly as symbolic matrices;
//! - [`CircuitGate`](crate::circuit::gate::CircuitGate) and circuit-backed
//! [`UnitaryGate`](crate::circuit::gate::UnitaryGate) definitions preserve
//! symbolic parameters through simultaneous substitution;
//! - non-unitary operations such as measurement and reset return
//! [`CircuitError::NoMatrixRepresentation`](crate::circuit::CircuitError::NoMatrixRepresentation);
//! - control-flow operations return
//! [`CircuitError::InvalidOperation`](crate::circuit::CircuitError::InvalidOperation) because
//! they do not have a single unconditional unitary matrix.
//!
//! # Qubit ordering
//!
//! Internally, matrix application uses the library's little-endian bit layout:
//! qubit position `0` is the least-significant bit. The public
//! [`circuit_to_symbolic_matrix`] API accepts an optional `qubits_order` argument
//! to control the visible matrix order:
//!
//! - `None` sorts circuit qubits by qubit index;
//! - `Some(order)` uses the supplied qubit order and validates that it contains
//! exactly the same qubit set as the circuit.
//!
//! Gate-local matrices, such as standard gate matrices and numeric custom
//! unitary matrices, follow the usual gate-local ordering convention and are
//! converted before application where necessary.
//!
//! # Deferred evaluation
//!
//! Use [`evaluate_symbolic_matrix`] to bind concrete values to all free symbols
//! in a symbolic matrix.
//!
//! ```
//! use cqlib_core::circuit::{Circuit, Parameter, Qubit};
//! use cqlib_core::circuit::symbolic_matrix::{
//! circuit_to_symbolic_matrix,
//! evaluate_symbolic_matrix,
//! };
//! use std::collections::HashMap;
//!
//! let theta = Parameter::symbol("theta");
//! let mut circuit = Circuit::new(1);
//! circuit.rx(Qubit::new(0), theta).unwrap();
//!
//! let symbolic = circuit_to_symbolic_matrix(&circuit, None).unwrap();
//!
//! let mut bindings = HashMap::new();
//! bindings.insert("theta", std::f64::consts::FRAC_PI_2);
//!
//! let numeric = evaluate_symbolic_matrix(&symbolic, &Some(bindings)).unwrap();
//! assert_eq!(numeric.shape(), &[2, 2]);
//! ```
//!
//! # Circuit-gate parameter substitution
//!
//! Circuit-defined gates are expanded by first obtaining the symbolic matrix of
//! the inner frozen circuit and then simultaneously substituting the outer call
//! arguments for the inner symbols. Simultaneous substitution is important for
//! cases such as `a -> b` and `b -> a`, where sequential replacement would be
//! order-dependent.
//!
//! ```
//! use cqlib_core::circuit::{Circuit, Parameter, ParameterValue, Qubit};
//! use cqlib_core::circuit::symbolic_matrix::{
//! circuit_to_symbolic_matrix,
//! evaluate_symbolic_matrix,
//! };
//! use std::collections::HashMap;
//!
//! let mut inner = Circuit::new(1);
//! inner.rx(Qubit::new(0), Parameter::symbol("theta")).unwrap();
//! let gate = inner.to_gate("InnerRx").unwrap();
//!
//! let mut outer = Circuit::new(1);
//! outer.append(
//! gate,
//! [Qubit::new(0)],
//! [ParameterValue::from(Parameter::symbol("x") * 2.0)],
//! None,
//! ).unwrap();
//!
//! let symbolic = circuit_to_symbolic_matrix(&outer, None).unwrap();
//!
//! let mut bindings = HashMap::new();
//! bindings.insert("x", 0.25);
//! let numeric = evaluate_symbolic_matrix(&symbolic, &Some(bindings)).unwrap();
//!
//! assert_eq!(numeric.shape(), &[2, 2]);
//! ```
//!
//! # Global-phase equivalence
//!
//! Quantum rewrite rules often need equivalence up to a global phase rather
//! than exact matrix equality. [`symbolic_matrices_equivalent`]
//! and [`circuits_equivalent`] provide a conservative symbolic
//! check for this purpose.
//!
//! The checker first verifies that both matrices have the same zero structure.
//! It then chooses a nonzero pivot entry and checks that all corresponding
//! entries have the same symbolic ratio by cross-multiplication. This avoids
//! symbolic division and works well for many small rewrite-rule matrices.
//!
//! ```
//! use cqlib_core::circuit::{Circuit, Qubit};
//! use cqlib_core::circuit::symbolic_matrix::circuits_equivalent;
//!
//! let mut lhs = Circuit::new(1);
//! lhs.x(Qubit::new(0)).unwrap();
//! lhs.set_global_phase(std::f64::consts::PI.into());
//!
//! let mut rhs = Circuit::new(1);
//! rhs.x(Qubit::new(0)).unwrap();
//!
//! // X and -X represent the same quantum operation up to global phase.
//! assert!(circuits_equivalent(&lhs, &rhs, None).unwrap());
//! ```
//!
//! This check is intentionally conservative. It depends on the simplification
//! power of [`Parameter::simplify`](crate::circuit::Parameter::simplify), so failure to prove equivalence does not
//! necessarily mean the two circuits are mathematically inequivalent.
//!
//! # Gate-application fast paths
//!
//! Symbolic expression growth is the main performance risk in this module.
//! To reduce unnecessary expression construction, gate matrices are classified
//! before application:
//!
//! - **Diagonal gates** only scale affected rows. This avoids building
//! expressions such as `0 * x + phase * y`.
//! - **Permutation gates** move or scale rows according to a monomial matrix.
//! This is efficient for gates such as `X`, `CX`, `SWAP`, `CCX`, and other
//! permutation-like unitaries.
//! - **Dense gates** fall back to the general matrix-vector update path.
//!
//! The same classification exists for both symbolic gate matrices and numeric
//! gate matrices. Constant-parameter gates therefore benefit from the numeric
//! fast path, while symbolic gates still avoid unnecessary zero multiplications
//! whenever their structure is exactly diagonal or permutation-like.
//!
//! # Parallelism and safety
//!
//! Matrix application and matrix evaluation use **rayon** once the matrix element
//! count exceeds `PARALLEL_THRESHOLD_OPS`. Below that threshold, work stays on
//! the current thread to avoid scheduling overhead.
//!
//! Some hot paths use raw pointers internally to split mutable access across
//! rows. The safety invariant is that each parallel worker receives a disjoint
//! set of row indices. No two workers may write to the same matrix row.
//!
//! # Complexity and performance notes
//!
//! A dense unitary matrix for `n` qubits has dimension `2^n × 2^n`, so both
//! memory use and matrix-element work scale as `O(4^n)`. Symbolic entries are
//! significantly more expensive than `Complex64` entries because each arithmetic
//! operation can create new [`Parameter`](crate::circuit::Parameter) expression trees.
//!
//! Recommended usage:
//!
//! - prefer this module for small circuits and local rewrite patterns;
//! - reuse cached symbolic matrices for frozen subcircuits and circuit gates;
//! - prefer circuit-backed custom gates when symbolic parameters must be
//! preserved;
//! - avoid constructing full symbolic matrices for large application-level
//! circuits.
//!
//! # Limitations
//!
//! - Numeric parameterized [`UnitaryGate`](crate::circuit::gate::UnitaryGate)
//! factories take `&[f64]` and therefore cannot preserve unbound symbolic
//! parameters. Use a circuit-backed custom gate when symbolic preservation is
//! required.
//! - Equivalence checking is conservative and is not a complete theorem prover.
//! It does not replace SMT-based or proof-assistant-based verification for
//! all symbolic identities.
//! - Gate classification uses exact structural zero/one checks. Expressions
//! that are mathematically zero but not simplified to exact zero may fall back
//! to the dense path.
//! - The representation is dense; sparse symbolic matrices and expression-DAG
//! common-subexpression sharing are future optimization directions.
//!
//! # Parallelism
//!
//! Gate application is parallelised with **rayon** when the matrix element
//! count exceeds `PARALLEL_THRESHOLD_OPS` (2²⁰). Small circuits run on a
//! single thread to avoid scheduling overhead.
/// Minimum number of matrix elements that triggers parallel gate application
/// via rayon. Below this threshold the work is done on the calling thread to
/// avoid the overhead of thread-pool scheduling.
///
/// Kept in sync with the numerical circuit-to-matrix path.
pub const PARALLEL_THRESHOLD_OPS: usize = 1 << 20;
pub use ;
pub use ;
pub use ;