qml.matrix

matrix(op, wire_order=None)[source]

The matrix representation of an operation or quantum circuit.

Parameters
  • op (Operator or QNode or QuantumTape or Callable or PauliWord or PauliSentence) – A quantum operator or quantum circuit.

  • wire_order (Sequence[Any], optional) –

    Order of the wires in the quantum circuit. The default wire order depends on the type of op:

    • If op is a QNode, then the wire order is determined by the associated device’s wires, if provided.

    • Otherwise, the wire order is determined by the order in which wires appear in the circuit.

    • See the usage details for more information.

Returns

If an operator, PauliWord or PauliSentence is provided as input, the matrix is returned directly in the form of a tensor. Otherwise, the transformed circuit is returned as described in qml.transform. Executing this circuit will provide its matrix representation.

Return type

TensorLike or qnode (QNode) or quantum function (Callable) or tuple[List[QuantumTape], function]

Example

Given an instantiated operator, qml.matrix returns the matrix representation:

>>> op = qml.RX(0.54, wires=0)
>>> qml.matrix(op)
[[0.9637709+0.j         0.       -0.26673144j]
[0.       -0.26673144j 0.9637709+0.j        ]]

It can also be used in a functional form:

>>> x = torch.tensor(0.6, requires_grad=True)
>>> matrix_fn = qml.matrix(qml.RX)
>>> matrix_fn(x, wires=0)
tensor([[0.9553+0.0000j, 0.0000-0.2955j],
        [0.0000-0.2955j, 0.9553+0.0000j]], grad_fn=<AddBackward0>)

In its functional form, it is fully differentiable with respect to gate arguments:

>>> loss = torch.real(torch.trace(matrix_fn(x, wires=0)))
>>> loss.backward()
>>> x.grad
tensor(-0.5910)

This operator transform can also be applied to QNodes, tapes, and quantum functions that contain multiple operations; see Usage Details below for more details.

qml.matrix can also be used with PauliWord and PauliSentence instances. Internally, we are using their to_mat() methods.

>>> X0 = PauliWord({0:"X"})
>>> np.allclose(qml.matrix(X0), X0.to_mat())
True

qml.matrix can also be used with QNodes, tapes, or quantum functions that contain multiple operations.

Consider the following quantum function:

def circuit(theta):
    qml.RX(theta, wires=1)
    qml.Z(0)

We can use qml.matrix to generate a new function that returns the unitary matrix corresponding to the function circuit:

>>> matrix_fn = qml.matrix(circuit)
>>> theta = np.pi / 4
>>> matrix_fn(theta)
array([[ 0.92387953+0.j,  0.+0.j ,  0.-0.38268343j,  0.+0.j],
[ 0.+0.j,  -0.92387953+0.j,  0.+0.j,  0. +0.38268343j],
[ 0. -0.38268343j,  0.+0.j,  0.92387953+0.j,  0.+0.j],
[ 0.+0.j,  0.+0.38268343j,  0.+0.j,  -0.92387953+0.j]])

Note that since wire_order was not specified, the default order [1, 0] for circuit was used, and the unitary matrix corresponds to the operation \(R_X(\theta)\otimes Z\). To obtain the matrix for \(Z\otimes R_X(\theta)\), specify wire_order=[0, 1] in the function call:

>>> matrix = qml.matrix(circuit, wire_order=[0, 1])

You can also get the unitary matrix for operations on a subspace of a larger Hilbert space. For example, with the same function circuit and wire_order=["a", 0, "b", 1] you obtain the \(16\times 16\) matrix for the operation \(I\otimes Z\otimes I\otimes R_X(\theta)\).

This unitary matrix can also be used in differentiable calculations. For example, consider the following cost function:

def circuit(theta):
    qml.RX(theta, wires=1) qml.Z(0)
    qml.CNOT(wires=[0, 1])

def cost(theta):
    matrix = qml.matrix(circuit)(theta)
    return np.real(np.trace(matrix))

Since this cost function returns a real scalar as a function of theta, we can differentiate it:

>>> theta = np.array(0.3, requires_grad=True)
>>> cost(theta)
1.9775421558720845
>>> qml.grad(cost)(theta)
-0.14943813247359922

Note

When using qml.matrix with a QNode, unless specified, the device wire order will be used. If the device wires are not set, the wire order will be inferred from the quantum function used to create the QNode. Consider the following example:

def circuit():
    qml.Hadamard(wires=1)
    qml.CZ(wires=[0, 1])
    qml.Hadamard(wires=1)
    return qml.state()

dev_with_wires = qml.device("default.qubit", wires=[0, 1])
dev_without_wires = qml.device("default.qubit")

qnode_with_wires = qml.QNode(circuit, dev_with_wires)
qnode_without_wires = qml.QNode(circuit, dev_without_wires)
>>> qml.matrix(qnode_with_wires)().round(2)
array([[ 1.+0.j, -0.+0.j,  0.+0.j,  0.+0.j],
       [-0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j],
       [ 0.+0.j,  0.+0.j, -0.+0.j,  1.+0.j],
       [ 0.+0.j,  0.+0.j,  1.+0.j, -0.+0.j]])
>>> qml.matrix(qnode_without_wires)().round(2)
array([[ 1.+0.j,  0.+0.j, -0.+0.j,  0.+0.j],
       [ 0.+0.j, -0.+0.j,  0.+0.j,  1.+0.j],
       [-0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j],
       [ 0.+0.j,  1.+0.j,  0.+0.j, -0.+0.j]])

The second matrix above uses wire order [1, 0] because the device does not have wires specified, and this is the order in which wires appear in circuit().

Contents

Using PennyLane

Development

API

Internals