def test_set_meas_levels(self): """Test setting of meas_levels.""" athens_backend = FakeAthens() athens_sim = PulseSimulator.from_backend(athens_backend) # test that a warning is thrown when meas_level 0 is attempted to be set with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") athens_sim.set_options(meas_levels=[0, 1, 2]) self.assertEqual(len(w), 1) self.assertTrue( 'Measurement level 0 not supported' in str(w[-1].message)) self.assertEqual(athens_sim.configuration().meas_levels, [1, 2]) self.assertTrue(athens_sim.configuration().meas_levels == [1, 2]) athens_sim.set_options(meas_levels=[2]) self.assertTrue(athens_sim.configuration().meas_levels == [2])
def test_from_backend(self): """Test that configuration, defaults, and properties are correclty imported.""" athens_backend = FakeAthens() athens_sim = PulseSimulator.from_backend(athens_backend) self.assertEqual(athens_backend.properties(), athens_sim.properties()) # check that configuration is correctly imported backend_dict = athens_backend.configuration().to_dict() sim_dict = athens_sim.configuration().to_dict() for key in sim_dict: if key == 'backend_name': self.assertEqual(sim_dict[key], 'pulse_simulator(fake_athens)') elif key == 'description': desc = 'A Pulse-based simulator configured from the backend: fake_athens' self.assertEqual(sim_dict[key], desc) elif key == 'simulator': self.assertTrue(sim_dict[key]) elif key == 'local': self.assertTrue(sim_dict[key]) elif key == 'parametric_pulses': self.assertEqual(sim_dict[key], []) else: self.assertEqual(sim_dict[key], backend_dict[key]) backend_dict = athens_backend.defaults().to_dict() sim_dict = athens_sim.defaults().to_dict() for key in sim_dict: if key == 'pulse_library': # need to compare pulse libraries directly due to containing dictionaries for idx, entry in enumerate(sim_dict[key]): for entry_key in entry: if entry_key == 'samples': self.assertTrue(np.array_equal(entry[entry_key], backend_dict[key][idx][entry_key])) else: self.assertTrue(entry[entry_key] == backend_dict[key][idx][entry_key]) else: self.assertEqual(sim_dict[key], backend_dict[key])
def test_validation_num_acquires(self): """Test that validation fails if 0 or >1 acquire is given in a schedule.""" test_sim = PulseSimulator.from_backend(FakeArmonk()) test_sim.set_options(meas_level=2, qubit_lo_freq=test_sim.defaults().qubit_freq_est, meas_return='single', shots=256) # check that too many acquires results in an error sched = self._1Q_schedule(num_acquires=2) try: test_sim.run(sched, validate=True).result() except AerError as error: self.assertTrue( 'does not support multiple Acquire' in error.message) # check that no acquires results in an error sched = self._1Q_schedule(num_acquires=0) try: test_sim.run(sched, validate=True).result() except AerError as error: self.assertTrue('requires at least one Acquire' in error.message)
def test_run_simulation_from_backend(self): """Construct from a backend and run a simulation.""" armonk_backend = FakeArmonk() # manually override parameters to insulate from future changes to FakeArmonk freq_est = 4.97e9 drive_est = 6.35e7 armonk_backend.defaults().qubit_freq_est = [freq_est] armonk_backend.configuration().hamiltonian['h_str'] = [ 'wq0*0.5*(I0-Z0)', 'omegad0*X0||D0' ] armonk_backend.configuration().hamiltonian['vars'] = { 'wq0': 2 * np.pi * freq_est, 'omegad0': drive_est } armonk_backend.configuration().hamiltonian['qub'] = {'0': 2} dt = 2.2222222222222221e-10 armonk_backend.configuration().dt = dt armonk_sim = PulseSimulator.from_backend(armonk_backend) total_samples = 250 amp = np.pi / (drive_est * dt * total_samples) sched = self._1Q_schedule(total_samples, amp) qobj = assemble([sched], backend=armonk_sim, meas_level=2, meas_return='single', shots=1) # run and verify that a pi pulse had been done result = armonk_sim.run(qobj).result() final_vec = result.get_statevector() probabilities = np.abs(final_vec)**2 self.assertTrue(probabilities[0] < 1e-5) self.assertTrue(probabilities[1] > 1 - 1e-5)
class TestPulseSimulator(common.QiskitAerTestCase): r"""PulseSimulator tests. Mathematical expressions are formulated in latex in docstrings for this class. # pylint: disable=anomalous backslash in string Uses single qubit Hamiltonian `H = -\frac{1}{2} \omega_0 \sigma_z + \frac{1}{2} \omega_a e^{i(\omega_{d0} t+\phi)} \sigma_x`. We make sure H is Hermitian by taking the complex conjugate of the lower triangular piece (as done by the simulator). To find the closed form, we move to a rotating frame via the unitary `Urot = e^{-i \omega t \sigma_z/2} (\ket{psi_{rot}}=Urot \ket{psi_{rot}})`. In this frame, the Hamiltonian becomes `Hrot = \frac{1}{2} \omega_a (\cos(\phi) \sigma_x - \sin(\phi) \sigma_y) + \frac{\omega_{d0}-\omega_0}{2} \sigma_z`. """ def setUp(self): """ Set configuration settings for pulse simulator WARNING: We do not support Python 3.5 because the digest algorithm relies on dictionary insertion order. This "feature" was introduced later on Python 3.6 and there's no official support for OrderedDict in the C API so Python 3.5 support has been disabled while looking for a propper fix. """ if sys.version_info.major == 3 and sys.version_info.minor == 5: self.skipTest("We don't support Python 3.5 for Pulse simulator") # Get pulse simulator backend self.backend_sim = PulseSimulator() # --------------------------------------------------------------------- # Test single qubit gates (using meas level 2 and square drive) # --------------------------------------------------------------------- def test_x_gate(self): """ Test x gate. Set omega_d0=omega_0 (drive on resonance), phi=0, omega_a = pi/time """ # setup system model total_samples = 100 omega_0 = 2 * np.pi omega_d0 = omega_0 omega_a = np.pi / total_samples system_model = self._system_model_1Q(omega_0, omega_a) # set up schedule and qobj schedule = self._simple_1Q_schedule(0, total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=256) # set backend backend_options backend_options = {'seed': 9000} # run simulation result = self.backend_sim.run( qobj, system_model=system_model, backend_options=backend_options).result() # test results counts = result.get_counts() exp_counts = {'1': 256} self.assertDictAlmostEqual(counts, exp_counts) def test_1Q_noise(self): """ Tests simulation of noise operators. Uses the same schedule as test_x_gate, but with a high level of amplitude damping noise. """ # setup system model total_samples = 100 omega_0 = 2 * np.pi omega_d0 = omega_0 omega_a = np.pi / total_samples system_model = self._system_model_1Q(omega_0, omega_a) # set up schedule and qobj schedule = self._simple_1Q_schedule(0, total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=256) # set seed for simulation, and set noise backend_options = {'seed': 9000} backend_options['noise_model'] = {"qubit": {"0": {"Sm": 1}}} # run simulation result = self.backend_sim.run( qobj, system_model=system_model, backend_options=backend_options).result() # test results # This level of noise is high enough that all counts should yield 0, # whereas in the noiseless simulation (in test_x_gate) all counts yield 1 counts = result.get_counts() exp_counts = {'0': 256} self.assertDictAlmostEqual(counts, exp_counts) def test_dt_scaling_x_gate(self): """ Test that dt is being used correctly by the solver. """ total_samples = 100 # do the same thing as test_x_gate, but scale dt and all frequency parameters # define test case for a single scaling def scale_test(scale): # set omega_0, omega_d0 equal (use qubit frequency) -> drive on resonance # Require omega_a*time = pi to implement pi pulse (x gate) omega_0 = 2 * np.pi / scale omega_d0 = omega_0 omega_a = np.pi / total_samples / scale # set up system model system_model = self._system_model_1Q(omega_0, omega_a) system_model.dt = system_model.dt * scale # set up schedule and qobj schedule = self._simple_1Q_schedule(0, total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=256) # set backend backend_options backend_options = {'seed': 9000} # run simulation result = self.backend_sim.run( qobj, system_model=system_model, backend_options=backend_options).result() counts = result.get_counts() exp_counts = {'1': 256} self.assertDictAlmostEqual(counts, exp_counts) # set scales and run tests scales = [2., 0.1234, 10.**5, 10**-5] for scale in scales: scale_test(scale) def test_hadamard_gate(self): """Test Hadamard. Is a rotation of pi/2 about the y-axis. Set omega_d0=omega_0 (drive on resonance), phi=-pi/2, omega_a = pi/2/time """ # set variables shots = 100000 # large number of shots so get good proportions total_samples = 100 # set omega_0, omega_d0 equal (use qubit frequency) -> drive on resonance omega_0 = 2 * np.pi omega_d0 = omega_0 # Require omega_a*time = pi/2 to implement pi/2 rotation pulse # num of samples gives time omega_a = np.pi / 2 / total_samples system_model = self._system_model_1Q(omega_0, omega_a) phi = -np.pi / 2 schedule = self._simple_1Q_schedule(phi, total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=shots) # set backend backend_options backend_options = {'seed': 9000} # run simulation result = self.backend_sim.run( qobj, system_model=system_model, backend_options=backend_options).result() counts = result.get_counts() # compare prop prop = {} for key in counts.keys(): prop[key] = counts[key] / shots exp_prop = {'0': 0.5, '1': 0.5} self.assertDictAlmostEqual(prop, exp_prop, delta=0.01) def test_arbitrary_gate(self): """Test a few examples w/ arbitary drive, phase and amplitude. """ shots = 10000 # large number of shots so get good proportions total_samples = 100 num_tests = 3 # set variables for each test omega_0 = 2 * np.pi omega_d0_vals = [omega_0 + 1, omega_0 + 0.02, omega_0 + 0.005] omega_a_vals = [ 2 * np.pi / 3 / total_samples, 7 * np.pi / 5 / total_samples, 0.1 ] phi_vals = [5 * np.pi / 7, 19 * np.pi / 14, np.pi / 4] for i in range(num_tests): with self.subTest(i=i): system_model = self._system_model_1Q(omega_0, omega_a_vals[i]) schedule = self._simple_1Q_schedule(phi_vals[i], total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0_vals[i] / (2 * np.pi)], memory_slots=2, shots=shots) # Run qobj and compare prop to expected result backend_options = {'seed': 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() counts = result.get_counts() prop = {} for key in counts.keys(): prop[key] = counts[key] / shots exp_prop = self._analytic_prop_1q_gates( total_samples=total_samples, omega_0=omega_0, omega_a=omega_a_vals[i], omega_d0=omega_d0_vals[i], phi=phi_vals[i]) self.assertDictAlmostEqual(prop, exp_prop, delta=0.01) def test_meas_level_1(self): """Test measurement level 1. """ shots = 10000 # run large number of shots for good proportions total_samples = 100 # perform hadamard setup (so get some 0's and some 1's), but use meas_level = 1 # set omega_0, omega_d0 equal (use qubit frequency) -> drive on resonance omega_0 = 2 * np.pi omega_d0 = omega_0 # Require omega_a*time = pi/2 to implement pi/2 rotation pulse # num of samples gives time omega_a = np.pi / 2 / total_samples phi = -np.pi / 2 system_model = self._system_model_1Q(omega_0, omega_a) phi = -np.pi / 2 schedule = self._simple_1Q_schedule(phi, total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=1, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=shots) # set backend backend_options backend_options = {'seed': 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() # Verify that (about) half the IQ vals have abs val 1 and half have abs val 0 # (use prop for easier comparison) mem = np.abs(result.get_memory()[:, 0]) iq_prop = {'0': 0, '1': 0} for i in mem: if i == 0: iq_prop['0'] += 1 / shots else: iq_prop['1'] += 1 / shots exp_prop = {'0': 0.5, '1': 0.5} self.assertDictAlmostEqual(iq_prop, exp_prop, delta=0.01) def test_gaussian_drive(self): """Test gaussian drive pulse using meas_level_2. Set omega_d0=omega_0 (drive on resonance), phi=0, omega_a = pi/time """ # set variables # set omega_0, omega_d0 equal (use qubit frequency) -> drive on resonance total_samples = 100 omega_0 = 2 * np.pi omega_d0 = omega_0 # Require omega_a*time = pi to implement pi pulse (x gate) # num of samples gives time omega_a = np.pi / total_samples phi = 0 # Test gaussian drive results for a few different sigma gauss_sigmas = {total_samples / 6, total_samples / 3, total_samples} system_model = self._system_model_1Q(omega_0, omega_a) for gauss_sigma in gauss_sigmas: with self.subTest(gauss_sigma=gauss_sigma): schedule = self._simple_1Q_schedule(phi, total_samples, "gaussian", gauss_sigma) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=1000) backend_options = {'seed': 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() statevector = result.get_statevector() exp_statevector = self._analytic_gaussian_statevector( total_samples, gauss_sigma=gauss_sigma, omega_a=omega_a) # Check fidelity of statevectors self.assertGreaterEqual( state_fidelity(statevector, exp_statevector), 0.99) def test_frame_change(self): """Test frame change command. """ shots = 10000 total_samples = 100 # set omega_0, omega_d0 equal (use qubit frequency) -> drive on resonance omega_0 = 2 * np.pi omega_d0 = omega_0 # set phi = 0 phi = 0 dur_drive1 = total_samples # first pulse duration fc_phi = np.pi # Test frame change where no shift in state results # specfically: do pi/2 pulse, then pi frame change, then another pi/2 pulse. # Verify left in |0> state dur_drive2 = dur_drive1 # same duration for both pulses omega_a = np.pi / 2 / dur_drive1 # pi/2 pulse amplitude system_model = self._system_model_1Q(omega_0, omega_a) schedule = self._1Q_frame_change_schedule(phi, fc_phi, total_samples, dur_drive1, dur_drive2) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=shots) backend_options = {'seed': 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() counts = result.get_counts() exp_counts = {'0': shots} self.assertDictAlmostEqual(counts, exp_counts) # Test frame change where a shift does result # specifically: do pi/4 pulse, then pi phase change, then do pi/8 pulse. # check that a net rotation of pi/4-pi/8 has occured on the Bloch sphere dur_drive2 = int(dur_drive1 / 2) # half time for second pulse (halves angle) omega_a = np.pi / 4 / dur_drive1 # pi/4 pulse amplitude system_model = self._system_model_1Q(omega_0, omega_a) schedule = self._1Q_frame_change_schedule(phi, fc_phi, total_samples, dur_drive1, dur_drive2) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=shots) backend_options = {'seed': 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() counts = result.get_counts() # verify props prop_shift = {} for key in counts.keys(): prop_shift[key] = counts[key] / shots # net angle is given by pi/4-pi/8 prop0 = np.cos((np.pi / 4 - np.pi / 8) / 2)**2 exp_prop = {'0': prop0, '1': 1 - prop0} self.assertDictAlmostEqual(prop_shift, exp_prop, delta=0.01) def test_three_level(self): r"""Test 3 level system. Compare statevectors as counts only use bitstrings. Analytic form given in _analytic_statevector_3level function docstring. """ def analytic_state_vector(omega_a, total_samples): r"""Returns analytically computed statevector for 3 level system with our Hamiltonian. Is given by `(\frac{1}{3} (2+\cos(\frac{\sqrt{3}}{2} \omega_a t)), -\frac{i}{\sqrt{3}} \sin(\frac{\sqrt{3}}{2} \omega_a t), -\frac{2\sqrt{2}}{3} \sin(\frac{\sqrt{3}}{4} \omega_a t)^2)`. Args: omega_a (float): Q0 drive amplitude total_samples (int): number of samples to use in pulses_idx Returns: exp_statevector (list): analytically computed statevector with Hamiltonian from above (Returned in the rotating frame) """ time = total_samples arg1 = np.sqrt( 3) * omega_a * time / 2 # cos arg for first component arg2 = arg1 # sin arg for first component arg3 = arg1 / 2 # sin arg for 3rd component exp_statevector = np.array( [(2 + np.cos(arg1)) / 3, -1j * np.sin(arg2) / np.sqrt(3), -2 * np.sqrt(2) * np.sin(arg3)**2 / 3], dtype=complex) return exp_statevector shots = 1000 total_samples = 100 # Set omega_0,omega_d0 (use qubit frequency) -> drive on resonance omega_0 = 2 * np.pi omega_d0 = omega_0 # Set phi = 0 for simplicity phi = 0 # Test pi pulse omega_a = np.pi / total_samples system_model = self._system_model_1Q(omega_0, omega_a, qubit_dim=3) schedule = self._simple_1Q_schedule(phi, total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=shots) backend_options = {'seed': 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() statevector = result.get_statevector() exp_statevector = analytic_state_vector(omega_a, total_samples) # Check fidelity of statevectors self.assertGreaterEqual(state_fidelity(statevector, exp_statevector), 0.99) # Test 2*pi pulse omega_a = 2 * np.pi / total_samples system_model = self._system_model_1Q(omega_0, omega_a, qubit_dim=3) schedule = self._simple_1Q_schedule(phi, total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0 / (2 * np.pi)], memory_slots=2, shots=shots) backend_options = {'seed': 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() statevector = result.get_statevector() exp_statevector = analytic_state_vector(omega_a, total_samples) # Check fidelity of vectors self.assertGreaterEqual(state_fidelity(statevector, exp_statevector), 0.99) def test_interaction(self): r"""Test 2 qubit interaction via swap gates.""" shots = 100000 total_samples = 100 # Do a standard SWAP gate # Interaction amp (any non-zero creates the swap gate) omega_i_swap = np.pi / 2 / total_samples # set omega_d0=omega_0 (resonance) omega_0 = 2 * np.pi omega_d0 = omega_0 # For swapping, set omega_d1 = 0 (drive on Q0 resonance) # Note: confused by this as there is no d1 term omega_d1 = 0 # do pi pulse on Q0 and verify state swaps from '01' to '10' (reverse bit order) # Q0 drive amp -> pi pulse omega_a_pi_swap = np.pi / total_samples system_model = self._system_model_2Q(omega_0, omega_a_pi_swap, omega_i_swap) schedule = self._schedule_2Q_interaction(total_samples) qobj = assemble( [schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0, 1]], qubit_lo_freq=[omega_d0 / (2 * np.pi), omega_d1 / (2 * np.pi)], memory_slots=2, shots=shots) backend_options = {'seed': 12387} result_pi_swap = self.backend_sim.run(qobj, system_model, backend_options).result() counts_pi_swap = result_pi_swap.get_counts() exp_counts_pi_swap = { '10': shots } # reverse bit order (qiskit convention) self.assertDictAlmostEqual(counts_pi_swap, exp_counts_pi_swap, delta=2) # do pi/2 pulse on Q0 and verify half the counts are '00' and half are swapped state '10' # Q0 drive amp -> pi/2 pulse omega_a_pi2_swap = np.pi / 2 / total_samples system_model = self._system_model_2Q(omega_0, omega_a_pi2_swap, omega_i_swap) result_pi2_swap = self.backend_sim.run(qobj, system_model, backend_options).result() counts_pi2_swap = result_pi2_swap.get_counts() # compare proportions for improved accuracy prop_pi2_swap = {} for key in counts_pi2_swap.keys(): prop_pi2_swap[key] = counts_pi2_swap[key] / shots exp_prop_pi2_swap = {'00': 0.5, '10': 0.5} # reverse bit order self.assertDictAlmostEqual(prop_pi2_swap, exp_prop_pi2_swap, delta=0.01) # Test that no SWAP occurs when omega_i=0 (no interaction) omega_i_no_swap = 0 # Q0 drive amp -> pi pulse omega_a_no_swap = np.pi / total_samples system_model = self._system_model_2Q(omega_0, omega_a_no_swap, omega_i_no_swap) result_no_swap = self.backend_sim.run(qobj, system_model, backend_options).result() counts_no_swap = result_no_swap.get_counts() exp_counts_no_swap = { '01': shots } # non-swapped state (reverse bit order) self.assertDictAlmostEqual(counts_no_swap, exp_counts_no_swap) def _system_model_1Q(self, omega_0, omega_a, qubit_dim=2): """Constructs a simple 1 qubit system model. Args: omega_0 (float): frequency of qubit omega_a (float): strength of drive term qubit_dim (int): dimension of qubit Returns: PulseSystemModel: model for qubit system """ # make Hamiltonian hamiltonian = {} hamiltonian['h_str'] = ['-0.5*omega0*Z0', '0.5*omegaa*X0||D0'] hamiltonian['vars'] = {'omega0': omega_0, 'omegaa': omega_a} hamiltonian['qub'] = {'0': qubit_dim} ham_model = HamiltonianModel.from_dict(hamiltonian) u_channel_lo = [] subsystem_list = [0] dt = 1. return PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) def _system_model_2Q(self, omega_0, omega_a, omega_i, qubit_dim=2): """Constructs a simple 1 qubit system model. Args: omega_0 (float): frequency of qubit omega_a (float): strength of drive term omega_i (float): strength of interaction qubit_dim (int): dimension of qubit Returns: PulseSystemModel: model for qubit system """ # make Hamiltonian hamiltonian = {} # qubit 0 terms hamiltonian['h_str'] = ['-0.5*omega0*Z0', '0.5*omegaa*X0||D0'] # interaction term hamiltonian['h_str'].append('omegai*(Sp0*Sm1+Sm0*Sp1)||U1') hamiltonian['vars'] = { 'omega0': omega_0, 'omegaa': omega_a, 'omegai': omega_i } hamiltonian['qub'] = {'0': qubit_dim, '1': qubit_dim} ham_model = HamiltonianModel.from_dict(hamiltonian) u_channel_lo = [[{ 'q': 0, 'scale': [1.0, 0.0] }], [{ 'q': 0, 'scale': [-1.0, 0.0] }, { 'q': 1, 'scale': [1.0, 0.0] }]] subsystem_list = [0, 1] dt = 1. return PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) def _simple_1Q_schedule(self, phi, total_samples, shape="square", gauss_sigma=0): """Creates schedule for single pulse test Args: phi (float): drive phase (phi in Hamiltonian) total_samples (int): length of pulses shape (str): shape of the pulse; defaults to square pulse gauss_sigma (float): std dev for gaussian pulse if shape=="gaussian" Returns: schedule (pulse schedule): schedule for this test """ # set up pulse command phase = np.exp(1j * phi) drive_pulse = None if shape == "square": const_pulse = np.ones(total_samples) drive_pulse = SamplePulse(phase * const_pulse, name='drive_pulse') if shape == "gaussian": times = 1.0 * np.arange(total_samples) gaussian = np.exp(-times**2 / 2 / gauss_sigma**2) drive_pulse = SamplePulse(phase * gaussian, name='drive_pulse') # add commands into a schedule for first qubit schedule = Schedule(name='drive_pulse') schedule |= Play(drive_pulse, DriveChannel(0)) schedule |= Acquire(total_samples, AcquireChannel(0), MemorySlot(0)) << schedule.duration return schedule def _1Q_frame_change_schedule(self, phi, fc_phi, total_samples, dur_drive1, dur_drive2): """Creates schedule for frame change test. Does a pulse w/ phase phi of duration dur_drive1, then frame change of phase fc_phi, then another pulse of phase phi of duration dur_drive2. The different durations for the pulses allow manipulation of rotation angles on Bloch sphere Args: phi (float): drive phase (phi in Hamiltonian) fc_phi (float): phase for frame change total_samples (int): length of pulses dur_drive1 (int): duration of first pulse dur_drive2 (int): duration of second pulse Returns: schedule (pulse schedule): schedule for frame change test """ phase = np.exp(1j * phi) drive_pulse_1 = SamplePulse(phase * np.ones(dur_drive1), name='drive_pulse_1') drive_pulse_2 = SamplePulse(phase * np.ones(dur_drive2), name='drive_pulse_2') # add commands to schedule schedule = Schedule(name='fc_schedule') schedule |= Play(drive_pulse_1, DriveChannel(0)) schedule += ShiftPhase(fc_phi, DriveChannel(0)) schedule += Play(drive_pulse_2, DriveChannel(0)) schedule |= Acquire(total_samples, AcquireChannel(0), MemorySlot(0)) << schedule.duration return schedule def _analytic_prop_1q_gates(self, total_samples, omega_0, omega_a, omega_d0, phi): """Compute proportion for 0 and 1 states analytically for single qubit gates. Args: total_samples (int): length of pulses omega_0 (float): Q0 freq omega_a (float): Q0 drive amplitude omega_d0 (flaot): Q0 drive frequency phi (float): drive phase Returns: exp_prop (dict): expected value of 0 and 1 proportions from analytic computation """ time = total_samples # write Hrot analytically h_rot = np.array([[ (omega_d0 - omega_0) / 2, np.exp(1j * phi) * omega_a / 2 ], [np.exp(-1j * phi) * omega_a / 2, -(omega_d0 - omega_0) / 2]]) # exponentiate u_rot = expm(-1j * h_rot * time) state0 = np.array([1, 0]) # compute analytic prob (proportion) of 0 state mat_elem0 = np.vdot(state0, np.dot(u_rot, state0)) prop0 = np.abs(mat_elem0)**2 # return expected proportion exp_prop = {'0': prop0, '1': 1 - prop0} return exp_prop def _analytic_gaussian_statevector(self, total_samples, gauss_sigma, omega_a): r"""Computes analytic statevector for gaussian drive. Solving the Schrodinger equation in the rotating frame leads to the analytic solution `(\cos(x), -i\sin(x)) with `x = \frac{1}{2}\sqrt{\frac{\pi}{2}}\sigma\omega_a erf(\frac{t}{\sqrt{2}\sigma}). Args: total_samples (int): length of pulses gauss_sigma (float): std dev for the gaussian drive omega_a (float): Q0 drive amplitude Returns: exp_statevector (list): analytic form of the statevector computed for gaussian drive (Returned in the rotating frame) """ time = total_samples arg = 1 / 2 * np.sqrt(np.pi / 2) * gauss_sigma * omega_a * erf( time / np.sqrt(2) / gauss_sigma) exp_statevector = [np.cos(arg), -1j * np.sin(arg)] return exp_statevector def _schedule_2Q_interaction(self, total_samples): """Creates schedule for testing two qubit interaction. Specifically, do a pi pulse on qub 0 so it starts in the `1` state (drive channel) and then apply constant pulses to each qubit (on control channel 1). This will allow us to test a swap gate. Args: total_samples (int): length of pulses Returns: schedule (pulse schedule): schedule for 2q experiment """ # create acquire schedule acq_sched = Schedule(name='acq_sched') acq_sched |= Acquire(total_samples, AcquireChannel(0), MemorySlot(0)) acq_sched += Acquire(total_samples, AcquireChannel(1), MemorySlot(1)) # set up const pulse const_pulse = SamplePulse(np.ones(total_samples), name='const_pulse') # add commands to schedule schedule = Schedule(name='2q_schedule') schedule |= Play(const_pulse, DriveChannel(0)) schedule += Play(const_pulse, ControlChannel(1)) << schedule.duration schedule |= acq_sched << schedule.duration return schedule
def test_shift_phase(self): """Test ShiftPhase command.""" omega_0 = 1.123 r = 1. system_model = self._system_model_1Q(omega_0, r) # run a schedule in which a shifted phase causes a pulse to cancel itself. # Also do it in multiple phase shifts to test accumulation sched = Schedule() amp1 = 0.12 sched += Play(Waveform([amp1]), DriveChannel(0)) phi1 = 0.12374 * np.pi sched += ShiftPhase(phi1, DriveChannel(0)) amp2 = 0.492 sched += Play(Waveform([amp2]), DriveChannel(0)) phi2 = 0.5839 * np.pi sched += ShiftPhase(phi2, DriveChannel(0)) amp3 = 0.12 + 0.21 * 1j sched += Play(Waveform([amp3]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration y0 = np.array([1., 0]) pulse_sim = PulseSimulator(system_model=system_model, initial_state=y0) qobj = assemble([sched], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_0], memory_slots=2, shots=1) results = pulse_sim.run(qobj).result() pulse_sim_yf = results.get_statevector() #run independent simulation samples = np.array([[amp1], [amp2 * np.exp(1j * phi1)], [amp3 * np.exp(1j * (phi1 + phi2))]]) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_0]), samples, 1.) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1 - (10**-5)) # run another schedule with only a single shift phase to verify sched = Schedule() amp1 = 0.12 sched += Play(Waveform([amp1]), DriveChannel(0)) phi1 = 0.12374 * np.pi sched += ShiftPhase(phi1, DriveChannel(0)) amp2 = 0.492 sched += Play(Waveform([amp2]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_0], memory_slots=2, shots=1) results = pulse_sim.run(qobj).result() pulse_sim_yf = results.get_statevector() #run independent simulation samples = np.array([[amp1], [amp2 * np.exp(1j * phi1)]]) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_0]), samples, 1.) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1 - (10**-5))
def test_delay_instruction(self): """Test for delay instruction.""" # construct system model specifically for this hamiltonian = {} hamiltonian['h_str'] = ['0.5*r*X0||D0', '0.5*r*Y0||D1'] hamiltonian['vars'] = {'r': np.pi} hamiltonian['qub'] = {'0': 2} ham_model = HamiltonianModel.from_dict(hamiltonian) u_channel_lo = [] subsystem_list = [0] dt = 1. system_model = PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) # construct a schedule that should result in a unitary -Z if delays are correctly handled # i.e. do a pi rotation about x, sandwiched by pi/2 rotations about y in opposite directions # so that the x rotation is transformed into a z rotation. # if delays are not handled correctly this process should fail sched = Schedule() sched += Play(Waveform([0.5]), DriveChannel(1)) sched += Delay(1, DriveChannel(1)) sched += Play(Waveform([-0.5]), DriveChannel(1)) sched += Delay(1, DriveChannel(0)) sched += Play(Waveform([1.]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration # Result of schedule should be the unitary -1j*Z, so check rotation of an X eigenstate pulse_sim = PulseSimulator(system_model=system_model, initial_state=np.array([1., 1.]) / np.sqrt(2)) qobj = assemble([sched], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[0., 0.], memory_slots=2, shots=1) results = pulse_sim.run(qobj).result() statevector = results.get_statevector() expected_vector = np.array([-1j, 1j]) / np.sqrt(2) self.assertGreaterEqual(state_fidelity(statevector, expected_vector), 1 - (10**-5)) # verify validity of simulation when no delays included sched = Schedule() sched += Play(Waveform([0.5]), DriveChannel(1)) sched += Play(Waveform([-0.5]), DriveChannel(1)) sched += Play(Waveform([1.]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[0., 0.], memory_slots=2, shots=1) results = pulse_sim.run(qobj).result() statevector = results.get_statevector() U = expm(1j * np.pi * self.Y / 4) @ expm(-1j * np.pi * (self.Y / 4 + self.X / 2)) expected_vector = U @ np.array([1., 1.]) / np.sqrt(2) self.assertGreaterEqual(state_fidelity(statevector, expected_vector), 1 - (10**-5))
def test_2Q_exchange(self): r"""Test a more complicated 2q simulation""" q_freqs = [5., 5.1] r = 0.02 j = 0.02 total_samples = 25 hamiltonian = {} hamiltonian['h_str'] = [ '2*np.pi*v0*0.5*Z0', '2*np.pi*v1*0.5*Z1', '2*np.pi*r*0.5*X0||D0', '2*np.pi*r*0.5*X1||D1', '2*np.pi*j*0.5*I0*I1', '2*np.pi*j*0.5*X0*X1', '2*np.pi*j*0.5*Y0*Y1', '2*np.pi*j*0.5*Z0*Z1' ] hamiltonian['vars'] = { 'v0': q_freqs[0], 'v1': q_freqs[1], 'r': r, 'j': j } hamiltonian['qub'] = {'0': 2, '1': 2} ham_model = HamiltonianModel.from_dict(hamiltonian) # set the U0 to have frequency of drive channel 0 u_channel_lo = [] subsystem_list = [0, 1] dt = 1. system_model = PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) # try some random schedule schedule = Schedule() drive_pulse = Waveform(np.ones(total_samples)) schedule += Play(drive_pulse, DriveChannel(0)) schedule |= Play(drive_pulse, DriveChannel(1)) << 2 * total_samples schedule |= Acquire(total_samples, AcquireChannel(0), MemorySlot(0)) << 3 * total_samples schedule |= Acquire(total_samples, AcquireChannel(1), MemorySlot(1)) << 3 * total_samples y0 = np.array([1., 0., 0., 0.]) pulse_sim = PulseSimulator(system_model=system_model, initial_state=y0, seed=9000) qobj = assemble([schedule], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=q_freqs, memory_slots=2, shots=1000) result = pulse_sim.run(qobj).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation d0_samps = np.concatenate( (np.ones(total_samples), np.zeros(2 * total_samples))) d1_samps = np.concatenate( (np.zeros(2 * total_samples), np.ones(total_samples))) samples = np.array([d0_samps, d1_samps]).transpose() q_freqs = np.array(q_freqs) yf = simulate_2q_exchange_model(y0, q_freqs, r, j, q_freqs, samples, 1.) # Check fidelity of statevectors self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5))
def test_subsystem_restriction(self): r"""Test behavior of subsystem_list subsystem restriction""" total_samples = 100 # set coupling term and drive channels to 0 frequency j = 0.5 / total_samples omega_d = 0. subsystem_list = [0, 2] y0 = np.kron(np.array([1., 0.]), np.array([0., 1.])) system_model = self._system_model_3Q(j, subsystem_list=subsystem_list) pulse_sim = PulseSimulator(system_model=system_model) schedule = self._3Q_constant_sched(total_samples, u_idx=0, subsystem_list=subsystem_list) qobj = assemble([schedule], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d, omega_d, omega_d], memory_slots=2, shots=1) result = pulse_sim.run(qobj, initial_state=y0).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) y0 = np.kron(np.array([1., 0.]), np.array([1., 0.])) result = pulse_sim.run(qobj, initial_state=y0).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) subsystem_list = [1, 2] system_model = self._system_model_3Q(j, subsystem_list=subsystem_list) y0 = np.kron(np.array([1., 0.]), np.array([0., 1.])) pulse_sim.set_options(system_model=system_model) schedule = self._3Q_constant_sched(total_samples, u_idx=1, subsystem_list=subsystem_list) qobj = assemble([schedule], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d, omega_d, omega_d], memory_slots=2, shots=1) result = pulse_sim.run(qobj, initial_state=y0).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) y0 = np.kron(np.array([1., 0.]), np.array([1., 0.])) pulse_sim.set_options(initial_state=y0) result = pulse_sim.run(qobj).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5))
def test_3d_oscillator(self): """Test simulation of a duffing oscillator truncated to 3 dimensions.""" total_samples = 100 freq = 5. anharm = -0.33 # Test pi pulse r = 0.5 / total_samples # set up simulator system_model = system_model = self._system_model_3d_oscillator(freq, anharm, r) pulse_sim = PulseSimulator(system_model=system_model) schedule = self._1Q_constant_sched(total_samples) qobj = assemble([schedule], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[freq], shots=1) result = pulse_sim.run(qobj).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation y0 = np.array([1., 0., 0.]) samples = np.ones((total_samples, 1)) indep_yf = simulate_3d_oscillator_model(y0, freq, anharm, r, np.array([freq]), samples, 1.) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1 - 10**-5) # test with different input state y0 = np.array([0., 0., 1.]) # Test some irregular value r = 1.49815 / total_samples system_model = self._system_model_3d_oscillator(freq, anharm, r) pulse_sim.set_options(system_model=system_model) schedule = self._1Q_constant_sched(total_samples) qobj = assemble([schedule], backend=pulse_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[freq], shots=1) result = pulse_sim.run(qobj, initial_state=y0).result() pulse_sim_yf = result.get_statevector() samples = np.ones((total_samples, 1)) indep_yf = simulate_3d_oscillator_model(y0, freq, anharm, r, np.array([freq]), samples, 1.) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1 - 10**-5)
class TestPulseSimulator(common.QiskitAerTestCase): r"""PulseSimulator tests.""" def setUp(self): """ Set configuration settings for pulse simulator WARNING: We do not support Python 3.5 because the digest algorithm relies on dictionary insertion order. This "feature" was introduced later on Python 3.6 and there's no official support for OrderedDict in the C API so Python 3.5 support has been disabled while looking for a propper fix. """ if sys.version_info.major == 3 and sys.version_info.minor == 5: self.skipTest("We don't support Python 3.5 for Pulse simulator") # Get pulse simulator backend self.backend_sim = PulseSimulator() self.X = np.array([[0., 1.], [1., 0.]]) self.Y = np.array([[0., -1j], [1j, 0.]]) self.Z = np.array([[1., 0.], [0., -1.]]) # --------------------------------------------------------------------- # Test single qubit gates # --------------------------------------------------------------------- def test_x_gate(self): """Test a schedule for a pi pulse on a 2 level system.""" # qubit frequency and drive frequency omega_0 = 1.1329824 omega_d = omega_0 # drive strength and length of pulse r = 0.01 total_samples = 100 system_model = self._system_model_1Q(omega_0, r) # set up constant pulse for doing a pi pulse schedule = self._1Q_constant_sched(total_samples) # set up schedule and qobj qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=1, shots=256) # set backend backend_options including initial state y0 = np.array([1.0, 0.0]) backend_options = {'seed' : 9000, 'initial_state' : y0} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation samples = np.ones((total_samples, 1)) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_0]), samples, 1.) # approximate analytic solution phases = np.exp(-1j * 2 * np.pi * omega_0 * total_samples * np.array([1., -1.]) / 2) approx_yf = phases * np.array([0., -1j]) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, approx_yf), 0.99) # test counts counts = result.get_counts() exp_counts = {'1': 256} self.assertDictAlmostEqual(counts, exp_counts) def test_x_gate_rwa(self): """Test a schedule for a pi pulse on a 2 level system in the rotating frame with a the rotating wave approximation.""" # qubit frequency and drive frequency omega_0 = 0. omega_d = omega_0 # drive strength and length of pulse # in rotating wave with RWA the drive strength is halved r = 0.01 / 2 total_samples = 100 system_model = self._system_model_1Q(omega_0, r) # set up constant pulse for doing a pi pulse schedule = self._1Q_constant_sched(total_samples) # set up schedule and qobj qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=1, shots=1) # set backend backend_options including initial state y0 = np.array([1.0, 0.0]) backend_options = {'seed' : 9000, 'initial_state' : y0} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() pulse_sim_yf = result.get_statevector() # expected final state yf = np.array([0., -1j]) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1-10**-5) def test_x_half_gate(self): """Test a schedule for a pi/2 pulse on a 2 level system. Same setup as test_x_gate but with half the time.""" # qubit frequency and drive frequency omega_0 = 1.1329824 omega_d = omega_0 # drive strength and length of pulse r = 0.01 total_samples = 50 system_model = self._system_model_1Q(omega_0, r) # set up constant pulse for doing a pi pulse schedule = self._1Q_constant_sched(total_samples) # set up schedule and qobj qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=1, shots=256) # set backend backend_options y0 = np.array([1.0, 0.0]) backend_options = {'seed' : 9000, 'initial_state' : y0} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation samples = np.ones((total_samples, 1)) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_d]), samples, 1.) # approximate analytic solution phases = np.exp(-1j * 2 * np.pi * omega_0 * total_samples * np.array([1., -1.]) / 2) approx_yf = phases * (expm(-1j * (np.pi / 4) * self.X) @ y0) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, approx_yf), 0.99) # test counts counts = result.get_counts() exp_counts = {'1': 132, '0': 124} self.assertDictAlmostEqual(counts, exp_counts) def test_y_half_gate(self): """Test a schedule for a pi/2 pulse about the y axis on a 2 level system. Same setup as test_x_half_gate but with amplitude of pulse 1j.""" # qubit frequency and drive frequency omega_0 = 1.1329824 omega_d = omega_0 # drive strength and length of pulse r = 0.01 total_samples = 50 system_model = self._system_model_1Q(omega_0, r) # set up constant pulse for doing a pi pulse schedule = self._1Q_constant_sched(total_samples, amp=1j) # set up schedule and qobj qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=1, shots=256) # set backend backend_options y0 = np.array([1.0, 0.0]) backend_options = {'seed' : 9000, 'initial_state' : y0} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation samples = 1j * np.ones((total_samples, 1)) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_d]), samples, 1.) # approximate analytic solution phases = np.exp(-1j * 2 * np.pi * omega_0 * total_samples * np.array([1., -1.]) / 2) approx_yf = phases * (expm(-1j * (np.pi / 4) * self.Y) @ y0) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, approx_yf), 0.99) # test counts counts = result.get_counts() exp_counts = {'1': 131, '0': 125} self.assertDictAlmostEqual(counts, exp_counts) def test_1Q_noise(self): """Tests simulation of noise operators. Uses the same schedule as test_x_gate, but with a high level of amplitude damping noise. """ # qubit frequency and drive frequency omega_0 = 1.1329824 omega_d = omega_0 # drive strength and length of pulse r = 0.01 total_samples = 100 system_model = self._system_model_1Q(omega_0, r) # set up constant pulse for doing a pi pulse schedule = self._1Q_constant_sched(total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=2, shots=10) # set seed for simulation, and set noise y0 = np.array([1., 0.]) backend_options = {'seed' : 9000, 'initial_state' : y0} backend_options['noise_model'] = {"qubit": {"0": {"Sm": 1.}}} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() # test results # This level of noise is high enough that all counts should yield 0, # whereas in the noiseless simulation (in test_x_gate) all counts yield 1 counts = result.get_counts() exp_counts = {'0': 10} self.assertDictAlmostEqual(counts, exp_counts) def test_unitary_parallel(self): """Test for parallel solving in unitary simulation. Uses same schedule as test_x_gate but runs it twice to trigger parallel execution. """ # qubit frequency and drive frequency omega_0 = 1. omega_d = omega_0 # drive strength and length of pulse r = 0.01 total_samples = 50 system_model = self._system_model_1Q(omega_0, r) # set up constant pulse for doing a pi pulse schedule = self._1Q_constant_sched(total_samples) # set up schedule and qobj qobj = assemble([schedule, schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=1, shots=256) # set backend backend_options y0 = np.array([1., 0.]) backend_options = backend_options = {'seed' : 9000, 'initial_state' : y0} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() # test results, checking both runs in parallel counts = result.get_counts() exp_counts0 = {'1': 132, '0': 124} exp_counts1 = {'0': 147, '1': 109} self.assertDictAlmostEqual(counts[0], exp_counts0) self.assertDictAlmostEqual(counts[1], exp_counts1) def test_dt_scaling_x_gate(self): """Test that dt is being used correctly by the solver.""" total_samples = 100 # do the same thing as test_x_gate, but scale dt and all frequency parameters # define test case for a single scaling def scale_test(scale): # qubit frequency and drive frequency omega_0 = 1. / scale omega_d = omega_0 # drive strength and length of pulse r = 0.01 / scale total_samples = 100 # set up system model and scale time system_model = self._system_model_1Q(omega_0, r) system_model.dt = system_model.dt * scale # set up constant pulse for doing a pi pulse schedule = self._1Q_constant_sched(total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=2, shots=256) # set backend backend_options y0 = np.array([1., 0.]) backend_options = {'seed' : 9000, 'initial_state': y0} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation samples = np.ones((total_samples, 1)) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_0]), samples, scale) # approximate analytic solution phases = np.exp(-1j * 2 * np.pi * omega_0 * total_samples * np.array([1., -1.]) / 2) approx_yf = phases * np.array([0., -1j]) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, approx_yf), 0.99) counts = result.get_counts() exp_counts = {'1': 256} self.assertDictAlmostEqual(counts, exp_counts) # set scales and run tests scales = [2., 0.1234, 10.**5, 10**-5] for scale in scales: scale_test(scale) def test_arbitrary_constant_drive(self): """Test a few examples w/ arbitary drive, phase and amplitude. """ total_samples = 100 num_tests = 3 omega_0 = 1. omega_d_vals = [omega_0 + 1., omega_0 + 0.02, omega_0 + 0.005] r_vals = [3 / total_samples, 5 / total_samples, 0.1] phase_vals = [5 * np.pi / 7, 19 * np.pi / 14, np.pi / 4] for i in range(num_tests): with self.subTest(i=i): system_model = self._system_model_1Q(omega_0, r_vals[i]) schedule = self._1Q_constant_sched(total_samples, amp=np.exp(-1j * phase_vals[i])) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d_vals[i]], memory_slots=2, shots=1) # Run qobj and compare prop to expected result y0 = np.array([1., 0.]) backend_options = {'seed' : 9000, 'initial_state' : y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation samples = np.exp(-1j * phase_vals[i]) * np.ones((total_samples, 1)) indep_yf = simulate_1q_model(y0, omega_0, r_vals[i], np.array([omega_d_vals[i]]), samples, 1.) # approximate analytic solution phases = np.exp(-1j * 2 * np.pi * omega_d_vals[i] * total_samples * np.array([1., -1.]) / 2) detuning = omega_0 - omega_d_vals[i] amp = np.exp(-1j * phase_vals[i]) rwa_ham = 2 * np.pi * (detuning * self.Z / 2 + r_vals[i] * np.array([[0, amp.conj()], [amp, 0.]]) / 4) approx_yf = phases * (expm(-1j * rwa_ham * total_samples) @ y0) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, approx_yf), 0.99) def test_3d_oscillator(self): """Test simulation of a duffing oscillator truncated to 3 dimensions.""" total_samples = 100 freq = 5. anharm = -0.33 # Test pi pulse r = 0.5 / total_samples system_model = self._system_model_3d_oscillator(freq, anharm, r) schedule = self._1Q_constant_sched(total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[freq], shots=1) backend_options = {'seed' : 9000} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation y0 = np.array([1., 0., 0.]) samples = np.ones((total_samples, 1)) indep_yf = simulate_3d_oscillator_model(y0, freq, anharm, r, np.array([freq]), samples, 1.) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) # Test some irregular value r = 1.49815 / total_samples system_model = self._system_model_3d_oscillator(freq, anharm, r) schedule = self._1Q_constant_sched(total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[freq], shots=1) y0 = np.array([0., 0., 1.]) backend_options = {'seed' : 9000, 'initial_state' : y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() samples = np.ones((total_samples, 1)) indep_yf = simulate_3d_oscillator_model(y0, freq, anharm, r, np.array([freq]), samples, 1.) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) def test_2Q_interaction(self): r"""Test 2 qubit interaction via controlled operations using u channels.""" total_samples = 100 # set coupling term and drive channels to 0 frequency j = 0.5 / total_samples omega_d0 = 0. omega_d1 = 0. system_model = self._system_model_2Q(j) schedule = self._2Q_constant_sched(total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d0, omega_d1], memory_slots=2, shots=1) y0 = np.kron(np.array([1., 0.]), np.array([0., 1.])) backend_options = {'seed' : 9000, 'initial_state': y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() # exact analytic solution yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) # run with different initial state y0 = np.kron(np.array([1., 0.]), np.array([1., 0.])) backend_options = {'seed' : 9000, 'initial_state': y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() # exact analytic solution yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) def test_subsystem_restriction(self): r"""Test behavior of subsystem_list subsystem restriction""" total_samples = 100 # set coupling term and drive channels to 0 frequency j = 0.5 / total_samples omega_d = 0. subsystem_list = [0, 2] system_model = self._system_model_3Q(j, subsystem_list=subsystem_list) schedule = self._3Q_constant_sched(total_samples, u_idx=0, subsystem_list=subsystem_list) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d, omega_d, omega_d], memory_slots=2, shots=1) y0 = np.kron(np.array([1., 0.]), np.array([0., 1.])) backend_options = {'seed' : 9000, 'initial_state': y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) y0 = np.kron(np.array([1., 0.]), np.array([1., 0.])) backend_options = {'seed' : 9000, 'initial_state': y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) subsystem_list = [1, 2] system_model = self._system_model_3Q(j, subsystem_list=subsystem_list) schedule = self._3Q_constant_sched(total_samples, u_idx=1, subsystem_list=subsystem_list) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d, omega_d, omega_d], memory_slots=2, shots=1) y0 = np.kron(np.array([1., 0.]), np.array([0., 1.])) backend_options = {'seed' : 9000, 'initial_state': y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) y0 = np.kron(np.array([1., 0.]), np.array([1., 0.])) backend_options = {'seed' : 9000, 'initial_state': y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() yf = expm(-1j * 0.5 * 2 * np.pi * np.kron(self.X, self.Z) / 4) @ y0 self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1 - (10**-5)) def test_simulation_without_variables(self): r"""Test behavior of subsystem_list subsystem restriction. Same setup as test_x_gate, but with explicit Hamiltonian construction without variables """ ham_dict = {'h_str': ['np.pi*Z0', '0.02*np.pi*X0||D0'], 'qub': {'0': 2}} ham_model = HamiltonianModel.from_dict(ham_dict) u_channel_lo = [] subsystem_list = [0] dt = 1. system_model = PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) # set up schedule and qobj total_samples = 50 schedule = self._1Q_constant_sched(total_samples) qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[1.], memory_slots=2, shots=256) # set backend backend_options backend_options = {'seed' : 9000, 'initial_state' : np.array([1., 0.])} # run simulation result = self.backend_sim.run(qobj, system_model=system_model, backend_options=backend_options).result() # test results counts = result.get_counts() exp_counts = {'1': 256} self.assertDictAlmostEqual(counts, exp_counts) def test_meas_level_1(self): """Test measurement level 1. """ shots = 10000 # run large number of shots for good proportions total_samples = 100 omega_0 = 1. omega_d = omega_0 # Require omega_a*time = pi to implement pi pulse (x gate) # num of samples gives time r = 1. / (2 * total_samples) system_model = self._system_model_1Q(omega_0, r) amp = np.exp(-1j * np.pi / 2) schedule = self._1Q_constant_sched(total_samples, amp=amp) qobj = assemble([schedule], backend=self.backend_sim, meas_level=1, meas_return='single', meas_map=[[0]], qubit_lo_freq=[1.], memory_slots=2, shots=shots) # set backend backend_options y0 = np.array([1.0, 0.0]) backend_options = {'seed' : 9000, 'initial_state' : y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() samples = amp * np.ones((total_samples, 1)) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_d]), samples, 1.) # test final state self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1-10**-5) # Verify that (about) half the IQ vals have abs val 1 and half have abs val 0 # (use prop for easier comparison) mem = np.abs(result.get_memory()[:, 0]) iq_prop = {'0': 0, '1': 0} for i in mem: if i == 0: iq_prop['0'] += 1 / shots else: iq_prop['1'] += 1 / shots exp_prop = {'0': 0.5, '1': 0.5} self.assertDictAlmostEqual(iq_prop, exp_prop, delta=0.01) def test_gaussian_drive(self): """Test gaussian drive pulse using meas_level_2. Set omega_d0=omega_0 (drive on resonance), phi=0, omega_a = pi/time """ # set omega_0, omega_d0 equal (use qubit frequency) -> drive on resonance total_samples = 100 omega_0 = 1. omega_d = omega_0 # Require omega_a*time = pi to implement pi pulse (x gate) # num of samples gives time r = np.pi / total_samples # Test gaussian drive results for a few different sigma gauss_sigmas = [total_samples / 6, total_samples / 3, total_samples] system_model = self._system_model_1Q(omega_0, r) for gauss_sigma in gauss_sigmas: with self.subTest(gauss_sigma=gauss_sigma): times = 1.0 * np.arange(total_samples) gaussian_samples = np.exp(-times**2 / 2 / gauss_sigma**2) drive_pulse = SamplePulse(gaussian_samples, name='drive_pulse') # construct schedule schedule = Schedule() schedule |= Play(drive_pulse, DriveChannel(0)) schedule |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << schedule.duration qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_d], memory_slots=2, shots=1) y0 = np.array([1., 0.]) backend_options = {'seed' : 9000, 'initial_state' : y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() # run independent simulation yf = simulate_1q_model(y0, omega_0, r, np.array([omega_d]), gaussian_samples, 1.) # Check fidelity of statevectors self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1-(10**-5)) def test_2Q_exchange(self): r"""Test a more complicated 2q simulation""" q_freqs = [5., 5.1] r = 0.02 j = 0.02 total_samples = 25 hamiltonian = {} hamiltonian['h_str'] = ['2*np.pi*v0*0.5*Z0', '2*np.pi*v1*0.5*Z1', '2*np.pi*r*0.5*X0||D0', '2*np.pi*r*0.5*X1||D1', '2*np.pi*j*0.5*I0*I1', '2*np.pi*j*0.5*X0*X1', '2*np.pi*j*0.5*Y0*Y1', '2*np.pi*j*0.5*Z0*Z1'] hamiltonian['vars'] = {'v0': q_freqs[0], 'v1': q_freqs[1], 'r': r, 'j': j} hamiltonian['qub'] = {'0': 2, '1': 2} ham_model = HamiltonianModel.from_dict(hamiltonian) # set the U0 to have frequency of drive channel 0 u_channel_lo = [] subsystem_list = [0, 1] dt = 1. system_model = PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) # try some random schedule schedule = Schedule() drive_pulse = SamplePulse(np.ones(total_samples)) schedule += Play(drive_pulse, DriveChannel(0)) schedule |= Play(drive_pulse, DriveChannel(1)) << 2 * total_samples schedule |= Acquire(total_samples, AcquireChannel(0), MemorySlot(0)) << 3 * total_samples schedule |= Acquire(total_samples, AcquireChannel(1), MemorySlot(1)) << 3 * total_samples qobj = assemble([schedule], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=q_freqs, memory_slots=2, shots=1000) y0 = np.array([1., 0., 0., 0.]) backend_options = {'seed' : 9000, 'initial_state' : y0} result = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = result.get_statevector() # set up and run independent simulation d0_samps = np.concatenate((np.ones(total_samples), np.zeros(2 * total_samples))) d1_samps = np.concatenate((np.zeros(2 * total_samples), np.ones(total_samples))) samples = np.array([d0_samps, d1_samps]).transpose() q_freqs = np.array(q_freqs) yf = simulate_2q_exchange_model(y0, q_freqs, r, j, q_freqs, samples, 1.) # Check fidelity of statevectors self.assertGreaterEqual(state_fidelity(pulse_sim_yf, yf), 1-(10**-5)) def test_delay_instruction(self): """Test for delay instruction.""" # construct system model specifically for this hamiltonian = {} hamiltonian['h_str'] = ['0.5*r*X0||D0', '0.5*r*Y0||D1'] hamiltonian['vars'] = {'r': np.pi} hamiltonian['qub'] = {'0': 2} ham_model = HamiltonianModel.from_dict(hamiltonian) u_channel_lo = [] subsystem_list = [0] dt = 1. system_model = PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) # construct a schedule that should result in a unitary -Z if delays are correctly handled # i.e. do a pi rotation about x, sandwiched by pi/2 rotations about y in opposite directions # so that the x rotation is transformed into a z rotation. # if delays are not handled correctly this process should fail sched = Schedule() sched += Play(SamplePulse([0.5]), DriveChannel(1)) sched += Delay(1, DriveChannel(1)) sched += Play(SamplePulse([-0.5]), DriveChannel(1)) sched += Delay(1, DriveChannel(0)) sched += Play(SamplePulse([1.]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[0., 0.], memory_slots=2, shots=1) # Result of schedule should be the unitary -1j*Z, so check rotation of an X eigenstate backend_options = {'initial_state': np.array([1., 1.]) / np.sqrt(2)} results = self.backend_sim.run(qobj, system_model, backend_options).result() statevector = results.get_statevector() expected_vector = np.array([-1j, 1j]) / np.sqrt(2) self.assertGreaterEqual(state_fidelity(statevector, expected_vector), 1 - (10**-5)) # verify validity of simulation when no delays included sched = Schedule() sched += Play(SamplePulse([0.5]), DriveChannel(1)) sched += Play(SamplePulse([-0.5]), DriveChannel(1)) sched += Play(SamplePulse([1.]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[0., 0.], memory_slots=2, shots=1) backend_options = {'initial_state': np.array([1., 1.]) / np.sqrt(2)} results = self.backend_sim.run(qobj, system_model, backend_options).result() statevector = results.get_statevector() U = expm(1j * np.pi * self.Y /4) @ expm(-1j * np.pi * (self.Y / 4 + self.X / 2)) expected_vector = U @ np.array([1., 1.]) / np.sqrt(2) self.assertGreaterEqual(state_fidelity(statevector, expected_vector), 1 - (10**-5)) def test_shift_phase(self): """Test ShiftPhase command.""" omega_0 = 1.123 r = 1. system_model = self._system_model_1Q(omega_0, r) # run a schedule in which a shifted phase causes a pulse to cancel itself. # Also do it in multiple phase shifts to test accumulation sched = Schedule() amp1 = 0.12 sched += Play(SamplePulse([amp1]), DriveChannel(0)) phi1 = 0.12374 * np.pi sched += ShiftPhase(phi1, DriveChannel(0)) amp2 = 0.492 sched += Play(SamplePulse([amp2]), DriveChannel(0)) phi2 = 0.5839 * np.pi sched += ShiftPhase(phi2, DriveChannel(0)) amp3 = 0.12 + 0.21 * 1j sched += Play(SamplePulse([amp3]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_0], memory_slots=2, shots=1) y0 = np.array([1., 0]) backend_options = {'initial_state': y0} results = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = results.get_statevector() #run independent simulation samples = np.array([[amp1], [amp2 * np.exp(1j * phi1)], [amp3 * np.exp(1j * (phi1 + phi2))]]) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_0]), samples, 1.) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1 - (10**-5)) # run another schedule with only a single shift phase to verify sched = Schedule() amp1 = 0.12 sched += Play(SamplePulse([amp1]), DriveChannel(0)) phi1 = 0.12374 * np.pi sched += ShiftPhase(phi1, DriveChannel(0)) amp2 = 0.492 sched += Play(SamplePulse([amp2]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_0], memory_slots=2, shots=1) y0 = np.array([1., 0]) backend_options = {'initial_state': y0} results = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = results.get_statevector() #run independent simulation samples = np.array([[amp1], [amp2 * np.exp(1j * phi1)]]) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_0]), samples, 1.) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1 - (10**-5)) def test_set_phase(self): """Test SetPhase command. Similar to the ShiftPhase test but includes a mixing of ShiftPhase and SetPhase instructions to test relative vs absolute changes""" omega_0 = 1.3981 r = 1. system_model = self._system_model_1Q(omega_0, r) # intermix shift and set phase instructions to verify absolute v.s. relative changes sched = Schedule() amp1 = 0.12 sched += Play(SamplePulse([amp1]), DriveChannel(0)) phi1 = 0.12374 * np.pi sched += ShiftPhase(phi1, DriveChannel(0)) amp2 = 0.492 sched += Play(SamplePulse([amp2]), DriveChannel(0)) phi2 = 0.5839 * np.pi sched += SetPhase(phi2, DriveChannel(0)) amp3 = 0.12 + 0.21 * 1j sched += Play(SamplePulse([amp3]), DriveChannel(0)) phi3 = 0.1 * np.pi sched += ShiftPhase(phi3, DriveChannel(0)) amp4 = 0.2 + 0.3 * 1j sched += Play(SamplePulse([amp4]), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_0], memory_slots=2, shots=1) y0 = np.array([1., 0.]) backend_options = {'initial_state': y0} results = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = results.get_statevector() #run independent simulation samples = np.array([[amp1], [amp2 * np.exp(1j * phi1)], [amp3 * np.exp(1j * phi2)], [amp4 * np.exp(1j * (phi2 + phi3))]]) indep_yf = simulate_1q_model(y0, omega_0, r, np.array([omega_0]), samples, 1.) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, indep_yf), 1 - (10**-5)) def test_set_phase_rwa(self): """Test SetPhase command using an RWA approximate solution.""" omega_0 = 5.123 r = 0.01 system_model = self._system_model_1Q(omega_0, r) sched = Schedule() sched += SetPhase(np.pi / 2, DriveChannel(0)) sched += Play(SamplePulse(np.ones(100)), DriveChannel(0)) sched |= Acquire(1, AcquireChannel(0), MemorySlot(0)) << sched.duration qobj = assemble([sched], backend=self.backend_sim, meas_level=2, meas_return='single', meas_map=[[0]], qubit_lo_freq=[omega_0], memory_slots=2, shots=1) y0 = np.array([1., 1.]) / np.sqrt(2) backend_options = {'initial_state': y0} results = self.backend_sim.run(qobj, system_model, backend_options).result() pulse_sim_yf = results.get_statevector() #run independent simulation phases = np.exp((-1j * 2 * np.pi * omega_0 * np.array([1, -1]) / 2) * 100) approx_yf = phases * (expm(-1j * (np.pi / 2) * self.Y) @ y0) self.assertGreaterEqual(state_fidelity(pulse_sim_yf, approx_yf), 0.99) def _system_model_1Q(self, omega_0, r): """Constructs a standard model for a 1 qubit system. Args: omega_0 (float): qubit frequency r (float): drive strength Returns: PulseSystemModel: model for qubit system """ hamiltonian = {} hamiltonian['h_str'] = ['2*np.pi*omega0*0.5*Z0', '2*np.pi*r*0.5*X0||D0'] hamiltonian['vars'] = {'omega0': omega_0, 'r': r} hamiltonian['qub'] = {'0': 2} ham_model = HamiltonianModel.from_dict(hamiltonian) u_channel_lo = [] subsystem_list = [0] dt = 1. return PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) def _1Q_constant_sched(self, total_samples, amp=1.): """Creates a runnable schedule for 1Q with a constant drive pulse of a given length. Args: total_samples (int): length of pulse amp (float): amplitude of constant pulse (can be complex) Returns: schedule (pulse schedule): schedule with a drive pulse followed by an acquire """ # set up constant pulse for doing a pi pulse drive_pulse = SamplePulse(amp * np.ones(total_samples)) schedule = Schedule() schedule |= Play(drive_pulse, DriveChannel(0)) schedule |= Acquire(total_samples, AcquireChannel(0), MemorySlot(0)) << schedule.duration return schedule def _system_model_2Q(self, j): """Constructs a model for a 2 qubit system with a U channel controlling coupling and no other Hamiltonian terms. Args: j (float): coupling strength Returns: PulseSystemModel: model for qubit system """ hamiltonian = {} hamiltonian['h_str'] = ['a*X0||D0', 'a*X0||D1', '2*np.pi*j*0.25*(Z0*X1)||U0'] hamiltonian['vars'] = {'a': 0, 'j': j} hamiltonian['qub'] = {'0': 2, '1': 2} ham_model = HamiltonianModel.from_dict(hamiltonian) # set the U0 to have frequency of drive channel 0 u_channel_lo = [[UchannelLO(0, 1.0+0.0j)]] subsystem_list = [0, 1] dt = 1. return PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) def _2Q_constant_sched(self, total_samples, amp=1., u_idx=0): """Creates a runnable schedule with a single pulse on a U channel for two qubits. Args: total_samples (int): length of pulse amp (float): amplitude of constant pulse (can be complex) u_idx (int): index of U channel Returns: schedule (pulse schedule): schedule with a drive pulse followed by an acquire """ # set up constant pulse for doing a pi pulse drive_pulse = SamplePulse(amp * np.ones(total_samples)) schedule = Schedule() schedule |= Play(drive_pulse, ControlChannel(u_idx)) schedule |= Acquire(total_samples, AcquireChannel(0), MemorySlot(0)) << total_samples schedule |= Acquire(total_samples, AcquireChannel(1), MemorySlot(1)) << total_samples return schedule def _system_model_3Q(self, j, subsystem_list=[0, 2]): """Constructs a model for a 3 qubit system, with the goal that the restriction to [0, 2] and to qubits [1, 2] is the same as in _system_model_2Q Args: j (float): coupling strength subsystem_list (list): list of subsystems to include Returns: PulseSystemModel: model for qubit system """ hamiltonian = {} hamiltonian['h_str'] = ['2*np.pi*j*0.25*(Z0*X2)||U0', '2*np.pi*j*0.25*(Z1*X2)||U1'] hamiltonian['vars'] = {'j': j} hamiltonian['qub'] = {'0': 2, '1': 2, '2': 2} ham_model = HamiltonianModel.from_dict(hamiltonian, subsystem_list=subsystem_list) # set the U0 to have frequency of drive channel 0 u_channel_lo = [[UchannelLO(0, 1.0 + 0.0j)], [UchannelLO(0, 1.0 + 0.0j)]] dt = 1. return PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt) def _3Q_constant_sched(self, total_samples, amp=1., u_idx=0, subsystem_list=[0, 2]): """Creates a runnable schedule for the 3Q system after the system is restricted to 2 qubits. Args: total_samples (int): length of pulse amp (float): amplitude of constant pulse (can be complex) u_idx (int): index of U channel subsystem_list (list): list of qubits to restrict to Returns: schedule (pulse schedule): schedule with a drive pulse followed by an acquire """ # set up constant pulse for doing a pi pulse drive_pulse = SamplePulse(amp * np.ones(total_samples)) schedule = Schedule() schedule |= Play(drive_pulse, ControlChannel(u_idx)) for idx in subsystem_list: schedule |= Acquire(total_samples, AcquireChannel(idx), MemorySlot(idx)) << total_samples return schedule def _system_model_3d_oscillator(self, freq, anharm, r): """Model for a duffing oscillator truncated to 3 dimensions. Args: freq (float): frequency of the oscillator anharm (float): anharmonicity of the oscillator r (float): drive strength Returns: PulseSystemModel: model for oscillator system """ hamiltonian = {} hamiltonian['h_str'] = ['np.pi*(2*v-alpha)*O0', 'np.pi*alpha*O0*O0', '2*np.pi*r*X0||D0'] hamiltonian['vars'] = {'v' : freq, 'alpha': anharm, 'r': r} hamiltonian['qub'] = {'0': 3} ham_model = HamiltonianModel.from_dict(hamiltonian) u_channel_lo = [] subsystem_list = [0] dt = 1. return PulseSystemModel(hamiltonian=ham_model, u_channel_lo=u_channel_lo, subsystem_list=subsystem_list, dt=dt)