Skip to content

Commit b6baef6

Browse files
fix: Wrong decomposition of CnZ gate (#1948)
closes #1944 While fixing the issue, I also refactored a bit of `modify_tket_op` to avoid overlapping in the match conditions. I haven't added any tests since there is no easy way to test these changes (we need #1886 to properly test the `modify_tket_op` function)
1 parent f8309ac commit b6baef6

2 files changed

Lines changed: 49 additions & 61 deletions

File tree

‎tket/src/modifier/modifier_resolver.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1780,7 +1780,7 @@ mod tests {
17801780
.is_some_and(|function| function.func_name().starts_with("__modified__"))
17811781
})
17821782
.unwrap();
1783-
assert_eq!(state_order_edge_count(&h, modified_function), 26);
1783+
assert_eq!(state_order_edge_count(&h, modified_function), 16);
17841784
assert_matches!(h.validate(), Ok(()));
17851785
}
17861786

‎tket/src/modifier/modifier_resolver/tket_op_modify.rs‎

Lines changed: 48 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl<N: HugrNode> ModifierResolver<N> {
5050
let control = self.control_num();
5151
let dagger = self.modifiers.dagger;
5252

53-
// No modification is needed
53+
// No modification is needed.
5454
if control == 0 && !dagger {
5555
let new = new_fn.add_child_node(tket_op);
5656
let incoming = 0..new_fn.hugr().num_inputs(new);
@@ -59,10 +59,12 @@ impl<N: HugrNode> ModifierResolver<N> {
5959
}
6060
match tket_op {
6161
X | CX | Toffoli | Y | CY | Z | CZ | S | Sdg | T | Tdg | V | Vdg | H
62-
if (control == 0)
63-
|| (control < 3 && tket_op == X)
62+
if (control == 0 && dagger)
63+
|| (control == 1 && tket_op == X)
64+
|| (control == 2 && tket_op == X)
6465
|| (control == 1 && matches!(tket_op, CX | Y | Z)) =>
6566
{
67+
// The controlled or daggered G is itself a TketOp: emit it directly.
6668
let gate = self
6769
.modifiers
6870
.modified(tket_op)
@@ -81,7 +83,8 @@ impl<N: HugrNode> ModifierResolver<N> {
8183
let if_rev = control..(control + qubits);
8284
Ok(self.port_vector_dagger(new, incoming, outgoing, if_rev))
8385
}
84-
Rz | CRz | Rx | Ry if control == 0 || (control == 1 && tket_op == Rz) => {
86+
Rz | CRz | Rx | Ry if (control == 0 && dagger) || (control == 1 && tket_op == Rz) => {
87+
// The modified rotation is itself a TketOp; for G†(θ), negate θ.
8588
let qubits = if CRz == tket_op { 2 } else { 1 };
8689

8790
let new_op = self
@@ -137,7 +140,7 @@ impl<N: HugrNode> ModifierResolver<N> {
137140
}
138141
}
139142
H => {
140-
// H = X * Ry(pi/2).
143+
// CnH(cs,t) = CnRy(cs,t,π/2); CnX(cs,t). For H†, reverse and dagger.
141144
let (mut pv_ry, pv_x) = if !dagger {
142145
(
143146
self.modify_tket_op(op_node, Ry, new_fn, ancilla)?,
@@ -165,6 +168,7 @@ impl<N: HugrNode> ModifierResolver<N> {
165168
})
166169
}
167170
Rx => {
171+
// CnRx(cs,t,θ) = H(t); CnRz(cs,t,θ); H(t).
168172
let h1 = new_fn.add_child_node(H);
169173
let h2 = new_fn.add_child_node(H);
170174
let mut pv = self.modify_tket_op(op_node, Rz, new_fn, ancilla)?;
@@ -173,6 +177,8 @@ impl<N: HugrNode> ModifierResolver<N> {
173177
Ok(pv)
174178
}
175179
Ry | CY => {
180+
// CnRy(cs,t,θ) = Sdg(t); CnRx(cs,t,θ); S(t).
181+
// CnCY(cs,c,t) = Sdg(t); CnCX(cs,c,t); S(t).
176182
let (gate, targ) = match tket_op {
177183
Ry => (Rx, 0),
178184
CY => (CX, 1),
@@ -191,7 +197,7 @@ impl<N: HugrNode> ModifierResolver<N> {
191197
Ok(pv)
192198
}
193199
T | Tdg | S | Sdg | V | Vdg => {
194-
// op(t) = Phase(θ) * U(t, 2θ)
200+
// CnOp(cs,t) = CnU(cs,t,2θ); CnPhase(cs,θ).
195201
let Some((gate, angle)) = self.modifiers.rot_angle(tket_op) else {
196202
unreachable!()
197203
};
@@ -314,6 +320,7 @@ impl<N: HugrNode> ModifierResolver<N> {
314320
Ok(PortVector { incoming, outgoing })
315321
}
316322
CX | X if !ancilla.is_empty() => {
323+
// Reinterpret modifier controls as native controls, reducing CnX or Cn+1X to Cn-2+2X or Cn-1+2X.
317324
let c_num = if tket_op == X { 2 } else { 1 };
318325
let mut ctrls = vec![];
319326
for _ in 0..c_num {
@@ -443,7 +450,7 @@ impl<N: HugrNode> ModifierResolver<N> {
443450
Ok(PortVector { incoming, outgoing })
444451
}
445452
Rz | Y | Z => {
446-
// reduce Rz, Y, Z to CRz, CY, CZ
453+
// CnG(cs,c,t) = Cn-1(CG)(cs,c,t), for G = Rz, Y, or Z.
447454
let c_op = if tket_op == Rz {
448455
CRz
449456
} else if tket_op == Y {
@@ -476,11 +483,12 @@ impl<N: HugrNode> ModifierResolver<N> {
476483
Ok(pv)
477484
}
478485
CZ => {
479-
// reduce CZ to CRz(pi)
480-
let mut pv = self.modify_tket_op(op_node, CRz, new_fn, ancilla)?;
481-
let halfturn = new_fn.add_load_value(ConstRotation::new(1.0).unwrap());
482-
let dw = pv.incoming.remove(2);
483-
connect(new_fn, &dw, &halfturn.into())?;
486+
// Cn+1Z(cs,c,t) = H(t); Cn+1X(cs,c,t); H(t).
487+
let h1 = new_fn.add_child_node(H);
488+
let h2 = new_fn.add_child_node(H);
489+
let mut pv = self.modify_tket_op(op_node, CX, new_fn, ancilla)?;
490+
pv.incoming[1] = connect_by_num(new_fn, &pv.incoming[1], h1, 0);
491+
pv.outgoing[1] = connect_by_num(new_fn, &pv.outgoing[1], h2, 0);
484492
Ok(pv)
485493
}
486494
X | CX | Toffoli => {
@@ -595,55 +603,35 @@ impl<N: HugrNode> ModifierResolver<N> {
595603
impl CombinedModifier {
596604
/// If the modified operation can be represented as a TketOp,
597605
/// returns the modified operation, otherwise returns `None`.
606+
///
607+
/// Not all the cases are handled here, since we assume that
608+
/// unmodified operations are handled directly in `modify_tket_op`.
598609
fn modified(&self, op: TketOp) -> Option<TketOp> {
599-
match op {
600-
X if self.control == 0 => Some(X),
601-
X if self.control == 1 => Some(CX),
602-
X if self.control == 2 => Some(Toffoli),
603-
Y if self.control == 0 => Some(Y),
604-
Y if self.control == 1 => Some(CY),
605-
Z if self.control == 0 => Some(Z),
606-
Z if self.control == 1 => Some(CZ),
607-
CX if self.control == 0 => Some(CX),
608-
CX if self.control == 1 => Some(Toffoli),
609-
CY if self.control == 0 => Some(CY),
610-
CZ if self.control == 0 => Some(CZ),
611-
Toffoli if self.control == 0 => Some(Toffoli),
612-
H if self.control == 0 => Some(H),
613-
Rz if self.control == 0 => Some(Rz),
614-
Rz if self.control == 1 => Some(CRz),
615-
CRz if self.control == 0 => Some(CRz),
616-
Rx if self.control == 0 => Some(Rx),
617-
Ry if self.control == 0 => Some(Ry),
618-
T if self.control == 0 => match self.dagger {
619-
false => Some(T),
620-
true => Some(Tdg),
621-
},
622-
Tdg if self.control == 0 => match self.dagger {
623-
false => Some(Tdg),
624-
true => Some(T),
625-
},
626-
S if self.control == 0 => match self.dagger {
627-
false => Some(S),
628-
true => Some(Sdg),
629-
},
630-
Sdg if self.control == 0 => match self.dagger {
631-
false => Some(Sdg),
632-
true => Some(S),
633-
},
634-
V if self.control == 0 => match self.dagger {
635-
false => Some(V),
636-
true => Some(Vdg),
637-
},
638-
Vdg if self.control == 0 => match self.dagger {
639-
false => Some(Vdg),
640-
true => Some(V),
641-
},
642-
Measure | MeasureFree | QAlloc | TryQAlloc | QFree | Reset
643-
if self.control == 0 && !self.dagger =>
644-
{
645-
Some(op)
646-
}
610+
match (op, self.control, self.dagger) {
611+
(X, 0, _) => Some(X),
612+
(X, 1, _) => Some(CX),
613+
(X, 2, _) => Some(Toffoli),
614+
(Y, 0, _) => Some(Y),
615+
(Y, 1, _) => Some(CY),
616+
(Z, 0, _) => Some(Z),
617+
(Z, 1, _) => Some(CZ),
618+
(CX, 0, _) => Some(CX),
619+
(CX, 1, _) => Some(Toffoli),
620+
(CY, 0, _) => Some(CY),
621+
(CZ, 0, _) => Some(CZ),
622+
(Toffoli, 0, _) => Some(Toffoli),
623+
(H, 0, _) => Some(H),
624+
(Rz, 0, _) => Some(Rz),
625+
(Rz, 1, _) => Some(CRz),
626+
(CRz, 0, _) => Some(CRz),
627+
(Rx, 0, _) => Some(Rx),
628+
(Ry, 0, _) => Some(Ry),
629+
(T, 0, true) => Some(Tdg),
630+
(Tdg, 0, true) => Some(T),
631+
(S, 0, true) => Some(Sdg),
632+
(Sdg, 0, true) => Some(S),
633+
(V, 0, true) => Some(Vdg),
634+
(Vdg, 0, true) => Some(V),
647635
_ => None,
648636
}
649637
}

0 commit comments

Comments
 (0)