Skip to content

Commit

Permalink
add backend template, tests, custom backend imports
Browse files Browse the repository at this point in the history
  • Loading branch information
Doomsk committed Aug 28, 2024
1 parent bb81974 commit 6de6e0a
Show file tree
Hide file tree
Showing 27 changed files with 421 additions and 139 deletions.
7 changes: 6 additions & 1 deletion qadence2_platforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from .abstracts import AbstractInterface

BACKEND_FOLDER_NAME = "backend"
PACKAGE_NAME = __name__
BACKEND_FOLDER_NAME = "backends"
USER_BACKENDS_FOLDER_NAME = "user_backends"
CUSTOM_BACKEND_FOLDER_NAME = "custom_backends"
TEMPLATES_FOLDER_NAME = "templates"

BASE_BACKEND_MODULE = f"{PACKAGE_NAME}.{BACKEND_FOLDER_NAME}"
USER_BACKEND_MODULE = f"{PACKAGE_NAME}.{USER_BACKENDS_FOLDER_NAME}"
20 changes: 10 additions & 10 deletions qadence2_platforms/abstracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ def info(self) -> dict[str, Any]:
@abstractmethod
def sequence(self) -> SequenceType:
"""
Outputs the backend-native sequence.
Outputs the backends-native sequence.
:return: The defined backend-native sequence.
:return: The defined backends-native sequence.
"""
pass

@abstractmethod
def set_parameters(self, params: dict[str, ParameterType]) -> None:
"""
Sets valid parameters for the backend to use it during simulation/execution step.
Sets valid parameters for the backends to use it during simulation/execution step.
:param params: the fixed parameters to be used by the backend.
:param params: the fixed parameters to be used by the backends.
"""
pass

Expand All @@ -67,9 +67,9 @@ def run(
:param values: dictionary of user-input parameters
:param callback: a callback function if necessary to run some extra processing
:param kwargs: any extra argument that are backend specific can be included in the
:param kwargs: any extra argument that are backends specific can be included in the
child method.
:return: any result type according to what is expected by the backend `run` method
:return: any result type according to what is expected by the backends `run` method
"""
pass

Expand All @@ -89,9 +89,9 @@ def sample(
:param values: dictionary of user-input parameters
:param shots: number of shots
:param callback: a callback function if necessary to run some extra processing
:param kwargs: any extra argument that are backend specific can be included in the
:param kwargs: any extra argument that are backends specific can be included in the
child method
:return: any result type according to what is expected by the backend `sample` method
:return: any result type according to what is expected by the backends `sample` method
"""
pass

Expand All @@ -111,8 +111,8 @@ def expectation(
:param values: dictionary of user-input parameters
:param observable: list of observables
:param callback: a callback function if necessary to run some extra processing
:param kwargs: any extra argument that are backend specific can be included in the
:param kwargs: any extra argument that are backends specific can be included in the
child method
:return: any result type according to what is expected by the backend `expectation` method
:return: any result type according to what is expected by the backends `expectation` method
"""
pass
3 changes: 0 additions & 3 deletions qadence2_platforms/backend/pyqtorch/__init__.py

This file was deleted.

File renamed without changes.
3 changes: 3 additions & 0 deletions qadence2_platforms/backends/pyqtorch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from __future__ import annotations

from .compiler import compile_to_backend
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
import pyqtorch as pyq
from qadence2_ir.types import Load, Model, QuInstruct

from qadence2_platforms.backends.pyqtorch.embedding import Embedding
from qadence2_platforms.backends.pyqtorch.interface import Interface
from qadence2_platforms.backends.pyqtorch.register import RegisterInterface

logger = getLogger(__name__)


Expand Down Expand Up @@ -43,3 +47,12 @@ def compile(
else:
pyq_operations.append(native_op(*native_support))
return pyq.QuantumCircuit(model.register.num_qubits, pyq_operations)


def compile_to_backend(model: Model) -> Interface:
register_interface = RegisterInterface(
model.register.num_qubits, model.register.options.get("init_state")
)
embedding = Embedding(model)
native_circ = Compiler().compile(model)
return Interface(register_interface, embedding, native_circ)
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@

import pyqtorch
import torch
from qadence2_ir.types import Model

from qadence2_platforms.abstracts import (
AbstractInterface,
)

from .compiler import Compiler
from .embedding import Embedding
from .register import RegisterInterface

Expand All @@ -28,7 +26,7 @@ class Interface(
torch.Tensor,
],
):
"""A class holding register,embedding, circuit, native backend and optional observable."""
"""A class holding register,embedding, circuit, native backends and optional observable."""

def __init__(
self,
Expand Down Expand Up @@ -165,12 +163,3 @@ def expectation(
observable=observable,
**kwargs,
)


def compile_to_backend(model: Model) -> Interface:
register_interface = RegisterInterface(
model.register.num_qubits, model.register.options.get("init_state")
)
embedding = Embedding(model)
native_circ = Compiler().compile(model)
return Interface(register_interface, embedding, native_circ)
7 changes: 3 additions & 4 deletions qadence2_platforms/compiler.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
from __future__ import annotations

from pathlib import Path

from qadence2_ir.types import Model

from qadence2_platforms.utils.module_importer import module_loader

from .abstracts import AbstractInterface as Interface
from .misc import module_loader


def compile_to_backend(backend: str | Path, model: Model) -> Interface:
def compile_to_backend(backend: str, model: Model) -> Interface:
plat = module_loader(backend)
return plat.compile_to_backend(model)
98 changes: 0 additions & 98 deletions qadence2_platforms/misc.py

This file was deleted.

3 changes: 3 additions & 0 deletions qadence2_platforms/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from __future__ import annotations

from .backend_template import BackendTemplate
111 changes: 111 additions & 0 deletions qadence2_platforms/utils/backend_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
from __future__ import annotations

import os
import traceback
from pathlib import Path
from shutil import copyfile

from qadence2_platforms import CUSTOM_BACKEND_FOLDER_NAME, TEMPLATES_FOLDER_NAME
from qadence2_platforms.utils.module_importer import _resolve_module_path

try:
from tkinter import filedialog as fd
except ImportError:
traceback.print_exc()
print(
"\n\nYou may need to install `tkinter` to use this feature. "
"On Mac:\n"
" `brew install [email protected]`\n"
" In case you use a different python version, replace `3.10` by it.\n\n"
"On Linux: `apt-get install python-tk`\n\n"
)
exit()


class BackendTemplate:
def __init__(self) -> None:
self._pwd: Path = Path()
self._root_backends_name: str = CUSTOM_BACKEND_FOLDER_NAME
self._backend_name: str = ""
self._backend_path: Path = Path()
self._template_path: Path = self._get_template_path()
self._template_files_list: list[str] = os.listdir(self._template_path)

def _new_file_path(self, name: str) -> Path:
return self._backend_path / name

def _get_template_path(self) -> Path:
return Path(__file__).parent / TEMPLATES_FOLDER_NAME

def create_folder(self, backend_name: str, current_path: str | Path) -> bool:
self._backend_name = backend_name
self._pwd = Path(current_path)
self._backend_path = Path(
self._pwd, self._root_backends_name, self._backend_name
)
already_exists = os.path.exists(self._backend_path)
self._backend_path.mkdir(parents=True, exist_ok=True)
return already_exists

def create_files(self) -> None:
for file in self._template_files_list:
self._new_file_path(file).touch()
copyfile(self._template_path / file, self._backend_path / file)

def create_template(
self,
backend_name: str,
gui: bool = True,
use_this_dir: str | Path | None = None,
) -> bool:
print(
"\nCreating a backend template.\n\n"
"You need to select a directory where all the custom backends will be located.\n"
" The organization will be as follows:\n\n"
" selected_root_dir/\n"
" └── custom_backends\n"
" ├── custom_backend1\n"
" │ ├── __init__.py\n"
" │ ├── compiler.py\n"
" │ └── interface.py\n"
" ├── custom_backend2\n"
" ...\n"
""
)

selected_dir: str | Path

if gui and use_this_dir is None:
selected_dir = fd.askdirectory(
initialdir=Path(__file__).parent,
title="Select a directory",
mustexist=True,
)
else:
if use_this_dir is None:
selected_dir = input("Paste the directory: ")
else:
selected_dir = use_this_dir

if selected_dir:
try:
already_exists = self.create_folder(
backend_name, current_path=selected_dir
)
self.create_files()
except Exception:
traceback.print_exc()
return False
else:
action = "replaced" if already_exists else "created"
print(
f"Backend template at {self._backend_path} has been {action} with success!"
)
return _resolve_module_path(
Path(selected_dir, CUSTOM_BACKEND_FOLDER_NAME)
)
else:
print(
"You must select a directory to create the template. Creation suspended."
)
return True
Loading

0 comments on commit 6de6e0a

Please sign in to comment.