|
13 | 13 | # --- |
14 | 14 |
|
15 | 15 | """ |
16 | | -# Beginner's guide to `dwave-gate` |
| 16 | +# Beginner's Guide to `dwave-gate` |
17 | 17 |
|
18 | | -With `dwave-gate` you can easily contruct and simulate quantum circuits. This tutorial will guide |
19 | | -you through how to use the `dwave-gate` library to inspect different quantum gates, construct |
20 | | -circuits out of them, and then simulate them using our performant state-vector simulator. |
| 18 | +`dwave-gate` lets you easily construct and simulate quantum circuits. |
21 | 19 |
|
22 | | -We begin by importing the necessary modules. |
| 20 | +This tutorial guides you through using the `dwave-gate` library to inspect, |
| 21 | +construct circuits from, and simulate quantum gates using a performant |
| 22 | +state-vector simulator. |
| 23 | +## Circuits and Operations |
| 24 | +Begin by importing the necessary modules. |
| 25 | +
|
| 26 | +* `Circuit` objects contain the full quantum circuit, with all the operations, |
| 27 | + measurements, qubits and bits. |
| 28 | +* Operations, or gates, are objects that contain information related to a |
| 29 | + particular operation, including its matrix representation, potential |
| 30 | + decompositions, and how to apply it to a circuit. |
23 | 31 | """ |
24 | 32 |
|
25 | 33 | from dwave.gate.circuit import Circuit |
26 | 34 | import dwave.gate.operations as ops |
27 | 35 |
|
28 | 36 | ############################################################################### |
29 | | -# The `Circuit` object contains the full quantum circuit, with all the operations, measurements, |
30 | | -# qubits and bits. Operations, or gates, are objects that contain information related to that |
31 | | -# specific operation, including its matrix representation, potential decompositions, and how to |
32 | | -# apply it to a circuit. Via the `operations` module, here shortened to `ops`, a variety of quantum |
33 | | -# gates can be accessed. |
| 37 | +# You can use the `operations` module (shortened above to `ops`) to access a |
| 38 | +# variety of quantum gates; for example, a Pauli X operator. |
34 | 39 |
|
35 | 40 | ops.X() |
36 | 41 |
|
37 | 42 | ############################################################################### |
38 | | -# As we can see, any operation can be instantiated without declaring which qubits it should be |
39 | | -# applied to. We can also call the matrix property on either the gate class itself or on an instance |
40 | | -# (i.e., an instantiated operation, which can contain additional parameters and/or qubit |
41 | | -# information). |
| 43 | +# Notice above that an operation can be instantiated without declaring which qubits |
| 44 | +# it should be applied to. |
| 45 | +# |
| 46 | +# You can also call the matrix property on either the gate class itself or on an |
| 47 | +# instance (i.e., an instantiated operation, which can contain additional |
| 48 | +# parameters and qubit information). |
42 | 49 |
|
43 | 50 | ops.X.matrix |
44 | 51 |
|
|
50 | 57 | ops.RZ(4.2).matrix |
51 | 58 |
|
52 | 59 | ############################################################################### |
53 | | -# Operations are applied when either the class, or an instance of the class, is called within a |
54 | | -# circuit's context. They can be applied to the circuit in several different ways, as detailed |
55 | | -# below. Both qubits and parameters can be passed either as single values (if supported by the gate) |
56 | | -# or as sequences. Note that different types of gates accept slightly different argument, although |
57 | | -# the qubits can _always_ be passed as sequences via the keyword argument `qubits`. |
58 | | -# |
59 | | -# When instantiated within a circuit context, operations must always be applied to specific qubits |
60 | | -# which in turn must be part of the circuits qubit register. The qubit register can be accessed via |
61 | | -# the named tuple returned by the context manager as `q`, indexing into it to retrieve the |
62 | | -# corresponding qubit. |
| 60 | +# ## Circuit Context |
| 61 | +# You apply operations by calling, within the context of a circuit, either a |
| 62 | +# class or an instance of a class. |
| 63 | +# |
| 64 | +# You can apply operations to a circuit in several different ways, as demonstrated |
| 65 | +# below. You can pass both qubits and parameters as either single values (when |
| 66 | +# supported by the gate) or sequences. Note that different types of gates accept |
| 67 | +# slightly different arguments, although you can _always_ pass the qubits as |
| 68 | +# sequences via the keyword argument `qubits`. |
63 | 69 | # |
64 | | -# Let's start by creating a circuit object with 2 qubits in its register, and apply a single X-gate |
65 | | -# to the first qubit. |
| 70 | +# Always apply any operations you instantiate within a circuit context to |
| 71 | +# specific qubits in the circuit's qubit register. You can access the qubit |
| 72 | +# register via the named tuple returned by the context manager as `q`, indexing |
| 73 | +# into it to retrieve the corresponding qubit. |
| 74 | +# ## Registers |
| 75 | +# This example starts by creating a circuit object with two qubits in its |
| 76 | +# register and applying a single X gate to the first qubit. |
66 | 77 |
|
67 | 78 | circuit = Circuit(2) |
68 | 79 |
|
69 | 80 | with circuit.context as reg: |
70 | 81 | ops.X(reg.q[0]) |
71 | 82 |
|
72 | 83 | ############################################################################### |
73 | | -# We can access the qubit register via the `Circuit.qregisters` attribute, which currently should |
74 | | -# contain a single qubit register with 2 qubits in it, but could contain any number of qubit |
75 | | -# registers. If we'd want to, we could add another register with the `Circuit.add_qregister(n)` |
76 | | -# method, where `n` would be the number of qubits in the new register, or add a qubit with |
77 | | -# `Circuit.add_qubit()`, optinally passing a qubit object and/or a register to which to add it. |
| 84 | +# You can access the qubit register via the `Circuit.qregisters` attribute. |
| 85 | +# |
| 86 | +# In the current example, this attribute contains a single qubit register with |
| 87 | +# two qubits; it could contain any number of qubit registers. You can |
78 | 88 | # |
79 | | -# The registers tuple can also be unwrapped directly into a qubit register `q` (and a classical |
80 | | -# register `c`, but we'll get into that later). |
| 89 | +# * add another register with the `Circuit.add_qregister(n)` method, where `n` |
| 90 | +# is the number of qubits in the new register |
| 91 | +# * add a qubit with `Circuit.add_qubit()`, optionally passing a qubit object |
| 92 | +# and/or a register to which to add it. |
81 | 93 | # |
82 | | -# Below follows a few examples for how to apply different gates to the circuit. |
| 94 | +# You can also unwrap the registers tuple into a qubit register `q` (and a classical |
| 95 | +# register `c`). |
| 96 | +# |
| 97 | +# ## Example: Applying Various gates to a Circuit |
83 | 98 |
|
84 | 99 |
|
85 | 100 | circuit = Circuit(3) |
|
109 | 124 | ops.Toffoli(q) |
110 | 125 |
|
111 | 126 | ############################################################################### |
112 | | -# Printing the above circuit gives us some general information about the circuit: the type of |
113 | | -# circuit, the number of qubits/bits and the number of operations. |
| 127 | +# You can print a circuit to see general information about it: type of circuit, |
| 128 | +# number of qubits/bits, and number of operations. |
114 | 129 |
|
115 | 130 | print(circuit) |
116 | 131 |
|
117 | 132 | ############################################################################### |
118 | | -# We can also access the operations in the circuit via the `Circuit.circuit` attribute. This will |
119 | | -# return a list of all operations which we can iterate over and print in the console. |
| 133 | +# You can also access operations in a circuit using the `Circuit.circuit` |
| 134 | +# attribute. The code below iterates over the returned list of all operations. |
120 | 135 |
|
121 | 136 |
|
122 | 137 | for op in circuit.circuit: |
123 | 138 | print(op) |
124 | 139 |
|
125 | 140 | ############################################################################### |
126 | | -# Note that e.g., CNOT and CX apply the exact same gate. CNOT is only an alias for CX (so they both |
127 | | -# are labelled as CX in the circuit). There are also other aliases which you can spot either in the |
128 | | -# source code for the operations module or read more about in the documentation. |
129 | | -# |
| 141 | +# Note that the CNOT alias for the controlled NOT gate is labelled CX in the |
| 142 | +# circuit. You can find all operation aliases in the source code and documentation |
| 143 | +# for the operations module. |
130 | 144 |
|
131 | 145 | ############################################################################### |
132 | 146 | # ## Simulating a circuit |
|
0 commit comments