def _transform_observable(obs, w, Z): """Apply a Gaussian linear transformation to each index of an observable. Args: obs (Observable): observable to transform w (int): number of wires in the circuit Z (array[float]): Heisenberg picture representation of the linear transformation Returns: Observable: transformed observable """ q = obs.heisenberg_obs(w) if q.ndim != obs.ev_order: raise QuantumFunctionError( "Mismatch between the polynomial order of observable and its Heisenberg representation" ) qp = q @ Z if q.ndim == 2: # 2nd order observable qp = qp + qp.T elif q.ndim > 2: raise NotImplementedError("Transforming observables of order > 2 not implemented.") return qml.expval(qml.PolyXP(qp, wires=range(w), do_queue=False))
def _transform_observable(obs, Z, device_wires): """Apply a Gaussian linear transformation to each index of an observable. Args: obs (.Observable): observable to transform Z (array[float]): Heisenberg picture representation of the linear transformation device_wires (.Wires): wires on the device the transformed observable is to be measured on Returns: .Observable: the transformed observable """ # Get the Heisenberg representation of the observable # in the position/momentum basis. The returned matrix/vector # will have been expanded to act on the entire device. if obs.ev_order > 2: raise NotImplementedError("Transforming observables of order > 2 not implemented.") A = obs.heisenberg_obs(device_wires) if A.ndim != obs.ev_order: raise ValueError( "Mismatch between the polynomial order of observable and its Heisenberg representation" ) # transform the observable by the linear transformation Z A = A @ Z if A.ndim == 2: A = A + A.T # TODO: if the A matrix corresponds to a known observable in PennyLane, # for example qml.X, qml.P, qml.NumberOperator, we should return that # instead. This will allow for greater device compatibility. return qml.PolyXP(A, wires=device_wires, do_queue=False)
def _transform_observable(observable, ob_successors, w, Z): """Transform the observable Args: observable (Observable): the observable to perform the transformation on ob_successors (list[Observable]): list of observable successors to current operation w (int): number of wires Z (array[float]): the Heisenberg picture representation of the linear transformation Returns: float: expectation value """ if observable not in ob_successors: return observable q = observable.heisenberg_obs(w) if q.ndim != observable.ev_order: raise QuantumFunctionError( "Mismatch between polynomial order of observable and heisenberg representation") qp = q @ Z if q.ndim == 2: # 2nd order observable qp = qp +qp.T return qml.expval(qml.PolyXP(qp, wires=range(w)))
def qf(x, y): qml.Displacement(0.5, 0, wires=[0]) qml.Squeezing(x, 0, wires=[0]) M = np.zeros((5, 5), dtype=object) M[1, 1] = y M[1, 2] = 1.0 M[2, 1] = 1.0 return qml.expval(qml.PolyXP(M, [0, 1]))
def test_multiple_output_values(self, tol): """Tests correct output shape and evaluation for a tape with multiple measurement types""" dev = qml.device("default.gaussian", wires=2) x = 0.543 y = -0.654 with QuantumTape() as tape: qml.Displacement(x, 0, wires=[0]) qml.Squeezing(y, 0, wires=[1]) qml.Beamsplitter(np.pi / 4, 0, wires=[0, 1]) qml.expval(qml.PolyXP(np.diag([0, 1, 0]), wires=0)) # X^2 qml.var(qml.P(1)) assert tape.output_dim == 2 res = tape.execute(dev) assert res.shape == (2, )
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.24"), ], ) 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 @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([[2, 0], [0, 2]]), np.array([1, 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.InterferometerUnitary( np.eye(4), wires=[1, 3]), 1, "InterferometerUnitary(M0)"), (qml.InterferometerUnitary( np.eye(4), wires=[1, 3]), 3, "InterferometerUnitary(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"), (qml.measure.sample(wires=[0, 1]), 0, "basis"), # not providing an observable in (qml.measure.sample(wires=[0, 1]), 1, "basis"), # sample gets displayed as raw (two_wire_quantum_tape(), 0, "QuantumTape:T0"), (two_wire_quantum_tape(), 1, "QuantumTape:T0"), ], ) 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, 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([[2, 0], [0, 2]]), np.array([1, 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.InterferometerUnitary( np.eye(4), wires=[1, 3]), 1, "InterferometerUnitary(M0)"), (qml.InterferometerUnitary( np.eye(4), wires=[1, 3]), 3, "InterferometerUnitary(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"), (qml.measure.sample(wires=[0, 1]), 0, "basis"), # not providing an observable in (qml.measure.sample(wires=[0, 1]), 1, "basis"), # sample gets displayed as raw (two_wire_quantum_tape(), 0, "QuantumTape:T0"), (two_wire_quantum_tape(), 1, "QuantumTape:T0"), ], ) 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, 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, 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, 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, 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, 0) == "" def test_element_representation_str(self, unicode_representation_resolver): """Test that element_representation properly handles strings.""" assert unicode_representation_resolver.element_representation( "Test", 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, wire) assert unicode_representation_resolver.output_representation.call_args[ 0] == (obs, 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, wire) assert unicode_representation_resolver.operator_representation.call_args[ 0] == (op, wire)
def circuit(x, *, k=0.0): qml.Displacement(x, 0, wires=0) qml.Rotation(k, wires=0) return qml.expval(qml.PolyXP(np.diag([0, 1, 0]), wires=0)) # X^2
def _pd_analytic_var(self, idx, args, kwargs, **options): """Partial derivative of the variance of an observable using the parameter-shift method. Args: idx (int): flattened index of the parameter wrt. which the p.d. is computed args (array[float]): flattened positional arguments at which to evaluate the p.d. kwargs (dict[str, Any]): auxiliary arguments Returns: array[float]: partial derivative of the node """ # boolean mask: elements are True where the return type is a variance, False for expectations where_var = [ e.return_type is ObservableReturnTypes.Variance for e in self.circuit.observables ] var_observables = [ e for e in self.circuit.observables if e.return_type == ObservableReturnTypes.Variance ] # first, replace each var(A) with <A^2> new_observables = [] for e in var_observables: # need to calculate d<A^2>/dp w = e.wires # CV first order observable # get the heisenberg representation # This will be a real 1D vector representing the # first order observable in the basis [I, x, p] A = e._heisenberg_rep(e.parameters) # pylint: disable=protected-access # take the outer product of the heisenberg representation # with itself, to get a square symmetric matrix representing # the square of the observable A = np.outer(A, A) new = qml.expval(qml.PolyXP(A, w, do_queue=False)) # replace the var(A) observable with <A^2> self.circuit.update_node(e, new) new_observables.append(new) # calculate the analytic derivatives of the <A^2> observables pdA2 = self._pd_analytic(idx, args, kwargs, force_order2=True) # restore the original observables, but convert their return types to expectation for e, new in zip(var_observables, new_observables): self.circuit.update_node(new, e) e.return_type = ObservableReturnTypes.Expectation # evaluate <A> evA = np.asarray(self.evaluate(args, kwargs)) # evaluate the analytic derivative of <A> pdA = self._pd_analytic(idx, args, kwargs) # restore return types for e in var_observables: e.return_type = ObservableReturnTypes.Variance # return d(var(A))/dp = d<A^2>/dp -2 * <A> * d<A>/dp for the variances, # d<A>/dp for plain expectations return np.where(where_var, pdA2 - 2 * evA * pdA, pdA)
def circuit(r, phi): qml.Squeezing(r, 0, wires=0) qml.Rotation(phi, wires=0) return qml.var(qml.PolyXP(np.array([0, 1, 0]), wires=0))
def circuit(x): qml.ThermalState(nbar, wires=0) qml.Displacement(x, 0, wires=0) return qml.expval(qml.PolyXP(Q, 0))
def circuit(x): qml.Displacement(x, 0, wires=0) return qml.expval(qml.PolyXP(Q, 0))
def parameter_shift_var(self, idx, params, **options): r"""Partial derivative using the first-order or second-order parameter-shift rule of a tape consisting of a mixture of expectation values and variances of observables. Expectation values may be of first- or second-order observables, but variances can only be taken of first-order variables. .. warning:: This method can only be executed on devices that support the :class:`~.PolyXP` observable. Args: idx (int): trainable parameter index to differentiate with respect to params (list[Any]): the quantum tape operation parameters Keyword Args: force_order2 (bool): iff True, use the order-2 method even if not necessary device (.Device): A PennyLane device that can execute quantum operations and return measurement statistics. This keyword argument is required, as the device labels may be needed to generate the quantum tapes for computing the gradient. Returns: array[float]: 1-dimensional array of length determined by the tape output measurement statistics """ # pylint: disable=protected-access device = options["device"] if "PolyXP" not in device.observables: # If the device does not support PolyXP, must fallback # to numeric differentiation. warnings.warn( f"The device {device.short_name} does not support " "the PolyXP observable. The analytic parameter-shift cannot be used for " "second-order observables; falling back to finite-differences.", UserWarning, ) return self.numeric_pd(idx, params, **options) tapes = [] # Get <A>, the expectation value of the tape with unshifted parameters. evA_tape = self.copy() # Temporarily convert all variance measurements on the tape into expectation values for i in self.var_idx: obs = evA_tape._measurements[i].obs evA_tape._measurements[i] = MeasurementProcess( qml.operation.Expectation, obs=obs) # evaluate the analytic derivative of <A> pdA_tapes, pdA_fn = evA_tape.parameter_shift_first_order( idx, params, **options) tapes.extend(pdA_tapes) pdA2_tape = self.copy() for i in self.var_idx: # We need to calculate d<A^2>/dp; to do so, we replace the # observables A in the queue with A^2. obs = pdA2_tape._measurements[i].obs # CV first order observable # get the heisenberg representation # This will be a real 1D vector representing the # first order observable in the basis [I, x, p] A = obs._heisenberg_rep(obs.parameters) # pylint: disable=protected-access # take the outer product of the heisenberg representation # with itself, to get a square symmetric matrix representing # the square of the observable obs = qml.PolyXP(np.outer(A, A), wires=obs.wires, do_queue=False) pdA2_tape._measurements[i] = MeasurementProcess( qml.operation.Expectation, obs=obs) # Here, we calculate the analytic derivatives of the <A^2> observables. pdA2_tapes, pdA2_fn = pdA2_tape.parameter_shift_second_order( idx, params, **options) tapes.extend(pdA2_tapes) # Make sure that the expectation value of the tape with unshifted parameters # is only calculated once, if `self._append_evA_tape` is True. if self._append_evA_tape: tapes.append(evA_tape) # Now that the <A> tape has been appended, we want to avoid # appending it for subsequent parameters, as the result can simply # be re-used. self._append_evA_tape = False def processing_fn(results): """Computes the gradient of the parameter at index ``idx`` via the second order CV parameter-shift method for a circuit containing a mixture of expectation values and variances. Args: results (list[real]): evaluated quantum tapes Returns: array[float]: 1-dimensional array of length determined by the tape output measurement statistics """ pdA = pdA_fn(results[0:2]) pdA2 = pdA2_fn(results[2:4]) # Check if the expectation value of the tape with unshifted parameters # has already been calculated. if self._evA_result is None: # The expectation value hasn't been previously calculated; # it will be the last element of the `results` argument. self._evA_result = np.array(results[-1]) # return d(var(A))/dp = d<A^2>/dp -2 * <A> * d<A>/dp for the variances, # d<A>/dp for plain expectations return np.where(self.var_mask, pdA2 - 2 * self._evA_result * pdA, pdA) return tapes, processing_fn
def var_param_shift(tape, dev_wires, argnum=None, shift=np.pi / 2, gradient_recipes=None, f0=None): r"""Partial derivative using the first-order or second-order parameter-shift rule of a tape consisting of a mixture of expectation values and variances of observables. Expectation values may be of first- or second-order observables, but variances can only be taken of first-order variables. .. warning:: This method can only be executed on devices that support the :class:`~.PolyXP` observable. Args: tape (.QuantumTape): quantum tape to differentiate dev_wires (.Wires): wires on the device the parameter-shift method is computed on argnum (int or list[int] or None): Trainable parameter indices to differentiate with respect to. If not provided, the derivative with respect to all trainable indices are returned. shift (float): The shift value to use for the two-term parameter-shift formula. Only valid if the operation in question supports the two-term parameter-shift rule (that is, it has two distinct eigenvalues) and ``gradient_recipes`` is ``None``. gradient_recipes (tuple(list[list[float]] or None)): List of gradient recipes for the parameter-shift method. One gradient recipe must be provided per trainable parameter. f0 (tensor_like[float] or None): Output of the evaluated input tape. If provided, and the gradient recipe contains an unshifted term, this value is used, saving a quantum evaluation. Returns: tuple[list[QuantumTape], function]: A tuple containing a list of generated tapes, in addition to a post-processing function to be applied to the evaluated tapes. """ argnum = argnum or tape.trainable_params # Determine the locations of any variance measurements in the measurement queue. var_mask = [ m.return_type is qml.operation.Variance for m in tape.measurements ] var_idx = np.where(var_mask)[0] # Get <A>, the expectation value of the tape with unshifted parameters. expval_tape = tape.copy(copy_operations=True) # Convert all variance measurements on the tape into expectation values for i in var_idx: obs = expval_tape._measurements[i].obs expval_tape._measurements[i] = qml.measure.MeasurementProcess( qml.operation.Expectation, obs=obs) gradient_tapes = [expval_tape] # evaluate the analytic derivative of <A> pdA_tapes, pdA_fn = expval_param_shift(expval_tape, argnum, shift, gradient_recipes, f0) gradient_tapes.extend(pdA_tapes) # Store the number of first derivative tapes, so that we know # the number of results to post-process later. tape_boundary = len(pdA_tapes) + 1 expval_sq_tape = tape.copy(copy_operations=True) for i in var_idx: # We need to calculate d<A^2>/dp; to do so, we replace the # observables A in the queue with A^2. obs = expval_sq_tape._measurements[i].obs # CV first-order observable # get the heisenberg representation # This will be a real 1D vector representing the # first-order observable in the basis [I, x, p] A = obs._heisenberg_rep(obs.parameters) # take the outer product of the heisenberg representation # with itself, to get a square symmetric matrix representing # the square of the observable obs = qml.PolyXP(np.outer(A, A), wires=obs.wires) expval_sq_tape._measurements[i] = qml.measure.MeasurementProcess( qml.operation.Expectation, obs=obs) # Non-involutory observables are present; the partial derivative of <A^2> # may be non-zero. Here, we calculate the analytic derivatives of the <A^2> # observables. pdA2_tapes, pdA2_fn = second_order_param_shift(expval_sq_tape, dev_wires, argnum, shift, gradient_recipes) gradient_tapes.extend(pdA2_tapes) def processing_fn(results): mask = qml.math.convert_like(qml.math.reshape(var_mask, [-1, 1]), results[0]) f0 = qml.math.expand_dims(results[0], -1) pdA = pdA_fn(results[1:tape_boundary]) pdA2 = pdA2_fn(results[tape_boundary:]) # return d(var(A))/dp = d<A^2>/dp -2 * <A> * d<A>/dp for the variances (mask==True) # d<A>/dp for plain expectations (mask==False) return qml.math.where(mask, pdA2 - 2 * f0 * pdA, pdA) return gradient_tapes, processing_fn
def parameter_shift_var(self, idx, device, params, **options): r"""Partial derivative using the first-order or second-order parameter-shift rule of a tape consisting of a mixture of expectation values and variances of observables. Expectation values may be of first- or second-order observables, but variances can only be taken of first-order variables. .. warning:: This method can only be executed on devices that support the :class:`~.PolyXP` observable. Args: idx (int): trainable parameter index to differentiate with respect to device (.Device): a PennyLane device that can execute quantum operations and return measurement statistics params (list[Any]): the quantum tape operation parameters Keyword Args: force_order2 (bool): iff True, use the order-2 method even if not necessary Returns: array[float]: 1-dimensional array of length determined by the tape output measurement statistics """ # pylint: disable=protected-access if "PolyXP" not in device.observables: # If the device does not support PolyXP, must fallback # to numeric differentiation. warnings.warn( f"The device {device.short_name} does not support " "the PolyXP observable. The analytic parameter-shift cannot be used for " "second-order observables; falling back to finite-differences.", UserWarning, ) return self.numeric_pd(idx, device, params, **options) temp_tape = self.copy() # Temporarily convert all variance measurements on the tape into expectation values for i in self.var_idx: obs = self._measurements[i].obs temp_tape._measurements[i] = MeasurementProcess(qml.operation.Expectation, obs=obs) # Get <A>, the expectation value of the tape with unshifted parameters. This is only # calculated once, if `self._evA` is not None. if self._evA is None: self._evA = np.asarray(temp_tape.execute_device(params, device)) # evaluate the analytic derivative of <A> pdA = temp_tape.parameter_shift_first_order(idx, device, params, **options) for i in self.var_idx: # We need to calculate d<A^2>/dp; to do so, we replace the # observables A in the queue with A^2. obs = self._measurements[i].obs # CV first order observable # get the heisenberg representation # This will be a real 1D vector representing the # first order observable in the basis [I, x, p] A = obs._heisenberg_rep(obs.parameters) # pylint: disable=protected-access # take the outer product of the heisenberg representation # with itself, to get a square symmetric matrix representing # the square of the observable obs = qml.PolyXP(np.outer(A, A), wires=obs.wires, do_queue=False) temp_tape._measurements[i] = MeasurementProcess(qml.operation.Expectation, obs=obs) # Here, we calculate the analytic derivatives of the <A^2> observables. pdA2 = temp_tape.parameter_shift_second_order(idx, device, params, **options) # return d(var(A))/dp = d<A^2>/dp -2 * <A> * d<A>/dp for the variances, # d<A>/dp for plain expectations return np.where(self.var_mask, pdA2 - 2 * self._evA * pdA, pdA)