Beispiel #1
0
    def test_wrapped_function_nontrainable_variable_args(self):
        """Test that a wrapped function with signature of the form
        func(arr1, arr2, ...) acting on non-trainable input returns non-trainable output"""
        arr1 = np.array([0, 1], requires_grad=False)
        arr2 = np.array([2, 3], requires_grad=False)

        res = np.arctan2(arr1, arr2)
        assert not res.requires_grad

        # If one of the inputs is trainable, the output always is.
        arr1.requires_grad = True
        res = np.arctan2(arr1, arr2)
        assert res.requires_grad
Beispiel #2
0
    def test_controlled_addition(self):
        """Test the CX symplectic transform."""
        self.logTestName()

        s = 0.543
        S = controlled_addition(s)

        # test that S = B(theta+pi/2, 0) [S(z) x S(-z)] B(theta, 0)
        r = np.arcsinh(-s / 2)
        theta = 0.5 * np.arctan2(-1 / np.cosh(r), -np.tanh(r))
        Sz = block_diag(squeezing(r, 0),
                        squeezing(-r, 0))[:, [0, 2, 1, 3]][[0, 2, 1, 3]]

        expected = beamsplitter(theta + np.pi / 2, 0) @ Sz @ beamsplitter(
            theta, 0)
        self.assertAllAlmostEqual(S, expected, delta=self.tol)

        # test that S[x1, x2, p1, p2] -> [x1, x2+sx1, p1-sp2, p2]
        x1 = 0.5432
        x2 = -0.453
        p1 = 0.154
        p2 = -0.123
        out = S @ np.array([x1, x2, p1, p2]) * np.sqrt(2 * hbar)
        expected = np.array([x1, x2 + s * x1, p1 - s * p2, p2]) * np.sqrt(
            2 * hbar)
        self.assertAllAlmostEqual(out, expected, delta=self.tol)
Beispiel #3
0
    def _rotosolve(objective_fn, x, generators, d):
        r"""The rotosolve step for one parameter and one set of generators.

        Updates the parameter :math:`\theta_d` based on Equation 1 in
        `Ostaszewski et al. (2019) <https://arxiv.org/abs/1905.09692>`_.

        Args:
            objective_fn (function): The objective function for optimization. It must have the
                signature ``objective_fn(x, generators=None)`` with a sequence of the values ``x``
                and a list of the gates ``generators`` as inputs, returning a single value.
            x (Union[Sequence[float], float]): Sequence containing the initial values of the
                variables to be optimized over, or a single float with the initial value.
            generators (list[~.Operation]): List containing the initial ``pennylane.ops.qubit``
                operators to be used in the circuit and optimized over.
            d (int): The position in the input sequence ``x`` containing the value to be optimized.

        Returns:
            array: The input sequence ``x`` with the value at position ``d`` optimized.
        """
        # helper function for x[d] = theta
        def insert(x, d, theta):
            x[d] = theta
            return x

        H_0 = float(objective_fn(insert(x, d, 0), generators))
        H_p = float(objective_fn(insert(x, d, np.pi / 2), generators))
        H_m = float(objective_fn(insert(x, d, -np.pi / 2), generators))

        a = np.arctan2(2 * H_0 - H_p - H_m, H_p - H_m)

        x[d] = -np.pi / 2 - a

        if x[d] <= -np.pi:
            x[d] += 2 * np.pi
        return x
Beispiel #4
0
    def _rotosolve(objective_fn, x, d):
        r"""The rotosolve step for one parameter.

        Updates the parameter :math:`\theta_d` based on Equation 1 in
        `Ostaszewski et al. (2019) <https://arxiv.org/abs/1905.09692>`_.

        Args:
            objective_fn (function): The objective function for optimization. It should take a
                sequence of the values ``x`` and a list of the gates ``generators`` as inputs, and
                return a single value.
            x (Union[Sequence[float], float]): Sequence containing the initial values of the
                variables to be optimized over, or a single float with the initial value.
            d (int): The position in the input sequence ``x`` containing the value to be optimized.

        Returns:
            array: The input sequence ``x`` with the value at position ``d`` optimized.
        """

        # helper function for x[d] = theta
        def insert(x, d, theta):
            x[d] = theta
            return x

        H_0 = float(objective_fn(insert(x, d, 0)))
        H_p = float(objective_fn(insert(x, d, np.pi / 2)))
        H_m = float(objective_fn(insert(x, d, -np.pi / 2)))

        a = np.arctan2(2 * H_0 - H_p - H_m, H_p - H_m)

        x[d] = -np.pi / 2 - a

        if x[d] <= -np.pi:
            x[d] += 2 * np.pi
        return x
Beispiel #5
0
def rotosolve(d, params, generators, cost, M_0):  # M_0 only calculated once
    params[d] = np.pi / 2.0
    M_0_plus = cost(params, generators)
    params[d] = -np.pi / 2.0
    M_0_minus = cost(params, generators)
    a = np.arctan2(2.0 * M_0 - M_0_plus - M_0_minus,
                   M_0_plus - M_0_minus)  # returns value in (-pi,pi]
    params[d] = -np.pi / 2.0 - a
    if params[d] <= -np.pi:
        params[d] += 2 * np.pi
    return cost(params, generators)
Beispiel #6
0
def opt_theta(d, params, cost):
    params[d] = 0.0
    M_0 = cost(params)
    params[d] = np.pi / 2.0
    M_0_plus = cost(params)
    params[d] = -np.pi / 2.0
    M_0_minus = cost(params)
    a = np.arctan2(2.0 * M_0 - M_0_plus - M_0_minus,
                   M_0_plus - M_0_minus)  # returns value in (-pi,pi]
    params[d] = -np.pi / 2.0 - a
    # restrict output to lie in (-pi,pi], a convention
    # consistent with the Rotosolve paper
    if params[d] <= -np.pi:
        params[d] += 2 * np.pi
 def QNNLayer(self, params):
     '''
     Definition of a single ST step for
     layering QNN
     '''
     ExcInts = params[0:3]
     ExtField = params[3:6]
     #  Parameters for external
     # field evol
     Hx = ExtField[0]
     Hy = ExtField[1]
     Hz = ExtField[2]
     H = np.sqrt(Hx**2 + Hy**2 + Hz**2)
     # Parameter values for Qiskit
     PHI = np.arctan2(Hy, Hx) + 2*np.pi
     THETA = np.arccos(Hz/H)
     LAMBDA = np.pi
     # Cascade Spin pair interaction
     for idx in range(self.num_spins-1):
         # Convert to computational basis
         qml.CNOT(wires=[idx, idx+1])
         qml.Hadamard(wires=idx)
         #  Compute J3 phase
         qml.RZ(ExcInts[2], wires=idx+1)
         # Compute J1 phase
         qml.RZ(ExcInts[0], wires=idx)
         # Compute J2 Phase
         qml.CNOT(wires=[idx, idx+1])
         qml.RZ(-ExcInts[1], wires=idx+1)
         qml.CNOT(wires=[idx, idx+1])
         # Return to computational basis
         qml.Hadamard(wires=idx)
         qml.CNOT(wires=[idx, idx+1])
         # Include external field
         qml.U3(-THETA, -LAMBDA, -PHI, wires=idx)
         qml.RZ(H, wires=idx)
         qml.U3(THETA, PHI, LAMBDA, wires=idx)
     #  Include external field for last spin
     qml.U3(-THETA, -LAMBDA, -PHI, wires=self.num_spins-1)
     qml.RZ(H, wires=self.num_spins-1)
     qml.U3(THETA, PHI, LAMBDA, wires=self.num_spins-1)
    def rotosolve_step(f, x):
        """Helper function to test the Rotosolve and Rotoselect optimizers"""
        # make sure that x is an array
        if np.ndim(x) == 0:
            x = np.array([x])

        # helper function for x[d] = theta
        def insert(x, d, theta):
            x[d] = theta
            return x

        for d, _ in enumerate(x):
            H_0 = float(f(insert(x, d, 0)))
            H_p = float(f(insert(x, d, np.pi / 2)))
            H_m = float(f(insert(x, d, -np.pi / 2)))

            a = np.arctan2(2 * H_0 - H_p - H_m, H_p - H_m)

            x[d] = -np.pi / 2 - a

            if x[d] <= -np.pi:
                x[d] += 2 * np.pi
        return x