def test_var_hermitian(self, tol, qvm): """Tests for variance calculation using an arbitrary Hermitian observable""" dev = plf.WavefunctionDevice(wires=2) phi = 0.543 theta = 0.6543 # test correct variance for <H> of a rotated state H = np.array([[4, -1 + 6j], [-1 - 6j, 2]]) with qml.tape.QuantumTape() as tape: qml.RX(phi, wires=[0]) qml.RY(theta, wires=[0]) O = qml.var(qml.Hermitian(H, wires=[0])) # test correct variance for <Z> of a rotated state dev.apply(tape.operations, rotations=tape.diagonalizing_gates) var = dev.var(O) 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=tol)
def test_apply(self, op, apply_unitary, tol): """Test the application of gates to a state""" dev = plf.WavefunctionDevice(wires=3) obs = qml.expval(qml.PauliZ(0)) if op.name == "QubitUnitary": state = apply_unitary(U, 3) elif op.name == "BasisState": state = np.array([0, 0, 0, 0, 0, 0, 0, 1]) elif op.name == "CPHASE": state = apply_unitary(test_operation_map["CPHASE"](0.432, 2), 3) elif op.name == "ISWAP": state = apply_unitary(test_operation_map["ISWAP"], 3) elif op.name == "PSWAP": state = apply_unitary(test_operation_map["PSWAP"](0.432), 3) else: state = apply_unitary(op.matrix, 3) with qml.tape.QuantumTape() as tape: qml.apply(op) obs dev.apply(tape.operations, rotations=tape.diagonalizing_gates) # verify the device is now in the expected state self.assertAllAlmostEqual(dev._state, state, delta=tol)
def test_sample_values_hermitian(self, tol): """Tests if the samples of a Hermitian observable returned by sample have the correct values """ dev = plf.WavefunctionDevice(wires=1, shots=1_000_000) theta = 0.543 A = np.array([[1, 2j], [-2j, 0]]) with qml.tape.QuantumTape() as tape: qml.RX(theta, wires=[0]) O = qml.sample(qml.Hermitian(A, wires=[0])) # test correct variance for <Z> of a rotated state dev.apply(tape.operations, rotations=tape.diagonalizing_gates) dev._samples = dev.generate_samples() s1 = dev.sample(O.obs) # s1 should only contain the eigenvalues of # the hermitian matrix eigvals = np.linalg.eigvalsh(A) assert np.allclose(sorted(list(set(s1))), sorted(eigvals), atol=tol, rtol=0) # the analytic mean is 2*sin(theta)+0.5*cos(theta)+0.5 assert np.allclose( np.mean(s1), 2 * np.sin(theta) + 0.5 * np.cos(theta) + 0.5, atol=0.1, rtol=0 ) # the analytic variance is 0.25*(sin(theta)-4*cos(theta))^2 assert np.allclose( np.var(s1), 0.25 * (np.sin(theta) - 4 * np.cos(theta)) ** 2, atol=0.1, rtol=0 )
def test_expand_one(self, tol): """Test that a 1 qubit gate correctly expands to 3 qubits.""" dev = plf.WavefunctionDevice(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=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=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=tol) # test exception raised if U is not 2x2 matrix with pytest.raises(ValueError, message="2x2 matrix required"): dev.expand_one(U2, [0]) # test exception raised if more than one subsystem provided with pytest.raises(ValueError, message="One target subsystem required"): dev.expand_one(U, [0, 1])
def test_var_hermitian(self, tol, qvm): """Tests for variance calculation using an arbitrary Hermitian observable""" dev = plf.WavefunctionDevice(wires=2) phi = 0.543 theta = 0.6543 # test correct variance for <H> of a rotated state H = np.array([[4, -1 + 6j], [-1 - 6j, 2]]) circuit_operations = [qml.RX(phi, wires=[0]), qml.RY(theta, wires=[0])] O = qml.var(qml.Hermitian(H, wires=[0])) observables = [O] circuit_graph = qml.CircuitGraph(circuit_operations + observables, {}, dev.wires) dev.apply(circuit_graph.operations, rotations=circuit_graph.diagonalizing_gates) var = dev.var(qml.Hermitian(H, wires=[0])) 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=tol)
def test_apply(self, gate, apply_unitary, tol, qvm, compiler): """Test the application of gates to a state""" dev = plf.WavefunctionDevice(wires=3) try: # get the equivalent pennylane operation class op = getattr(qml.ops, gate) except AttributeError: # get the equivalent pennylane-forest operation class op = getattr(plf, gate) # the list of wires to apply the operation to w = list(range(op.num_wires)) obs = qml.expval(qml.PauliZ(0)) if op.par_domain == "A": # the parameter is an array if gate == "QubitUnitary": p = np.array(U) w = [0] state = apply_unitary(U, 3) elif gate == "BasisState": p = np.array([1, 1, 1]) state = np.array([0, 0, 0, 0, 0, 0, 0, 1]) w = list(range(dev.num_wires)) with qml.tape.QuantumTape() as tape: op(p, wires=w) obs else: p = [0.432_423, 2, 0.324][:op.num_params] fn = test_operation_map[gate] 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 state = apply_unitary(O, 3) # Creating the tape using a parametrized operation if p: with qml.tape.QuantumTape() as tape: op(*p, wires=w) obs # Creating the tape using an operation that take no parameters else: with qml.tape.QuantumTape() as tape: op(wires=w) obs dev.apply(tape.operations, rotations=tape.diagonalizing_gates) res = dev.expval(obs) # verify the device is now in the expected state self.assertAllAlmostEqual(dev._state, state, delta=tol)
def test_expand_state(self): """Test that a multi-qubit state is correctly expanded for a N-qubit device""" dev = plf.WavefunctionDevice(wires=3) # expand a two qubit state to the 3 qubit device dev.state = np.array([0, 1, 1, 0])/np.sqrt(2) dev.active_wires = {0, 2} dev.expand_state() self.assertAllEqual(dev.state, np.array([0, 1, 0, 0, 1, 0, 0, 0])/np.sqrt(2)) # expand a three qubit state to the 3 qubit device dev.state = np.array([0, 1, 1, 0, 0, 1, 1, 0])/2 dev.active_wires = {0, 1, 2} dev.expand_state() self.assertAllEqual(dev.state, np.array([0, 1, 1, 0, 0, 1, 1, 0])/2)
def test_sample_values_hermitian_multi_qubit(self, tol): """Tests if the samples of a multi-qubit Hermitian observable returned by sample have the correct values """ shots = 1_000_000 dev = plf.WavefunctionDevice(wires=2, shots=shots) theta = 0.543 A = np.array([ [1, 2j, 1 - 2j, 0.5j], [-2j, 0, 3 + 4j, 1], [1 + 2j, 3 - 4j, 0.75, 1.5 - 2j], [-0.5j, 1, 1.5 + 2j, -1], ]) circuit_operations = [ qml.RX(theta, wires=[0]), qml.RY(2 * theta, wires=[1]), qml.CNOT(wires=[0, 1]), ] O = qml.sample(qml.Hermitian(A, wires=[0, 1])) observables = [O] circuit_graph = qml.CircuitGraph(circuit_operations + observables, {}, dev.wires) dev.apply(circuit_graph.operations, rotations=circuit_graph.diagonalizing_gates) dev._samples = dev.generate_samples() s1 = dev.sample(O) # s1 should only contain the eigenvalues of # the hermitian matrix eigvals = np.linalg.eigvalsh(A) assert np.allclose(sorted(list(set(s1))), sorted(eigvals), atol=tol, rtol=0) # make sure the mean matches the analytic mean expected = (88 * np.sin(theta) + 24 * np.sin(2 * theta) - 40 * np.sin(3 * theta) + 5 * np.cos(theta) - 6 * np.cos(2 * theta) + 27 * np.cos(3 * theta) + 6) / 32 assert np.allclose(np.mean(s1), expected, atol=0.1, rtol=0)
def test_var(self, tol): """Tests for variance calculation""" dev = plf.WavefunctionDevice(wires=2) dev.active_wires = {0} phi = 0.543 theta = 0.6543 # test correct variance for <Z> of a rotated state dev.apply("RX", wires=[0], par=[phi]) dev.apply("RY", wires=[0], par=[theta]) dev.pre_measure() var = 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=tol)
def test_expand_one(self, tol): """Test that a 1 qubit gate correctly expands to 3 qubits.""" dev = plf.WavefunctionDevice(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=tol) # test applied to wire 1 res = dev.expand(U, [1]) expected = np.kron(np.kron(I, U), I) self.assertAllAlmostEqual(res, expected, delta=tol) # test applied to wire 2 res = dev.expand(U, [2]) expected = np.kron(np.kron(I, I), U) self.assertAllAlmostEqual(res, expected, delta=tol)
def test_apply(self, gate, apply_unitary, tol, qvm, compiler): """Test the application of gates to a state""" dev = plf.WavefunctionDevice(wires=3) try: # get the equivalent pennylane operation class op = getattr(qml.ops, gate) except AttributeError: # get the equivalent pennylane-forest operation class op = getattr(plf, gate) # 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 == 'QubitUnitary': p = [U] w = [0] expected_out = apply_unitary(U, 3) elif gate == 'BasisState': p = [np.array([1, 1, 1])] expected_out = np.array([0, 0, 0, 0, 0, 0, 0, 1]) else: p = [0.432423, 2, 0.324][:op.num_params] fn = test_operation_map[gate] 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 expected_out = apply_unitary(O, 3) dev.apply(gate, wires=w, par=p) dev.pre_expval() # verify the device is now in the expected state self.assertAllAlmostEqual(dev.state, expected_out, delta=tol)
def test_var(self, tol, qvm): """Tests for variance calculation""" dev = plf.WavefunctionDevice(wires=2) phi = 0.543 theta = 0.6543 with qml.tape.QuantumTape() as tape: qml.RX(phi, wires=[0]) qml.RY(theta, wires=[0]) O = qml.var(qml.PauliZ(wires=[0])) # test correct variance for <Z> of a rotated state dev.apply(tape.operations, rotations=tape.diagonalizing_gates) var = dev.var(O) expected = 0.25 * (3 - np.cos(2 * theta) - 2 * np.cos(theta) ** 2 * np.cos(2 * phi)) self.assertAlmostEqual(var, expected, delta=tol)
def test_sample_values_hermitian(self, tol): """Tests if the samples of a Hermitian observable returned by sample have the correct values """ dev = plf.WavefunctionDevice(wires=1, shots=1000_000) theta = 0.543 A = np.array([[1, 2j], [-2j, 0]]) circuit_operations = [qml.RX(theta, wires=[0])] O = qml.sample(qml.Hermitian(A, wires=[0])) observables = [O] circuit_graph = qml.CircuitGraph(circuit_operations + observables, {}) dev.apply(circuit_graph.operations, rotations=circuit_graph.diagonalizing_gates) dev.generate_samples() s1 = dev.sample(O) # s1 should only contain the eigenvalues of # the hermitian matrix eigvals = np.linalg.eigvalsh(A) assert np.allclose(sorted(list(set(s1))), sorted(eigvals), atol=tol, rtol=0) # the analytic mean is 2*sin(theta)+0.5*cos(theta)+0.5 assert np.allclose(np.mean(s1), 2 * np.sin(theta) + 0.5 * np.cos(theta) + 0.5, atol=0.1, rtol=0) # the analytic variance is 0.25*(sin(theta)-4*cos(theta))^2 assert np.allclose(np.var(s1), 0.25 * (np.sin(theta) - 4 * np.cos(theta))**2, atol=0.1, rtol=0)
def test_sample_values(self, tol): """Tests if the samples returned by sample have the correct values """ dev = plf.WavefunctionDevice(wires=1, shots=10) theta = 1.5708 circuit_operations = [qml.RX(theta, wires=[0])] O = qml.sample(qml.PauliZ(0)) observables = [O] circuit_graph = qml.CircuitGraph(circuit_operations + observables, {}) dev.apply(circuit_graph.operations, rotations=circuit_graph.diagonalizing_gates) dev._samples = dev.generate_samples() s1 = dev.sample(O) # s1 should only contain 1 and -1 self.assertAllAlmostEqual(s1**2, 1, delta=tol)
def test_var_hermitian(self, tol): """Tests for variance calculation using an arbitrary Hermitian observable""" dev = plf.WavefunctionDevice(wires=2) dev.active_wires = {0} phi = 0.543 theta = 0.6543 # test correct variance for <H> of a rotated state H = np.array([[4, -1 + 6j], [-1 - 6j, 2]]) dev.apply("RX", wires=[0], par=[phi]) dev.apply("RY", wires=[0], par=[theta]) dev.pre_measure() var = 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=tol)
def test_expand_three(self, tol): """Test that a 3 qubit gate correctly expands to 4 qubits.""" dev = plf.WavefunctionDevice(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=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=tol) # test applied to wire 0,2,3 res = dev.expand(U_toffoli, [0, 2, 3]) expected = (np.kron(SWAP, np.kron(I, I)) @ np.kron(I, U_toffoli) @ np.kron(SWAP, np.kron(I, I))) self.assertAllAlmostEqual(res, expected, delta=tol) # test applied to wire 0,1,3 res = dev.expand(U_toffoli, [0, 1, 3]) expected = (np.kron(np.kron(I, I), SWAP) @ np.kron(U_toffoli, I) @ np.kron(np.kron(I, I), SWAP)) self.assertAllAlmostEqual(res, expected, delta=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=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(SWAP, np.kron(I, I)) @ np.kron( I, U_toffoli[:, rows][rows]) @ np.kron(SWAP, np.kron(I, I))) self.assertAllAlmostEqual(res, expected, delta=tol)
def test_ev(self, ev, tol): """Test that expectation values are calculated correctly""" dev = plf.WavefunctionDevice(wires=2) # start in the following initial state dev.state = np.array([1, 0, 1, 1])/np.sqrt(3) dev.active_wires = {0, 1} # get the equivalent pennylane operation class op = getattr(qml.expval.qubit, ev) O = test_operation_map[ev] # calculate the expected output if op.num_wires == 1 or op.num_wires == 0: expected_out = dev.state.conj() @ np.kron(O, I) @ dev.state elif op.num_wires == 2: expected_out = dev.state.conj() @ O @ dev.state res = dev.ev(O, wires=[0]) # verify the device is now in the expected state self.assertAllAlmostEqual(res, expected_out, delta=tol)
def test_sample_values(self, tol): """Tests if the samples returned by sample have the correct values """ dev = plf.WavefunctionDevice(wires=1, shots=10) theta = 1.5708 circuit_operations = [] O = qml.sample(qml.PauliZ(0)) observables = [O] with qml.tape.QuantumTape() as tape: qml.RX(theta, wires=[0]) qml.sample(qml.PauliZ(0)) dev.apply(tape._ops, rotations=tape.diagonalizing_gates) dev._samples = dev.generate_samples() s1 = dev.sample(O.obs) # s1 should only contain 1 and -1 self.assertAllAlmostEqual(s1**2, 1, delta=tol)
def test_var(self, tol, qvm): """Tests for variance calculation""" dev = plf.WavefunctionDevice(wires=2) phi = 0.543 theta = 0.6543 circuit_operations = [qml.RX(phi, wires=[0]), qml.RY(theta, wires=[0])] O = qml.var(qml.PauliZ(wires=[0])) observables = [O] circuit_graph = qml.CircuitGraph(circuit_operations + observables, {}) # test correct variance for <Z> of a rotated state dev.apply(circuit_graph.operations, rotations=circuit_graph.diagonalizing_gates) var = dev.var(qml.PauliZ(0)) expected = 0.25 * (3 - np.cos(2 * theta) - 2 * np.cos(theta)**2 * np.cos(2 * phi)) self.assertAlmostEqual(var, expected, delta=tol)
def test_expand_two(self, tol): """Test that a 2 qubit gate correctly expands to 3 qubits.""" dev = plf.WavefunctionDevice(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=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=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=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=tol) # test exception raised if unphysical subsystems provided with pytest.raises( ValueError, match="Invalid target subsystems provided in 'wires' argument." ): dev.expand(U2, [-1, 5]) # test exception raised if incorrect sized matrix provided with pytest.raises(ValueError, match="Matrix parameter must be of size"): dev.expand(U, [0, 1])