qml.specs

specs(qnode, level='gradient', compute_depth=True)[source]

Provides the specifications of a quantum circuit.

This transform converts a QNode into a callable that provides resource information about the circuit after applying the specified amount of transforms/expansions first.

Parameters:

qnode (.QNode | .QJIT) – the QNode to calculate the specifications for.

Keyword Arguments:
  • level (str | int | slice | iter[int]) – An indication of which transforms to apply before computing the resource information.

  • compute_depth (bool) – Whether to compute the depth of the circuit. If False, the depth will not be included in the returned information. Default: True

Returns:

A function that has the same argument signature as qnode. This function returns a CircuitSpecs object containing the qnode specifications.

Example

from pennylane import numpy as pnp

dev = qml.device("default.qubit", wires=2)
x = pnp.array([0.1, 0.2])
Hamiltonian = qml.dot([1.0, 0.5], [qml.X(0), qml.Y(0)])
gradient_kwargs = {"shifts": pnp.pi / 4}

@qml.qnode(dev, diff_method="parameter-shift", gradient_kwargs=gradient_kwargs)
def circuit(x, add_ry=True):
    qml.RX(x[0], wires=0)
    qml.CNOT(wires=(0,1))
    qml.TrotterProduct(Hamiltonian, time=1.0, n=4, order=2)
    if add_ry:
        qml.RY(x[1], wires=1)
    qml.TrotterProduct(Hamiltonian, time=1.0, n=4, order=4)
    return qml.probs(wires=(0,1))
>>> print(qml.specs(circuit)(x, add_ry=False))
Device: default.qubit
Device wires: 2
Shots: Shots(total=None)
Level: gradient

Resource specifications:
  Total qubit allocations: 2
  Total gates: 98
  Circuit depth: 98

  Gate types:
    RX: 1
    CNOT: 1
    Evolution: 96

  Measurements:
    probs(all wires): 1

Here you can see how the number of gates and their types change as we apply different amounts of transforms through the level argument:

dev = qml.device("default.qubit")
gradient_kwargs = {"shifts": pnp.pi / 4}

@qml.transforms.merge_rotations
@qml.transforms.undo_swaps
@qml.transforms.cancel_inverses
@qml.qnode(dev, diff_method="parameter-shift", gradient_kwargs=gradient_kwargs)
def circuit(x):
    qml.RandomLayers(pnp.array([[1.0, 2.0]]), wires=(0, 1))
    qml.RX(x, wires=0)
    qml.RX(-x, wires=0)
    qml.SWAP((0, 1))
    qml.X(0)
    qml.X(0)
    return qml.expval(qml.X(0) + qml.Y(1))

First, we can check the resource information of the QNode without any modifications. Note that level=top would return the same results:

>>> print(qml.specs(circuit, level=0)(0.1).resources)
Total qubit allocations: 2
Total gates: 6
Circuit depth: 6

Gate types:
  RandomLayers: 1
  RX: 2
  SWAP: 1
  PauliX: 2

Measurements:
  expval(Sum(num_wires=2, num_terms=2)): 1

We then check the resources after applying all transforms:

>>> print(qml.specs(circuit, level="device")(0.1).resources)
Total qubit allocations: 2
Total gates: 2
Circuit depth: 1

Gate types:
  RY: 1
  RX: 1

Measurements:
  expval(Sum(num_wires=2, num_terms=2)): 1

We can also notice that SWAP and PauliX are not present in the circuit if we set level=2:

>>> print(qml.specs(circuit, level=2)(0.1).resources)
Total qubit allocations: 2
Total gates: 3
Circuit depth: 3

Gate types:
  RandomLayers: 1
  RX: 2

Measurements:
  expval(Sum(num_wires=2, num_terms=2)): 1

If a QNode with a tape-splitting transform is supplied to the function, with the transform included in the desired transforms, the specs output’s resources field is instead returned as a list with a CircuitSpecs for each resulting tape:

dev = qml.device("default.qubit")
H = qml.Hamiltonian([0.2, -0.543], [qml.X(0) @ qml.Z(1), qml.Z(0) @ qml.Y(2)])
gradient_kwargs = {"shifts": pnp.pi / 4}

@qml.transforms.split_non_commuting
@qml.qnode(dev, diff_method="parameter-shift", gradient_kwargs=gradient_kwargs)
def circuit():
    qml.RandomLayers(qml.numpy.array([[1.0, 2.0]]), wires=(0, 1))
    return qml.expval(H)
>>> from pprint import pprint
>>> pprint(qml.specs(circuit, level="user")())
CircuitSpecs(device_name='default.qubit',
             num_device_wires=None,
             shots=Shots(total_shots=None, shot_vector=()),
             level='user',
             resources=[SpecsResources(gate_types={'RandomLayers': 1},
                                       gate_sizes={2: 1},
                                       measurements={'expval(Prod(num_wires=2, num_terms=2))': 1},
                                       num_allocs=2,
                                       depth=1),
                        SpecsResources(gate_types={'RandomLayers': 1},
                                       gate_sizes={2: 1},
                                       measurements={'expval(Prod(num_wires=2, num_terms=2))': 1},
                                       num_allocs=3,
                                       depth=1)])