10static constexpr real PI = 3.141592653589793238462643383279502884;
18 [](
const auto&
a,
const auto&
b){ return a.second > b.second; });
22 if (
cnt == 0)
continue;
23 double p =
static_cast<double>(
cnt) /
shots;
27 std::printf(
"|%s> [%s] %5.1f%% (%d)\n",
28 label.c_str(),
bar.c_str(), p * 100.0,
cnt);
62 Op
op{Op::RX, q};
op.theta = theta; ops_.push_back(
op);
return *
this;
65 Op
op{Op::RY, q};
op.theta = theta; ops_.push_back(
op);
return *
this;
68 Op
op{Op::RZ, q};
op.theta = theta; ops_.push_back(
op);
return *
this;
71 Op
op{Op::P, q};
op.lambda = lambda; ops_.push_back(
op);
return *
this;
74 Op
op{Op::U, q};
op.theta = theta;
op.phi =
phi;
op.lambda = lambda;
75 ops_.push_back(
op);
return *
this;
81 Op
op{Op::CX,
ctrl,
tgt}; ops_.push_back(
op);
return *
this;
84 Op
op{Op::CY,
ctrl,
tgt}; ops_.push_back(
op);
return *
this;
87 Op
op{Op::CZ,
ctrl,
tgt}; ops_.push_back(
op);
return *
this;
91 ops_.push_back(
op);
return *
this;
94 Op
op{Op::SWAP, q0, q1}; ops_.push_back(
op);
return *
this;
100 Op
op{Op::CCX, c0,
c1,
tgt}; ops_.push_back(
op);
return *
this;
103 Op
op{Op::CSWAP,
ctrl, q0, q1}; ops_.push_back(
op);
return *
this;
109 Op
op{Op::Custom1Q, q};
110 op.g1 = G;
op.label = std::move(label);
111 ops_.push_back(
op);
return *
this;
114 Op
op{Op::Custom2Q, q0, q1};
115 op.g2 = G;
op.label = std::move(label);
116 ops_.push_back(
op);
return *
this;
122 using namespace quantum;
124 case Op::H: apply_1q(sv, n_qubits_, gate_h(),
op.q0);
break;
125 case Op::X: apply_1q(sv, n_qubits_, gate_x(),
op.q0);
break;
126 case Op::Y: apply_1q(sv, n_qubits_, gate_y(),
op.q0);
break;
127 case Op::Z: apply_1q(sv, n_qubits_, gate_z(),
op.q0);
break;
128 case Op::S: apply_1q(sv, n_qubits_, gate_s(),
op.q0);
break;
129 case Op::Sdg: apply_1q(sv, n_qubits_, gate_sdg(),
op.q0);
break;
130 case Op::T: apply_1q(sv, n_qubits_, gate_t(),
op.q0);
break;
131 case Op::Tdg: apply_1q(sv, n_qubits_, gate_tdg(),
op.q0);
break;
132 case Op::RX: apply_1q(sv, n_qubits_, gate_rx(
op.theta),
op.q0);
break;
133 case Op::RY: apply_1q(sv, n_qubits_, gate_ry(
op.theta),
op.q0);
break;
134 case Op::RZ: apply_1q(sv, n_qubits_, gate_rz(
op.theta),
op.q0);
break;
135 case Op::P: apply_1q(sv, n_qubits_, gate_p(
op.lambda),
op.q0);
break;
136 case Op::U: apply_1q(sv, n_qubits_,
137 gate_u(
op.theta,
op.phi,
op.lambda),
op.q0);
break;
139 case Op::CX: apply_cnot (sv, n_qubits_,
op.q0,
op.q1);
break;
140 case Op::CY: apply_cy (sv, n_qubits_,
op.q0,
op.q1);
break;
141 case Op::CZ: apply_cz (sv, n_qubits_,
op.q0,
op.q1);
break;
142 case Op::CP: apply_cp (sv, n_qubits_,
op.lambda,
op.q0,
op.q1);
break;
143 case Op::SWAP: apply_swap (sv, n_qubits_,
op.q0,
op.q1);
break;
145 case Op::CCX: apply_toffoli(sv, n_qubits_,
op.q0,
op.q1,
op.q2);
break;
146 case Op::CSWAP: apply_cswap (sv, n_qubits_,
op.q0,
op.q1,
op.q2);
break;
148 case Op::Custom1Q: apply_1q(sv, n_qubits_,
op.g1,
op.q0);
break;
149 case Op::Custom2Q: apply_2q(sv, n_qubits_,
op.g2,
op.q0,
op.q1);
break;
161 int limit = std::min(n,
static_cast<int>(ops_.size()));
163 apply_op(ops_[
i], sv);
171 std::mt19937 rng(
seed);
172 std::discrete_distribution<int> dist(
probs.begin(),
probs.end());
178 for (
int k = 0;
k < (1 << n_qubits_); ++
k) {
179 std::string label(n_qubits_,
'0');
180 for (
int b = 0;
b < n_qubits_; ++
b)
181 label[n_qubits_ - 1 -
b] = ((
k >>
b) & 1) ?
'1' :
'0';
184 for (
int i = 0;
i < shots; ++
i) {
186 std::string label(n_qubits_,
'0');
187 for (
int b = 0;
b < n_qubits_; ++
b)
188 label[n_qubits_ - 1 -
b] = ((
k >>
b) & 1) ?
'1' :
'0';
196std::pair<std::vector<int>,
int> Circuit::compute_layout()
const {
197 std::vector<int> last(n_qubits_, -1);
198 std::vector<int>
gate_col(ops_.size(), 0);
201 for (
int i = 0;
i < (
int)ops_.size(); ++
i) {
202 const auto&
op = ops_[
i];
204 if (
op.q1 >= 0) c = std::max(c, last[
op.q1]);
205 if (
op.q2 >= 0) c = std::max(c, last[
op.q2]);
207 total = std::max(total,
gate_col[
i] + 1);
222 std::snprintf(
buf,
sizeof(
buf),
"%.2f", theta);
223 return std::string(
buf);
226 std::vector<GateView>
out;
227 out.reserve(ops_.size());
229 for (
int i = 0;
i < (
int)ops_.size(); ++
i) {
230 const auto&
op = ops_[
i];
236 case Op::H: v.
label =
"H"; v.
kind = 0;
break;
237 case Op::X: v.
label =
"X"; v.
kind = 0;
break;
238 case Op::Y: v.
label =
"Y"; v.
kind = 0;
break;
239 case Op::Z: v.
label =
"Z"; v.
kind = 0;
break;
240 case Op::S: v.
label =
"S"; v.
kind = 0;
break;
241 case Op::Sdg: v.
label =
"S†"; v.
kind = 0;
break;
242 case Op::T: v.
label =
"T"; v.
kind = 0;
break;
243 case Op::Tdg: v.
label =
"T†"; v.
kind = 0;
break;
248 case Op::U: v.
label =
"U"; v.
kind = 0;
break;
249 case Op::CX: v.
label =
"CX"; v.
kind = 1;
break;
250 case Op::CY: v.
label =
"CY"; v.
kind = 1;
break;
251 case Op::CZ: v.
label =
"CZ"; v.
kind = 1;
break;
253 case Op::SWAP: v.
label =
"SWAP"; v.
kind = 2;
break;
254 case Op::CCX: v.
label =
"CCX"; v.
kind = 3;
break;
255 case Op::CSWAP:v.
label =
"CSWAP"; v.
kind = 3;
break;
256 case Op::Custom1Q: v.
label =
op.label; v.
kind = 0;
break;
257 case Op::Custom2Q: v.
label =
op.label; v.
kind = 1;
break;
269 for (
int q = 0; q < n_qubits_; ++q)
270 out +=
"q[" + std::to_string(q) +
"]: ---\n";
279 std::vector<std::vector<std::string>> grid(
289 grid[2*q + 1][col] =
" | ";
290 for (
int q =
q_lo + 1; q <
q_hi; ++q)
291 grid[2*q][col] =
"-+-";
294 for (
int i = 0;
i < (
int)ops_.size(); ++
i) {
295 const auto&
op = ops_[
i];
298 auto set = [&](
int q, std::string
sym) { grid[2*q][c] =
sym; };
301 case Op::H: set(
op.q0,
"-H-");
break;
302 case Op::X: set(
op.q0,
"-X-");
break;
303 case Op::Y: set(
op.q0,
"-Y-");
break;
304 case Op::Z: set(
op.q0,
"-Z-");
break;
305 case Op::S: set(
op.q0,
"-S-");
break;
306 case Op::Sdg: set(
op.q0,
"S†-");
break;
307 case Op::T: set(
op.q0,
"-T-");
break;
308 case Op::Tdg: set(
op.q0,
"T†-");
break;
309 case Op::RX: set(
op.q0,
"Rx-");
break;
310 case Op::RY: set(
op.q0,
"Ry-");
break;
311 case Op::RZ: set(
op.q0,
"Rz-");
break;
312 case Op::P: set(
op.q0,
"-P-");
break;
313 case Op::U: set(
op.q0,
"-U-");
break;
345 std::max({
op.q0,
op.q1,
op.q2}), c);
352 std::max({
op.q0,
op.q1,
op.q2}), c);
355 std::string
sym =
op.label.substr(0, 1);
356 set(
op.q0,
"-" +
sym +
"-");
361 set(
op.q1,
"-" +
op.label.substr(0,1) +
"-");
368 for (
int q = 0; q < n_qubits_; ++q) {
369 std::string
prefix =
"q[" + std::to_string(q) +
"]: ";
375 if (q < n_qubits_ - 1) {
378 out += grid[2*q + 1][c];
390 int n = std::max(q0, q1) + 1;
392 return c.
h(q0).
cx(q0, q1);
398 for (
int q = 1; q < n_qubits; ++q)
405 for (
int k = n_qubits - 1;
k >= 0; --
k) {
407 for (
int j =
k - 1;
j >= 0; --
j)
408 c.
cp(PI / (1 << (
k -
j)),
j,
k);
411 for (
int i = 0;
i < n_qubits / 2; ++
i)
412 c.
swap(
i, n_qubits - 1 -
i);
417 int N = 1 << n_qubits;
418 for (
int k = 0;
k < N; ++
k) {
421 std::string label(n_qubits,
'0');
422 for (
int b = 0;
b < n_qubits; ++
b)
423 label[n_qubits - 1 -
b] = ((
k >>
b) & 1) ?
'1' :
'0';
424 double p = std::norm(sv[
k]);
425 std::printf(
"|%s> %+.4f%+.4fi (%.1f%%)\n",
426 label.c_str(), sv[
k].real(), sv[
k].imag(), p * 100.0);
Fluent quantum circuit builder and statevector simulator.
Dense vector with optional GPU storage, templated over scalar type T.
Chainable quantum circuit builder.
std::vector< GateView > views() const
Gate descriptions with layout columns – for renderers.
std::string diagram() const
ASCII wire diagram (Unicode box-drawing characters)
Circuit & cx(int ctrl, int tgt)
CNOT.
Circuit & swap(int q0, int q1)
SWAP.
Circuit & cp(real lambda, int ctrl, int tgt)
Controlled phase.
Circuit & h(int q)
Hadamard.
Circuit & rz(real theta, int q)
R_z(theta) = e^{-ithetaZ/2}.
Circuit & rx(real theta, int q)
R_x(theta) = e^{-ithetaX/2}.
Circuit & s(int q)
Phase S = sqrtZ.
Circuit & ry(real theta, int q)
R_y(theta) = e^{-ithetaY/2}.
quantum::Statevector statevector() const
Run full circuit -> statevector (no measurement collapse)
Circuit & t(int q)
T = sqrtS.
quantum::Statevector statevector_at(int n) const
Apply first n gates only (0 = bare |0...0>). Useful for stepping through the circuit in a visualiser.
Circuit & z(int q)
Pauli-Z.
Circuit & p(real lambda, int q)
Phase P(lambda)
Circuit & x(int q)
Pauli-X (NOT)
Circuit & u(real theta, real phi, real lambda, int q)
General SU(2)
Circuit & cz(int ctrl, int tgt)
Controlled-Z.
Circuit & ccx(int c0, int c1, int tgt)
Toffoli (CCX)
void print() const
Print diagram to stdout.
Result run(int shots=1024, unsigned seed=42) const
Sample shots measurements from the ideal distribution. Uses pre-computed statevector probabilities – ...
Circuit & cy(int ctrl, int tgt)
Controlled-Y.
Circuit & cswap(int ctrl, int q0, int q1)
Fredkin (CSWAP)
Circuit & y(int q)
Pauli-Y.
Circuit & gate(const quantum::Gate &G, int q, std::string label="U")
std::array< cplx, 16 > Gate2Q
4x4 two-qubit gate stored row-major (16 entries)
Statevector zero_state(int n_qubits)
Returns |0...0> computational basis state for n_qubits qubits.
std::vector< real > probabilities(const Statevector &sv)
Measurement probability for each basis state: p_i = |a_i|^2.
std::array< cplx, 4 > Gate
2x2 single-qubit gate stored row-major: { G[0,0], G[0,1], G[1,0], G[1,1] }
constexpr real phi
Golden ratio.
Circuit ghz_state(int n_qubits)
n-qubit GHZ state (|00...0> + |11...1>)/sqrt2
constexpr T ipow(T x) noexcept
Compute x^N at compile time via repeated squaring.
Circuit qft_circuit(int n_qubits)
n-qubit Quantum Fourier Transform circuit Applied to |0...0> unless you prepend state-preparation gat...
void print_state(const quantum::Statevector &sv, int n_qubits, real threshold=1e-6)
Pretty-print a statevector, hiding amplitudes below threshold.
Circuit bell_pair(int q0=0, int q1=1)
Bell state |Phi+> = (|00> + |11>)/sqrt2 on qubits q0, q1.
Compact gate description used by visualisation code.
int q1
secondary qubit (-1 if 1Q)
int q2
tertiary qubit (-1 if not 3Q)
std::string label
e.g. "H", "CX", "RY(1.57)", "SWAP"
int q0
primary qubit (target for 1Q; control for 2/3Q)
Measurement outcome from Circuit::run()
void print() const
Print counts sorted by count (descending)
std::string most_likely() const
Basis label with the highest count.
quantum::Statevector sv
pre-measurement statevector
real probability(const std::string &label) const
Empirical probability for a given label (e.g. "11")
std::map< std::string, int > counts
basis label (e.g. "011") -> shot count