def classify_data(X_train, Y_train, X_test):
    """Develop and train your very own variational quantum classifier.

    Use the provided training data to train your classifier. The code you write
    for this challenge should be completely contained within this function
    between the # QHACK # comment markers. The number of qubits, choice of
    variational ansatz, cost function, and optimization method are all to be
    developed by you in this function.

    Args:
        X_train (np.ndarray): An array of floats of size (250, 3) to be used as training data.
        Y_train (np.ndarray): An array of size (250,) which are the categorical labels
            associated to the training data. The categories are labeled by -1, 0, and 1.
        X_test (np.ndarray): An array of floats of (50, 3) to serve as testing data.

    Returns:
        str: The predicted categories of X_test, converted from a list of ints to a
            comma-separated string.
    """

    # Use this array to make a prediction for the labels of the data in X_test
    predictions = []

    # Fix random seed
    np.random.seed(0)

    # Hyperparameters
    n_wires = 3
    n_layers = 3
    batch_size = 5
    #opt = qml.NesterovMomentumOptimizer(0.01)
    opt = qml.AdamOptimizer()
    n_iter = 80

    # QHACK #

    # Initialize the device
    dev = qml.device("default.qubit", wires=n_wires)

    # ----------------------------------------------------------------------------
    # Source : https://pennylane.ai/qml/demos/tutorial_variational_classifier.html
    # ----------------------------------------------------------------------------
    def layer(W):
        qml.Rot(W[0, 0], W[0, 1], W[0, 2], wires=0)
        qml.Rot(W[1, 0], W[1, 1], W[1, 2], wires=1)
        qml.Rot(W[2, 0], W[2, 1], W[2, 2], wires=2)
        qml.CNOT(wires=[0, 1])
        qml.CNOT(wires=[1, 2])
        qml.CNOT(wires=[2, 0])

    @qml.qnode(dev)
    def circuit(weights, x=None):
        qml.QubitStateVector(x, wires=[0, 1])

        for W in weights:
            layer(W)

        return [qml.expval(qml.PauliZ(i)) for i in range(3)]

    def variational_classifier(var, angles):
        weights = var[0]
        bias = var[1]
        return circuit(weights, x=angles) + bias

    def square_loss(labels, predictions):
        loss = 0
        for l, p in zip(labels, predictions):
            loss = loss + (l - p)**2

        loss = loss / len(labels)
        return loss

    def one_hot(lbl):
        ret = None
        if lbl == -1:
            ret = [-1, 1, 1]
        elif lbl == 0:
            ret = [1, -1, 1]
        else:
            ret = [1, 1, -1]
        return ret

    def one_hot_loss(labels, predictions):
        loss = 0
        for l, p in zip(labels, predictions):
            l = one_hot(l)
            loss = loss + np.sum(np.subtract(l, p)**2)

        loss = loss / len(labels)
        return loss

    def cost(weights, features, labels):
        predictions = [variational_classifier(weights, f) for f in features]
        return one_hot_loss(labels, predictions)

    def discretize(out):
        return np.argmin(out) - 1

    # ----------------------------------------------------------------------------
    # ----------------------------------------------------------------------------

    # Parameter initialization
    var_init = (0.01 * np.random.randn(n_layers, n_wires, 3), 0.0)

    # Pad zero to the last dimension to use 2 wires
    # Source : https://pennylane.ai/qml/demos/tutorial_variational_classifier.html
    X_train_pad = np.c_[X_train, np.zeros((len(X_train), 1))]
    X_test_pad = np.c_[X_test, np.zeros((len(X_test), 1))]

    # Normalization
    # Source : https://pennylane.ai/qml/demos/tutorial_variational_classifier.html
    train_norm = np.sqrt(np.sum(X_train_pad**2, -1))
    test_norm = np.sqrt(np.sum(X_test_pad**2, -1))

    X_train_norm = (X_train_pad.T / train_norm).T
    X_test_norm = (X_test_pad.T / test_norm).T

    # Source : https://pennylane.ai/qml/demos/tutorial_variational_classifier.html
    var = var_init
    for it in range(n_iter):
        batch_index = np.random.randint(0, len(X_train), (batch_size, ))
        X_batch = X_train_norm[batch_index]
        Y_batch = Y_train[batch_index]

        var = opt.step(lambda v: cost(v, X_batch, Y_batch), var)
    # QHACK #
    predictions = [
        discretize(variational_classifier(var, x)) for x in X_test_norm
    ]

    #print("Accuracy : {0}".format(np.sum(np.equal(ANS, predictions))/ len(ANS)))

    return array_to_concatenated_string(predictions)
#            coeffs = (0.05 + 0.05j) * degree
#            coeff0 = 0.0
#

######################################################################
# Define the serial quantum model
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#

######################################################################
# We now define the quantum model itself.
#

scaling = 1

dev = qml.device('default.qubit', wires=1)


def S(x):
    """Data-encoding circuit block."""
    qml.RX(scaling * x, wires=0)


def W(theta):
    """Trainable circuit block."""
    qml.Rot(theta[0], theta[1], theta[2], wires=0)


@qml.qnode(dev)
def serial_quantum_model(weights, x=None):
Exemplo n.º 3
0
def qubit_device_1_wire():
    return qml.device('default.qubit', wires=1)
Exemplo n.º 4
0
 def test_cost_expvals(self, coeffs, observables, expected):
     """Tests that the cost function returns correct expectation values"""
     dev = qml.device("default.qubit", wires=2)
     hamiltonian = qml.vqe.Hamiltonian(coeffs, observables)
     cost = qml.VQECost(lambda params, **kwargs: None, hamiltonian, dev)
     assert cost([]) == sum(expected)
Exemplo n.º 5
0
    def test_two_qubits_cz_opposite_direction(self):
        """Test that two adjacent CZ with the control/target flipped do cancel due to symmetry."""
        def qfunc():
            qml.CZ(wires=[0, 1])
            qml.CZ(wires=[1, 0])

        transformed_qfunc = cancel_inverses(qfunc)

        ops = qml.transforms.make_tape(transformed_qfunc)().operations

        assert len(ops) == 0


# Example QNode and device for interface testing
dev = qml.device("default.qubit", wires=3)


def qfunc(theta):
    qml.Hadamard(wires=0)
    qml.PauliX(wires=1)
    qml.S(wires=1)
    qml.adjoint(qml.S)(wires=1)
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    qml.RZ(theta[0], wires=2)
    qml.PauliX(wires=1)
    qml.CZ(wires=[1, 0])
    qml.RY(theta[1], wires=2)
    qml.CZ(wires=[0, 1])
    return qml.expval(qml.PauliX(0) @ qml.PauliX(2))
Exemplo n.º 6
0
]

##############################################################################
# We can now create our quantum device (let's use the ``default.qubit`` simulator),
# and begin constructing some QNodes to evaluate each observable. For our ansatz, we'll use the
# :class:`~.pennylane.templates.layers.StronglyEntanglingLayers`.

from pennylane import expval
from pennylane.init import strong_ent_layers_uniform
from pennylane.templates.layers import StronglyEntanglingLayers

num_layers = 2
num_wires = 2

# create a device that estimates expectation values using a finite number of shots
non_analytic_dev = qml.device("default.qubit", wires=num_wires, analytic=False)

# create a device that calculates exact expectation values
analytic_dev = qml.device("default.qubit", wires=num_wires, analytic=True)

##############################################################################
# We use :func:`~.pennylane.map` to map our ansatz over our list of observables,
# returning a collection of QNodes, each one evaluating the expectation value
# of each Hamiltonian.

qnodes = qml.map(StronglyEntanglingLayers, obs, device=non_analytic_dev)

##############################################################################
# Now, let's set the total number of shots, and determine the probability
# for sampling each Hamiltonian term.
Exemplo n.º 7
0
 def test_circuits_expvals(self, coeffs, observables, expected):
     """Tests that the vqe.circuits function returns correct expectation values"""
     dev = qml.device("default.qubit", wires=2)
     circuits = qml.map(lambda params, **kwargs: None, observables, dev)
     res = [a * c([]) for a, c in zip(coeffs, circuits)]
     assert np.all(res == expected)
Exemplo n.º 8
0
    def test_differentiable_expand(self, dev_name, diff_method, mocker, tol):
        """Test that operation and nested tapes expansion
        is differentiable"""
        mock = mocker.patch.object(qml.operation.Operation, "do_check_domain",
                                   False)

        class U3(qml.U3):
            def expand(self):
                theta, phi, lam = self.data
                wires = self.wires

                with JacobianTape() as tape:
                    qml.Rot(lam, theta, -lam, wires=wires)
                    qml.PhaseShift(phi + lam, wires=wires)

                return tape

        dev = qml.device(dev_name, wires=1)
        a = np.array(0.1, requires_grad=False)
        p = np.array([0.1, 0.2, 0.3], requires_grad=True)

        @qnode(dev, diff_method=diff_method, interface="autograd")
        def circuit(a, p):
            qml.RX(a, wires=0)
            U3(p[0], p[1], p[2], wires=0)
            return qml.expval(qml.PauliX(0))

        res = circuit(a, p)

        if diff_method == "finite-diff":
            assert circuit.qtape.trainable_params == {1, 2, 3, 4}
        elif diff_method == "backprop":
            # For a backprop device, no interface wrapping is performed, and JacobianTape.jacobian()
            # is never called. As a result, JacobianTape.trainable_params is never set --- the ML
            # framework uses its own backprop logic and its own bookkeeping re: trainable parameters.
            assert circuit.qtape.trainable_params == {0, 1, 2, 3, 4}

        assert [i.name for i in circuit.qtape.operations
                ] == ["RX", "Rot", "PhaseShift"]

        if diff_method == "finite-diff":
            assert np.all(circuit.qtape.get_parameters() ==
                          [p[2], p[0], -p[2], p[1] + p[2]])
        elif diff_method == "backprop":
            # In backprop mode, all parameters are returned.
            assert np.all(circuit.qtape.get_parameters() ==
                          [a, p[2], p[0], -p[2], p[1] + p[2]])

        expected = np.cos(a) * np.cos(p[1]) * np.sin(
            p[0]) + np.sin(a) * (np.cos(p[2]) * np.sin(p[1]) +
                                 np.cos(p[0]) * np.cos(p[1]) * np.sin(p[2]))
        assert np.allclose(res, expected, atol=tol, rtol=0)

        res = qml.grad(circuit)(a, p)
        expected = np.array([
            np.cos(p[1]) * (np.cos(a) * np.cos(p[0]) -
                            np.sin(a) * np.sin(p[0]) * np.sin(p[2])),
            np.cos(p[1]) * np.cos(p[2]) * np.sin(a) - np.sin(p[1]) *
            (np.cos(a) * np.sin(p[0]) +
             np.cos(p[0]) * np.sin(a) * np.sin(p[2])),
            np.sin(a) * (np.cos(p[0]) * np.cos(p[1]) * np.cos(p[2]) -
                         np.sin(p[1]) * np.sin(p[2])),
        ])
        assert np.allclose(res, expected, atol=tol, rtol=0)
Exemplo n.º 9
0
def test_transform(dev_name, diff_method, monkeypatch, tol):
    """Test an example transform"""
    monkeypatch.setattr(qml.operation.Operation, "do_check_domain", False)

    dev = qml.device(dev_name, wires=1)

    @qnode(dev, interface="autograd", diff_method=diff_method)
    def circuit(weights):
        # the following global variables are defined simply for testing
        # purposes, so that we can easily extract the operations for verification.
        global op1, op2
        op1 = qml.RY(weights[0], wires=0)
        op2 = qml.RX(weights[1], wires=0)
        return qml.expval(qml.PauliZ(wires=0))

    weights = np.array([0.32, 0.543], requires_grad=True)
    a = np.array(0.5, requires_grad=True)

    def loss(weights, a):
        # the following global variable is defined simply for testing
        # purposes, so that we can easily extract the transformed QNode
        # for verification.
        global new_circuit

        # transform the circuit QNode with trainable weight 'a'
        new_circuit = qtransform(circuit, a)

        # evaluate the transformed QNode
        res = new_circuit(weights)

        # evaluate the original QNode with pre-processed parameters
        res2 = circuit(np.sin(weights))

        # return the sum of the two QNode evaluations
        return res + res2

    res = loss(weights, a)

    # verify that the transformed QNode has the expected operations
    assert circuit.qtape.operations == [op1, op2]
    # RY(y) gate is transformed to RX(-a*cos(y))
    assert new_circuit.qtape.operations[0] == t_op[0]
    # RX gate is is not transformed
    assert new_circuit.qtape.operations[1].name == op2.name
    assert new_circuit.qtape.operations[1].wires == op2.wires

    # check that the incident gate arguments of both QNode tapes are correct
    assert np.all(np.array(circuit.qtape.get_parameters()) == np.sin(weights))
    assert np.all(
        np.array(new_circuit.qtape.get_parameters()) ==
        [-a * np.cos(weights[0]), weights[1]])

    # verify that the gradient has the correct shape
    grad = qml.grad(loss)(weights, a)
    assert len(grad) == 2
    assert grad[0].shape == weights.shape
    assert grad[1].shape == a.shape

    # compare against the expected values
    assert np.allclose(res, 1.8244501889992706, atol=tol, rtol=0)
    assert np.allclose(grad[0], [-0.26610258, -0.47053553], atol=tol, rtol=0)
    assert np.allclose(grad[1], 0.06486032, atol=tol, rtol=0)
Exemplo n.º 10
0
def run_tree_architecture_search(config: dict, dev_type: str):
    """
    The main workhorse for running the algorithm

    dev_type: device type specified as either "remote" or "local"
    "remote" is to use aws SV1
    "local" is to use pennylane simulator

    :param config: Dictionary with configuration parameters for the algorithm. Possible keys are:
        - nqubits: Integer. The number of qubits in the circuit
        - min_tree_depth: Integer. Minimum circuit depth before we start pruning
        - max_tree_depth: Integer. Maximum circuit depth
        - prune_rate: Integer. Percentage of nodes that we throw away when we prune
        - prune_step: Integer. How often do we prune
        - plot_trees: Boolean. Do we want to plot the tree at every depth?
        - data_set: String. Which dataset are we learning? Can be 'moons' or 'circles'
        - nsteps: Integer. The number of steps for training.
        - opt: qml.Optimizer. Pennylane optimizer
        - batch_size: Integer. Batch size for training.
        - n_samples: Integer. Number of samples that we want to take from the data set.
        - learning_rate: Float. Optimizer learning rate.
        - save_frequency: Integer. How often do we want to save the tree? Set to 0 for no saving.
        - save_path: String. Location to store the data.

    """
    # build in:  circuit type
    # if circuit_type=='schuld' use controlled rotation gates and cycle layout for entangling layers
    # if circuit_type=='hardware' use minimal gate set and path layout for entangling layers
    # Parse configuration parameters.
    NQUBITS = config['nqubits']
    NSAMPLES = config['n_samples']
    PATH = config['save_path']

    if dev_type == "local":
        dev = qml.device("default.qubit.autograd", wires=NQUBITS)
    elif dev_type == "remote":
        my_bucket = "amazon-braket-0fc49b964f85"  # the name of the bucket
        my_prefix = PATH.split(
            '/'
        )[1]  # name of the folder in the bucket is the same as experiment name
        s3_folder = (my_bucket, my_prefix)
        device_arn = "arn:aws:braket:::device/quantum-simulator/amazon/sv1"
        dev = qml.device("braket.aws.qubit",
                         device_arn=device_arn,
                         wires=NQUBITS,
                         s3_destination_folder=s3_folder,
                         parallel=True,
                         max_parallel=10,
                         poll_timeout_seconds=30)

    MIN_TREE_DEPTH = config['min_tree_depth']
    MAX_TREE_DEPTH = config['max_tree_depth']
    SAVE_FREQUENCY = config['save_frequency']

    PRUNE_DEPTH_STEP = config['prune_step']  # EVERY ith step is a prune step
    PRUNE_RATE = config[
        'prune_rate']  # Percentage of nodes to throw away at each layer
    PLOT_INTERMEDIATE_TREES = config['plot_trees']

    assert MIN_TREE_DEPTH < MAX_TREE_DEPTH, 'MIN_TREE_DEPTH must be smaller than MAX_TREE_DEPTH'
    assert 0.0 < PRUNE_RATE < 1.0, f'The PRUNE_RATE must be between 0 and 1, found {PRUNE_RATE}'

    if config['data_set'] == 'circles':
        X_train, y_train = datasets.make_circles(n_samples=NSAMPLES,
                                                 factor=.5,
                                                 noise=.05)
    elif config['data_set'] == 'moons':
        X_train, y_train = datasets.make_moons(n_samples=NSAMPLES, noise=.05)
    # rescale data to -1 1
    X_train = np.multiply(
        1.0,
        np.subtract(
            np.multiply(
                np.divide(np.subtract(X_train, X_train.min()),
                          (X_train.max() - X_train.min())), 2.0), 1.0))
    if config['readout_layer'] == 'one_hot':
        # one hot encode labels
        # y_train_ohe = np.zeros((y_train.size, y_train.max() + 1))
        y_train_ohe = np.zeros((y_train.size, NQUBITS))
        y_train_ohe[np.arange(y_train.size), y_train] = 1
    elif config['readout_layer'] == 'weighted_neuron':
        y_train_ohe = y_train
    # automatically determine the number of classes
    NCLASSES = len(np.unique(y_train))
    assert NQUBITS >= NCLASSES, 'The number of qubits must be equal or larger than the number of classes'
    save_timing = config.get('save_timing', False)
    if save_timing:
        print('saving timing info')
        import time
    # Create a directed graph.
    G = nx.DiGraph()
    # Add the root
    G.add_node("ROOT")
    G.nodes['ROOT']["W"] = 0.0
    G.nodes['ROOT']['weights'] = []
    # nx.set_node_attributes(G, {'ROOT': 0.0}, 'W')
    # Define allowed layers
    ct_ = config.get('circuit_type', None)
    if ct_ == 'schuld':
        possible_layers = ['ZZ', 'X', 'Y', 'Z']
        config['parameterized_gates'] = ['ZZ', 'X', 'Y', 'Z']
    if ct_ == 'hardware':
        possible_layers = ['hw_CNOT', 'X', 'Y', 'Z']
        config['parameterized_gates'] = ['X', 'Y', 'Z']
    possible_embeddings = [
        'E1',
    ]
    assert all([l in string_to_layer_mapping.keys() for l in possible_layers
                ]), 'No valid mapping from string to function found'
    assert all([
        l in string_to_embedding_mapping.keys() for l in possible_embeddings
    ]), 'No valid mapping from string to function found'
    leaves_at_depth_d = dict(
        zip(range(MAX_TREE_DEPTH), [[] for _ in range(MAX_TREE_DEPTH)]))
    leaves_at_depth_d[0].append('ROOT')
    # Iteratively construct tree, pruning at set rate
    for d in range(1, MAX_TREE_DEPTH):
        print(f"Depth = {d}")
        # Save trees
        if (SAVE_FREQUENCY > 0) & ~(d % SAVE_FREQUENCY):
            nx.write_gpickle(G,
                             config['save_path'] + f'/tree_depth_{d}.pickle')
        # Plot trees
        if PLOT_INTERMEDIATE_TREES:
            plot_tree(G)
        # If we are not passed MIN_TREE_DEPTH, don't prune
        if d < MIN_TREE_DEPTH:
            # First depth connects to root
            if d == 1:
                tree_grow_root(G, leaves_at_depth_d, possible_embeddings)
                # At the embedding level we don't need to train because there are no params.
                for v in leaves_at_depth_d[d]:
                    G.nodes[v]['W'] = 1.0
                    G.nodes[v]['weights'] = []
                # print('current graph: ',list(G.nodes(data=True)))
                # nx.set_node_attributes(G, {v: 1.0}, 'W')
            else:
                tree_grow(G, leaves_at_depth_d, d, possible_layers)
                best_arch = max(nx.get_node_attributes(G, 'W').items(),
                                key=operator.itemgetter(1))[0]
                print('Current best architecture: ', best_arch)
                print('max W:', G.nodes[best_arch]['W'])
                print('weights:', G.nodes[best_arch]['weights'])
                # For every leaf, create a circuit and run the optimization.
                for v in leaves_at_depth_d[d]:
                    print(f'Training leaf {v}')
                    # print('current graph: ',list(G.nodes(data=True)))
                    circuit, pshape, numcnots = construct_circuit_from_leaf(
                        v, NQUBITS, NCLASSES, dev, config)
                    config['numcnots'] = numcnots
                    # w_cost = train_circuit(circuit, pshape, X_train, y_train_ohe, 'accuracy', **config)
                    if save_timing:
                        start = time.time()
                        w_cost, weights = evaluate_w(circuit, pshape, X_train,
                                                     y_train_ohe, **config)
                        end = time.time()
                        clock_time = end - start
                        attrs = {
                            "W": w_cost,
                            "weights": weights.numpy(),
                            "timing": clock_time
                        }
                    else:
                        w_cost, weights = evaluate_w(circuit, pshape, X_train,
                                                     y_train_ohe, **config)
                        attrs = {
                            "W": w_cost,
                            "weights": weights.numpy(),
                            'num_cnots': None
                        }
                    # Add the w_cost to the node so we can use it later for pruning
                    # nx.set_node_attributes(G, {v: w_cost}, 'W')
                    # nx.set_node_attributes(G, {v: w_cost}, 'W')
                    for kdx in attrs.keys():
                        G.nodes[v][kdx] = attrs[kdx]
                    # nx.set_node_attributes(G, attrs)

        else:
            # Check that we are at the correct prune depth step.
            if not (d - MIN_TREE_DEPTH) % PRUNE_DEPTH_STEP:
                print('Prune Tree')
                best_arch = max(nx.get_node_attributes(G, 'W').items(),
                                key=operator.itemgetter(1))[0]
                print('Current best architecture: ', best_arch)
                print('max W:', G.nodes[best_arch]['W'])
                print('weights:', G.nodes[best_arch]['weights'])
                # print(nx.get_node_attributes(G,'W'))
                tree_prune(G, leaves_at_depth_d, d, PRUNE_RATE)
                print('Grow Pruned Tree')
                tree_grow(G, leaves_at_depth_d, d, possible_layers)
                # For every leaf, create a circuit and run the optimization.
                for v in leaves_at_depth_d[d]:
                    # print(f'Training leaf {v}')
                    # print('current graph: ',list(G.nodes(data=True)))
                    circuit, pshape, numcnots = construct_circuit_from_leaf(
                        v, NQUBITS, NCLASSES, dev, config)
                    config['numcnots'] = numcnots
                    # w_cost = train_circuit(circuit, pshape, X_train, y_train_ohe, 'accuracy', **config)
                    if save_timing:
                        start = time.time()
                        w_cost, weights = evaluate_w(circuit, pshape, X_train,
                                                     y_train_ohe, **config)
                        end = time.time()
                        clock_time = end - start
                        attrs = {
                            "W": w_cost,
                            "weights": weights.numpy(),
                            "timing": clock_time
                        }
                    else:
                        w_cost, weights = evaluate_w(circuit, pshape, X_train,
                                                     y_train_ohe, **config)
                        attrs = {
                            "W": w_cost,
                            "weights": weights.numpy(),
                            'num_cnots': None
                        }
                    # Add the w_cost to the node so we can use it later for pruning
                    # nx.set_node_attributes(G, attrs)
                    for kdx in attrs.keys():
                        G.nodes[v][kdx] = attrs[kdx]
            else:
                print('Grow Tree')
                best_arch = max(nx.get_node_attributes(G, 'W').items(),
                                key=operator.itemgetter(1))[0]
                print('Current best architecture: ', best_arch)
                print('max W:', G.nodes[best_arch]['W'])
                print('weights:', G.nodes[best_arch]['weights'])
                tree_grow(G, leaves_at_depth_d, d, possible_layers)
                for v in leaves_at_depth_d[d]:
                    # print(f'Training leaf {v}')
                    # print('current graph: ',list(G.nodes(data=True)))
                    circuit, pshape, numcnots = construct_circuit_from_leaf(
                        v, NQUBITS, NCLASSES, dev, config)
                    config['numcnots'] = numcnots
                    # w_cost = train_circuit(circuit, pshape, X_train, y_train_ohe, 'accuracy', **config)
                    if save_timing:
                        start = time.time()
                        w_cost, weights = evaluate_w(circuit, pshape, X_train,
                                                     y_train_ohe, **config)
                        end = time.time()
                        clock_time = end - start
                        attrs = {
                            "W": w_cost,
                            "weights": weights.numpy(),
                            "timing": clock_time
                        }
                    else:
                        w_cost, weights = evaluate_w(circuit, pshape, X_train,
                                                     y_train_ohe, **config)
                        attrs = {"W": w_cost, "weights": weights.numpy()}
                    # Add the w_cost to the node so we can use it later for pruning
                    # nx.set_node_attributes(G, {v: w_cost}, 'W')
                    # nx.set_node_attributes(G, {v: w_cost}, 'W')
                    # nx.set_node_attributes(G, attrs)
                    for kdx in attrs.keys():
                        G.nodes[v][kdx] = attrs[kdx]
    best_arch = max(nx.get_node_attributes(G, 'W').items(),
                    key=operator.itemgetter(1))[0]
    print('architecture with max W: ', best_arch)
    print('max W:', G.nodes[best_arch]['W'])
    print('weights: ', G.nodes[best_arch]['weights'])
Exemplo n.º 11
0
    # create PauliZ ⊗ ... ⊗ PauliZ
    op = qml.PauliZ(0)
    for i in range(1, len(wires)):
        op = op @ qml.PauliZ(i)

    return qml.sample(op)


def get_data_and_weights(wires, n_layers, how_many):
    X, Y = hemi.get_labelled_dataset(wires, how_many)
    weights = rcnp.get_initial_weights(wires, n_layers)
    return X, Y, weights


if __name__ == "__main__":
    dev_expval = qml.device("default.qubit", wires=range(3))
    print(dev_expval)

    dev_1shot = qml.device("default.qubit", wires=range(3), shots=1)
    print(dev_1shot)

    @qml.qnode(dev_expval)
    def my_combined_expval(weights, x, wires):
        return combined_expval(weights, x, wires)

    @qml.qnode(dev_1shot)
    def my_combined_1shot(weights, x, wires):
        return combined_1shot(weights, x, wires)

    X, Y, weights = get_data_and_weights(dev_expval.wires, 2, 5)
    print(X)
Exemplo n.º 12
0
 def test_matrix_elem(self, wires, vec1, obs, vec2, expected):
     """Tests for the helper function _matrix_elem"""
     dev = qml.device("default.qubit", wires=wires)
     tape = ReversibleTape()
     res = tape._matrix_elem(vec1, obs, vec2, dev)
     assert res == expected
def parameter_shift(weights):
    """Compute the gradient of the variational circuit given by the
    ansatz function using the parameter-shift rule.

    Write your code below between the # QHACK # markers—create a device with
    the correct number of qubits, create a QNode that applies the above ansatz,
    and compute the gradient of the provided ansatz using the parameter-shift rule.

    Args:
        weights (array): An array of floating-point numbers with size (2, 3).

    Returns:
        array: The gradient of the variational circuit. The shape should match
        the input weights array.
    """
    dev = qml.device("default.qubit", wires=3)

    @qml.qnode(dev, diff_method="parameter-shift")
    def circuit(weights):
        # for i in range(len(weights)):
        #     qml.RX(weights[i, 0], wires=0)
        #     qml.RY(weights[i, 1], wires=1)
        #     qml.RZ(weights[i, 2], wires=2)
        #
        #     qml.CNOT(wires=[0, 1])
        #     qml.CNOT(wires=[1, 2])
        #     qml.CNOT(wires=[2, 0])

        qml.RX(weights[0], wires=0)
        qml.RY(weights[1], wires=1)
        qml.RZ(weights[2], wires=2)

        qml.CNOT(wires=[0, 1])
        qml.CNOT(wires=[1, 2])
        qml.CNOT(wires=[2, 0])

        qml.RX(weights[3], wires=0)
        qml.RY(weights[4], wires=1)
        qml.RZ(weights[5], wires=2)

        qml.CNOT(wires=[0, 1])
        qml.CNOT(wires=[1, 2])
        qml.CNOT(wires=[2, 0])

        return qml.expval(qml.PauliY(0) @ qml.PauliZ(2))

    gradient = np.zeros_like(weights)

    # QHACK #
    def apply_parameter_shift(qnode, params, i):
        shifted = params.copy()
        shifted[i] += np.pi / 2
        forward = qnode(shifted)  # forward evaluation

        shifted[i] -= np.pi
        backward = qnode(shifted)  # backward evaluation

        return 0.5 * (forward - backward)

    params = []
    for weight_arr in weights:
        for val in weight_arr:
            params.append(float(val))

    gradient = [
        apply_parameter_shift(circuit, params, i) for i in range(len(params))
    ]

    # QHACK #

    return gradient
Exemplo n.º 14
0
# ---------------------------------------------------
#
# First, we import PennyLane, NumPy, and Matplotlib

import pennylane as qml
from pennylane import numpy as np
import matplotlib.pyplot as plt

##################################################
# Next, we create a randomized variational circuit

# Set a seed for reproducibility
np.random.seed(42)

num_qubits = 4
dev = qml.device("default.qubit", wires=num_qubits)
gate_set = [qml.RX, qml.RY, qml.RZ]


def rand_circuit(params, random_gate_sequence=None, num_qubits=None):
    """A random variational quantum circuit.

    Args:
        params (array[float]): array of parameters
        random_gate_sequence (dict): a dictionary of random gates
        num_qubits (int): the number of qubits in the circuit

    Returns:
        float: the expectation value of the target observable
    """
    for i in range(num_qubits):
Exemplo n.º 15
0
(1) Single-qubit VQE example
----------------------------

The first step is to import the required libraries and packages:
"""

import matplotlib.pyplot as plt
from pennylane import numpy as np
import pennylane as qml

##############################################################################
# For this simple example, we consider the following single-qubit Hamiltonian: :math:`\sigma_x + \sigma_z`.
#
# We define the device:

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


##############################################################################
# For the variational ansatz, we use two single-qubit rotations, which the user may recognize
# from a previous :doc:`tutorial </demos/tutorial_qubit_rotation>` on qubit rotations.


def circuit(params, wires=0):
    qml.RX(params[0], wires=wires)
    qml.RY(params[1], wires=wires)


##############################################################################
# We then define our cost function using the ``ExpvalCost`` class, which supports the computation of
# block-diagonal or diagonal approximations to the Fubini-Study metric tensor [#stokes2019]_. This tensor is a
Exemplo n.º 16
0

######################################################################
# Note that we return both the list of :math:`\beta_k` values, as well as the expectation value of the cost Hamiltonian
# for each step.
#
# We can now run FALQON for our MaxClique problem! It is important that we choose :math:`\Delta t` small enough
# such that the approximate time evolution is close enough to the real time evolution, otherwise we the expectation
# value of :math:`H_c` may not strictly decrease. For this demonstration, we set :math:`\Delta t = 0.03`,
# :math:`n = 40`, and :math:`\beta_1 = 0`. These are comparable to the hyperparameters chosen in the original paper.

n = 40
beta_1 = 0.0
delta_t = 0.03

dev = qml.device("default.qubit",
                 wires=graph.nodes)  # Creates a device for the simulation
res_beta, res_energies = max_clique_falqon(graph, n, beta_1, delta_t, dev)

######################################################################
# We can then plot the expectation value of the cost Hamiltonian over the
# iterations of the algorithm:
#

plt.plot(range(n + 1)[1:], res_energies)
plt.xlabel("Iteration")
plt.ylabel("Cost Function Value")
plt.show()

######################################################################
# The expectation value decreases!
#
Exemplo n.º 17
0
import pennylane as qml
from pennylane import numpy as np

dev = qml.device('default.qubit', wires=1)


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


print('The expectation value {}'.format(circuit([0])))
print('The expectation value {}'.format(circuit([np.pi / 3])))
print('The expectation value {}'.format(circuit([np.pi])))


def objective(var):
    return circuit(var)


np.random.seed(2019)
initial_theta = 2 * np.pi * np.random.random_sample()
init_params = np.array([initial_theta])
print('Initial objective function value {:.7f} for theta={:.2f}'.format(
    objective(init_params), initial_theta))
# Initilize Gradient Descent Optimizer
opt = qml.GradientDescentOptimizer(stepsize=0.4)

# set the number of steps
steps = 30
Exemplo n.º 18
0
def gaussian_device_1_wire():
    """Fixture of a default.gaussian device with 1 wire."""
    return qml.device('default.gaussian', wires=1)
Exemplo n.º 19
0
from matplotlib.ticker import LinearLocator, FormatStrFormatter

######################################################################
# Visualizing the problem
# -----------------------
#
# To start, let's look at the task of learning the identity gate
# across multiple qubits. This will help us visualize the problem and get
# a sense of what is happening in the cost landscape.
#
# First we define a number of wires we want to train on. The work by
# Cerezo et al. shows that circuits are trainable under certain regimes, so
# how many qubits we train on will effect our results.

wires = 6
dev = qml.device("default.qubit", wires=wires, shots=10000, analytic=False)

######################################################################
# Next, we want to define our QNodes and our circuit ansatz. For this
# simple example, an ansatz that works well is simply a rotation along X,
# and a rotation along Y, repeated across all the qubits.
#
# We will also define our cost functions here. Since we are trying to
# learn the identity gate, a natural cost function is 1 minus the probability of measuring the
# zero state, denoted here as :math:`1 - p_{|0\rangle}`.
#
# .. math:: C = \langle  \psi(\theta) | \left(I - |0\rangle \langle 0|\right)  | \psi(\theta)  \rangle   =1-p_{|0\rangle}
#
# We will apply this across all qubits for our global cost function, i.e.,
#
# .. math:: C_{G} = \langle  \psi(\theta) | \left(I - |00 \ldots 0\rangle \langle 00 \ldots 0|\right) | \psi(\theta) \rangle  = 1-p_{|00 \ldots 0\rangle}
Exemplo n.º 20
0
def gaussian_device_3_wires():
    """Fixture of a default.gaussian device with 3 wires."""
    return qml.device('default.gaussian', wires=3)
Exemplo n.º 21
0
 def test_aggregate_expval(self, coeffs, observables, expected):
     """Tests that the aggregate function returns correct expectation values"""
     dev = qml.device("default.qubit", wires=2)
     qnodes = qml.map(lambda params, **kwargs: None, observables, dev)
     expval = qml.dot(coeffs, qnodes)
     assert expval([]) == sum(expected)
Exemplo n.º 22
0
qb2wire = {i: j for i, j in zip(qubits, range(wires))}

######################################################################
# Now let's create the ``qsim`` device, available via the Cirq plugin, making
# use of the ``wires`` and ``qubits`` keywords that we defined above.
# First, we need to define the number of 'shots' per circuit instance to
# be used---where the number of shots simply corresponds to the number
# of times that the circuit is sampled. This will also be needed later when
# calculating the cross-entropy benchmarking fidelity. The more shots, the
# more accurate the results will be. 500,000 shots will be used here---the same
# number of samples used in the paper---but feel free to
# change this (depending on your own computational restrictions).
#

shots = 500000
dev = qml.device('cirq.qsim', wires=wires, qubits=qubits, shots=shots)

######################################################################
# The next step would be to prepare the necessary gates. Some of these
# gates are not natively supported in PennyLane, but are accessible
# through the Cirq plugin. We can define the remaining gates by hand.
#
# For the single-qubit gates we need the :math:`\sqrt{X}` and
# :math:`\sqrt{Y}` gates, which can be written as :math:`RX(\pi/2)` and
# :math:`RY(\pi/2)` respectively, as well as the :math:`\sqrt{W}` gate,
# where :math:`W = \frac{X + Y}{2}`. The latter is easiest defined by its
# unitary matrix
#
# .. math::
#
#    \frac{1}{\sqrt{2}}
Exemplo n.º 23
0
import pennylane as qml
from pennylane import numpy as np

## Plugin
dev_fock = qml.device("strawberryfields.fock", wires=2, cutoff_dim=2)


@qml.qnode(dev_fock, diff_method="parameter-shift")
def photon_redirection(params):
    qml.FockState(1, wires=0)
    qml.Beamsplitter(params[0], params[1], wires=[0, 1])
    return qml.expval(qml.NumberOperator(1))


def cost(params):
    return -photon_redirection(params)


init_params = np.array([0.01, 0.01])
print(cost(init_params))

# Start from 0 => gradient = 0 => trapped in local maxima

dphoton_redirection = qml.grad(photon_redirection, argnum=0)
print(dphoton_redirection([0.0, 0.0]))

# initialise the optimizer
opt = qml.GradientDescentOptimizer(stepsize=0.4)

# set the number of steps
steps = 100
Exemplo n.º 24
0
# a measurement of multiple qubits in the computational basis, so we define
# a Hermitian operator to do this. The eigenvalues of the operator are
# the qubit measurement values in integer form.


def comp_basis_measurement(wires):
    n_wires = len(wires)
    return qml.Hermitian(np.diag(range(2**n_wires)), wires=wires)


##############################################################################
# Circuit
# ~~~~~~~
# Next, we create a quantum device with 4 qubits.

dev = qml.device("default.qubit", wires=n_wires, analytic=True, shots=1)

##############################################################################
# We also require a quantum node which will apply the operators according to the
# angle parameters, and return the expectation value of the observable
# :math:`\sigma_z^{j}\sigma_z^{k}` to be used in each term of the objective function later on. The
# argument ``edge`` specifies the chosen edge term in the objective function, :math:`(j,k)`.
# Once optimized, the same quantum node can be used for sampling an approximately optimal bitstring
# if executed with the ``edge`` keyword set to ``None``. Additionally, we specify the number of layers
# (repeated applications of :math:`U_BU_C`) using the keyword ``n_layers``.

pauli_z = [[1, 0], [0, -1]]
pauli_z_2 = np.kron(pauli_z, pauli_z)


@qml.qnode(dev)
Exemplo n.º 25
0
    def test_construct_subcircuit_layers(self):
        """Test correct subcircuits constructed
        when a layer structure exists"""
        dev = qml.device("default.qubit", wires=3)

        def circuit(params):
            # section 1
            qml.RX(params[0], wires=0)
            # section 2
            qml.RY(params[1], wires=0)
            qml.CNOT(wires=[0, 1])
            qml.CNOT(wires=[1, 2])
            # section 3
            qml.RX(params[2], wires=0)
            qml.RY(params[3], wires=1)
            qml.RZ(params[4], wires=2)
            qml.CNOT(wires=[0, 1])
            qml.CNOT(wires=[1, 2])
            # section 4
            qml.RX(params[5], wires=0)
            qml.RY(params[6], wires=1)
            qml.RZ(params[7], wires=2)
            qml.CNOT(wires=[0, 1])
            qml.CNOT(wires=[1, 2])
            return qml.expval(qml.PauliX(0)), qml.expval(
                qml.PauliX(1)), qml.expval(qml.PauliX(2))

        circuit = QubitQNode(circuit, dev)

        params = np.ones([8])
        circuit.metric_tensor([params], only_construct=True)
        res = circuit._metric_tensor_subcircuits

        # this circuit should split into 4 independent
        # sections or layers when constructing subcircuits
        assert len(res) == 4

        # first layer subcircuit
        layer = res[(0, )]
        assert len(layer["queue"]) == 0
        assert len(layer["observable"]) == 1
        assert isinstance(layer["observable"][0], qml.PauliX)

        # second layer subcircuit
        layer = res[(1, )]
        assert len(layer["queue"]) == 1
        assert len(layer["observable"]) == 1
        assert isinstance(layer["queue"][0], qml.RX)
        assert isinstance(layer["observable"][0], qml.PauliY)

        # third layer subcircuit
        layer = res[(2, 3, 4)]
        assert len(layer["queue"]) == 4
        assert len(layer["observable"]) == 3
        assert isinstance(layer["queue"][0], qml.RX)
        assert isinstance(layer["queue"][1], qml.RY)
        assert isinstance(layer["queue"][2], qml.CNOT)
        assert isinstance(layer["queue"][3], qml.CNOT)
        assert isinstance(layer["observable"][0], qml.PauliX)
        assert isinstance(layer["observable"][1], qml.PauliY)
        assert isinstance(layer["observable"][2], qml.PauliZ)

        # fourth layer subcircuit
        layer = res[(5, 6, 7)]
        assert len(layer["queue"]) == 9
        assert len(layer["observable"]) == 3
        assert isinstance(layer["queue"][0], qml.RX)
        assert isinstance(layer["queue"][1], qml.RY)
        assert isinstance(layer["queue"][2], qml.CNOT)
        assert isinstance(layer["queue"][3], qml.CNOT)
        assert isinstance(layer["queue"][4], qml.RX)
        assert isinstance(layer["queue"][5], qml.RY)
        assert isinstance(layer["queue"][6], qml.RZ)
        assert isinstance(layer["queue"][7], qml.CNOT)
        assert isinstance(layer["queue"][8], qml.CNOT)
        assert isinstance(layer["observable"][0], qml.PauliX)
        assert isinstance(layer["observable"][1], qml.PauliY)
        assert isinstance(layer["observable"][2], qml.PauliZ)
Exemplo n.º 26
0
def run_vqe(H):
    """Runs the variational quantum eigensolver on the problem Hamiltonian using the
    variational ansatz specified above.

    Fill in the missing parts between the # QHACK # markers below to run the VQE.

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

    Returns:
        The ground state energy of the Hamiltonian.
    """
    energy = 0

    # QHACK #

    # Initialize the quantum device
    dev = qml.device('default.qubit', wires=H.wires)

    # Randomly choose initial parameters (how many do you need?)
    num_qubits = len(H.wires)

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

    # Set up a cost function

    cost_fn = qml.ExpvalCost(variational_ansatz, H, dev)

    # Set up an optimizer
    eta = 0.2
    opt = qml.AdamOptimizer(stepsize=eta)
    max_iterations = 500
    # Run the VQE by iterating over many steps of the optimizer
    conv_tol = 1e-6

    for n in range(max_iterations):
        #print(params)
        params, prev_energy = opt.step_and_cost(cost_fn, params)
        #print(params)
        energy = cost_fn((params))
        conv = np.abs(energy - prev_energy)
        #if n % 20 == 0:
        #print('Iteration = {:},  Energy = {:.8f}, Convergence={:.8f} Ha'.format(n, energy,conv))
        if 1e-4 <= conv < 1e-2:
            opt.update_stepsize(0.05)
        elif conv_tol < conv < 1e-4:
            opt.update_stepsize(0.01)
        elif conv <= conv_tol:
            break
        else:
            continue

    #print()
    #print('Final convergence parameter = {:.8f} Ha'.format(conv))
    #print('Final value of the ground-state energy = {:.8f} Ha'.format(energy))
    #print('Accuracy with respect to the FCI energy: {:.8f} Ha ({:.8f} kcal/mol)'.format(
    #np.abs(energy - (-1.136189454088)), np.abs(energy - (-1.136189454088))*627.503
    #))
    #print()
    #print('Final circuit parameters = \n', params)
    # QHACK #

    # Return the ground state energy
    return energy
Exemplo n.º 27
0
def qubit_device(n_subsystems):
    """Number of qubits or modes."""
    return qml.device('default.qubit', wires=n_subsystems)
##############################################################################
# To estimate the overlap of the ground state with the post-selected state, one could
# directly make use of the measurement samples. However, since we want to optimize the cost
# function, it is useful to express everything in terms of expectation values through
# Bayes' theorem:
#
# .. math::
#   |\langle b | \Psi \rangle|^2=
#   P( \mathrm{sys}=\mathrm{ground}\,|\, \mathrm{anc} = \mathrm{ground}) =
#   P( \mathrm{all}=\mathrm{ground})/P( \mathrm{anc}=\mathrm{ground})
#
# To evaluate the two probabilities appearing on the right hand side of the previous equation
# we initialize a ``default.qubit`` device and we define two different ``qnode`` circuits.

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


@qml.qnode(dev)
def global_ground(weights):
    # Circuit gates
    full_circuit(weights)
    # Projector on the global ground state
    P = np.zeros((2**tot_qubits, 2**tot_qubits))
    P[0, 0] = 1.0
    return qml.expval(qml.Hermitian(P, wires=range(tot_qubits)))


@qml.qnode(dev)
def ancilla_ground(weights):
    # Circuit gates
Exemplo n.º 29
0
def qubit_device_3_wires():
    return qml.device('default.qubit', wires=3)
Exemplo n.º 30
0
#     PennyLane-SF plugin).
#
# .. tip::
#
#    *Devices are loaded in PennyLane via the function* :func:`~.pennylane.device`
#
#
# PennyLane supports devices using both the qubit model of quantum computation and devices
# using the CV model of quantum computation. In fact, even a hybrid computation containing
# both qubit and CV quantum nodes is possible; see the
# :ref:`hybrid computation example <hybrid_computation_example>` for more details.
#
# For this tutorial, we are using the qubit model, so let's initialize the ``'default.qubit'`` device
# provided by PennyLane; a simple pure-state qubit simulator.

dev1 = qml.device("default.qubit", wires=1)

##############################################################################
# For all devices, :func:`~.pennylane.device` accepts the following arguments:
#
# * ``name``: the name of the device to be loaded
# * ``wires``: the number of subsystems to initialize the device with
#
# Here, as we only require a single qubit for this example, we set ``wires=1``.

##############################################################################
# Constructing the QNode
# ----------------------
#
# Now that we have initialized our device, we can begin to construct a
# **quantum node** (or QNode).