Skip to content

Commit

Permalink
Qadence Transition (#34)
Browse files Browse the repository at this point in the history
* legacy prototype 14/01/2025

* remove folder from tracking

* test

* test

* block system

* check

* block system done

* parameters

* reorganize

* function lists

* complete transition.md

* complete transition.md

* apply review

---------

Co-authored-by: Sungwoo AHN <[email protected]>
  • Loading branch information
sungwoo-pasqal and Sungwoo AHN authored Jan 28, 2025
1 parent d66a9a9 commit 2f2e632
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 14 deletions.
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)

0 comments on commit 2f2e632

Please sign in to comment.