QCDL#

The Using dwave-gate section provides an introduction to using QCDL to program quantum circuits.

Classes#

class Scope(*qubits: QCDLModuleContainer, use_scope_id: bool = True)[source]#

Bases: QCDLModuleContainer

Qubits to be used for quantum operations.

This subclass of QCDLModuleContainer facilitates use of features related to real-time control flow.

Parameters:
  • *qubits – Qubits that are all or a subset of those of the main procedure decorated by the qcdl() decorator.

  • use_scope_id – Create the identity of the scope, the scope_id attribute.

Examples

This example defines a group of two qubits which it passes into a loop of three operations on each.

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import measure, sx

@qcdl(2)
def scope_example(q0, q1):
    sc = Scope(q0, q1)
    with sc.Repeat(3):
        sx(q0)
        sx(q1)
    measure(q0)
    measure(q1)

qcdl_program = scope_example()
Array(*args: Any, **kwargs: Any) Any[source]#

Instantiate an Array for all qubits in this container.

See:

Array

Break() None[source]#

Exit current While(), DoWhile(), or For() loop.

Raises:

QCDLUserError – If a Break() is encountered outside a loop.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def break_example(q0, q1):
    sc = Scope(q0, q1)
    r0 = sc.Register(name="r0")
    h(q0)
    h(q1)
    measure(q0)
    with sc.For(
        loop_register=r0, initial_value=1, condition=r0<0, update=1
    ):
        x(q1)
        with sc.If(False):
            sc.Break()
        measure(q1)


qcdl_program = break_example()
Continue() None[source]#

Start next iteration of current While(), DoWhile(), or For() loop.

Raises:

QCDLUserError – If a Continue() is encountered outside a loop.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def break_example(q0, q1):
    sc = Scope(q0, q1)
    r0 = sc.Register(name="r0")
    h(q0)
    h(q1)
    measure(q0)
    with sc.For(
        loop_register=r0, initial_value=1, condition=r0<0, update=1
    ):
        x(q1)
        with sc.If(False):
            sc.Continue()
        measure(q1)


qcdl_program = break_example()
DoWhile(condition: RegisterExpression | QCDLModule | bool | str | None = None, all_sources_identical: bool = True) Iterator[None][source]#

Execute context statements while condition is true, and at least once.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

Examples

This example stops running once a value of zero is measured for q0.

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def while_use(q0, q1):
    sc = Scope(q0, q1)
    with sc.DoWhile(condition=q0):
        h(q0)
        measure(q0)
        x(q1)

qcdl_program = while_use()
FixedPointRegister(*args: Any, **kwargs: Any) Any[source]#

Instantiate a FixedPointRegister for all qubits in this container.

See:

FixedPointRegister

For(loop_register: Any, initial_value: Any, condition: Any, update: Any, all_sources_identical: bool = True, _base_name: str = 'for', _base_idx: int | None = None) Iterator[None][source]#

Loop while a specified condition is true.

An almost C-style for loop, similar to a While() loop with minor enhancement.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • loop_register – Register for the loop.

  • initial_value – Assigns an initial value to loop_register.

  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • update – Value by which to increment loop_register in every iteration.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

  • _base_name – Label names. Defaults to “for”.

Examples

This example runs the loop twice.

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure

@qcdl(1)
def for_loop(q0):
    sc = Scope(q0)
    r0 = sc.Register(name="r0")
    r1 = sc.Register(name="r1")
    with sc.For(
        loop_register=r1, initial_value=1, condition=r1<3, update=1
    ):
        h(q0)
        measure(q0, register=r0)

qcdl_program = for_loop()
Goto(label: str) None[source]#

Goto a label.

Label() and Goto() in QCDL are assumed to be used non-deterministically.

Parameters:

label – Name of the label.

Examples

See examples in the Label() method.

If(condition: RegisterExpression | QCDLModule | bool | str | None, all_sources_identical: bool = True, debug: bool = False, true_goto: str | None = None, false_goto: str | None = None, _indentation: int = 3, **kwargs: Any) Iterator[Callable[[...], Any]][source]#

Conditionally execute an expression.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

  • true_goto – Upon completion of a True branch, instead of continuing to the statement that by default should execute after the If body, control moves to the labeled statement. Used for loop control flow.

  • false_goto – Upon completion of a False branch, instead of continuing to the statement that by default should execute after the If body, control moves to the labeled statement. Used for loop control flow.

Examples

A simple example with one If statement:

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x, z

@qcdl(2)
def single_if(q0, q1):
    sc = Scope(q0, q1)
    h(q0)
    h(q1)
    measure(q0)
    with sc.If(True):
        x(q1)
    z(q1)
    measure(q1)

qcdl_program = single_if()

A more complex example with nested conditionals:

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure

@qcdl(2)
def nested_if(q0, q1):
    sc = Scope(q0, q1)
    r1 = sc.FixedPointRegister(0.75, name="r1")
    h(q0)
    h(q1)
    measure(q0)
    with sc.If(True) as Else:
        r1 <<= -0.75
        measure(q1)
        with sc.If(False):
            r1 += 0.1
    with Else():
        r1 += 0.2

qcdl_program = nested_if()
Label(label: str) None[source]#

Place a label at this point in the instruction sequence.

Label() and Goto() in QCDL are assumed to be used non-deterministically.

Parameters:

label – Name of the label.

Examples

The code below uses the Goto() method to return to statements preceded by the Label() method.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import h, mced, measure

@qcdl(1)
def use_goto(q0):
    sc = Scope(q0)
    sc.Label("reset")       # Next line resets the qubit
    q0.reset()

    # The next line represents the quantum algorithm
    h(q0)

    # This section returns to labeled section upon qubit erasure
    erased = q0.Register(name="erased")
    erased <<= 0
    mced(q0, register=erased)
    with q0.If(erased == 1):
        sc.Goto("reset")

    measure(q0)    # Next section of the quantum algorithm

qcdl_program = use_goto()
Register(*args: Any, **kwargs: Any) Any[source]#

Instantiate a Register for all qubits in this container.

See:

Register

Repeat(number: int | Register, ascending: bool = False) Iterator[Register][source]#

Loop over a fixed number of iterations.

All qubits in this QCDLModuleContainer participate in the loop.

Parameters:
  • number – Number of repetitions. Supports integer values greater than \(1\). If a specified register would cause an infinite loop, performs zero iterations.

  • ascending – Increment the counter if True or decrement if False.

Returns:

The counter register.

Return type:

Register

Raises:

QCDLUserError – Integer value less than one, which causes an infinite loop.

Examples

This example repeats three times a branch that inverts a qubit and measures it.

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import measure, x

@qcdl(1)
def repeat_use(q0):
    sc = Scope(q0)
    with sc.Repeat(3, ascending=False):
        x(q0)
        measure(q0)

qcdl_program = repeat_use()
Return() None[source]#

Return from a procedure.

This method is intended for use by developers of QCDL.

While(condition: RegisterExpression | QCDLModule | bool | str | None = None, all_sources_identical: bool = True, _base_name: str = 'while', _base_idx: int | None = None) Iterator[None][source]#

Execute context statements while condition is true.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

  • _base_name – Label names. Defaults to “while”.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure

@qcdl(2)
def while_use(q0, q1):
    sc = Scope(q0)
    r0 = sc.Register(name="r0")
    r1 = sc.Register(name="r1")
    r1 <<= 1
    with sc.While(condition=r1<3):
        h(q0)
        measure(q0, register=r0)
        r1 += 1

qcdl_program = while_use()
all_to_all(send: RegisterExpression, reduce_op: str, **kwargs: Any) None[source]#

Send a 1-bit message from all qubits to all other qubits.

The Signals for Branch Conditions section describes how your QCDL must ensure the information in any qubit’s register is mirrored to all qubits for a conditional statement.

This method implements the following algorithm:

  1. Evaluate the same expression for each qubit

  2. Each qubit broadcasts its bit

  3. The reduce_op converts the set of input bits into one output bit

  4. That bit is then sent back to each qubit

  5. The computed bit is placed on the branch condition of each qubit and is therefore identical for all

Parameters:
  • send – An expression that evaluates to a Boolean.

  • reduce_op – The reduce operator.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def all_to_all_use(q0, q1):
    sc = Scope(q0, q1)
    r1 = sc.Register(name="r1")
    h(q0)
    measure(q0, register=q0.Register(name="r1"))
    sc.all_to_all(send=r1==1, reduce_op="&")
    with sc.If(None):
        x(q1)
        measure(q1)

qcdl_program = all_to_all_use()
Raises:

QCDLUserError – Invalid reduction operator.

See also

Examples in the Signals for Branch Conditions section.

append_table_row(*args: Any, table_name: str | None = None, shape: RecordOutput | None = None, **kwargs: Any) Any[source]#

Return register data.

Result records are used to return register values for your analysis.

Registers can be tricky to use, with interpretation dependent on run-time conditions. This method provides an abstraction layer that facilitates easier interpretation.

This method supports returning multiple tables; each invocation adds one row to one of the tables. Column names in the returned table are:

  • By default, the column name for each register is the register name.

  • If you pass the register as a keyword argument, the column name is the parameter name.

  • You may also use a tuple, (col_name, register), to name the column.

  • If the name of the column is None, data is returned anonymously in an array instead of as a dict.

This method also executes post-processing: If the register is a float, an int is recast in the returned table.

Parameters:
  • table_name – Name of the table to append this row to.

  • shape – Metadata describing the data. If not specified, created based on the registers.

  • *args – Registers or literals that you want returned in this row. You can leave unspecified; such rows present as NaN or None in the returned table.

  • **kwargs – Registers or literals that you want returned in this row. You can leave unspecified; such rows present as NaN or None in the returned table.

Raises:

QCDLUserError – Specified an object that is not a register.

Returns:

Description of the table row.

Return type:

dict

Examples

This example appends dict keys q0 and q1 that each has as its value a dict with key after_swap for which the value is the returned measurements.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import swap

@qcdl(2)
def table_row(q0, q1):
    sc = Scope(q0, q1)
    r1 = sc.Register(name="r1")
    swap(q0, q1)
    measure(q0, register=r1)
    measure(q1, register=r1)
    sc.append_table_row(r1, table_name="after_swap")

qcdl_program = table_row()
arbitrary_function(*args: Any, **kwargs: Any) Any[source]#

Instantiate an arbitrary function for all qubits in this container.

See:

arbitrary_function()

barrier(*args: Any, label: str | None = None) None[source]#

Signal to transpiler a border for combining gates.

The transpiler does not combine gates across a barrier. See the Barrier section for more information.

Parameters:

label – Label for the barrier.

Examples

The code below prevents the transpiler from combining the two sequential Pauli-X gates.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import barrier, measure, x

@qcdl(1)
def set_barrier(q0):
    x(q0)
    barrier(q0)
    x(q0)
    measure(q0)

qcdl_program = set_barrier()
comment(message: Any = None) None[source]#

Insert a comment in the QCDL.

The comment is attached to a single qubit so it is printed just once.

Parameters:

message – Comment to print. If None, prints a blank line.

Examples

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import measure, x
from dwave.gate.utils.display import print_qcdl

@qcdl(2)
def add_comment(q0, q1):
    x(q0)
    q1.comment("This is my comment")
    measure(q0)

qcdl_program = add_comment()
print_qcdl(qcdl_program)

The code above prints the following QCDL.

begin quantum
   x([q0], q0)
   # This is my comment
   measure([q0], q0, log=True)
end quantum
cpu(expression: str, **kwargs: Any) None[source]#

Add a CPU statement.

CPU statements are classical operations that run in parallel to quantum operations, for example, operations on registers.

This parameter is intended for use by developers of QCDL. Do not call this method directly, use the Register or FixedPointRegister classes instead.

Parameters:

expression – A CPU expression.

Examples

The example below generates a CPU statement seen in the final line of the output.

from dwave.gate.qcdl import qcdl, Register
from dwave.gate.qcdl.operations import measure, x
from dwave.gate.utils.display import print_qcdl

@qcdl(1)
def cpu_example(q0):
    x(q0)
    r1 = Register(q0, name="r1")
    measure(q0)
    r1 += 1         # This is a CPU instruction

qcdl_program = cpu_example()
print_qcdl(qcdl_program)

The code above prints the following QCDL.

begin quantum
    x([q0], q0)
    q0.allocate_memory("r1", initial_value=0, ...
    measure([q0], q0, log=True)
    q0.cpu("r1 += 1", scope_id=None)
end quantum
get_next_index_in_proc(name: str) int[source]#

Return an index that is unique within the current procedure.

This method is mostly intended for use by developers of QCDL.

Parameters:

name – Namespace for the index.

Returns:

A unique index.

Return type:

int

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import measure

@qcdl(1)
def unique(q0):
    sc = Scope(q0)
    print(q0.get_next_index_in_proc("q"))
    print(q0.get_next_index_in_proc("q"))
    measure(q0)

qcdl_program = unique()

The code above prints two unique indices for q.

0
1
property op_key: str | None[source]#

Intended to represent the contents of a QCDLModuleContainerBase besides its qcdl_modules, used for procedure names when this is an argument.

property qcdl_modules: list[QCDLModule][source]#

The QCDL modules, typically qubits, in this scope.

Examples

This example uses the qcdl_modules property to add a comment in generated QCDL with a qubit’s name. In practice, you would likely use the simpler name property (i.e., q0.name)

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.utils.display import print_qcdl
from dwave.gate.qcdl.operations import measure, sx

@qcdl(2)
def modules_property(q0, q1):
    sc = Scope(q0, q1)
    sx(q0)
    measure(q0)
    sc.comment(f"measure {sc.qcdl_modules[0].name}")

qcdl_program = modules_property()
print_qcdl(qcdl_program)

The code above prints the following QCDL.

begin quantum
    sx([q0], q0)
    measure([q0], q0, log=True)
    # measure q0
end quantum
Type:

list[QCDLModule]

property scope_id: int | None[source]#

Identity of the scope.

Examples:

from dwave.gate.qcdl import qcdl, Scope

@qcdl(2)
def scope_id_example(q0, q1):
    sc = Scope(q0, q1, use_scope_id=False)
    print(sc.scope_id)

qcdl_program = scope_id_example()

The code above creates a scope wihtout an identifier.

None
serialize() list[str][source]#

Convert the object into something (jsonifiable) that can go to the compiler.

set_procedure(new_proc: Procedure) None[source]#

Rewrap the QCDLModule objects with the new procedure

The default implementation assumes that qcdl_modules is a list.

Parameters:

new_proc (Procedure) – the new Procedure

sync(*args: Any, **kwargs: Any) None[source]#

Synchronize all qubits.

Synchronizes qubits in the container along with any passed in as arguments.

Examples

The code below ensures that all operations before the sync are completed before any operations after the sync are started.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import measure, x

@qcdl(2)
def add_sync1(q0, q1):
    sc = Scope(q0, q1)
    x(q0)
    sc.sync()
    measure(q1)

qcdl_program = add_sync1()

The above is equivalent to the following use of sync on the qubit.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import x

@qcdl(2)
def add_sync2(q0, q1):
    x(q0)
    q0.sync(q1)
    x(q1)

qcdl_program = add_sync2()

Utilities#

print_qcdl(qcdl: QCDLProgram | Mapping[str, Any], to_Display: bool = True, blacken: bool = False, filename: str | None = None) QCDLV2 | None[source]#

Print a QCDl program.

Parameters:
  • qcdl – A QCDL model or mapping. Typically created by instantiating a Python function containing QCDL instructions and annotated with the qcdl() decorator.

  • to_Display – If True, outputs the string to an IPython terminal. Outside of a Jupyter notebook, equivalent to a print statement. Set to False to return the string.

  • blacken – Apply the Black Python formatter to the input QCDL.

  • filename – File name to write the string to.

Returns:

If displaying, the return is None; otherwise, the “qcdlv2” string.

Examples

See the examples in the display_qcdl() function.

display_qcdl(qcdl: QCDLProgram | Mapping[str, Any], **kwargs: Any) None[source]#

Display formatted QCDL in a Jupyter notebook or similar.

Creates an IPython Code object.

Parameters:

qcdl – A QCDL model or mapping. Typically created by instantiating a Python function containing QCDL instructions and annotated with the qcdl() decorator.

Examples

from dwave.gate.qcdl import qcdl
from dwave.gate.utils.display import display_qcdl

@qcdl(1)
def display_program(q0):

    q0.h()
    q0.measure()

qcdl_program = display_program()
display_qcdl(qcdl_program)

The code above displays the following QCDL program.

begin quantum
    q0.h()
    q0.measure()
end quantum
class LogicalOutcomeToInteger(*values)[source]#

Bases: IntEnum

Maps the numerical encoding to an erasure bitstring representation. .. rubric:: Examples

from dwave.gate.qcdl import LogicalOutcomeToInteger

for val in [LogicalOutcomeToInteger.ONE, LogicalOutcomeToInteger.SPLAT]:
    print(val, val.as_bit)

The code above outputs the following values.

1 1
-1 *
property as_bit: str[source]#

Return the erasure bitstring representation for the numerical encoding.

Returns:

A “0”, “1”, or “*”.

Return type:

str

Examples

See examples for the LogicalOutcomeToInteger class.

static from_outcome(val: str | int | float) LogicalOutcomeToInteger[source]#

Instantiate a LogicalOutcomeToInteger from various inputs.

Parameters:

val – Input value.

Returns:

Erasure bitstring representation.

Return type:

LogicalOutcomeToInteger

Examples

See examples for the LogicalOutcomeToInteger class.

Mirroring Utilities#

Implementations of various simple algorithms in QCDL.

mirror_bool_register(sender: QCDLModule, register: Register, receivers: list[QCDLModule] | None = None) None[source]#

Mirror a Boolean register to registers associated with other qubits.

May be useful for a Register object used for the outcome of a mced() operation. See the Mirroring section to learn about mirroring.

This function requires the following:

  • Allocate the Register to all the relevant qubits.

  • A value of either \(0\) or \(1\) in the register associated with the qubit specified by the sender parameter.

Parameters:
  • sender – Qubit associated with the register you are assigning the measurement outcome to.

  • registerRegister object to mirror.

  • receivers – Qubits that need their associated registers updated. If not specified, defaults to the other qubits associated with the register selected by the Register parameter.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.implementations import mirror_bool_register
from dwave.gate.qcdl.operations import h, mced, x

@qcdl(3)
def mirror_bool_example(q0, q1, q2):
    sc = Scope(q0, q1, q2)
    h(q0)
    r0 = Register(sc.qcdl_modules, name="r0")
    receivers = [qubit for qubit in sc.qcdl_modules if qubit != q0]
    mced(q0, register=r0)
    mirror_bool_register(sender=q0, register=r0, receivers=receivers)

qcdl_program = mirror_bool_example()
mirror_measurement_register(sender: QCDLModule, register: Register, receivers: list[QCDLModule] | None = None) None[source]#

Mirror a two-bit qubit measurement to registers associated with other qubits.

May be useful for a Register object used for the outcome of a measure() operation. See the Mirroring section to learn about mirroring.

This function requires the following:

  • Allocate the Register to all the relevant qubits.

  • The measurement outcome is written to the register associated with the qubit specified by the sender parameter.

  • The measurement outcome is two bits (a dual-rail qubit measurement).

Parameters:
  • sender – Qubit associated with the register you are assigning the measurement outcome to.

  • registerRegister object to mirror.

  • receivers – Qubits that need their associated registers updated. If not specified, defaults to the other qubits associated with the register selected by the Register parameter.

Examples

This is an artificial example to demonstrate usage (see the mirror parameter in the measure() operation).

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.implementations import mirror_measurement_register
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(3)
def mirror_measurement_example(q0, q1, q2):
    sc = Scope(q0, q1, q2)
    h(q0)
    r0 = Register(sc.qcdl_modules, name="r0")
    receivers = [qubit for qubit in sc.qcdl_modules if qubit != q0]
    measure(q0, register=r0)
    mirror_measurement_register(sender=q0, register=r0, receivers=receivers)

qcdl_program = mirror_measurement_example()

Decorators#

arbitrary_function(modules: Sequence[QCDLModule], in_dtype: Type[int] | Type[float], out_dtype: Type[int] | Type[float], name: str | None = None, scope_id: int | None = None) Callable[[Callable[[np.ndarray], Sequence[int | float] | np.ndarray]], Callable[..., RegisterExpression]][source]#

Decorator for creating an interpolation table.

You can use this decorator to create an arbitrary function, such as a trigonometric function.

Parameters:
  • modules – Modules, typically qubits, for which to create the arbitrary function.

  • in_dtype – Type of numerical value of the input register. The range of supported values depends on the selected register (see the Register and FixedPointRegister classes).

  • out_dtype – Type of numerical value of the output register. The range of supported values depends on the selected register.

  • name – Name of the interpolation table. Defaults to None.

  • scope_id – Identity of the Scope object the arbitrary function is derived from.

Examples

This example creates a trigonometric function.

import numpy as np
from dwave.gate.qcdl import arbitrary_function, qcdl, Scope
from dwave.gate.qcdl.operations import h

@qcdl(2)
def an_arbitrary_func(q0, q1):

    @arbitrary_function(
        modules=[q0, q1],
        in_dtype=float,
        out_dtype=float,
        name="my_func"
    )
    def sin_half_x(x):
        return np.sin(np.pi * x / 2)

    sc = Scope(q0, q1)

    r0 = sc.Register(name="r0")

    r1 = sc.FixedPointRegister(name="r1", initial_value=0.5)
    r2 = sc.FixedPointRegister(name="r2")
    r2 <<= sin_half_x(r1)   # set r2 to sin(pi * r1 / 2) = sin(pi/4)

    h(q0)
    measure(q0, register=r0)

qcdl_program = an_arbitrary_func()
procedure(f: Any, proc_name: str | None = None, use_signature_modules: bool = True, validate_reused_procedures: bool = True) Any[source]#

Decorator for creating a QCDL procedure.

Parameters:
  • f – Decorated function.

  • proc_name – Procedure name. Every procedure must have a unique name, which is determined from the decorated method’s signature. If unspecified, generates a “mangled” name.

  • use_signature_modules – If True, bases procedure name on module name. If False, the procedure is defined by its qcdl_modules attribute.

  • validate_reused_procedures

    If False and if the procedure has been seen before, as determined by its mangled name, it is automatically reused. Where safe to do so, set to False to save some time.

    If arguments change from one invocation of a procedure to the next (from the Python metaprogramming stage), the contents of the procedure may change. If this is a risk for your procedure, set to True.

Examples

This example reuses a defined procedure while switching the qubits given as parameters.

from dwave.gate.qcdl import procedure, qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, swap

@procedure
def my_procedure(qa, qb, r):
    h(qa)
    qa.sync(qb)
    qb.h()
    swap(qb, qa)
    measure(qa, register=r)

@qcdl(2)
def use_procedure(q0, q1):
    sc = Scope(q0, q1)
    r0 = sc.Register(name="r0")
    r1 = sc.Register(name="r1")
    my_procedure(q0, q1, r0)
    my_procedure(q1, q0, r1)

qcdl_program = use_procedure()
qcdl(num_qubits: int | None = None, environment: Environment | None = None, machine: Machine | None = None, next_indices: dict[str, int] | None = None, to_qcdlv2: Literal[False] = False, validate_non_deterministic_qubits_mid: bool = True, validate_non_deterministic_qubits_end: bool = True) Callable[[Callable[[...], None]], Callable[[...], QCDLProgram]][source]#
qcdl(num_qubits: int | None = None, environment: Environment | None = None, machine: Machine | None = None, next_indices: dict[str, int] | None = None, to_qcdlv2: Literal[True] = True, validate_non_deterministic_qubits_mid: bool = True, validate_non_deterministic_qubits_end: bool = True) Callable[[Callable[[...], None]], Callable[[...], str]]

Decorator to construct a QCDL program.

The decorated function returns a Pydantic model that you can submit to a solver in the Leap service, as described in the Submitting Programs section.

Parameters:
  • num_qubits – Number of qubits to generate. If you do not specify a number of qubits, infers qubits from the signature of the decorated function: any q<N> arguments, where <N> is an integer, are considered qubits. Generated qubits are passed in to the decorated function through keyword arguments, so unless the decorated function accepts **kwargs, it must declare a q<N> parameter for each generated qubit.

  • environment – Environment. The number of qubits supplied is the full set supported by the environment. This parameter is intended for use by developers of QCDL.

  • machine – If a machine is provided, the machine supplies system instances instead of the QCDLModule instance. This parameter is intended for use by developers of QCDL.

  • next_indices – Values from which to start the circuit’s indices. This facilitates uniqueness across compiler “visitors”.

  • to_qcdlv2 – If True, returns v2 format. Version v2 can hold less information than v3, so this is mostly useful for visualization.

  • validate_non_deterministic_qubits_mid – If True, validate non-deterministic qubits in mid-circuit statements.

  • validate_non_deterministic_qubits_end – If True, validate non-deterministic qubits at the end of the circuit.

Examples

The first example specifies the number of qubits (three) in the decorator.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import cx, measure, rx

@qcdl(3)
def specify_num_qubits(q0, q1, q2, my_angle=0):
    r0 = q0.FixedPointRegister(name="r0", initial_value=my_angle)
    rx(q0, r0)
    cx(q0, q1)
    cx(q1, q2)
    measure(q0)
    measure(q1)
    measure(q2)

qcdl_model = specify_num_qubits(my_angle=0.5)

The next example infers the number of qubits (two) from the q0, q1 arguments in the decorated function.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import cx, h, measure

@qcdl()
def my_bell_circuit(q0, q1):
    h(q0)
    cx(q0, q1)
    measure(q0)
    measure(q1)

qcdl_model = my_bell_circuit()

QCDL Development#

These classes are of interest mostly to developers of QCDL.

class QCDLModule(module_name: str, proc: Procedure)[source]#

Bases: QCDLModuleContainer

Wrapper around a Procedure instance.

Important

This class is intended for use by developers of QCDL to append instructions to a module, typically a qubit.

A QCDLModule instance is associated with a specific Procedure instance. This means that if a procedure is entered, a new QCDLModule is instantiated.

Important

This class is intended for use by developers of QCDL to append instructions to a module, typically a qubit.

This class is designed to provide an intuitive and convenient way to call the add_statement() method. It does not perform validation and does not raise an error for unknown or invalid methods and positional/keyword arguments. Use the operations methods instead, where applicable.

Parameters:
  • module_name – Name of the module, typically a qubit.

  • procProcedure instance this QCDLModule is in.

Examples

This example shows a typical use of operations methods, which indirectly instantiates a QCDLModule instance.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import h, measure

@qcdl(1)
def direct_op(q0):
    h(q0)
    print(q0.qcdl_module_name)      # Added for the example output
    measure(q0)

qcdl_program = direct_op()

The code above prints the qcdl_module_name property of the QCDLModule class.

q0

This example demonstrates explicit use of the add_statement() method.

from dwave.gate.qcdl import procedure, qcdl
from dwave.gate.qcdl.operations import h, measure

@procedure
def my_procedure(qa, proc_name="proc1"):
    qa.h()

@qcdl(1)
def using_qcdlmodule(q0):
    my_procedure(q0)
    q0.procedure.add_statement(
        qubit="q0", op="measure", args=None, kwargs=None
    )

qcdl_program = using_qcdlmodule()
get_other_qcdl_module(module_name: str) QCDLModule[source]#

Create an ad hoc QCDLModule in the same procedure.

Applying instructions to this will automatically register it as one of the modules used by this procedure.

Parameters:

module_name – Name of the other module.

Raises:
Returns:

The other module is in this same procedure scope.

Return type:

QCDLModule

property name: str[source]#

Name of this instance.

Examples

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import h, measure

@qcdl(1)
def name_example(q0):
    h(q0)
    print(q0.name)      # Added for the example output
    measure(q0)

name_example()

The code above prints the module name.

q0
Type:

str

one_to_all(destinations: Scope | QCDLModule | Sequence[QCDLModule], send: RegisterExpression, **kwargs: Any) None[source]#

Send a bit from one qubit to other qubits.

See the Signals for Branch Conditions section for a description and examples of signals.

Parameters:
  • destinations – The qubits to send the message to. The bit is placed on each qubit’s branch condition to be used in a conditional statement. Statements are tagged with the scope_id if a Scope is specified.

  • send – The expression to compute the bit on the sender.

Raises:

QCDLUserError – If destinations does not hold any qubits.

property qcdl_module_name: str[source]#

Return the name of the module.

Examples

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import h, measure

@qcdl(1)
def qcdl_module_name(q0):
    h(q0)
    print(q0.qcdl_module_name)      # Added for the example output
    measure(q0)

qcdl_module_name()

The code above prints the module name.

q0
property qcdl_modules: tuple[QcdlModule][source]#

The QCDLModule this container holds.

Examples

This is an artificial example; see the example for the name for comparison.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import h, measure

@qcdl(1)
def qcdl_modules(q0):
    h(q0)
    print(q0.qcdl_modules[0].name)      # Added for the example output
    measure(q0)

qcdl_modules()

The code above prints the module name.

q0
property signal: str[source]#

Signal that other qubits can condition a branch upon.

See the Signals for Branch Conditions section for a description and examples of signals and the Conditionals section for an introduction to conditioned execution.

class QCDLModuleContainer[source]#

Bases: QCDLModuleContainerBase

Base class for the QCDLModule and Scope classes.

Important

This class is not meant to be instantiated directly. Typical QCDL programs use the Scope class.

Defines shared methods such as If(), comment(), and sync().

Array(*args: Any, **kwargs: Any) Any[source]#

Instantiate an Array for all qubits in this container.

See:

Array

Break() None[source]#

Exit current While(), DoWhile(), or For() loop.

Raises:

QCDLUserError – If a Break() is encountered outside a loop.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def break_example(q0, q1):
    sc = Scope(q0, q1)
    r0 = sc.Register(name="r0")
    h(q0)
    h(q1)
    measure(q0)
    with sc.For(
        loop_register=r0, initial_value=1, condition=r0<0, update=1
    ):
        x(q1)
        with sc.If(False):
            sc.Break()
        measure(q1)


qcdl_program = break_example()
Continue() None[source]#

Start next iteration of current While(), DoWhile(), or For() loop.

Raises:

QCDLUserError – If a Continue() is encountered outside a loop.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def break_example(q0, q1):
    sc = Scope(q0, q1)
    r0 = sc.Register(name="r0")
    h(q0)
    h(q1)
    measure(q0)
    with sc.For(
        loop_register=r0, initial_value=1, condition=r0<0, update=1
    ):
        x(q1)
        with sc.If(False):
            sc.Continue()
        measure(q1)


qcdl_program = break_example()
DoWhile(condition: RegisterExpression | QCDLModule | bool | str | None = None, all_sources_identical: bool = True) Iterator[None][source]#

Execute context statements while condition is true, and at least once.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

Examples

This example stops running once a value of zero is measured for q0.

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def while_use(q0, q1):
    sc = Scope(q0, q1)
    with sc.DoWhile(condition=q0):
        h(q0)
        measure(q0)
        x(q1)

qcdl_program = while_use()
FixedPointRegister(*args: Any, **kwargs: Any) Any[source]#

Instantiate a FixedPointRegister for all qubits in this container.

See:

FixedPointRegister

For(loop_register: Any, initial_value: Any, condition: Any, update: Any, all_sources_identical: bool = True, _base_name: str = 'for', _base_idx: int | None = None) Iterator[None][source]#

Loop while a specified condition is true.

An almost C-style for loop, similar to a While() loop with minor enhancement.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • loop_register – Register for the loop.

  • initial_value – Assigns an initial value to loop_register.

  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • update – Value by which to increment loop_register in every iteration.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

  • _base_name – Label names. Defaults to “for”.

Examples

This example runs the loop twice.

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure

@qcdl(1)
def for_loop(q0):
    sc = Scope(q0)
    r0 = sc.Register(name="r0")
    r1 = sc.Register(name="r1")
    with sc.For(
        loop_register=r1, initial_value=1, condition=r1<3, update=1
    ):
        h(q0)
        measure(q0, register=r0)

qcdl_program = for_loop()
Goto(label: str) None[source]#

Goto a label.

Label() and Goto() in QCDL are assumed to be used non-deterministically.

Parameters:

label – Name of the label.

Examples

See examples in the Label() method.

If(condition: RegisterExpression | QCDLModule | bool | str | None, all_sources_identical: bool = True, debug: bool = False, true_goto: str | None = None, false_goto: str | None = None, _indentation: int = 3, **kwargs: Any) Iterator[Callable[[...], Any]][source]#

Conditionally execute an expression.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

  • true_goto – Upon completion of a True branch, instead of continuing to the statement that by default should execute after the If body, control moves to the labeled statement. Used for loop control flow.

  • false_goto – Upon completion of a False branch, instead of continuing to the statement that by default should execute after the If body, control moves to the labeled statement. Used for loop control flow.

Examples

A simple example with one If statement:

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x, z

@qcdl(2)
def single_if(q0, q1):
    sc = Scope(q0, q1)
    h(q0)
    h(q1)
    measure(q0)
    with sc.If(True):
        x(q1)
    z(q1)
    measure(q1)

qcdl_program = single_if()

A more complex example with nested conditionals:

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure

@qcdl(2)
def nested_if(q0, q1):
    sc = Scope(q0, q1)
    r1 = sc.FixedPointRegister(0.75, name="r1")
    h(q0)
    h(q1)
    measure(q0)
    with sc.If(True) as Else:
        r1 <<= -0.75
        measure(q1)
        with sc.If(False):
            r1 += 0.1
    with Else():
        r1 += 0.2

qcdl_program = nested_if()
Label(label: str) None[source]#

Place a label at this point in the instruction sequence.

Label() and Goto() in QCDL are assumed to be used non-deterministically.

Parameters:

label – Name of the label.

Examples

The code below uses the Goto() method to return to statements preceded by the Label() method.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import h, mced, measure

@qcdl(1)
def use_goto(q0):
    sc = Scope(q0)
    sc.Label("reset")       # Next line resets the qubit
    q0.reset()

    # The next line represents the quantum algorithm
    h(q0)

    # This section returns to labeled section upon qubit erasure
    erased = q0.Register(name="erased")
    erased <<= 0
    mced(q0, register=erased)
    with q0.If(erased == 1):
        sc.Goto("reset")

    measure(q0)    # Next section of the quantum algorithm

qcdl_program = use_goto()
Register(*args: Any, **kwargs: Any) Any[source]#

Instantiate a Register for all qubits in this container.

See:

Register

Repeat(number: int | Register, ascending: bool = False) Iterator[Register][source]#

Loop over a fixed number of iterations.

All qubits in this QCDLModuleContainer participate in the loop.

Parameters:
  • number – Number of repetitions. Supports integer values greater than \(1\). If a specified register would cause an infinite loop, performs zero iterations.

  • ascending – Increment the counter if True or decrement if False.

Returns:

The counter register.

Return type:

Register

Raises:

QCDLUserError – Integer value less than one, which causes an infinite loop.

Examples

This example repeats three times a branch that inverts a qubit and measures it.

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import measure, x

@qcdl(1)
def repeat_use(q0):
    sc = Scope(q0)
    with sc.Repeat(3, ascending=False):
        x(q0)
        measure(q0)

qcdl_program = repeat_use()
Return() None[source]#

Return from a procedure.

This method is intended for use by developers of QCDL.

While(condition: RegisterExpression | QCDLModule | bool | str | None = None, all_sources_identical: bool = True, _base_name: str = 'while', _base_idx: int | None = None) Iterator[None][source]#

Execute context statements while condition is true.

All qubits in this QCDLModuleContainer participate in the conditional. See the Conditionals section for information on conditional branching.

Parameters:
  • condition – Branch condition for the qubits. The Conditionals section describes the supported conditions.

  • all_sources_identical – For conditional statements with multiple qubits, if the same condition computed on each qubit yields the same result, skip broadcasting the message.

  • _base_name – Label names. Defaults to “while”.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure

@qcdl(2)
def while_use(q0, q1):
    sc = Scope(q0)
    r0 = sc.Register(name="r0")
    r1 = sc.Register(name="r1")
    r1 <<= 1
    with sc.While(condition=r1<3):
        h(q0)
        measure(q0, register=r0)
        r1 += 1

qcdl_program = while_use()
all_to_all(send: RegisterExpression, reduce_op: str, **kwargs: Any) None[source]#

Send a 1-bit message from all qubits to all other qubits.

The Signals for Branch Conditions section describes how your QCDL must ensure the information in any qubit’s register is mirrored to all qubits for a conditional statement.

This method implements the following algorithm:

  1. Evaluate the same expression for each qubit

  2. Each qubit broadcasts its bit

  3. The reduce_op converts the set of input bits into one output bit

  4. That bit is then sent back to each qubit

  5. The computed bit is placed on the branch condition of each qubit and is therefore identical for all

Parameters:
  • send – An expression that evaluates to a Boolean.

  • reduce_op – The reduce operator.

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import h, measure, x

@qcdl(2)
def all_to_all_use(q0, q1):
    sc = Scope(q0, q1)
    r1 = sc.Register(name="r1")
    h(q0)
    measure(q0, register=q0.Register(name="r1"))
    sc.all_to_all(send=r1==1, reduce_op="&")
    with sc.If(None):
        x(q1)
        measure(q1)

qcdl_program = all_to_all_use()
Raises:

QCDLUserError – Invalid reduction operator.

See also

Examples in the Signals for Branch Conditions section.

append_table_row(*args: Any, table_name: str | None = None, shape: RecordOutput | None = None, **kwargs: Any) Any[source]#

Return register data.

Result records are used to return register values for your analysis.

Registers can be tricky to use, with interpretation dependent on run-time conditions. This method provides an abstraction layer that facilitates easier interpretation.

This method supports returning multiple tables; each invocation adds one row to one of the tables. Column names in the returned table are:

  • By default, the column name for each register is the register name.

  • If you pass the register as a keyword argument, the column name is the parameter name.

  • You may also use a tuple, (col_name, register), to name the column.

  • If the name of the column is None, data is returned anonymously in an array instead of as a dict.

This method also executes post-processing: If the register is a float, an int is recast in the returned table.

Parameters:
  • table_name – Name of the table to append this row to.

  • shape – Metadata describing the data. If not specified, created based on the registers.

  • *args – Registers or literals that you want returned in this row. You can leave unspecified; such rows present as NaN or None in the returned table.

  • **kwargs – Registers or literals that you want returned in this row. You can leave unspecified; such rows present as NaN or None in the returned table.

Raises:

QCDLUserError – Specified an object that is not a register.

Returns:

Description of the table row.

Return type:

dict

Examples

This example appends dict keys q0 and q1 that each has as its value a dict with key after_swap for which the value is the returned measurements.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import swap

@qcdl(2)
def table_row(q0, q1):
    sc = Scope(q0, q1)
    r1 = sc.Register(name="r1")
    swap(q0, q1)
    measure(q0, register=r1)
    measure(q1, register=r1)
    sc.append_table_row(r1, table_name="after_swap")

qcdl_program = table_row()
arbitrary_function(*args: Any, **kwargs: Any) Any[source]#

Instantiate an arbitrary function for all qubits in this container.

See:

arbitrary_function()

barrier(*args: Any, label: str | None = None) None[source]#

Signal to transpiler a border for combining gates.

The transpiler does not combine gates across a barrier. See the Barrier section for more information.

Parameters:

label – Label for the barrier.

Examples

The code below prevents the transpiler from combining the two sequential Pauli-X gates.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import barrier, measure, x

@qcdl(1)
def set_barrier(q0):
    x(q0)
    barrier(q0)
    x(q0)
    measure(q0)

qcdl_program = set_barrier()
comment(message: Any = None) None[source]#

Insert a comment in the QCDL.

The comment is attached to a single qubit so it is printed just once.

Parameters:

message – Comment to print. If None, prints a blank line.

Examples

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import measure, x
from dwave.gate.utils.display import print_qcdl

@qcdl(2)
def add_comment(q0, q1):
    x(q0)
    q1.comment("This is my comment")
    measure(q0)

qcdl_program = add_comment()
print_qcdl(qcdl_program)

The code above prints the following QCDL.

begin quantum
   x([q0], q0)
   # This is my comment
   measure([q0], q0, log=True)
end quantum
cpu(expression: str, **kwargs: Any) None[source]#

Add a CPU statement.

CPU statements are classical operations that run in parallel to quantum operations, for example, operations on registers.

This parameter is intended for use by developers of QCDL. Do not call this method directly, use the Register or FixedPointRegister classes instead.

Parameters:

expression – A CPU expression.

Examples

The example below generates a CPU statement seen in the final line of the output.

from dwave.gate.qcdl import qcdl, Register
from dwave.gate.qcdl.operations import measure, x
from dwave.gate.utils.display import print_qcdl

@qcdl(1)
def cpu_example(q0):
    x(q0)
    r1 = Register(q0, name="r1")
    measure(q0)
    r1 += 1         # This is a CPU instruction

qcdl_program = cpu_example()
print_qcdl(qcdl_program)

The code above prints the following QCDL.

begin quantum
    x([q0], q0)
    q0.allocate_memory("r1", initial_value=0, ...
    measure([q0], q0, log=True)
    q0.cpu("r1 += 1", scope_id=None)
end quantum
get_next_index_in_proc(name: str) int[source]#

Return an index that is unique within the current procedure.

This method is mostly intended for use by developers of QCDL.

Parameters:

name – Namespace for the index.

Returns:

A unique index.

Return type:

int

Examples

from dwave.gate.qcdl import qcdl, Scope
from dwave.gate.qcdl.operations import measure

@qcdl(1)
def unique(q0):
    sc = Scope(q0)
    print(q0.get_next_index_in_proc("q"))
    print(q0.get_next_index_in_proc("q"))
    measure(q0)

qcdl_program = unique()

The code above prints two unique indices for q.

0
1
property scope_id: int | None[source]#

Identity of the container.

Examples

See example for the scope_id property.

sync(*args: Any, **kwargs: Any) None[source]#

Synchronize all qubits.

Synchronizes qubits in the container along with any passed in as arguments.

Examples

The code below ensures that all operations before the sync are completed before any operations after the sync are started.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import measure, x

@qcdl(2)
def add_sync1(q0, q1):
    sc = Scope(q0, q1)
    x(q0)
    sc.sync()
    measure(q1)

qcdl_program = add_sync1()

The above is equivalent to the following use of sync on the qubit.

from dwave.gate.qcdl import qcdl
from dwave.gate.qcdl.operations import x

@qcdl(2)
def add_sync2(q0, q1):
    x(q0)
    q0.sync(q1)
    x(q1)

qcdl_program = add_sync2()
class QCDLProcedureDef(*, statements: list[QCDLStatement], statement_hash: str | None = None, signature: QCDLSignature)[source]#

Bases: BaseModel

Pydantic model for a compiled procedure definition.

Nesting QCDLStatement and QCDLSignature means that calling QCDLProcedureDef.model_validate(proc_dict) recursively validates the entire sub-tree.

model_config: ClassVar[ConfigDict] = {'extra': 'forbid'}[source]#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class QCDLProgram(*, program: QCDLProcedureDef, procedures: dict[str, QCDLProcedureDef], next_indices: dict[str, int], **extra_data: Any)[source]#

Bases: BaseModel

Pydantic model for a QCDL program.

Important

This class is intended for use by developers of QCDL, though the interfaces are of broader usage.

Use the qcdl decorator to generate an instance of this class.

The extra="allow" setting in model_config ensures that implementation-specific keys of various compiler versions are preserved through round trips.

model_config: ClassVar[ConfigDict] = {'extra': 'allow'}[source]#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

class QCDLStatement(*, op: str | None = None, qubit: ~dwave.gate.qcdl.models.QCDLModuleName | None = None, args: list[~typing.Any] = <factory>, kwargs: dict[str, ~typing.Any] = <factory>, caller_qubits: list[~dwave.gate.qcdl.models.QCDLModuleName] = <factory>, **extra_data: ~typing.Any)[source]#

Bases: BaseModel

Pydantic model for a single QCDL statement.

Every declared field is optional (defaults are provided for all fields). The extra="allow" configuration preserves compiler-specific keys (e.g. card_name) so they round-trip transparently through model_dump().

Set the exclude_unset=True parameter for the model_dump() method to reproduce the sparse dict representation that add_statement() method generates (fields that are not explicitly set are omitted).

property cargs: None[source]#

Always None (Qiskit-style interface).

property condition: Any[source]#

The condition value for conditional operations.

Valid for op values "If", "c_if", and "c_if_else". Raises QCDLInternalError for any other op.

property is_procedure_call: bool[source]#

True when qubit is None (bare procedure call).

model_config: ClassVar[ConfigDict] = {'extra': 'allow', 'validate_assignment': True}[source]#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

property modules: list[QCDLModuleName][source]#

All modules referenced by this statement.

Alias for qubits. See that attribute for the derivation order and sources.

property non_module_args: list[Any][source]#

Arguments that are not qubit/coupler/module references.

Equivalent to the positional arguments after stripping out any module-name strings (mirrors the filtering that the legacy Statement._args attribute performed).

property qargs: list[int][source]#

Integer qubit indices derived from modules.

property qubit_name: str | None[source]#

Alias for qubit that returns the plain string module name.

property qubit_names: list[str][source]#

Names of all qubits referenced by this statement, from all sources.

Unlike the qubits property, which returns QCDLModuleName objects for all modules (including couplers), this property returns plain string names for qubits only.

Equivalent to [m.name for m in self.modules if m.is_qubit].

property qubits: list[QCDLModuleName][source]#

All modules (qubits and couplers) referenced by this statement.

Derived from every source in insertion order, without duplicates: the qubit field, caller_qubits (for procedure calls), kwargs["qubits"], module-like args, and op-specific logic for If/Else/Endif.

This is the primary API for callers that need to know which modules a statement touches. Always current—recomputed on each access so mutations via reassign_arg() / reassign_kwarg() are reflected immediately.

reassign_arg(i: int, new_value: Any) None[source]#

Replace the \(i\)-th entry in the raw args list.

reassign_kwarg(name: str, new_value: Any) None[source]#

Set or replace the keyword argument name in kwargs argument.

simple_desc() str[source]#

Return a short human-readable description of the statement.

The circuit coordinates data across the qubits/procedures

class QCDLCircuit(environment: Environment | None = None, next_indices: dict[str, int] | None = None, validate_non_deterministic_qubits_mid: bool = True, validate_non_deterministic_qubits_end: bool = True)[source]#

Bases: IndexerMixin

Coordinates and stores data across qubits and procedures.

Also prepares the data structure for transmission to a compiler.

The code below shows a simple use case.

qcdl = QCDLCircuit(environment)

# create the objects used for invoking statements
qmods = qcdl.initialize_modules()

# call whatever instructions on those QCDLModules
qmods["q0"].x()
qmods["q0"].measure()

# convert this data structure to JSON and pass it to the compiler
qcdl_model = qcdl.to_model()
property all_modules: dict[str, QCDLModule] | None[source]#

A dict of all QCDL modules in the system.

These objects are not necessarily ready to be used as-is in a circuit, needing to be rewrapped based on the procedure. Use the get_other_qcdl_module() instead of accessing this property directly.

Returns:

QCDL modules objects.

available_outputs(qubits: QCDLModule | Sequence[QCDLModule], category: str) set[str] | None[source]#

Outputs that are available for one or more qubits.

For a list of qubits, searches for an output that is available for all the qubits.

Parameters:
  • qubits – Spaces to search.

  • category – DYN or GOF types of registers.

Raises:

QCDLUserError – If the category is not DYN or GOF.

Returns:

Which outputs are available.

get_or_add_arbitrary_function(qubit: QCDLModule, tag: Any, foo: Any, dtype: Any, scope_id: int | None = None, desc: Any = None, validate: bool = True, qubits: Any = None) None[source]#

Add an arbitrary function.

An arbitrary function is a table of values stored on the qubit (for domain and range of supported values, see the FixedPointRegister class).

The implementation here allows the table to be defined as follows:

  • An array of 512 values. This is not currently exposed in QCDL as the arbitrary_function() decorator takes a callable.

  • A callable function that takes a NumPy array and returns a NumPy array or a string (to be evaluated on-server).

Note

Calling this function directly from QCDL is not supported (it is used by the arbitrary_function() decorator).

Parameters:
  • qubit – The qubits associated with this arbitrary function.

  • tag – Name of the arbitrary function.

  • foo – The mechanism for generating the table.

  • dtype – Data type of the output.

  • desc – Description for the comment.

  • validate – Validate the data for range. Skip if you are not using this function outside the supported domain.

  • qubits – Other qubits to associate with this arbitrary function.

Raises:
  • QCDLUserError – Only up to 8 arbitrary functions are allowed.

  • QCDLUserError – If a string is used for an arbitrary function, must be a function of \(x\).

  • QCDLUserError – Pass either a list of values or a function.

initialize_modules(main_name: str = 'main', **kwargs: Any) dict[str, QCDLModule][source]#

Initialize a dict of QCDL modules.

Parameters:

main_name – Name for the main procedure.

Raises:

QCDLUserError – Can only do this once.

Returns:

Dictionary of QCDL modules.

register_procedure(procedure: Procedure) None[source]#

Register a procedure.

Parameters:

procedure – Procedure to register.

Raises:

QCDLUserError – If procedure is not unique.

release_output(qubit: QCDLModule, output: str) None[source]#

Release an output back to the pool of available outputs.

Parameters:
  • qubit – Where the output was reserved.

  • output – The DYNi or GOFi to release.

Raises:

QCDLUserError – If the output was not reserved.

reserve_output(qubit: QCDLModule, category: str | None = None, name: str | None = None) str | None[source]#

Reserve an output.

A way to ensure that multiple parts of code are not trying to use the same output simultaneously. Three DYN and four GOF outputs are available.

Parameters:
  • qubit – Where to reserve the output.

  • category – DYN or GOF types of registers.

  • name – Reserve a specific output (e.g., DYN1).

Raises:

QCDLUserError – The output is unavailable.

Returns:

One of the DYN or GOF outputs.

set_or_check_nondeterministic_modules(modules: Iterable[str | QCDLModule | QCDLModuleName], validate: bool = True, description: str | None = None) None[source]#

Check for and set circuits that desynchronize qubits.

QCDL supports non-deterministic control flow, as described in the Control Flow section. Your QCDl program must ensure that when any qubits execute a non-deterministic section of a circuit, all qubits in the circuit must be kept synchronized.

The first time this method is called it assigns the set of modules. Subsequent calls assert that these sets are the same.

Note

Conditional statements in themselves are not non-deterministic and the compiler synchronizes them such that the True and False branches execute in the same amount of time.

Tip

Some loops are not actually non-deterministic; you can unroll such loops in your Python code.

to_model() QCDLProgram[source]#

Build and return the validated QCDL model for this circuit.

Returns:

Validated program model, ready to pass to a compiler or convert to a plain dict via the model_dump() method with exclude_unset=True.

The code here facilitates constructing a circuit (in a json format) from python code.

class Procedure(proc_name: str, state: QCDLCircuit, modules: list[QCDLModule] | None = None, args: Sequence[Any] | None = None, kwargs: Mapping[Any, Any] | None = None, qcdl_operator: str | None = None, is_main: bool = True)[source]#

Bases: IndexerMixin

A QCDL procedure.

Parameters:
  • proc_name – Name of the procedure. To ensure a unique name, the procedure decorator incorporates arguments or keyword arguments (or a hash of these) to create a mangled name.

  • state – State of the overall circuit.

  • modules – Modules refers to the externally facing method signature if set to None, taken from modules_used (i.e., determines the signature based on the statements).

  • args – Arguments for the signature. Not used for QCDL.

  • kwargs – Keyword arguments for the signature.

  • qcdl_operator – Original name of the method before mangling.

  • is_main – True for the top-most procedure.

add_statement(qubit: str | None, op: str, args: Sequence[Any] | None, kwargs: Mapping[Any, Any] | None, caller_qubits: Sequence[str] | None = None) None[source]#

Append a statement to this procedure.

A procedure() is merely a list of statements. This method appends a statement to that list.

No validation done here on the statement (any method name, arguments, and keyword arguments are accepted).

Parameters:
  • qubit – To give the appearance of the method invoked on an instance, this is its name.

  • op – Name of the method invoked.

  • args – Arguments to the method.

  • kwargs – Keyword arguments to the method.

  • caller_qubits – Caller qubits.

begin_procedure(name: str, **kwargs: Any) Procedure[source]#

Start a child procedure of the current procedure.

Note

Argument validation relies on the execution of the python code, so not everything is trapped.

You are not allowed to add a statement to a caller procedure if the callee procedure is not ended.

end_procedure() None[source]#

End this procedure and register it.

property expression_queue: list | None[source]#

Create an expression queue.

Expression queues let you combine multiple CPU expressions into one QCDL statement (see the ExpressionAggregator class).

If this property is None, it is inactive. Otherwise, it is active and managed by the ExpressionAggregator class, and statements are added to it instead of to the procedure.

q(name: str | int) QCDLModule[source]#

Dynamically get a qubit QCDLModule.

Parameters:

name – Name of the module.

Returns:

Module ready for your instructions.

register_module_used(module_name: str | None) None[source]#

Track the modules a procedure uses.

Record which modules this procedure uses, which may be different from the call-signature modules.

This list is only for the given QCDL; for example, qa.swap(qb) includes qa and qb in its signature, but if those qubits are not connected, transpilation may add qubits.

Parameters:

module_name – Name of a module.

property statement_hash: str[source]#

Hash of current statements.

Used for comparing the statements of two procedures.

Tip

To ensure that the hash cannot change, end the procedure (blocking additional statements) before obtaining its hash.

Returns:

Hash of the statements.

to_model() QCDLProcedureDef[source]#

Encoded version of the python code.

Note

The compiler calls the fields qubits and qubits_used even for couplers.

class QCDLModuleName(*, kind: str, index: Annotated[int | None, Ge(ge=0)] = None)[source]#

Bases: BaseModel

A qubit, coupler, or arbitrary-prefix module referenced in a QCDL statement.

Validates from string names used in raw QCDL dicts (e.g., "q0", "c3", "m1", "m" etc.) and serializes back to that form, so that the model_dump() method on any parent model still produces plain strings for the compiler.

Parameters:
  • kind

    • "qubit" for q or qN names

    • "coupler" for c or cN names

    • Raw prefix string for any other pattern (e.g. "m" for m or mN).

  • index – The non-negative integer embedded in the name, or None when the name carries no numeric suffix (e.g. "q" returns index=None).

Examples

>>> from dwave.gate.qcdl.components import QCDLModuleName
...
>>> QCDLModuleName.model_validate("q2")
QCDLModuleName('q2')
>>> QCDLModuleName(kind="coupler", index=5).name
'c5'
>>> QCDLModuleName.model_validate("m0")
QCDLModuleName('m0')
>>> QCDLModuleName.model_validate("m")
QCDLModuleName('m')
property is_coupler: bool[source]#

True for coupler modules.

property is_qubit: bool[source]#

True for qubit modules.

model_config: ClassVar[ConfigDict] = {'frozen': True}[source]#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

property name: str[source]#

String form of this module.

Examples

Example strings: "q0", "c3", "m".

property prefix: str[source]#

String prefix used in the module name (e.g. "q", "c", "m").

class QCDLSignature(*, qcdl_operator: str | None, qubits: list[QCDLModuleName], qubits_used: list[QCDLModuleName], args: list[Any], kwargs: dict[str, Any])[source]#

Bases: BaseModel

Pydantic model for a procedure signature.

All fields are required. The extra="forbid" setting in model_config rejects unrecognized keys so that signature construction errors surface early.

model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'validate_assignment': True}[source]#

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].