Skip to content

Commit 0718763

Browse files
committed
Update demo build
1 parent d90829f commit 0718763

File tree

2 files changed

+170
-57
lines changed

2 files changed

+170
-57
lines changed

docs/Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,16 @@ demos:
4343
-e $(ADD_FILE_NAME_LABEL) \
4444
-e "s/\`\`Circuit\`\`/:class:\`~dwave.gate.circuit.Circuit\`/g" \
4545
-e "s/\`\`Circuit\.\(\w*\)\`\`/:meth:\`~dwave.gate.circuit.Circuit.\1\`/g" \
46+
-e "s/\`\`ParametricCircuit\`\`/:class:\`~dwave.gate.circuit.ParametricCircuit\`/g" \
47+
-e "s/\`\`Operation\`\`/:class:\`~dwave.gate.operations.base.Operation\`/g" \
4648
-e "s/\`\`operations\`\`/:mod:\`~dwave.gate.operations\`/g" \
4749
-e "s/\`\`ops\.\(\w*\)\`\`/:class:\`~dwave.gate.operations.operations.\1\`/g" \
50+
-e "s/\`\`Measurement\`\`/:mod:\`~dwave.gate.operations.base.Measurement\`/g" \
51+
-e "s/\`\`ParametricOperation\`\`/:mod:\`~dwave.gate.operations.base.ParametricOperation\`/g" \
52+
-e "s/\`\`ControlledOperation\`\`/:mod:\`~dwave.gate.operations.base.ControlledOperation\`/g" \
53+
-e "s/\`\`ParametricControlledOperation\`\`/:mod:\`~dwave.gate.operations.base.ParametricControlledOperation\`/g" \
54+
-e "s/\`\`simulate\`\`/:mod:\`~dwave.gate.simulator.simulate\`/g" \
55+
-e "s/\`\`simulator\.\(\w*\)\`\`/:class:\`~dwave.gate.simulator.\1\`/g" \
4856
-e $(ADD_DOWNLOAD_BUTTONS) \
4957
$$1' sh {} \; \
5058

docs/demos/demos/intro_demo.py

Lines changed: 162 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,89 +18,106 @@
1818
`dwave-gate` lets you easily construct and simulate quantum circuits.
1919
2020
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.
21+
construct, and simulate quantum circuits using a performant state-vector
22+
simulator.
2323
24-
## Circuits and Operations
25-
Begin by importing the necessary modules.
26-
27-
* `Circuit` objects contain the full quantum circuit, with all the operations,
28-
measurements, qubits and bits.
29-
* Operations, or gates, are objects that contain information related to a
30-
particular operation, including its matrix representation, potential
31-
decompositions, and how to apply it to a circuit.
24+
## Circuits and operations
25+
Begin with two necessary imports: The `Circuit` class, which will contain the
26+
full quantum circuit with all the operations, measurements, qubits and bits, and
27+
the operations module. The operations, or gates, contain information related to
28+
a particular operation, including its matrix representation, potential
29+
decompositions, and how to apply it to a circuit.
3230
"""
3331

3432
from dwave.gate.circuit import Circuit
3533
import dwave.gate.operations as ops
3634

35+
###############################################################################
36+
# The `Circuit` keeps track of the operations and the logical flow of their
37+
# excecutions. It also stores the qubits, bits and measurments.
38+
#
39+
# When initializing a circuit, the number of qubits and (optionally) the number of
40+
# bits, i.e., classical measurement results containers, need to be declared. More
41+
# qubits and bits can be added using the `Circuit.add_qubit` and `Circuit.add_bit`
42+
# methods.
43+
44+
circuit = Circuit(num_qubits=2, num_bits=2)
45+
3746
###############################################################################
3847
# You can use the `operations` module (shortened above to `ops`) to access a
3948
# variety of quantum gates; for example, a Pauli X operator.
4049

4150
ops.X()
4251

4352
###############################################################################
44-
# Notice above that an operation can be instantiated without declaring which qubits
45-
# it should be applied to.
53+
# Notice above that an operation can be instantiated without declaring which
54+
# qubits it should be applied to.
4655
#
47-
# The matrix property can be accessed either via the gate class itself or an
48-
# instance (i.e., an instantiated operation, which can contain additional
49-
# parameters and qubit information).
56+
# The matrix property can be accessed either via the operation class itself or an
57+
# instance of the operation, which additionally can contain parameters and qubit
58+
# information.
5059

5160
ops.X.matrix
5261

5362
###############################################################################
54-
# If the matrix representation is dependent on parameters (e.g., the X-rotation operation) it can
55-
# only be retrieved from an instance.
56-
63+
# If the matrix representation is dependent on parameters (e.g., the rotation operation `ops.RX`)
64+
# it can only be retrieved from an instance.
5765

5866
ops.RZ(4.2).matrix
5967

6068
###############################################################################
61-
# ## Circuit Context
62-
# Operations are applied by calling, within the context of a circuit, either a
63-
# class or an instance of a class.
69+
# ## The circuit context
6470
#
65-
# You can apply operations to a circuit in several different ways, as demonstrated
66-
# below. You can pass both qubits and parameters as either single values (when
67-
# supported by the gate) or sequences. Note that different types of gates accept
68-
# slightly different arguments, although you can _always_ pass the qubits as
69-
# sequences via the keyword argument `qubits`.
71+
# Operations are applied by calling either a class or an instance of a class
72+
# within the context of the circuit.
73+
#
74+
# ```python
75+
# with circuit.context:
76+
# # apply operations here
77+
# ```
7078
#
71-
# Always apply any operations you instantiate within a circuit context to
72-
# specific qubits in the circuit's qubit register. You can access the qubit
73-
# register via the named tuple returned by the context manager as `q`, indexing
74-
# into it to retrieve the corresponding qubit.
7579

7680
###############################################################################
77-
# ## Registers
78-
# This example starts by creating a circuit object with two qubits in its
79-
# register and applying a single X gate to the first qubit.
81+
# When activating the context, a named tuple containing reference registers to the
82+
# circuit's qubits and classical bits is returned. You can also access the qubit
83+
# registers directly via the `Circuit.qregisters` property, or the reference
84+
# registers containing all the qubits via `Circuit.qubits`.
85+
#
86+
# In the example below, the circuit contains a single qubit register with two
87+
# qubits; it could contain any number of qubit registers. You can
88+
#
89+
# * add another register with the `Circuit.add_qregister` method, where an argument `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.
8093

8194
circuit = Circuit(2)
8295

8396
with circuit.context as reg:
8497
ops.X(reg.q[0])
8598

8699
###############################################################################
87-
# You can access the qubit register via the `Circuit.qregisters` attribute.
88-
#
89-
# In the current example, this attribute contains a single qubit register with
90-
# two qubits; it could contain any number of qubit registers. You can
91-
#
92-
# * add another register with the `Circuit.add_qregister(n)` method, where `n`
93-
# is the number of qubits in the new register
94-
# * add a qubit with `Circuit.add_qubit()`, optionally passing a qubit object
95-
# and/or a register to which to add it.
96-
#
97-
# The registers tuple can also be unwrapped directly into a qubit register `q` (and a
98-
# classical register `c`).
100+
# This example created a circuit object with two qubits in its register, applying
101+
# a single X gate to the first qubit. Print the circuit to see general information
102+
# about it: type of circuit, number of qubits/bits, and number of operations.
99103

104+
print(circuit)
100105

101106
###############################################################################
102-
# ## Example: Applying Various gates to a Circuit
103-
107+
# ## Applying gates to circuits
108+
#
109+
# You can apply operations to a circuit in several different ways, as demonstrated
110+
# in the example below. You can pass both qubits and parameters as either single
111+
# values (when supported by the gate) or sequences. Note that different types of
112+
# gates accept slightly different arguments, although you can _always_ pass the
113+
# qubits as sequences via the keyword argument `qubits`.
114+
#
115+
# :::note
116+
# Always apply any operations you instantiate within a circuit context to
117+
# specific qubits in the circuit's qubit register. You can access the qubit
118+
# register via the named tuple returned by the context manager as `q`, indexing
119+
# into it to retrieve the corresponding qubit.
120+
# :::
104121

105122
circuit = Circuit(3)
106123

@@ -129,23 +146,111 @@
129146
ops.Toffoli(q)
130147

131148
###############################################################################
132-
# Print the circuit above to see general information about it: type of circuit,
133-
# number of qubits/bits, and number of operations.
149+
# You can access all the operations in a circuit using the `Circuit.circuit`
150+
# property. The code below iterates over the returned list of all operations that
151+
# have been applied to the circuit.
152+
153+
for op in circuit.circuit:
154+
print(op)
155+
156+
###############################################################################
157+
# :::note
158+
# The CNOT alias for the controlled NOT gate is labelled CX in the circuit. You
159+
# can find all operation aliases in the source code and documentation for the
160+
# operations module.
161+
# :::
162+
163+
###############################################################################
164+
# ## Simulating a circuit
165+
#
166+
# `dwave-gate` comes with a performant state-vector simulator. It can be called by passing a circuit to the `simulate` method, which will update the quatum state stored in the circuit, accessible via `Circuit.state`.
167+
168+
from dwave.gate.simulator import simulate
169+
170+
###############################################################################
171+
# We create a circuit object with 2 qubits and 1 bit in a quantum and classical registers respectively --- the bit is required to store a single qubit measurement --- and then apply a Hadamard gate and a CNOT gate to the circuit.
172+
173+
circuit = Circuit(2, 1)
174+
175+
with circuit.context as (q, c):
176+
ops.Hadamard(q[0])
177+
ops.CNOT(q[0], q[1])
178+
179+
###############################################################################
180+
# We can now simulate the circuit, which will update its stored quantum state.
181+
182+
simulate(circuit)
183+
184+
###############################################################################
185+
# Printing the state reveals the expected state-vector $\frac{1}{\sqrt{2}}\left[1,
186+
# 0, 0, 1\right]$ corresponding to the state:
187+
#
188+
# $$\vert\psi\rangle = \frac{1}{\sqrt{2}}\left(\vert00\rangle + \vert11\rangle\right)$$
189+
190+
circuit.state
191+
192+
###############################################################################
193+
# ## Measurements
194+
#
195+
# Measurements work like any other operation in dwave-gate. The main difference is that the operation generates a measurement value when simulated which can be stored in the classical register by piping it into a classical bit.
196+
#
197+
# We can reuse the circuit from above by simply unlocking it and appending a `Measurement` to it.
198+
199+
circuit.unlock()
200+
with circuit.context as (q, c):
201+
m = ops.Measurement(q[1]) | c[0]
202+
203+
###############################################################################
204+
# :::note
205+
# We stored the measurement instance as `m`, which we can use for post-processing. It's also possible to do this with all other operations in the same way, allowing for multiple identical operation applications.
206+
# ```python
207+
# with circuit.context as q, _:
208+
# single_x_op = ops.X(q[0])
209+
# # apply the X-gate again to the second qubit
210+
# # using the previously stored operation
211+
# single_x_op(q[1])
212+
# ```
213+
# This procedure can also be shortened into a single line for further convience.
214+
# ```python
215+
# ops.CNOT(q[0], q[1])(q[1], q[2])(q[2], q[3])
216+
# ```
217+
# :::
218+
219+
###############################################################################
220+
# The circuit should now contain 3 operations: a Hadamard, a CNOT and a measurment.
134221

135222
print(circuit)
136223

137224
###############################################################################
138-
# You can also access operations in a circuit using the `Circuit.circuit`
139-
# attribute. The code below iterates over the returned list of all operations.
225+
# When simulating this circuit, the measurement will be applied and the measured value will be stored in the classical register. Since a measurement will affect the quantum state, the resulting state will have collapsed into the expected result dependent on the value which has been measured.
140226

227+
simulate(circuit)
141228

142-
for op in circuit.circuit:
143-
print(op)
229+
###############################################################################
230+
# If the measurement result is 0 the state should collapse into $\vert00\rangle$, and if the measurement result is 1 the state should collapse into $\vert11\rangle$. Outputting the measurement value and the state reveals that this is indeed the case.
231+
232+
print(circuit.bits[0].value)
233+
print(circuit.state)
144234

145235
###############################################################################
146-
# Note that the CNOT alias for the controlled NOT gate is labelled CX in the
147-
# circuit. You can find all operation aliases in the source code and documentation
148-
# for the operations module.
236+
# ## Measurement post-access
237+
# Since we stored the measurement operation in `m`, we can use it to access the state as it was before the measurement.
238+
#
239+
# :::note
240+
# Accessing the state of the circuit along with any measurement post-sampling and state-access is only available for simulators.
241+
# :::
242+
243+
m.state
149244

150245
###############################################################################
151-
# ## Simulating a circuit
246+
# We can also sample that same state again using the `Measurement.sample` method, which by default only samples the state once. Here, we request 10 samples.
247+
248+
m.sample(num_samples=10)
249+
250+
###############################################################################
251+
# Finally, we can calculate the expected value of the measurment based on a specific number of samples.
252+
253+
m.expval(num_samples=10000)
254+
255+
""
256+

0 commit comments

Comments
 (0)