-
Notifications
You must be signed in to change notification settings - Fork 6
[CQT-369] Add CircuitBuilder user documentation #699
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
rares1609
merged 5 commits into
develop
from
CQT-369-Write-circuit-builder-section-User-documentation
Aug 6, 2026
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
15fd92b
add first draft of CircuitBuilder user documentation
c14cd52
resolve merge conflicts
1aec48e
review line breaks and implement feedback on documentation
4f26c45
remove some extra spaces and blank lines at the end of the file
841fdf6
Update docs/circuit-builder/index.md
rares1609 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,191 @@ | ||
| !!! note "Coming soon" | ||
| The `CircuitBuilder` is OpenSquirrel's programmatic API for constructing a circuit. | ||
| It offers an alternative to writing out a | ||
| [cQASM string](../tutorial/creating-a-circuit.md#1-from-a-cqasm-string). Instead of | ||
| describing your program as text, you assemble it instruction by instruction, directly in | ||
| Python. | ||
|
|
||
| The `CircuitBuilder` may be more convenient when the structure of your program is more | ||
| naturally and easily expressed with code than as a static string. | ||
| Since you're building in Python, you have all of the language's programmatic tools at | ||
| your disposal, such as loops, conditionals or list comprehensions, to generate your | ||
| circuit. | ||
|
|
||
| To get started, import the `CircuitBuilder` from `opensquirrel`: | ||
|
|
||
| ```python | ||
| from opensquirrel import CircuitBuilder | ||
| ``` | ||
|
|
||
| ## Instantiating the builder | ||
|
|
||
| A builder is created by declaring the sizes of its qubit and (optionally) bit registers: | ||
|
|
||
| ```python | ||
| builder = CircuitBuilder(qubit_register_size=3, bit_register_size=2) | ||
| ``` | ||
|
|
||
| This reserves a qubit register `q` of size 3 and a bit register `b` of size 2. | ||
| Qubits and bits are always referred to by their integer index into these registers, | ||
| starting at `0`. | ||
|
|
||
| This is the simplest way to get started. | ||
| If you need more control, for instance to define multiple named registers, see | ||
| [Named registers](#named-registers) below. | ||
|
|
||
| ## Adding instructions | ||
|
|
||
| Once the builder is instantiated, instructions are added by calling their name directly | ||
| on the builder, passing the qubit and bit indices (and any parameters for e.g. | ||
| _parameterized gates_) as arguments. | ||
| The available instructions fall into three main categories: | ||
|
|
||
| - [Gates](instructions/gates.md): the unitary instructions, from single-qubit gates | ||
| such as `H`, `X` and `Rz` to two-qubit gates such as `CNOT`, abd `CZ` , | ||
| - [Non-unitaries](instructions/non-unitaries.md): `init`, `measure` and `reset`, and | ||
| - [Control instructions](instructions/control-instructions.md): `barrier` and `wait`. | ||
|
|
||
| ```python | ||
| builder.H(0) | ||
| builder.CNOT(0, 1) | ||
| builder.Rz(2, 3.14) | ||
| ``` | ||
|
|
||
| Instruction calls can also be _chained_ together into a single expression: | ||
|
|
||
| ```python | ||
| builder.H(0).CNOT(0, 1).CNOT(0, 2) | ||
| ``` | ||
|
|
||
| The builder checks every call as it is made. | ||
| Referring to a qubit or bit that lies outside its register raises an `IndexError`, | ||
| calling an instruction that does not exist raises an `AttributeError`, and passing the | ||
| wrong number or type of arguments raises a `TypeError`. | ||
|
|
||
| Instructions can also be appended with `add_instruction`, which accepts either | ||
| a single instruction or an iterable of them: | ||
|
|
||
| ```python | ||
| from opensquirrel import CircuitBuilder, H, CNOT | ||
|
|
||
| builder = CircuitBuilder(2) | ||
| builder.add_instruction(H(0)) | ||
| builder.add_instruction([H(1), CNOT(0, 1)]) | ||
| ``` | ||
|
|
||
| ## Building the circuit | ||
|
|
||
| Calling `to_circuit()` finalizes the construction and returns the `Circuit` object: | ||
|
|
||
| ```python | ||
| from opensquirrel import CircuitBuilder | ||
|
|
||
| builder = CircuitBuilder(qubit_register_size=2, bit_register_size=2) | ||
| builder.add_instruction([H(0) CNOT(0, 1)]) | ||
| builder.measure(1, 0).measure(0, 1) | ||
| circuit = builder.to_circuit() | ||
| ``` | ||
|
|
||
| ??? example "`print(circuit)`" | ||
|
|
||
| ```linenums="1" | ||
| version 3.0 | ||
| qubit[2] q | ||
| bit[2] b | ||
| H q[0] | ||
| CNOT q[0], q[1] | ||
|
elenbaasc marked this conversation as resolved.
|
||
| b[0] = measure q[1] | ||
| b[1] = measure q[0] | ||
| ``` | ||
|
|
||
| From here on, one can proceed to, for instance, | ||
| [apply compilation passes](../tutorial/applying-compilation-passes.md) to the `circuit` | ||
| object. | ||
|
|
||
| ## Building circuits programmatically | ||
|
|
||
| Because you are building the circuit in Python, the whole language is available to | ||
| generate the instructions. | ||
| Loops, conditionals and list comprehensions make it easy to describe circuits whose | ||
| size or structure depends on a parameter: | ||
|
|
||
| ```python | ||
| from opensquirrel import CircuitBuilder | ||
|
|
||
| qubit_register_size = 10 | ||
| builder = CircuitBuilder(qubit_register_size) | ||
| for qubit_index in range(0, qubit_register_size, 2): | ||
| builder.H(qubit_index) | ||
| circuit = builder.to_circuit() | ||
| ``` | ||
|
|
||
| ??? example "`print(circuit)`" | ||
|
|
||
| ```linenums="1" | ||
| version 3.0 | ||
|
|
||
| qubit[10] q | ||
|
|
||
| H q[0] | ||
| H q[2] | ||
| H q[4] | ||
| H q[6] | ||
| H q[8] | ||
| ``` | ||
|
|
||
| The [tutorial](../tutorial/creating-a-circuit.md#2-by-using-the-circuit-builder) works | ||
| through a larger example of this pattern, generating a | ||
| [quantum Fourier transform](https://en.wikipedia.org/wiki/Quantum_Fourier_transform) of | ||
| arbitrary size. | ||
|
|
||
| ## Single-gate-multiple-qubit notation (SGMQ) | ||
|
|
||
| Instructions accept a _list_ of indices wherever they accept a single index, following | ||
| the | ||
| [single-gate-multiple-qubit (SGMQ) notation](https://qutech-delft.github.io/cQASM-spec/latest/language_specification/statements/instructions/single-gate-multiple-qubit-notation.html). | ||
| The builder unpacks such a call into separate, consecutive instructions: | ||
|
|
||
| ```python | ||
| builder = CircuitBuilder(3) | ||
| builder.H([0, 1, 2]) | ||
| ``` | ||
|
|
||
| is equivalent to `builder.H(0).H(1).H(2)`. | ||
| Any parameters are shared across the expansion, so `builder.Rx([0, 1, 2], math.pi / 2)` | ||
| applies the same rotation to each of the three qubits. | ||
|
|
||
| For two-operand instructions, such as two-qubit gates and `measure`, both operands may | ||
| be lists, in which case they are zipped together and must be of equal length: | ||
|
|
||
| ```python | ||
| builder = CircuitBuilder(4) | ||
| builder.CNOT([0, 1], [2, 3]) | ||
| ``` | ||
|
|
||
| adds `CNOT q[0], q[2]` followed by `CNOT q[1], q[3]`. | ||
|
|
||
| ## Named registers | ||
|
|
||
| The register created by the constructor is always called `q` (and `b` for the bits). If | ||
| you need more than one named register, for example to keep logical qubits separate from | ||
| ancillas, start from an empty builder and add the registers yourself with `add_register` | ||
| : | ||
|
|
||
| ```python | ||
| from opensquirrel import CircuitBuilder, QubitRegister, BitRegister | ||
|
|
||
| builder = CircuitBuilder() | ||
|
|
||
| data = QubitRegister(2, "data") | ||
| ancilla = QubitRegister(2, "ancilla") | ||
| bits = BitRegister(2, "measurement") | ||
|
|
||
| builder.add_register(data) | ||
| builder.add_register(ancilla) | ||
| builder.add_register(bits) | ||
|
|
||
| for d, a in zip(data, ancilla): | ||
| builder.CNOT(d, a) | ||
| builder.measure(data, bits) | ||
|
|
||
| circuit = builder.to_circuit() | ||
| ``` | ||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.