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

[Draft] Qadence Transition #34

Merged
merged 14 commits into from
Jan 28, 2025
192 changes: 192 additions & 0 deletions docs/transition.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@

# Transition Guide
This document is for Qadence users to use Qadence2 funcationalities with Qadence format.
For Qadence2 code details, please refer to https://github.com/pasqal-io/qadence2-core.

## Operators

Qadence operators can be used in the same way (including the order of target and control) in Qadence 2.

Qadence
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence import RX, CNOT

rx = RX(0, 0.5)
cnot = CNOT(0, 1)
```

Qadence2
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence2.extensions.legacy import RX, CNOT

rx = RX(0, 0.5)
cnot = CNOT(0, 1)
```

## Block System

Building quantum expressions with `chain` and `kron` is also identical.

Qadence
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence import X, Y, chain, kron

chain_0 = chain(X(0), Y(0))
chain_1 = chain(X(1), Y(1))

kron_block = kron(chain_0, chain_1)
```

Qadence2
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence2.extensions.legacy import X, Y, chain, kron

chain_0 = chain(X(0), Y(0))
chain_1 = chain(X(1), Y(1))

kron_block = kron(chain_0, chain_1)
```

## Compose Functions

Custom gates can also be applied in the same way, and with the definition change, Qadence2 now use `*` instead of `@`.

Qadence
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence import X, Y, add

def xy_int(i: int, j: int):
return (1/2) * (X(i)@X(j) + Y(i)@Y(j))

n_qubits = 3

xy_ham = add(xy_int(i, i+1) for i in range(n_qubits-1))
```

Qadence2
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence2.extensions.legacy import X, Y, add

def xy_int(i: int, j: int):
return (1/2) * (X(i)*X(j) + Y(i)*Y(j))

n_qubits = 3

xy_ham = add(xy_int(i, i+1) for i in range(n_qubits-1))
```

## Quantum Fourier Transform Example

The code initializes a quantum circuit with `CPHASE` gates and applies `qft_layer` to transform the input quantum state into its frequency domain representation.

Qadence
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence import H, CPHASE, PI, chain, kron

def qft_layer(qs: tuple, l: int):
cphases = chain(CPHASE(qs[j], qs[l], PI/2**(j-l)) for j in range(l+1, len(qs)))
return H(qs[l]) * cphases

def qft(qs: tuple):
return chain(qft_layer(qs, l) for l in range(len(qs)))
```

Qadence2
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence2.extensions.legacy import H, CPHASE, PI, chain, kron

def qft_layer(qs: tuple, l: int):
cphases = chain(CPHASE(qs[j], qs[l], PI/2**(j-l)) for j in range(l+1, len(qs)))
return H(qs[l]) * cphases

def qft(qs: tuple):
return chain(qft_layer(qs, l) for l in range(len(qs)))
```


## Quantum Models

Qadence2 uses `Expression` and `IR` to represent the details of the quantum circuits and algorithms and `Platform` to match the executing backend, whereas Qadence is not separated.
As a result, Qadence can call `run,` `sample,` and `expectation` with the circuit representation itself.
However, Qadence2 requires it first to be compiled to execute them with the backends.
Qadence2 uses `compile_to_model` and `compile_to_backend` to compile them into `model` and `compiled_model,` respectively. After this procedure, it is only ready to execute `run,` `sample,` and `expectation.`

Qadence
```python exec="on" source="material-block" html="1" session="getting_started"
import torch
from qadence import QuantumModel, PI, Z
from qadence import QuantumCircuit, RX, RY, chain, kron
from qadence import FeatureParameter, VariationalParameter

phi = FeatureParameter("phi")

block = chain(
kron(RX(0, phi), RY(1, phi)),
)

circuit = QuantumCircuit(2, block)

observable = Z(0) + Z(1)

model = QuantumModel(circuit, observable)

values = {"phi": torch.tensor(PI)}

wf = model.run(values)
xs = model.sample(values, n_shots=100)
ex = model.expectation(values)
```

Qadence2
```python exec="on" source="material-block" html="1" session="getting_started"
import torch
from qadence2.extensions.legacy import PI, Z, RX, RY, chain, kron
from qadence2_expressions import compile_to_model, parameter
from qadence2_platforms.compiler import compile_to_backend

phi = parameter("phi")

block = chain(
kron(RX(0, phi), RY(1, phi)),
)

model = compile_to_model(block)
compiled_model = compile_to_backend(model, "pyqtorch")

observable = Z(0) + Z(1)

values = {"phi": torch.tensor(PI)}

wf = compiled_model.run(values)
xs = compiled_model.sample(values, shots=100)
ex = compiled_model.expectation(values, observable=observable)
```

## Quantum Registers

Qadence2 can represent the relationships between qubits using `grid_type`, `grid_scale`, and `qubit_position`. The `connectivity` in `qadence2_ir` is for accurately representing the connectivity between qubits.

Qadence
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence import Register

reg = Register.all_to_all(n_qubits = 4)
reg_line = Register.line(n_qubits = 4)
reg_squre = Register.square(qubits_side = 2)
reg_triang = Register.triangular_lattice(n_cells_row = 2, n_cells_col = 2)
```

Qadence2
```python exec="on" source="material-block" html="1" session="getting_started"
from qadence2_expressions.core import set_grid_type, set_qubits_positions, set_grid_scale
from qadence2.extensions.legacy import PI, RX, RY, CZ
from qadence2_expressions import compile_to_model

expr = RX(2, PI / 2) * CZ() * RY(0, PI / 2)

set_grid_type("linear") # "square", "triangular"
set_qubits_positions([(-2, 1), (0, 0), (3, 1)])
set_grid_scale(0.4)

compile_to_model(expr)
```
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ repo_name: "qadence2_core"
nav:
- Overview: index.md
- Sample page: getting_started_example.md
- Qadence transition: transition.md

theme:
name: material
Expand Down
11 changes: 8 additions & 3 deletions qadence2/extensions/legacy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from __future__ import annotations

from qadence2_expressions.operators import CZ, X, Y, Z
from qadence2_expressions.operators import CZ, H, X, Y, Z

from .model import QuantumModel
from .operators import CNOT, RX, RY, RZ, N
from .utils import add, chain, kron, mul, pow
from .operators import CNOT, CPHASE, PHASE, RX, RY, RZ, N, T
from .utils import PI, add, chain, kron, mul, pow

__all__ = [
"QuantumModel",
Expand All @@ -22,4 +22,9 @@
"Z",
"CZ",
"N",
"PI",
"H",
"T",
"PHASE",
"CPHASE",
]
24 changes: 23 additions & 1 deletion qadence2/extensions/legacy/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@

import qadence2_expressions.operators as ops
from qadence2_expressions import variable
from qadence2_expressions.core.constructors import (
parametric_operator,
promote,
unitary_hermitian_operator,
)
from qadence2_expressions.core.expression import Expression
from qadence2_expressions.operators import _join_rotation

# The N = (1/2)(I-Z) operator
N = ops.Z1


def CNOT(target: int, control: int) -> Expression:
def CNOT(control: int, target: int) -> Expression:
return ops.NOT(target=(target,), control=(control,))


Expand All @@ -29,3 +35,19 @@ def _get_variable(expr: Expression | str | float) -> Expression:
return variable(expr)
if isinstance(expr, (Expression, float)):
return expr


def T(target: int) -> Expression:
return unitary_hermitian_operator("T")(target=(target,))


def PHASE(target: int, parameters: Expression | str | float) -> Expression:
return parametric_operator("PHASE", promote(_get_variable(parameters)), join=_join_rotation)(
target=(target,)
)


def CPHASE(control: int, target: int, parameters: Expression | str | float) -> Expression:
return parametric_operator("CPHASE", promote(_get_variable(parameters)), join=_join_rotation)(
target=(target,), control=(control,)
)
31 changes: 21 additions & 10 deletions qadence2/extensions/legacy/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
from __future__ import annotations

import math
from enum import Enum, auto
from typing import Generator

from qadence2_expressions.core.expression import Expression

PI = math.pi


class ParadigmStrategy(Enum):
DIGITAL = auto()
Expand All @@ -14,21 +17,29 @@ class ParadigmStrategy(Enum):
RYDBERG = auto()


def add(expr: Generator | list[Expression]) -> Expression:
return Expression.add(*tuple(expr))
def add(*expr: Generator | list[Expression]) -> Expression:
if len(expr) > 0 and isinstance(expr[0], Generator):
return Expression.add(*tuple(*expr))
return Expression.add(*expr)


def mul(expr: Generator | list[Expression]) -> Expression:
return Expression.mul(*tuple(expr))
def mul(*expr: Generator | list[Expression]) -> Expression:
if len(expr) > 0 and isinstance(expr[0], Generator):
return Expression.mul(*tuple(*expr))
return Expression.mul(*expr)


def chain(expr: Generator | list[Expression]) -> Expression:
return mul(expr)
def chain(*expr: Generator | list[Expression]) -> Expression:
return mul(*expr)


def kron(expr: Generator | list[Expression]) -> Expression:
return Expression.kron(*tuple(expr))
def kron(*expr: Generator | list[Expression]) -> Expression:
if len(expr) > 0 and isinstance(expr[0], Generator):
return Expression.kron(*tuple(*expr))
return Expression.kron(*expr)


def pow(expr: Generator | list[Expression]) -> Expression:
return Expression.pow(*tuple(expr))
def pow(*expr: Generator | list[Expression]) -> Expression:
if len(expr) > 0 and isinstance(expr[0], Generator):
return Expression.pow(*tuple(*expr))
return Expression.pow(*expr)
Loading