From f59c1e8e25a242ad6c736509dcbd4d45fc2618f4 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Sun, 7 Aug 2022 00:28:59 +0200 Subject: [PATCH 1/5] create_fake_backend: tool for creating fake backend files and settings --- tools/create_fake_backend.py | 199 +++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100755 tools/create_fake_backend.py diff --git a/tools/create_fake_backend.py b/tools/create_fake_backend.py new file mode 100755 index 000000000000..8041c6df910b --- /dev/null +++ b/tools/create_fake_backend.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python3 + +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Utility script to create fake backends""" + +import argparse +import sys +from string import Template +import os + +from update_fake_backends import DEFAULT_DIR +from qiskit import IBMQ + +RENO_DIR = os.path.join( + os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + "releasenotes", + "notes", +) + +HEADER = """# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. +""" + +init_py = Template( + """${HEADER} +\"\"\"Mock ${capital_backend_name} backend\"\"\" + +from .fake_${backend_name} import Fake${capital_backend_name}V2 +from .fake_${backend_name} import Fake${capital_backend_name} +""" +) + +fake_be_py = Template( + """${HEADER} + +\"\"\" +Fake ${capital_backend_name} device (${no_qubits} qubit). +\"\"\" + +import os +from qiskit.providers.fake_provider import fake_pulse_backend, fake_backend + + +class Fake${capital_backend_name}V2(fake_backend.FakeBackendV2): + \"\"\"A fake ${no_qubits} qubit backend.\"\"\" + + dirname = os.path.dirname(__file__) + conf_filename = "conf_${backend_name}.json" + props_filename = "props_${backend_name}.json" + defs_filename = "defs_${backend_name}.json" + backend_name = "fake_${backend_name}_v2" + + +class Fake${capital_backend_name}(fake_pulse_backend.FakePulseBackend): + \"\"\"A fake ${no_qubits} qubit backend.\"\"\" + + dirname = os.path.dirname(__file__) + conf_filename = "conf_${backend_name}.json" + props_filename = "props_${backend_name}.json" + defs_filename = "defs_${backend_name}.json" + backend_name = "fake_${backend_name}" +""" +) + +reno_template = Template( + """--- +features: + - | + The fake backend :class:`~Fake${capital_backend_name}` was added with the information + from IBM Quantum `${system_name}` system. +""" +) + + +def _insert_line_in_section( + line_to_insert, section, line_in_section_starts_with, backend_dir, file_to_modify +): + tmp_file = os.path.join(backend_dir, "_tmp_.py") + with open(file_to_modify, "r") as original, open(tmp_file, "a+") as destination: + previous_line = "" + in_section = False + for line in original.readlines(): + if line.startswith(section): + in_section = True + elif ( + in_section + and line.startswith(line_in_section_starts_with) + and previous_line < line_to_insert < line + ): + in_section = False + destination.write(line_to_insert) + destination.write(line) + os.replace(tmp_file, file_to_modify) + + +def _main(): + parser = argparse.ArgumentParser(description="Create fake backend") + parser.add_argument("--dir", "-d", type=str, default=DEFAULT_DIR) + parser.add_argument("backend_name", type=str, default=None) + parser.add_argument("system_name", type=str, default=None) + parser.add_argument("--project", type=str, default=None) + parser.add_argument("--hub", type=str, default=None) + parser.add_argument("--group", type=str, default=None) + args = parser.parse_args() + + backend_dir = os.path.join(args.dir, args.backend_name) + if os.path.isdir(backend_dir): + print(f"ERROR: Directory {backend_dir} already exists") + sys.exit(1) + + provider = IBMQ.load_account() + if args.hub or args.group or args.project: + provider = IBMQ.get_provider(hub=args.hub, group=args.group, project=args.project) + ibmq_backend = provider.get_backend(args.system_name) + + vars_ = { + "HEADER": HEADER, + "backend_name": args.backend_name, + "capital_backend_name": args.backend_name.capitalize(), + "no_qubits": len(ibmq_backend.properties().qubits), + "system_name": args.system_name, + } + + # Step 1. Create the directory for the backend + os.mkdir(backend_dir) + + # Step 2. /__init__.py + result = init_py.substitute(vars_) + with open(os.path.join(backend_dir, "__init__.py"), "w") as fd: + fd.write(result) + + # Step 3. /fake_.py + result = fake_be_py.substitute(vars_) + with open(os.path.join(backend_dir, f"fake_{args.backend_name}.py"), "w") as fd: + fd.write(result) + + # Step 4. update /../__init__.py + init_file = os.path.join(backend_dir, "..", "__init__.py") + backend_v1_line = f"from .{vars_['backend_name']} import Fake{vars_['capital_backend_name']}\n" + backend_v2_line = ( + f"from .{vars_['backend_name']} import Fake{vars_['capital_backend_name']}V2\n" + ) + _insert_line_in_section(backend_v1_line, "# BackendV1", "from", backend_dir, init_file) + _insert_line_in_section(backend_v2_line, "# BackendV2", "from", backend_dir, init_file) + + # Step 5. update /../../__init__.py + init_file = os.path.join(backend_dir, "..", "..", "__init__.py") + backend_v1_line = f" Fake{vars_['capital_backend_name']}\n" + backend_v2_line = f" Fake{vars_['capital_backend_name']}V2\n" + _insert_line_in_section(backend_v1_line, "Fake V1 Backends", " ", backend_dir, init_file) + _insert_line_in_section(backend_v2_line, "Fake V2 Backends", " ", backend_dir, init_file) + + # Step 6. update /../../fake_provider.py + init_file = os.path.join(backend_dir, "..", "..", "fake_provider.py") + backend_v1_line = f" Fake{vars_['capital_backend_name']}(),\n" + backend_v2_line = f" Fake{vars_['capital_backend_name']}V2(),\n" + _insert_line_in_section( + backend_v1_line, + "class FakeProvider(ProviderV1)", + " Fake", + backend_dir, + init_file, + ) + _insert_line_in_section( + backend_v2_line, + "class FakeProviderForBackendV2", + " Fake", + backend_dir, + init_file, + ) + + # Step 7. releasenotes/notes/fake_-deadbeef.yaml + result = reno_template.substitute(vars_) + with open(os.path.join(RENO_DIR, f"fake_{args.backend_name}-deadbeef.yaml"), "w") as fd: + fd.write(result) + + +if __name__ == "__main__": + _main() From 19a864aed8bc0732bdf7a88b360a5804382faeb6 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 16 Sep 2022 17:06:29 +0200 Subject: [PATCH 2/5] remove v1 generation --- tools/create_fake_backend.py | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/tools/create_fake_backend.py b/tools/create_fake_backend.py index 8041c6df910b..66c4e6256fef 100755 --- a/tools/create_fake_backend.py +++ b/tools/create_fake_backend.py @@ -43,10 +43,9 @@ init_py = Template( """${HEADER} -\"\"\"Mock ${capital_backend_name} backend\"\"\" +\"\"\"Fake ${capital_backend_name} device ({no_qubits} qubits)\"\"\" from .fake_${backend_name} import Fake${capital_backend_name}V2 -from .fake_${backend_name} import Fake${capital_backend_name} """ ) @@ -58,7 +57,7 @@ \"\"\" import os -from qiskit.providers.fake_provider import fake_pulse_backend, fake_backend +from qiskit.providers.fake_provider import fake_backend class Fake${capital_backend_name}V2(fake_backend.FakeBackendV2): @@ -70,15 +69,6 @@ class Fake${capital_backend_name}V2(fake_backend.FakeBackendV2): defs_filename = "defs_${backend_name}.json" backend_name = "fake_${backend_name}_v2" - -class Fake${capital_backend_name}(fake_pulse_backend.FakePulseBackend): - \"\"\"A fake ${no_qubits} qubit backend.\"\"\" - - dirname = os.path.dirname(__file__) - conf_filename = "conf_${backend_name}.json" - props_filename = "props_${backend_name}.json" - defs_filename = "defs_${backend_name}.json" - backend_name = "fake_${backend_name}" """ ) @@ -156,31 +146,19 @@ def _main(): # Step 4. update /../__init__.py init_file = os.path.join(backend_dir, "..", "__init__.py") - backend_v1_line = f"from .{vars_['backend_name']} import Fake{vars_['capital_backend_name']}\n" backend_v2_line = ( f"from .{vars_['backend_name']} import Fake{vars_['capital_backend_name']}V2\n" ) - _insert_line_in_section(backend_v1_line, "# BackendV1", "from", backend_dir, init_file) _insert_line_in_section(backend_v2_line, "# BackendV2", "from", backend_dir, init_file) # Step 5. update /../../__init__.py init_file = os.path.join(backend_dir, "..", "..", "__init__.py") - backend_v1_line = f" Fake{vars_['capital_backend_name']}\n" backend_v2_line = f" Fake{vars_['capital_backend_name']}V2\n" - _insert_line_in_section(backend_v1_line, "Fake V1 Backends", " ", backend_dir, init_file) _insert_line_in_section(backend_v2_line, "Fake V2 Backends", " ", backend_dir, init_file) # Step 6. update /../../fake_provider.py init_file = os.path.join(backend_dir, "..", "..", "fake_provider.py") - backend_v1_line = f" Fake{vars_['capital_backend_name']}(),\n" backend_v2_line = f" Fake{vars_['capital_backend_name']}V2(),\n" - _insert_line_in_section( - backend_v1_line, - "class FakeProvider(ProviderV1)", - " Fake", - backend_dir, - init_file, - ) _insert_line_in_section( backend_v2_line, "class FakeProviderForBackendV2", From 9626f6ca20a54ee9056de735a983725eadd27b31 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 16 Sep 2022 17:28:13 +0200 Subject: [PATCH 3/5] ups --- tools/create_fake_backend.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/create_fake_backend.py b/tools/create_fake_backend.py index 66c4e6256fef..452b2ee681a8 100755 --- a/tools/create_fake_backend.py +++ b/tools/create_fake_backend.py @@ -43,7 +43,7 @@ init_py = Template( """${HEADER} -\"\"\"Fake ${capital_backend_name} device ({no_qubits} qubits)\"\"\" +\"\"\"Fake ${capital_backend_name} device (${no_qubits} qubits)\"\"\" from .fake_${backend_name} import Fake${capital_backend_name}V2 """ @@ -53,7 +53,7 @@ """${HEADER} \"\"\" -Fake ${capital_backend_name} device (${no_qubits} qubit). +Fake ${capital_backend_name} device (${no_qubits} qubits). \"\"\" import os From d1e8ba4b7d1e568b19b9e93791b9d7b3218ee193 Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Fri, 16 Sep 2022 18:41:36 +0200 Subject: [PATCH 4/5] remove the V2 from the name --- tools/create_fake_backend.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tools/create_fake_backend.py b/tools/create_fake_backend.py index 452b2ee681a8..1c7a0c039421 100755 --- a/tools/create_fake_backend.py +++ b/tools/create_fake_backend.py @@ -45,7 +45,7 @@ """${HEADER} \"\"\"Fake ${capital_backend_name} device (${no_qubits} qubits)\"\"\" -from .fake_${backend_name} import Fake${capital_backend_name}V2 +from .fake_${backend_name} import Fake${capital_backend_name} """ ) @@ -60,14 +60,14 @@ from qiskit.providers.fake_provider import fake_backend -class Fake${capital_backend_name}V2(fake_backend.FakeBackendV2): +class Fake${capital_backend_name}(fake_backend.FakeBackendV2): \"\"\"A fake ${no_qubits} qubit backend.\"\"\" dirname = os.path.dirname(__file__) conf_filename = "conf_${backend_name}.json" props_filename = "props_${backend_name}.json" defs_filename = "defs_${backend_name}.json" - backend_name = "fake_${backend_name}_v2" + backend_name = "fake_${backend_name}" """ ) @@ -146,22 +146,20 @@ def _main(): # Step 4. update /../__init__.py init_file = os.path.join(backend_dir, "..", "__init__.py") - backend_v2_line = ( - f"from .{vars_['backend_name']} import Fake{vars_['capital_backend_name']}V2\n" - ) + backend_v2_line = f"from .{vars_['backend_name']} import Fake{vars_['capital_backend_name']}\n" _insert_line_in_section(backend_v2_line, "# BackendV2", "from", backend_dir, init_file) # Step 5. update /../../__init__.py init_file = os.path.join(backend_dir, "..", "..", "__init__.py") - backend_v2_line = f" Fake{vars_['capital_backend_name']}V2\n" + backend_v2_line = f" Fake{vars_['capital_backend_name']}\n" _insert_line_in_section(backend_v2_line, "Fake V2 Backends", " ", backend_dir, init_file) # Step 6. update /../../fake_provider.py init_file = os.path.join(backend_dir, "..", "..", "fake_provider.py") - backend_v2_line = f" Fake{vars_['capital_backend_name']}V2(),\n" + backend_v2_line = f" Fake{vars_['capital_backend_name']}(),\n" _insert_line_in_section( backend_v2_line, - "class FakeProviderForBackendV2", + "class FakeProviderForBackend", " Fake", backend_dir, init_file, From dfb4df6cd299184d63215b4278ba3a261d53923b Mon Sep 17 00:00:00 2001 From: Luciano Bello Date: Mon, 19 Sep 2022 17:50:12 +0200 Subject: [PATCH 5/5] use reno for random string --- tools/create_fake_backend.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tools/create_fake_backend.py b/tools/create_fake_backend.py index 1c7a0c039421..f2034b97e39b 100755 --- a/tools/create_fake_backend.py +++ b/tools/create_fake_backend.py @@ -18,6 +18,7 @@ import sys from string import Template import os +from reno.utils import get_random_string from update_fake_backends import DEFAULT_DIR from qiskit import IBMQ @@ -165,9 +166,11 @@ def _main(): init_file, ) - # Step 7. releasenotes/notes/fake_-deadbeef.yaml + # Step 7. releasenotes/notes/fake_-.yaml result = reno_template.substitute(vars_) - with open(os.path.join(RENO_DIR, f"fake_{args.backend_name}-deadbeef.yaml"), "w") as fd: + with open( + os.path.join(RENO_DIR, f"fake_{args.backend_name}-{get_random_string()}.yaml"), "w" + ) as fd: fd.write(result)