Skip to content

Commit

Permalink
Documentation review and improvements (#43)
Browse files Browse the repository at this point in the history
Co-authored-by: Joao Moutinho <[email protected]>
Co-authored-by: Mario Dagrada <[email protected]>
Co-authored-by: Vytautas Abramavicius <[email protected]>
  • Loading branch information
4 people authored and madagra committed Oct 10, 2023
1 parent d3a6ccf commit 81b2e0b
Show file tree
Hide file tree
Showing 27 changed files with 742 additions and 597 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ differentiable state vector simulator). You can install one or all of the follo
backends and the circuit visualization library using the following extras:

* `braket`: install the Amazon Braket quantum backend
* `emu-c`: install the Pasqal circuit tensor network emulator EMU-C
* `pulser`: install the [Pulser](https://github.com/pasqal-io/Pulser) backend. Pulser is a framework
for composing, simulating and executing pulse sequences for neutral-atom quantum devices.
* `visualization`: install the library necessary to visualize quantum circuits.
Expand Down
4 changes: 2 additions & 2 deletions docs/advanced_tutorials/custom-models.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class CustomQuantumModel(QuantumModel):
The custom model can be used like any other `QuantumModel`:
```python exec="on" source="material-block" result="json" session="custom-model"
from qadence import Parameter, RX, CNOT, QuantumCircuit
from qadence import chain, kron, total_magnetization
from qadence import chain, kron, hamiltonian_factory, Z
from sympy import acos

def quantum_circuit(n_qubits):
Expand All @@ -64,7 +64,7 @@ def quantum_circuit(n_qubits):
n_qubits = 4
batch_size = 10
circuit = quantum_circuit(n_qubits)
observable = total_magnetization(n_qubits)
observable = hamiltonian_factory(n_qubits, detuning=Z) # Total magnetization

model = CustomQuantumModel(circuit, observable, backend="pyqtorch")

Expand Down
4 changes: 2 additions & 2 deletions docs/advanced_tutorials/differentiability.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ In Qadence, the GPSR differentiation engine can be selected by passing `diff_mod

```python exec="on" source="material-block" session="differentiability"
from qadence import (FeatureParameter, HamEvo, X, I, Z,
total_magnetization, QuantumCircuit,
hamiltonian_factory, QuantumCircuit,
QuantumModel, BackendName, DiffMode)
import torch

Expand All @@ -83,7 +83,7 @@ block = HamEvo(generator, x)
circuit = QuantumCircuit(n_qubits, block)

# create total magnetization cost operator
obs = total_magnetization(n_qubits)
obs = hamiltonian_factory(n_qubits, detuning=Z)

# create models with AD and GPSR differentiation engines
model_ad = QuantumModel(circuit, obs,
Expand Down
189 changes: 100 additions & 89 deletions docs/digital_analog_qc/pulser-basic.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
!!! warning
This tutorial needs to be fixed.


Qadence offers a direct interface with Pulser[^1], an open-source pulse-level interface written in Python and specifically designed for programming neutral atom quantum computers.

Using directly Pulser requires deep knowledge on pulse-level programming and on how neutral atom devices work. Qadence abstracts out this complexity by using the familiar block-based interface for building pulse sequences in Pulser while leaving the possibility
Expand Down Expand Up @@ -54,7 +58,7 @@ bell_state = chain(
To convert the chain block into a pulse sequence, we define a `Register` with two qubits and combine it to create a circuit as usual. Then we construct a `QuantumModel` with a Pulser backend to convert it into a proper parametrized pulse sequence. Supplying the
parameter values allows to sample from the pulse sequence result.

```python exec="on" source="material-block" session="pulser-basic"
```python exec="on" source="material-block" "html=1" session="pulser-basic"
import torch
import matplotlib.pyplot as plt
from qadence import Register, QuantumCircuit, QuantumModel
Expand Down Expand Up @@ -96,7 +100,10 @@ At variance with other backends, the Pulser one provides the concept of `Device`

A `Device` instance encapsulate all the properties defining a real neutral atoms processor, including but not limited to the maximum laser amplitude for the pulses, the maximum distance between two qubits and the maximum duration of the pulse.

`qadence` offers a simplified interface with only two devices which can be found [here][qadence.backends.pulser.devices]
!!! warning
Fix link below.

Qadence offers a simplified interface with only two devices which can be found [here](/backends.pulser.devices)

* `IDEALIZED` (default): ideal device which should be used only for testing purposes. It does not have any limitation in what can be run with it.
* `REALISTIC`: device specification very similar to a real neutral atom quantum processor.
Expand All @@ -121,16 +128,17 @@ model = QuantumModel(
configuration={"device_type": Device.REALISTIC}
)

# alternatively directly one of the devices available in Pulser
# can also be supplied in the same way
from pulser.devices import AnalogDevice

model = QuantumModel(
circuit,
backend="pulser",
diff_mode="gpsr",
configuration={"device_type": AnalogDevice}
)
# FIXME: Specified device is not supported.
# # alternatively directly one of the devices available in Pulser
# # can also be supplied in the same way
# from pulser.devices import AnalogDevice

# model = QuantumModel(
# circuit,
# backend="pulser",
# diff_mode="gpsr",
# configuration={"device_type": AnalogDevice}
# )
```

## Create your own gate
Expand All @@ -156,32 +164,33 @@ obs = [zz, xy + yx]

Now we define the `QuantumModel` and pass the observable list to it together with the constructed circuit.

```python exec="on" source="material-block" result="json" session="pulser-basic"
from qadence import RX, AnalogRot
```python exec="on" source="material-block" html="1" session="pulser-basic"
# FIXME: protocol not defined
# from qadence import RX, AnalogRot

register = Register(2)
circuit = QuantumCircuit(register, protocol)
model = QuantumModel(circuit, backend="pulser", diff_mode='gpsr')
# register = Register(2)
# circuit = QuantumCircuit(register, protocol)
# model = QuantumModel(circuit, backend="pulser", diff_mode='gpsr')

params = {
"t": torch.tensor([383]), # ns
"y": torch.tensor([torch.pi / 2]),
}
# params = {
# "t": torch.tensor([383]), # ns
# "y": torch.tensor([torch.pi / 2]),
# }

sample = model.sample(params, n_shots=50)[0]
# sample = model.sample(params, n_shots=50)[0]

fig, ax = plt.subplots()
plt.bar(sample.keys(), sample.values())
from docs import docsutils # markdown-exec: hide
print(docsutils.fig_to_html(fig)) # markdown-exec: hide
# fig, ax = plt.subplots()
# plt.bar(sample.keys(), sample.values())
# from docs import docsutils # markdown-exec: hide
# print(docsutils.fig_to_html(fig)) # markdown-exec: hide
```

One can also easily access and manipulate the underlying pulse sequence.

```python exec="on" source="material-block" html="1" session="pulser-basic"
model.assign_parameters(params).draw(draw_phase_area=True, show=False)
from docs import docsutils # markdown-exec: hide
print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
# model.assign_parameters(params).draw(draw_phase_area=True, show=False)
# from docs import docsutils # markdown-exec: hide
# print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
```

## Large qubits registers
Expand All @@ -191,48 +200,50 @@ with two or three qubits. But for the blocks we have so far, large registers
work better with a square loop layout like the following.

```python exec="on" source="material-block" html="1" session="pulser-basic"
register = Register.square(qubits_side=4)
register.draw(show=False)
from docs import docsutils # markdown-exec: hide
print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
# register = Register.square(qubits_side=4)
# register.draw(show=False)
# from docs import docsutils # markdown-exec: hide
# print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
```

In those cases, global pulses are preferred to generate entanglement to avoid
changing the addressing pattern on the fly.

```python exec="on" source="material-block" html="1" session="pulser-basic"
protocol = chain(
entangle("t"),
AnalogRY(torch.pi / 2),
)
# from qadence import AnalogRY

register = Register.square(qubits_side=2)
circuit = QuantumCircuit(register, protocol)
model = QuantumModel(circuit, backend="pulser", diff_mode="gpsr")
# protocol = chain(
# entangle("t"),
# AnalogRY(torch.pi / 2),
# )

# add modulation to the pulse sequence by modifying the
# backend configuration
model.backend.backend.config.with_modulation = True
# register = Register.square(qubits_side=2)
# circuit = QuantumCircuit(register, protocol)
# model = QuantumModel(circuit, backend="pulser", diff_mode="gpsr")

params = {
"x": torch.tensor([3*torch.pi/2]), # ns
}
# # add modulation to the pulse sequence by modifying the
# # backend configuration
# model.backend.backend.config.with_modulation = True

sample = model.sample(params, n_shots=500)[0]
# params = {
# "x": torch.tensor([3*torch.pi/2]), # ns
# }

fig, ax = plt.subplots()
ax.bar(sample.keys(), sample.values())
plt.xticks(rotation='vertical')
from docs import docsutils # markdown-exec: hide
print(docsutils.fig_to_html(fig)) # markdown-exec: hide
# sample = model.sample(params, n_shots=500)[0]

# fig, ax = plt.subplots()
# ax.bar(sample.keys(), sample.values())
# plt.xticks(rotation='vertical')
# from docs import docsutils # markdown-exec: hide
# print(docsutils.fig_to_html(fig)) # markdown-exec: hide
```

Again, let's plot the corresponding pulse sequence.

```python exec="on" source="material-block" html="1" session="pulser-basic"
model.assign_parameters(params).draw(draw_phase_area=True, show=False)
from docs import docsutils # markdown-exec: hide
print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
# model.assign_parameters(params).draw(draw_phase_area=True, show=False)
# from docs import docsutils # markdown-exec: hide
# print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
```

!!! note
Expand All @@ -247,40 +258,40 @@ version of a quantum neural network circuit with feature map and variational
ansatz.

```python exec="on" source="material-block" html="1" session="pulser-basic"
from qadence import kron, fourier_feature_map
from qadence.operations import RX, RY, AnalogRX

hea_one_layer = chain(
kron(RY(0, "th00"), RY(1, "th01")),
kron(RX(0, "th10"), RX(1, "th11")),
kron(RY(0, "th20"), RY(1, "th21")),
entangle("t", qubit_support=(0,1)),
)

protocol = chain(
fourier_feature_map(1, param="x"),
hea_one_layer,
AnalogRX(torch.pi/4)
)

register = Register(2)
circuit = QuantumCircuit(register, protocol)
model = QuantumModel(circuit, backend="pulser", diff_mode="gpsr")

params = {
"x": torch.tensor([0.8]), # rad
"t": torch.tensor([900]), # ns
"th00": torch.rand(1), # rad
"th01": torch.rand(1), # rad
"th10": torch.rand(1), # rad
"th11": torch.rand(1), # rad
"th20": torch.rand(1), # rad
"th21": torch.rand(1), # rad
}

model.assign_parameters(params).draw(draw_phase_area=True, show=True)
from docs import docsutils # markdown-exec: hide
print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
# from qadence import kron, fourier_feature_map
# from qadence.operations import RX, RY, AnalogRX

# hea_one_layer = chain(
# kron(RY(0, "th00"), RY(1, "th01")),
# kron(RX(0, "th10"), RX(1, "th11")),
# kron(RY(0, "th20"), RY(1, "th21")),
# entangle("t", qubit_support=(0,1)),
# )

# protocol = chain(
# fourier_feature_map(1, param="x"),
# hea_one_layer,
# AnalogRX(torch.pi/4)
# )

# register = Register(2)
# circuit = QuantumCircuit(register, protocol)
# model = QuantumModel(circuit, backend="pulser", diff_mode="gpsr")

# params = {
# "x": torch.tensor([0.8]), # rad
# "t": torch.tensor([900]), # ns
# "th00": torch.rand(1), # rad
# "th01": torch.rand(1), # rad
# "th10": torch.rand(1), # rad
# "th11": torch.rand(1), # rad
# "th20": torch.rand(1), # rad
# "th21": torch.rand(1), # rad
# }

# model.assign_parameters(params).draw(draw_phase_area=True, show=True)
# from docs import docsutils # markdown-exec: hide
# print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
```

## References
Expand Down
Loading

0 comments on commit 81b2e0b

Please sign in to comment.