Skip to content

Commit f0dccf3

Browse files
authored
Feat/improved hugr engine (#227)
* Add complete Guppy-to-HUGR simulation support with all extension handlers
1 parent 92f6aa1 commit f0dccf3

File tree

12 files changed

+4147
-222
lines changed

12 files changed

+4147
-222
lines changed

.typos.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ typ = "typ"
1414
# These are operation names in HUGR/Guppy integer operations
1515
ine = "ine" # Integer not equal operation
1616
inot = "inot" # Integer bitwise NOT operation
17+
# Float comparison operation (float less-than-or-equal)
18+
fle = "fle"
19+
Fle = "Fle"
1720
# QuEST v4.1.0 uses "calcExpec" (not "calcExpect") in function names
1821
Expec = "Expec"
1922
# NumPy uses "arange" (array range), not "arrange"

crates/pecos-core/src/gate_type.rs

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,23 @@ pub enum GateType {
5555
// SYYdg = 56
5656
SZZ = 57,
5757
SZZdg = 58,
58-
// SWAP = 59
58+
SWAP = 59,
5959
// iSWAP = 60
6060
// G = 61
61+
/// Controlled-RZ gate (2 qubits, 1 angle parameter)
62+
CRZ = 70,
6163

6264
// RXX = 80
6365
// RYY = 81
6466
RZZ = 82,
6567
// RXXYYZZ
68+
/// Toffoli gate (CCX, 3 qubits)
69+
CCX = 90,
70+
71+
/// Square root of X gate (also known as V gate)
72+
SX = 24,
73+
/// Inverse of square root of X gate (also known as Vdg gate)
74+
SXdg = 25,
6675

6776
// MX = 100
6877
// MnX = 101
@@ -109,12 +118,17 @@ impl From<u8> for GateType {
109118
34 => GateType::Tdg,
110119
35 => GateType::U,
111120
36 => GateType::R1XY,
121+
24 => GateType::SX,
122+
25 => GateType::SXdg,
112123
50 => GateType::CX,
113124
51 => GateType::CY,
114125
52 => GateType::CZ,
115126
57 => GateType::SZZ,
116127
58 => GateType::SZZdg,
128+
59 => GateType::SWAP,
129+
70 => GateType::CRZ,
117130
82 => GateType::RZZ,
131+
90 => GateType::CCX,
118132
104 => GateType::Measure,
119133
105 => GateType::MeasureLeaked,
120134
106 => GateType::MeasureFree,
@@ -148,11 +162,15 @@ impl GateType {
148162
| GateType::H
149163
| GateType::T
150164
| GateType::Tdg
165+
| GateType::SX
166+
| GateType::SXdg
151167
| GateType::CX
152168
| GateType::CY
153169
| GateType::CZ
154170
| GateType::SZZ
155171
| GateType::SZZdg
172+
| GateType::SWAP
173+
| GateType::CCX
156174
| GateType::Measure
157175
| GateType::MeasureLeaked
158176
| GateType::MeasureFree
@@ -163,7 +181,12 @@ impl GateType {
163181
| GateType::QFree => 0,
164182

165183
// Gates with one parameter
166-
GateType::RX | GateType::RY | GateType::RZ | GateType::RZZ | GateType::Idle => 1,
184+
GateType::RX
185+
| GateType::RY
186+
| GateType::RZ
187+
| GateType::RZZ
188+
| GateType::CRZ
189+
| GateType::Idle => 1,
167190

168191
// Gates with two parameters
169192
GateType::R1XY => 2,
@@ -195,6 +218,8 @@ impl GateType {
195218
| GateType::RZ
196219
| GateType::T
197220
| GateType::Tdg
221+
| GateType::SX
222+
| GateType::SXdg
198223
| GateType::R1XY
199224
| GateType::U
200225
| GateType::Measure
@@ -213,7 +238,12 @@ impl GateType {
213238
| GateType::CZ
214239
| GateType::SZZ
215240
| GateType::SZZdg
241+
| GateType::SWAP
242+
| GateType::CRZ
216243
| GateType::RZZ => 2,
244+
245+
// Three-qubit gates
246+
GateType::CCX => 3,
217247
}
218248
}
219249

@@ -225,7 +255,7 @@ impl GateType {
225255
pub const fn angle_arity(self) -> usize {
226256
match self {
227257
// Rotation gates with angle parameters
228-
GateType::RX | GateType::RY | GateType::RZ | GateType::RZZ => 1,
258+
GateType::RX | GateType::RY | GateType::RZ | GateType::RZZ | GateType::CRZ => 1,
229259
GateType::R1XY => 2,
230260
GateType::U => 3,
231261
// All other gates have no angle parameters
@@ -278,12 +308,17 @@ impl fmt::Display for GateType {
278308
GateType::Tdg => write!(f, "Tdg"),
279309
GateType::U => write!(f, "U"),
280310
GateType::R1XY => write!(f, "R1XY"),
311+
GateType::SX => write!(f, "SX"),
312+
GateType::SXdg => write!(f, "SXdg"),
281313
GateType::CX => write!(f, "CX"),
282314
GateType::CY => write!(f, "CY"),
283315
GateType::CZ => write!(f, "CZ"),
284316
GateType::SZZ => write!(f, "SZZ"),
285317
GateType::SZZdg => write!(f, "SZZdg"),
318+
GateType::SWAP => write!(f, "SWAP"),
319+
GateType::CRZ => write!(f, "CRZ"),
286320
GateType::RZZ => write!(f, "RZZ"),
321+
GateType::CCX => write!(f, "CCX"),
287322
GateType::Measure => write!(f, "Measure"),
288323
GateType::MeasureLeaked => write!(f, "MeasureLeaked"),
289324
GateType::MeasureFree => write!(f, "MeasureFree"),

crates/pecos-engines/src/noise/biased_depolarizing.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ impl BiasedDepolarizingNoiseModel {
161161
| GateType::Z
162162
| GateType::SZ
163163
| GateType::SZdg
164+
| GateType::SX
165+
| GateType::SXdg
164166
| GateType::H
165167
| GateType::T
166168
| GateType::Tdg
@@ -178,11 +180,19 @@ impl BiasedDepolarizingNoiseModel {
178180
| GateType::CZ
179181
| GateType::RZZ
180182
| GateType::SZZ
181-
| GateType::SZZdg => {
183+
| GateType::SZZdg
184+
| GateType::SWAP
185+
| GateType::CRZ => {
182186
NoiseUtils::add_gate_to_builder(&mut builder, gate);
183187
trace!("Applying two-qubit gate with possible fault");
184188
self.apply_tq_faults(&mut builder, gate);
185189
}
190+
GateType::CCX => {
191+
NoiseUtils::add_gate_to_builder(&mut builder, gate);
192+
trace!("Applying three-qubit gate with possible fault");
193+
// Apply fault to each qubit pair (treat as three two-qubit interactions)
194+
self.apply_tq_faults(&mut builder, gate);
195+
}
186196
GateType::Measure | GateType::MeasureLeaked | GateType::MeasureFree => {
187197
trace!("Applying measurement. Will apply bias after engine returns results.");
188198
// we apply biased measurement after the engine

crates/pecos-engines/src/noise/depolarizing.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ impl DepolarizingNoiseModel {
167167
| GateType::Z
168168
| GateType::SZ
169169
| GateType::SZdg
170+
| GateType::SX
171+
| GateType::SXdg
170172
| GateType::H
171173
| GateType::T
172174
| GateType::Tdg
@@ -183,11 +185,19 @@ impl DepolarizingNoiseModel {
183185
| GateType::CZ
184186
| GateType::RZZ
185187
| GateType::SZZ
186-
| GateType::SZZdg => {
188+
| GateType::SZZdg
189+
| GateType::SWAP
190+
| GateType::CRZ => {
187191
NoiseUtils::add_gate_to_builder(&mut builder, gate);
188192
trace!("Applying two-qubit gate with possible fault");
189193
self.apply_tq_faults(&mut builder, gate);
190194
}
195+
GateType::CCX => {
196+
NoiseUtils::add_gate_to_builder(&mut builder, gate);
197+
trace!("Applying three-qubit gate with possible fault");
198+
// Apply fault to each qubit pair
199+
self.apply_tq_faults(&mut builder, gate);
200+
}
191201
GateType::RZ => {
192202
NoiseUtils::add_gate_to_builder(&mut builder, gate);
193203
}

crates/pecos-engines/src/quantum.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,18 @@ impl Engine for StateVecEngine {
165165
self.simulator.szdg(usize::from(*q));
166166
}
167167
}
168+
GateType::SX => {
169+
for q in &cmd.qubits {
170+
debug!("Processing SX gate on qubit {q:?}");
171+
self.simulator.sx(usize::from(*q));
172+
}
173+
}
174+
GateType::SXdg => {
175+
for q in &cmd.qubits {
176+
debug!("Processing SXdg gate on qubit {q:?}");
177+
self.simulator.sxdg(usize::from(*q));
178+
}
179+
}
168180
GateType::T => {
169181
for q in &cmd.qubits {
170182
debug!("Processing T gate on qubit {q:?}");
@@ -276,6 +288,86 @@ impl Engine for StateVecEngine {
276288
.szzdg(usize::from(qubits[0]), usize::from(qubits[1]));
277289
}
278290
}
291+
GateType::SWAP => {
292+
if cmd.qubits.len() % 2 != 0 {
293+
return Err(quantum_error(format!(
294+
"SWAP gate requires even number of qubits, got {}",
295+
cmd.qubits.len()
296+
)));
297+
}
298+
for qubits in cmd.qubits.chunks_exact(2) {
299+
debug!(
300+
"Processing SWAP gate on qubits {:?} and {:?}",
301+
qubits[0], qubits[1]
302+
);
303+
// SWAP = CX(0,1) CX(1,0) CX(0,1)
304+
let q0 = usize::from(qubits[0]);
305+
let q1 = usize::from(qubits[1]);
306+
self.simulator.cx(q0, q1);
307+
self.simulator.cx(q1, q0);
308+
self.simulator.cx(q0, q1);
309+
}
310+
}
311+
GateType::CRZ => {
312+
if cmd.qubits.len() % 2 != 0 {
313+
return Err(quantum_error(format!(
314+
"CRZ gate requires even number of qubits, got {}",
315+
cmd.qubits.len()
316+
)));
317+
}
318+
if cmd.angles.is_empty() {
319+
return Err(quantum_error("CRZ gate requires at least one angle"));
320+
}
321+
let angle = cmd.angles[0].to_radians();
322+
let half_angle = angle / 2.0;
323+
for qubits in cmd.qubits.chunks_exact(2) {
324+
debug!(
325+
"Processing CRZ gate on qubits {:?} and {:?} with angle {:?}",
326+
qubits[0], qubits[1], angle
327+
);
328+
// CRZ(θ) = Rz(θ/2) on target, CX, Rz(-θ/2) on target, CX
329+
let control = usize::from(qubits[0]);
330+
let target = usize::from(qubits[1]);
331+
self.simulator.rz(half_angle, target);
332+
self.simulator.cx(control, target);
333+
self.simulator.rz(-half_angle, target);
334+
self.simulator.cx(control, target);
335+
}
336+
}
337+
GateType::CCX => {
338+
if cmd.qubits.len() % 3 != 0 {
339+
return Err(quantum_error(format!(
340+
"CCX gate requires a multiple of 3 qubits, got {}",
341+
cmd.qubits.len()
342+
)));
343+
}
344+
for qubits in cmd.qubits.chunks_exact(3) {
345+
debug!(
346+
"Processing CCX gate with controls {:?}, {:?} and target {:?}",
347+
qubits[0], qubits[1], qubits[2]
348+
);
349+
// Toffoli decomposition into Clifford+T gates
350+
let c0 = usize::from(qubits[0]);
351+
let c1 = usize::from(qubits[1]);
352+
let target = usize::from(qubits[2]);
353+
// Standard decomposition (15 gates)
354+
self.simulator.h(target);
355+
self.simulator.cx(c1, target);
356+
self.simulator.tdg(target);
357+
self.simulator.cx(c0, target);
358+
self.simulator.t(target);
359+
self.simulator.cx(c1, target);
360+
self.simulator.tdg(target);
361+
self.simulator.cx(c0, target);
362+
self.simulator.t(c1);
363+
self.simulator.t(target);
364+
self.simulator.cx(c0, c1);
365+
self.simulator.h(target);
366+
self.simulator.t(c0);
367+
self.simulator.tdg(c1);
368+
self.simulator.cx(c0, c1);
369+
}
370+
}
279371
GateType::RX => {
280372
if !cmd.angles.is_empty() {
281373
let angle = cmd.angles[0].to_radians();

crates/pecos-experimental/src/hugr_executor.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,12 @@ where
304304
| GateType::R1XY
305305
| GateType::SZZ
306306
| GateType::SZZdg
307-
| GateType::RZZ => {
307+
| GateType::RZZ
308+
| GateType::SWAP
309+
| GateType::CRZ
310+
| GateType::CCX
311+
| GateType::SX
312+
| GateType::SXdg => {
308313
return Err(HugrExecutionError::UnsupportedGate {
309314
gate_type: gate.gate_type,
310315
gate_index: gate_idx,

0 commit comments

Comments
 (0)