Skip to content
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

Docs suggestions. #33

Merged
merged 17 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions .github/workflows/test_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ on:
pull_request:
branches:
- main
paths:
- docs
workflow_dispatch: {}

concurrency:
Expand All @@ -17,11 +15,11 @@ concurrency:

jobs:
test_qadence_ubuntu:
name: Test Qadence (ubuntu)
name: Test Qadence docs (ubuntu)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Select Python 3.10
- name: Set up Python 3.10
uses: actions/setup-python@v4
with:
python-version: '3.10'
Expand Down
24 changes: 11 additions & 13 deletions docs/digital_analog_qc/daqc-basics.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
# Digital-Analog Quantum Computation

_**Digital-analog quantum computation**_ (DAQC) is a universal quantum computing
paradigm [^1]. The main ingredients of a DAQC program are:
paradigm[^1], based on two primary computations:

- Fast single-qubit operations (digital).
- Multi-partite entangling operations acting on all qubits (analog).

Analog operations are typically assumed to follow device-specific interacting qubit Hamiltonians, such as the Ising Hamiltonian [^2]. The most common realization of the DAQC paradigm is on neutral atoms quantum computing platforms.
The DAQC paradigm is typically implemented on quantum computing hardware based on neutral-atoms where both these computations are realizable.

## Digital-Analog Emulation

Qadence simplifies the execution of DAQC programs on neutral-atom devices
by providing a simplified interface for adding interaction and interfacing
with pulse-level programming in `pulser`[^3].
Qadence simplifies the execution of DAQC programs on either emulated or real neutral-atom devices
by providing a simplified interface for customizing interactions and interfacing
with pulse-level programming in `Pulser`[^3].

## Digital-Analog Transformation

## DAQC Transform

Furthermore, essential to digital-analog computation is the ability to represent an arbitrary Hamiltonian
with the evolution of a fixed and device-amenable Hamiltonian. Such a transform was described in the
DAQC[^2] paper for ZZ interactions, which is natively implemented in Qadence.
Furthermore, the essence of digital-analog computation is the ability to represent any analog operation, _i.e._ any arbitrary Hamiltonian, using an
auxiliary device-amenable Hamiltonian, such as the ubiquitous Ising model[^2]. This is at the core of the DAQC implementation in Qadence.

## References

[^1]: [Dodd et al., Universal quantum computation and simulation using any entangling Hamiltonian and local unitaries, PRA 65, 040301 (2002).](https://arxiv.org/abs/quant-ph/0106064)
[^1]: [Dodd _et al._, Universal quantum computation and simulation using any entangling Hamiltonian and local unitaries, PRA 65, 040301 (2002).](https://arxiv.org/abs/quant-ph/0106064)

[^2]: [Parra-Rodriguez et al., Digital-Analog Quantum Computation, PRA 101, 022305 (2020).](https://arxiv.org/abs/1812.03637)
[^2]: [Pulser: An open-source package for the design of pulse sequences in programmable neutral-atom arrays](https://pulser.readthedocs.io/en/stable/)

[^3]: [Pulser: An open-source package for the design of pulse sequences in programmable neutral-atom arrays](https://pulser.readthedocs.io/en/stable/)
[^3]: [Parra-Rodriguez _et al._, Digital-Analog Quantum Computation, PRA 101, 022305 (2020).](https://arxiv.org/abs/1812.03637)
157 changes: 26 additions & 131 deletions docs/digital_analog_qc/pulser-basic.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The current backend has the following operations:

| gate | description | trainable parameter |
|-------------|--------------------------------------------------------------------------------------------------|---------------------|
| `Rot` | Single qubit rotations. | rotation angle |
| `RX`, `RY` | Single qubit rotations. | rotation angle |
| `AnalogRot` | Span a single qubit rotation among the entire register. | rotation angle |
| `entangle` | Fully entangle the register. | interaction time |
| `wait` | An idle block to wait for the system to evolve for a specific time according to the interaction. | free evolution time |
Expand Down Expand Up @@ -67,15 +67,15 @@ To run the pulse sequence we have to provide values for the parametrized block w
import torch

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

# Visualise the final state vector
final_vector = model.run(params)
print(final_vector)

sample = model.sample(params, n_shots=50)[0]
sample = model.sample(params, n_shots=500)[0]
print(sample)
```
```python exec="on" source="material-block" html="1" session="pulser-basic"
Expand All @@ -97,151 +97,47 @@ print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
```


## Create your own gate
A big advantage of the `chain` block is it makes it easy to create complex
operations from simple ones. Take the entanglement operation as an example.

The operation consists of moving _all_ the qubits to the `X` basis having the
atoms' interaction perform a controlled-Z operation during the free evolution.
And we can easily recreate this pattern using the `AnFreeEvo` and `AnRY` blocks.

```python exec="on" source="material-block" session="pulser-basic"
from qadence import AnalogRY, chain, wait

def my_entanglement(duration):
return chain(
AnalogRY(-torch.pi / 2),
wait(duration)
)
```
## Working with observables

Then we proceed as before.
You can calculate expectation value of `Observables` in the Pulser backend the same way as in other backends by using the `expectation` method.
First we create the desired observables using Qadence blocks.

```python exec="on" source="material-block" session="pulser-basic" html="1"
protocol = chain(
my_entanglement("t"),
RY(0, "y"),
)

register = Register(2)
circuit = QuantumCircuit(register, protocol)
model = QuantumModel(circuit, backend="pulser", diff_mode='gpsr')
```python exec="on" source="material-block" session="pulser-basic"
from qadence.operations import I, X, Y, Z, kron

params = {
"t": torch.tensor([383]), # ns
"y": torch.tensor([torch.pi / 2]),
}
zz = kron(I(0), Z(1), I(2), Z(3))
xy = kron(I(0), X(1), I(2), Y(3))
yx = kron(I(0), Y(1), I(2), X(3))

sample = model.sample(params, n_shots=50)[0]
obs = [zz, xy + yx]

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
```

```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
```

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

## Large qubits registers
The constructor `Register(n_qubits)` generates a linear register that works fine
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
```

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" result="json" session="pulser-basic"
from qadence import RX, AnalogRot

```python exec="on" source="material-block" html="1" session="pulser-basic"
protocol = chain(
entangle("t"),
AnalogRY(torch.pi / 2),
blocks = chain(
RX(0, "x"),
RX(2, "x"),
AnalogRot(duration=300, omega=5*torch.pi)
)

register = Register.square(qubits_side=2)
circuit = QuantumCircuit(register, protocol)
model = QuantumModel(circuit, backend="pulser", diff_mode='gpsr')
model.backend.backend.config.with_modulation = True
circuit = QuantumCircuit(register, blocks)
model = QuantumModel(circuit, observable=obs, backend="pulser", diff_mode="gpsr")

params = {
"t": torch.tensor([1956]), # ns
"x": torch.tensor([3*torch.pi/2]), # ns
}

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
```
```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
```

!!! note
The gates shown here don't work with arbitrary registers since they rely on
the registered geometry to work properly.

## Working with observables

The current backend version does not support Qadence `Observables`. However, it's
still possible to use the regular Pulser simulations to calculate expected
values.

To do so, we use the `assign_parameters` property to recover the Pulser sequence.

```python exec="on" source="material-block" session="pulser-basic"
params = {
"t": torch.tensor([383]), # ns
}

built_sequence = model.assign_parameters(params)
final_result = model.expectation(values=params)
```


Next, we create the desired observables using `Qutip` [^2].


```python exec="on" source="material-block" session="pulser-basic"
import qutip

zz = qutip.tensor([qutip.qeye(2), qutip.sigmaz(), qutip.qeye(2), qutip.sigmaz()])
xy = qutip.tensor([qutip.qeye(2), qutip.sigmax(), qutip.qeye(2), qutip.sigmay()])
yx = qutip.tensor([qutip.qeye(2), qutip.sigmay(), qutip.qeye(2), qutip.sigmax()])
```


We use the Pulser `Simulation` class to run the sequence and call the method `expect` over our observables.

```python exec="on" source="material-block" result="json" session="pulser-basic"
from pulser_simulation import Simulation

sim = Simulation(built_sequence)
result = sim.run()

final_result = result.expect([zz, xy + yx])
print(final_result[0][-1], final_result[1][-1])
```

We use the Pulser `Simulation` class to run the sequence and call the method
`expect` over our observables.

Here the `final_result` contains the expected values during the evolution (one
point per nanosecond). In this case, looking only at the final values, we see
the qubits `q0` and `q2` are on the *Bell-diagonal* state $\Big(|00\rangle -i |11\rangle\Big)/\sqrt{2}$.
We use the `expectation` method of the `QuantumModel` instance to calculate the expectation values.
Here the `final_result` contains the expected values of observables in `obs` list.

```python exec="on" source="material-block" html="1" session="pulser-basic"
from qadence import fourier_feature_map, RX, RY
Expand Down Expand Up @@ -272,4 +168,3 @@ print(docsutils.fig_to_html(plt.gcf())) # markdown-exec: hide
## References

[^1]: [Pulser: An open-source package for the design of pulse sequences in programmable neutral-atom arrays](https://pulser.readthedocs.io/en/stable/)
[^2]: [Qutip](https://qutip.org)
19 changes: 19 additions & 0 deletions docs/environment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: readthedocs
channels:
- defaults
dependencies:
- python=3.10
- python-graphviz
- pip
- pip:
- markdown-exec
- mkdocs-exclude
- mkdocs-jupyter
- mkdocs-material
- mkdocs-section-index==0.3.6
- mkdocs==1.5.2
- mkdocstrings
- mkdocstrings-python
- -e ../
- pulser>=0.12.0
- amazon-braket-sdk
Loading