qml.allocate

allocate(num_wires, state=AllocateState.ZERO, restored=False)[source]

Dynamically allocates new wires in-line, or as a context manager which also safely deallocates the new wires upon exiting the context.

Parameters:

num_wires (int) – The number of wires to dynamically allocate.

Keyword Arguments:
  • state (Literal["any", "zero"]) – Specifies whether to allocate num_wires in the all-zeros state ("zero") or in any arbitrary state ("any"). The default value is state="zero".

  • restored (bool) – Whether or not the dynamically allocated wires are returned to the same state they started in. restored=True indicates that the user promises to restore the dynamically allocated wires to their original state before being deallocated. restored=False indicates that the user does not promise to restore the dynamically allocated wires before being deallocated. The default value is False.

Returns:

an object, behaving similarly to Wires, that represents the dynamically allocated wires.

Return type:

DynamicRegister

Note

The allocate function can be used as a context manager with automatic deallocation (recommended for most cases) or with manual deallocation via deallocate().

Note

The num_wires argument must be static when capture is enabled.

See also

deallocate()

Example

Using allocate to dynamically request wires returns an array of wires (DynamicRegister) that can be indexed into:

>>> wires = qml.allocate(3)
>>> wires
<DynamicRegister: size=3>
>>> wires[1]
<DynamicWire>

Note that allocating just one wire still requires indexing into:

>>> wire = qml.allocate(1)
>>> wire
<DynamicRegister: size=1>
>>> wire[0]
<DynamicWire>

Most use cases for allocate are covered by using it as a context manager, which ensures that allocation and safe deallocation are controlled within a localized scope.

import pennylane as qml

@qml.qnode(qml.device("default.qubit"))
def circuit():
    qml.H(0)
    qml.H(1)

    with qml.allocate(2, state="zero", restored=False) as new_wires:
        qml.H(new_wires[0])
        qml.H(new_wires[1])

    return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit)())
            0: ──H───────────────────────┤  <Z>
            1: ──H───────────────────────┤
<DynamicWire>: ─╭Allocate──H─╭Deallocate─┤
<DynamicWire>: ─╰Allocate──H─╰Deallocate─┤

Equivalenty, allocate can be used in-line along with deallocate() for manual handling:

new_wires = qml.allocate(2, state="zero", restored=False)
qml.H(new_wires[0])
qml.H(new_wires[1])
qml.deallocate(new_wires)

Efficient wire management

For more complex dynamic allocation in circuits, PennyLane will resolve the dynamic allocation calls in a resource-efficient manner before sending the program to the device. Consider the following circuit, which contains two dynamic allocations within a for loop.

@qml.qnode(qml.device("default.qubit"), mcm_method="tree-traversal")
def circuit():
    qml.H(0)

    for i in range(2):
        with qml.allocate(1, state="zero", restored=True) as new_qubit1:
            with qml.allocate(1, state="any", restored=False) as new_qubit2:
                m0 = qml.measure(new_qubit1[0], reset=True)
                qml.cond(m0 == 1, qml.Z)(new_qubit2[0])
                qml.CNOT((0, new_qubit2[0]))

    return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit)())
            0: ──H─────────────────────╭●───────────────────────╭●─────────────┤  <Z>
<DynamicWire>: ──Allocate──┤↗│  │0⟩────│──────────Deallocate────│──────────────┤
<DynamicWire>: ──Allocate───║────────Z─╰X─────────Deallocate────│──────────────┤
<DynamicWire>: ─────────────║────────║──Allocate──┤↗│  │0⟩──────│───Deallocate─┤
<DynamicWire>: ─────────────║────────║──Allocate───║──────────Z─╰X──Deallocate─┤
                            ╚════════╝             ╚══════════╝

The user-level circuit drawing shows four separate allocations and deallocations (two per loop iteration). However, the circuit that the device receives gets automatically compiled to only use two additional wires (wires labelled 1 and 2 in the diagram below). This is due to the fact that new_qubit1 and new_qubit2 can both be reused after they’ve been deallocated in the first iteration of the for loop:

>>> print(qml.draw(circuit, level="device")())
0: ──H───────────╭●──────────────╭●─┤  <Z>
1: ──┤↗│  │0⟩────│───┤↗│  │0⟩────│──┤
2: ───║────────Z─╰X───║────────Z─╰X─┤
      ╚════════╝      ╚════════╝

Additionally, in circuits that deallocate a wire in “any” state, this wire can be reused as a “zero”. The arbitrary-state wire is reset back to a zero state by introducing a mid-circuit measurement. This is illustrated in the example below, where the first wire allocation is deallocated in an arbitrary state, but the only other dynamic wire allocation in the circuit requires a zero state:

@qml.qnode(qml.device("default.qubit"), mcm_method="device")
def circuit():
    with qml.allocate(1, state="zero", restored=False) as [wire]:
        qml.H(wire)

    with qml.allocate(1, state="zero", restored=False) as [wire]:
        qml.X(wire)

    return qml.expval(qml.Z(0))
>>> print(qml.draw(circuit, level="user")())
<DynamicWire>: ──Allocate──H──Deallocate─┤
<DynamicWire>: ──Allocate──X──Deallocate─┤
            0: ──────────────────────────┤  <Z>
>>> print(qml.draw(circuit, level="device")())
0: ─────────────────┤  <Z>
1: ──H──┤↗│  │0⟩──X─┤

Contents

Using PennyLane

Release news

Development

API

Internals