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:
QCDLModuleContainerQubits to be used for quantum operations.
This subclass of
QCDLModuleContainerfacilitates use of features related to real-time control flow.- Parameters:
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
Arrayfor all qubits in this container.- See:
- Break() None[source]#
Exit current
While(),DoWhile(), orFor()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(), orFor()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
QCDLModuleContainerparticipate 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
FixedPointRegisterfor all qubits in this container.
- 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
QCDLModuleContainerparticipate 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_registerin 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()andGoto()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
QCDLModuleContainerparticipate 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
Ifbody, 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
Ifbody, control moves to the labeled statement. Used for loop control flow.
Examples
A simple example with one
Ifstatement: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()andGoto()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 theLabel()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
Registerfor all qubits in this container.- See:
- Repeat(number: int | Register, ascending: bool = False) Iterator[Register][source]#
Loop over a fixed number of iterations.
All qubits in this
QCDLModuleContainerparticipate 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:
- 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
QCDLModuleContainerparticipate 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:
Evaluate the same expression for each qubit
Each qubit broadcasts its bit
The
reduce_opconverts the set of input bits into one output bitThat bit is then sent back to each qubit
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:
Examples
This example appends dict keys
q0andq1that each has as its value a dict with keyafter_swapfor 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.
- 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
RegisterorFixedPointRegisterclasses 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:
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_modulesproperty to add a comment in generated QCDL with a qubit’s name. In practice, you would likely use the simplernameproperty (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:
- 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
syncare completed before any operations after thesyncare 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
syncon 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:
IntEnumMaps 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:
Examples
See examples for the
LogicalOutcomeToIntegerclass.
- static from_outcome(val: str | int | float) LogicalOutcomeToInteger[source]#
Instantiate a
LogicalOutcomeToIntegerfrom various inputs.- Parameters:
val – Input value.
- Returns:
Erasure bitstring representation.
- Return type:
Examples
See examples for the
LogicalOutcomeToIntegerclass.
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
Registerobject used for the outcome of amced()operation. See the Mirroring section to learn about mirroring.This function requires the following:
Allocate the
Registerto all the relevant qubits.A value of either \(0\) or \(1\) in the register associated with the qubit specified by the
senderparameter.
- Parameters:
sender – Qubit associated with the register you are assigning the measurement outcome to.
register –
Registerobject 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
Registerparameter.
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
Registerobject used for the outcome of ameasure()operation. See the Mirroring section to learn about mirroring.This function requires the following:
Allocate the
Registerto all the relevant qubits.The measurement outcome is written to the register associated with the qubit specified by the
senderparameter.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.
register –
Registerobject 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
Registerparameter.
Examples
This is an artificial example to demonstrate usage (see the
mirrorparameter in themeasure()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
RegisterandFixedPointRegisterclasses).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
Scopeobject 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_modulesattribute.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 aq<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
QCDLModuleinstance. 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, q1arguments 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:
QCDLModuleContainerWrapper around a
Procedureinstance.Important
This class is intended for use by developers of QCDL to append instructions to a module, typically a qubit.
A
QCDLModuleinstance is associated with a specificProcedureinstance. This means that if a procedure is entered, a newQCDLModuleis 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 theoperationsmethods instead, where applicable.- Parameters:
module_name – Name of the module, typically a qubit.
proc –
Procedureinstance thisQCDLModuleis in.
Examples
This example shows a typical use of
operationsmethods, which indirectly instantiates aQCDLModuleinstance.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_nameproperty of theQCDLModuleclass.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
QCDLModulein 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:
QCDLInternalError – Other modules are not available.
QCDLUserError – Other module is not in this procedure.
- Returns:
The other module is in this same procedure scope.
- Return type:
- 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:
- 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_idif aScopeis specified.send – The expression to compute the bit on the sender.
- Raises:
QCDLUserError – If
destinationsdoes 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
QCDLModulethis container holds.Examples
This is an artificial example; see the example for the
namefor 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:
QCDLModuleContainerBaseBase class for the
QCDLModuleandScopeclasses.Important
This class is not meant to be instantiated directly. Typical QCDL programs use the
Scopeclass.Defines shared methods such as
If(),comment(), andsync().- Array(*args: Any, **kwargs: Any) Any[source]#
Instantiate an
Arrayfor all qubits in this container.- See:
- Break() None[source]#
Exit current
While(),DoWhile(), orFor()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(), orFor()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
QCDLModuleContainerparticipate 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
FixedPointRegisterfor all qubits in this container.
- 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
QCDLModuleContainerparticipate 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_registerin 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()andGoto()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
QCDLModuleContainerparticipate 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
Ifbody, 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
Ifbody, control moves to the labeled statement. Used for loop control flow.
Examples
A simple example with one
Ifstatement: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()andGoto()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 theLabel()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
Registerfor all qubits in this container.- See:
- Repeat(number: int | Register, ascending: bool = False) Iterator[Register][source]#
Loop over a fixed number of iterations.
All qubits in this
QCDLModuleContainerparticipate 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:
- 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
QCDLModuleContainerparticipate 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:
Evaluate the same expression for each qubit
Each qubit broadcasts its bit
The
reduce_opconverts the set of input bits into one output bitThat bit is then sent back to each qubit
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:
Examples
This example appends dict keys
q0andq1that each has as its value a dict with keyafter_swapfor 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.
- 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
RegisterorFixedPointRegisterclasses 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:
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_idproperty.
- 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
syncare completed before any operations after thesyncare 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
syncon 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:
BaseModelPydantic model for a compiled procedure definition.
Nesting
QCDLStatementandQCDLSignaturemeans that callingQCDLProcedureDef.model_validate(proc_dict)recursively validates the entire sub-tree.
- class QCDLProgram(*, program: QCDLProcedureDef, procedures: dict[str, QCDLProcedureDef], next_indices: dict[str, int], **extra_data: Any)[source]#
Bases:
BaseModelPydantic 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
qcdldecorator to generate an instance of this class.The
extra="allow"setting inmodel_configensures that implementation-specific keys of various compiler versions are preserved through round trips.
- 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:
BaseModelPydantic 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 throughmodel_dump().Set the
exclude_unset=Trueparameter for themodel_dump()method to reproduce the sparsedictrepresentation thatadd_statement()method generates (fields that are not explicitly set are omitted).- property condition: Any[source]#
The condition value for conditional operations.
Valid for
opvalues"If","c_if", and"c_if_else". RaisesQCDLInternalErrorfor any otherop.
- 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._argsattribute performed).
- property qubit_names: list[str][source]#
Names of all qubits referenced by this statement, from all sources.
Unlike the
qubitsproperty, which returnsQCDLModuleNameobjects 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
qubitfield,caller_qubits(for procedure calls),kwargs["qubits"], module-likeargs, andop-specific logic forIf/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.
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:
IndexerMixinCoordinates 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
FixedPointRegisterclass).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 withexclude_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:
IndexerMixinA QCDL procedure.
- Parameters:
proc_name – Name of the procedure. To ensure a unique name, the
proceduredecorator 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.
- property expression_queue: list | None[source]#
Create an expression queue.
Expression queues let you combine multiple CPU expressions into one QCDL statement (see the
ExpressionAggregatorclass).If this property is None, it is inactive. Otherwise, it is active and managed by the
ExpressionAggregatorclass, 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)includesqaandqbin 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
qubitsandqubits_usedeven for couplers.
- class QCDLModuleName(*, kind: str, index: Annotated[int | None, Ge(ge=0)] = None)[source]#
Bases:
BaseModelA 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 themodel_dump()method on any parent model still produces plain strings for the compiler.- Parameters:
kind –
"qubit"forqorqNnames"coupler"forcorcNnamesRaw prefix string for any other pattern (e.g.
"m"formormN).
index – The non-negative integer embedded in the name, or
Nonewhen the name carries no numeric suffix (e.g."q"returnsindex=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')
- class QCDLSignature(*, qcdl_operator: str | None, qubits: list[QCDLModuleName], qubits_used: list[QCDLModuleName], args: list[Any], kwargs: dict[str, Any])[source]#
Bases:
BaseModelPydantic model for a procedure signature.
All fields are required. The
extra="forbid"setting inmodel_configrejects unrecognized keys so that signature construction errors surface early.