Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions crates/pecos-core/src/gate_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub enum GateType {
RXXRYYRZZ = 83,
/// General 2-qubit unitary via KAK decomposition
U2q = 84,
/// exp(-i theta/2 P(phi) tensor P(phi)), where P(phi) = cos(phi) X + sin(phi) Y.
RXYXY2Q = 85,
/// Toffoli gate (CCX, 3 qubits)
CCX = 90,

Expand Down Expand Up @@ -462,6 +464,7 @@ impl TryFrom<u8> for GateType {
82 => GateType::RZZ,
83 => GateType::RXXRYYRZZ,
84 => GateType::U2q,
85 => GateType::RXYXY2Q,
90 => GateType::CCX,
100 => GateType::MX,
104 => GateType::MZ,
Expand Down Expand Up @@ -646,7 +649,7 @@ impl GateType {
| GateType::Idle => 1,

// Gates with two parameters
GateType::RXY1Q => 2,
GateType::RXY1Q | GateType::RXYXY2Q => 2,

// Gates with three parameters
GateType::U | GateType::RXXRYYRZZ => 3,
Expand Down Expand Up @@ -722,7 +725,8 @@ impl GateType {
| GateType::RYY
| GateType::RZZ
| GateType::RXXRYYRZZ
| GateType::U2q => 2,
| GateType::U2q
| GateType::RXYXY2Q => 2,

// Three-qubit gates
GateType::CCX => 3,
Expand Down Expand Up @@ -766,7 +770,7 @@ impl GateType {
| GateType::RXX
| GateType::RYY
| GateType::RZZ => 1,
GateType::RXY1Q => 2,
GateType::RXY1Q | GateType::RXYXY2Q => 2,
GateType::U | GateType::RXXRYYRZZ => 3,
GateType::U2q => 15,
// All other gates have no angle parameters
Expand Down Expand Up @@ -825,6 +829,7 @@ impl fmt::Display for GateType {
GateType::Tdg => write!(f, "Tdg"),
GateType::U => write!(f, "U"),
GateType::RXY1Q => write!(f, "RXY1Q"),
GateType::RXYXY2Q => write!(f, "RXYXY2Q"),
GateType::CX => write!(f, "CX"),
GateType::CY => write!(f, "CY"),
GateType::CZ => write!(f, "CZ"),
Expand Down Expand Up @@ -896,6 +901,7 @@ impl std::str::FromStr for GateType {
"RY" => Ok(GateType::RY),
"RZ" => Ok(GateType::RZ),
"RXY1Q" | "R1XY" => Ok(GateType::RXY1Q),
"RXYXY2Q" => Ok(GateType::RXYXY2Q),
"U" => Ok(GateType::U),
"CX" | "CNOT" => Ok(GateType::CX),
"CY" => Ok(GateType::CY),
Expand Down
14 changes: 14 additions & 0 deletions crates/pecos-core/src/gates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,20 @@ impl Gate {
)
}

/// Create RXYXY2Q(theta, phi) gates on qubit pairs.
#[must_use]
pub fn rxyxy2q(
theta: Angle64,
phi: Angle64,
pairs: &[(impl Into<QubitId> + Copy, impl Into<QubitId> + Copy)],
) -> Self {
Self::with_angles(
GateType::RXYXY2Q,
smallvec::smallvec![theta, phi],
Self::flatten_qubit_pairs(pairs),
)
}

/// Create U gate on multiple qubits
#[must_use]
pub fn u(
Expand Down
14 changes: 14 additions & 0 deletions crates/pecos-engines/src/byte_message/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,20 @@ impl ByteMessageBuilder {
self.add_gate_parts(GateType::RXY1Q, qubits, &[theta, phi], &[])
}

/// Add RXYXY2Q(theta, phi) gates, with one shared XY-plane axis on both qubits.
pub fn rxyxy2q(&mut self, theta: Angle64, phi: Angle64, pairs: &[(usize, usize)]) -> &mut Self {
if let [(q0, q1)] = pairs {
return self.add_two_qubit_gate_parts(GateType::RXYXY2Q, *q0, *q1, &[theta, phi], &[]);
}
self.add_gate_parts_from_usizes(
GateType::RXYXY2Q,
pairs.len() * 2,
pairs.iter().copied().flat_map(|(q0, q1)| [q0, q1]),
&[theta, phi],
&[],
)
}

/// Add a U gate
pub fn u(
&mut self,
Expand Down
1 change: 1 addition & 0 deletions crates/pecos-engines/src/noise/biased_depolarizing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ impl BiasedDepolarizingNoiseModel {
| GateType::SZZ
| GateType::SZZdg
| GateType::SWAP
| GateType::RXYXY2Q
| GateType::RXX
| GateType::RYY
| GateType::RZZ
Expand Down
1 change: 1 addition & 0 deletions crates/pecos-engines/src/noise/depolarizing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ impl DepolarizingNoiseModel {
| GateType::SZZ
| GateType::SZZdg
| GateType::SWAP
| GateType::RXYXY2Q
| GateType::RXX
| GateType::RYY
| GateType::RZZ
Expand Down
22 changes: 22 additions & 0 deletions crates/pecos-engines/src/quantum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,13 @@ fn process_clifford_message<S: CliffordGateable + CliffordRotation + QuantumSimu
};
result.map_err(PecosError::Processing)?;
}
GateType::RXYXY2Q => {
with_flat_pairs(&cmd.qubits, &mut pair_scratch, |pairs| {
sim.try_rxyxy2q(cmd.angles[0], cmd.angles[1], pairs)
.map(|_| ())
})
.map_err(PecosError::Processing)?;
}
GateType::RXY1Q => {
sim.try_rxy1q(cmd.angles[0], cmd.angles[1], &cmd.qubits)
.map_err(PecosError::Processing)?;
Expand Down Expand Up @@ -530,6 +537,11 @@ fn process_general_message<
sim.ryy(cmd.angles[0], pairs);
});
}
GateType::RXYXY2Q => {
with_flat_pairs(&cmd.qubits, &mut pair_scratch, |pairs| {
sim.rxyxy2q(cmd.angles[0], cmd.angles[1], pairs);
});
}
GateType::RXY1Q => {
sim.rxy1q(cmd.angles[0], cmd.angles[1], &cmd.qubits);
}
Expand Down Expand Up @@ -1112,6 +1124,16 @@ where
);
self.simulator.rz(angle, &cmd.qubits);
}
GateType::RXYXY2Q => {
if cmd.qubits.len() % 2 != 0 {
return Err(quantum_error(format!(
"RXYXY2Q gate requires even number of qubits, got {}",
cmd.qubits.len()
)));
}
let pairs = flat_to_pairs(&cmd.qubits);
self.simulator.rxyxy2q(cmd.angles[0], cmd.angles[1], &pairs);
}
GateType::RXY1Q => {
let theta = cmd.angles[0];
let phi = cmd.angles[1];
Expand Down
3 changes: 3 additions & 0 deletions crates/pecos-qasm/includes/pecos.inc
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ gate syydg a,b { SYYDG a,b; }
gate szz a,b { SZZ a,b; }
gate szzdg a,b { SZZDG a,b; }

// XY-plane two-qubit rotation: exp(-i theta/2 (XY_phi x XY_phi))
gate rxyxy2q(theta,phi) a,b { RXYXY2Q(theta,phi) a,b; }

// --- Measurement ---
// Note: Measurement is a built-in operation in QASM
// measure q -> c;
14 changes: 14 additions & 0 deletions crates/pecos-qasm/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,19 @@ impl QASMEngine {
);
}
}
GateType::RXYXY2Q => {
let theta = params[0];
let phi = params[1];
for chunk in qubits.chunks(2) {
if chunk.len() == 2 {
self.message_builder.rxyxy2q(
Angle64::from_radians(theta),
Angle64::from_radians(phi),
&[(chunk[0], chunk[1])],
);
}
}
}
GateType::U => {
let theta = params[0];
let phi = params[1];
Expand Down Expand Up @@ -701,6 +714,7 @@ impl QASMEngine {
| GateType::RXX
| GateType::RYY
| GateType::RZZ
| GateType::RXYXY2Q
| GateType::RXXRYYRZZ
| GateType::U2q
| GateType::RXY1Q
Expand Down
6 changes: 6 additions & 0 deletions crates/pecos-qasm/src/parser/native_gates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub fn parse_native_gate(name: &str) -> Option<CoreGateType> {
"RX" => Some(CoreGateType::RX),
"RY" => Some(CoreGateType::RY),
"RZZ" => Some(CoreGateType::RZZ),
"RXYXY2Q" => Some(CoreGateType::RXYXY2Q),
"RXY1Q" | "R1XY" => Some(CoreGateType::RXY1Q),
"U" => Some(CoreGateType::U),
_ => None,
Expand Down Expand Up @@ -89,6 +90,7 @@ mod tests {
| CoreGateType::Tdg
| CoreGateType::U
| CoreGateType::RXY1Q
| CoreGateType::RXYXY2Q
| CoreGateType::CX
| CoreGateType::CY
| CoreGateType::CZ
Expand Down Expand Up @@ -134,6 +136,10 @@ mod tests {
};
if is_qasm_native_gate(gate) {
assert_eq!(parse_native_gate(&gate.to_string()), Some(gate));
} else {
// A gate the engine cannot run must not parse as native either,
// or a program is accepted at parse time and rejected at run time.
assert_eq!(parse_native_gate(&gate.to_string()), None, "{gate}");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,3 +627,60 @@ fn qasm_u_s_gate() {
assert_eq!(value, 1, "Two u(0,0,pi/2) = Z, H*Z*H = X");
}
}

#[test]
fn rxyxy2q_spellings_parse_and_execute_identically() {
// Uppercase native and the pecos.inc alias. theta = pi about the YY axis
// maps |00> to |11>.
let programs = [
r"
OPENQASM 2.0;
qreg q[2];
creg c[2];
RXYXY2Q(pi, pi/2) q[0], q[1];
measure q -> c;
",
r#"
OPENQASM 2.0;
include "pecos.inc";
qreg q[2];
creg c[2];
rxyxy2q(pi, pi/2) q[0], q[1];
measure q -> c;
"#,
];

for qasm in programs {
let results = qasm_engine()
.program(Qasm::from_string(qasm))
.to_sim()
.seed(42)
.workers(1)
.run(4)
.unwrap();
for shot in &results.shots {
assert_eq!(shot.data.get("c").unwrap().as_u32(), Some(3), "{qasm}");
}
}

// Both spellings must reach the engine as the native gate carrying both
// angles, in order.
for qasm in programs {
let program = QASMParser::parse_str(qasm).unwrap();
let gate = program
.operations
.iter()
.find_map(|op| match op {
Operation::NativeGate(gate) if gate.gate_type == GateType::RXYXY2Q => Some(gate),
_ => None,
})
.unwrap();
assert_eq!(
gate.angles.as_slice(),
&[
pecos_core::Angle64::HALF_TURN,
pecos_core::Angle64::QUARTER_TURN
]
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,31 @@ fn rxx_matrix(theta: f64) -> Matrix {
])
}

/// `exp(-i theta/2 (P (x) P))` with `P = cos(phi) X + sin(phi) Y`. Written out
/// directly from `P|0> = e^{i phi}|1>` and `P|1> = e^{-i phi}|0>`, so the
/// `|00> <-> |11>` coupling carries `e^{+-2i phi}` and `|01> <-> |10>` none.
fn rxyxy2q_matrix(theta: f64, phi: f64) -> Matrix {
let cosine = complex((theta / 2.0).cos(), 0.0);
let coupling = complex(0.0, -(theta / 2.0).sin());
let twist = cis(2.0 * phi);
matrix([
[
cosine,
complex(0.0, 0.0),
complex(0.0, 0.0),
coupling * twist.conj(),
],
[complex(0.0, 0.0), cosine, coupling, complex(0.0, 0.0)],
[complex(0.0, 0.0), coupling, cosine, complex(0.0, 0.0)],
[
coupling * twist,
complex(0.0, 0.0),
complex(0.0, 0.0),
cosine,
],
])
}

/// Conventional two-qubit root: `((1+i) I + (1-i) P) / 2` for an involution P.
fn conventional_root(pauli: &Matrix) -> Matrix {
let a = complex(0.5, 0.5);
Expand Down Expand Up @@ -341,6 +366,7 @@ fn reference_matrix(name: &str, parameters: &[f64]) -> Matrix {
"cphase90" => controlled(&phase_matrix(PI / 2.0)),
"rzz" => rzz_matrix(parameters[0]),
"rxx" => rxx_matrix(parameters[0]),
"rxyxy2q" => rxyxy2q_matrix(parameters[0], parameters[1]),
"szz" | "ZZ" => szz_matrix(),
"szzdg" => conventional_root_dagger(&zz_pauli()),
"sxx" => conventional_root(&xx_pauli()),
Expand All @@ -364,6 +390,7 @@ fn has_non_periodic_theta(name: &str) -> bool {
| "cry"
| "crz"
| "rxx"
| "rxyxy2q"
| "rzz"
| "u"
| "u3"
Expand Down
1 change: 1 addition & 0 deletions crates/pecos-qis-ffi-types/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ pub enum QuantumOp {
// ZZ interaction
ZZ(usize, usize),
RZZ(f64, usize, usize),
RXYXY2Q(f64, f64, usize, usize), // theta, phi, qubit_1, qubit_2

// Measurement
Measure(usize, usize), // qubit, result_id
Expand Down
Loading
Loading