diff --git a/sequencing/modes.py b/sequencing/modes.py index 02ecdfe..3c3b80b 100644 --- a/sequencing/modes.py +++ b/sequencing/modes.py @@ -706,7 +706,7 @@ def anharmonicity(self, alpha): self.kerr = alpha @capture_operation - def rotate(self, angle, phase, pulse_name=None, **kwargs): + def rotate(self, angle, phase, pulse_name=None, unitary=False, **kwargs): """Generate a pulse to rotate the qubit by a given angle about a given axis. @@ -717,10 +717,18 @@ def rotate(self, angle, phase, pulse_name=None, **kwargs): phase (float): Rotation axis relative to the x axis. pulse_name (optional, str): Name of the pulse to use. If None, will use ``self.default_pulse``. Default: None. + unitary (optional, bool): Whether to return the corresponding + unitary operator instead of executing the pulse. + Default: False. Returns: - Operation: The resulting ``Operation`` object. + ``qutip.Qobj`` or Operation: If ``unitary`` is True, returns + the unitary operator Rx(angle), otherwise returns the resulting + ``Operation`` object. """ + if unitary: + full_space = kwargs.get("full_space", True) + return self.Raxis(angle, phase, full_space=full_space) pulse_name = pulse_name or self.default_pulse pulse = getattr(self, pulse_name) # Assuming that default_pulse.amp = 1 corresponds to rotation of pi @@ -775,7 +783,7 @@ def rotate_y(self, angle, unitary=False, **kwargs): if unitary: full_space = kwargs.get("full_space", True) return self.Ry(angle, full_space=full_space) - return self.rotate(-angle, np.pi / 2, **kwargs) + return self.rotate(angle, np.pi / 2, **kwargs) @attr.s diff --git a/sequencing/pulses.py b/sequencing/pulses.py index f7df8cd..548240d 100644 --- a/sequencing/pulses.py +++ b/sequencing/pulses.py @@ -83,7 +83,7 @@ def array_pulse( else: i_wave = amp * i_wave + i_noise q_wave = amp * q_wave + q_noise - c_wave = (i_wave + 1j * q_wave) * np.exp(-1j * phase) + c_wave = (i_wave + 1j * q_wave) * np.exp(1j * phase) return c_wave diff --git a/sequencing/test/test_rotations.py b/sequencing/test/test_rotations.py new file mode 100644 index 0000000..524b2ab --- /dev/null +++ b/sequencing/test/test_rotations.py @@ -0,0 +1,147 @@ +import unittest +import numpy as np +import qutip +from sequencing import ( + Transmon, + Cavity, + System, + Sequence, + CTerm, + Operation, + capture_operation, + get_sequence, + sync, + delay, + delay_channels, + ket2dm, + ops2dms, +) +from sequencing.sequencing import ( + ValidatedList, + CompiledPulseSequence, + PulseSequence, + SyncOperation, + HamiltonianChannels, +) +from sequencing.calibration import tune_rabi + + +class TestSequenceVsUnitary(unittest.TestCase): + @classmethod + def setUpClass(cls): + qubit = Transmon("qubit", levels=2) + system = System("system", modes=[qubit]) + _ = tune_rabi(system, system.ground_state(), plot=False, verify=False) + cls.system = system + + def test_sequence_Rx(self): + + system = self.system + qubit = system.qubit + + init_state = system.ground_state() + + angles = np.linspace(-np.pi, np.pi, 11) + for theta in angles: + seq = Sequence(system) + qubit.rotate_x(theta) + sync() + unitary = qubit.rotate_x(theta, unitary=True) + + result = seq.run(init_state) + states = result.states + fidelity = qutip.fidelity(states[-1], qubit.Rx(theta) * init_state) ** 2 + self.assertGreater(fidelity, 0.999) + fidelity = qutip.fidelity(states[-1], unitary * init_state) ** 2 + self.assertGreater(fidelity, 0.999) + + def test_sequence_Ry(self): + + system = self.system + qubit = system.qubit + + init_state = system.ground_state() + + angles = np.linspace(-np.pi, np.pi, 11) + for theta in angles: + seq = Sequence(system) + qubit.rotate_y(theta) + sync() + unitary = qubit.rotate_y(theta, unitary=True) + + result = seq.run(init_state) + states = result.states + fidelity = qutip.fidelity(states[-1], qubit.Ry(theta) * init_state) ** 2 + self.assertGreater(fidelity, 0.999) + fidelity = qutip.fidelity(states[-1], unitary * init_state) ** 2 + self.assertGreater(fidelity, 0.999) + + def test_sequence_Raxis(self): + + system = self.system + qubit = system.qubit + + init_state = system.ground_state() + + angles = np.linspace(-np.pi, np.pi, 11) + for theta in angles: + for phi in angles: + seq = Sequence(system) + qubit.rotate(theta, phi) + sync() + unitary = qubit.rotate(theta, phi, unitary=True) + + result = seq.run(init_state) + states = result.states + fidelity = ( + qutip.fidelity(states[-1], qubit.Raxis(theta, phi) * init_state) + ** 2 + ) + self.assertGreater(fidelity, 0.999) + fidelity = qutip.fidelity(states[-1], unitary * init_state) ** 2 + self.assertGreater(fidelity, 0.999) + + def test_sequence_Raxis_vs_Rx_Ry(self): + + system = self.system + qubit = system.qubit + + init_state = system.ground_state() + + angles = np.linspace(-np.pi, np.pi, 11) + for theta in angles: + # Test Raxis vs. Rx + seq = Sequence(system) + qubit.rotate(theta, 0) + sync() + unitary = qubit.rotate(theta, 0, unitary=True) + + result = seq.run(init_state) + states = result.states + fidelity = qutip.fidelity(states[-1], qubit.Rx(theta) * init_state) ** 2 + self.assertGreater(fidelity, 0.999) + + fidelity = ( + qutip.fidelity(unitary * init_state, qubit.Rx(theta) * init_state) ** 2 + ) + self.assertGreater(fidelity, 0.999) + + # Test Raxis vs. Ry + seq = Sequence(system) + qubit.rotate(theta, np.pi / 2) + sync() + unitary = qubit.rotate(theta, np.pi / 2, unitary=True) + + result = seq.run(init_state) + states = result.states + fidelity = qutip.fidelity(states[-1], qubit.Ry(theta) * init_state) ** 2 + self.assertGreater(fidelity, 0.999) + + fidelity = ( + qutip.fidelity(unitary * init_state, qubit.Ry(theta) * init_state) ** 2 + ) + self.assertGreater(fidelity, 0.999) + + +if __name__ == "__main__": + unittest.main()