def circuit1(x): qml.RX(x, wires=0) qml.CRX(x, wires=[0, 1]) return qml.expval(qml.PauliZ(0))
with QuantumTape() as tape: qml.WireCut(wires=(0, 1)) _, ax = tape_mpl(tape) layer = 0 assert len(ax.lines) == 2 assert len(ax.collections) == 2 plt.close() controlled_data = [ (qml.CY(wires=(0, 1)), "Y"), (qml.CRX(1.2345, wires=(0, 1)), "RX"), (qml.CRot(1.2, 2.2, 3.3, wires=(0, 1)), "Rot"), ] class TestControlledGates: """Tests generic controlled gates""" width = 0.75 - 2 * 0.2 @pytest.mark.parametrize("op, label", controlled_data) def test_control_gates(self, op, label): """Test a variety of non-special gates. Checks control wires are drawn, and that a box is drawn over the target wires.""" with QuantumTape() as tape:
class TestRepresentationResolver: """Test the RepresentationResolver class.""" @pytest.mark.parametrize( "list,element,index,list_after", [ ([1, 2, 3], 2, 1, [1, 2, 3]), ([1, 2, 2, 3], 2, 1, [1, 2, 2, 3]), ([1, 2, 3], 4, 3, [1, 2, 3, 4]), ], ) def test_index_of_array_or_append(self, list, element, index, list_after): """Test the method index_of_array_or_append.""" assert RepresentationResolver.index_of_array_or_append(element, list) == index assert list == list_after @pytest.mark.parametrize("par,expected", [ (3, "3"), (5.236422, "5.236"), ]) def test_single_parameter_representation(self, unicode_representation_resolver, par, expected): """Test that single parameters are properly resolved.""" assert unicode_representation_resolver.single_parameter_representation( par) == expected def test_single_parameter_representation_variable( self, unicode_representation_resolver, variable): """Test that variables are properly resolved.""" assert unicode_representation_resolver.single_parameter_representation( variable) == "2" def test_single_parameter_representation_kwarg_variable( self, unicode_representation_resolver, kwarg_variable): """Test that kwarg variables are properly resolved.""" assert (unicode_representation_resolver. single_parameter_representation(kwarg_variable) == "1") @pytest.mark.parametrize("par,expected", [ (3, "3"), (5.236422, "5.236"), ]) def test_single_parameter_representation_varnames( self, unicode_representation_resolver_varnames, par, expected): """Test that single parameters are properly resolved when show_variable_names is True.""" assert (unicode_representation_resolver_varnames. single_parameter_representation(par) == expected) def test_single_parameter_representation_variable_varnames( self, unicode_representation_resolver_varnames, variable): """Test that variables are properly resolved when show_variable_names is True.""" assert (unicode_representation_resolver_varnames. single_parameter_representation(variable) == "test") def test_single_parameter_representation_kwarg_variable_varnames( self, unicode_representation_resolver_varnames, kwarg_variable): """Test that kwarg variables are properly resolved when show_variable_names is True.""" assert ( unicode_representation_resolver_varnames. single_parameter_representation(kwarg_variable) == "kwarg_test") @pytest.mark.parametrize( "op,wire,target", [ (qml.PauliX(wires=[1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 1, "X"), (qml.Toffoli(wires=[0, 2, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 2, "C"), (qml.CSWAP(wires=[0, 2, 1]), 1, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 2, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 0, "C"), (qml.PauliY(wires=[1]), 1, "Y"), (qml.PauliZ(wires=[1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 0, "C"), (qml.Identity(wires=[1]), 1, "I"), (qml.Hadamard(wires=[1]), 1, "H"), (qml.PauliRot(3.14, "XX", wires=[0, 1]), 1, "RX(3.14)"), (qml.PauliRot(3.14, "YZ", wires=[0, 1]), 1, "RZ(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 0, "RI(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 1, "RX(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 2, "RY(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 3, "RZ(3.14)"), (qml.PauliRot(3.14, "IXYZI", wires=[0, 1, 2, 3, 4 ]), 4, "RI(3.14)"), (qml.MultiRZ(3.14, wires=[0, 1]), 0, "RZ(3.14)"), (qml.MultiRZ(3.14, wires=[0, 1]), 1, "RZ(3.14)"), (qml.CRX(3.14, wires=[0, 1]), 1, "RX(3.14)"), (qml.CRX(3.14, wires=[0, 1]), 0, "C"), (qml.CRY(3.14, wires=[0, 1]), 1, "RY(3.14)"), (qml.CRY(3.14, wires=[0, 1]), 0, "C"), (qml.CRZ(3.14, wires=[0, 1]), 1, "RZ(3.14)"), (qml.CRZ(3.14, wires=[0, 1]), 0, "C"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1 ]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1]), 0, "C"), (qml.PhaseShift(3.14, wires=[0]), 0, "Rϕ(3.14)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 1, "BS(1, 2)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 0, "BS(1, 2)"), (qml.Squeezing(1, 2, wires=[1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 0, "S(1, 2)"), (qml.Displacement(1, 2, wires=[1]), 1, "D(1, 2)"), (qml.NumberOperator(wires=[1]), 1, "n"), (qml.Rotation(3.14, wires=[1]), 1, "R(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 1, "X(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 0, "C"), (qml.ControlledPhase(3.14, wires=[0, 1]), 1, "Z(3.14)"), (qml.ControlledPhase(3.14, wires=[0, 1]), 0, "C"), (qml.ThermalState(3, wires=[1]), 1, "Thermal(3)"), ( qml.GaussianState( np.array([1, 2]), np.array([[2, 0], [0, 2]]), wires=[1]), 1, "Gaussian(M0,M1)", ), (qml.QuadraticPhase(3.14, wires=[1]), 1, "P(3.14)"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.S(wires=[2]), 2, "S"), (qml.T(wires=[2]), 2, "T"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.RY(3.14, wires=[1]), 1, "RY(3.14)"), (qml.RZ(3.14, wires=[1]), 1, "RZ(3.14)"), (qml.Rot(3.14, 2.14, 1.14, wires=[1]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.U1(3.14, wires=[1]), 1, "U1(3.14)"), (qml.U2(3.14, 2.14, wires=[1]), 1, "U2(3.14, 2.14)"), (qml.U3(3.14, 2.14, 1.14, wires=[1]), 1, "U3(3.14, 2.14, 1.14)"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 1, "|0⟩"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 2, "|1⟩"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 3, "|0⟩"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 1, "QubitStateVector(M0)"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 2, "QubitStateVector(M0)"), (qml.QubitUnitary(np.eye(2), wires=[1]), 1, "U0"), (qml.QubitUnitary(np.eye(4), wires=[1, 2]), 2, "U0"), (qml.Kerr(3.14, wires=[1]), 1, "Kerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 1, "CrossKerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 2, "CrossKerr(3.14)"), (qml.CubicPhase(3.14, wires=[1]), 1, "V(3.14)"), (qml.Interferometer(np.eye(4), wires=[1, 3 ]), 1, "Interferometer(M0)"), (qml.Interferometer(np.eye(4), wires=[1, 3 ]), 3, "Interferometer(M0)"), (qml.CatState(3.14, 2.14, 1, wires=[1]), 1, "CatState(3.14, 2.14, 1)"), (qml.CoherentState(3.14, 2.14, wires=[1]), 1, "CoherentState(3.14, 2.14)"), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 1, "FockDensityMatrix(M0)", ), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 2, "FockDensityMatrix(M0)", ), ( qml.DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14, wires=[1]), 1, "DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14)", ), (qml.FockState(7, wires=[1]), 1, "|7⟩"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 1, "|4⟩"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 2, "|5⟩"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 3, "|7⟩"), (qml.SqueezedState(3.14, 2.14, wires=[1]), 1, "SqueezedState(3.14, 2.14)"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 1, "H0"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 2, "H0"), (qml.X(wires=[1]), 1, "x"), (qml.P(wires=[1]), 1, "p"), (qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]), 1, "|4,5,7╳4,5,7|"), ( qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1]), 2, "1+2x₀-1.3x₁+6p₁", ), ( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1]), 1, "1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀", ), ( qml.PolyXP( np.array([ [1.2, 2.3, 4.5, 0, 0], [-1.2, 1.2, -1.5, 0, 0], [-1.3, 4.5, 2.3, 0, 0], [0, 2.6, 0, 0, 0], [0, 0, 0, -4.7, -1.0], ]), wires=[1], ), 1, "1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀+2.6x₀x₁-p₁²-4.7x₁p₁", ), (qml.QuadOperator(3.14, wires=[1]), 1, "cos(3.14)x+sin(3.14)p"), (qml.PauliX(wires=[1]).inv(), 1, "X⁻¹"), (qml.CNOT(wires=[0, 1]).inv(), 1, "X⁻¹"), (qml.CNOT(wires=[0, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 1, "X⁻¹"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 2, "C"), ], ) def test_operator_representation_unicode(self, unicode_representation_resolver, op, wire, target): """Test that an Operator instance is properly resolved.""" assert unicode_representation_resolver.operator_representation( op, Wires(wire)) == target @pytest.mark.parametrize( "op,wire,target", [ (qml.PauliX(wires=[1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 1, "X"), (qml.CNOT(wires=[0, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 1, "X"), (qml.Toffoli(wires=[0, 2, 1]), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]), 2, "C"), (qml.CSWAP(wires=[0, 2, 1]), 1, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 2, "SWAP"), (qml.CSWAP(wires=[0, 2, 1]), 0, "C"), (qml.PauliY(wires=[1]), 1, "Y"), (qml.PauliZ(wires=[1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 1, "Z"), (qml.CZ(wires=[0, 1]), 0, "C"), (qml.Identity(wires=[1]), 1, "I"), (qml.Hadamard(wires=[1]), 1, "H"), (qml.CRX(3.14, wires=[0, 1]), 1, "RX(3.14)"), (qml.CRX(3.14, wires=[0, 1]), 0, "C"), (qml.CRY(3.14, wires=[0, 1]), 1, "RY(3.14)"), (qml.CRY(3.14, wires=[0, 1]), 0, "C"), (qml.CRZ(3.14, wires=[0, 1]), 1, "RZ(3.14)"), (qml.CRZ(3.14, wires=[0, 1]), 0, "C"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1 ]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.CRot(3.14, 2.14, 1.14, wires=[0, 1]), 0, "C"), (qml.PhaseShift(3.14, wires=[0]), 0, "Rϕ(3.14)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 1, "BS(1, 2)"), (qml.Beamsplitter(1, 2, wires=[0, 1]), 0, "BS(1, 2)"), (qml.Squeezing(1, 2, wires=[1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 1, "S(1, 2)"), (qml.TwoModeSqueezing(1, 2, wires=[0, 1]), 0, "S(1, 2)"), (qml.Displacement(1, 2, wires=[1]), 1, "D(1, 2)"), (qml.NumberOperator(wires=[1]), 1, "n"), (qml.Rotation(3.14, wires=[1]), 1, "R(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 1, "X(3.14)"), (qml.ControlledAddition(3.14, wires=[0, 1]), 0, "C"), (qml.ControlledPhase(3.14, wires=[0, 1]), 1, "Z(3.14)"), (qml.ControlledPhase(3.14, wires=[0, 1]), 0, "C"), (qml.ThermalState(3, wires=[1]), 1, "Thermal(3)"), ( qml.GaussianState( np.array([1, 2]), np.array([[2, 0], [0, 2]]), wires=[1]), 1, "Gaussian(M0,M1)", ), (qml.QuadraticPhase(3.14, wires=[1]), 1, "P(3.14)"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.S(wires=[2]), 2, "S"), (qml.T(wires=[2]), 2, "T"), (qml.RX(3.14, wires=[1]), 1, "RX(3.14)"), (qml.RY(3.14, wires=[1]), 1, "RY(3.14)"), (qml.RZ(3.14, wires=[1]), 1, "RZ(3.14)"), (qml.Rot(3.14, 2.14, 1.14, wires=[1]), 1, "Rot(3.14, 2.14, 1.14)"), (qml.U1(3.14, wires=[1]), 1, "U1(3.14)"), (qml.U2(3.14, 2.14, wires=[1]), 1, "U2(3.14, 2.14)"), (qml.U3(3.14, 2.14, 1.14, wires=[1]), 1, "U3(3.14, 2.14, 1.14)"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 1, "|0>"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 2, "|1>"), (qml.BasisState(np.array([0, 1, 0]), wires=[1, 2, 3]), 3, "|0>"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 1, "QubitStateVector(M0)"), (qml.QubitStateVector(np.array([0, 1, 0, 0]), wires=[1, 2]), 2, "QubitStateVector(M0)"), (qml.QubitUnitary(np.eye(2), wires=[1]), 1, "U0"), (qml.QubitUnitary(np.eye(4), wires=[1, 2]), 2, "U0"), (qml.Kerr(3.14, wires=[1]), 1, "Kerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 1, "CrossKerr(3.14)"), (qml.CrossKerr(3.14, wires=[1, 2]), 2, "CrossKerr(3.14)"), (qml.CubicPhase(3.14, wires=[1]), 1, "V(3.14)"), (qml.Interferometer(np.eye(4), wires=[1, 3 ]), 1, "Interferometer(M0)"), (qml.Interferometer(np.eye(4), wires=[1, 3 ]), 3, "Interferometer(M0)"), (qml.CatState(3.14, 2.14, 1, wires=[1]), 1, "CatState(3.14, 2.14, 1)"), (qml.CoherentState(3.14, 2.14, wires=[1]), 1, "CoherentState(3.14, 2.14)"), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 1, "FockDensityMatrix(M0)", ), ( qml.FockDensityMatrix(np.kron(np.eye(4), np.eye(4)), wires=[1, 2]), 2, "FockDensityMatrix(M0)", ), ( qml.DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14, wires=[1]), 1, "DisplacedSqueezedState(3.14, 2.14, 1.14, 0.14)", ), (qml.FockState(7, wires=[1]), 1, "|7>"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 1, "|4>"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 2, "|5>"), (qml.FockStateVector(np.array([4, 5, 7]), wires=[1, 2, 3 ]), 3, "|7>"), (qml.SqueezedState(3.14, 2.14, wires=[1]), 1, "SqueezedState(3.14, 2.14)"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 1, "H0"), (qml.Hermitian(np.eye(4), wires=[1, 2]), 2, "H0"), (qml.X(wires=[1]), 1, "x"), (qml.P(wires=[1]), 1, "p"), (qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]), 1, "|4,5,7X4,5,7|"), ( qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1]), 2, "1+2x_0-1.3x_1+6p_1", ), ( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1]), 1, "1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0", ), ( qml.PolyXP( np.array([ [1.2, 2.3, 4.5, 0, 0], [-1.2, 1.2, -1.5, 0, 0], [-1.3, 4.5, 2.3, 0, 0], [0, 2.6, 0, 0, 0], [0, 0, 0, -4.7, 0], ]), wires=[1], ), 1, "1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0+2.6x_0x_1-4.7x_1p_1", ), (qml.QuadOperator(3.14, wires=[1]), 1, "cos(3.14)x+sin(3.14)p"), (qml.QuadOperator(3.14, wires=[1]), 1, "cos(3.14)x+sin(3.14)p"), (qml.PauliX(wires=[1]).inv(), 1, "X^-1"), (qml.CNOT(wires=[0, 1]).inv(), 1, "X^-1"), (qml.CNOT(wires=[0, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 1, "X^-1"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 0, "C"), (qml.Toffoli(wires=[0, 2, 1]).inv(), 2, "C"), ], ) def test_operator_representation_ascii(self, ascii_representation_resolver, op, wire, target): """Test that an Operator instance is properly resolved.""" assert ascii_representation_resolver.operator_representation( op, Wires(wire)) == target @pytest.mark.parametrize( "obs,wire,target", [ (qml.expval(qml.PauliX(wires=[1])), 1, "⟨X⟩"), (qml.expval(qml.PauliY(wires=[1])), 1, "⟨Y⟩"), (qml.expval(qml.PauliZ(wires=[1])), 1, "⟨Z⟩"), (qml.expval(qml.Hadamard(wires=[1])), 1, "⟨H⟩"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "⟨H0⟩"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "⟨H0⟩"), (qml.expval(qml.NumberOperator(wires=[1])), 1, "⟨n⟩"), (qml.expval(qml.X(wires=[1])), 1, "⟨x⟩"), (qml.expval(qml.P(wires=[1])), 1, "⟨p⟩"), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "⟨|4,5,7╳4,5,7|⟩", ), ( qml.expval(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "⟨1+2x₀-1.3x₁+6p₁⟩", ), ( qml.expval( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "⟨1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀⟩", ), (qml.expval(qml.QuadOperator( 3.14, wires=[1])), 1, "⟨cos(3.14)x+sin(3.14)p⟩"), (qml.var(qml.PauliX(wires=[1])), 1, "Var[X]"), (qml.var(qml.PauliY(wires=[1])), 1, "Var[Y]"), (qml.var(qml.PauliZ(wires=[1])), 1, "Var[Z]"), (qml.var(qml.Hadamard(wires=[1])), 1, "Var[H]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "Var[H0]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "Var[H0]"), (qml.var(qml.NumberOperator(wires=[1])), 1, "Var[n]"), (qml.var(qml.X(wires=[1])), 1, "Var[x]"), (qml.var(qml.P(wires=[1])), 1, "Var[p]"), ( qml.var( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Var[|4,5,7╳4,5,7|]", ), ( qml.var(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1])), 2, "Var[1+2x₀-1.3x₁+6p₁]", ), ( qml.var( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Var[1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀]", ), (qml.var(qml.QuadOperator( 3.14, wires=[1])), 1, "Var[cos(3.14)x+sin(3.14)p]"), (qml.sample(qml.PauliX(wires=[1])), 1, "Sample[X]"), (qml.sample(qml.PauliY(wires=[1])), 1, "Sample[Y]"), (qml.sample(qml.PauliZ(wires=[1])), 1, "Sample[Z]"), (qml.sample(qml.Hadamard(wires=[1])), 1, "Sample[H]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 1, "Sample[H0]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 2, "Sample[H0]"), (qml.sample(qml.NumberOperator(wires=[1])), 1, "Sample[n]"), (qml.sample(qml.X(wires=[1])), 1, "Sample[x]"), (qml.sample(qml.P(wires=[1])), 1, "Sample[p]"), ( qml.sample( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Sample[|4,5,7╳4,5,7|]", ), ( qml.sample(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "Sample[1+2x₀-1.3x₁+6p₁]", ), ( qml.sample( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Sample[1.2+1.1x₀+3.2p₀+1.2x₀²+2.3p₀²+3x₀p₀]", ), (qml.sample(qml.QuadOperator( 3.14, wires=[1])), 1, "Sample[cos(3.14)x+sin(3.14)p]"), ( qml.expval( qml.PauliX(wires=[1]) @ qml.PauliY(wires=[2]) @ qml.PauliZ(wires=[3])), 1, "⟨X ⊗ Y ⊗ Z⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 1, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 2, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 3, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 4, "⟨|4,5,7╳4,5,7| ⊗ x⟩", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( np.eye(4), wires=[0, 3])), 0, "Sample[H0 ⊗ H0]", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( 2 * np.eye(4), wires=[0, 3])), 0, "Sample[H0 ⊗ H1]", ), (qml.probs([0]), 0, "Probs"), (state(), 0, "State"), ], ) def test_output_representation_unicode(self, unicode_representation_resolver, obs, wire, target): """Test that an Observable instance with return type is properly resolved.""" assert unicode_representation_resolver.output_representation( obs, Wires(wire)) == target def test_fallback_output_representation_unicode( self, unicode_representation_resolver): """Test that an Observable instance with return type is properly resolved.""" obs = qml.PauliZ(0) obs.return_type = "TestReturnType" assert unicode_representation_resolver.output_representation( obs, Wires(0)) == "TestReturnType[Z]" @pytest.mark.parametrize( "obs,wire,target", [ (qml.expval(qml.PauliX(wires=[1])), 1, "<X>"), (qml.expval(qml.PauliY(wires=[1])), 1, "<Y>"), (qml.expval(qml.PauliZ(wires=[1])), 1, "<Z>"), (qml.expval(qml.Hadamard(wires=[1])), 1, "<H>"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "<H0>"), (qml.expval(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "<H0>"), (qml.expval(qml.NumberOperator(wires=[1])), 1, "<n>"), (qml.expval(qml.X(wires=[1])), 1, "<x>"), (qml.expval(qml.P(wires=[1])), 1, "<p>"), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "<|4,5,7X4,5,7|>", ), ( qml.expval(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "<1+2x_0-1.3x_1+6p_1>", ), ( qml.expval( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "<1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0>", ), (qml.expval(qml.QuadOperator( 3.14, wires=[1])), 1, "<cos(3.14)x+sin(3.14)p>"), (qml.var(qml.PauliX(wires=[1])), 1, "Var[X]"), (qml.var(qml.PauliY(wires=[1])), 1, "Var[Y]"), (qml.var(qml.PauliZ(wires=[1])), 1, "Var[Z]"), (qml.var(qml.Hadamard(wires=[1])), 1, "Var[H]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 1, "Var[H0]"), (qml.var(qml.Hermitian(np.eye(4), wires=[1, 2])), 2, "Var[H0]"), (qml.var(qml.NumberOperator(wires=[1])), 1, "Var[n]"), (qml.var(qml.X(wires=[1])), 1, "Var[x]"), (qml.var(qml.P(wires=[1])), 1, "Var[p]"), ( qml.var( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Var[|4,5,7X4,5,7|]", ), ( qml.var(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1])), 2, "Var[1+2x_0-1.3x_1+6p_1]", ), ( qml.var( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Var[1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0]", ), (qml.var(qml.QuadOperator( 3.14, wires=[1])), 1, "Var[cos(3.14)x+sin(3.14)p]"), (qml.sample(qml.PauliX(wires=[1])), 1, "Sample[X]"), (qml.sample(qml.PauliY(wires=[1])), 1, "Sample[Y]"), (qml.sample(qml.PauliZ(wires=[1])), 1, "Sample[Z]"), (qml.sample(qml.Hadamard(wires=[1])), 1, "Sample[H]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 1, "Sample[H0]"), (qml.sample(qml.Hermitian(np.eye(4), wires=[1, 2 ])), 2, "Sample[H0]"), (qml.sample(qml.NumberOperator(wires=[1])), 1, "Sample[n]"), (qml.sample(qml.X(wires=[1])), 1, "Sample[x]"), (qml.sample(qml.P(wires=[1])), 1, "Sample[p]"), ( qml.sample( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3])), 1, "Sample[|4,5,7X4,5,7|]", ), ( qml.sample(qml.PolyXP(np.array([1, 2, 0, -1.3, 6]), wires=[1 ])), 2, "Sample[1+2x_0-1.3x_1+6p_1]", ), ( qml.sample( qml.PolyXP(np.array([[1.2, 2.3, 4.5], [-1.2, 1.2, -1.5], [-1.3, 4.5, 2.3]]), wires=[1])), 1, "Sample[1.2+1.1x_0+3.2p_0+1.2x_0^2+2.3p_0^2+3x_0p_0]", ), (qml.sample(qml.QuadOperator( 3.14, wires=[1])), 1, "Sample[cos(3.14)x+sin(3.14)p]"), ( qml.expval( qml.PauliX(wires=[1]) @ qml.PauliY(wires=[2]) @ qml.PauliZ(wires=[3])), 1, "<X @ Y @ Z>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 1, "<|4,5,7X4,5,7| @ x>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 2, "<|4,5,7X4,5,7| @ x>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 3, "<|4,5,7X4,5,7| @ x>", ), ( qml.expval( qml.FockStateProjector(np.array([4, 5, 7]), wires=[1, 2, 3]) @ qml.X(wires=[4])), 4, "<|4,5,7X4,5,7| @ x>", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( np.eye(4), wires=[0, 3])), 0, "Sample[H0 @ H0]", ), ( qml.sample( qml.Hermitian(np.eye(4), wires=[1, 2]) @ qml.Hermitian( 2 * np.eye(4), wires=[0, 3])), 0, "Sample[H0 @ H1]", ), (qml.probs([0]), 0, "Probs"), (state(), 0, "State"), ], ) def test_output_representation_ascii(self, ascii_representation_resolver, obs, wire, target): """Test that an Observable instance with return type is properly resolved.""" assert ascii_representation_resolver.output_representation( obs, Wires(wire)) == target def test_element_representation_none(self, unicode_representation_resolver): """Test that element_representation properly handles None.""" assert unicode_representation_resolver.element_representation( None, Wires(0)) == "" def test_element_representation_str(self, unicode_representation_resolver): """Test that element_representation properly handles strings.""" assert unicode_representation_resolver.element_representation( "Test", Wires(0)) == "Test" def test_element_representation_calls_output( self, unicode_representation_resolver): """Test that element_representation calls output_representation for returned observables.""" unicode_representation_resolver.output_representation = Mock() obs = qml.sample(qml.PauliX(3)) wire = 3 unicode_representation_resolver.element_representation( obs, Wires(wire)) assert unicode_representation_resolver.output_representation.call_args[ 0] == (obs, Wires(wire)) def test_element_representation_calls_operator( self, unicode_representation_resolver): """Test that element_representation calls operator_representation for all operators that are not returned.""" unicode_representation_resolver.operator_representation = Mock() op = qml.PauliX(3) wire = 3 unicode_representation_resolver.element_representation(op, Wires(wire)) assert unicode_representation_resolver.operator_representation.call_args[ 0] == (op, Wires(wire))
def ansatz(x): """Test ansatz""" qml.Hadamard(wires=0) qml.CRX(x, wires=[0, 1])
[ ([1, 1, 1, 2, 2, 2, 3, 2, 3], [1, 2, 3]), (["a", "b", "c", "c", "a", "d"], ["a", "b", "c", "d"]), ([1, 2, 3, 4], [1, 2, 3, 4]), ], ) def test_remove_duplicates(self, input, expected_output): """Test the function _remove_duplicates.""" assert _remove_duplicates(input) == expected_output op_CNOT21 = qml.CNOT(wires=[2, 1]) op_SWAP03 = qml.SWAP(wires=[0, 3]) op_SWAP12 = qml.SWAP(wires=[1, 2]) op_X0 = qml.PauliX(0) op_CRX20 = qml.CRX(2.3, wires=[2, 0]) op_Z3 = qml.PauliZ(3) dummy_raw_operation_grid = [ [None, op_SWAP03, op_X0, op_CRX20], [op_CNOT21, op_SWAP12, None, None], [op_CNOT21, op_SWAP12, None, op_CRX20], [op_Z3, op_SWAP03, None, None], ] dummy_raw_observable_grid = [ [qml.sample(qml.Hermitian(2 * np.eye(2), wires=[0]))], [None], [qml.expval(qml.PauliY(wires=[2]))], [qml.var(qml.Hadamard(wires=[3]))], ]
def shallow_circuit(weights, x, wires, n_layers=1, circuit_ID=1): """ Circuits are designed based on paper arXiv:1905.10876. Example one layer, 4 wires, 2 inputs: |0> - R_x(x1) - |^| -------- |_| - R_y(w5) - |0> - R_x(x2) - |_|-|^| ---------- R_y(w6) - |0> - ___H___ ------|_|-|^| ------ R_y(w7) - |0> - ___H___ ----------|_| -|^| - R_y(w8) - After the last layer, another block of R_x(x_i) rotations is applied. :param weights: trainable weights of shape 2*n_layers*n_wires :param 1d x: input, len(x) is <= len(wires) :param wires: list of wires on which the feature map acts :param n_layers: number of repetitions of the first layer :param circuit_ID: the ID of the circuit based on """ n_wires = len(wires) if n_wires == 1: n_weights_needed = n_layers elif n_wires == 2: n_weights_needed = 3 * n_layers else: n_weights_needed = 2 * n_wires * n_layers if len(x) > n_wires: raise ValueError( "Feat map can encode at most {} features (which is the " "number of wires), got {}.".format(n_wires, len(x))) if len(weights) != n_weights_needed: raise ValueError("Feat map needs {} weights, got {}.".format( n_weights_needed, len(weights))) for l in range(n_layers): # inputs for i in range(n_wires): # Either feed in feature if i < len(x): if circuit_ID == 18 or circuit_ID == 19: qml.RX(x[i], wires=wires[i]) elif circuit_ID == 11 or circuit_ID == 12: qml.RY(x[i], wires=wires[i]) else: raise ValueError( "Wrong circuit_ID: It should be between 1-19, got {}.". format(circuit_ID)) else: qml.Hadamard(wires=wires[i]) # 1-d nearest neighbour coupling if n_wires == 1: if circuit_ID == 18 or circuit_ID == 19: qml.RZ(weights[l], wires=wires[0]) elif n_wires == 2: # local fields for i in range(n_wires): if circuit_ID == 18 or circuit_ID == 19: qml.RZ(weights[l * 3 + i], wires=wires[i]) else: raise ValueError( "Wrong circuit_ID: It should be between 1-19, got {}.". format(circuit_ID)) if circuit_ID == 18: qml.CRZ(weights[l * 3 + 2], wires=[wires[1], wires[0]]) elif circuit_ID == 19: qml.CRX(weights[l * 3 + 2], wires=[wires[1], wires[0]]) else: # local fields for i in range(n_wires): if circuit_ID == 18 or circuit_ID == 19: qml.RZ(weights[l * 2 * n_wires + i], wires=wires[i]) for i in range(n_wires): if i == 0: if circuit_ID == 18: qml.CRZ(weights[l * 2 * n_wires + n_wires + i], wires=[wires[n_wires - 1], wires[0]]) elif circuit_ID == 19: qml.CRX(weights[l * 2 * n_wires + n_wires + i], wires=[wires[n_wires - 1], wires[0]]) elif i < n_wires - 1: if circuit_ID == 18: qml.CRZ(weights[l * 2 * n_wires + n_wires + i], wires=[wires[i], wires[i + 1]]) elif circuit_ID == 19: qml.CRX(weights[l * 2 * n_wires + n_wires + i], wires=[wires[i], wires[i + 1]]) # repeat feature encoding once more at the end for i in range(n_wires): # Either feed in feature if i < len(x): if circuit_ID == 18 or circuit_ID == 19: qml.RX(x[i], wires=wires[i]) # or a Hadamard else: qml.Hadamard(wires=wires[i])
def qfunc(x): qml.Hadamard(wires=0) qml.CRX(x, wires=[0, 1])
class TestAdjointJacobian: """Tests for the adjoint_jacobian method""" @pytest.fixture(params=[np.complex64, np.complex128]) def dev(self, request): return qml.device("lightning.qubit", wires=3, c_dtype=request.param) def test_not_expval(self, dev): """Test if a QuantumFunctionError is raised for a tape with measurements that are not expectation values""" with qml.tape.QuantumTape() as tape: qml.RX(0.1, wires=0) qml.var(qml.PauliZ(0)) with pytest.raises(qml.QuantumFunctionError, match="Adjoint differentiation method does not"): dev.adjoint_jacobian(tape) def test_finite_shots_warns(self): """Tests warning raised when finite shots specified""" dev = qml.device("lightning.qubit", wires=1, shots=1) with qml.tape.QuantumTape() as tape: qml.expval(qml.PauliZ(0)) with pytest.warns( UserWarning, match= "Requested adjoint differentiation to be computed with finite shots." ): dev.adjoint_jacobian(tape) def test_empty_measurements(self, tol, dev): """Tests if an empty array is returned when the measurements of the tape is empty.""" with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) jac = dev.adjoint_jacobian(tape) assert len(jac) == 0 @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_unsupported_op(self, dev): """Test if a QuantumFunctionError is raised for an unsupported operation, i.e., multi-parameter operations that are not qml.Rot""" with qml.tape.QuantumTape() as tape: qml.CRot(0.1, 0.2, 0.3, wires=[0, 1]) qml.expval(qml.PauliZ(0)) with pytest.raises( qml.QuantumFunctionError, match="The CRot operation is not supported using the"): dev.adjoint_jacobian(tape) @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_proj_unsupported(self, dev): """Test if a QuantumFunctionError is raised for a Projector observable""" with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) qml.expval(qml.Projector([0, 1], wires=[0, 1])) with pytest.raises( qml.QuantumFunctionError, match="differentiation method does not support the Projector"): dev.adjoint_jacobian(tape) with qml.tape.QuantumTape() as tape: qml.CRX(0.1, wires=[0, 1]) qml.expval(qml.Projector([0], wires=[0]) @ qml.PauliZ(0)) with pytest.raises( qml.QuantumFunctionError, match="differentiation method does not support the Projector"): dev.adjoint_jacobian(tape) @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) @pytest.mark.parametrize("G", [qml.RX, qml.RY, qml.RZ]) def test_pauli_rotation_gradient(self, G, theta, dev): """Tests that the automatic gradients of Pauli rotations are correct.""" with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) G(theta, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1} calculated_val = dev.adjoint_jacobian(tape) h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 # compare to finite differences tapes, fn = qml.gradients.finite_diff(tape, h=h) numeric_val = fn(qml.execute(tapes, dev, None)) assert np.allclose(calculated_val, numeric_val[0][2], atol=tol, rtol=0) @pytest.mark.parametrize("theta", np.linspace(-2 * np.pi, 2 * np.pi, 7)) def test_Rot_gradient(self, theta, dev): """Tests that the device gradient of an arbitrary Euler-angle-parameterized gate is correct.""" params = np.array([theta, theta**3, np.sqrt(2) * theta]) with qml.tape.QuantumTape() as tape: qml.QubitStateVector(np.array([1.0, -1.0]) / np.sqrt(2), wires=0) qml.Rot(*params, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} calculated_val = dev.adjoint_jacobian(tape) h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 # compare to finite differences tapes, fn = qml.gradients.finite_diff(tape, h=h) numeric_val = fn(qml.execute(tapes, dev, None)) assert np.allclose(calculated_val, numeric_val[0][2:], atol=tol, rtol=0) @pytest.mark.parametrize("par", [1, -2, 1.623, -0.051, 0] ) # integers, floats, zero def test_ry_gradient(self, par, tol, dev): """Test that the gradient of the RY gate matches the exact analytic formula.""" with qml.tape.QuantumTape() as tape: qml.RY(par, wires=[0]) qml.expval(qml.PauliX(0)) tape.trainable_params = {0} # gradients exact = np.cos(par) grad_A = dev.adjoint_jacobian(tape) # different methods must agree assert np.allclose(grad_A, exact, atol=tol, rtol=0) def test_rx_gradient(self, tol, dev): """Test that the gradient of the RX gate matches the known formula.""" a = 0.7418 with qml.tape.QuantumTape() as tape: qml.RX(a, wires=0) qml.expval(qml.PauliZ(0)) # circuit jacobians dev_jacobian = dev.adjoint_jacobian(tape) expected_jacobian = -np.sin(a) assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0) def test_multiple_rx_gradient_pauliz(self, tol, dev): """Tests that the gradient of multiple RX gates in a circuit yields the correct result.""" params = np.array([np.pi, np.pi / 2, np.pi / 3]) with qml.tape.QuantumTape() as tape: qml.RX(params[0], wires=0) qml.RX(params[1], wires=1) qml.RX(params[2], wires=2) for idx in range(3): qml.expval(qml.PauliZ(idx)) # circuit jacobians dev_jacobian = dev.adjoint_jacobian(tape) expected_jacobian = -np.diag(np.sin(params)) assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0) def test_multiple_rx_gradient_hermitian(self, tol, dev): """Tests that the gradient of multiple RX gates in a circuit yields the correct result with Hermitian observable """ params = np.array([np.pi, np.pi / 2, np.pi / 3]) with qml.tape.QuantumTape() as tape: qml.RX(params[0], wires=0) qml.RX(params[1], wires=1) qml.RX(params[2], wires=2) for idx in range(3): qml.expval(qml.Hermitian([[1, 0], [0, -1]], wires=[idx])) tape.trainable_params = {0, 1, 2} # circuit jacobians dev_jacobian = dev.adjoint_jacobian(tape) expected_jacobian = -np.diag(np.sin(params)) assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0) qubit_ops = [getattr(qml, name) for name in qml.ops._qubit__ops__] ops = { qml.RX, qml.RY, qml.RZ, qml.PhaseShift, qml.CRX, qml.CRY, qml.CRZ, qml.Rot } def test_multiple_rx_gradient_expval_hermitian(self, tol, dev): """Tests that the gradient of multiple RX gates in a circuit yields the correct result with Hermitian observable """ params = np.array([np.pi / 3, np.pi / 4, np.pi / 5]) with qml.tape.QuantumTape() as tape: qml.RX(params[0], wires=0) qml.RX(params[1], wires=1) qml.RX(params[2], wires=2) qml.expval( qml.Hermitian( [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], wires=[0, 2])) tape.trainable_params = {0, 1, 2} dev_jacobian = dev.adjoint_jacobian(tape) expected_jacobian = np.array([ -np.sin(params[0]) * np.cos(params[2]), 0, -np.cos(params[0]) * np.sin(params[2]) ]) assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0) qubit_ops = [getattr(qml, name) for name in qml.ops._qubit__ops__] ops = { qml.RX, qml.RY, qml.RZ, qml.PhaseShift, qml.CRX, qml.CRY, qml.CRZ, qml.Rot } @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_multiple_rx_gradient_expval_hamiltonian(self, tol, dev): """Tests that the gradient of multiple RX gates in a circuit yields the correct result with Hermitian observable """ params = np.array([np.pi / 3, np.pi / 4, np.pi / 5]) ham = qml.Hamiltonian( [1.0, 0.3, 0.3, 0.4], [ qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(0), qml.PauliZ(1), qml.Hermitian( [[1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]], wires=[0, 2]), ], ) with qml.tape.QuantumTape() as tape: qml.RX(params[0], wires=0) qml.RX(params[1], wires=1) qml.RX(params[2], wires=2) qml.expval(ham) tape.trainable_params = {0, 1, 2} dev_jacobian = dev.adjoint_jacobian(tape) expected_jacobian = (0.3 * np.array([-np.sin(params[0]), 0, 0]) + 0.3 * np.array([0, -np.sin(params[1]), 0]) + 0.4 * np.array([ -np.sin(params[0]) * np.cos(params[2]), 0, -np.cos(params[0]) * np.sin(params[2]) ])) assert np.allclose(dev_jacobian, expected_jacobian, atol=tol, rtol=0) qubit_ops = [getattr(qml, name) for name in qml.ops._qubit__ops__] ops = { qml.RX, qml.RY, qml.RZ, qml.PhaseShift, qml.CRX, qml.CRY, qml.CRZ, qml.Rot } @pytest.mark.parametrize("obs", [qml.PauliX, qml.PauliY]) @pytest.mark.parametrize( "op", [ qml.RX(0.4, wires=0), qml.RY(0.6, wires=0), qml.RZ(0.8, wires=0), qml.CRX(1.0, wires=[0, 1]), qml.CRY(2.0, wires=[0, 1]), qml.CRZ(3.0, wires=[0, 1]), qml.Rot(0.2, -0.1, 0.2, wires=0), ], ) def test_gradients_pauliz(self, op, obs, dev): """Tests that the gradients of circuits match between the finite difference and device methods.""" # op.num_wires and op.num_params must be initialized a priori with qml.tape.QuantumTape() as tape: qml.Hadamard(wires=0) qml.RX(0.543, wires=0) qml.CNOT(wires=[0, 1]) op qml.Rot(1.3, -2.3, 0.5, wires=[0]) qml.RZ(-0.5, wires=0) qml.RY(0.5, wires=1).inv() qml.CNOT(wires=[0, 1]) qml.expval(obs(wires=0)) qml.expval(qml.PauliZ(wires=1)) tape.trainable_params = set(range(1, 1 + op.num_params)) h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))( *qml.gradients.finite_diff(tape, h=h)) grad_D = dev.adjoint_jacobian(tape) assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) @pytest.mark.parametrize( "op", [ qml.RX(0.4, wires=0), qml.RY(0.6, wires=0), qml.RZ(0.8, wires=0), qml.CRX(1.0, wires=[0, 1]), qml.CRY(2.0, wires=[0, 1]), qml.CRZ(3.0, wires=[0, 1]), qml.Rot(0.2, -0.1, 0.2, wires=0), ], ) def test_gradients_hermitian(self, op, dev): """Tests that the gradients of circuits match between the finite difference and device methods.""" # op.num_wires and op.num_params must be initialized a priori with qml.tape.QuantumTape() as tape: qml.Hadamard(wires=0) qml.RX(0.543, wires=0) qml.CNOT(wires=[0, 1]) op.queue() qml.Rot(1.3, -2.3, 0.5, wires=[0]) qml.RZ(-0.5, wires=0) qml.RY(0.5, wires=1).inv() qml.CNOT(wires=[0, 1]) qml.expval( qml.Hermitian( [[0, 0, 1, 1], [0, 1, 2, 1], [1, 2, 1, 0], [1, 1, 0, 0]], wires=[0, 1])) tape.trainable_params = set(range(1, 1 + op.num_params)) h = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_F = (lambda t, fn: fn(qml.execute(t, dev, None)))( *qml.gradients.finite_diff(tape, h=h)) grad_D = dev.adjoint_jacobian(tape) assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) def test_gradient_gate_with_multiple_parameters_pauliz(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_D = dev.adjoint_jacobian(tape) tapes, fn = qml.gradients.finite_diff(tape, h=h) grad_F = fn(qml.execute(tapes, dev, None)) # gradient has the correct shape and every element is nonzero assert grad_D.shape == (1, 3) assert np.count_nonzero(grad_D) == 3 # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) def test_gradient_gate_with_multiple_parameters_hermitian(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(qml.Hermitian([[0, 1], [1, 1]], wires=0)) tape.trainable_params = {1, 2, 3} h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_D = dev.adjoint_jacobian(tape) tapes, fn = qml.gradients.finite_diff(tape, h=h) grad_F = fn(qml.execute(tapes, dev, None)) # gradient has the correct shape and every element is nonzero assert grad_D.shape == (1, 3) assert np.count_nonzero(grad_D) == 3 # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_gradient_gate_with_multiple_parameters_hamiltonian(self, dev): """Tests that gates with multiple free parameters yield correct gradients.""" x, y, z = [0.5, 0.3, -0.7] ham = qml.Hamiltonian( [1.0, 0.3, 0.3], [qml.PauliX(0) @ qml.PauliX(1), qml.PauliZ(0), qml.PauliZ(1)]) with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(ham) tape.trainable_params = {1, 2, 3} h = 2e-3 if dev.R_DTYPE == np.float32 else 1e-7 tol = 1e-3 if dev.R_DTYPE == np.float32 else 1e-7 grad_D = dev.adjoint_jacobian(tape) tapes, fn = qml.gradients.finite_diff(tape, h=h) grad_F = fn(qml.execute(tapes, dev, None)) # gradient has the correct shape and every element is nonzero assert grad_D.shape == (1, 3) assert np.count_nonzero(grad_D) == 3 # the different methods agree assert np.allclose(grad_D, grad_F, atol=tol, rtol=0) def test_use_device_state(self, tol, dev): """Tests that when using the device state, the correct answer is still returned.""" x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} dM1 = dev.adjoint_jacobian(tape) qml.execute([tape], dev, None) dM2 = dev.adjoint_jacobian(tape, use_device_state=True) assert np.allclose(dM1, dM2, atol=tol, rtol=0) def test_provide_starting_state(self, tol, dev): """Tests provides correct answer when provided starting state.""" x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} dM1 = dev.adjoint_jacobian(tape) qml.execute([tape], dev, None) dM2 = dev.adjoint_jacobian(tape, starting_state=dev._pre_rotated_state) assert np.allclose(dM1, dM2, atol=tol, rtol=0) @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_provide_wrong_starting_state(self, dev): """Tests raise an exception when provided starting state mismatches.""" x, y, z = [0.5, 0.3, -0.7] with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.Rot(x, y, z, wires=[0]) qml.RY(-0.2, wires=[0]) qml.expval(qml.PauliZ(0)) tape.trainable_params = {1, 2, 3} with pytest.raises( qml.QuantumFunctionError, match= "The number of qubits of starting_state must be the same as", ): dev.adjoint_jacobian(tape, starting_state=np.ones(7)) @pytest.mark.skipif(not lq._CPP_BINARY_AVAILABLE, reason="Lightning binary required") def test_state_return_type(self, dev): """Tests raise an exception when the return type is State""" with qml.tape.QuantumTape() as tape: qml.RX(0.4, wires=[0]) qml.state() tape.trainable_params = {0} with pytest.raises( qml.QuantumFunctionError, match="This method does not support statevector return type."): dev.adjoint_jacobian(tape)
class TestOperations: """Tests that the CirqDevice correctly handles the requested operations.""" def test_reset_on_empty_circuit(self, cirq_device_1_wire): """Tests that reset resets the internal circuit when it is not initialized.""" assert cirq_device_1_wire.circuit is None cirq_device_1_wire.reset() # Check if circuit is an empty cirq.Circuit assert cirq_device_1_wire.circuit == cirq.Circuit() def test_reset_on_full_circuit(self, cirq_device_1_wire): """Tests that reset resets the internal circuit when it is filled.""" cirq_device_1_wire.reset() cirq_device_1_wire.apply([qml.PauliX(0)]) # Assert that the queue is filled assert list(cirq_device_1_wire.circuit.all_operations()) cirq_device_1_wire.reset() # Assert that the queue is empty assert not list(cirq_device_1_wire.circuit.all_operations()) @pytest.mark.parametrize( "gate,expected_cirq_gates", [ (qml.PauliX(wires=[0]), [cirq.X]), (qml.PauliY(wires=[0]), [cirq.Y]), (qml.PauliZ(wires=[0]), [cirq.Z]), (qml.PauliX(wires=[0]).inv(), [cirq.X ** -1]), (qml.PauliY(wires=[0]).inv(), [cirq.Y ** -1]), (qml.PauliZ(wires=[0]).inv(), [cirq.Z ** -1]), (qml.Hadamard(wires=[0]), [cirq.H]), (qml.Hadamard(wires=[0]).inv(), [cirq.H ** -1]), (qml.S(wires=[0]), [cirq.S]), (qml.S(wires=[0]).inv(), [cirq.S ** -1]), (qml.PhaseShift(1.4, wires=[0]), [cirq.ZPowGate(exponent=1.4 / np.pi)]), (qml.PhaseShift(-1.2, wires=[0]), [cirq.ZPowGate(exponent=-1.2 / np.pi)]), (qml.PhaseShift(2, wires=[0]), [cirq.ZPowGate(exponent=2 / np.pi)]), ( qml.PhaseShift(1.4, wires=[0]).inv(), [cirq.ZPowGate(exponent=-1.4 / np.pi)], ), ( qml.PhaseShift(-1.2, wires=[0]).inv(), [cirq.ZPowGate(exponent=1.2 / np.pi)], ), (qml.PhaseShift(2, wires=[0]).inv(), [cirq.ZPowGate(exponent=-2 / np.pi)]), (qml.RX(1.4, wires=[0]), [cirq.rx(1.4)]), (qml.RX(-1.2, wires=[0]), [cirq.rx(-1.2)]), (qml.RX(2, wires=[0]), [cirq.rx(2)]), (qml.RX(1.4, wires=[0]).inv(), [cirq.rx(-1.4)]), (qml.RX(-1.2, wires=[0]).inv(), [cirq.rx(1.2)]), (qml.RX(2, wires=[0]).inv(), [cirq.rx(-2)]), (qml.RY(1.4, wires=[0]), [cirq.ry(1.4)]), (qml.RY(0, wires=[0]), [cirq.ry(0)]), (qml.RY(-1.3, wires=[0]), [cirq.ry(-1.3)]), (qml.RY(1.4, wires=[0]).inv(), [cirq.ry(-1.4)]), (qml.RY(0, wires=[0]).inv(), [cirq.ry(0)]), (qml.RY(-1.3, wires=[0]).inv(), [cirq.ry(+1.3)]), (qml.RZ(1.4, wires=[0]), [cirq.rz(1.4)]), (qml.RZ(-1.1, wires=[0]), [cirq.rz(-1.1)]), (qml.RZ(1, wires=[0]), [cirq.rz(1)]), (qml.RZ(1.4, wires=[0]).inv(), [cirq.rz(-1.4)]), (qml.RZ(-1.1, wires=[0]).inv(), [cirq.rz(1.1)]), (qml.RZ(1, wires=[0]).inv(), [cirq.rz(-1)]), ( qml.Rot(1.4, 2.3, -1.2, wires=[0]), [cirq.rz(1.4), cirq.ry(2.3), cirq.rz(-1.2)], ), (qml.Rot(1, 2, -1, wires=[0]), [cirq.rz(1), cirq.ry(2), cirq.rz(-1)]), ( qml.Rot(-1.1, 0.2, -1, wires=[0]), [cirq.rz(-1.1), cirq.ry(0.2), cirq.rz(-1)], ), ( qml.Rot(1.4, 2.3, -1.2, wires=[0]).inv(), [cirq.rz(1.2), cirq.ry(-2.3), cirq.rz(-1.4)], ), ( qml.Rot(1, 2, -1, wires=[0]).inv(), [cirq.rz(1), cirq.ry(-2), cirq.rz(-1)], ), ( qml.Rot(-1.1, 0.2, -1, wires=[0]).inv(), [cirq.rz(1), cirq.ry(-0.2), cirq.rz(1.1)], ), ( qml.QubitUnitary(np.array([[1, 0], [0, 1]]), wires=[0]), [cirq.MatrixGate(np.array([[1, 0], [0, 1]]))], ), ( qml.QubitUnitary(np.array([[1, 0], [0, -1]]), wires=[0]), [cirq.MatrixGate(np.array([[1, 0], [0, -1]]))], ), ( qml.QubitUnitary(np.array([[-1, 1], [1, 1]]) / math.sqrt(2), wires=[0]), [cirq.MatrixGate(np.array([[-1, 1], [1, 1]]) / math.sqrt(2))], ), ( qml.QubitUnitary(np.array([[1, 0], [0, 1]]), wires=[0]).inv(), [cirq.MatrixGate(np.array([[1, 0], [0, 1]])) ** -1], ), ( qml.QubitUnitary(np.array([[1, 0], [0, -1]]), wires=[0]).inv(), [cirq.MatrixGate(np.array([[1, 0], [0, -1]])) ** -1], ), ( qml.QubitUnitary(np.array([[-1, 1], [1, 1]]) / math.sqrt(2), wires=[0]).inv(), [cirq.MatrixGate(np.array([[-1, 1], [1, 1]]) / math.sqrt(2)) ** -1], ), ], ) def test_apply_single_wire(self, cirq_device_1_wire, gate, expected_cirq_gates): """Tests that apply adds the correct gates to the circuit for single-qubit gates.""" cirq_device_1_wire.reset() cirq_device_1_wire.apply([gate]) ops = list(cirq_device_1_wire.circuit.all_operations()) assert len(ops) == len(expected_cirq_gates) for i in range(len(ops)): assert ops[i]._gate == expected_cirq_gates[i] @pytest.mark.parametrize( "gate,expected_cirq_gates", [ (qml.CNOT(wires=[0, 1]), [cirq.CNOT]), (qml.CNOT(wires=[0, 1]).inv(), [cirq.CNOT ** -1]), (qml.SWAP(wires=[0, 1]), [cirq.SWAP]), (qml.SWAP(wires=[0, 1]).inv(), [cirq.SWAP ** -1]), (qml.CZ(wires=[0, 1]), [cirq.CZ]), (qml.CZ(wires=[0, 1]).inv(), [cirq.CZ ** -1]), (qml.CRX(1.4, wires=[0, 1]), [cirq.ControlledGate(cirq.rx(1.4))]), (qml.CRX(-1.2, wires=[0, 1]), [cirq.ControlledGate(cirq.rx(-1.2))]), (qml.CRX(2, wires=[0, 1]), [cirq.ControlledGate(cirq.rx(2))]), (qml.CRX(1.4, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.rx(-1.4))]), (qml.CRX(-1.2, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.rx(1.2))]), (qml.CRX(2, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.rx(-2))]), (qml.CRY(1.4, wires=[0, 1]), [cirq.ControlledGate(cirq.ry(1.4))]), (qml.CRY(0, wires=[0, 1]), [cirq.ControlledGate(cirq.ry(0))]), (qml.CRY(-1.3, wires=[0, 1]), [cirq.ControlledGate(cirq.ry(-1.3))]), (qml.CRY(1.4, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.ry(-1.4))]), (qml.CRY(0, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.ry(0))]), (qml.CRY(-1.3, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.ry(1.3))]), (qml.CRZ(1.4, wires=[0, 1]), [cirq.ControlledGate(cirq.rz(1.4))]), (qml.CRZ(-1.1, wires=[0, 1]), [cirq.ControlledGate(cirq.rz(-1.1))]), (qml.CRZ(1, wires=[0, 1]), [cirq.ControlledGate(cirq.rz(1))]), (qml.CRZ(1.4, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.rz(-1.4))]), (qml.CRZ(-1.1, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.rz(1.1))]), (qml.CRZ(1, wires=[0, 1]).inv(), [cirq.ControlledGate(cirq.rz(-1))]), ( qml.CRot(1.4, 2.3, -1.2, wires=[0, 1]), [ cirq.ControlledGate(cirq.rz(1.4)), cirq.ControlledGate(cirq.ry(2.3)), cirq.ControlledGate(cirq.rz(-1.2)), ], ), ( qml.CRot(1, 2, -1, wires=[0, 1]), [ cirq.ControlledGate(cirq.rz(1)), cirq.ControlledGate(cirq.ry(2)), cirq.ControlledGate(cirq.rz(-1)), ], ), ( qml.CRot(-1.1, 0.2, -1, wires=[0, 1]), [ cirq.ControlledGate(cirq.rz(-1.1)), cirq.ControlledGate(cirq.ry(0.2)), cirq.ControlledGate(cirq.rz(-1)), ], ), ( qml.CRot(1.4, 2.3, -1.2, wires=[0, 1]).inv(), [ cirq.ControlledGate(cirq.rz(1.2)), cirq.ControlledGate(cirq.ry(-2.3)), cirq.ControlledGate(cirq.rz(-1.4)), ], ), ( qml.CRot(1, 2, -1, wires=[0, 1]).inv(), [ cirq.ControlledGate(cirq.rz(1)), cirq.ControlledGate(cirq.ry(-2)), cirq.ControlledGate(cirq.rz(-1)), ], ), ( qml.CRot(-1.1, 0.2, -1, wires=[0, 1]).inv(), [ cirq.ControlledGate(cirq.rz(1)), cirq.ControlledGate(cirq.ry(-0.2)), cirq.ControlledGate(cirq.rz(1.1)), ], ), (qml.QubitUnitary(np.eye(4), wires=[0, 1]), [cirq.MatrixGate(np.eye(4))]), ( qml.QubitUnitary( np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]), wires=[0, 1], ), [ cirq.MatrixGate( np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]) ) ], ), ( qml.QubitUnitary( np.array([[1, -1, -1, 1], [-1, -1, 1, 1], [-1, 1, -1, 1], [1, 1, 1, 1]]) / 2, wires=[0, 1], ), [ cirq.MatrixGate( np.array( [ [1, -1, -1, 1], [-1, -1, 1, 1], [-1, 1, -1, 1], [1, 1, 1, 1], ] ) / 2 ) ], ), ( qml.QubitUnitary(np.eye(4), wires=[0, 1]).inv(), [cirq.MatrixGate(np.eye(4)) ** -1], ), ( qml.QubitUnitary( np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]), wires=[0, 1], ).inv(), [ cirq.MatrixGate( np.array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]) ) ** -1 ], ), ( qml.QubitUnitary( np.array([[1, -1, -1, 1], [-1, -1, 1, 1], [-1, 1, -1, 1], [1, 1, 1, 1]]) / 2, wires=[0, 1], ).inv(), [ cirq.MatrixGate( np.array( [ [1, -1, -1, 1], [-1, -1, 1, 1], [-1, 1, -1, 1], [1, 1, 1, 1], ] ) / 2 ) ** -1 ], ), ], ) def test_apply_two_wires(self, cirq_device_2_wires, gate, expected_cirq_gates): """Tests that apply adds the correct gates to the circuit for two-qubit gates.""" cirq_device_2_wires.reset() cirq_device_2_wires.apply([gate]) ops = list(cirq_device_2_wires.circuit.all_operations()) assert len(ops) == len(expected_cirq_gates) for i in range(len(ops)): assert ops[i].gate == expected_cirq_gates[i]
class TestProgramConverter: """Test that PyQuil Program instances are properly converted.""" @pytest.mark.parametrize( "pyquil_operation,expected_pl_operation", [ (g.I(0), qml.Identity(wires=[0])), (g.H(0), qml.Hadamard(0)), (g.H(0).dagger(), qml.Hadamard(0).inv()), (g.H(0).dagger().dagger(), qml.Hadamard(0).inv().inv()), (g.S(0), qml.S(wires=[0])), (g.S(0).dagger(), qml.S(wires=[0]).inv()), (g.S(0).dagger().dagger(), qml.S(wires=[0]).inv().inv()), (g.T(0), qml.T(wires=[0])), (g.T(0).dagger(), qml.T(wires=[0]).inv()), (g.T(0).dagger().dagger(), qml.T(wires=[0]).inv().inv()), (g.X(0), qml.PauliX(0)), (g.X(0).dagger(), qml.PauliX(0).inv()), (g.X(0).dagger().dagger(), qml.PauliX(0).inv().inv()), (g.X(0).controlled(1), qml.CNOT(wires=[1, 0])), (g.X(0).controlled(1).dagger(), qml.CNOT(wires=[1, 0]).inv()), (g.X(0).controlled(1).dagger().dagger(), qml.CNOT(wires=[1, 0]).inv().inv()), (g.X(0).controlled(1).controlled(2), plf.ops.CCNOT(wires=[2, 1, 0])), (g.X(0).controlled(1).controlled(2).dagger(), plf.ops.CCNOT(wires=[2, 1, 0]).inv()), ( g.X(0).controlled(1).controlled(2).dagger().dagger(), plf.ops.CCNOT(wires=[2, 1, 0]).inv().inv(), ), (g.Y(0), qml.PauliY(0)), (g.Y(0).dagger(), qml.PauliY(0).inv()), (g.Y(0).dagger().dagger(), qml.PauliY(0).inv().inv()), (g.Z(0), qml.PauliZ(0)), (g.Z(0).dagger(), qml.PauliZ(0).inv()), (g.Z(0).dagger().dagger(), qml.PauliZ(0).inv().inv()), (g.Z(0).controlled(1), qml.CZ(wires=[1, 0])), (g.Z(0).controlled(1).dagger(), qml.CZ(wires=[1, 0]).inv()), (g.Z(0).controlled(1).dagger().dagger(), qml.CZ(wires=[1, 0]).inv().inv()), (g.CNOT(0, 1), qml.CNOT(wires=[0, 1])), (g.CNOT(0, 1).dagger(), qml.CNOT(wires=[0, 1]).inv()), (g.CNOT(0, 1).dagger().dagger(), qml.CNOT(wires=[0, 1]).inv().inv()), (g.CNOT(0, 1).controlled(2), plf.ops.CCNOT(wires=[2, 0, 1])), (g.CNOT(0, 1).controlled(2).dagger(), plf.ops.CCNOT(wires=[2, 0, 1]).inv()), ( g.CNOT(0, 1).controlled(2).dagger().dagger(), plf.ops.CCNOT(wires=[2, 0, 1]).inv().inv(), ), (g.SWAP(0, 1), qml.SWAP(wires=[0, 1])), (g.SWAP(0, 1).dagger(), qml.SWAP(wires=[0, 1]).inv()), (g.SWAP(0, 1).dagger().dagger(), qml.SWAP(wires=[0, 1]).inv().inv()), (g.SWAP(0, 1).controlled(2), qml.CSWAP(wires=[2, 0, 1])), (g.SWAP(0, 1).controlled(2).dagger(), qml.CSWAP(wires=[2, 0, 1]).inv()), (g.SWAP(0, 1).controlled(2).dagger().dagger(), qml.CSWAP(wires=[2, 0, 1]).inv().inv()), (g.ISWAP(0, 1), plf.ops.ISWAP(wires=[0, 1])), (g.ISWAP(0, 1).dagger(), plf.ops.ISWAP(wires=[0, 1]).inv()), (g.ISWAP(0, 1).dagger().dagger(), plf.ops.ISWAP(wires=[0, 1]).inv().inv()), (g.PSWAP(0.3, 0, 1), plf.ops.PSWAP(0.3, wires=[0, 1])), (g.PSWAP(0.3, 0, 1).dagger(), plf.ops.PSWAP(0.3, wires=[0, 1 ]).inv()), (g.PSWAP(0.3, 0, 1).dagger().dagger(), plf.ops.PSWAP(0.3, wires=[0, 1]).inv().inv()), (g.CZ(0, 1), qml.CZ(wires=[0, 1])), (g.CZ(0, 1).dagger(), qml.CZ(wires=[0, 1]).inv()), (g.CZ(0, 1).dagger().dagger(), qml.CZ(wires=[0, 1]).inv().inv()), (g.PHASE(0.3, 0), qml.PhaseShift(0.3, wires=[0])), (g.PHASE(0.3, 0).dagger(), qml.PhaseShift(0.3, wires=[0]).inv()), (g.PHASE(0.3, 0).dagger().dagger(), qml.PhaseShift( 0.3, wires=[0]).inv().inv()), (g.PHASE(0.3, 0).controlled(1), plf.ops.CPHASE( 0.3, 3, wires=[1, 0])), (g.PHASE(0.3, 0).controlled(1).dagger(), plf.ops.CPHASE(0.3, 3, wires=[1, 0]).inv()), ( g.PHASE(0.3, 0).controlled(1).dagger().dagger(), plf.ops.CPHASE(0.3, 3, wires=[1, 0]).inv().inv(), ), (g.RX(0.3, 0), qml.RX(0.3, wires=[0])), (g.RX(0.3, 0).dagger(), qml.RX(0.3, wires=[0]).inv()), (g.RX(0.3, 0).dagger().dagger(), qml.RX(0.3, wires=[0]).inv().inv()), (g.RX(0.3, 0).controlled(1), qml.CRX(0.3, wires=[1, 0])), (g.RX(0.3, 0).controlled(1).dagger(), qml.CRX(0.3, wires=[1, 0]).inv()), (g.RX(0.3, 0).controlled(1).dagger().dagger(), qml.CRX(0.3, wires=[1, 0]).inv().inv()), (g.RY(0.3, 0), qml.RY(0.3, wires=[0])), (g.RY(0.3, 0).dagger(), qml.RY(0.3, wires=[0]).inv()), (g.RY(0.3, 0).dagger().dagger(), qml.RY(0.3, wires=[0]).inv().inv()), (g.RY(0.3, 0).controlled(1), qml.CRY(0.3, wires=[1, 0])), (g.RY(0.3, 0).controlled(1).dagger(), qml.CRY(0.3, wires=[1, 0]).inv()), (g.RY(0.3, 0).controlled(1).dagger().dagger(), qml.CRY(0.3, wires=[1, 0]).inv().inv()), (g.RZ(0.3, 0), qml.RZ(0.3, wires=[0])), (g.RZ(0.3, 0).dagger(), qml.RZ(0.3, wires=[0]).inv()), (g.RZ(0.3, 0).dagger().dagger(), qml.RZ(0.3, wires=[0]).inv().inv()), (g.RZ(0.3, 0).controlled(1), qml.CRZ(0.3, wires=[1, 0])), (g.RZ(0.3, 0).controlled(1).dagger(), qml.CRZ(0.3, wires=[1, 0]).inv()), (g.RZ(0.3, 0).controlled(1).dagger().dagger(), qml.CRZ(0.3, wires=[1, 0]).inv().inv()), (g.CPHASE(0.3, 0, 1), plf.ops.CPHASE(0.3, 3, wires=[0, 1])), (g.CPHASE(0.3, 0, 1).dagger(), plf.ops.CPHASE(0.3, 3, wires=[0, 1]).inv()), ( g.CPHASE(0.3, 0, 1).dagger().dagger(), plf.ops.CPHASE(0.3, 3, wires=[0, 1]).inv().inv(), ), (g.CPHASE00(0.3, 0, 1), plf.ops.CPHASE(0.3, 0, wires=[0, 1])), (g.CPHASE00(0.3, 0, 1).dagger(), plf.ops.CPHASE(0.3, 0, wires=[0, 1]).inv()), ( g.CPHASE00(0.3, 0, 1).dagger().dagger(), plf.ops.CPHASE(0.3, 0, wires=[0, 1]).inv().inv(), ), (g.CPHASE01(0.3, 0, 1), plf.ops.CPHASE(0.3, 1, wires=[0, 1])), (g.CPHASE01(0.3, 0, 1).dagger(), plf.ops.CPHASE(0.3, 1, wires=[0, 1]).inv()), ( g.CPHASE01(0.3, 0, 1).dagger().dagger(), plf.ops.CPHASE(0.3, 1, wires=[0, 1]).inv().inv(), ), (g.CPHASE10(0.3, 0, 1), plf.ops.CPHASE(0.3, 2, wires=[0, 1])), (g.CPHASE10(0.3, 0, 1).dagger(), plf.ops.CPHASE(0.3, 2, wires=[0, 1]).inv()), ( g.CPHASE10(0.3, 0, 1).dagger().dagger(), plf.ops.CPHASE(0.3, 2, wires=[0, 1]).inv().inv(), ), (g.CSWAP(0, 1, 2), qml.CSWAP(wires=[0, 1, 2])), (g.CSWAP(0, 1, 2).dagger(), qml.CSWAP(wires=[0, 1, 2]).inv()), (g.CSWAP(0, 1, 2).dagger().dagger(), qml.CSWAP(wires=[0, 1, 2]).inv().inv()), (g.CCNOT(0, 1, 2), plf.ops.CCNOT(wires=[0, 1, 2])), (g.CCNOT(0, 1, 2).dagger(), plf.ops.CCNOT(wires=[0, 1, 2]).inv()), (g.CCNOT(0, 1, 2).dagger().dagger(), plf.ops.CCNOT(wires=[0, 1, 2]).inv().inv()), ], ) def test_convert_operation(self, pyquil_operation, expected_pl_operation): """Test that single pyquil gates are properly converted.""" program = pyquil.Program() program += pyquil_operation with OperationRecorder() as rec: loader = load_program(program) loader(wires=range(len(loader.defined_qubits))) assert rec.queue[0].name == expected_pl_operation.name assert rec.queue[0].wires == expected_pl_operation.wires assert rec.queue[0].params == expected_pl_operation.params def test_convert_simple_program(self): """Test that a simple program is properly converted.""" program = pyquil.Program() program += g.H(0) program += g.RZ(0.34, 1) program += g.CNOT(0, 3) program += g.H(2) program += g.H(7) program += g.X(7) program += g.Y(1) program += g.RZ(0.34, 1) with OperationRecorder() as rec: load_program(program)(wires=range(5)) # The wires should be assigned as # 0 1 2 3 7 # 0 1 2 3 4 expected_queue = [ qml.Hadamard(0), qml.RZ(0.34, wires=[1]), qml.CNOT(wires=[0, 3]), qml.Hadamard(2), qml.Hadamard(4), qml.PauliX(4), qml.PauliY(1), qml.RZ(0.34, wires=[1]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params def test_convert_simple_program_with_parameters(self): """Test that a simple program with parameters is properly converted.""" program = pyquil.Program() alpha = program.declare("alpha", "REAL") beta = program.declare("beta", "REAL") gamma = program.declare("gamma", "REAL") program += g.H(0) program += g.CNOT(0, 1) program += g.RX(alpha, 1) program += g.RZ(beta, 1) program += g.RX(gamma, 1) program += g.CNOT(0, 1) program += g.H(0) a, b, c = 0.1, 0.2, 0.3 parameter_map = {"alpha": a, "beta": b, "gamma": c} with OperationRecorder() as rec: load_program(program)(wires=range(2), parameter_map=parameter_map) expected_queue = [ qml.Hadamard(0), qml.CNOT(wires=[0, 1]), qml.RX(0.1, wires=[1]), qml.RZ(0.2, wires=[1]), qml.RX(0.3, wires=[1]), qml.CNOT(wires=[0, 1]), qml.Hadamard(0), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params def test_parameter_not_given_error(self): """Test that the correct error is raised if a parameter is not given.""" program = pyquil.Program() alpha = program.declare("alpha", "REAL") beta = program.declare("beta", "REAL") program += g.H(0) program += g.CNOT(0, 1) program += g.RX(alpha, 1) program += g.RZ(beta, 1) a = 0.1 parameter_map = {"alpha": a} with pytest.raises( qml.DeviceError, match= "The PyQuil program defines a variable .* that is not present in the given variable map", ): load_program(program)(wires=range(2), parameter_map=parameter_map) def test_convert_simple_program_with_parameters_mixed_keys(self): """Test that a parametrized program is properly converted when the variable map contains mixed key types.""" program = pyquil.Program() alpha = program.declare("alpha", "REAL") beta = program.declare("beta", "REAL") gamma = program.declare("gamma", "REAL") delta = program.declare("delta", "REAL") program += g.H(0) program += g.CNOT(0, 1) program += g.RX(alpha, 1) program += g.RZ(beta, 1) program += g.RX(gamma, 1) program += g.CNOT(0, 1) program += g.RZ(delta, 0) program += g.H(0) a, b, c, d = 0.1, 0.2, 0.3, 0.4 parameter_map = {"alpha": a, beta: b, gamma: c, "delta": d} with OperationRecorder() as rec: load_program(program)(wires=range(2), parameter_map=parameter_map) expected_queue = [ qml.Hadamard(0), qml.CNOT(wires=[0, 1]), qml.RX(0.1, wires=[1]), qml.RZ(0.2, wires=[1]), qml.RX(0.3, wires=[1]), qml.CNOT(wires=[0, 1]), qml.RZ(0.4, wires=[0]), qml.Hadamard(0), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params def test_convert_simple_program_wire_assignment(self): """Test that the assignment of qubits to wires works as expected.""" program = pyquil.Program() program += g.H(0) program += g.RZ(0.34, 1) program += g.CNOT(0, 3) program += g.H(2) program += g.H(7) program += g.X(7) program += g.Y(1) program += g.RZ(0.34, 1) with OperationRecorder() as rec: load_program(program)(wires=[3, 6, 4, 9, 1]) # The wires should be assigned as # 0 1 2 3 7 # 3 6 4 9 1 expected_queue = [ qml.Hadamard(3), qml.RZ(0.34, wires=[6]), qml.CNOT(wires=[3, 9]), qml.Hadamard(4), qml.Hadamard(1), qml.PauliX(1), qml.PauliY(6), qml.RZ(0.34, wires=[6]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params @pytest.mark.parametrize("wires", [[0, 1, 2, 3], [4, 5]]) def test_convert_wire_error(self, wires): """Test that the conversion raises an error if the given number of wires doesn't match the number of qubits in the Program.""" program = pyquil.Program() program += g.H(0) program += g.H(1) program += g.H(2) with pytest.raises( qml.DeviceError, match= "The number of given wires does not match the number of qubits in the PyQuil Program", ): load_program(program)(wires=wires) def test_convert_program_with_inverses(self): """Test that a program with inverses is properly converted.""" program = pyquil.Program() program += g.H(0) program += g.RZ(0.34, 1).dagger() program += g.CNOT(0, 3).dagger() program += g.H(2) program += g.H(7).dagger().dagger() program += g.X(7).dagger() program += g.X(7) program += g.Y(1) program += g.RZ(0.34, 1) with OperationRecorder() as rec: load_program(program)(wires=range(5)) expected_queue = [ qml.Hadamard(0), qml.RZ(0.34, wires=[1]).inv(), qml.CNOT(wires=[0, 3]).inv(), qml.Hadamard(2), qml.Hadamard(4), qml.PauliX(4).inv(), qml.PauliX(4), qml.PauliY(1), qml.RZ(0.34, wires=[1]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params def test_convert_program_with_controlled_operations(self): """Test that a program with controlled operations is properly converted.""" program = pyquil.Program() program += g.RZ(0.34, 1) program += g.RY(0.2, 3).controlled(2) program += g.RX(0.4, 2).controlled(0) program += g.CNOT(1, 4) program += g.CNOT(1, 6).controlled(3) program += g.X(3).controlled(4).controlled(1) with OperationRecorder() as rec: load_program(program)(wires=range(6)) expected_queue = [ qml.RZ(0.34, wires=[1]), qml.CRY(0.2, wires=[2, 3]), qml.CRX(0.4, wires=[0, 2]), qml.CNOT(wires=[1, 4]), plf.ops.CCNOT(wires=[3, 1, 5]), plf.ops.CCNOT(wires=[1, 4, 3]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params def test_convert_program_with_controlled_operations_not_in_pl_core( self, tol): """Test that a program with controlled operations out of scope of PL core/PLF is properly converted, i.e. the operations are replaced with controlled operations.""" program = pyquil.Program() CS_matrix = np.eye(4, dtype=complex) CS_matrix[3, 3] = 1j CCT_matrix = np.eye(8, dtype=complex) CCT_matrix[7, 7] = np.exp(1j * np.pi / 4) program += g.CNOT(0, 1) program += g.S(0).controlled(1) program += g.S(1).controlled(0) program += g.T(0).controlled(1).controlled(2) program += g.T(1).controlled(0).controlled(2) program += g.T(2).controlled(1).controlled(0) with OperationRecorder() as rec: load_program(program)(wires=range(3)) expected_queue = [ qml.CNOT(wires=[0, 1]), qml.QubitUnitary(CS_matrix, wires=[1, 0]), qml.QubitUnitary(CS_matrix, wires=[0, 1]), qml.QubitUnitary(CCT_matrix, wires=[2, 1, 0]), qml.QubitUnitary(CCT_matrix, wires=[2, 0, 1]), qml.QubitUnitary(CCT_matrix, wires=[0, 1, 2]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert np.allclose(converted.params, expected.params, atol=tol, rtol=0) def test_convert_program_with_controlled_dagger_operations(self): """Test that a program that combines controlled and daggered operations is properly converted.""" program = pyquil.Program() program += g.CNOT(0, 1).controlled(2) program += g.CNOT(0, 1).dagger().controlled(2) program += g.CNOT(0, 1).controlled(2).dagger() program += g.CNOT(0, 1).dagger().controlled(2).dagger() program += g.RX(0.3, 3).controlled(4) program += g.RX(0.2, 3).controlled(4).dagger() program += g.RX(0.3, 3).dagger().controlled(4) program += g.RX(0.2, 3).dagger().controlled(4).dagger() program += g.X(2).dagger().controlled(4).controlled(1).dagger() program += g.X(0).dagger().controlled(4).controlled(1) program += g.X(0).dagger().controlled(4).dagger().dagger().controlled( 1).dagger() with OperationRecorder() as rec: load_program(program)(wires=range(5)) expected_queue = [ plf.ops.CCNOT(wires=[2, 0, 1]), plf.ops.CCNOT(wires=[2, 0, 1]).inv(), plf.ops.CCNOT(wires=[2, 0, 1]).inv(), plf.ops.CCNOT(wires=[2, 0, 1]), qml.CRX(0.3, wires=[4, 3]), qml.CRX(0.2, wires=[4, 3]).inv(), qml.CRX(0.3, wires=[4, 3]).inv(), qml.CRX(0.2, wires=[4, 3]), plf.ops.CCNOT(wires=[1, 4, 2]), plf.ops.CCNOT(wires=[1, 4, 0]).inv(), plf.ops.CCNOT(wires=[1, 4, 0]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params def test_convert_program_with_defgates(self): """Test that a program that defines its own gates is properly converted.""" program = pyquil.Program() sqrt_x = np.array([[0.5 + 0.5j, 0.5 - 0.5j], [0.5 - 0.5j, 0.5 + 0.5j]]) sqrt_x_t2 = np.kron(sqrt_x, sqrt_x) sqrt_x_t3 = np.kron(sqrt_x, sqrt_x_t2) sqrt_x_definition = pyquil.quil.DefGate("SQRT-X", sqrt_x) SQRT_X = sqrt_x_definition.get_constructor() sqrt_x_t2_definition = pyquil.quil.DefGate("SQRT-X-T2", sqrt_x_t2) SQRT_X_T2 = sqrt_x_t2_definition.get_constructor() sqrt_x_t3_definition = pyquil.quil.DefGate("SQRT-X-T3", sqrt_x_t3) SQRT_X_T3 = sqrt_x_t3_definition.get_constructor() program += sqrt_x_definition program += sqrt_x_t2_definition program += sqrt_x_t3_definition program += g.CNOT(0, 1) program += SQRT_X(0) program += SQRT_X_T2(1, 2) program += SQRT_X_T3(1, 0, 2) program += g.CNOT(0, 1) program += g.CNOT(1, 2) program += g.CNOT(2, 0) with OperationRecorder() as rec: load_program(program)(wires=range(3)) expected_queue = [ qml.CNOT(wires=[0, 1]), qml.QubitUnitary(sqrt_x, wires=[0]), qml.QubitUnitary(sqrt_x_t2, wires=[1, 2]), qml.QubitUnitary(sqrt_x_t3, wires=[1, 0, 2]), qml.CNOT(wires=[0, 1]), qml.CNOT(wires=[1, 2]), qml.CNOT(wires=[2, 0]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert converted.params == expected.params def test_convert_program_with_controlled_defgates(self, tol): """Test that a program with controlled defined gates is properly converted.""" program = pyquil.Program() sqrt_x = np.array([[0.5 + 0.5j, 0.5 - 0.5j], [0.5 - 0.5j, 0.5 + 0.5j]]) sqrt_x_t2 = np.kron(sqrt_x, sqrt_x) c_sqrt_x = np.eye(4, dtype=complex) c_sqrt_x[2:, 2:] = sqrt_x c_sqrt_x_t2 = np.eye(8, dtype=complex) c_sqrt_x_t2[4:, 4:] = sqrt_x_t2 sqrt_x_definition = pyquil.quil.DefGate("SQRT-X", sqrt_x) SQRT_X = sqrt_x_definition.get_constructor() sqrt_x_t2_definition = pyquil.quil.DefGate("SQRT-X-T2", sqrt_x_t2) SQRT_X_T2 = sqrt_x_t2_definition.get_constructor() program += sqrt_x_definition program += sqrt_x_t2_definition program += g.CNOT(0, 1) program += SQRT_X(0).controlled(1) program += SQRT_X_T2(1, 2).controlled(0) program += g.X(0).controlled(1) program += g.RX(0.4, 0) with OperationRecorder() as rec: load_program(program)(wires=range(3)) expected_queue = [ qml.CNOT(wires=[0, 1]), qml.QubitUnitary(c_sqrt_x, wires=[1, 0]), qml.QubitUnitary(c_sqrt_x_t2, wires=[0, 1, 2]), qml.CNOT(wires=[1, 0]), qml.RX(0.4, wires=[0]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert np.allclose(converted.params, expected.params, atol=tol, rtol=0) def test_convert_program_with_defpermutationgates(self): """Test that a program with gates defined via DefPermutationGate is properly converted.""" program = pyquil.Program() expected_matrix = np.eye(4) expected_matrix = expected_matrix[:, [1, 0, 3, 2]] x_plus_x_definition = pyquil.quil.DefPermutationGate( "X+X", [1, 0, 3, 2]) X_plus_X = x_plus_x_definition.get_constructor() program += x_plus_x_definition program += g.CNOT(0, 1) program += X_plus_X(0, 1) program += g.CNOT(0, 1) with OperationRecorder() as rec: load_program(program)(wires=range(2)) expected_queue = [ qml.CNOT(wires=[0, 1]), qml.QubitUnitary(expected_matrix, wires=[0, 1]), qml.CNOT(wires=[0, 1]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert np.array_equal(converted.params, expected.params) def test_convert_program_with_controlled_defpermutationgates(self): """Test that a program that uses controlled permutation gates is properly converted.""" program = pyquil.Program() expected_matrix = np.eye(4) expected_matrix = expected_matrix[:, [1, 0, 3, 2]] expected_controlled_matrix = np.eye(8) expected_controlled_matrix[4:, 4:] = expected_matrix x_plus_x_definition = pyquil.quil.DefPermutationGate( "X+X", [1, 0, 3, 2]) X_plus_X = x_plus_x_definition.get_constructor() program += x_plus_x_definition program += g.CNOT(0, 1) program += X_plus_X(0, 1).controlled(2) program += X_plus_X(1, 2).controlled(0) program += g.CNOT(0, 1) with OperationRecorder() as rec: load_program(program)(wires=range(3)) expected_queue = [ qml.CNOT(wires=[0, 1]), qml.QubitUnitary(expected_controlled_matrix, wires=[2, 0, 1]), qml.QubitUnitary(expected_controlled_matrix, wires=[0, 1, 2]), qml.CNOT(wires=[0, 1]), ] for converted, expected in zip(rec.queue, expected_queue): assert converted.name == expected.name assert converted.wires == expected.wires assert np.array_equal(converted.params, expected.params) def test_forked_gate_error(self): """Test that an error is raised if conversion of a forked gate is attempted.""" program = pyquil.Program() program += g.CNOT(0, 1) program += g.RX(0.3, 1).forked(2, [0.5]) program += g.CNOT(0, 1) with pytest.raises( qml.DeviceError, match= "Forked gates can not be imported into PennyLane, as this functionality is not supported", ): load_program(program)(wires=range(3))
def layer(qubits, w): rx_layer(w[:qubits]) rz_layer(w[qubits:-qubits + 1]) for i in range(qubits - 1): qml.CRX(w[2 * qubits + i], wires=[i, i + 1])
def qfunc(): qml.CRX(0.2, wires=["w1", "w2"]) qml.CRX(0.3, wires=["w2", "w1"])
class TestMergeRotations: """Test that adjacent rotation gates of the same type will add the angles.""" @pytest.mark.parametrize( ("theta_1", "theta_2", "expected_ops"), [ (0.3, -0.2, [qml.RZ(0.1, wires=0)]), (0.15, -0.15, []), ], ) def test_one_qubit_rotation_merge(self, theta_1, theta_2, expected_ops): """Test that a single-qubit circuit with adjacent rotation along the same axis either merge, or cancel if the angles sum to 0.""" def qfunc(): qml.RZ(theta_1, wires=0) qml.RZ(theta_2, wires=0) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) # Check that all operations and parameter values are as expected for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters) @pytest.mark.parametrize( ("theta_1", "theta_2", "expected_ops"), [ (0.3, -0.2, [qml.RZ(0.3, wires=0), qml.RZ(-0.2, wires=1)]), (0.15, -0.15, [qml.RZ(0.15, wires=0), qml.RZ(-0.15, wires=1)]), ], ) def test_two_qubits_rotation_no_merge(self, theta_1, theta_2, expected_ops): """Test that a two-qubit circuit with rotations on different qubits do not get merged.""" def qfunc(): qml.RZ(theta_1, wires=0) qml.RZ(theta_2, wires=1) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters) def test_two_qubits_rotation_merge_tolerance(self): """Test whether tolerance argument is respected for merging.""" def qfunc(): qml.RZ(1e-7, wires=0) qml.RZ(-2e-7, wires=0) # Try with default tolerance; these ops should still be applied transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == 1 assert ops[0].name == "RZ" assert ops[0].parameters[0] == -1e-7 # Now try with higher tolerance threshold; the ops should cancel transformed_qfunc = merge_rotations(atol=1e-5)(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == 0 @pytest.mark.parametrize( ("theta_11", "theta_12", "theta_21", "theta_22", "expected_ops"), [ (0.3, -0.2, 0.5, -0.8, [qml.RX(0.1, wires=0), qml.RY(-0.3, wires=1)]), (0.3, -0.3, 0.7, -0.1, [qml.RY(0.6, wires=1)]), ], ) def test_two_qubits_rotation_no_merge(self, theta_11, theta_12, theta_21, theta_22, expected_ops): """Test that a two-qubit circuit with rotations on different qubits get merged.""" def qfunc(): qml.RX(theta_11, wires=0) qml.RY(theta_21, wires=1) qml.RX(theta_12, wires=0) qml.RY(theta_22, wires=1) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters) @pytest.mark.parametrize( ("theta_11", "theta_12", "theta_21", "theta_22", "expected_ops"), [ (0.3, -0.2, 0.5, -0.8, [qml.CRX(0.5, wires=[0, 1]), qml.RY(-1.3, wires=1)]), (0.3, 0.3, 0.7, -0.1, [qml.RY(-0.8, wires=1)]), ], ) def test_two_qubits_merge_with_adjoint(self, theta_11, theta_12, theta_21, theta_22, expected_ops): """Test that adjoint rotations on different qubits get merged.""" def qfunc(): qml.CRX(theta_11, wires=[0, 1]) qml.adjoint(qml.RY)(theta_21, wires=2) qml.adjoint(qml.CRX)(theta_12, wires=[0, 1]) qml.RY(theta_22, wires=2) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters) def test_two_qubits_merge_gate_subset(self): """Test that specifying a subset of operations to include merges correctly.""" def qfunc(): qml.CRX(0.1, wires=[0, 1]) qml.CRX(0.2, wires=[0, 1]) qml.RY(0.3, wires=["a"]) qml.RY(0.5, wires=["a"]) qml.RX(-0.5, wires=[2]) qml.RX(0.2, wires=[2]) transformed_qfunc = merge_rotations(include_gates=["RX", "CRX"])(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["CRX", "RY", "RY", "RX"] wires_expected = [Wires([0, 1]), Wires("a"), Wires("a"), Wires(2)] compare_operation_lists(ops, names_expected, wires_expected) assert qml.math.isclose(ops[0].parameters[0], 0.3) assert qml.math.isclose(ops[1].parameters[0], 0.3) assert qml.math.isclose(ops[2].parameters[0], 0.5) assert qml.math.isclose(ops[3].parameters[0], -0.3) def test_one_qubit_rotation_blocked(self): """Test that rotations on one-qubit separated by a "blocking" operation don't merge.""" def qfunc(): qml.RX(0.5, wires=0) qml.Hadamard(wires=0) qml.RX(0.4, wires=0) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["RX", "Hadamard", "RX"] wires_expected = [Wires(0)] * 3 compare_operation_lists(ops, names_expected, wires_expected) assert ops[0].parameters[0] == 0.5 assert ops[2].parameters[0] == 0.4 def test_two_qubits_rotation_blocked(self): """Test that rotations on a two-qubit system separated by a "blocking" operation don't merge.""" def qfunc(): qml.RX(-0.42, wires=0) qml.CNOT(wires=[0, 1]) qml.RX(0.8, wires=0) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["RX", "CNOT", "RX"] wires_expected = [Wires(0), Wires([0, 1]), Wires(0)] compare_operation_lists(ops, names_expected, wires_expected) assert ops[0].parameters[0] == -0.42 assert ops[2].parameters[0] == 0.8 @pytest.mark.parametrize( ("theta_1", "theta_2", "expected_ops"), [ (0.3, -0.2, [qml.CRY(0.1, wires=["w1", "w2"])]), (0.15, -0.15, []), ], ) def test_controlled_rotation_merge(self, theta_1, theta_2, expected_ops): """Test that adjacent controlled rotations on the same wires in same order get merged.""" def qfunc(): qml.CRY(theta_1, wires=["w1", "w2"]) qml.CRY(theta_2, wires=["w1", "w2"]) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations assert len(ops) == len(expected_ops) # Check that all operations and parameter values are as expected for op_obtained, op_expected in zip(ops, expected_ops): assert op_obtained.name == op_expected.name assert np.allclose(op_obtained.parameters, op_expected.parameters) def test_controlled_rotation_no_merge(self): """Test that adjacent controlled rotations on the same wires in different order don't merge.""" def qfunc(): qml.CRX(0.2, wires=["w1", "w2"]) qml.CRX(0.3, wires=["w2", "w1"]) transformed_qfunc = merge_rotations()(qfunc) ops = qml.transforms.make_tape(transformed_qfunc)().operations names_expected = ["CRX", "CRX"] wires_expected = [Wires(["w1", "w2"]), Wires(["w2", "w1"])] compare_operation_lists(ops, names_expected, wires_expected) assert ops[0].parameters[0] == 0.2 assert ops[1].parameters[0] == 0.3
def qfunc(): qml.CRX(theta_11, wires=[0, 1]) qml.adjoint(qml.RY)(theta_21, wires=2) qml.adjoint(qml.CRX)(theta_12, wires=[0, 1]) qml.RY(theta_22, wires=2)
pytestmark = pytest.mark.skip_unsupported np.random.seed(42) # ========================================================== # Some useful global variables # gates for which device support is tested ops = { "BasisState": qml.BasisState(np.array([0]), wires=[0]), "CNOT": qml.CNOT(wires=[0, 1]), "CRX": qml.CRX(0, wires=[0, 1]), "CRY": qml.CRY(0, wires=[0, 1]), "CRZ": qml.CRZ(0, wires=[0, 1]), "CRot": qml.CRot(0, 0, 0, wires=[0, 1]), "CSWAP": qml.CSWAP(wires=[0, 1, 2]), "CZ": qml.CZ(wires=[0, 1]), "CY": qml.CY(wires=[0, 1]), "DiagonalQubitUnitary": qml.DiagonalQubitUnitary(np.array([1, 1]), wires=[0]), "Hadamard":
], ) def test_join_spectra(spectrum1, spectrum2, expected): """Test that spectra are joined correctly.""" joined = join_spectra(spectrum1, spectrum2) assert joined == expected @pytest.mark.parametrize( "op, expected", [ (qml.RX(0.1, wires=0), [0, 1]), # generator is a class (qml.RY(0.1, wires=0), [0, 1]), # generator is a class (qml.RZ(0.1, wires=0), [0, 1]), # generator is a class (qml.PhaseShift(0.5, wires=0), [0, 1]), # generator is an array (qml.CRX(0.2, wires=[0, 1]), [0, 0.5, 1]), # generator is an array (qml.ControlledPhaseShift( 0.5, wires=[0, 1]), [0, 1]), # generator is an array ], ) def test_get_spectrum(op, expected): """Test that the spectrum is correctly extracted from an operator.""" spec = get_spectrum(op, decimals=10) assert np.allclose(sorted(spec), expected, atol=1e-6, rtol=0) def test_get_spectrum_complains_no_generator(): """Test that an error is raised if the operator has no generator defined.""" # Observables have no generator attribute with pytest.raises(ValueError, match="Generator of operation"):
def circuit(a, b): qml.RX(a, wires=0) qml.CRX(b, wires=[0, 1]) return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))
def testcircuit(): qml.CRX(testangle, wires=[2, 0])