Skip to content

Commit

Permalink
Test qasm (#17)
Browse files Browse the repository at this point in the history
Completed QASM interface for all gates and tested basic funcs.

---------

Signed-off-by: burgholzer <[email protected]>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Lukas Burgholzer <[email protected]>
  • Loading branch information
3 people authored Apr 30, 2024
1 parent 425e102 commit c1dee1a
Show file tree
Hide file tree
Showing 13 changed files with 251 additions and 66 deletions.
Binary file removed MQT_Qudits_Tutorial/MQT slide.pdf
Binary file not shown.
4 changes: 2 additions & 2 deletions src/mqt/qudits/quantum_circuit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from .circuit import QuantumCircuit
from .circuit import QuantumCircuit, QuantumRegister
from .qasm import QASM

__all__ = ["QASM", "QuantumCircuit"]
__all__ = ["QASM", "QuantumCircuit", "QuantumRegister"]
66 changes: 47 additions & 19 deletions src/mqt/qudits/quantum_circuit/circuit.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import annotations

import copy
import locale
from pathlib import Path
from typing import TYPE_CHECKING

from .components.quantum_register import QuantumRegister
import numpy as np

from .components import ClassicRegister, QuantumRegister
from .gates import (
LS,
MS,
Expand All @@ -28,12 +30,16 @@
from .qasm import QASM

if TYPE_CHECKING:
import numpy as np

from .components.extensions.controls import ControlData
from .gate import Gate


def is_not_none_or_empty(variable):
return (variable is not None and hasattr(variable, "__iter__") and len(variable) > 0) or (
isinstance(variable, np.ndarray) and variable.size > 0
)


def add_gate_decorator(func):
def gate_constructor(circ, *args):
gate = func(circ, *args)
Expand Down Expand Up @@ -67,14 +73,18 @@ class QuantumCircuit:
}

def __init__(self, *args) -> None:
self.cl_inverse_sitemap = {}
self.inverse_sitemap = {}
self.number_gates = 0
self.instructions = []
self.quantum_registers = []
self.classic_registers = []
self._sitemap = {}
self._classic_site_map = {}
self._num_cl = 0
self._num_qudits = 0
self._dimensions = []
self.path_save = None

if len(args) == 0:
return
Expand Down Expand Up @@ -104,14 +114,18 @@ def dimensions(self):
return self._dimensions

def reset(self) -> None:
self.cl_inverse_sitemap = {}
self.inverse_sitemap = {}
self.number_gates = 0
self.instructions = []
self.quantum_registers = []
self.inverse_sitemap = {}
self.classic_registers = []
self._sitemap = {}
self._classic_site_map = {}
self._num_cl = 0
self._num_qudits = 0
self._dimensions = []
self.path_save = None

def copy(self):
return copy.deepcopy(self)
Expand All @@ -127,6 +141,16 @@ def append(self, qreg: QuantumRegister) -> None:
self._sitemap[(str(qreg.label), i)] = (num_lines_stored + i, qreg.dimensions[i])
self.inverse_sitemap[num_lines_stored + i] = (str(qreg.label), i)

def append_classic(self, creg: ClassicRegister) -> None:
self.classic_registers.append(creg)
self._num_cl += creg.size

num_lines_stored = len(self._classic_site_map)
for i in range(creg.size):
creg.local_sitemap[i] = num_lines_stored + i
self._classic_site_map[(str(creg.label), i)] = (num_lines_stored + i,)
self.cl_inverse_sitemap[num_lines_stored + i] = (str(creg.label), i)

@add_gate_decorator
def csum(self, qudits: list[int]):
return CSum(
Expand All @@ -140,7 +164,7 @@ def cu_one(self, qudits: int, parameters: np.ndarray, controls: ControlData | No
)

@add_gate_decorator
def cu_two(self, qudits: int, parameters: np.ndarray, controls: ControlData | None = None):
def cu_two(self, qudits: list[int], parameters: np.ndarray, controls: ControlData | None = None):
return CustomTwo(
self,
"CUt" + str([self.dimensions[i] for i in qudits]),
Expand All @@ -151,7 +175,7 @@ def cu_two(self, qudits: int, parameters: np.ndarray, controls: ControlData | No
)

@add_gate_decorator
def cu_multi(self, qudits: int, parameters: np.ndarray, controls: ControlData | None = None):
def cu_multi(self, qudits: list[int], parameters: np.ndarray, controls: ControlData | None = None):
return CustomMulti(
self,
"CUm" + str([self.dimensions[i] for i in qudits]),
Expand Down Expand Up @@ -259,14 +283,19 @@ def set_instructions(self, sequence: list[Gate]):
return self

def from_qasm(self, qasm_prog) -> None:
"""Create a circuit from qasm text"""
self.reset()
qasm_parser = QASM().parse_ditqasm2_str(qasm_prog)
instructions = qasm_parser["instructions"]
temp_sitemap = qasm_parser["sitemap"]
cl_sitemap = qasm_parser["sitemap_classic"]

for qreg in QuantumRegister.from_map(temp_sitemap):
self.append(qreg)

for creg in ClassicRegister.from_map(cl_sitemap):
self.append_classic(creg)

qasm_set = self.get_qasm_set()

for op in instructions:
Expand All @@ -285,7 +314,7 @@ def from_qasm(self, qasm_prog) -> None:
# Extract the first element from each tuple and return as a list
else:
qudits_call = [t[0] for t in list(tuples_qudits)]
if op["params"]:
if is_not_none_or_empty(op["params"]):
if op["controls"]:
function(qudits_call, op["params"], op["controls"])
else:
Expand Down Expand Up @@ -316,7 +345,7 @@ def to_qasm(self):

return text

def save_to_file(self, file_name, file_path="/"):
def save_to_file(self, file_name: str, file_path: str = ".") -> str:
"""
Save qasm into a file with the specified name and path.
Expand All @@ -329,15 +358,17 @@ def save_to_file(self, file_name, file_path="/"):
str: The full path of the saved file.
"""
# Combine the file path and name to get the full file path
full_file_path = f"{file_path}/{file_name}.qasm"
self.path_save = file_path
full_file_path = Path(file_path) / (file_name + ".qasm")

# Write the text to the file
with open(full_file_path, "w+", encoding=locale.getpreferredencoding(False)) as file:
with full_file_path.open("w+") as file:
file.write(self.to_qasm())

return full_file_path
self.path_save = None
return str(full_file_path)

def load_from_file(self, file_path):
def load_from_file(self, file_path: str) -> None:
"""
Load text from a file.
Expand All @@ -347,12 +378,9 @@ def load_from_file(self, file_path):
Returns:
str: The text loaded from the file.
"""
try:
with open(file_path, encoding=locale.getpreferredencoding(False)) as file:
text = file.read()
return self.from_qasm(text)
except FileNotFoundError:
return None
with Path(file_path).open("r") as file:
text = file.read()
self.from_qasm(text)

def draw(self) -> None:
# TODO
Expand Down
6 changes: 6 additions & 0 deletions src/mqt/qudits/quantum_circuit/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from __future__ import annotations

from .classic_register import ClassicRegister
from .quantum_register import QuantumRegister

__all__ = ["ClassicRegister", "QuantumRegister"]
40 changes: 40 additions & 0 deletions src/mqt/qudits/quantum_circuit/components/classic_register.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from __future__ import annotations


class ClassicRegister:
@classmethod
def from_map(cls, sitemap: dict) -> list[ClassicRegister]:
registers_map = {}

for creg_with_index, line_info in sitemap.items():
reg_name, inreg_line_index = creg_with_index
if reg_name not in registers_map:
registers_map[reg_name] = [{inreg_line_index: creg_with_index[0]}, line_info]
else:
registers_map[reg_name][0][inreg_line_index] = line_info

registers_from_qasm = []
for label, data in registers_map.items():
temp = ClassicRegister(label, len(data[0]))
temp.local_sitemap = data[0]
registers_from_qasm.append(temp)

return registers_from_qasm

def __init__(self, name, size) -> None:
"""
Class for classical register memories
"""
self.label = name
self.size = size
self.local_sitemap = {}

def __qasm__(self):
return "creg " + self.label + " [" + str(self.size) + "]" + ";"

def __getitem__(self, key):
if isinstance(key, slice):
start, stop = key.start, key.stop
return [self.local_sitemap[i] for i in range(start, stop)]

return self.local_sitemap[key]
23 changes: 19 additions & 4 deletions src/mqt/qudits/quantum_circuit/gate.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
from __future__ import annotations

import random
import string
from abc import ABC, abstractmethod
from pathlib import Path
from typing import TYPE_CHECKING

import numpy as np

from mqt.qudits.quantum_circuit.components.extensions.matrix_factory import MatrixFactory

from ..exceptions import CircuitError
Expand All @@ -12,8 +17,6 @@
if TYPE_CHECKING:
import enum

import numpy as np

from .circuit import QuantumCircuit


Expand All @@ -33,7 +36,7 @@ def __init__(
gate_type: enum,
target_qudits: list[int] | int,
dimensions: list[int] | int,
params: list | None = None,
params: list | np.ndarray | None = None,
control_set=None,
label: str | None = None,
duration=None,
Expand Down Expand Up @@ -127,8 +130,11 @@ def validate_parameter(self, parameter):
pass

def __qasm__(self) -> str:
"""Generate QASM for Gate export"""
string = f"{self.qasm_tag} "
if self._params:
if isinstance(self._params, np.ndarray):
string += self.return_custom_data()
elif self._params:
string += "("
for parameter in self._params:
string += f"{parameter}, "
Expand Down Expand Up @@ -187,3 +193,12 @@ def control_info(self):
"params": self._params,
"controls": self._controls_data,
}

def return_custom_data(self) -> str:
if not self.parent_circuit.path_save:
return "(custom_data) "

key = "".join(random.choice(string.ascii_letters) for _ in range(4))
file_path = Path(self.parent_circuit.path_save) / f"{self._name}_{key}.npy"
np.save(file_path, self._params)
return f"({file_path}) "
5 changes: 4 additions & 1 deletion src/mqt/qudits/quantum_circuit/gates/custom_multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@


class CustomMulti(Gate):
"""Multi body custom gate"""

def __init__(
self,
circuit: QuantumCircuit,
Expand All @@ -29,6 +31,7 @@ def __init__(
target_qudits=target_qudits,
dimensions=dimensions,
control_set=controls,
params=parameters,
)
if self.validate_parameter(parameters):
self.__array_storage = parameters
Expand All @@ -38,7 +41,7 @@ def __init__(
def __array__(self) -> np.ndarray:
return self.__array_storage

def validate_parameter(self, parameter=None):
def validate_parameter(self, parameter=None) -> bool:
return isinstance(parameter, np.ndarray)

def __str__(self) -> str:
Expand Down
6 changes: 4 additions & 2 deletions src/mqt/qudits/quantum_circuit/gates/custom_one.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@


class CustomOne(Gate):
"""One body custom gate"""

def __init__(
self,
circuit: QuantumCircuit,
Expand All @@ -29,16 +31,16 @@ def __init__(
target_qudits=target_qudits,
dimensions=dimensions,
control_set=controls,
params=parameters,
)
if self.validate_parameter(parameters):
self.__array_storage = parameters
self._params = "--"
self.qasm_tag = "cuone"

def __array__(self) -> np.ndarray:
return self.__array_storage

def validate_parameter(self, parameter=None):
def validate_parameter(self, parameter=None) -> bool:
return isinstance(parameter, np.ndarray)

def __str__(self) -> str:
Expand Down
6 changes: 4 additions & 2 deletions src/mqt/qudits/quantum_circuit/gates/custom_two.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@


class CustomTwo(Gate):
"""Two body custom gate"""

def __init__(
self,
circuit: QuantumCircuit,
Expand All @@ -29,16 +31,16 @@ def __init__(
target_qudits=target_qudits,
dimensions=dimensions,
control_set=controls,
params=parameters,
)
if self.validate_parameter(parameters):
self.__array_storage = parameters

self.qasm_tag = "cutwo"

def __array__(self) -> np.ndarray:
return self.__array_storage

def validate_parameter(self, parameter=None):
def validate_parameter(self, parameter=None) -> bool:
return isinstance(parameter, np.ndarray)

def __str__(self) -> str:
Expand Down
Loading

0 comments on commit c1dee1a

Please sign in to comment.