qml.qnn.KerasLayer

class KerasLayer(qnode, weight_shapes: dict, output_dim, weight_specs: Optional[dict] = None, **kwargs)[source]

Bases: tensorflow.python.keras.engine.base_layer.Layer

Converts a QNode() to a Keras Layer.

The result can be used within the Keras Sequential or Model classes for creating quantum and hybrid models.

Parameters
  • qnode (qml.QNode) – the PennyLane QNode to be converted into a Keras Layer

  • weight_shapes (dict[str, tuple]) – a dictionary mapping from all weights used in the QNode to their corresponding shapes

  • output_dim (int) – the output dimension of the QNode

  • weight_specs (dict[str, dict]) – An optional dictionary for users to provide additional specifications for weights used in the QNode, such as the method of parameter initialization. This specification is provided as a dictionary with keys given by the arguments of the add_weight() method and values being the corresponding specification.

  • **kwargs – additional keyword arguments passed to the Layer base class

Example

First let’s define the QNode that we want to convert into a Keras Layer:

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

@qml.qnode(dev)
def qnode(inputs, weights_0, weight_1):
    qml.RX(inputs[0], wires=0)
    qml.RX(inputs[1], wires=1)
    qml.Rot(*weights_0, wires=0)
    qml.RY(weight_1, wires=1)
    qml.CNOT(wires=[0, 1])
    return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

The signature of the QNode must contain an inputs named argument for input data, with all other arguments to be treated as internal weights. We can then convert to a Keras Layer with:

>>> weight_shapes = {"weights_0": 3, "weight_1": 1}
>>> qlayer = qml.qnn.KerasLayer(qnode, weight_shapes, output_dim=2)

The internal weights of the QNode are automatically initialized within the KerasLayer and must have their shapes specified in a weight_shapes dictionary. It is then easy to combine with other neural network layers from the tensorflow.keras.layers module and create a hybrid:

>>> clayer = tf.keras.layers.Dense(2)
>>> model = tf.keras.models.Sequential([qlayer, clayer])

QNode signature

The QNode must have a signature that satisfies the following conditions:

  • Contain an inputs named argument for input data.

  • All other arguments must accept an array or tensor and are treated as internal weights of the QNode.

  • All other arguments must have no default value.

  • The inputs argument is permitted to have a default value provided the gradient with respect to inputs is not required.

  • There cannot be a variable number of positional or keyword arguments, e.g., no *args or **kwargs present in the signature.

Initializing weights

The optional weight_specs argument of KerasLayer allows for a more fine-grained specification of the QNode weights, such as the method of initialization and any regularization or constraints. For example, the initialization method of the weights argument in the example above could be specified by:

weight_specs = {"weights": {"initializer": "random_uniform"}}

The values of weight_specs are dictionaries with keys given by arguments of the Keras add_weight() method. For the "initializer" argument, one can specify a string such as "random_uniform" or an instance of an Initializer class, such as tf.keras.initializers.RandomUniform.

If weight_specs is not specified, weights will be added using the Keras default initialization and without any regularization or constraints.

Additional example

The code block below shows how a circuit composed of templates from the qml.templates module can be combined with classical Dense layers to learn the two-dimensional moons dataset.

import pennylane as qml
import tensorflow as tf
import sklearn.datasets

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

@qml.qnode(dev)
def qnode(inputs, weights):
    qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))
    qml.templates.StronglyEntanglingLayers(weights, wires=range(n_qubits))
    return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))

weight_shapes = {"weights": (3, n_qubits, 3)}

qlayer = qml.qnn.KerasLayer(qnode, weight_shapes, output_dim=2)
clayer1 = tf.keras.layers.Dense(2)
clayer2 = tf.keras.layers.Dense(2, activation="softmax")
model = tf.keras.models.Sequential([clayer1, qlayer, clayer2])

data = sklearn.datasets.make_moons()
X = tf.constant(data[0])
Y = tf.one_hot(data[1], depth=2)

opt = tf.keras.optimizers.SGD(learning_rate=0.5)
model.compile(opt, loss='mae')

The model can be trained using:

>>> model.fit(X, Y, epochs=8, batch_size=5)
Train on 100 samples
Epoch 1/8
100/100 [==============================] - 9s 90ms/sample - loss: 0.3524
Epoch 2/8
100/100 [==============================] - 9s 87ms/sample - loss: 0.2441
Epoch 3/8
100/100 [==============================] - 9s 87ms/sample - loss: 0.1908
Epoch 4/8
100/100 [==============================] - 9s 87ms/sample - loss: 0.1832
Epoch 5/8
100/100 [==============================] - 9s 88ms/sample - loss: 0.1596
Epoch 6/8
100/100 [==============================] - 9s 87ms/sample - loss: 0.1637
Epoch 7/8
100/100 [==============================] - 9s 86ms/sample - loss: 0.1613
Epoch 8/8
100/100 [==============================] - 9s 87ms/sample - loss: 0.1474

Returning a state

If your QNode returns the state of the quantum circuit using state() or density_matrix(), you must immediately follow your quantum Keras Layer with a layer that casts to reals. For example, you could use tf.keras.layers.Lambda with the function lambda x: tf.abs(x). This casting is required because TensorFlow’s Keras layers require a real input and are differentiated with respect to real parameters.

activity_regularizer

Optional regularizer function for the output of this layer.

compute_dtype

The dtype of the layer’s computations.

dtype

The dtype of the layer weights.

dtype_policy

The dtype policy associated with this layer.

dynamic

Whether the layer is dynamic (eager-only); set in the constructor.

inbound_nodes

Deprecated, do NOT use! Only for compatibility with external Keras.

input

Retrieves the input tensor(s) of a layer.

input_arg

Name of the argument to be used as the input to the Keras Layer.

input_mask

Retrieves the input mask tensor(s) of a layer.

input_shape

Retrieves the input shape(s) of a layer.

input_spec

InputSpec instance(s) describing the input format for this layer.

losses

List of losses added using the add_loss() API.

metrics

List of metrics added using the add_metric() API.

name

Name of the layer (string), set in the constructor.

name_scope

Returns a tf.name_scope instance for this class.

non_trainable_variables

Sequence of non-trainable variables owned by this module and its submodules.

non_trainable_weights

List of all non-trainable weights tracked by this layer.

outbound_nodes

Deprecated, do NOT use! Only for compatibility with external Keras.

output

Retrieves the output tensor(s) of a layer.

output_mask

Retrieves the output mask tensor(s) of a layer.

output_shape

Retrieves the output shape(s) of a layer.

stateful

submodules

Sequence of all sub-modules.

supports_masking

Whether this layer supports computing a mask using compute_mask.

trainable

trainable_variables

Sequence of trainable variables owned by this module and its submodules.

trainable_weights

List of all trainable weights tracked by this layer.

updates

variable_dtype

Alias of Layer.dtype, the dtype of the weights.

variables

Returns the list of all layer variables/weights.

weights

Returns the list of all layer variables/weights.

input_arg

Name of the argument to be used as the input to the Keras Layer. Set to "inputs".

add_loss(losses, **kwargs)

Add loss tensor(s), potentially dependent on layer inputs.

add_metric(value[, name])

Adds metric tensor to the layer.

add_update(updates[, inputs])

Add update op(s), potentially dependent on layer inputs.

add_variable(*args, **kwargs)

Deprecated, do NOT use! Alias for add_weight.

add_weight([name, shape, dtype, …])

Adds a new variable to the layer.

apply(inputs, *args, **kwargs)

Deprecated, do NOT use!

build(input_shape)

Initializes the QNode weights.

call(inputs)

Evaluates the QNode on input data using the initialized weights.

compute_mask(inputs[, mask])

Computes an output mask tensor.

compute_output_shape(input_shape)

Computes the output shape after passing data of shape input_shape through the QNode.

compute_output_signature(input_signature)

Compute the output tensor signature of the layer based on the inputs.

count_params()

Count the total number of scalars composing the weights.

from_config(config)

Creates a layer from its config.

get_config()

Returns the config of the layer.

get_input_at(node_index)

Retrieves the input tensor(s) of a layer at a given node.

get_input_mask_at(node_index)

Retrieves the input mask tensor(s) of a layer at a given node.

get_input_shape_at(node_index)

Retrieves the input shape(s) of a layer at a given node.

get_losses_for(inputs)

Deprecated, do NOT use!

get_output_at(node_index)

Retrieves the output tensor(s) of a layer at a given node.

get_output_mask_at(node_index)

Retrieves the output mask tensor(s) of a layer at a given node.

get_output_shape_at(node_index)

Retrieves the output shape(s) of a layer at a given node.

get_updates_for(inputs)

Deprecated, do NOT use!

get_weights()

Returns the current weights of the layer, as NumPy arrays.

set_weights(weights)

Sets the weights of the layer, from NumPy arrays.

with_name_scope(method)

Decorator to automatically enter the module name scope.

build(input_shape)[source]

Initializes the QNode weights.

Parameters

input_shape (tuple or tf.TensorShape) – shape of input data

call(inputs)[source]

Evaluates the QNode on input data using the initialized weights.

Parameters

inputs (tensor) – data to be processed

Returns

output data

Return type

tensor

compute_output_shape(input_shape)[source]

Computes the output shape after passing data of shape input_shape through the QNode.

Parameters

input_shape (tuple or tf.TensorShape) – shape of input data

Returns

shape of output data

Return type

tf.TensorShape