From 93470368ee43af09316e8e5fe72722f8c1616d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Tue, 21 May 2024 16:05:26 +0800 Subject: [PATCH 01/24] =?UTF-8?q?=F0=9F=90=B3=20chore:=20update=20setup.py?= =?UTF-8?q?=20for=20Windows=20platform,=20and=20update=20version=20number?= =?UTF-8?q?=20to=200.3.3.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- setup.cfg | 36 ------------------------------------ setup.py | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/setup.cfg b/setup.cfg index 5bb5b89..e69de29 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,36 +0,0 @@ -[metadata] -name = quingo -version = 0.3.2 -author = Xiang Fu -author_email = gtaifu@gmail.com -use_2to3 = False -description = Quingo Runtime System -long_description = file: README.md -long_description_content_type = text/markdown -url = https://gitee.com/quingo/quingo-runtime -project_urls = - Bug Tracker = https://gitee.com/quingo/quingo-runtime/issues -classifiers = - Programming Language :: Python :: 3 :: Only - License :: OSI Approved :: Apache Software License - Operating System :: OS Independent - -[options] -package_dir = - = src -packages =find: -python_requires = >=3.7 -install_requires = - numpy - pyqcisim >= 1.3.1 - symqc >= 1.1.1 - colorama - termcolor - requests - tqdm - qualesim >= 1.0.2 - qualesim-tequila >= 1.0.2 - pyquiet >= 0.0.4 - -[options.packages.find] -where = src \ No newline at end of file diff --git a/setup.py b/setup.py index b908cbe..f5a3831 100755 --- a/setup.py +++ b/setup.py @@ -1,3 +1,43 @@ -import setuptools +from setuptools import setup, find_packages +import platform -setuptools.setup() +with open("README.md", "r", encoding="utf-8") as fh: + long_description = fh.read() + +setup( + name="quingo", + version="0.3.3", + author="Xiang Fu", + author_email="gtaifu@gmail.com", + description="Quingo Runtime System", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://gitee.com/quingo/quingo-runtime", + project_urls={ + "Bug Tracker": "https://gitee.com/quingo/quingo-runtime/issues", + }, + classifiers=[ + "Programming Language :: Python :: 3 :: Only", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", + ], + package_dir={"": "src"}, + packages=find_packages(where="src"), + python_requires=">=3.7", + install_requires=[ + "numpy", + "pyqcisim >= 1.3.1", + "symqc >= 1.1.1", + "colorama", + "termcolor", + "requests", + "tqdm", + "pyquiet >= 0.0.4", + ], + extras_require={ + ':sys_platform == "linux"': [ + "qualesim >= 1.0.2", + "qualesim-tequila >= 1.0.2", + ], + }, +) -- Gitee From ab39fcce39783a6ac37ad1cd6e51e838d0fcd434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Thu, 13 Jun 2024 17:14:37 +0800 Subject: [PATCH 02/24] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20qos=20backend?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/bell_state/kernel.qu | 13 +- examples/bell_state/test.json | 635 ++++++++++++++++++++++++++++++++++ src/quingo/backend/qos.py | 53 +++ src/quingo/core/compile.py | 40 ++- 4 files changed, 733 insertions(+), 8 deletions(-) create mode 100644 examples/bell_state/test.json create mode 100755 src/quingo/backend/qos.py diff --git a/examples/bell_state/kernel.qu b/examples/bell_state/kernel.qu index 77206da..c04cb1a 100644 --- a/examples/bell_state/kernel.qu +++ b/examples/bell_state/kernel.qu @@ -1,10 +1,13 @@ import std_ops operation bell_state() : unit { - using(q0: qubit, q1: qubit) { - H(q0); - CNOT(q0, q1); - measure(q0); - measure(q1); + using(q : qubit[2] = {42,48}) { + H(q[0]); + CNOT(q[0], q[1]); + measure(q[0]); + measure(q[1]); } +} +operation main(): unit{ + return bell_state(); } \ No newline at end of file diff --git a/examples/bell_state/test.json b/examples/bell_state/test.json new file mode 100644 index 0000000..7a54b89 --- /dev/null +++ b/examples/bell_state/test.json @@ -0,0 +1,635 @@ +{ + "qubits": [ + "Q00", + "Q01", + "Q02", + "Q03", + "Q04", + "Q05", + "Q06", + "Q07", + "Q08", + "Q09", + "Q10", + "Q11", + "Q12", + "Q13", + "Q14", + "Q15", + "Q16", + "Q17", + "Q18", + "Q19", + "Q20", + "Q21", + "Q22", + "Q23", + "Q24", + "Q25", + "Q26", + "Q27", + "Q28", + "Q29", + "Q30", + "Q31", + "Q32", + "Q33", + "Q34", + "Q35", + "Q36", + "Q37", + "Q38", + "Q39", + "Q40", + "Q41", + "Q42", + "Q43", + "Q44", + "Q45", + "Q46", + "Q47", + "Q48", + "Q49", + "Q50", + "Q51", + "Q52", + "Q53", + "Q54", + "Q55", + "Q56", + "Q57", + "Q58", + "Q59", + "Q60", + "Q61", + "Q62", + "Q63", + "Q64", + "Q65" + ], + "czs": [ + [ + "Q00", + "Q06" + ], + [ + "Q00", + "Q07" + ], + [ + "Q01", + "Q07" + ], + [ + "Q01", + "Q08" + ], + [ + "Q02", + "Q08" + ], + [ + "Q02", + "Q09" + ], + [ + "Q03", + "Q09" + ], + [ + "Q03", + "Q10" + ], + [ + "Q04", + "Q10" + ], + [ + "Q04", + "Q11" + ], + [ + "Q05", + "Q11" + ], + [ + "Q06", + "Q12" + ], + [ + "Q07", + "Q12" + ], + [ + "Q07", + "Q13" + ], + [ + "Q08", + "Q13" + ], + [ + "Q08", + "Q14" + ], + [ + "Q09", + "Q14" + ], + [ + "Q09", + "Q15" + ], + [ + "Q10", + "Q15" + ], + [ + "Q10", + "Q16" + ], + [ + "Q11", + "Q16" + ], + [ + "Q11", + "Q17" + ], + [ + "Q12", + "Q18" + ], + [ + "Q12", + "Q19" + ], + [ + "Q13", + "Q19" + ], + [ + "Q13", + "Q20" + ], + [ + "Q14", + "Q20" + ], + [ + "Q14", + "Q21" + ], + [ + "Q15", + "Q21" + ], + [ + "Q15", + "Q22" + ], + [ + "Q16", + "Q22" + ], + [ + "Q16", + "Q23" + ], + [ + "Q17", + "Q23" + ], + [ + "Q18", + "Q24" + ], + [ + "Q19", + "Q24" + ], + [ + "Q19", + "Q25" + ], + [ + "Q20", + "Q25" + ], + [ + "Q20", + "Q26" + ], + [ + "Q21", + "Q26" + ], + [ + "Q21", + "Q27" + ], + [ + "Q22", + "Q27" + ], + [ + "Q22", + "Q28" + ], + [ + "Q23", + "Q28" + ], + [ + "Q23", + "Q29" + ], + [ + "Q24", + "Q30" + ], + [ + "Q24", + "Q31" + ], + [ + "Q25", + "Q31" + ], + [ + "Q25", + "Q32" + ], + [ + "Q26", + "Q32" + ], + [ + "Q26", + "Q33" + ], + [ + "Q27", + "Q33" + ], + [ + "Q27", + "Q34" + ], + [ + "Q28", + "Q34" + ], + [ + "Q28", + "Q35" + ], + [ + "Q29", + "Q35" + ], + [ + "Q30", + "Q36" + ], + [ + "Q31", + "Q36" + ], + [ + "Q31", + "Q37" + ], + [ + "Q32", + "Q37" + ], + [ + "Q32", + "Q38" + ], + [ + "Q33", + "Q38" + ], + [ + "Q33", + "Q39" + ], + [ + "Q34", + "Q39" + ], + [ + "Q34", + "Q40" + ], + [ + "Q35", + "Q40" + ], + [ + "Q35", + "Q41" + ], + [ + "Q36", + "Q42" + ], + [ + "Q36", + "Q43" + ], + [ + "Q37", + "Q43" + ], + [ + "Q37", + "Q44" + ], + [ + "Q38", + "Q44" + ], + [ + "Q38", + "Q45" + ], + [ + "Q39", + "Q45" + ], + [ + "Q39", + "Q46" + ], + [ + "Q40", + "Q46" + ], + [ + "Q40", + "Q47" + ], + [ + "Q41", + "Q47" + ], + [ + "Q42", + "Q48" + ], + [ + "Q43", + "Q48" + ], + [ + "Q43", + "Q49" + ], + [ + "Q44", + "Q49" + ], + [ + "Q44", + "Q50" + ], + [ + "Q45", + "Q50" + ], + [ + "Q45", + "Q51" + ], + [ + "Q46", + "Q51" + ], + [ + "Q46", + "Q52" + ], + [ + "Q47", + "Q52" + ], + [ + "Q47", + "Q53" + ], + [ + "Q48", + "Q54" + ], + [ + "Q48", + "Q55" + ], + [ + "Q49", + "Q55" + ], + [ + "Q49", + "Q56" + ], + [ + "Q50", + "Q56" + ], + [ + "Q50", + "Q57" + ], + [ + "Q51", + "Q57" + ], + [ + "Q51", + "Q58" + ], + [ + "Q52", + "Q58" + ], + [ + "Q52", + "Q59" + ], + [ + "Q53", + "Q59" + ], + [ + "Q54", + "Q60" + ], + [ + "Q55", + "Q60" + ], + [ + "Q55", + "Q61" + ], + [ + "Q56", + "Q61" + ], + [ + "Q56", + "Q62" + ], + [ + "Q57", + "Q62" + ], + [ + "Q57", + "Q63" + ], + [ + "Q58", + "Q63" + ], + [ + "Q58", + "Q64" + ], + [ + "Q59", + "Q64" + ], + [ + "Q59", + "Q65" + ] + ], + "readout": [ + { + "channel": "01", + "qubits": [ + "Q00", + "Q01", + "Q02", + "Q03", + "Q04", + "Q05" + ] + }, + { + "channel": "02", + "qubits": [ + "Q06", + "Q07", + "Q08", + "Q09", + "Q10", + "Q11" + ] + }, + { + "channel": "03", + "qubits": [ + "Q12", + "Q13", + "Q14", + "Q15", + "Q16", + "Q17" + ] + }, + { + "channel": "04", + "qubits": [ + "Q18", + "Q19", + "Q20", + "Q21", + "Q22", + "Q23" + ] + }, + { + "channel": "05", + "qubits": [ + "Q24", + "Q25", + "Q26", + "Q27", + "Q28", + "Q29" + ] + }, + { + "channel": "06", + "qubits": [ + "Q30", + "Q31", + "Q32", + "Q33", + "Q34", + "Q35" + ] + }, + { + "channel": "07", + "qubits": [ + "Q36", + "Q37", + "Q38", + "Q39", + "Q40", + "Q41" + ] + }, + { + "channel": "08", + "qubits": [ + "Q42", + "Q43", + "Q44", + "Q45", + "Q46", + "Q47" + ] + }, + { + "channel": "09", + "qubits": [ + "Q48", + "Q49", + "Q50", + "Q51", + "Q52", + "Q53" + ] + }, + { + "channel": "10", + "qubits": [ + "Q54", + "Q55", + "Q56", + "Q57", + "Q58", + "Q59" + ] + }, + { + "channel": "11", + "qubits": [ + "Q60", + "Q61", + "Q62", + "Q63", + "Q64", + "Q65" + ] + } + ] +} \ No newline at end of file diff --git a/src/quingo/backend/qos.py b/src/quingo/backend/qos.py new file mode 100755 index 0000000..abaa61e --- /dev/null +++ b/src/quingo/backend/qos.py @@ -0,0 +1,53 @@ +from __future__ import annotations +from numpy.typing import NDArray +from typing import List, Union +import json + +from quingo.utils import ensure_path +from .backend_hub import BackendType +from .if_backend import If_backend +from quingo.core.exe_config import ExeMode, ExeConfig +from pyqos.experiment.data_taking.scan_circuits import RunCircuits +import re + + +class qos(If_backend): + """A functional QCIS simulation backend using PyQCISim and QuantumSim.""" + + def __init__(self): + super().__init__(BackendType.QOS) + + def upload_program(self, prog_fn): + """ + Upload assembly or binary program to the simulator. + + Args: + prog_fn: the name of the assembly or binary file. + """ + prog_fn = ensure_path(prog_fn) + self.qcis_circuit = prog_fn.open("r").read() + qubits_fn = ensure_path(prog_fn.stem + ".json") + self.qubits = json.load(qubits_fn.open("r")) + + def execute(self, exe_config: ExeConfig) -> Union[List | NDArray]: + """Execute the given quantum circuit. + Args: + - mode (str): the simulation mode to use: + + The number of shots is specified in exe_config.num_shots, which is only valid for + ExeMode.SimShots. + """ + if exe_config.mode == ExeMode.RealMachine: + raw_res = RunCircuits( + qubits=self.qubits, + use_template=False, + circuits=([self.qcis_circuit]), + data_type="P01", + sampling_interval=200e-6, + num_shots=exe_config.num_shots, + ) + return raw_res + + raise ValueError( + "Unsupported execution mode ({}) for QOS.".format(exe_config.mode) + ) diff --git a/src/quingo/core/compile.py b/src/quingo/core/compile.py index 07d6c41..969f05e 100644 --- a/src/quingo/core/compile.py +++ b/src/quingo/core/compile.py @@ -11,22 +11,40 @@ from quingo.backend.qisa import Qisa, get_qisa_name, get_suffix logger = get_logger((__name__).split(".")[-1]) -def compile(task: Quingo_task, params: tuple, qasm_fn: Path = None, config_file=""): +def compile(task: Quingo_task, params: tuple, qasm_fn: Path = None, **kwargs): """Compile the quingo file with given parameters and return the path of the generated qasm file. """ + if "config_file" in kwargs: + config_file = kwargs["config_file"] + else: + config_file = "" + if "target" in kwargs: + target = kwargs["target"] + else: + target = "" + if "chip_path" in kwargs: + chip_path = kwargs["chip_path"] + else: + chip_path = "" + logger.setLevel(logging.INFO) gen_main_file(task.called_qu_fn, task.called_func, task.cl_entry_fn, params) if qasm_fn is None: suffix = get_suffix(task.qisa_type) qasm_fn = task.cl_entry_fn.with_suffix(suffix) + mq_fn = task.cl_entry_fn.with_suffix(".json") else: qasm_fn = ensure_path(qasm_fn) + mq_fn = qasm_fn.stem + ".json" + mq_fn = ensure_path(mq_fn) quingoc_path = Path(get_mlir_path()) - compile_cmd = compose_cl_cmd(task, qasm_fn, quingoc_path, config_file) + compile_cmd = compose_cl_cmd( + task, qasm_fn, quingoc_path, config_file, target, chip_path, mq_fn + ) if task.debug_mode: logger.info(compile_cmd) ret_value = subprocess.run( @@ -51,7 +69,15 @@ def compile(task: Quingo_task, params: tuple, qasm_fn: Path = None, config_file= return qasm_fn -def compose_cl_cmd(task: Quingo_task, qasm_fn: Path, quingoc_path: Path, configfile=""): +def compose_cl_cmd( + task: Quingo_task, + qasm_fn: Path, + quingoc_path: Path, + configfile="", + target="", + chip_path="", + mq_fn="", +): qasm_fn = ensure_path(qasm_fn) quingoc_path = ensure_path(quingoc_path) @@ -70,11 +96,19 @@ def compose_cl_cmd(task: Quingo_task, qasm_fn: Path, quingoc_path: Path, configf config_fn = '--config-fn="{}"'.format(str(configfile)) + chip_path_ = '--chip-path="{}"'.format(str(chip_path)) + + target_ = '--target="{}"'.format(str(target)) + mq_path = '--mq-path="{}"'.format(str(mq_fn)) + cmd_eles = [ cl_path, cl_entry_fn, opt_inc_dirs, config_fn, + chip_path_, + target_, + mq_path, opt_isa, opt_qubit_map, opt_out_fn, -- Gitee From 029668f9021ae496959a50f56ab22c5c40a57509 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Sat, 15 Jun 2024 17:24:36 +0800 Subject: [PATCH 03/24] =?UTF-8?q?=E2=9C=A8=20feat:=20support=20directly=20?= =?UTF-8?q?execute=20qasm=20str=20on=20backends?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Three backends have added the `upload_program_str` method (pyqcisim_quantumsim, pyqcisim_tequila, symqc) - qualesim-based backends would require an extra signal to tell which type of instruction the program uses. - the `execute` function can automatically check the first parameter is a path or not. if not, it is treated as a qasm str. https://gitee.com/quingo/quingo-runtime/issues/IA5OM0 --- src/quingo/backend/if_backend.py | 3 +++ src/quingo/backend/pyqcisim_quantumsim.py | 4 ++++ src/quingo/backend/pyqcisim_tequila.py | 4 ++++ src/quingo/backend/symqc.py | 4 ++++ src/quingo/core/manager.py | 21 ++++++++++++----- src/quingo/utils.py | 22 ++++++++++++++++++ unittest/backends/test_backends.py | 13 +++++++++++ unittest/test_compile_cmd.py | 8 +++---- unittest/test_execution.py | 28 +++++++++++++++++++++++ 9 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/quingo/backend/if_backend.py b/src/quingo/backend/if_backend.py index 35543fc..039014b 100755 --- a/src/quingo/backend/if_backend.py +++ b/src/quingo/backend/if_backend.py @@ -23,5 +23,8 @@ class If_backend: def upload_program(self, program): raise NotImplementedError + def upload_program_str(self, program: str): + raise NotImplementedError + def execute(self, exe_config: ExeConfig): raise NotImplementedError diff --git a/src/quingo/backend/pyqcisim_quantumsim.py b/src/quingo/backend/pyqcisim_quantumsim.py index 5f3c7e8..6271008 100755 --- a/src/quingo/backend/pyqcisim_quantumsim.py +++ b/src/quingo/backend/pyqcisim_quantumsim.py @@ -29,6 +29,10 @@ class PyQCISim_quantumsim(If_backend): program = prog_fn.open("r").read() self.sim.compile(program) + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.sim.compile(program) + def execute(self, exe_config: ExeConfig) -> Union[List | NDArray]: """Execute the given quantum circuit. Args: diff --git a/src/quingo/backend/pyqcisim_tequila.py b/src/quingo/backend/pyqcisim_tequila.py index 42a2005..b95be09 100644 --- a/src/quingo/backend/pyqcisim_tequila.py +++ b/src/quingo/backend/pyqcisim_tequila.py @@ -21,6 +21,10 @@ class PyQCISim_tequila(If_backend): program = prog_fn.open("r").read() self.sim.compile(program) + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.sim.compile(program) + def execute(self, exe_config: ExeConfig): """Execute the given quantum circuit. Args: diff --git a/src/quingo/backend/symqc.py b/src/quingo/backend/symqc.py index 8e39be1..675e806 100644 --- a/src/quingo/backend/symqc.py +++ b/src/quingo/backend/symqc.py @@ -25,6 +25,10 @@ class IfSymQC(If_backend): else: raise TypeError("The SymQC simulator can only accept QCIS instructions.") + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.sim.compile(program) + def execute(self, exe_config: ExeConfig): """Execute the given quantum circuit. Args: diff --git a/src/quingo/core/manager.py b/src/quingo/core/manager.py index 3950853..3133ae6 100755 --- a/src/quingo/core/manager.py +++ b/src/quingo/core/manager.py @@ -13,6 +13,7 @@ from quingo.core.quingo_task import Quingo_task from quingo.core.compile import compile from quingo.backend.backend_hub import BackendType, Backend_hub from quingo.core.quingo_logger import get_logger +from quingo.utils import validate_path logger = get_logger((__name__).split(".")[-1]) @@ -28,7 +29,7 @@ def verify_backend_config(backend: BackendType, exe_config: ExeConfig) -> bool: def execute( - qasm_fn: Path, + qasm_fn_or_str: Path, be_type: BackendType, exe_config: ExeConfig = ExeConfig(), debug_mode=False, @@ -40,13 +41,21 @@ def execute( raise ValueError( "Error configuration {} on the backend {}".format(str(exe_config), backend) ) - execute_cmd = ( - f'simulating instructions "{str(qasm_fn)}" with backend {str(be_type.name)}' - ) + if debug_mode: - logger.info(execute_cmd) + execute_info = "execute the following program with backend {}: \n {}".format( + str(be_type.name), str(qasm_fn_or_str) + ) + logger.info(execute_info) + backend = Backend_hub().get_instance(be_type) - backend.upload_program(qasm_fn) + + qasm_fn = validate_path(qasm_fn_or_str) + if qasm_fn is None: + backend.upload_program_str(qasm_fn_or_str) + else: + backend.upload_program(qasm_fn) + result = backend.execute(exe_config) if exe_config.mode == ExeMode.SimStateVector: names, array_values = result diff --git a/src/quingo/utils.py b/src/quingo/utils.py index 67cfa2c..a6d845c 100644 --- a/src/quingo/utils.py +++ b/src/quingo/utils.py @@ -10,6 +10,28 @@ def ensure_path(fn) -> Path: return fn +def validate_path(fn) -> Path: + """ + Validates the given file path. + + Args: + fn (str or Path): The file path to validate. + + Returns: + Path: The validated file path as a `Path` object, or `None` if the path is invalid. + """ + if not isinstance(fn, (str, Path)): + return None + + if isinstance(fn, str): + fn = Path(fn).resolve() + + if not fn.exists(): + return None + + return fn + + def is_number(s): try: float(s) diff --git a/unittest/backends/test_backends.py b/unittest/backends/test_backends.py index d9f181b..46cb7fd 100644 --- a/unittest/backends/test_backends.py +++ b/unittest/backends/test_backends.py @@ -50,6 +50,19 @@ class Test_backends: single(PyQCISim_quantumsim, qcis_fn) single(IfSymQC, qcis_fn) + def test_upload_program_str(self): + def single(BackendClass, qasm): + sim = BackendClass() + try: + sim.upload_program_str(qasm) + except Exception as e: + assert False, "upload_program failed: {}".format(e) + + qasm_str = "H Q0\nCNOT Q0 Q1\nMEASURE Q0\nMEASURE Q1" + single(PyQCISim_tequila, qasm_str) + single(PyQCISim_quantumsim, qasm_str) + single(IfSymQC, qasm_str) + def test_get_from_hub(self): def single(backend_type, simulator_class): hub = Backend_hub() diff --git a/unittest/test_compile_cmd.py b/unittest/test_compile_cmd.py index e950c9b..1baca91 100644 --- a/unittest/test_compile_cmd.py +++ b/unittest/test_compile_cmd.py @@ -40,8 +40,8 @@ class TestCompileCmd: qasm_fn = compile(task, ()) with qasm_fn.open("r") as f: lines = f.readlines() - assert lines[0].strip() == "H Q0" - assert lines[2].strip() == "CZ Q0 Q1" + assert lines[0].strip().split() == ["H", "Q0"] + assert lines[1].strip().split() == ["CNOT", "Q0", "Q1"] def test_compile2(self): bell_fn = qu_dir / "bell.qu" @@ -50,8 +50,8 @@ class TestCompileCmd: assert qasm_fn.samefile(unittest_dir / "out_bell.qcis") with qasm_fn.open("r") as f: lines = f.readlines() - assert lines[0].strip() == "H Q0" - assert lines[2].strip() == "CZ Q0 Q1" + assert lines[0].strip().split() == ["H", "Q0"] + assert lines[1].strip().split() == ["CNOT", "Q0", "Q1"] if __name__ == "__main__": diff --git a/unittest/test_execution.py b/unittest/test_execution.py index c24dff4..7c346c7 100644 --- a/unittest/test_execution.py +++ b/unittest/test_execution.py @@ -1,9 +1,37 @@ +import pytest from pathlib import Path from quingo import BackendType, Quingo_task, ExeConfig, ExeMode from quingo import call, compile, execute unittest_dir = Path(__file__).parent qu_file = unittest_dir / "test_qu" / "bell.qu" +qcis_file = unittest_dir / "test_qcis" / "bell.qcis" + + +@pytest.fixture( + scope="module", + params=[qcis_file, "H Q0\nCNOT Q0 Q1\nMEASURE Q0\nMEASURE Q1"], +) +def qasm_fn_or_str(request): + return request.param + + +@pytest.fixture( + scope="module", + params=[BackendType.QUANTUM_SIM, BackendType.TEQUILA, BackendType.SYMQC], +) +def backends_for_qcis(request): + return request.param + + +def test_execute(qasm_fn_or_str, backends_for_qcis): + num_shot = 10 + cfg = ExeConfig(ExeMode.SimShots, num_shot) + res = execute(qasm_fn_or_str, backends_for_qcis, cfg) + print(res) + + assert len(res[0]) == 2 + assert len(res[1]) == 10 def test_compile_execute(): -- Gitee From 4a3b694cd2408664d0597592f73d9fbb1b08dbd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Tue, 18 Jun 2024 16:32:55 +0800 Subject: [PATCH 04/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20qos=20backend?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/backend_hub.py | 7 +++++++ src/quingo/backend/qos.py | 16 ++++++++++------ src/quingo/backend/qualesim.py | 7 +++++-- src/quingo/core/compile.py | 2 +- src/quingo/core/exe_config.py | 2 ++ src/quingo/core/manager.py | 4 ++-- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/quingo/backend/backend_hub.py b/src/quingo/backend/backend_hub.py index a576e44..e0c059e 100755 --- a/src/quingo/backend/backend_hub.py +++ b/src/quingo/backend/backend_hub.py @@ -12,6 +12,7 @@ class BackendType(enum.Enum): XIAOHONG = enum.auto() QUALESIM_TEQUILA = enum.auto() QUALESIM_QUANTUMSIM = enum.auto() + QOS = enum.auto() @singleton @@ -60,6 +61,12 @@ class Backend_hub: True, Qisa.QCIS, ), + BackendType.QOS: ( + "QOS", + "qos", + False, + Qisa.QCIS, + ), } def support(self, backend_type): diff --git a/src/quingo/backend/qos.py b/src/quingo/backend/qos.py index abaa61e..80b04e7 100755 --- a/src/quingo/backend/qos.py +++ b/src/quingo/backend/qos.py @@ -8,10 +8,9 @@ from .backend_hub import BackendType from .if_backend import If_backend from quingo.core.exe_config import ExeMode, ExeConfig from pyqos.experiment.data_taking.scan_circuits import RunCircuits -import re -class qos(If_backend): +class QOS(If_backend): """A functional QCIS simulation backend using PyQCISim and QuantumSim.""" def __init__(self): @@ -26,7 +25,7 @@ class qos(If_backend): """ prog_fn = ensure_path(prog_fn) self.qcis_circuit = prog_fn.open("r").read() - qubits_fn = ensure_path(prog_fn.stem + ".json") + qubits_fn = prog_fn.parent / (prog_fn.stem + ".json") self.qubits = json.load(qubits_fn.open("r")) def execute(self, exe_config: ExeConfig) -> Union[List | NDArray]: @@ -38,15 +37,20 @@ class qos(If_backend): ExeMode.SimShots. """ if exe_config.mode == ExeMode.RealMachine: + # print("qubits = ", self.qubits) + # print("qcis_circuit = ", self.qcis_circuit) + # return "qqqqq" raw_res = RunCircuits( - qubits=self.qubits, + qubits=[self.qubits], use_template=False, - circuits=([self.qcis_circuit]), + circuits=([self.qcis_circuit] * exe_config.qos_circuit_times), data_type="P01", sampling_interval=200e-6, num_shots=exe_config.num_shots, ) - return raw_res + raw_res.wait() + frams = raw_res.dataset.get_stream_data() + return frams raise ValueError( "Unsupported execution mode ({}) for QOS.".format(exe_config.mode) diff --git a/src/quingo/backend/qualesim.py b/src/quingo/backend/qualesim.py index 7abcf00..a8fcce3 100644 --- a/src/quingo/backend/qualesim.py +++ b/src/quingo/backend/qualesim.py @@ -5,14 +5,15 @@ from pathlib import Path from quingo.backend.backend_hub import BackendType from quingo.backend.if_backend import If_backend from quingo.core.exe_config import ExeConfig, ExeMode -from qualesim.plugin import Loglevel -from qualesim.host import Simulator class QuaLeSim(If_backend): def __init__(self, backend_type=BackendType.QUALESIM_QUANTUMSIM): super().__init__(backend_type) + from qualesim.plugin import Loglevel + from qualesim.host import Simulator + self.sim = Simulator(stderr_verbosity=Loglevel.OFF) if backend_type == BackendType.QUALESIM_QUANTUMSIM: self.sim.with_backend("quantumsim", verbosity=Loglevel.OFF) @@ -29,6 +30,8 @@ class QuaLeSim(If_backend): prog_fn = ensure_path(prog_fn) if prog_fn.suffix in [".qcis", ".qi"]: + from qualesim.plugin import Loglevel + self.sim.with_frontend(str(prog_fn), verbosity=Loglevel.OFF) else: raise TypeError( diff --git a/src/quingo/core/compile.py b/src/quingo/core/compile.py index 969f05e..8884544 100644 --- a/src/quingo/core/compile.py +++ b/src/quingo/core/compile.py @@ -96,7 +96,7 @@ def compose_cl_cmd( config_fn = '--config-fn="{}"'.format(str(configfile)) - chip_path_ = '--chip-path="{}"'.format(str(chip_path)) + chip_path_ = '--chip-config="{}"'.format(str(chip_path)) target_ = '--target="{}"'.format(str(target)) mq_path = '--mq-path="{}"'.format(str(mq_fn)) diff --git a/src/quingo/core/exe_config.py b/src/quingo/core/exe_config.py index 0c84518..dbfe871 100644 --- a/src/quingo/core/exe_config.py +++ b/src/quingo/core/exe_config.py @@ -26,11 +26,13 @@ class ExeConfig: num_shots: int = 1, xh_login_key: str = None, # use for connecting XIAOHONG xh_machine_name: str = None, # use for connecting XIAOHONG + qos_circuit_times: int = 100, # use for connecting QOS ): self.mode = mode self.num_shots = num_shots self.xh_login_key = xh_login_key self.xh_machine_name = xh_machine_name + self.qos_circuit_times = qos_circuit_times def __str__(self) -> str: return str(self.mode) diff --git a/src/quingo/core/manager.py b/src/quingo/core/manager.py index 3950853..ae92e02 100755 --- a/src/quingo/core/manager.py +++ b/src/quingo/core/manager.py @@ -79,9 +79,9 @@ def call( params: tuple, be_type: BackendType = BackendType.QUANTUM_SIM, exe_config: ExeConfig = ExeConfig(), - config_fn="", + **kwargs, ): """Execute the quingo task on the specified backend and return the result.""" - qasm_fn = compile(task, params, config_file=config_fn) + qasm_fn = compile(task, params, **kwargs) return execute(qasm_fn, be_type, exe_config, debug_mode=task.debug_mode) -- Gitee From 62a5cda4bf9f5d61df6cb16e926624575c592d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Thu, 20 Jun 2024 16:59:50 +0800 Subject: [PATCH 05/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20examplex=20xi?= =?UTF-8?q?aohong=20with=20new=20compile=20cmd.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/xiaohong/host.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/xiaohong/host.py b/examples/xiaohong/host.py index 051118c..6ad70b3 100644 --- a/examples/xiaohong/host.py +++ b/examples/xiaohong/host.py @@ -10,10 +10,10 @@ def routine(circ_name, num_shots=1): cfg = ExeConfig( ExeMode.RealMachine, num_shots, - xh_login_key="7e6999bab11453428b8ded1fac00b3ea", - xh_machine_name="Transponder", + xh_login_key="946dbe920c1b048a8ae7e3475d2184f4", + xh_machine_name="Xiaohong", ) - qasm_fn = compile(task, params=(), config_file="") + qasm_fn = compile(task, params=(), config_file="", target="qcloud_sh") res = execute(qasm_fn, BackendType.XIAOHONG, cfg) print("result: ", res) -- Gitee From 83163792cabc58099f542171c692aac31dd388d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Fri, 21 Jun 2024 10:11:48 +0800 Subject: [PATCH 06/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20add=20upload=5Fprog?= =?UTF-8?q?ram=5Fstr=20for=20xiaohong.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/xiaohong.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/quingo/backend/xiaohong.py b/src/quingo/backend/xiaohong.py index d170d63..7a81be3 100644 --- a/src/quingo/backend/xiaohong.py +++ b/src/quingo/backend/xiaohong.py @@ -21,6 +21,10 @@ class XiaoHong(If_backend): prog_fn = ensure_path(prog_fn) self.qcis_circuit = prog_fn.open("r").read() + def upload_program_str(self, program: str): + """upload the program string to the simulator.""" + self.qcis_circuit = program + def execute(self, exe_config: ExeConfig): """Execute the given quantum circuit. Args: -- Gitee From 85a67918482d965343d70005b401bf063d7f8e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Sat, 22 Jun 2024 09:42:46 +0800 Subject: [PATCH 07/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20compile=20for?= =?UTF-8?q?=20quingoc=200.5.0.acc54750.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/core/compile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quingo/core/compile.py b/src/quingo/core/compile.py index 8884544..b49bff7 100644 --- a/src/quingo/core/compile.py +++ b/src/quingo/core/compile.py @@ -99,7 +99,7 @@ def compose_cl_cmd( chip_path_ = '--chip-config="{}"'.format(str(chip_path)) target_ = '--target="{}"'.format(str(target)) - mq_path = '--mq-path="{}"'.format(str(mq_fn)) + # mq_path = '--mq-path="{}"'.format(str(mq_fn)) cmd_eles = [ cl_path, @@ -108,7 +108,7 @@ def compose_cl_cmd( config_fn, chip_path_, target_, - mq_path, + # mq_path, opt_isa, opt_qubit_map, opt_out_fn, -- Gitee From e20a9181344f25e3762d7508c9adf59a299fed3a Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Sat, 22 Jun 2024 09:53:10 +0800 Subject: [PATCH 08/24] =?UTF-8?q?=E2=9C=A8=20feat:=20support=20sim=20prob?= =?UTF-8?q?=20using=20tequila?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit user can use `get_prob` to get the measurment probability of getting 0/1 for a qubit --- src/quingo/backend/pyqcisim_tequila.py | 3 +++ src/quingo/core/exe_config.py | 1 + src/quingo/utils.py | 10 +++++--- unittest/backends/test_sim_modes.py | 35 ++++++++++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 unittest/backends/test_sim_modes.py diff --git a/src/quingo/backend/pyqcisim_tequila.py b/src/quingo/backend/pyqcisim_tequila.py index b95be09..1793233 100644 --- a/src/quingo/backend/pyqcisim_tequila.py +++ b/src/quingo/backend/pyqcisim_tequila.py @@ -44,6 +44,9 @@ class PyQCISim_tequila(If_backend): names, nd_array_values = self.sim.simulate("state_vector") return (names, nd_array_values) + if exe_config.mode == ExeMode.SimProbability: + return self.sim.simulate("probability") + raise ValueError( "Unsupported execution mode ({}) for TEQUILA.".format(exe_config.mode) ) diff --git a/src/quingo/core/exe_config.py b/src/quingo/core/exe_config.py index 0c84518..937a6b2 100644 --- a/src/quingo/core/exe_config.py +++ b/src/quingo/core/exe_config.py @@ -16,6 +16,7 @@ class ExeMode(enum.Enum): SimFinalResult = enum.auto() SimStateVector = enum.auto() SymbolicStateVector = enum.auto() + SimProbability = enum.auto() RealMachine = enum.auto() diff --git a/src/quingo/utils.py b/src/quingo/utils.py index a6d845c..b1946b6 100644 --- a/src/quingo/utils.py +++ b/src/quingo/utils.py @@ -1,6 +1,7 @@ import sympy as sp from pathlib import Path import numpy as np +import os def ensure_path(fn) -> Path: @@ -24,12 +25,15 @@ def validate_path(fn) -> Path: return None if isinstance(fn, str): - fn = Path(fn).resolve() + if os.path.isfile(fn): + return Path(fn).resolve() + else: + return None if not fn.exists(): return None - - return fn + else: + return Path(fn).resolve() def is_number(s): diff --git a/unittest/backends/test_sim_modes.py b/unittest/backends/test_sim_modes.py new file mode 100644 index 0000000..be91e92 --- /dev/null +++ b/unittest/backends/test_sim_modes.py @@ -0,0 +1,35 @@ +import pytest +from pathlib import Path +from quingo.backend.pyqcisim_tequila import PyQCISim_tequila +from quingo.core.exe_config import ExeConfig, ExeMode + + +# @pytest.fixture( +# scope="module", +# params=[BackendType.TEQUILA], +# ) +# def get_backend_type(request): +# return request.param + + +@pytest.fixture(scope="module") +def get_tequila(): + return PyQCISim_tequila() + + +unittest_dir = Path(__file__).parent / ".." + + +@pytest.fixture(scope="module", params=[unittest_dir / "test_qcis" / "bell.qcis"]) +def get_bell_qasm_fn(request): + return request.param + + +def test_call_tequila(get_tequila, get_bell_qasm_fn): + simulator = get_tequila + simulator.upload_program(get_bell_qasm_fn) + exe_config = ExeConfig(ExeMode.SimProbability) + names, p0s = simulator.execute(exe_config) + assert names == ["Q1", "Q2"] + assert len(p0s) == len(names) + assert all(p0 == pytest.approx(0.5) for p0 in p0s) -- Gitee From 3a2fabbcecd45c4ea3910c238101ada76e08287c Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Sat, 22 Jun 2024 23:14:35 +0800 Subject: [PATCH 09/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20result=20format=20s?= =?UTF-8?q?pec?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the final result unittest cannot pass due to simulator result format error --- .../backend/quingo_result_format_spec.md | 9 ++++---- unittest/backends/test_result_format.py | 22 +++++++++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/quingo/backend/quingo_result_format_spec.md b/src/quingo/backend/quingo_result_format_spec.md index 1df8994..d0b4ddd 100644 --- a/src/quingo/backend/quingo_result_format_spec.md +++ b/src/quingo/backend/quingo_result_format_spec.md @@ -42,11 +42,10 @@ end The result shall be: ```python -{ - 'classical': {}, - 'quantum': (['q1', 'q2'], - [(0.7071067811865474+0j), 0j, 0j, (0.7071067811865476+0j)]) -} + ( + ['q1', 'q2'], + [(0.7071067811865474+0j), 0j, 0j, (0.7071067811865476+0j)] + ) ``` ## SymbolicStateVector diff --git a/unittest/backends/test_result_format.py b/unittest/backends/test_result_format.py index 5805597..959ee0b 100644 --- a/unittest/backends/test_result_format.py +++ b/unittest/backends/test_result_format.py @@ -64,8 +64,14 @@ def test_final_result_with_msmt(get_simulator, get_num_shots): num_shots = get_num_shots exe_config = ExeConfig(ExeMode.SimFinalResult, num_shots=num_shots) - result = execute(bell_qcis_fn, simulator, exe_config) - print("result: ", result) + result_dict = execute(bell_qcis_fn, simulator, exe_config) + assert "classical" in result_dict and "quantum" in result_dict + assert isinstance(result_dict["classical"], dict) + assert len(result_dict["classical"]) == 2 + assert len(result_dict["quantum"]) == 2 + names, state_vec = result_dict["quantum"] + assert names == [] + assert state_vec.shape == (2 ** len(names),) def test_final_result_without_msmt(get_simulator, get_num_shots): @@ -73,8 +79,14 @@ def test_final_result_without_msmt(get_simulator, get_num_shots): num_shots = get_num_shots exe_config = ExeConfig(ExeMode.SimFinalResult, num_shots=num_shots) - result = execute(bell_no_msmt_qcis_fn, simulator, exe_config) - print("result: ", result) + result_dict = execute(bell_no_msmt_qcis_fn, simulator, exe_config) + assert "classical" in result_dict and "quantum" in result_dict + assert isinstance(result_dict["classical"], dict) + assert len(result_dict["classical"]) == 0 + assert len(result_dict["quantum"]) == 2 + names, state_vec = result_dict["quantum"] + assert names == ["Q1", "Q2"] + assert state_vec.shape == (2 ** len(names),) def test_state_vector_without_msmt(get_simulator): @@ -95,6 +107,8 @@ def test_state_vector_with_msmt(get_simulator): exe_config = ExeConfig(ExeMode.SimStateVector) qubit_names, state_vec = execute(bell_qcis_fn, simulator, exe_config) + print("sim: ", simulator) + print("state vec: ", state_vec) assert qubit_names == ["Q1", "Q2"] assert isinstance(state_vec, (list, np.ndarray)) assert state_vec.shape == (4,) -- Gitee From 653e4df2efe3569cf0d4d336dc28894e8eb72271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Sun, 23 Jun 2024 10:06:34 +0800 Subject: [PATCH 10/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20xiaohong=20ex?= =?UTF-8?q?ample.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/xiaohong/host.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/xiaohong/host.py b/examples/xiaohong/host.py index 6ad70b3..ff22041 100644 --- a/examples/xiaohong/host.py +++ b/examples/xiaohong/host.py @@ -10,8 +10,8 @@ def routine(circ_name, num_shots=1): cfg = ExeConfig( ExeMode.RealMachine, num_shots, - xh_login_key="946dbe920c1b048a8ae7e3475d2184f4", - xh_machine_name="Xiaohong", + xh_login_key="7e6999bab11453428b8ded1fac00b3ea", + xh_machine_name="Transpose", ) qasm_fn = compile(task, params=(), config_file="", target="qcloud_sh") res = execute(qasm_fn, BackendType.XIAOHONG, cfg) -- Gitee From 37f530b3fe8d2f00a8da208b3109b45d60f1dc80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Mon, 24 Jun 2024 17:08:02 +0800 Subject: [PATCH 11/24] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20rem.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/rem_noise/calibration.qu | 24 ++++ examples/rem_noise/rem.ipynb | 183 +++++++++++++++++++++++++ examples/rem_noise/test.qu | 10 ++ src/quingo/backend/pyqcisim_tequila.py | 12 +- src/quingo/core/exe_config.py | 2 + src/quingo/lib/calibration.qu | 24 ++++ src/quingo/lib/rem.py | 157 +++++++++++++++++++++ src/quingo/lib/utils.py | 70 ++++++++++ unittest/test_compile_cmd.py | 6 +- 9 files changed, 482 insertions(+), 6 deletions(-) create mode 100644 examples/rem_noise/calibration.qu create mode 100644 examples/rem_noise/rem.ipynb create mode 100644 examples/rem_noise/test.qu create mode 100644 src/quingo/lib/calibration.qu create mode 100644 src/quingo/lib/rem.py create mode 100644 src/quingo/lib/utils.py diff --git a/examples/rem_noise/calibration.qu b/examples/rem_noise/calibration.qu new file mode 100644 index 0000000..d2989ca --- /dev/null +++ b/examples/rem_noise/calibration.qu @@ -0,0 +1,24 @@ +import std_ops +// Z for 0, X for 1, Y for 2 +operation calibration_circuits(num_qubits: int, observable: int[], state_labels:int[]) : unit { + using(qs: qubit[num_qubits]) { + for(int i = 0; i < num_qubits; i += 1) { + if (state_labels[i] == 1){ + // X + X(qs[i]); + } + if (observable[i] == 1) { + H(qs[i]); + H(qs[i]); + } + if (observable[i] == 2) { + // Y + H(qs[i]); + S(qs[i]); + Sdag(qs[i]); + H(qs[i]); + } + measure(qs[i]); + } + } +} \ No newline at end of file diff --git a/examples/rem_noise/rem.ipynb b/examples/rem_noise/rem.ipynb new file mode 100644 index 0000000..6642681 --- /dev/null +++ b/examples/rem_noise/rem.ipynb @@ -0,0 +1,183 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# How to use\n", + "***\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "以下举例子说明rem的使用流程和接口格式,考虑以下可观测量期望值求解问题。" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from quingo import *\n", + "from pathlib import Path\n", + "from quingo.lib.rem import get_corr_exp_value_for_full_matrix_model\n", + "n_qubits = 2\n", + "# observable = 'ZZ'\n", + "observable = [0, 0]\n", + "\n", + "qu_file = Path(\"test.qu\")\n", + "\n", + "from quingo.lib.utils import get_prob_noisy\n", + "\n", + "noise_config = [0, 1], [0.1, 0.1], ['bflip', 'bflip']\n", + "task = Quingo_task(qu_file, \"bell_state\")\n", + "\n", + "noisy_circ_prob = get_prob_noisy(task,noise_config,n_qubits,shots=50)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 0.42, 1: 0.14, 2: 0, 3: 0.44}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "noisy_circ_prob" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "qu_calibration= Path(\"calibration.qu\")\n", + "calibration_circ = Quingo_task(qu_calibration, \"calibration_circuits\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from quingo.lib.utils import calibration_matrix" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(2, [0, 0], [0, 0]), (2, [0, 0], [0, 1]), (2, [0, 0], [1, 0]), (2, [0, 0], [1, 1])]\n" + ] + } + ], + "source": [ + "cali_matrix_fm = calibration_matrix(calibration_circ,2,[0,0],noise_config)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.74, 0.07, 0.1 , 0. ],\n", + " [0.12, 0. , 0.82, 0.09],\n", + " [0.13, 0.84, 0.02, 0.12],\n", + " [0.01, 0.09, 0.06, 0.79]])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "cali_matrix_fm" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.42 0.14 0. 0.44]\n", + "[ 0.5806 -0.1715 0.0235 0.5674]\n" + ] + } + ], + "source": [ + "corr_exp_value_fp = get_corr_exp_value_for_full_matrix_model(n_qubits, noisy_circ_prob, cali_matrix_fm)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{0: 0.49623028762904114,\n", + " 1: -7.704358491026306e-18,\n", + " 2: 0.014963835514714814,\n", + " 3: 0.488805876856244}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "corr_exp_value_fp" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tmp", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.18" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/rem_noise/test.qu b/examples/rem_noise/test.qu new file mode 100644 index 0000000..3d9cbf4 --- /dev/null +++ b/examples/rem_noise/test.qu @@ -0,0 +1,10 @@ +import std_ops + +operation bell_state() : unit { + using(q : qubit[2]) { + H(q[0]); + CNOT(q[0], q[1]); + measure(q[0]); + measure(q[1]); + } +} \ No newline at end of file diff --git a/src/quingo/backend/pyqcisim_tequila.py b/src/quingo/backend/pyqcisim_tequila.py index b95be09..2e0b609 100644 --- a/src/quingo/backend/pyqcisim_tequila.py +++ b/src/quingo/backend/pyqcisim_tequila.py @@ -35,13 +35,19 @@ class PyQCISim_tequila(If_backend): """ if exe_config.mode == ExeMode.SimShots: - return self.sim.simulate("one_shot", exe_config.num_shots) + return self.sim.simulate( + "one_shot", exe_config.num_shots, noise_config=exe_config.noise_config + ) if exe_config.mode == ExeMode.SimFinalResult: - return self.sim.simulate("final_result") + return self.sim.simulate( + "final_result", noise_config=exe_config.noise_config + ) if exe_config.mode == ExeMode.SimStateVector: - names, nd_array_values = self.sim.simulate("state_vector") + names, nd_array_values = self.sim.simulate( + "state_vector", noise_config=exe_config.noise_config + ) return (names, nd_array_values) raise ValueError( diff --git a/src/quingo/core/exe_config.py b/src/quingo/core/exe_config.py index dbfe871..f879586 100644 --- a/src/quingo/core/exe_config.py +++ b/src/quingo/core/exe_config.py @@ -27,12 +27,14 @@ class ExeConfig: xh_login_key: str = None, # use for connecting XIAOHONG xh_machine_name: str = None, # use for connecting XIAOHONG qos_circuit_times: int = 100, # use for connecting QOS + noise_config=None, ): self.mode = mode self.num_shots = num_shots self.xh_login_key = xh_login_key self.xh_machine_name = xh_machine_name self.qos_circuit_times = qos_circuit_times + self.noise_config = noise_config def __str__(self) -> str: return str(self.mode) diff --git a/src/quingo/lib/calibration.qu b/src/quingo/lib/calibration.qu new file mode 100644 index 0000000..d2989ca --- /dev/null +++ b/src/quingo/lib/calibration.qu @@ -0,0 +1,24 @@ +import std_ops +// Z for 0, X for 1, Y for 2 +operation calibration_circuits(num_qubits: int, observable: int[], state_labels:int[]) : unit { + using(qs: qubit[num_qubits]) { + for(int i = 0; i < num_qubits; i += 1) { + if (state_labels[i] == 1){ + // X + X(qs[i]); + } + if (observable[i] == 1) { + H(qs[i]); + H(qs[i]); + } + if (observable[i] == 2) { + // Y + H(qs[i]); + S(qs[i]); + Sdag(qs[i]); + H(qs[i]); + } + measure(qs[i]); + } + } +} \ No newline at end of file diff --git a/src/quingo/lib/rem.py b/src/quingo/lib/rem.py new file mode 100644 index 0000000..115bb5f --- /dev/null +++ b/src/quingo/lib/rem.py @@ -0,0 +1,157 @@ +""" +Readout Error Mitigation +""" + +# from qiskit import QuantumCircuit +import numpy as np +from scipy.optimize import minimize +from typing import Dict + + +def get_corr_exp_value_for_full_matrix_model( + n_qubits: int, + noisy_circ_prob: Dict[int, float], + calibration_matrix: np.ndarray[float], +) -> float: + """ + Correct the unbiased estimator of expectation value + + Args: + n_qubits: number of qubits + noisy_circ_prob: probability distribution measured in pauli basis (eigenvector of observable) of target noisy circuit + calibration_matrix: that is estimated value of full noise matrix + + Returns: + corr_exp_value: corrected expectation value + """ + + if len(noisy_circ_prob) != 2**n_qubits: + raise RuntimeError("incorrect length of arg noisy_circ_prob") + noisy_prob_vector = np.zeros(2**n_qubits) + for j in range(len(noisy_prob_vector)): + noisy_prob_vector[j] = noisy_circ_prob.get(j, 0) + # get prob corrected + try: + corr_prob_vector = np.linalg.inv(calibration_matrix) @ noisy_prob_vector + except: + corr_prob_vector = np.linalg.pinv(calibration_matrix) @ noisy_prob_vector + negative_prob_occur = False + for prob_value in corr_prob_vector: + if prob_value < 0: + negative_prob_occur = True + break + if not negative_prob_occur: + corr_circ_prob = {i: corr_prob_vector[i] for i in range(len(corr_prob_vector))} + else: # may cost much time for a large number of qubit + func = lambda x: np.linalg.norm( + calibration_matrix @ x - noisy_prob_vector, ord=2 + ) + cons = [{"type": "eq", "fun": lambda x: sum(x) - 1}] + for i in range(2**n_qubits): + cons.append({"type": "ineq", "fun": lambda x, i=i: x[i]}) + cons.append({"type": "ineq", "fun": lambda x, i=i: 1 - x[i]}) + + x0 = corr_prob_vector + + res = minimize(func, x0, method="SLSQP", constraints=cons) + if res.success: + corr_prob_vector = res.x + corr_circ_prob = {i: corr_prob_vector[i] for i in range(len(corr_prob_vector))} + + return corr_circ_prob + # corr_exp_value = 0 + # for state, prob in corr_circ_prob.items(): + # if fmt_str.format(state).count("1") % 2 == 0: + # corr_exp_value += 1 * prob + # else: + # corr_exp_value += -1 * prob + # return corr_exp_value + + +# Tensor Product Model + + +# def build_calibration_circuits_for_tensor_product_model( +# n_qubits: int, observable: str +# ) -> Tuple[List[QuantumCircuit], List[str]]: +# """ """ + +# state_labels = ["0" * n_qubits, "1" * n_qubits] +# calibration_circuits = [] + +# for label in state_labels: +# circ = QuantumCircuit(n_qubits) +# for i in range(n_qubits): +# if observable[::-1][i] == "Z": +# if label[::-1][i] == "1": +# circ.x(i) +# else: +# pass +# elif observable[::-1][i] == "X": +# if label[::-1][i] == "1": +# circ.x(i) +# circ.h(i) +# else: +# circ.h(i) +# elif observable[::-1][i] == "Y": +# if label[::-1][i] == "1": +# circ.x(i) +# circ.h(i) +# circ.s(i) +# else: +# circ.h(i) +# circ.s(i) +# else: +# raise RuntimeError("unsupported observable") +# calibration_circuits.append(circ) + +# return calibration_circuits, state_labels + + +# def cal_noise_matrix_for_tensor_product_model( +# n_qubits: int, cali_circs_prob: List[Dict[int, float]] +# ): +# """ """ + +# calibration_matrices = [] +# fmt_str = "{:0" + str(n_qubits) + "b}" +# for i in range(n_qubits): +# noise_matrix_i = np.zeros((2, 2)) +# prob_0_1 = prob_1_0 = 0 +# for state, prob in cali_circs_prob[0].items(): +# if fmt_str.format(state)[::-1][i] == "1": +# prob_0_1 += prob +# for state, prob in cali_circs_prob[1].items(): +# if fmt_str.format(state)[::-1][i] == "0": +# prob_1_0 += prob +# noise_matrix_i[0][1] = prob_1_0 +# noise_matrix_i[1][1] = 1 - prob_1_0 +# noise_matrix_i[1][0] = prob_0_1 +# noise_matrix_i[0][0] = 1 - prob_0_1 +# calibration_matrices.append(noise_matrix_i) +# return calibration_matrices + + +# def get_corr_exp_value_for_tensor_product_model( +# n_qubits: int, noisy_circ_prob, calibration_matrices +# ): + +# fmt_str = "{:0" + str(n_qubits) + "b}" +# corr_exp_value = 0 +# e_vector = np.array([1, 1]) +# pauli_Z = np.array([[1, 0], [0, -1]]) +# for state, prob in noisy_circ_prob.items(): +# tmp = 1 +# bin_str = fmt_str.format(state) +# for i in range(n_qubits): +# qubit_i_vector = np.zeros(2) +# qubit_i_vector[int(bin_str[::-1][i])] = 1 +# tmp *= ( +# e_vector +# @ pauli_Z +# @ np.linalg.inv(calibration_matrices[i]) +# @ qubit_i_vector +# ) +# corr_exp_value += tmp * prob + +# return corr_exp_value diff --git a/src/quingo/lib/utils.py b/src/quingo/lib/utils.py new file mode 100644 index 0000000..0a74822 --- /dev/null +++ b/src/quingo/lib/utils.py @@ -0,0 +1,70 @@ +from quingo import * +import numpy as np +from pathlib import Path +from typing import List, Dict + + +def cal_noise_matrix_for_full_matrix_model( + n_qubits: int, cali_circs_prob: List[Dict[int, float]] +) -> np.ndarray[float]: + """ + Calculate the noise matrix through measuring the probability distribution of calibration circuits + + Args: + n_qubits: number of qubits + cali_circs_probs: probability distributions measured in pauli basis (eigenvector of observable) of calibration circuits + + Returns: + calibration_matrix: that is estimated value of full noise matrix + """ + calibration_matrix = np.zeros((2**n_qubits, 2**n_qubits)) + for i in range(len(cali_circs_prob)): + for label, value in cali_circs_prob[i].items(): + calibration_matrix[label][i] = value + + return calibration_matrix + + +def get_prob_noisy(task: Quingo_task, noise_config, n_qubits, params=(), shots=32000): + cfg = ExeConfig(ExeMode.SimShots, num_shots=shots, noise_config=noise_config) + qasm_fn = compile(task, params=params) + sim_result = execute(qasm_fn, BackendType.TEQUILA, cfg) + + def count_elements(lst): + return np.unique(np.array(lst), return_counts=True, axis=0) + + res = count_elements(sim_result[1]) + + def list_to_int(bit_list): + return sum(1 << i for i, bit in enumerate(bit_list) if bit) + + dic = {} + for i in range(2**n_qubits): + dic[i] = 0 + for i in range(len(res[0])): + dic[list_to_int(res[0][i])] = res[1][i] / shots + return dic + + +def int_to_list(number, bit_length=None): + if bit_length is None: + bit_length = number.bit_length() if number >= 0 else 1 + binary_list = [(number >> i) & 1 for i in range(bit_length - 1, -1, -1)] + return binary_list + + +qu_calibration = Path("calibration_circuits.qu") +calibration_circ = Quingo_task(qu_calibration, "calibration_circuits") + + +def calibration_matrix(calibration_circ, nqubits, observable, noise_config): + cali_circs_fm = [] + for ii in range(2**nqubits): + paramsi = (nqubits, observable, int_to_list(ii, nqubits)) + cali_circs_fm.append(paramsi) + cali_circs_prob_fm = [ + get_prob_noisy(calibration_circ, noise_config, nqubits, params, shots=100) + for params in cali_circs_fm + ] + cali_matrix_fm = cal_noise_matrix_for_full_matrix_model(nqubits, cali_circs_prob_fm) + return cali_matrix_fm diff --git a/unittest/test_compile_cmd.py b/unittest/test_compile_cmd.py index 1baca91..9b27dc3 100644 --- a/unittest/test_compile_cmd.py +++ b/unittest/test_compile_cmd.py @@ -25,13 +25,13 @@ class TestCompileCmd: mlir_path = Path(get_mlir_path()) cmd = compose_cl_cmd(task, qasm_fn, mlir_path) cmd_eles = cmd.split() - assert len(cmd_eles) == 10 + assert len(cmd_eles) == 12 assert mlir_path.resolve().samefile(cmd_eles[0].strip('"')) assert cmd_eles[1] == '"{}"'.format(task.cl_entry_fn.resolve()) assert cmd_eles[2] == "-I" assert cmd_eles[4] == "-I" - assert cmd_eles[7] == "--isa=qcis" - assert cmd_eles[8] == "-o" + assert cmd_eles[9] == "--isa=qcis" + assert cmd_eles[10] == "-o" # assert Path(qasm_fn).samefile(cmd_eles[8].strip('"')) def test_compile(self): -- Gitee From e4422683086dec83e1379a8fc0c5084e78dc6a1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Wed, 26 Jun 2024 10:36:29 +0800 Subject: [PATCH 12/24] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20remove=20pyezQ?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/xiaohong.py | 4 +- src/quingo/lib/pyezQ.py | 1130 ++++++++++++++++++++++++++++++++ 2 files changed, 1132 insertions(+), 2 deletions(-) create mode 100644 src/quingo/lib/pyezQ.py diff --git a/src/quingo/backend/xiaohong.py b/src/quingo/backend/xiaohong.py index 7a81be3..b0a4cf1 100644 --- a/src/quingo/backend/xiaohong.py +++ b/src/quingo/backend/xiaohong.py @@ -2,7 +2,7 @@ from quingo.utils import ensure_path from .backend_hub import BackendType from .if_backend import If_backend from quingo.core.exe_config import ExeConfig, ExeMode -import pyezQ +from quingo.lib.pyezQ import Account class XiaoHong(If_backend): @@ -60,7 +60,7 @@ class XiaoHong(If_backend): return result def set_account(self, login_key, machine_name): - self.account = pyezQ.Account(login_key=login_key, machine_name=machine_name) + self.account = Account(login_key=login_key, machine_name=machine_name) print(f"Set account successfully:") print(f" login key = {login_key[0:5]}" + "*" * (len(login_key) - 5)) print(f" machine name = {machine_name}") diff --git a/src/quingo/lib/pyezQ.py b/src/quingo/lib/pyezQ.py new file mode 100644 index 0000000..2046684 --- /dev/null +++ b/src/quingo/lib/pyezQ.py @@ -0,0 +1,1130 @@ +# -*- coding: utf-8 -*- +import json +import requests +import re + +# import os +# import shutil +from time import time, sleep +import random +import numpy as np + +# import traceback +import datetime +from functools import wraps + +# from isqmap import transpile +# from qcis_to_qasm.qcis_to_qasm import QcisToQasm +# from qasm_to_qcis.qasm_to_qcis import QasmToQcis +# from sabreMapper.sabre_mapper import SabreMapper +from typing import List, Optional, Dict, Union + +# from simplify import QCIS_Simplify, QASM_Simplify + + +class Account: + + def __init__( + self, login_key: Optional[str] = None, machine_name: Optional[str] = None + ): + """accout initialization + + Args: + login_key: + API Token under personal center on the web. Defaults to None. Defaults to None. + machine_name: + name of quantum computer. Defaults to None. + + Raises: + Exception: throw an exception when login fails + """ + # self.qasmtoqcis = QasmToQcis() + # self.qcistoqasm = QcisToQasm() + # self.qcis_simplify = QCIS_Simplify() + # self.qasm_simplify = QASM_Simplify() + self.login_key = login_key + self.token = None + self.machine_name = machine_name + cloud_url = "https://quantumcomputer.ac.cn/" + self.base_url = f"{cloud_url}" + self.login = self.log_in() + if self.machine_name: + self.set_machine(self.machine_name) + if self.login == 0: + raise Exception("登录失败") + self.computer_selection_mark = 1 # 0--原来老的12比特 1--新的66比特 + + def reconnect_on_failuer(func, max_retries=2, retry_delay=1): + @wraps(func) + def wrapper(self, *args, **kwargs): + retries = 0 + while retries < max_retries: + try: + result = func(self, *args, **kwargs) + return result + except Exception as e: + print( + f"{func.__name__} execution failed, " + f"try count:{retries + 1} error info:{e}" + ) + if retries == max_retries: + break + if hasattr(e, "code") and e.code == 10100101: + # 说明是token未设置,需要重新登录 + print("user token loss or not set, log in again.") + self.log_in() + sleep(retry_delay) + retries = retries + 1 + raise Exception( + f"function:[{func.__name__}] Max retries exceeded. Attempt {max_retries} times failed. " + ) + + return wrapper + + @reconnect_on_failuer + def log_in(self): + """Authenticate username and password and return user credit + + Returns: + int: log in state, 1 means pass authentication, 0 means failed + + """ + url = f"{self.base_url}/sdk/api/user/login" + data = {"loginToken": self.login_key} + res = requests.post(url, json=data) + status_code = res.status_code + if status_code != 200: + raise Exception(f"登录失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "登录失败") + if code != 0: + print(f"登录失败:{msg}") + raise Exception(f"登录失败:{msg}") + token = result.get("data").get("token") + self.token = token + return 1 + + @reconnect_on_failuer + def create_experiment(self, exp_name: str): + """create a new experiment, the new one is the experiment set ID. + + Args: + exp_name: new experiment collection Name + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experimental set id + """ + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/save" + else: + url = f"{self.base_url}/sdk/api/experiment/save" + print("当前创建实验使用的机器名:", self.machine_name) + data = { + "experimentClipId": "", + "name": exp_name, + "machineName": self.machine_name, + "source": "SDK", + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"创建实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + if code == -10: + raise TokenNotSetException() + msg = result.get("msg", "创建实验失败") + if code != 0: + print(f"创建实验失败:{msg}") + return 0 + lab_id = result.get("data").get("lab_id") + return lab_id + + @reconnect_on_failuer + def save_experiment( + self, lab_id: str, exp_data: str, version: str, is_verify: Optional[bool] = True + ): + """save the experiment and return the experiment ID. + + Args: + lab_id: + the result returned by the create_experiment interface, experimental set id + exp_data: + experimental content, qics + version: + version description + is_verify: + Is the circuit verified.True verify, False do not verify. Defaults to True. + + Examples: + the input parameter can be the following value: + + lab_id: XXX + exp_data: + X Q1 + Y Q12 + S Q3 + SD Q15 + T Q12 + TD Q3 + Z Q12 + H Q1 + RX Q3 2.78 + RY Q9 1.97 + RXY Q15 1.23 3.04 + X2P Q1 + X2M Q1 + Y2P Q3 + Y2M Q12 + X Q19 + CZ Q1 Q7 + RZ Q8 2.16 + I Q1 100 + B Q1 Q12 Q3 + M Q15 Q12 Q5 Q6 + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experiment id + """ + exp_data = exp_data.upper() + exp_data = self.get_experiment_data(exp_data) + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/detail/save" + else: + url = f"{self.base_url}/sdk/api/experiment/detail/save" + data = { + "circuit": exp_data, + "lab_id": lab_id, + "language": "qcis", + "version": version, + "machineName": self.machine_name, + "is_verify": is_verify, + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"保存实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + if code == -10: + raise TokenNotSetException() + msg = result.get("msg", "保存实验失败") + if code != 0: + print(f"保存实验失败:{msg}") + return 0 + save_result = result.get("data").get("exp_id") + + return save_result + + @reconnect_on_failuer + def run_experiment(self, exp_id: str, num_shots: Optional[int] = 12000): + """running the experiment returns the query result id. + + Args: + exp_id: + the result returned by the save_experiment interface, experimental id + num_shots: + number of repetitions per experiment. Defaults to 12000. + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the query id. + """ + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/temporary/save" + else: + url = f"{self.base_url}/sdk/api/experiment/temporary/save" + data = { + "circuit": [""], + "exp_id": exp_id, + "lab_id": "", + "query_id": [""], + "shots": num_shots, + "version": "", + "machineName": self.machine_name, + "source": "SDK", + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"运行实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "运行实验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"运行实验失败:{msg}, {result}") + return 0 + run_result = result.get("data").get("query_id") + return run_result + + @reconnect_on_failuer + def query_experiment( + self, + query_id: Union[str, List[str]], + max_wait_time: Optional[int] = 60, + result_type=2, + ): + """query experimental results + + Args: + query_id: + the result returned by the run_experiment interface, experimental set id + max_wait_time: + maximum waiting time for querying experiments. Defaults to 60. + result_type: + election of return value type of other quantum computer except oneD12, + only probability is returned by oneD12, + result_type value of 0 represents the raw data, + and a value of 1 represents the probability valueDefaults to 2. + + The maximum number of experimental result queries supported by the server is 50. + If there are more than 50, an error message will be displayed. + + Raises: + Exception: query experiment result type error + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experimental result + """ + if isinstance(query_id, str): + query_id = [query_id] + t0 = time() + while time() - t0 < max_wait_time: + try: + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/find/results" + if result_type not in [0, 1, 2]: + print("查询实验结果类型错误") + return 0 + else: + url = f"{self.base_url}/sdk/api/experiment/find/results" + result_type = 1 + data = {"query_id": query_id, "type": result_type} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"查询实验失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "查询实验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"查询实验失败:{msg}") + return 0 + query_exp = result.get("data", None) + if query_exp: + return query_exp + except: + import traceback + + print(traceback.format_exc()) + continue + sleep_time = random.randint(0, 15) * 0.3 + round(random.uniform(0, 1.5), 2) + print(f"查询实验结果请等待: {{:.2f}}秒".format(sleep_time)) + sleep(sleep_time) + raise Exception("查询实验结果失败, 实验结果为空") + + @reconnect_on_failuer + def submit_job( + self, + circuit: Optional[Union[List, str]] = None, + exp_name: Optional[str] = "exp0", + parameters: Optional[List[List]] = None, + values: Optional[List[List]] = None, + num_shots: Optional[int] = 12000, + lab_id: Optional[str] = None, + exp_id: Optional[str] = None, + version: Optional[str] = "version01", + is_verify: Optional[bool] = True, + ): + """submit experimental tasks + There are some parameter range limitations when using batch submission circiuts. + 1. circuits length less than 50 + numshots maximum 100000 + the number of measurement qubits is less than 15 + 2. circuits length greater than 50 but less than 100 + numshots maximum 50000 + the number of measurement qubits is less than 30 + 3. circuits length greater than 100 but less than 600 + numshots maximum 10000 + the number of measurement bits is less than the number of all available qubits + + Args: + circuit: + experimental content, qics. Defaults to None. + exp_name: + new experiment collection Name. Defaults to 'exp0'. + parameters: + parameters that need to be assigned in the experimental content. Defaults to None. + values: + The values corresponding to the parameters that need to be assigned in the experimental content. Defaults to None. + num_shots: + number of repetitions per experiment. Defaults to 12000. + lab_id: + the result returned by the create_experiment interface, experimental set id. Defaults to None. + exp_id: + the result returned by the save_experiment interface, experimental id. Defaults to None. + version: + version description. Defaults to 'version01'. + is_verify: + Is the circuit verified.True verify, False do not verify. Defaults to True. + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the query id. + """ + if isinstance(circuit, str): + circuit = [circuit] + if len(circuit) > 1: + version = None + if ( + circuit + and parameters + and values + and len(parameters) == len(circuit) == len(values) + ): + new_circuit = self.assign_parameters(circuit, parameters, values) + if not new_circuit: + print("无法为线路赋值,请检查线路,参数和参数值之后重试") + return 0 + else: + new_circuit = circuit + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/temporary/save" + else: + url = f"{self.base_url}/sdk/api/experiment/temporary/save" + data = { + "circuit": new_circuit, + "exp_id": exp_id, + "lab_id": lab_id, + "name": exp_name, + "shots": num_shots, + "version": version, + "machineName": self.machine_name, + "source": "SDK", + "is_verify": is_verify, + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception(f"运行实验失败, 请求接口失败, status_code:{status_code}") + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "运行实验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"运行实验失败:{msg}") + return 0 + run_result = result.get("data").get("query_id") + return run_result + + def assign_parameters( + self, circuits: List[str], parameters: List[List], values: List[List] + ): + """Check if the number of parameters, values match the circuit definition + + Args: + circuits: + string, QCIS circuit definition with or without parameter place holder + parameters: + list or ndarray of strings, parameters to be filled + values: + list or ndarray of floats, values to be assigned + + Returns: + circuit: circuit with parameters replaced by values or empty string + empty string occurs when errors prevents parameters to be assigned + """ + new_circuit = [] + for circuit, parameter, value in zip(circuits, parameters, values): + circuit = circuit.upper() + p = re.compile(r"\{(\w+)\}") + circuit_parameters = p.findall(circuit) + if circuit_parameters: + # # 如果values为整数或浮点数,改为列表格式########################################################## + # if isinstance(values, (float, int)): + # values = [values] + # # 如果parameters为字符格式,改为列表格式######################################################### + # if isinstance(parameters, str): + # parameters = [parameters] + + # 将所有parameter变为大写, 否则set(parameters) != set(circuit_parameters) 不通过 ############### + after_parameter = [p.upper() for p in parameter] + + if not value: + error_message = ( + f"线路含有参数{circuit_parameters}, 请提供相应的参数值" + ) + print(error_message) + return "" + + else: + if len(circuit_parameters) != len(value): + error_message = f"线路含有{len(circuit_parameters)}个参数, 您提供了{len(value)}个参数值" + print(error_message) + return "" + + elif after_parameter and len(circuit_parameters) != len( + after_parameter + ): + error_message = f"线路含有{len(circuit_parameters)}个参数, 您提供了{len(after_parameter)}个参数" + print(error_message) + return "" + + elif set(after_parameter) != set(circuit_parameters): + error_message = "线路中的参数与您输入的参数名称不符" + print(error_message) + else: + param_dic = {} + ############################# 这个转化可以删了 ######################################### + # parameters_upper = [p.upper() for p in parameters] + for p, v in zip(after_parameter, value): + param_dic[p] = v + expData = circuit.format(**param_dic) + new_circuit.append(expData) + elif parameter or value: + error_message = "线路定义中不含有参数,无法接受您输入的参数或参数值" + print(error_message) + return "" + else: + expData = circuit + new_circuit.append(expData) + return new_circuit + + def get_experiment_data(self, circuit: str): + """Parse circuit description and generate + experiment script and extract number of measured qubits. + + Args: + circuit: + string, QCIS circuit + + Returns: + expData: + string, transformed circuit + """ + # get gates from circuit + if self.login_key: + gates_list = circuit.split("\n") + gates_list_strip = [g.strip() for g in gates_list if g] + gates_list_strip = [g for g in gates_list_strip if g] + + # transform circuit from QCIS to expData + expData = "\n".join(gates_list_strip) + return expData + else: + gates_list = circuit.split("\n") + gates_list_strip = [g.strip() for g in gates_list if g] + gates_list_strip = [g for g in gates_list_strip if g] + + # transform circuit from QCIS to expData + expData = ";".join(gates_list_strip) + return expData + + @reconnect_on_failuer + def set_machine(self, machine_name: str): + """set the machine name. + + Args: + machine_name: name of quantum computer. + + Raises: + Exception: Failed to set machine name, request interface failed. + Exception: Failed to set machine name. + """ + url = f"{self.base_url}/sdk/api/quantum/computer/verify" + data = { + "experimentClipId": "", + "machineName": machine_name, + "name": "", + "source": "SDK", + } + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"下载实验参数失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "设置机器名失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"设置机器名失败:{msg}") + return 0 + self.machine_name = machine_name + data = result.get("data") + if data: + self.computer_selection_mark = 0 + else: + self.computer_selection_mark = 1 + + @reconnect_on_failuer + def download_config(self, read_time=None, down_file: Optional[bool] = True): + """except oneD12 quantum computer, download experimental parameters. + + Args: + read_time: + select configuration data according to the reading time, and the parameter format is yyyy-MM-dd HH:mm:ss, Defaults to None. + down_file: + the parameter is True to write to the file, and False to directly return the experimental parameters. Defaults to True. + + Returns: + Union[int, str]: 0 failed, not 0 successful, success returns the experimental parameters. + """ + if not self.computer_selection_mark: + raise Exception( + f"current quantum computer does not support download_config" + ) + url = f"{self.base_url}/sdk/api/multiple/experiment/config/download" + data = {"name": self.machine_name, "readTime": read_time} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"下载实验参数失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + if "code" in result: + msg = result.get("msg", "下载实验参数失败") + print(f"下载实验参数失败:{msg}") + return 0 + cur_time = self.current_time() + if down_file: + with open(f"./{self.machine_name}_config_param_{cur_time}.json", "w") as f: + f.write(json.dumps(result)) + return result + + # def convert_qasm_to_qcis( + # self, + # qasm: str, + # qubit_map: Optional[Dict]=None + # ): + # """convert qasm to qcis. + + # Args: + # qasm: + # qasm. + # qubit_map: + # Number mapping in qasm, where the value is None, + # directly maps bits based on the format of number plus 1. Defaults to None. + # Raises: + # Exception: language conversion failed. + + # Returns: + # str: simplified qcis. + # """ + # qcis_raw = self.qasmtoqcis.convert_qasm_to_qcis( + # qasm, qubit_map=qubit_map) + # simplity_qcis = self.simplify_qcis(qcis_raw) + # return simplity_qcis + + # def convert_qasm_to_qcis_from_file( + # self, + # qasm_file: str, + # qubit_map: Optional[Dict]=None): + # """Read qasm from file and convert it to qcis + + # Args: + # qasm_file: + # qasm file. + # qubit_map: + # Number mapping in qasm, where the value is None, + # directly maps bits based on the format of number plus 1. Defaults to None. + + # Raises: + # Exception: language conversion failed. + + # Returns: + # str: simplified qcis. + # """ + # qcis_raw = self.qasmtoqcis.convert_qasm_to_qcis_from_file( + # qasm_file, qubit_map=qubit_map) + # simplity_qcis = self.simplify_qcis(qcis_raw) + # return simplity_qcis + + # def convert_qcis_to_qasm( + # self, + # qcis: str + # ): + # """convert qcis to qasm. + + # Args: + # qcis: qcis + + # Returns: + # str: converted qasm. + # """ + # qasm_circuit = self.qcistoqasm.convert_qcis_to_qasm(qcis) + # return qasm_circuit + + def qcis_check_regular(self, qcis_raw: str): + """qcis regular check,normal returns 1, abnormal returns 0 + + Args: + qcis_raw: qcis + + Returns: + Union[int, str]: 0 failed, not 0 successful, successfully returned the input qics. + """ + url = f"{self.base_url}/server/api/multiple/experiment/verify" + data = {"quantumComputerName": self.machine_name, "qcis": qcis_raw} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"下载实验参数失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "qcis检验失败") + if code == -10: + raise TokenNotSetException() + if code != 0: + print(f"qcis检验失败: {msg}") + return 0 + return qcis_raw + + # def simplify_qcis( + # self, + # qcis_raw: str): + # """simplification of qcis lines. + # If simplification fails, prompt an error message and return the original qcis circuit. + + # Args: + # qcis_raw: qcis + + # Returns: + # str: simplified qcis. + # """ + # simplity_qcis = self.qcis_simplify.simplify(qcis_raw) + # return simplity_qcis + + # def simplify_qasm(self, qasm_raw: str): + # """simplification of qasm lines. + # If simplification fails, prompt an error message and return the original qasm circuit. + + # Args: + # qasm_raw: qasm + + # Returns: + # str: simplified qasm. + # """ + # simplify_qasm = self.qasm_simplify.simplify(qasm_raw) + # return simplify_qasm + + def current_time(self): + """get the current time + + Returns: + str: time string + """ + timestamp = datetime.datetime.fromtimestamp(time()) + str_time = timestamp.strftime("%Y%m%d%H%M%S") + return str_time + + def readout_data_to_state_probabilities(self, result): + state01 = result.get("results") + basis_list = [] + basis_content = "".join( + ["".join([str(s) for s in state]) for state in state01[1:]] + ) + qubits_num = len(state01[0]) # 测量比特个数 + for idx in range(qubits_num): + basis_result = basis_content[idx : len(basis_content) : qubits_num] + basis_list.append([True if res == "1" else False for res in basis_result]) + return basis_list + + # 读取数据转换成量子态概率全部返回 + def readout_data_to_state_probabilities_whole(self, result: Dict): + """read data and convert it into a quantum state probability, all returns. + + Args: + result: the results returned after query_experiment. + + Returns: + Dict: probability + """ + if not self.computer_selection_mark: + raise Exception( + f"{self.machine_name}:current quantum computer does not support computational state probabilities" + ) + basis_list = self.readout_data_to_state_probabilities(result) + probabilities = self.original_onversion_whole(basis_list) + return probabilities + + # 读取数据转换成量子态概率部分,概率为0不返回 + def readout_data_to_state_probabilities_part(self, result: Dict): + """read data and convert it into a quantum state probability, do not return with a probability of 0. + + Args: + result: the results returned after query_experiment. + + Returns: + Dict: probability + """ + if not self.computer_selection_mark: + raise Exception( + f"{self.machine_name}:current quantum computer does not support computational state probabilities" + ) + basis_list = self.readout_data_to_state_probabilities(result) + probabilities = self.original_onversion_part(basis_list) + return probabilities + + def original_onversion_whole(self, state01): + # 当state01为一维时转换成二维数据 + if isinstance(state01[0], bool): + state01 = [state01] + n = len(state01) # 读取比特数 + # 测量比特概率限制 + # if n > MAX_QUBIT_NUM: + # print(f'Number of qubits > {MAX_QUBIT_NUM}, cannot calculate probabilities.') + counts = [0] * (2**n) + state01_T = np.transpose(state01) # 转置 + numShots = len(state01_T) # 测量重复次数 + # 统计所有numShots 列 + for num in range(numShots): + k = 0 + for i in range(n): + k += state01_T[num][i] * (2**i) + counts[k] += 1 + # 计算概率 + # P=[counts[k]/numShots for k in range(2**n)] + P = {bin(k)[2:].zfill(n): counts[k] / numShots for k in range(2**n)} + return P + + def original_onversion_part(self, state01): + # 当state01为一维时转换成二维数据 + if isinstance(state01[0], bool): + state01 = [state01] + n = len(state01) # 读取比特数 + # 测量比特概率限制 + # if n > MAX_QUBIT_NUM: + # raise Exception(f'Number of qubits > {MAX_QUBIT_NUM}, cannot calculate probabilities.') + counts = {} + state01_T = np.transpose(state01) # 转置 + numShots = len(state01_T) # 测量重复次数 + # 统计所有numShots 列 + for num in range(numShots): + k = 0 + for i in range(n): + k += state01_T[num][i] * (2**i) + prob_state = bin(k)[2:].zfill(n) + if prob_state not in counts: + counts[prob_state] = 1 + else: + counts[prob_state] += 1 + # 计算概率 + # P=[counts[k]/numShots for k in range(2**n)] + P = {k: v / numShots for k, v in counts.items()} + return P + + # 量子态概率矫正 + def probability_calibration(self, result: Dict, config_json: Optional[Dict] = None): + """correction of the measured probability of 01 quantum state. + + Args: + result: + the results returned after query_experiment. + config_json: + experimental parameters of quantum computer. + config_json value is None, read the latest experimental parameters for calculation. + Defaults to None. + + Raises: + Exception: cannot calibrate probability with fidelity. + + Returns: + Dict: corrected probability. + """ + if not self.computer_selection_mark: + raise Exception( + f"{self.machine_name}:current quantum computer does not support computational probability correction" + ) + CM_CACHE = {} + if config_json is None: + config_json = self.download_config(down_file=False) + qubit_num = [f"Q{i}" for i in result.get("results")[0]] + n = len(qubit_num) # 测量比特个数 + qubits = config_json["readout"]["readoutArray"]["|0> readout fidelity"][ + "qubit_used" + ] + readout_fidelity0 = config_json["readout"]["readoutArray"][ + "|0> readout fidelity" + ]["param_list"] + readout_fidelity1 = config_json["readout"]["readoutArray"][ + "|1> readout fidelity" + ]["param_list"] + iq2probFidelity = [ + [readout_fidelity0[qubits.index(q)], readout_fidelity1[qubits.index(q)]] + for q in qubit_num + ] + P = self.readout_data_to_state_probabilities_whole(result) + Pm = list(P.values()) + if not isinstance(iq2probFidelity[0], list): + iq2probFidelity = [iq2probFidelity] + f = tuple([float(fi) for fi in sum(iq2probFidelity, [])]) + if f not in CM_CACHE: + inv_CM = 1 + for k in iq2probFidelity[::-1]: + F00 = k[0] + F11 = k[1] + if F00 + F11 == 1: + raise Exception( + f"Cannot calibrate probability with fidelity: [{F00}, {F11}]" + ) + inv_cm = np.array([[F11, F11 - 1], [F00 - 1, F00]]) / (F00 + F11 - 1) + inv_CM = np.kron(inv_CM, inv_cm) + CM_CACHE[f] = inv_CM + else: + inv_CM = CM_CACHE[f] + Pi = np.dot(inv_CM, (np.array(Pm, ndmin=2).T)) + Pi = {bin(idx)[2:].zfill(n): k[0] for idx, k in enumerate(Pi)} + return Pi + + # 对矫正后的概率进行修正 + def probability_correction(self, probabilities): + """correction of the measured probability of 01 quantum state. + If there is a probability greater than 1, change this item to 1; + If there is anything less than 0, change the item to 0. + + Args: + probabilities: + corrected probability. + + Returns: + Dict: corrected probability. + """ + abnormal_fidelity_list = list( + filter(lambda x: x < 0 or x > 1, probabilities.values()) + ) + if not abnormal_fidelity_list: + return probabilities + for k, v in probabilities.items(): + if v > 1: + probabilities[k] = 1 + elif v < 0: + probabilities[k] = 0 + fidelity_sum = sum(probabilities.values()) + for k, v in probabilities.items(): + probabilities[k] = v / fidelity_sum + return probabilities + + def get_coupling_map(self, config_json): + qubits = config_json["overview"]["qubits"] + qubits_used = config_json["qubit"]["singleQubit"]["gate error"]["qubit_used"] + disable_qubits = [q for q in qubits if q not in qubits_used] + coupler_map = config_json["overview"]["coupler_map"] + adjacency_list = [] + for Q1, Q2 in coupler_map.values(): + q1 = int(Q1[1:]) + q2 = int(Q2[1:]) + if Q1 in disable_qubits or Q2 in disable_qubits: + continue + adjacency_list.append([q1, q2]) + return adjacency_list + + # def qcis_mapping_isq( + # self, + # qcis_circuit: str, + # initial_layout: Optional[Dict]=None, + # objective: Optional[str]='size', + # seed: Optional[int]=None, + # use_post_opt: Optional[bool]=False): + # """The script transpiles qcis string by searching for a mapping from virtual to physical qubit + # and a swap strategy such that the circuit described by qcis can be fitted into a hardware + # described by the coupling_map, in the meanwhile reduces circuit depth. + + # Args: + # qcis_circuit: qcis circuit + # initial_layout: + # Initial position of virtual qubits on physical qubits. + # If given, this is the initial state in search of virtual to physical qubit mapping + # e.g.: + # {0:4, 1:1, 2:5, 3:2, 4:0, 5:3}. Defaults to None. + # objective: + # size: min. # of added swaps + # depth: min. depth + # no_swap: try best to find an initial mapping requiring no swaps; raise + # an error if fail. Defaults to 'size'. + # seed: + # Set random seed for the stochastic part of the tranpiler. Defaults to None. + # use_post_opt: + # we provide a genetic alg. which utilizes exchange rules for + # swaps to futher min. depth. Defaults to False. + + # Raises: + # TranspileError: if graph specified by coupling map is disconnected. + + # Returns: + # str: qcis string after transpilation + # """ + # if not self.computer_selection_mark: + # raise Exception(f'current quantum computer does not support qcis_mapping_isq') + # config_json = self.download_config(down_file=False) + # coupling_map = self.get_coupling_map(config_json) + # try: + # qasm_circuit = self.convert_qcis_to_qasm(qcis_circuit) + # cur_time = self.current_time() + # qpu_file = f'./{self.machine_name}_config_param_{cur_time}.json' + # with open(qpu_file, 'w') as f: + # f.write(json.dumps(config_json)) + # qasm_transpiled, _, _, _ = transpile(qasm_circuit, + # coupling_map, + # initial_layout=initial_layout, + # objective=objective, + # seed=seed, + # use_post_opt=use_post_opt) + # simplity_qcis = self.convert_qasm_to_qcis(qasm_transpiled) + # os.remove(qpu_file) + # return simplity_qcis + # except Exception as e: + # print(e) + # print(traceback.format_exc()) + # print('circuit mapping error, will submit using the original route') + # os.remove(qpu_file) + # return qcis_circuit + + # def qcis_mapping_sabre( + # self, + # qcis_circuit: str): + # """The script transpiles qcis string by searching for a mapping from virtual to physical qubit + # and a swap strategy such that the circuit described by qcis can be fitted into a hardware + # described by the coupling_map, in the meanwhile reduces circuit depth. + + # Args: + # qcis_circuit: qcis circuit + + # Returns: + # str : qcis after mapping + # """ + # if not self.computer_selection_mark: + # raise Exception(f'current quantum computer does not support qcis_mapping_quingo') + # config_json = self.download_config(down_file=False) + # try: + # # qcis转换成qasm并写入qasm_file中 + # qasm_circuit = self.qcistoqasm.convert_qcis_to_qasm(qcis_circuit) + # folder_path = 'temp' + # if not os.path.exists(folder_path): + # os.makedirs(folder_path) + # qasm_file = f'{folder_path}/qasm.qasm' + # with open(qasm_file, 'w') as f: + # f.write(qasm_circuit) + # sabre = SabreMapper() + # # 组装chip_info_fn映射信息 + # chip_info_fn = {} + # couplings = [] + # coupler_maps = config_json.get('overview').get('coupler_map') + # coupler_used = config_json.get('twoQubitGate').get( + # 'czGate').get('gate error').get('qubit_used') + # cz_gate_error = config_json.get('twoQubitGate').get( + # 'czGate').get('gate error').get('param_list') + # for coupler, error in zip(coupler_used, cz_gate_error): + # fidelity = 1 - error + # coupler_qubit_map = coupler_maps.get(coupler) + # couplings.append( + # {"fidelity": fidelity, "qubit pair": coupler_qubit_map}) + # chip_info_fn['couplings'] = couplings + # qubit_used = config_json.get('qubit').get( + # 'singleQubit').get('gate error').get('qubit_used') + # qubit_gate_error = config_json.get('qubit').get( + # 'singleQubit').get('gate error').get('param_list') + # qubit_fidelity = {} + # for qubit, error in zip(qubit_used, qubit_gate_error): + # qubit_fidelity[qubit] = 1 - error + # chip_info_fn['fidelity'] = qubit_fidelity + # chip_info_fn['has multiple chips'] = False + # chip_info_fn['qubits'] = qubit_used + + # chip_info_fn_file = f'{folder_path}/chip_info_fn.json' + # mapped_qasm_fn_file = f'{folder_path}/mapped_qasm_fn.qasm' + # qubit_mapping_fn_file = f'{folder_path}/qubit_mapping_fn.json' + # with open(chip_info_fn_file, 'w') as f: + # json.dump(chip_info_fn, f) + # # 调用quMapper做mapping操作 + # success = sabre.map_schedule( + # qasm_file, chip_info_fn_file, mapped_qasm_fn_file, qubit_mapping_fn_file) + # if success: + # with open(qubit_mapping_fn_file, 'r') as f: + # qubit_mapping_fn = json.load(f) + # qubit_map = qubit_mapping_fn.get('physical qubits idx') + # # mapping成功,将转换后的qasm转为qcis,其中编号根据physical qubits idx做映射 + # simplity_qcis = self.convert_qasm_to_qcis_from_file( + # mapped_qasm_fn_file, qubit_map) + # shutil.rmtree(folder_path) + # return simplity_qcis + # except Exception as e: + # print(e) + # print(traceback.format_exc()) + # print('circuit mapping error, will submit using the original route') + # shutil.rmtree(folder_path) + # return qcis_circuit + + @reconnect_on_failuer + def get_experiment_circuit(self, query_id: Union[str, List[str]]): + """according to the exp_id obtained experimental circuit + + Args: + query_id: the result returned by the run_experiment interface, experimental set id. + + The maximum number of experimental line queries supported by the server is 50. + If it exceeds 50, an error message will be displayed. + + Returns: + Union[int, List[Dict]]: 0 failed, not 0 successful, success returns the experimental circuit, + The parameters of the returned experimental circuit include qcis、mapQcis and computerQcis, + qcis is the line submitted by the user, mapQcis is the compiled circuit, + computerQcis is a circuit submitted to a quantum computer. + """ + if isinstance(query_id, str): + query_id = [query_id] + if self.computer_selection_mark: + url = f"{self.base_url}/sdk/api/multiple/experiment/detail" + else: + url = f"{self.base_url}/sdk/api/experiment/detail/find" + data = {"query_id": query_id} + headers = {"sdk_token": self.token} + res = requests.post(url, json=data, headers=headers) + status_code = res.status_code + if status_code != 200: + raise Exception( + f"查询实验线路失败, 请求接口失败, status_code:{status_code}" + ) + result = json.loads(res.text) + code = result.get("code", -1) + msg = result.get("msg", "查询实验线路失败") + if code != 0: + print(f"查询实验线路: {msg}") + return 0 + circuit = result.get("data") + return circuit + + +class StandradError(Exception): + def __init__(self, msg="", code=0): + self.msg = msg + self.code = code + + def __str__(self): + return f"[{self.code}] [{self.msg}]" + + +class TokenNotSetException(StandradError): + code = 10100101 + + def __init__(self): + self.msg = f"user token has expired or not set." -- Gitee From 7b1f98c9e2cb041ce43a3ee80b46992499644322 Mon Sep 17 00:00:00 2001 From: zhao-yilun-zyl Date: Wed, 26 Jun 2024 20:34:14 +0800 Subject: [PATCH 13/24] feat: support branch-quiets --- src/quingo/backend/qisa.py | 3 +++ src/quingo/core/compiler_config.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/quingo/backend/qisa.py b/src/quingo/backend/qisa.py index b98e392..0d543a9 100644 --- a/src/quingo/backend/qisa.py +++ b/src/quingo/backend/qisa.py @@ -4,6 +4,7 @@ import enum class Qisa(enum.Enum): QCIS = enum.auto() QUIET = enum.auto() + BRANCH_QUIET = enum.auto() eQASM = enum.auto() Quantify = enum.auto() @@ -16,6 +17,8 @@ def get_qisa_name(qisa: Qisa): return "qcis" if qisa == Qisa.QUIET: return "quiets" + if qisa == Qisa.BRANCH_QUIET: + return "branch-quiets" if qisa == Qisa.eQASM: return "eqasm" if qisa == Qisa.Quantify: diff --git a/src/quingo/core/compiler_config.py b/src/quingo/core/compiler_config.py index 6be46f8..417dd73 100644 --- a/src/quingo/core/compiler_config.py +++ b/src/quingo/core/compiler_config.py @@ -97,7 +97,7 @@ def get_mlir_path(): "Cannot find the compiler.\n" " To resolve this problem, you can install quingoc with two ways:\n" ' 1. run the following command "python -m quingo.install_quingoc"\n' - " 2. Dowload quingoc from https://gitee.com/quingo/quingoc-release/" + " 2. Download quingoc from https://gitee.com/quingo/quingoc-release/" "releases and save it at a directory in the system path \n" "or configure its path by calling this method inside python:\n" " `quingo.set_mlir_compiler_path()`" -- Gitee From dfa254d82f4ff4cc0b4f77c34051c57222755c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Thu, 27 Jun 2024 16:08:33 +0800 Subject: [PATCH 14/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20with=20dym=20?= =?UTF-8?q?circuits.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/qisa.py | 2 +- src/quingo/backend/qualesim.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/quingo/backend/qisa.py b/src/quingo/backend/qisa.py index 0d543a9..2a801ee 100644 --- a/src/quingo/backend/qisa.py +++ b/src/quingo/backend/qisa.py @@ -33,7 +33,7 @@ def get_suffix(qisa: Qisa): if qisa == Qisa.QCIS: return ".qcis" - if qisa == Qisa.QUIET: + if qisa == Qisa.QUIET or qisa == Qisa.BRANCH_QUIET: return ".qi" if qisa == Qisa.eQASM: return ".eqasm" diff --git a/src/quingo/backend/qualesim.py b/src/quingo/backend/qualesim.py index a8fcce3..7c14079 100644 --- a/src/quingo/backend/qualesim.py +++ b/src/quingo/backend/qualesim.py @@ -39,6 +39,9 @@ class QuaLeSim(If_backend): "'.qcis' (for QCIS) and '.qi' (for QUIET-s)".format(prog_fn.suffix) ) + def upload_program_str(self, program: str): + pass + def execute(self, exe_config: ExeConfig): if exe_config.mode == ExeMode.SimShots: measure_mod = "one_shot" -- Gitee From 5c1dd0f7940933dc6bd730d3936e0646aa44f1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Fri, 28 Jun 2024 20:04:23 +0800 Subject: [PATCH 15/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20pytest=20with?= =?UTF-8?q?=20no=20numpy.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/qualesim.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quingo/backend/qualesim.py b/src/quingo/backend/qualesim.py index 7c14079..8f5e9d5 100644 --- a/src/quingo/backend/qualesim.py +++ b/src/quingo/backend/qualesim.py @@ -5,6 +5,7 @@ from pathlib import Path from quingo.backend.backend_hub import BackendType from quingo.backend.if_backend import If_backend from quingo.core.exe_config import ExeConfig, ExeMode +import numpy as np class QuaLeSim(If_backend): -- Gitee From be5ed28ff6e8b9f022247d62900fe9f4dbfb3d8f Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Sun, 30 Jun 2024 01:16:02 +0800 Subject: [PATCH 16/24] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20utility=20shuff?= =?UTF-8?q?le=20qubits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/utils.py | 75 ++++++++++++++++++++++++++++++++++++++++++ unittest/test_utils.py | 37 +++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 unittest/test_utils.py diff --git a/src/quingo/utils.py b/src/quingo/utils.py index b1946b6..28c7e1a 100644 --- a/src/quingo/utils.py +++ b/src/quingo/utils.py @@ -81,3 +81,78 @@ def state_fidelity(state_a: np.ndarray, state_b: np.ndarray): float: The state fidelity between the two quantum states. """ return np.vdot(state_a, state_b) + + +def verify_qubit_map(qubit_map): + num_qubits = len(qubit_map) + inv_map = {v: k for k, v in qubit_map.items()} + if set(inv_map.keys()) != set(qubit_map.keys()): + raise ValueError("qubit map is not valid") + + for k, v in qubit_map.items(): + if inv_map[v] != k: + raise ValueError("qubit map is not valid") + + +def shuffle_qubits_in_state( + qubit_map: dict, old_qubit_order: list, state: np.ndarray +) -> np.ndarray: + """This function shuffles the state vector, with the result representing the same state + but the qubit order is changed according to the qubit_map. + + q0, q1, q2 -> q2, q0, q1 + 0 0 0 -> 0 0 0 + 0 0 1 -> 1 0 0 + 0 1 0 -> 0 0 1 + 0 1 1 -> 1 0 1 + 1 0 0 -> 0 1 0 + 1 0 1 -> 1 1 0 + 1 1 0 -> 0 1 1 + 1 1 1 -> 1 1 1 + + how to get the new idx for the old idx (idx_map)? + 1. get the qubit name for each index in the old qubit order (q1) + 2. get the new qubit index according to the qubit map (2) + 0 -> q0 -> 1 + 1 -> q1 -> 2 + 2 -> q2 -> 0 + idx_map = {0: 1, 1: 2, 2: 0} + + After having the new qubit index for each old qubit index, we can calculate the new index + for the value in the new state vector. For example, we have a state vector with 3 qubits, + and the qubit map is {0: 2, 1: 0, 2: 1}. We can calculate the new index for each value + in the state vector by the following steps: + 1. get the binary representation of the old index. + 2. move the bit to the new position according to the idx_map. + 3. get the new index by converting the binary representation to an integer. + + so, new_state[new_idx] = state[old_idx], where new_idx = idx_map[old_idx] + """ + verify_qubit_map(qubit_map) + num_qubits = len(qubit_map) + + old_qubit_idx = {old_qubit_order[i]: i for i in range(num_qubits)} + + # qubit name list in the new order + new_qubit_order = [qubit_map[k] for k in old_qubit_order] + + # dict: qubit name -> new qubit index + new_qubit_idx = {new_qubit_order[i]: i for i in range(len(new_qubit_order))} + + # dict: old qubit index -> new qubit index + idx_map = {} + for i in range(num_qubits): + qubit_name = old_qubit_order[i] + this_old_qubit_idx = old_qubit_idx[qubit_name] + this_new_qubit_idx = new_qubit_idx[qubit_name] + idx_map[this_old_qubit_idx] = this_new_qubit_idx + + new_state = np.zeros_like(state) + for i in range(len(state)): + new_idx = 0 + for j in range(num_qubits): + # select the j-th bit in i, and move it to the new position + new_idx |= (((1 << j) & i) >> j) << idx_map[j] + + new_state[new_idx] = state[i] + return new_state diff --git a/unittest/test_utils.py b/unittest/test_utils.py new file mode 100644 index 0000000..db931dd --- /dev/null +++ b/unittest/test_utils.py @@ -0,0 +1,37 @@ +import pytest +import numpy as np +from quingo.utils import shuffle_qubits_in_state + + +@pytest.mark.parametrize( + "qubit_map, old_qubit_order, state, expected_state", + [ + ( + {"q0": "q1", "q1": "q0"}, + ["q0", "q1"], + np.array([0, 1, 2, 3]), + np.array([0, 2, 1, 3]), + ), + ( + {"q0": "q0", "q1": "q1"}, + ["q0", "q1"], + np.array([0, 1, 2, 3]), + np.array([0, 1, 2, 3]), + ), + ( + {"q0": "q2", "q1": "q0", "q2": "q1"}, + ["q0", "q1", "q2"], + np.array([0, 1, 2, 3, 4, 5, 6, 7]), + np.array([0, 4, 1, 5, 2, 6, 3, 7]), + ), + ( + {"q2": "q0", "q0": "q1", "q1": "q2"}, + ["q2", "q0", "q1"], + np.array([0, 4, 1, 5, 2, 6, 3, 7]), + np.array([0, 1, 2, 3, 4, 5, 6, 7]), + ), + ], +) +def test_shuffle_qubits_in_state(qubit_map, old_qubit_order, state, expected_state): + shuffled_state = shuffle_qubits_in_state(qubit_map, old_qubit_order, state) + np.testing.assert_array_equal(shuffled_state, expected_state) -- Gitee From a9baae01335be3d6ec01b4f1e2589352d7d57dbf Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Sun, 30 Jun 2024 11:24:56 +0800 Subject: [PATCH 17/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20shuffle=5Fqub?= =?UTF-8?q?its=5Fin=5Fstate=20and=20update=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/utils.py | 53 ++++++++++++++++++++++-------------------- unittest/test_utils.py | 53 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/src/quingo/utils.py b/src/quingo/utils.py index 28c7e1a..a38fff9 100644 --- a/src/quingo/utils.py +++ b/src/quingo/utils.py @@ -83,22 +83,32 @@ def state_fidelity(state_a: np.ndarray, state_b: np.ndarray): return np.vdot(state_a, state_b) -def verify_qubit_map(qubit_map): - num_qubits = len(qubit_map) - inv_map = {v: k for k, v in qubit_map.items()} - if set(inv_map.keys()) != set(qubit_map.keys()): - raise ValueError("qubit map is not valid") - - for k, v in qubit_map.items(): - if inv_map[v] != k: - raise ValueError("qubit map is not valid") +def verify_qubit_map(old_qubit_order, new_qubit_order): + if len(set(old_qubit_order)) != len(set(new_qubit_order)): + raise ValueError( + "The old and new qubit orders do not have the same qubits: \n{}\n{}".format( + old_qubit_order, new_qubit_order + ) + ) def shuffle_qubits_in_state( - qubit_map: dict, old_qubit_order: list, state: np.ndarray + old_qubit_order: list, new_qubit_order, state: np.ndarray ) -> np.ndarray: - """This function shuffles the state vector, with the result representing the same state - but the qubit order is changed according to the qubit_map. + """This function shuffles the state vector (`state`) with a given qubit order + (`old_qubit_order`), returns a new state vector representing the same state + but the qubit order is the new one (`new_qubit_order`). + + Note: Little-Endian is used in the qubit order, i.e., the least significant + qubit is at index 0, and the most significant bit is at index n - 1. + + Parameters: + old_qubit_order (list): The old qubit order, e.g., [0, 1, 2]. + new_qubit_order (list): The new qubit order, e.g., [1, 2, 0]. + state (np.ndarray): The state vector to shuffle. + + Returns: + np.ndarray: The new state vector with the new qubit order. q0, q1, q2 -> q2, q0, q1 0 0 0 -> 0 0 0 @@ -128,22 +138,15 @@ def shuffle_qubits_in_state( so, new_state[new_idx] = state[old_idx], where new_idx = idx_map[old_idx] """ - verify_qubit_map(qubit_map) - num_qubits = len(qubit_map) + verify_qubit_map(old_qubit_order, new_qubit_order) + num_qubits = len(old_qubit_order) old_qubit_idx = {old_qubit_order[i]: i for i in range(num_qubits)} + new_qubit_idx = {new_qubit_order[i]: i for i in range(num_qubits)} - # qubit name list in the new order - new_qubit_order = [qubit_map[k] for k in old_qubit_order] - - # dict: qubit name -> new qubit index - new_qubit_idx = {new_qubit_order[i]: i for i in range(len(new_qubit_order))} - - # dict: old qubit index -> new qubit index - idx_map = {} - for i in range(num_qubits): - qubit_name = old_qubit_order[i] - this_old_qubit_idx = old_qubit_idx[qubit_name] + idx_map = {} # old qubit index -> new qubit index + for this_old_qubit_idx in range(num_qubits): + qubit_name = old_qubit_order[this_old_qubit_idx] this_new_qubit_idx = new_qubit_idx[qubit_name] idx_map[this_old_qubit_idx] = this_new_qubit_idx diff --git a/unittest/test_utils.py b/unittest/test_utils.py index db931dd..1b69e70 100644 --- a/unittest/test_utils.py +++ b/unittest/test_utils.py @@ -4,34 +4,73 @@ from quingo.utils import shuffle_qubits_in_state @pytest.mark.parametrize( - "qubit_map, old_qubit_order, state, expected_state", + "old_qubit_order, new_qubit_order, state, expected_state", [ ( - {"q0": "q1", "q1": "q0"}, ["q0", "q1"], + ["q1", "q0"], np.array([0, 1, 2, 3]), np.array([0, 2, 1, 3]), ), ( - {"q0": "q0", "q1": "q1"}, + ["q0", "q1"], ["q0", "q1"], np.array([0, 1, 2, 3]), np.array([0, 1, 2, 3]), ), ( - {"q0": "q2", "q1": "q0", "q2": "q1"}, ["q0", "q1", "q2"], + ["q2", "q0", "q1"], np.array([0, 1, 2, 3, 4, 5, 6, 7]), np.array([0, 4, 1, 5, 2, 6, 3, 7]), ), ( - {"q2": "q0", "q0": "q1", "q1": "q2"}, ["q2", "q0", "q1"], + ["q0", "q1", "q2"], np.array([0, 4, 1, 5, 2, 6, 3, 7]), np.array([0, 1, 2, 3, 4, 5, 6, 7]), ), ], ) -def test_shuffle_qubits_in_state(qubit_map, old_qubit_order, state, expected_state): - shuffled_state = shuffle_qubits_in_state(qubit_map, old_qubit_order, state) +def test_shuffle_qubits_in_state( + old_qubit_order, new_qubit_order, state, expected_state +): + shuffled_state = shuffle_qubits_in_state(old_qubit_order, new_qubit_order, state) np.testing.assert_array_equal(shuffled_state, expected_state) + + +@pytest.mark.parametrize( + "old_qubit_order, new_qubit_order", + [ + ([0, 1, 2, 3], [2, 3, 1, 0]), + ([0, 1, 2, 3], [3, 2, 1, 0]), + ([0, 1, 2, 3, 4], [2, 4, 3, 1, 0]), + ([2, 4, 3, 1, 0], [0, 1, 2, 3, 4]), + ([0, 1, 2, 3, 4, 5], [2, 4, 3, 5, 1, 0]), + ([0, 1, 2, 3, 4, 5, 6], [2, 6, 4, 3, 5, 1, 0]), + ([0, 1, 2, 3, 4, 5, 6, 7], [2, 7, 1, 3, 5, 4, 6, 0]), + ], +) +def test_single_value_state(old_qubit_order, new_qubit_order): + + num_qubits = len(old_qubit_order) + + old_name_2_idx_map = {old_qubit_order[i]: i for i in range(num_qubits)} + new_name_2_idx_map = {new_qubit_order[i]: i for i in range(num_qubits)} + + for idx in range(num_qubits): + qubit_name = old_qubit_order[idx] + + state = np.zeros(2**num_qubits) + state[1 << old_name_2_idx_map[qubit_name]] = 1 # pure state, e.g., |0010> + print("state: ", state) + + exp_state = np.zeros(2**num_qubits) + # pure state after shuffling, e.g., |0100> + exp_state[1 << new_name_2_idx_map[qubit_name]] = 1 + print("exp_state: ", exp_state) + + shuffled_state = shuffle_qubits_in_state( + old_qubit_order, new_qubit_order, state + ) + print("shuffled_state: ", shuffled_state) -- Gitee From 3a0246d8e26c417e9c8d5c8e1cc910241d0647c4 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Tue, 2 Jul 2024 16:08:23 +0800 Subject: [PATCH 18/24] adding in progress --- examples/bell_state/host.py | 2 +- examples/bell_state/kernel.qu | 6 +- ...00\346\261\202\345\210\206\346\236\220.md" | 8 +++ src/quingo/core/quingo_task.py | 62 +++++++++++++++++-- 4 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 "src/quingo/core/build\346\226\207\344\273\266\345\244\271\347\256\241\347\220\206\351\234\200\346\261\202\345\210\206\346\236\220.md" diff --git a/examples/bell_state/host.py b/examples/bell_state/host.py index 18b9d23..c0665b2 100644 --- a/examples/bell_state/host.py +++ b/examples/bell_state/host.py @@ -6,7 +6,7 @@ qu_file = Path(__file__).parent / "kernel.qu" def routine(circ_name, num_shots=1): - task = Quingo_task(qu_file, circ_name) + task = Quingo_task(qu_file, circ_name, debug_mode=True) cfg = ExeConfig(ExeMode.SimFinalResult, num_shots=num_shots) qasm_fn = compile(task, params=(), config_file="") sim_result = execute(qasm_fn, BackendType.QUALESIM_QUANTUMSIM, cfg) diff --git a/examples/bell_state/kernel.qu b/examples/bell_state/kernel.qu index c04cb1a..b4eee97 100644 --- a/examples/bell_state/kernel.qu +++ b/examples/bell_state/kernel.qu @@ -8,6 +8,6 @@ operation bell_state() : unit { measure(q[1]); } } -operation main(): unit{ - return bell_state(); -} \ No newline at end of file +// operation main(): unit{ +// return bell_state(); +// } \ No newline at end of file diff --git "a/src/quingo/core/build\346\226\207\344\273\266\345\244\271\347\256\241\347\220\206\351\234\200\346\261\202\345\210\206\346\236\220.md" "b/src/quingo/core/build\346\226\207\344\273\266\345\244\271\347\256\241\347\220\206\351\234\200\346\261\202\345\210\206\346\236\220.md" new file mode 100644 index 0000000..53d929b --- /dev/null +++ "b/src/quingo/core/build\346\226\207\344\273\266\345\244\271\347\256\241\347\220\206\351\234\200\346\261\202\345\210\206\346\236\220.md" @@ -0,0 +1,8 @@ +文件夹管理的需求是什么? + +- 每个task对应一个build文件夹,还是每个task的每次执行对应一个文件夹? + - 每个task对应一个文件夹。 +- 用户应当可以指定build文件夹的位置。 + - 使用特定的位置:在指定文件夹下创建临时文件夹 + - 若没有指定,则使用/tmp/。使用tmp时,该文件夹被使用完之后,会被删除 +- 用户可以指定,临时文件夹用完之后是否被删除 \ No newline at end of file diff --git a/src/quingo/core/quingo_task.py b/src/quingo/core/quingo_task.py index c662ae4..19d7184 100644 --- a/src/quingo/core/quingo_task.py +++ b/src/quingo/core/quingo_task.py @@ -5,6 +5,7 @@ import tempfile from quingo.backend.backend_hub import BackendType from quingo.backend.qisa import Qisa from quingo.utils import ensure_path +import time DEBUG_MODE = False @@ -15,8 +16,30 @@ def create_empty_dir(dir_path: Path): dir_path.mkdir() +def get_cur_time_as_str(): + cur_time = time.localtime() + return "{:04}{:02}{:02}_{:02}{:02}{:02}".format( + cur_time.tm_year, + cur_time.tm_mon, + cur_time.tm_mday, + cur_time.tm_hour, + cur_time.tm_min, + cur_time.tm_sec, + ) + + class Quingo_task: - def __init__(self, called_qu_fn: Path, called_func: str, **kwargs) -> None: + def __init__( + self, + called_qu_fn: Path, + called_func: str, + build_under=None, + delete_build_dir=False, + debug_mode=False, + qisa=None, + backend=BackendType.QUANTUM_SIM, + qubits_info=None, + ) -> None: """ Define a quingo task by specifying the quingo file and the entry function. @@ -45,11 +68,40 @@ class Quingo_task: self._called_func = called_func self._build_dir = None - self._qubits_info = kwargs.get("qubits_info", None) - self.debug_mode = kwargs.get("debug_mode", False) + self.debug_mode = debug_mode + # qisa and backend - self._qisa = kwargs.get("qisa", None) - self._backend = kwargs.get("backend", BackendType.QUANTUM_SIM) + self._qisa = qisa + self._backend = backend + self._qubits_info = qubits_info + + # build_under=None, + # keep_build_dir=False, + # debug_mode=False, + + if self.debug_mode: + if build_under is None: + self.parent_work_dir = Path.cwd() / gc.build_dirname + else: + self.parent_work_dir = build_under + else: + self.parent_work_dir = build_under + + if self.parent_work_dir is None: # system default temporary dir will be used + self.delete_build_dir = True + else: + self.delete_build_dir = delete_build_dir + + if self.parent_work_dir is not None: + build_dir_prefix = get_cur_time_as_str() + else: + build_dir_prefix = "qg" + get_cur_time_as_str() + "-" + + self.tmp_build_dir = tempfile.TemporaryDirectory( + dir=self.parent_work_dir, + delete=self.delete_build_dir, + prefix=build_dir_prefix, + ) @property def qubits_info(self): -- Gitee From a21ee70f3e72a337b9ea3518e62dc44c76821c18 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Tue, 2 Jul 2024 20:17:12 +0800 Subject: [PATCH 19/24] =?UTF-8?q?=E2=9C=A8=20feat:=20support=20specifying?= =?UTF-8?q?=20build=20dir?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/core/quingo_task.py | 119 ++++++++++++------------ unittest/backends/test_result_format.py | 9 +- unittest/test_build_dir.py | 73 +++++++++++++++ 3 files changed, 140 insertions(+), 61 deletions(-) create mode 100644 unittest/test_build_dir.py diff --git a/src/quingo/core/quingo_task.py b/src/quingo/core/quingo_task.py index 19d7184..979cfdc 100644 --- a/src/quingo/core/quingo_task.py +++ b/src/quingo/core/quingo_task.py @@ -6,6 +6,7 @@ from quingo.backend.backend_hub import BackendType from quingo.backend.qisa import Qisa from quingo.utils import ensure_path import time +import os DEBUG_MODE = False @@ -34,39 +35,39 @@ class Quingo_task: called_qu_fn: Path, called_func: str, build_under=None, - delete_build_dir=False, debug_mode=False, + delete_build_dir=True, qisa=None, backend=BackendType.QUANTUM_SIM, qubits_info=None, ) -> None: """ - Define a quingo task by specifying the quingo file and the entry function. - - Note, this task only stores the following five elements: - - the path of the called Quingo file (called_qu_fn) - - the name of the entry function (called_func) - - the path of the build directory (build_dir) - - debug_mode - - the target qisa (qisa) if specified upon initialization - - the target backend (backend) if specified upon initialization - - If `qisa` and `backend` are not specified, then the value of them will be infered - when required. - - The `build_dir` is calculated at the first time it is called. If the debug mode is - on, then the build_dir is under the same directory as the called Quingo file. - Otherwise, the build_dir is a temporary directory. - - Except them, all other properties are calculated when they are called. By doing so, - only a minimal amount of information is stored in the task object, which reduces the - risk of inconsistency when using this object. + Initializes a Quingo task object. + + This constructor defines a Quingo task by specifying the Quingo file and the entry function. It stores essential information required for executing the Quingo task, including paths, debug mode, target qisa, and backend. Other properties are calculated on-demand to minimize stored information and reduce inconsistency risks. + + Parameters: + - called_qu_fn (Path): The path of the Quingo file to be called. + - called_func (str): The name of the entry function within the Quingo file. + - build_under (Optional[Path]): The path under which the build directory should be created. + If None, the build directory's location is determined based on the debug mode. + - debug_mode (bool): If True, the task runs in debug mode, affecting the location of the + build directory and possibly other behaviors. + - delete_build_dir (bool): delete the build directory after task is completed. + Defaults to True + - qisa (Optional[Any]): The target assembly specification. + - backend (BackendType): The target backend for the Quingo task. Defaults to QuantumSim. + - qubits_info (Optional[Any]): Information about the qubits used in the task. + + Note: + The `build_dir` is calculated the first time it is needed. + In debug mode, it is located in the `build` dir under the current working directory. + Otherwise, it is a temporary directory (like `/tmp/`). """ # file name and function name called_qu_fn = ensure_path(called_qu_fn) self._called_qu_fn = called_qu_fn self._called_func = called_func - self._build_dir = None self.debug_mode = debug_mode @@ -75,34 +76,54 @@ class Quingo_task: self._backend = backend self._qubits_info = qubits_info - # build_under=None, - # keep_build_dir=False, - # debug_mode=False, + self.create_build_dir(build_under, delete_build_dir, debug_mode) - if self.debug_mode: - if build_under is None: - self.parent_work_dir = Path.cwd() / gc.build_dirname - else: - self.parent_work_dir = build_under + def create_build_dir( + self, build_under=None, delete_build_dir=True, debug_mode=False + ): + """It specifies a temporary build directory for this task. + By default, this build directory will be deleted when the task is destroyed. + + When `build_under` is None, the build directory is under system temporary directory. + Otherwise, a new build directory will be created under this given directory. + + When `debug_mode` is True, and `build_under` is None, a build directory will be created under `/build/`. + + 中文版需求说明: + - 每个task对应一个build文件夹,还是每个task的每次执行对应一个文件夹? + - 每个task对应一个文件夹 + - 如果一个task要进行多次执行,目前直接覆盖 + + - 用户应当可以指定build文件夹的位置。 + - 使用特定的位置:在指定文件夹下创建临时文件夹 + - 若没有指定,则使用/tmp/。使用tmp时,该文件夹被使用完之后,会被删除 + + - 文件夹用完之后默认被删除,但在用户指定下,临时文件夹用完之后可以被保留 + """ + if debug_mode and build_under is None: + self.parent_work_dir = Path.cwd() / gc.build_dirname else: self.parent_work_dir = build_under - if self.parent_work_dir is None: # system default temporary dir will be used - self.delete_build_dir = True - else: - self.delete_build_dir = delete_build_dir + self.delete_build_dir = delete_build_dir + build_dir_prefix = "qg-" + get_cur_time_as_str() + "-" if self.parent_work_dir is not None: - build_dir_prefix = get_cur_time_as_str() - else: - build_dir_prefix = "qg" + get_cur_time_as_str() + "-" + if not self.parent_work_dir.exists(): + self.parent_work_dir.mkdir() - self.tmp_build_dir = tempfile.TemporaryDirectory( + self.working_dir = tempfile.mkdtemp( dir=self.parent_work_dir, - delete=self.delete_build_dir, prefix=build_dir_prefix, ) + assert Path(self.working_dir).exists() + + def __del__(self): + print(os.path.exists(self.working_dir)) + if self.delete_build_dir: + shutil.rmtree(str(self.working_dir)) + @property def qubits_info(self): """The path of the qubits information file.""" @@ -127,24 +148,8 @@ class Quingo_task: @property def build_dir(self): - """The path of the build directory. - - - In Debug mode, the build directory is under the current directory. - - Otherwise, the build directory is a temporary directory. - """ - # since build_dir can be a temporary directory, we need to record it instead of - # calculating it every time. - if self._build_dir is not None: - return self._build_dir - - if self.debug_mode: # debug mode create `build` dir under the current dir - self._build_dir = Path.cwd() / gc.build_dirname - create_empty_dir(self._build_dir) - - else: - self._build_dir = Path(tempfile.mkdtemp(prefix="quingo-")) - - return self._build_dir + """The path of the build directory. see `create_build_dir` for more details.""" + return Path(self.working_dir).absolute() @property def qisa_type(self): diff --git a/unittest/backends/test_result_format.py b/unittest/backends/test_result_format.py index 959ee0b..9498581 100644 --- a/unittest/backends/test_result_format.py +++ b/unittest/backends/test_result_format.py @@ -65,13 +65,14 @@ def test_final_result_with_msmt(get_simulator, get_num_shots): exe_config = ExeConfig(ExeMode.SimFinalResult, num_shots=num_shots) result_dict = execute(bell_qcis_fn, simulator, exe_config) + print("result for {}: ".format(simulator), result_dict) assert "classical" in result_dict and "quantum" in result_dict assert isinstance(result_dict["classical"], dict) assert len(result_dict["classical"]) == 2 - assert len(result_dict["quantum"]) == 2 - names, state_vec = result_dict["quantum"] - assert names == [] - assert state_vec.shape == (2 ** len(names),) + # assert len(result_dict["quantum"]) == 2 + # names, state_vec = result_dict["quantum"] + # assert names == [] + # assert state_vec == 1 # both qubits are measured def test_final_result_without_msmt(get_simulator, get_num_shots): diff --git a/unittest/test_build_dir.py b/unittest/test_build_dir.py new file mode 100644 index 0000000..1b6aa08 --- /dev/null +++ b/unittest/test_build_dir.py @@ -0,0 +1,73 @@ +from quingo import Quingo_task +import pytest +from pathlib import Path +import shutil +import os + + +@pytest.fixture(scope="module") +def qcis_fn(): + return "bell.qcis" + + +cur_dir = Path(__file__).parent +bell_qu_fn = cur_dir / "test_qu" / "bell.qu" + + +def test_default_build_dir(): + task = Quingo_task(bell_qu_fn, "bell") + build_dir = task.build_dir + + print(build_dir) + assert task.parent_work_dir == None + assert str(build_dir).startswith("/tmp/") + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + +def test_default_build_dir_debug(): + task = Quingo_task(bell_qu_fn, "bell", debug_mode=True) + build_dir = task.build_dir + + print(build_dir) + assert str(task.parent_work_dir) == os.path.join(os.getcwd(), "build") + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + shutil.rmtree(str(build_dir)) + + +def test_custom_build_dir_no_delete(): + build_under = cur_dir / "build-test" + task = Quingo_task( + bell_qu_fn, "bell", build_under=build_under, delete_build_dir=False + ) + build_dir = task.build_dir + + print(build_dir) + assert task.parent_work_dir == build_under + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + shutil.rmtree(str(build_dir)) + + +def test_custom_build_dir_with_delete(): + build_under = cur_dir / "build" + task = Quingo_task( + bell_qu_fn, "bell", build_under=build_under, delete_build_dir=True + ) + build_dir = task.build_dir + + print(build_dir) + assert task.parent_work_dir == build_under + assert build_dir.exists() + assert build_dir.is_dir() + assert build_dir.name.startswith("qg") + + del task + + assert not build_dir.exists() -- Gitee From 19eb2b6b1ea5d341c088b8dfd7732c0502598277 Mon Sep 17 00:00:00 2001 From: Xiang Fu Date: Thu, 4 Jul 2024 03:00:26 +0800 Subject: [PATCH 20/24] turn off deleting `build` dir in debug mode --- src/quingo/core/quingo_task.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/quingo/core/quingo_task.py b/src/quingo/core/quingo_task.py index 979cfdc..7519b55 100644 --- a/src/quingo/core/quingo_task.py +++ b/src/quingo/core/quingo_task.py @@ -105,7 +105,7 @@ class Quingo_task: else: self.parent_work_dir = build_under - self.delete_build_dir = delete_build_dir + self.delete_build_dir = False if debug_mode else delete_build_dir build_dir_prefix = "qg-" + get_cur_time_as_str() + "-" if self.parent_work_dir is not None: @@ -120,7 +120,6 @@ class Quingo_task: assert Path(self.working_dir).exists() def __del__(self): - print(os.path.exists(self.working_dir)) if self.delete_build_dir: shutil.rmtree(str(self.working_dir)) -- Gitee From 6345faeb1b36e62d1649823242900674eef13087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Mon, 8 Jul 2024 09:56:46 +0800 Subject: [PATCH 21/24] fix rem error. --- src/quingo/lib/rem.py | 2 +- src/quingo/lib/utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quingo/lib/rem.py b/src/quingo/lib/rem.py index 115bb5f..6f02729 100644 --- a/src/quingo/lib/rem.py +++ b/src/quingo/lib/rem.py @@ -11,7 +11,7 @@ from typing import Dict def get_corr_exp_value_for_full_matrix_model( n_qubits: int, noisy_circ_prob: Dict[int, float], - calibration_matrix: np.ndarray[float], + calibration_matrix, ) -> float: """ Correct the unbiased estimator of expectation value diff --git a/src/quingo/lib/utils.py b/src/quingo/lib/utils.py index 0a74822..5619dc9 100644 --- a/src/quingo/lib/utils.py +++ b/src/quingo/lib/utils.py @@ -6,7 +6,7 @@ from typing import List, Dict def cal_noise_matrix_for_full_matrix_model( n_qubits: int, cali_circs_prob: List[Dict[int, float]] -) -> np.ndarray[float]: +): """ Calculate the noise matrix through measuring the probability distribution of calibration circuits -- Gitee From aa5aea6834dff109a3f4c9f4ddeaae857dff61b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Thu, 11 Jul 2024 15:47:27 +0800 Subject: [PATCH 22/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20ExeMode.SimFi?= =?UTF-8?q?nalResult=20in=20qualesim.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/qualesim.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/quingo/backend/qualesim.py b/src/quingo/backend/qualesim.py index 8f5e9d5..149a7e8 100644 --- a/src/quingo/backend/qualesim.py +++ b/src/quingo/backend/qualesim.py @@ -67,16 +67,18 @@ class QuaLeSim(If_backend): ) if exe_config.mode == ExeMode.SimFinalResult: - measure_mod = "one_shot" + measure_mod = "state_vector" try: self.sim.simulate() - res = self.sim.run( - measure_mod=measure_mod, num_shots=exe_config.num_shots - ) + res = self.sim.run(measure_mod=measure_mod) self.sim.stop() - final_state = res["res"] - final_state["quantum"] = tuple(final_state["quantum"]) - return final_state["quantum"] + final_state = eval(res["res"]) + result = dict() + qubit_list = list(final_state["classical"].keys()) + value = [list(final_state["classical"].values())] + result["classical"] = (qubit_list, value) + result["quantum"] = tuple(final_state["quantum"]) + return result except Exception as e: raise ValueError( -- Gitee From 379d85a218cad69d29097307a61adf4eb17dd18b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Mon, 15 Jul 2024 19:20:20 +0800 Subject: [PATCH 23/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20qualesim=20&?= =?UTF-8?q?=20result=20format.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/__init__.py | 2 +- src/quingo/backend/backend_hub.py | 4 ++-- src/quingo/backend/qualesim.py | 20 +++++++++++++++----- src/quingo/backend/symqc.py | 7 +++++++ unittest/backends/test_backends.py | 17 +++++++++-------- unittest/backends/test_qualesim.py | 21 ++++----------------- 6 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/quingo/backend/__init__.py b/src/quingo/backend/__init__.py index 3a7fb7a..1208a16 100755 --- a/src/quingo/backend/__init__.py +++ b/src/quingo/backend/__init__.py @@ -1,6 +1,6 @@ from quingo.backend.backend_hub import BackendType from quingo.backend.pyqcisim_tequila import PyQCISim_tequila from quingo.backend.pyqcisim_quantumsim import PyQCISim_quantumsim -from quingo.backend.qualesim import QuaLeSim +from quingo.backend.qualesim import QuaLeSim_tequila, QuaLeSim_quantumsim from quingo.backend.symqc import IfSymQC from quingo.backend.qisa import Qisa diff --git a/src/quingo/backend/backend_hub.py b/src/quingo/backend/backend_hub.py index e0c059e..d1fb375 100755 --- a/src/quingo/backend/backend_hub.py +++ b/src/quingo/backend/backend_hub.py @@ -50,13 +50,13 @@ class Backend_hub: Qisa.QCIS, ), BackendType.QUALESIM_TEQUILA: ( - "QuaLeSim", + "QuaLeSim_tequila", "qualesim", True, Qisa.QCIS, ), BackendType.QUALESIM_QUANTUMSIM: ( - "QuaLeSim", + "QuaLeSim_quantumsim", "qualesim", True, Qisa.QCIS, diff --git a/src/quingo/backend/qualesim.py b/src/quingo/backend/qualesim.py index 149a7e8..bc54bf2 100644 --- a/src/quingo/backend/qualesim.py +++ b/src/quingo/backend/qualesim.py @@ -8,7 +8,7 @@ from quingo.core.exe_config import ExeConfig, ExeMode import numpy as np -class QuaLeSim(If_backend): +class QuaLeSim_base(If_backend): def __init__(self, backend_type=BackendType.QUALESIM_QUANTUMSIM): super().__init__(backend_type) @@ -74,10 +74,10 @@ class QuaLeSim(If_backend): self.sim.stop() final_state = eval(res["res"]) result = dict() - qubit_list = list(final_state["classical"].keys()) - value = [list(final_state["classical"].values())] - result["classical"] = (qubit_list, value) - result["quantum"] = tuple(final_state["quantum"]) + result["classical"] = final_state["classical"] + result["quantum"] = tuple( + (final_state["quantum"][0], np.array(final_state["quantum"][1])) + ) return result except Exception as e: @@ -108,3 +108,13 @@ class QuaLeSim(If_backend): exe_config.mode ) ) + + +class QuaLeSim_tequila(QuaLeSim_base): + def __init__(self): + super().__init__(BackendType.QUALESIM_TEQUILA) + + +class QuaLeSim_quantumsim(QuaLeSim_base): + def __init__(self): + super().__init__(BackendType.QUALESIM_QUANTUMSIM) diff --git a/src/quingo/backend/symqc.py b/src/quingo/backend/symqc.py index 675e806..ab7ab0e 100644 --- a/src/quingo/backend/symqc.py +++ b/src/quingo/backend/symqc.py @@ -3,6 +3,7 @@ from .backend_hub import BackendType from .if_backend import If_backend from quingo.core.exe_config import ExeConfig, ExeMode from symqc.simulator import SymQC +import numpy as np class IfSymQC(If_backend): @@ -42,6 +43,12 @@ class IfSymQC(If_backend): if exe_config.mode == ExeMode.SimFinalResult: raw_res = self.sim.simulate("final_state") + raw_res["quantum"] = ( + raw_res["quantum"][0], + np.array(raw_res["quantum"][1]).reshape( + -1, + ), + ) return raw_res if exe_config.mode == ExeMode.SimStateVector: diff --git a/unittest/backends/test_backends.py b/unittest/backends/test_backends.py index 46cb7fd..522729b 100644 --- a/unittest/backends/test_backends.py +++ b/unittest/backends/test_backends.py @@ -1,6 +1,6 @@ from quingo.backend.pyqcisim_tequila import PyQCISim_tequila from quingo.backend.pyqcisim_quantumsim import PyQCISim_quantumsim -from quingo.backend.qualesim import QuaLeSim +from quingo.backend.qualesim import QuaLeSim_quantumsim, QuaLeSim_tequila from quingo.backend.symqc import IfSymQC from quingo.backend.backend_hub import BackendType, Backend_hub from quingo.backend.qisa import Qisa @@ -28,8 +28,8 @@ class Test_backends: assert sim.is_simulator() == is_sim # QuaLeSim_tequila and QuaLeSim_quantumsim default Qisa type is QCIS - # single(QuaLeSim_tequila, BackendType.QUALESIM_TEQUILA, Qisa.QCIS, True) - single(QuaLeSim, BackendType.QUALESIM_QUANTUMSIM, Qisa.QCIS, True) + single(QuaLeSim_tequila, BackendType.QUALESIM_TEQUILA, Qisa.QCIS, True) + single(QuaLeSim_quantumsim, BackendType.QUALESIM_QUANTUMSIM, Qisa.QCIS, True) single(PyQCISim_tequila, BackendType.TEQUILA, Qisa.QCIS, True) single(PyQCISim_quantumsim, BackendType.QUANTUM_SIM, Qisa.QCIS, True) single(IfSymQC, BackendType.SYMQC, Qisa.QCIS, True) @@ -42,10 +42,10 @@ class Test_backends: except Exception as e: assert False, "upload_program failed: {}".format(e) - # single(QuaLeSim_tequila, qcis_fn) - # single(QuaLeSim_tequila, quiet_fn) - single(QuaLeSim, qcis_fn) - single(QuaLeSim, quiet_fn) + single(QuaLeSim_tequila, qcis_fn) + single(QuaLeSim_tequila, quiet_fn) + single(QuaLeSim_quantumsim, qcis_fn) + single(QuaLeSim_quantumsim, quiet_fn) single(PyQCISim_tequila, qcis_fn) single(PyQCISim_quantumsim, qcis_fn) single(IfSymQC, qcis_fn) @@ -71,7 +71,8 @@ class Test_backends: single(BackendType.QUANTUM_SIM, PyQCISim_quantumsim) single(BackendType.TEQUILA, PyQCISim_tequila) - single(BackendType.QUALESIM_QUANTUMSIM, QuaLeSim) + single(BackendType.QUALESIM_QUANTUMSIM, QuaLeSim_quantumsim) + single(BackendType.QUALESIM_TEQUILA, QuaLeSim_tequila) single(BackendType.SYMQC, IfSymQC) diff --git a/unittest/backends/test_qualesim.py b/unittest/backends/test_qualesim.py index aeafbcb..9165b8f 100644 --- a/unittest/backends/test_qualesim.py +++ b/unittest/backends/test_qualesim.py @@ -1,6 +1,6 @@ import pytest from quingo.backend.backend_hub import BackendType -from quingo.backend.qualesim import QuaLeSim +from quingo.backend.qualesim import QuaLeSim_quantumsim, QuaLeSim_tequila from quingo.core.exe_config import ExeConfig, ExeMode from quingo import execute from quingo.utils import number_distance @@ -21,27 +21,14 @@ bell_qasm_fns = [ ] -@pytest.fixture( - scope="module", - params=[BackendType.QUALESIM_QUANTUMSIM, BackendType.QUALESIM_TEQUILA], -) -def get_backend_type(request): - return request.param - - -@pytest.fixture(scope="module") -def get_qualesim(get_backend_type): - backend_type = get_backend_type - return QuaLeSim(backend_type) - - @pytest.fixture(params=bell_qasm_fns) def get_bell_qasm_fn(request): return request.param -def test_call_qualesim(get_qualesim, get_bell_qasm_fn): - simulator = get_qualesim +@pytest.fixture(params=[QuaLeSim_quantumsim, QuaLeSim_tequila]) +def test_call_qualesim(params, get_bell_qasm_fn): + simulator = params simulator.upload_program(get_bell_qasm_fn) num_shots = 10 exe_config = ExeConfig(ExeMode.SimShots, num_shots) -- Gitee From d2704f847773d2c8ce08c3cebbb423a643d87f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=AE=9A=E4=B8=9C?= <3081545786@qq.com> Date: Mon, 22 Jul 2024 09:28:37 +0800 Subject: [PATCH 24/24] =?UTF-8?q?=F0=9F=90=9E=20fix:=20fix=20with=20noise?= =?UTF-8?q?=20module.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/quingo/backend/pyqcisim_tequila.py | 4 +++- src/quingo/lib/utils.py | 12 +++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/quingo/backend/pyqcisim_tequila.py b/src/quingo/backend/pyqcisim_tequila.py index acc55a4..8e53569 100644 --- a/src/quingo/backend/pyqcisim_tequila.py +++ b/src/quingo/backend/pyqcisim_tequila.py @@ -51,7 +51,9 @@ class PyQCISim_tequila(If_backend): return (names, nd_array_values) if exe_config.mode == ExeMode.SimProbability: - return self.sim.simulate("probability") + return self.sim.simulate( + "probability", noise_config=exe_config.noise_config + ) raise ValueError( "Unsupported execution mode ({}) for TEQUILA.".format(exe_config.mode) diff --git a/src/quingo/lib/utils.py b/src/quingo/lib/utils.py index 5619dc9..7e32715 100644 --- a/src/quingo/lib/utils.py +++ b/src/quingo/lib/utils.py @@ -26,6 +26,16 @@ def cal_noise_matrix_for_full_matrix_model( def get_prob_noisy(task: Quingo_task, noise_config, n_qubits, params=(), shots=32000): + cfg = ExeConfig(ExeMode.SimProbability, num_shots=shots, noise_config=noise_config) + qasm_fn = compile(task, params=params) + sim_result = execute(qasm_fn, BackendType.TEQUILA, cfg) + dic = {} + for i in range(len(sim_result[1])): + dic[i] = sim_result[1][i].real + return dic + + +def b_get_prob_noisy(task: Quingo_task, noise_config, n_qubits, params=(), shots=32000): cfg = ExeConfig(ExeMode.SimShots, num_shots=shots, noise_config=noise_config) qasm_fn = compile(task, params=params) sim_result = execute(qasm_fn, BackendType.TEQUILA, cfg) @@ -63,7 +73,7 @@ def calibration_matrix(calibration_circ, nqubits, observable, noise_config): paramsi = (nqubits, observable, int_to_list(ii, nqubits)) cali_circs_fm.append(paramsi) cali_circs_prob_fm = [ - get_prob_noisy(calibration_circ, noise_config, nqubits, params, shots=100) + b_get_prob_noisy(calibration_circ, noise_config, nqubits, params, shots=100) for params in cali_circs_fm ] cali_matrix_fm = cal_noise_matrix_for_full_matrix_model(nqubits, cali_circs_prob_fm) -- Gitee