def test_expand_one(self): """Test that a 1 qubit gate correctly expands to 3 qubits.""" self.logTestName() dev = DefaultQubit(wires=3) # test applied to wire 0 res = dev.expand_one(U, [0]) expected = np.kron(np.kron(U, I), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 1 res = dev.expand_one(U, [1]) expected = np.kron(np.kron(I, U), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 2 res = dev.expand_one(U, [2]) expected = np.kron(np.kron(I, I), U) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test exception raised if U is not 2x2 matrix with self.assertRaisesRegex(ValueError, "2x2 matrix required"): dev.expand_one(U2, [0]) # test exception raised if more than one subsystem provided with self.assertRaisesRegex(ValueError, "One target subsystem required"): dev.expand_one(U, [0, 1])
def test_expand_two(self): """Test that a 2 qubit gate correctly expands to 3 qubits.""" self.logTestName() dev = DefaultQubit(wires=4) # test applied to wire 0+1 res = dev.expand(U2, [0, 1]) expected = np.kron(np.kron(U2, I), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 1+2 res = dev.expand(U2, [1, 2]) expected = np.kron(np.kron(I, U2), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 2+3 res = dev.expand(U2, [2, 3]) expected = np.kron(np.kron(I, I), U2) self.assertAllAlmostEqual(res, expected, delta=self.tol) # CNOT with target on wire 1 res = dev.expand(CNOT, [1, 0]) rows = np.array([0, 2, 1, 3]) expected = np.kron(np.kron(CNOT[:, rows][rows], I), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test exception raised if unphysical subsystems provided with self.assertRaisesRegex(ValueError, "Invalid target subsystems provided in 'wires' argument"): dev.expand(U2, [-1, 5])
def setUp(self): super().setUp() self.devices = [DefaultQubit(wires=self.num_subsystems)] if self.args.device == 'simulator' or self.args.device == 'all': self.devices.append( ProjectQSimulator(wires=self.num_subsystems, verbose=True)) self.devices.append( ProjectQSimulator(wires=self.num_subsystems, shots=20000000, verbose=True)) if self.args.device == 'ibm' or self.args.device == 'all': ibm_options = pennylane.default_config['projectq.ibm'] if "user" in ibm_options and "password" in ibm_options: self.devices.append( ProjectQIBMBackend(wires=self.num_subsystems, use_hardware=False, num_runs=8 * 1024, user=ibm_options['user'], password=ibm_options['password'], verbose=True)) else: log.warning( "Skipping test of the ProjectQIBMBackend device because IBM login credentials could not be found in the PennyLane configuration file." ) if self.args.device == 'classical' or self.args.device == 'all': self.devices.append( ProjectQClassicalSimulator(wires=self.num_subsystems, verbose=True))
def test_expand_one(self): """Test that a 1 qubit gate correctly expands to 3 qubits.""" self.logTestName() dev = DefaultQubit(wires=3) # test applied to wire 0 res = dev.expand(U, [0]) expected = np.kron(np.kron(U, I), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 1 res = dev.expand(U, [1]) expected = np.kron(np.kron(I, U), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 2 res = dev.expand(U, [2]) expected = np.kron(np.kron(I, I), U) self.assertAllAlmostEqual(res, expected, delta=self.tol)
def test_expand_three(self): """Test that a 3 qubit gate correctly expands to 4 qubits.""" self.logTestName() dev = DefaultQubit(wires=4) # test applied to wire 0,1,2 res = dev.expand(U_toffoli, [0, 1, 2]) expected = np.kron(U_toffoli, I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 1,2,3 res = dev.expand(U_toffoli, [1, 2, 3]) expected = np.kron(I, U_toffoli) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 0,2,3 res = dev.expand(U_toffoli, [0, 2, 3]) expected = np.kron(U_swap, np.kron(I, I)) @ np.kron(I, U_toffoli) @ np.kron(U_swap, np.kron(I, I)) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 0,1,3 res = dev.expand(U_toffoli, [0, 1, 3]) expected = np.kron(np.kron(I, I), U_swap) @ np.kron(U_toffoli, I) @ np.kron(np.kron(I, I), U_swap) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 3, 1, 2 res = dev.expand(U_toffoli, [3, 1, 2]) # change the control qubit on the Toffoli gate rows = np.array([0, 4, 1, 5, 2, 6, 3, 7]) expected = np.kron(I, U_toffoli[:, rows][rows]) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 3, 0, 2 res = dev.expand(U_toffoli, [3, 0, 2]) # change the control qubit on the Toffoli gate rows = np.array([0, 4, 1, 5, 2, 6, 3, 7]) expected = np.kron(U_swap, np.kron(I, I)) @ np.kron(I, U_toffoli[:, rows][rows]) @ np.kron(U_swap, np.kron(I, I)) self.assertAllAlmostEqual(res, expected, delta=self.tol)
def setUp(self): super().setUp() self.devices = [DefaultQubit(wires=self.num_subsystems)] if self.args.provider == 'basicaer' or self.args.provider == 'all': self.devices.append( BasicAerQiskitDevice(wires=self.num_subsystems)) if self.args.provider == 'aer' or self.args.provider == 'all': self.devices.append(AerQiskitDevice(wires=self.num_subsystems)) if self.args.provider == 'legacy' or self.args.provider == 'all': self.devices.append( LegacySimulatorsQiskitDevice(wires=self.num_subsystems)) if self.args.provider == 'ibm' or self.args.provider == 'all': if IBMQX_TOKEN is not None: self.devices.append( IbmQQiskitDevice(wires=self.num_subsystems, num_runs=8 * 1024, ibmqx_token=IBMQX_TOKEN)) else: log.warning( "Skipping test of the IbmQQiskitDevice device because IBM login credentials could not be found in the PennyLane configuration file." )
def setUp(self): self.dev = DefaultQubit(wires=2, shots=0)
class TestDefaultQubitDevice(BaseTest): """Test the default qubit device. The test ensures that the device is properly applying qubit operations and calculating the correct observables.""" def setUp(self): self.dev = DefaultQubit(wires=2, shots=0) def test_operation_map(self): """Test that default qubit device supports all PennyLane discrete gates.""" self.logTestName() self.assertEqual(set(qml.ops._qubit__ops__), set(self.dev._operation_map)) def test_observable_map(self): """Test that default qubit device supports all PennyLane discrete observables.""" self.logTestName() self.assertEqual( set(qml.ops._qubit__obs__) | {"Identity"}, set(self.dev._observable_map)) def test_get_operator_matrix(self): """Test the the correct matrix is returned given an operation name""" self.logTestName() for name, fn in { **self.dev._operation_map, **self.dev._observable_map, }.items(): try: op = getattr(qml.ops, name) except AttributeError: op = getattr(qml.expval, name) p = [0.432423, -0.12312, 0.324][:op.num_params] if name == "QubitStateVector": p = [np.array([1, 0, 0, 0])] elif name == "QubitUnitary": p = [U] elif name == "Hermitian": p = [H] res = self.dev._get_operator_matrix(name, p) if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. expected = fn(*p) else: # otherwise, the operation is simply an array. expected = fn self.assertAllAlmostEqual(res, expected, delta=self.tol) def test_apply(self): """Test the application of gates to a state""" self.logTestName() self.dev.reset() # loop through all supported operations for gate_name, fn in self.dev._operation_map.items(): log.debug("\tTesting %s gate...", gate_name) # start in the state |00> self.dev._state = np.array([1, 0, 0, 0]) # get the equivalent pennylane operation class op = getattr(qml.ops, gate_name) # the list of wires to apply the operation to w = list(range(op.num_wires)) if op.par_domain == "A": # the parameter is an array if gate_name == "QubitStateVector": p = [np.array([1, 0, 1, 1]) / np.sqrt(3)] expected_out = p elif gate_name == "QubitUnitary": p = [U] w = [0] expected_out = np.kron(U @ np.array([1, 0]), np.array([1, 0])) elif gate_name == "BasisState": p = [np.array([1, 1])] expected_out = np.array([0, 0, 0, 1]) elif op.par_domain == "N": # the parameter is an integer p = [1, 3, 4][:op.num_params] else: # the parameter is a float p = [0.432423, -0.12312, 0.324][:op.num_params] if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. O = fn(*p) else: # otherwise, the operation is simply an array. O = fn # calculate the expected output if op.num_wires == 1: expected_out = np.kron(O @ np.array([1, 0]), np.array([1, 0])) elif op.num_wires == 2: expected_out = O @ self.dev._state self.dev.apply(gate_name, wires=w, par=p) # verify the device is now in the expected state self.assertAllAlmostEqual(self.dev._state, expected_out, delta=self.tol) def test_apply_errors(self): """Test that apply fails for incorrect state preparation, and > 2 qubit gates""" self.logTestName() with self.assertRaisesRegex( ValueError, r"State vector must be of length 2\*\*wires."): p = [np.array([1, 0, 1, 1, 1]) / np.sqrt(3)] self.dev.apply("QubitStateVector", wires=[0, 1], par=[p]) with self.assertRaisesRegex( ValueError, "BasisState parameter must be an array of 0 or 1 integers of length at most 2.", ): self.dev.apply("BasisState", wires=[0, 1], par=[np.array([-0.2, 4.2])]) with self.assertRaisesRegex( ValueError, "The default.qubit plugin can apply BasisState only to all of the 2 wires.", ): self.dev.apply("BasisState", wires=[0, 1, 2], par=[np.array([0, 1])]) def test_ev(self): """Test that expectation values are calculated correctly""" self.logTestName() self.dev.reset() # loop through all supported observables for name, fn in self.dev._observable_map.items(): log.debug("\tTesting %s observable...", name) # start in the state |00> self.dev._state = np.array([1, 0, 1, 1]) / np.sqrt(3) # get the equivalent pennylane operation class op = getattr(qml.ops, name) if op.par_domain == "A": # the parameter is an array p = [H] else: # the parameter is a float p = [0.432423, -0.12312, 0.324][:op.num_params] if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. O = fn(*p) else: # otherwise, the operation is simply an array. O = fn # calculate the expected output if op.num_wires == 1 or op.num_wires == 0: expected_out = self.dev._state.conj() @ np.kron( O, I) @ self.dev._state elif op.num_wires == 2: expected_out = self.dev._state.conj() @ O @ self.dev._state else: raise NotImplementedError( "Test for operations with num_wires=" + op.num_wires + " not implemented.") res = self.dev.ev(O, wires=[0]) # verify the device is now in the expected state self.assertAllAlmostEqual(res, expected_out, delta=self.tol) # text warning raised if matrix is complex with pytest.warns(RuntimeWarning, match='Nonvanishing imaginary part'): self.dev.ev(H + 1j, [0]) def test_var_pauliz(self): """Test that variance of PauliZ is the same as I-<Z>^2""" self.logTestName() self.dev.reset() phi = 0.543 theta = 0.6543 self.dev.apply('RX', wires=[0], par=[phi]) self.dev.apply('RY', wires=[0], par=[theta]) var = self.dev.var('PauliZ', [0], []) mean = self.dev.expval('PauliZ', [0], []) self.assertAlmostEqual(var, 1 - mean**2, delta=self.tol) def test_var_pauliz_rotated_state(self): """test correct variance for <Z> of a rotated state""" self.logTestName() self.dev.reset() phi = 0.543 theta = 0.6543 self.dev.apply('RX', wires=[0], par=[phi]) self.dev.apply('RY', wires=[0], par=[theta]) var = self.dev.var('PauliZ', [0], []) expected = 0.25 * (3 - np.cos(2 * theta) - 2 * np.cos(theta)**2 * np.cos(2 * phi)) self.assertAlmostEqual(var, expected, delta=self.tol) def test_var_hermitian_rotated_state(self): """test correct variance for <H> of a rotated state""" self.logTestName() self.dev.reset() phi = 0.543 theta = 0.6543 H = np.array([[4, -1 + 6j], [-1 - 6j, 2]]) self.dev.apply('RX', wires=[0], par=[phi]) self.dev.apply('RY', wires=[0], par=[theta]) var = self.dev.var('Hermitian', [0], [H]) expected = 0.5*(2*np.sin(2*theta)*np.cos(phi)**2+24*np.sin(phi)\ *np.cos(phi)*(np.sin(theta)-np.cos(theta))+35*np.cos(2*phi)+39) self.assertAlmostEqual(var, expected, delta=self.tol)
class TestDefaultQubitDevice(BaseTest): """Test the default qubit device. The test ensures that the device is properly applying qubit operations and calculating the correct observables.""" def setUp(self): self.dev = DefaultQubit(wires=2, shots=0) def test_operation_map(self): """Test that default qubit device supports all PennyLane discrete gates.""" self.logTestName() self.assertEqual(set(qml.ops.qubit.__all__), set(self.dev._operation_map)) def test_expectation_map(self): """Test that default qubit device supports all PennyLane discrete expectations.""" self.logTestName() self.assertEqual( set(qml.expval.qubit.__all__) | {"Identity"}, set(self.dev._expectation_map)) def test_expand_one(self): """Test that a 1 qubit gate correctly expands to 3 qubits.""" self.logTestName() dev = DefaultQubit(wires=3) # test applied to wire 0 res = dev.expand_one(U, [0]) expected = np.kron(np.kron(U, I), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 1 res = dev.expand_one(U, [1]) expected = np.kron(np.kron(I, U), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 2 res = dev.expand_one(U, [2]) expected = np.kron(np.kron(I, I), U) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test exception raised if U is not 2x2 matrix with self.assertRaisesRegex(ValueError, "2x2 matrix required"): dev.expand_one(U2, [0]) # test exception raised if more than one subsystem provided with self.assertRaisesRegex(ValueError, "One target subsystem required"): dev.expand_one(U, [0, 1]) def test_expand_two(self): """Test that a 2 qubit gate correctly expands to 3 qubits.""" self.logTestName() dev = DefaultQubit(wires=4) # test applied to wire 0+1 res = dev.expand_two(U2, [0, 1]) expected = np.kron(np.kron(U2, I), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 1+2 res = dev.expand_two(U2, [1, 2]) expected = np.kron(np.kron(I, U2), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test applied to wire 2+3 res = dev.expand_two(U2, [2, 3]) expected = np.kron(np.kron(I, I), U2) self.assertAllAlmostEqual(res, expected, delta=self.tol) # CNOT with target on wire 1 res = dev.expand_two(CNOT, [1, 0]) rows = np.array([0, 2, 1, 3]) expected = np.kron(np.kron(CNOT[:, rows][rows], I), I) self.assertAllAlmostEqual(res, expected, delta=self.tol) # test exception raised if U is not 4x4 matrix with self.assertRaisesRegex(ValueError, "4x4 matrix required"): dev.expand_two(U, [0]) # test exception raised if two subsystems are not provided with self.assertRaisesRegex(ValueError, "Two target subsystems required"): dev.expand_two(U2, [0]) # test exception raised if unphysical subsystems provided with self.assertRaisesRegex(ValueError, "Bad target subsystems."): dev.expand_two(U2, [-1, 5]) def test_expand_multi(self): """Test that any arbitrary qubit gate correctly expands to the full system.""" pass def test_get_operator_matrix(self): """Test the the correct matrix is returned given an operation name""" self.logTestName() for name, fn in { **self.dev._operation_map, **self.dev._expectation_map, }.items(): try: op = qml.ops.__getattribute__(name) except AttributeError: op = qml.expval.__getattribute__(name) p = [0.432423, -0.12312, 0.324][:op.num_params] if name == "QubitStateVector": p = [np.array([1, 0, 0, 0])] elif name == "QubitUnitary": p = [U] elif name == "Hermitian": p = [H] res = self.dev._get_operator_matrix(name, p) if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. expected = fn(*p) else: # otherwise, the operation is simply an array. expected = fn self.assertAllAlmostEqual(res, expected, delta=self.tol) def test_apply(self): """Test the application of gates to a state""" self.logTestName() self.dev.reset() # loop through all supported operations for gate_name, fn in self.dev._operation_map.items(): log.debug("\tTesting %s gate...", gate_name) # start in the state |00> self.dev._state = np.array([1, 0, 0, 0]) # get the equivalent pennylane operation class op = qml.ops.__getattribute__(gate_name) # the list of wires to apply the operation to w = list(range(op.num_wires)) if op.par_domain == "A": # the parameter is an array if gate_name == "QubitStateVector": p = [np.array([1, 0, 1, 1]) / np.sqrt(3)] expected_out = p elif gate_name == "QubitUnitary": p = [U] w = [0] expected_out = np.kron(U @ np.array([1, 0]), np.array([1, 0])) elif gate_name == "BasisState": p = [np.array([1, 1])] expected_out = np.array([0, 0, 0, 1]) elif op.par_domain == "N": # the parameter is an integer p = [1, 3, 4][:op.num_params] else: # the parameter is a float p = [0.432423, -0.12312, 0.324][:op.num_params] if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. O = fn(*p) else: # otherwise, the operation is simply an array. O = fn # calculate the expected output if op.num_wires == 1: expected_out = np.kron(O @ np.array([1, 0]), np.array([1, 0])) elif op.num_wires == 2: expected_out = O @ self.dev._state self.dev.apply(gate_name, wires=w, par=p) # verify the device is now in the expected state self.assertAllAlmostEqual(self.dev._state, expected_out, delta=self.tol) def test_apply_errors(self): """Test that apply fails for incorrect state preparation, and > 2 qubit gates""" self.logTestName() with self.assertRaisesRegex( ValueError, r"State vector must be of length 2\*\*wires."): p = [np.array([1, 0, 1, 1, 1]) / np.sqrt(3)] self.dev.apply("QubitStateVector", wires=[0, 1], par=[p]) with self.assertRaisesRegex( ValueError, "BasisState parameter must be an array of 0 or 1 integers of length at most 2.", ): self.dev.apply("BasisState", wires=[0, 1], par=[np.array([-0.2, 4.2])]) with self.assertRaisesRegex( ValueError, "The default.qubit plugin can apply BasisState only to all of the 2 wires.", ): self.dev.apply("BasisState", wires=[0, 1, 2], par=[np.array([0, 1])]) with self.assertRaisesRegex( ValueError, "This plugin supports only one- and two-qubit gates."): self.dev.apply("QubitUnitary", wires=[0, 1, 2], par=[U2]) def test_ev(self): """Test that expectation values are calculated correctly""" self.logTestName() self.dev.reset() # loop through all supported observables for name, fn in self.dev._expectation_map.items(): print(name) log.debug("\tTesting %s observable...", name) # start in the state |00> self.dev._state = np.array([1, 0, 1, 1]) / np.sqrt(3) # get the equivalent pennylane operation class op = qml.expval.__getattribute__(name) if op.par_domain == "A": # the parameter is an array p = [H] else: # the parameter is a float p = [0.432423, -0.12312, 0.324][:op.num_params] if callable(fn): # if the default.qubit is an operation accepting parameters, # initialise it using the parameters generated above. O = fn(*p) else: # otherwise, the operation is simply an array. O = fn print("op.num_wires=" + str(op.num_wires)) # calculate the expected output if op.num_wires == 1 or op.num_wires == 0: expected_out = self.dev._state.conj() @ np.kron( O, I) @ self.dev._state elif op.num_wires == 2: expected_out = self.dev._state.conj() @ O @ self.dev._state else: raise NotImplementedError( "Test for operations with num_wires=" + op.num_wires + " not implemented.") res = self.dev.ev(O, wires=[0]) # verify the device is now in the expected state self.assertAllAlmostEqual(res, expected_out, delta=self.tol) # text exception raised if matrix is not 2x2 or 4x4 with self.assertRaisesRegex( ValueError, "Only one and two-qubit expectation is supported."): self.dev.ev(U_toffoli, [0]) # text warning raised if matrix is complex with self.assertLogs(level="WARNING") as l: self.dev.ev(H + 1j, [0]) self.assertEqual(len(l.output), 1) self.assertEqual(len(l.records), 1) self.assertIn("Nonvanishing imaginary part", l.output[0])