|
| 1 | +// This code is part of Qiskit. |
| 2 | +// |
| 3 | +// (C) Copyright IBM 2026 |
| 4 | +// |
| 5 | +// This code is licensed under the Apache License, Version 2.0. You may |
| 6 | +// obtain a copy of this license in the LICENSE.txt file in the root directory |
| 7 | +// of this source tree or at https://www.apache.org/licenses/LICENSE-2.0. |
| 8 | +// |
| 9 | +// Any modifications or derivative works of this code must retain this |
| 10 | +// copyright notice, and modified files need to carry a notice indicating |
| 11 | +// that they have been altered from the originals. |
| 12 | + |
| 13 | +use super::StandardGate; |
| 14 | +use crate::operations::{Operation, Param}; |
| 15 | +use qiskit_quantum_info::versor_u2::{VersorSU2, VersorU2, VersorU2Error}; |
| 16 | + |
| 17 | +use nalgebra::{Quaternion, Unit}; |
| 18 | +use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, FRAC_PI_8}; |
| 19 | + |
| 20 | +const COS_FRAC_PI_8: f64 = 0.9238795325112867; |
| 21 | +const SIN_FRAC_PI_8: f64 = 0.3826834323650898; |
| 22 | + |
| 23 | +/// Conversion logic of `StandardGate::versor_u2`. |
| 24 | +pub fn versor_u2(gate: StandardGate, params: &[Param]) -> Result<VersorU2, VersorU2Error> { |
| 25 | + debug_assert_eq!(params.len(), gate.num_params() as usize); |
| 26 | + match gate { |
| 27 | + StandardGate::GlobalPhase => { |
| 28 | + let &[Param::Float(phase)] = params else { |
| 29 | + return Err(VersorU2Error::Symbolic); |
| 30 | + }; |
| 31 | + Ok(VersorSU2::identity().with_phase(phase)) |
| 32 | + } |
| 33 | + StandardGate::H => { |
| 34 | + Ok( |
| 35 | + VersorSU2::from_quaternion_unchecked(0., -FRAC_1_SQRT_2, 0., -FRAC_1_SQRT_2) |
| 36 | + .with_phase(FRAC_PI_2), |
| 37 | + ) |
| 38 | + } |
| 39 | + StandardGate::I => Ok(VersorU2::identity()), |
| 40 | + StandardGate::X => { |
| 41 | + Ok(VersorSU2::from_quaternion_unchecked(0., 0., 0., -1.).with_phase(FRAC_PI_2)) |
| 42 | + } |
| 43 | + StandardGate::Y => { |
| 44 | + Ok(VersorSU2::from_quaternion_unchecked(0., 0., -1., 0.).with_phase(FRAC_PI_2)) |
| 45 | + } |
| 46 | + StandardGate::Z => { |
| 47 | + Ok(VersorSU2::from_quaternion_unchecked(0., -1., 0., 0.).with_phase(FRAC_PI_2)) |
| 48 | + } |
| 49 | + StandardGate::Phase | StandardGate::U1 => { |
| 50 | + let &[Param::Float(angle)] = params else { |
| 51 | + return Err(VersorU2Error::Symbolic); |
| 52 | + }; |
| 53 | + Ok(VersorSU2::from_rz(angle).with_phase(angle * 0.5)) |
| 54 | + } |
| 55 | + StandardGate::R => { |
| 56 | + let &[Param::Float(angle), Param::Float(axis)] = params else { |
| 57 | + return Err(VersorU2Error::Symbolic); |
| 58 | + }; |
| 59 | + let (sin_angle, cos_angle) = (angle * 0.5).sin_cos(); |
| 60 | + let (sin_axis, cos_axis) = axis.sin_cos(); |
| 61 | + Ok(VersorSU2::from_quaternion_unchecked( |
| 62 | + cos_angle, |
| 63 | + 0., |
| 64 | + -sin_axis * sin_angle, |
| 65 | + -cos_axis * sin_angle, |
| 66 | + ) |
| 67 | + .into()) |
| 68 | + } |
| 69 | + StandardGate::RX => { |
| 70 | + let &[Param::Float(angle)] = params else { |
| 71 | + return Err(VersorU2Error::Symbolic); |
| 72 | + }; |
| 73 | + Ok(VersorSU2::from_rx(angle).into()) |
| 74 | + } |
| 75 | + StandardGate::RY => { |
| 76 | + let &[Param::Float(angle)] = params else { |
| 77 | + return Err(VersorU2Error::Symbolic); |
| 78 | + }; |
| 79 | + Ok(VersorSU2::from_ry(angle).into()) |
| 80 | + } |
| 81 | + StandardGate::RZ => { |
| 82 | + let &[Param::Float(angle)] = params else { |
| 83 | + return Err(VersorU2Error::Symbolic); |
| 84 | + }; |
| 85 | + Ok(VersorSU2::from_rz(angle).into()) |
| 86 | + } |
| 87 | + StandardGate::S => { |
| 88 | + Ok( |
| 89 | + VersorSU2::from_quaternion_unchecked(FRAC_1_SQRT_2, -FRAC_1_SQRT_2, 0., 0.) |
| 90 | + .with_phase(FRAC_PI_4), |
| 91 | + ) |
| 92 | + } |
| 93 | + StandardGate::Sdg => { |
| 94 | + Ok( |
| 95 | + VersorSU2::from_quaternion_unchecked(FRAC_1_SQRT_2, FRAC_1_SQRT_2, 0., 0.) |
| 96 | + .with_phase(-FRAC_PI_4), |
| 97 | + ) |
| 98 | + } |
| 99 | + StandardGate::SX => { |
| 100 | + Ok( |
| 101 | + VersorSU2::from_quaternion_unchecked(FRAC_1_SQRT_2, 0., 0., -FRAC_1_SQRT_2) |
| 102 | + .with_phase(FRAC_PI_4), |
| 103 | + ) |
| 104 | + } |
| 105 | + StandardGate::SXdg => { |
| 106 | + Ok( |
| 107 | + VersorSU2::from_quaternion_unchecked(FRAC_1_SQRT_2, 0., 0., FRAC_1_SQRT_2) |
| 108 | + .with_phase(-FRAC_PI_4), |
| 109 | + ) |
| 110 | + } |
| 111 | + StandardGate::T => { |
| 112 | + Ok( |
| 113 | + VersorSU2::from_quaternion_unchecked(COS_FRAC_PI_8, -SIN_FRAC_PI_8, 0., 0.) |
| 114 | + .with_phase(FRAC_PI_8), |
| 115 | + ) |
| 116 | + } |
| 117 | + StandardGate::Tdg => { |
| 118 | + Ok( |
| 119 | + VersorSU2::from_quaternion_unchecked(COS_FRAC_PI_8, SIN_FRAC_PI_8, 0., 0.) |
| 120 | + .with_phase(-FRAC_PI_8), |
| 121 | + ) |
| 122 | + } |
| 123 | + StandardGate::U | StandardGate::U3 => { |
| 124 | + let &[Param::Float(theta), Param::Float(phi), Param::Float(lambda)] = params else { |
| 125 | + return Err(VersorU2Error::Symbolic); |
| 126 | + }; |
| 127 | + Ok( |
| 128 | + (VersorSU2::from_rz(phi) * VersorSU2::from_ry(theta) * VersorSU2::from_rz(lambda)) |
| 129 | + .with_phase((phi + lambda) * 0.5), |
| 130 | + ) |
| 131 | + } |
| 132 | + StandardGate::U2 => { |
| 133 | + let &[Param::Float(phi), Param::Float(lambda)] = params else { |
| 134 | + return Err(VersorU2Error::Symbolic); |
| 135 | + }; |
| 136 | + let (sin, cos) = (lambda * 0.5).sin_cos(); |
| 137 | + // The RY(pi/2).RZ(lambda) part of the decomposition, without the phase term. |
| 138 | + let ry_rz = VersorSU2(Unit::new_unchecked( |
| 139 | + FRAC_1_SQRT_2 * Quaternion::new(cos, -sin, -cos, -sin), |
| 140 | + )); |
| 141 | + Ok((VersorSU2::from_rz(phi) * ry_rz).with_phase((phi + lambda) * 0.5)) |
| 142 | + } |
| 143 | + _ => Err(VersorU2Error::MultiQubit), |
| 144 | + } |
| 145 | +} |
0 commit comments