예제 #1
0
def test_enable_tape_mode_decorator():
    """Test that the enable_tape function properly
    enables tape mode when creating QNodes using the decorator."""
    dev = qml.device("default.qubit", wires=1)

    qml.disable_tape()

    @qml.qnode(dev)
    def circuit(x, y):
        qml.RX(x, wires=0)
        return qml.expval(qml.PauliZ(0))

    circuit(0.5, 0.1)

    assert not isinstance(circuit, qml.tape.QNode)
    assert isinstance(circuit, qml.qnodes.BaseQNode)
    assert not hasattr(circuit, "qtape")

    qml.enable_tape()

    assert "tape" in qml.expval.__module__

    @qml.qnode(dev)
    def circuit(x, y):
        qml.RX(x, wires=0)
        return qml.expval(qml.PauliZ(0))

    circuit(0.5, 0.1)

    assert isinstance(circuit, qml.tape.QNode)
    assert not isinstance(circuit, qml.qnodes.BaseQNode)
    assert hasattr(circuit, "qtape")
예제 #2
0
def test_enable_tape_mode_class():
    """Test that the enable_tape function properly
    enables tape mode when creating QNodes using the class."""
    dev = qml.device("default.qubit", wires=1)

    def circuit(x, y):
        qml.RX(x, wires=0)
        return qml.expval(qml.PauliZ(0))

    qnode = qml.QNode(circuit, dev)
    qnode(0.5, 0.1)

    assert not isinstance(qnode, qml.tape.QNode)
    assert isinstance(qnode, qml.qnodes.BaseQNode)
    assert not hasattr(qnode, "qtape")

    qml.enable_tape()

    def circuit(x, y):
        qml.RX(x, wires=0)
        return qml.expval(qml.PauliZ(0))

    qnode = qml.QNode(circuit, dev)
    qnode(0.5, 0.1)

    assert isinstance(qnode, qml.tape.QNode)
    assert not isinstance(qnode, qml.qnodes.BaseQNode)
    assert hasattr(qnode, "qtape")

    qml.disable_tape()
예제 #3
0
def test_disable_tape_exception():
    """Test that disabling tape mode raises a warning
    if not currently in tape mode"""
    qml.disable_tape()
    with pytest.warns(UserWarning, match="Tape mode is not currently enabled"):
        qml.disable_tape()
    qml.enable_tape()
예제 #4
0
def tape_mode(request, mocker):
    """Tests using this fixture will be run twice, once in tape mode and once without."""

    if request.param:
        # Several attributes and methods on the old QNode have a new location on the new QNode/tape.
        # Here, we dynamically mock so that the tests do not have to be modified to support both
        # tape and non-tape mode. Once tape mode is default, we can make the equivalent
        # changes directly in the tests.
        mocker.patch(
            "pennylane.tape.QNode.ops",
            property(
                lambda self: self.qtape.operations + self.qtape.observables),
            create=True)
        mocker.patch("pennylane.tape.QNode.h",
                     property(lambda self: self.diff_options["h"]),
                     create=True)
        mocker.patch("pennylane.tape.QNode.order",
                     property(lambda self: self.diff_options["order"]),
                     create=True)
        mocker.patch("pennylane.tape.QNode.jacobian",
                     lambda self: self.qtape.jacobian,
                     create=True)

        qml.enable_tape()

    yield

    if request.param:
        qml.disable_tape()
예제 #5
0
def test_disable_tape():
    """Test that the disable_tape function reverts QNode creation
    to standard behaviour"""
    dev = qml.device("default.qubit", wires=1)

    def circuit(x, y):
        qml.RX(x, wires=0)
        return qml.expval(qml.PauliZ(0))

    # doesn't matter how many times we call it
    qml.enable_tape()
    qml.enable_tape()

    qnode = qml.QNode(circuit, dev)
    qnode(0.5, 0.1)

    assert isinstance(qnode, qml.tape.QNode)
    assert not isinstance(qnode, qml.qnodes.BaseQNode)
    assert hasattr(qnode, "qtape")

    qml.disable_tape()

    qnode = qml.QNode(circuit, dev)
    qnode(0.5, 0.1)

    assert not isinstance(qnode, qml.tape.QNode)
    assert isinstance(qnode, qml.qnodes.BaseQNode)
    assert not hasattr(qnode, "qtape")
예제 #6
0
def tape_mode(request):
    """Tests using this fixture will be run twice, once in tape mode and once without."""
    if request.param:
        qml.enable_tape()
    yield
    if request.param:
        qml.disable_tape()
예제 #7
0
def test_tape_mode_detection():
    """Test that the function `tape_mode_active` returns True
    only if tape mode is activated."""
    qml.disable_tape()
    assert not qml.tape_mode_active()
    qml.enable_tape()
    assert qml.tape_mode_active()
예제 #8
0
    def test_identity_single_batched(self, dev):
        """Test computing the expectation value of the identity for a single return value."""
        qml.enable_tape()
        dev = qml.device(dev, wires=1)

        with qml.tape.QuantumTape() as tape1:
            qml.expval(qml.Identity(wires=[0]))

        res = dev.batch_execute([tape1])
        assert len(res) == 1
        assert np.allclose(res[0], np.array([1]))
        qml.disable_tape()
예제 #9
0
    def test_batch_exec(self, keep, tmpdir, monkeypatch, test_batch_result):
        """Test that the batch_execute method returns the desired result and
        that the result preserves the order in which circuits were
        submitted."""
        qml.enable_tape()

        dev = qml.device("orquestra.forest", wires=3, keep_files=keep)

        with qml.tape.QuantumTape() as tape1:
            qml.expval(qml.PauliZ(wires=[0]))

        with qml.tape.QuantumTape() as tape2:
            qml.RX(0.432, wires=0)
            qml.RY(0.543, wires=0)
            qml.expval(qml.PauliZ(wires=[0]))

        with qml.tape.QuantumTape() as tape3:
            qml.RX(0.432, wires=0)
            qml.expval(qml.PauliZ(wires=[0]))

        circuits = [tape1, tape2, tape3]

        test_uuid = "1234"
        assert not os.path.exists(tmpdir.join(f"expval-{test_uuid}-0.yaml"))

        with monkeypatch.context() as m:
            m.setattr(pennylane_orquestra.cli_actions, "user_data_dir",
                      lambda *args: tmpdir)

            # Disable submitting to the Orquestra platform by mocking Popen
            m.setattr(subprocess, "Popen", lambda *args, **kwargs: MockPopen())
            m.setattr(
                pennylane_orquestra.orquestra_device,
                "loop_until_finished",
                lambda *args, **kwargs: test_batch_result,
            )

            # Disable random uuid generation
            m.setattr(uuid, "uuid4", lambda *args: test_uuid)

            res = dev.batch_execute(circuits)

            # Correct order of results is expected
            assert np.allclose(res[0], test_batch_res0)
            assert np.allclose(res[1], test_batch_res1)
            assert np.allclose(res[2], test_batch_res2)
            file_kept = os.path.exists(
                tmpdir.join(f"expval-{test_uuid}-0.yaml"))

            assert file_kept if keep else not file_kept

        qml.disable_tape()
예제 #10
0
    def test_identity_multiple_tape(self, dev, tmpdir, monkeypatch):
        """Test computing the expectation value of the identity for multiple
        return values."""
        qml.enable_tape()

        dev = qml.device(dev, wires=2, keep_files=False)

        with qml.tape.QuantumTape() as tape1:
            qml.RX(0.133, wires=0)
            qml.expval(qml.Identity(wires=[0]))

        with qml.tape.QuantumTape() as tape2:
            qml.RX(0.432, wires=0)
            qml.expval(qml.Identity(wires=[0]))
            qml.expval(qml.Identity(wires=[1]))

        circuits = [tape1, tape2]

        test_uuid = "1234"
        with monkeypatch.context() as m:
            m.setattr(pennylane_orquestra.cli_actions, "user_data_dir",
                      lambda *args: tmpdir)

            # Disable submitting to the Orquestra platform by mocking Popen
            m.setattr(subprocess, "Popen", lambda *args, **kwargs: MockPopen())
            m.setattr(
                pennylane_orquestra.orquestra_device,
                "loop_until_finished",
                lambda *args, **kwargs: None,
            )

            # Disable random uuid generation
            m.setattr(uuid, "uuid4", lambda *args: test_uuid)

            res = dev.batch_execute(circuits)

            # No workflow files were created because we only computed with
            # identities
            assert not os.path.exists(tmpdir.join(f"expval-{test_uuid}.yaml"))
            assert not os.path.exists(tmpdir.join(f"expval-{test_uuid}.yaml"))

            expected = [
                np.ones(1),
                np.ones(2),
            ]

            for r, e in zip(res, expected):
                assert np.allclose(r, e)

        qml.disable_tape()
예제 #11
0
def tape_mode(request, mocker):
    """Tests using this fixture will be run twice, once in tape mode and once without."""

    if request.param:
        # Several attributes and methods on the old QNode have a new location on the new QNode/tape.
        # Here, we dynamically mock so that the tests do not have to be modified to support both
        # tape and non-tape mode. Once tape mode is default, we can make the equivalent
        # changes directly in the tests.
        mocker.patch(
            "pennylane.tape.QNode.ops",
            property(
                lambda self: self.qtape.operations + self.qtape.observables),
            create=True,
        )
        mocker.patch("pennylane.tape.QNode.h",
                     property(lambda self: self.diff_options["h"]),
                     create=True)
        mocker.patch(
            "pennylane.tape.QNode.order",
            property(lambda self: self.diff_options["order"]),
            create=True,
        )
        mocker.patch("pennylane.tape.QNode.circuit",
                     property(lambda self: self.qtape.graph),
                     create=True)

        def patched_jacobian(self, args, **kwargs):  # pylint: disable=unused-argument
            method = kwargs.get("method", "best")

            if method == "A":
                method = "analytic"
            elif method == "F":
                method = "numeric"

            kwargs["method"] = method
            dev = kwargs["options"]["device"]

            return self.qtape.jacobian(dev, **kwargs)

        mocker.patch("pennylane.tape.QNode.jacobian",
                     patched_jacobian,
                     create=True)

        qml.enable_tape()

    yield

    if request.param:
        qml.disable_tape()
예제 #12
0
    def test_draw_transform_raises(self):
        qml.disable_tape()
        dev = qml.device("default.qubit", wires=2)
        @qml.qnode(dev, interface="autograd")
        def circuit(p1, p2, **kwargs):
            qml.RX(p1, wires=0)
            qml.RY(p2[0] * p2[1], wires=1)
            qml.RX(kwargs["p3"], wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0) @ qml.PauliX(1))

        with pytest.raises(ValueError, match="only works when tape mode is enabled"):
            result = draw(circuit, charset="ascii")

        qml.enable_tape()
예제 #13
0
    def test_jacobian_with_batch_execute(self):
        """Test that the value of the jacobian computed using the internal
        batch_execute method corresponds to the value computed with
        the default.qubit device.

        There are ``qubits * layers * 3 * 2`` many circuits to evaluate.
        """
        try_resp = qe_list_workflow()
        need_login_msg = "token has expired, please log in again\n"

        if need_login_msg in try_resp:
            pytest.skip("Has not logged in to the Orquestra platform.")

        qml.enable_tape()

        # Evaluate 12 circuits (2 * 1 * 3 * 2)
        # By default, this fits into two separate workflow files
        qubits = 2
        layers = 1
        weights = qml.init.strong_ent_layers_uniform(layers, qubits)

        dev1 = qml.device(
            "orquestra.qiskit",
            backend="statevector_simulator",
            wires=qubits,
            analytic=True,
            keep_files=False,
        )
        dev2 = qml.device("default.qubit", wires=qubits, analytic=True)

        def func(weights):
            qml.templates.StronglyEntanglingLayers(weights,
                                                   wires=range(qubits))
            return qml.expval(qml.PauliZ(0))

        orquestra_qnode = qml.QNode(func, dev1)
        default_qnode = qml.QNode(func, dev2)

        dfunc1 = qml.grad(orquestra_qnode)
        dfunc2 = qml.grad(default_qnode)

        res_orquestra = dfunc1(weights)
        res_default_qubit = dfunc2(weights)

        assert np.allclose(res_orquestra, res_default_qubit)
        qml.disable_tape()
예제 #14
0
    def test_error_if_not_expval_batched(self):
        """Test that an error is raised if not an expectation value is
        computed during batched execution"""
        qml.enable_tape()
        dev = qml.device("orquestra.qiskit", wires=2)

        with qml.tape.QuantumTape() as tape1:
            qml.expval(qml.PauliZ(wires=[0]))
            qml.var(qml.PauliZ(wires=[0]))

        with qml.tape.QuantumTape() as tape2:
            qml.expval(qml.PauliZ(wires=[0]))

        circuits = [tape1, tape2]
        with pytest.raises(NotImplementedError):
            res = dev.batch_execute(circuits)

        qml.disable_tape()
예제 #15
0
    def test_serialize_circuit_no_rotations_tape(self, monkeypatch, tmpdir,
                                                 test_batch_result):
        """Test that a circuit that is serialized correctly without rotations for
        a simulator backend in tape mode"""
        qml.enable_tape()
        dev = QeQiskitDevice(wires=1,
                             shots=1000,
                             backend="statevector_simulator",
                             analytic=True)

        circuit_history = []

        with qml.tape.QuantumTape() as tape1:
            qml.Hadamard(wires=[0])
            qml.expval(qml.Hadamard(0))

        with monkeypatch.context() as m:
            m.setattr(pennylane_orquestra.cli_actions, "user_data_dir",
                      lambda *args: tmpdir)
            m.setattr(
                pennylane_orquestra.orquestra_device,
                "gen_expval_workflow",
                lambda component, backend_specs, circuits, operators, **kwargs:
                circuit_history.extend(circuits),
            )

            # Disable submitting to the Orquestra platform by mocking Popen
            m.setattr(subprocess, "Popen", lambda *args, **kwargs: MockPopen())
            m.setattr(
                pennylane_orquestra.orquestra_device,
                "loop_until_finished",
                lambda *args, **kwargs:
                test_batch_result,  # The exact results are not considered in the test
            )

            dev.execute(tape1)

        expected = 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[1];\ncreg c[1];\nh q[0];\n'
        assert circuit_history[0] == expected
        qml.disable_tape()
예제 #16
0
    def test_correct_number_of_executions_autograd(self):
        """Test that number of executions are tracked in the autograd interface."""
        qml.enable_tape()

        def func():
            qml.Hadamard(wires=0)
            qml.CNOT(wires=[0, 1])
            return qml.expval(qml.PauliZ(0))

        dev = qml.device("default.qubit", wires=2)
        qn = QNode(func, dev, interface="autograd")

        for i in range(2):
            qn()

        assert dev.num_executions == 2

        qn2 = QNode(func, dev, interface="autograd")
        for i in range(3):
            qn2()

        assert dev.num_executions == 5
예제 #17
0
    def test_non_tape_mode(self):
        """Tests that an exception is raised when attempting to use caching outside of tape mode"""
        dev = qml.device("default.qubit", wires=3, cache=10)

        try:
            qml.disable_tape()

            def qfunc(x, y):
                """Simple quantum function"""
                qml.RX(x, wires=0)
                qml.RX(y, wires=1)
                qml.CNOT(wires=[0, 1])
                return qml.expval(qml.PauliZ(wires=1))

            qn = qml.QNode(qfunc, dev)

            with pytest.raises(
                    ValueError,
                    match="Caching is only available when using tape mode"):
                qn(0.1, 0.2)

        finally:
            qml.enable_tape()
예제 #18
0
    def test_metric_tensor_tape_mode(self):
        """Test that the metric tensor can be calculated in tape mode, and that it is equal to a
        metric tensor calculated in non-tape mode."""
        if not qml.tape_mode_active():
            pytest.skip("This test is only intended for tape mode")

        dev = qml.device("default.qubit", wires=2)
        p = np.array([1., 1., 1.])

        def ansatz(params, **kwargs):
            qml.RX(params[0], wires=0)
            qml.RY(params[1], wires=0)
            qml.CNOT(wires=[0, 1])
            qml.PhaseShift(params[2], wires=1)

        h = qml.Hamiltonian([1, 1], [qml.PauliZ(0), qml.PauliZ(1)])
        qnodes = qml.ExpvalCost(ansatz, h, dev)
        mt = qml.metric_tensor(qnodes)(p)
        assert qml.tape_mode_active()  # Check that tape mode is still active

        try:
            qml.disable_tape()

            @qml.qnode(dev)
            def circuit(params):
                qml.RX(params[0], wires=0)
                qml.RY(params[1], wires=0)
                qml.CNOT(wires=[0, 1])
                qml.PhaseShift(params[2], wires=1)
                return qml.expval(qml.PauliZ(0))

            mt2 = circuit.metric_tensor([p])
        finally:
            qml.enable_tape()

        assert np.allclose(mt, mt2)
예제 #19
0
    def test_batch_exec_multiple_workflow(self, keep, dev_name, tmpdir,
                                          monkeypatch, test_batch_result):
        """Test that the batch_execute method returns the desired result and
        that the result preserves the order in which circuits were submitted
        when batches are created in multiple workflows ."""

        qml.enable_tape()

        with qml.tape.QuantumTape() as tape1:
            qml.RX(0.133, wires=0)
            qml.CNOT(wires=[0, 1])
            qml.expval(qml.PauliZ(wires=[0]))

        with qml.tape.QuantumTape() as tape2:
            qml.RX(0.432, wires=0)
            qml.RY(0.543, wires=0)
            qml.expval(qml.PauliZ(wires=[0]))

        with qml.tape.QuantumTape() as tape3:
            qml.RX(0.432, wires=0)
            qml.expval(qml.PauliZ(wires=[0]))

        circuits = [tape1, tape2, tape3]

        # Setting batch size: allow only a single circuit for each workflow
        dev = qml.device(dev_name, wires=3, batch_size=1, keep_files=keep)

        # Check that no workflow files were created before
        test_uuid = "1234"
        assert not os.path.exists(tmpdir.join(f"expval-{test_uuid}-0.yaml"))
        assert not os.path.exists(tmpdir.join(f"expval-{test_uuid}-1.yaml"))
        assert not os.path.exists(tmpdir.join(f"expval-{test_uuid}-2.yaml"))

        with monkeypatch.context() as m:
            m.setattr(pennylane_orquestra.cli_actions, "user_data_dir",
                      lambda *args: tmpdir)

            # Disable submitting to the Orquestra platform by mocking Popen
            m.setattr(subprocess, "Popen", lambda *args, **kwargs: MockPopen())
            m.setattr(
                pennylane_orquestra.orquestra_device,
                "loop_until_finished",
                lambda *args, **kwargs: test_batch_result,
            )

            # Disable random uuid generation
            m.setattr(uuid, "uuid4", lambda *args: test_uuid)

            res = dev.batch_execute(circuits)

            # Correct order of results is expected
            assert np.allclose(res[0], test_batch_res0)
            assert np.allclose(res[1], test_batch_res1)
            assert np.allclose(res[2], test_batch_res2)
            file0_kept = os.path.exists(
                tmpdir.join(f"expval-{test_uuid}-0.yaml"))
            file1_kept = os.path.exists(
                tmpdir.join(f"expval-{test_uuid}-1.yaml"))
            file2_kept = os.path.exists(
                tmpdir.join(f"expval-{test_uuid}-2.yaml"))

        # Check that workflow files were either all kept or all deleted
        files_kept = file0_kept and file1_kept and file2_kept
        assert files_kept and file0_kept if keep else not files_kept

        qml.disable_tape()
예제 #20
0
def natural_gradient(params):
    """Calculate the natural gradient of the qnode() cost function.
    The code you write for this challenge should be completely contained within this function
    between the # QHACK # comment markers.
    You should evaluate the metric tensor and the gradient of the QNode, and then combine these
    together using the natural gradient definition. The natural gradient should be returned as a
    NumPy array.
    The metric tensor should be evaluated using the equation provided in the problem text. Hint:
    you will need to define a new QNode that returns the quantum state before measurement.
    Args:
        params (np.ndarray): Input parameters, of dimension 6
    Returns:
        np.ndarray: The natural gradient evaluated at the input parameters, of dimension 6
    """

    natural_grad = np.zeros(6)

    # QHACK #

    dcircuit = qml.grad(qnode, argnum=0)
    normal_grad = dcircuit(params)

    def normalise(a1):
        return (np.vdot(a1, a1))

    qml.enable_tape()

    @qml.qnode(dev)
    def circuit(params):
        variational_circuit(params)
        return qml.state()

    F_matrix = np.zeros([6, 6], dtype=np.float64)
    initial_prod = circuit(params)
    s = np.pi / 2
    for i in range(6):
        for j in range(6):
            params[i] += s
            params[j] += s
            a11 = circuit(params)
            params[j] -= 2 * s
            a22 = circuit(params)
            params[i] -= 2 * s
            a44 = circuit(params)
            params[j] += 2 * s
            a33 = circuit(params)
            params[i] += s
            params[j] -= s
            aa1 = np.vdot(a11, initial_prod)
            a1 = normalise(aa1)
            aa2 = np.vdot(a22, initial_prod)
            a2 = normalise(aa2)
            aa3 = np.vdot(a33, initial_prod)
            a3 = normalise(aa3)
            aa4 = np.vdot(a44, initial_prod)
            a4 = normalise(aa4)
            #print(str((a2 + a3 - a1 - a4)/8.0))
            #print(a1)
            F_matrix[i][j] = (np.absolute(a2) + np.absolute(a3) -
                              np.absolute(a1) - np.absolute(a4)) / 8.0

    #print(circuit(params))
    final_F = np.linalg.inv(F_matrix)
    natural_grad = final_F.dot(normal_grad)
    # QHACK #

    return natural_grad
예제 #21
0
the address of the `S3 bucket <https://aws.amazon.com/s3/>`__ where results are to be stored:
"""

my_bucket = "amazon-braket-Your-Bucket-Name"  # the name of the bucket
my_prefix = "Your-Folder-Name"  # the name of the folder in the bucket
s3_folder = (my_bucket, my_prefix)

device_arn = "arn:aws:braket:::device/quantum-simulator/amazon/sv1"

##############################################################################
# SV1 can now be loaded with the standard PennyLane :func:`~.pennylane.device`:

import pennylane as qml
from pennylane import numpy as np

qml.enable_tape()  # Unlocks the latest features in PennyLane
n_wires = 25

dev_remote = qml.device(
    "braket.aws.qubit",
    device_arn=device_arn,
    wires=n_wires,
    s3_destination_folder=s3_folder,
    parallel=True,
)

##############################################################################
# Note the ``parallel=True`` argument. This setting allows us to unlock the power of parallel
# execution on SV1 for gradient calculations. We'll also load ``default.qubit`` for comparison.

dev_local = qml.device("default.qubit", wires=n_wires)
예제 #22
0
 def enable_tape_mode(self):
     qml.enable_tape()
     yield
     qml.disable_tape()
예제 #23
0
def non_tape_mode_only():
    """Run the test in tape mode"""
    qml.disable_tape()
    yield
    qml.enable_tape()
예제 #24
0
파일: base.py 프로젝트: markhop20/pennylane
    def _construct(self, args, kwargs):
        """Construct the quantum circuit graph by calling the quantum function.

        For immutable nodes this method is called the first time :meth:`BaseQNode.evaluate`
        or :meth:`.JacobianQNode.jacobian` is called, and for mutable nodes *each time*
        they are called. It executes the quantum function,
        stores the resulting sequence of :class:`.Operator` instances,
        converts it into a circuit graph, and creates the Variable mapping.

        .. note::
           The Variables are only required for analytic differentiation,
           for evaluation we could simply reconstruct the circuit each time.

        Args:
            args (tuple[Any]): Positional arguments passed to the quantum function.
                During the construction we are not concerned with the numerical values, but with
                the nesting structure.
                Each positional argument is replaced with a :class:`~.Variable` instance.
            kwargs (dict[str, Any]): Auxiliary arguments passed to the quantum function.
        """
        # TODO: Update the docstring to reflect the kwargs and the raising conditions
        # pylint: disable=attribute-defined-outside-init, too-many-branches, too-many-statements

        self.arg_vars, self.kwarg_vars = self._make_variables(args, kwargs)

        # temporary queues for operations and observables
        # TODO rename self.queue to self.op_queue
        self.queue = []  #: list[Operation]: applied operations
        self.obs_queue = []  #: list[Observable]: applied observables

        tape_mode = qml.tape_mode_active()
        if tape_mode:
            qml.disable_tape()

        try:
            # set up the context for Operator entry
            with self:
                try:
                    # generate the program queue by executing the quantum circuit function
                    if self.mutable:
                        # it's ok to directly pass auxiliary arguments since the circuit is
                        # re-constructed each time (positional args must be replaced because
                        # parameter-shift differentiation requires Variables)
                        res = self.func(*self.arg_vars, **kwargs)
                    else:
                        # TODO: Maybe we should only convert the kwarg_vars that were actually given
                        res = self.func(*self.arg_vars, **self.kwarg_vars)
                except:
                    # The qfunc call may have failed because the user supplied bad parameters,
                    # which is why we must wipe the created Variables.
                    self.arg_vars = None
                    self.kwarg_vars = None
                    raise
        finally:
            if tape_mode:
                qml.enable_tape()

        # check the validity of the circuit
        self._check_circuit(res)
        del self.queue
        del self.obs_queue

        # Prune all the Tensor objects that have been used in the circuit
        self.ops = self._prune_tensors(self.ops)

        # map each free variable to the operators which depend on it
        self.variable_deps = {k: [] for k in range(self.num_variables)}
        for op in self.ops:
            for j, p in enumerate(_flatten(op.data)):
                if isinstance(p, Variable):
                    if not p.is_kwarg:  # ignore auxiliary arguments
                        self.variable_deps[p.idx].append(ParameterDependency(op, j))

        # generate the DAG
        self.circuit = CircuitGraph(self.ops, self.variable_deps, self.device.wires)

        # check for unused positional params
        if self.kwargs.get("par_check", False):
            unused = [k for k, v in self.variable_deps.items() if not v]
            if unused:
                raise QuantumFunctionError(
                    "The positional parameters {} are unused.".format(unused)
                )

        # check for operations that cannot affect the output
        if self.kwargs.get("vis_check", False):
            invisible = self.circuit.invisible_operations()
            if invisible:
                raise QuantumFunctionError(
                    "The operations {} cannot affect the circuit output.".format(invisible)
                )
예제 #25
0
import pennylane as qml
from pennylane import numpy as np
from config import config

qml.enable_tape()
num_words = config['NUM_WORDS']
qbits_per_word = config['QUBITS_PER_WORDS']

#wires = num_words*qbits_per_word


def Word_Shuffle_circuit(params, wires):
    """Apply a sequence of controlled-rotation
    params : 3 angles [0:2pi], 1 per qubit
    wires : the 3 wires indexing the current word"""
    for i in range(len(wires)):
        if i == len(wires) - 1:  #the last wire controls the first qubit
            qml.CRY(params[i], wires=[wires[i], wires[0]])
        else:
            qml.CRY(params[i], wires=[wires[i], wires[i + 1]])


def Words_Loader_circuit(params, wires):
    """Load each vector with Word_Entangler_circuit
    all with the same set of parameters 'params'
    word_1 = wires[0:3], word_2 = wires[3:6], etc."""
    for i in range(len(wires) //
                   qbits_per_word):  #loop on each of the 3-qubits words
        i_ = i * qbits_per_word
        Word_Shuffle_circuit(params, wires[i_:i_ + qbits_per_word])
예제 #26
0
def in_tape_mode():
    """Run the test in tape mode"""
    qml.enable_tape()
    yield
    qml.disable_tape()
예제 #27
0
    def __init__(
        self,
        ansatz,
        hamiltonian,
        device,
        interface="autograd",
        diff_method="best",
        optimize=False,
        **kwargs,
    ):
        coeffs, observables = hamiltonian.terms

        self.hamiltonian = hamiltonian
        """Hamiltonian: the input Hamiltonian."""

        self.qnodes = None
        """QNodeCollection: The QNodes to be evaluated. Each QNode corresponds to the expectation
        value of each observable term after applying the circuit ansatz."""

        self._multiple_devices = isinstance(device, Sequence)
        """Bool: Records if multiple devices are input"""

        tape_mode = qml.tape_mode_active()
        if tape_mode:

            d = device[0] if self._multiple_devices else device
            w = d.wires.tolist()

            try:
                qml.disable_tape()

                @qml.qnode(d,
                           interface=interface,
                           diff_method=diff_method,
                           **kwargs)
                def qnode_for_metric_tensor_in_tape_mode(
                        *qnode_args, **qnode_kwargs):
                    """The metric tensor cannot currently be calculated in tape-mode QNodes. As a
                    short-term fix for ExpvalCost, we create a non-tape mode QNode just for
                    calculation of the metric tensor. In doing so, we reintroduce the same
                    restrictions of the old QNode but allow users to access new functionality
                    such as measurement grouping and batch execution of the gradient."""
                    ansatz(*qnode_args, wires=w, **qnode_kwargs)
                    return qml.expval(qml.PauliZ(0))

                self._qnode_for_metric_tensor_in_tape_mode = qnode_for_metric_tensor_in_tape_mode
            finally:
                qml.enable_tape()

        self._optimize = optimize

        if self._optimize:
            if not tape_mode:
                raise ValueError(
                    "Observable optimization is only supported in tape mode. Tape "
                    "mode can be enabled with the command:\n"
                    "qml.enable_tape()")

            if self._multiple_devices:
                raise ValueError(
                    "Using multiple devices is not supported when optimize=True"
                )

            obs_groupings, coeffs_groupings = qml.grouping.group_observables(
                observables, coeffs)

            @qml.qnode(device,
                       interface=interface,
                       diff_method=diff_method,
                       **kwargs)
            def circuit(*qnode_args, obs, **qnode_kwargs):
                """Converting ansatz into a full circuit including measurements"""
                ansatz(*qnode_args, wires=w, **qnode_kwargs)
                return [qml.expval(o) for o in obs]

            def cost_fn(*qnode_args, **qnode_kwargs):
                """Combine results from grouped QNode executions with grouped coefficients"""
                total = 0
                for o, c in zip(obs_groupings, coeffs_groupings):
                    res = circuit(*qnode_args, obs=o, **qnode_kwargs)
                    total += sum([r * c_ for r, c_ in zip(res, c)])
                return total

            self.cost_fn = cost_fn

        else:
            self.qnodes = qml.map(ansatz,
                                  observables,
                                  device,
                                  interface=interface,
                                  diff_method=diff_method,
                                  **kwargs)

            self.cost_fn = qml.dot(coeffs, self.qnodes)
예제 #28
0
def find_excited_states(H):
    """
    Fill in the missing parts between the # QHACK # markers below. Implement
    a variational method that can find the three lowest energies of the provided
    Hamiltonian.

    Args:
        H (qml.Hamiltonian): The input Hamiltonian

    Returns:
        The lowest three eigenenergies of the Hamiltonian as a comma-separated string,
        sorted from smallest to largest.
    """

    energies = np.zeros(3)

    # QHACK #
    num_qubits = len(H.wires)
    #print(H.wires)
    num_param_sets = (2**num_qubits) - 1
    saved_params = []

    dev = qml.device("default.qubit", wires=num_qubits)

    # circuit from vqe-100
    def variational_ansatz(params, wires):
        n_qubits = len(wires)
        n_rotations = len(params)

        if n_rotations > 1:
            n_layers = n_rotations // n_qubits
            n_extra_rots = n_rotations - n_layers * n_qubits

            # Alternating layers of unitary rotations on every qubit followed by a
            # ring cascade of CNOTs.
            for layer_idx in range(n_layers):
                layer_params = params[layer_idx *
                                      n_qubits:layer_idx * n_qubits + n_qubits]
                if layer_idx == 0:
                    qml.broadcast(qml.RY,
                                  wires,
                                  pattern="single",
                                  parameters=layer_params)
                else:
                    qml.broadcast(qml.CNOT, wires, pattern="ring")
                    qml.broadcast(qml.RY,
                                  wires,
                                  pattern="single",
                                  parameters=layer_params)

            # There may be "extra" parameter sets required for which it's not necessarily
            # to perform another full alternating cycle. Apply these to the qubits as needed.
            extra_params = params[-n_extra_rots:]
            extra_wires = wires[:n_qubits - 1 - n_extra_rots:-1]
            extra_wires2 = wires[:n_qubits - 2 - n_extra_rots:-1]
            #print("ew",extra_wires)
            #print("test",extra_wires2)
            if n_qubits > 2:
                qml.broadcast(qml.CNOT, extra_wires2, pattern="ring")
            else:
                qml.broadcast(qml.CNOT, [1, 0], pattern='chain')
            qml.broadcast(qml.RY,
                          extra_wires,
                          pattern="single",
                          parameters=extra_params)
        else:
            # For 1-qubit case, just a single rotation to the qubit
            qml.RY(params[0], wires=wires[0])

    params = np.random.uniform(low=-np.pi / 2,
                               high=np.pi / 2,
                               size=(num_param_sets))

    # test
    #@qml.qnode(dev)
    #def circuit(params):
    #    variational_ansatz(params,dev.wires)
    #    return qml.expval(qml.PauliZ(0))
    #result = circuit(params)
    #print(circuit.draw())

    #coeffs = H.coeffs
    #ops = H.ops

    #Hmat = np.zeros((2**num_qubits,2**num_qubits))
    #print(coeffs,ops)
    #print(ops[0].matrix)
    #for i in range(len(coeffs)):
    #    Hmat += coeffs[i] * ops[i].matrix
    #print(Hmat)
    #print(np.linalg.eigs(Hmat))

    #print("H",H)
    # find ground state
    cost0 = qml.ExpvalCost(variational_ansatz, H, dev)

    #opt = qml.GradientDescentOptimizer(0.1)
    opt = qml.AdamOptimizer(0.1)
    #opt = qml.AdagradOptimizer(0.1)

    #print(H.wires)

    min_50 = np.inf
    for i in range(500):
        if i % 25 == 0:
            if abs(cost0(params) - min_50) < 1e-4: break
            min_50 = cost0(params)
            #print(f"step {i}, E_0 {cost0(params)}")
        params = opt.step(cost0, params)

    energies[0] = cost0(params)
    saved_params.append(params)
    #print(energies[0],cost0(params))

    # function for overlaps
    qml.enable_tape()

    @qml.qnode(dev)
    def get_state(params):
        variational_ansatz(params, dev.wires)
        return qml.state()

    overlap_state1 = get_state(params)
    overlap_herm1 = np.outer(overlap_state1.conj(), overlap_state1)
    #print("psi_0",overlap_state1)
    #print(overlap_herm1)

    # find the first excited
    params = np.random.uniform(low=-np.pi / 2,
                               high=np.pi / 2,
                               size=(num_param_sets))
    a = 100  # big number to enforce orthogonality
    overlap_Ham = qml.Hamiltonian(coeffs=[
        a,
    ],
                                  observables=[
                                      qml.Hermitian(overlap_herm1, dev.wires),
                                  ])
    #print("a|psi_0><psi_0",overlap_Ham,overlap_Ham.ops)
    H1 = H + overlap_Ham
    #print("H1",H1)
    cost = qml.ExpvalCost(
        variational_ansatz, H1,
        dev)  # + qml.ExpvalCost(variational_ansatz, overlap_Ham, dev)
    #print(cost(saved_params[0]),a+energies[0],a+cost0(saved_params[0]))

    min_50 = np.inf
    for i in range(1500):
        if i % 25 == 0:
            if abs(cost0(params) - min_50) < 1e-4: break
            #print(f"step {i}, E_1 {cost0(params)}, cost {cost(params)}")
            min_50 = cost0(params)
        params = opt.step(cost, params)

    energies[1] = cost0(params)
    saved_params.append(params)
    #print(energies[1],cost(params))

    overlap_state2 = get_state(params)
    overlap_herm2 = np.outer(overlap_state2.conj(), overlap_state2)
    #print("|psi_1>",overlap_state2)
    #print(overlap_herm2)

    # find the second excited
    params = np.random.uniform(low=-np.pi / 2,
                               high=np.pi / 2,
                               size=(num_param_sets))
    b = 100
    overlap_Ham = qml.Hamiltonian(coeffs=[a, b],
                                  observables=[
                                      qml.Hermitian(overlap_herm1, dev.wires),
                                      qml.Hermitian(overlap_herm2, dev.wires)
                                  ])
    #print("a|psi_0><psi_0|+b|psi_1><psi_1",overlap_Ham,overlap_Ham.ops)
    H2 = H + overlap_Ham
    #print("H2",H2)
    cost = qml.ExpvalCost(
        variational_ansatz, H2,
        dev)  # + qml.ExpvalCost(variational_ansatz, overlap_Ham, dev)

    min_50 = np.inf
    for i in range(1500):
        if i % 25 == 0:
            if abs(cost0(params) - min_50) < 1e-4: break
            #print(f"step {i}, E_2 {cost0(params)}, cost {cost(params)}")
            min_50 = cost0(params)
        params = opt.step(cost, params)

    energies[2] = cost0(params)
    saved_params.append(params)

    # QHACK #

    return ",".join([str(E) for E in energies])