├── src └── graph_state_generation │ ├── __init__.py │ ├── utility │ ├── __init__.py │ └── constant.py │ ├── substrate_scheduler │ ├── __init__.py │ └── substrate_scheduler_two_row.py │ ├── visualization_tools │ ├── __init__.py │ └── ascii_visualizer.py │ ├── optimizers │ ├── __init__.py │ ├── node_to_patch_mapper.py │ ├── stabilizer_measurement_scheduler.py │ └── pre_mapping_optimizer.py │ └── old_code │ └── visualize_result.py ├── data ├── __init__.py └── gen_graphs.py ├── code_visualization ├── 0.png ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── graph.png ├── animated.gif └── graphtest.png ├── actions ├── style │ └── action.yml └── test │ └── action.yml ├── tests ├── test_stabilizer_measurement_scheduler.py └── test_pre_mapping_optimizer.py ├── .github ├── pull_request_template.md ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── python-publish.yml │ ├── style.yml │ └── test.yml ├── .vscode └── settings.json ├── setup.cfg ├── LICENSE ├── pyproject.toml ├── usage_example.py ├── Makefile ├── .gitignore └── README.md /src/graph_state_generation/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/graph_state_generation/utility/__init__.py: -------------------------------------------------------------------------------- 1 | from .constant import Basis 2 | -------------------------------------------------------------------------------- /data/__init__.py: -------------------------------------------------------------------------------- 1 | from .gen_graphs import gen_erdos_renyi_graph_single_component 2 | -------------------------------------------------------------------------------- /code_visualization/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/0.png -------------------------------------------------------------------------------- /code_visualization/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/1.png -------------------------------------------------------------------------------- /code_visualization/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/2.png -------------------------------------------------------------------------------- /code_visualization/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/3.png -------------------------------------------------------------------------------- /code_visualization/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/4.png -------------------------------------------------------------------------------- /code_visualization/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/5.png -------------------------------------------------------------------------------- /code_visualization/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/6.png -------------------------------------------------------------------------------- /code_visualization/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/7.png -------------------------------------------------------------------------------- /code_visualization/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/graph.png -------------------------------------------------------------------------------- /src/graph_state_generation/substrate_scheduler/__init__.py: -------------------------------------------------------------------------------- 1 | from .substrate_scheduler_two_row import TwoRowSubstrateScheduler 2 | -------------------------------------------------------------------------------- /src/graph_state_generation/visualization_tools/__init__.py: -------------------------------------------------------------------------------- 1 | from .ascii_visualizer import ascii_instruction_visualization 2 | -------------------------------------------------------------------------------- /src/graph_state_generation/utility/constant.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class Basis(Enum): 5 | X = 1 6 | Z = 2 7 | -------------------------------------------------------------------------------- /code_visualization/animated.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/animated.gif -------------------------------------------------------------------------------- /code_visualization/graphtest.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sfc-aqua/gosc-graph-state-generation/HEAD/code_visualization/graphtest.png -------------------------------------------------------------------------------- /src/graph_state_generation/optimizers/__init__.py: -------------------------------------------------------------------------------- 1 | from .node_to_patch_mapper import random_mapper 2 | from .pre_mapping_optimizer import ( 3 | approximate_static_stabilizer_reduction, 4 | fast_maximal_independent_set_stabilizer_reduction, 5 | ) 6 | from .stabilizer_measurement_scheduler import greedy_stabilizer_measurement_scheduler 7 | -------------------------------------------------------------------------------- /src/graph_state_generation/optimizers/node_to_patch_mapper.py: -------------------------------------------------------------------------------- 1 | from typing import List, Set 2 | 3 | import networkx as nx 4 | import numpy as np 5 | 6 | 7 | def random_mapper(g: nx.Graph, _: Set[int]) -> List[int]: 8 | """This mapper randomly permutes the label and ignore the skipped_stabilizer set.""" 9 | return list(np.random.permutation(g.number_of_nodes())) 10 | 11 | 12 | # def min_cut_mapper(g: nx.Graph, skipped_stabilizer: Set[int]) -> List[int]: 13 | # return [x for x in range(g.number_of_nodes())] 14 | -------------------------------------------------------------------------------- /actions/style/action.yml: -------------------------------------------------------------------------------- 1 | runs: 2 | using: "composite" 3 | steps: 4 | 5 | # Load a specific version of Python 6 | - name: Setup python 7 | uses: actions/setup-python@v2 8 | with: 9 | python-version: ${{ matrix.python }} 10 | architecture: x64 11 | 12 | - name: Install deps 13 | shell: bash 14 | run: make github_actions 15 | env: 16 | SSH_AUTH_SOCK: /tmp/ssh_agent.sock 17 | 18 | # Leverages Makefile to check all style linters 19 | - name: Check style 20 | shell: bash 21 | run: make style 22 | -------------------------------------------------------------------------------- /tests/test_stabilizer_measurement_scheduler.py: -------------------------------------------------------------------------------- 1 | from graph_state_generation.optimizers.stabilizer_measurement_scheduler import ( 2 | greedy_stabilizer_measurement_scheduler, 3 | ) 4 | 5 | 6 | def test_greedy_scheduler_0(): 7 | stabilizers_set_0 = [(0, (0, 5)), (1, (1, 2)), (2, (1, 10))] 8 | assert len(greedy_stabilizer_measurement_scheduler(stabilizers_set_0)) == 3 9 | 10 | 11 | def test_greedy_scheduler_1(): 12 | stabilizers_set_1 = [(0, (0, 5)), (1, (1, 2)), (2, (6, 10))] 13 | assert len(greedy_stabilizer_measurement_scheduler(stabilizers_set_1)) == 2 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Include description of feature this PR introduces or a bug that it fixes. Include the following information: 4 | 5 | - Context: why is it needed? In case of bug, what was the cause for the bug? 6 | - Concise description of the implemented solution. 7 | - If any dependencies are added, list them and justify why they are needed. 8 | 9 | ## Please verify that you have completed the following steps 10 | 11 | - [ ] I have self-reviewed my code. 12 | - [ ] I have included test cases validating introduced feature/fix. 13 | - [ ] I have updated documentation. 14 | -------------------------------------------------------------------------------- /actions/test/action.yml: -------------------------------------------------------------------------------- 1 | inputs: 2 | codecov_secret: 3 | description: | 4 | Should be copy of secrets.CODECOV_TOKEN from calling environment. 5 | required: true 6 | 7 | runs: 8 | using: "composite" 9 | steps: 10 | 11 | # Load a specific version of Python 12 | - name: Setup python 13 | uses: actions/setup-python@v2 14 | with: 15 | python-version: ${{ matrix.python }} 16 | architecture: x64 17 | 18 | - name: Install deps 19 | shell: bash 20 | run: make github_actions 21 | env: 22 | SSH_AUTH_SOCK: /tmp/ssh_agent.sock 23 | 24 | - name: Run tests 25 | shell: bash 26 | run: make test 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Is your feature request related to a problem? If so, please describe it. 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | ## How you envision the solution 14 | A clear and concise description of what you want to happen. 15 | 16 | ## Describe alternatives you've considered 17 | A clear and concise description of any alternative solutions or features you have considered. 18 | 19 | ## Additional context 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | A clear and concise description of what the bug is. 12 | 13 | ## How to Reproduce 14 | Steps to reproduce the behavior, as minimal as possible. 15 | 16 | ## Expected behavior 17 | Description of what you expected to happen. 18 | 19 | ## Actual behavior 20 | Description of what actually happens. If needed may contain logs/outputs. 21 | 22 | ## Environment (please complete the following information):** 23 | - OS: [e.g. OSX, Windows] 24 | - Python version [e.g. 3.7.11] 25 | 26 | ## Additional context 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /tests/test_pre_mapping_optimizer.py: -------------------------------------------------------------------------------- 1 | import networkx as nx 2 | 3 | from graph_state_generation.optimizers.pre_mapping_optimizer import ( 4 | fast_maximal_independent_set_stabilizer_reduction, 5 | ) 6 | 7 | 8 | def test_max_independet_set_reduction_0(): 9 | graph_0 = {0: [2, 5], 1: [3], 2: [0, 3], 3: [1, 2, 4], 4: [3], 5: [0]} 10 | G_0 = nx.Graph(graph_0) 11 | assert sorted(fast_maximal_independent_set_stabilizer_reduction(G_0)[0]) == [ 12 | 1, 13 | 2, 14 | 4, 15 | 5, 16 | ] 17 | 18 | 19 | def test_max_independet_set_reduction_1(): 20 | graph_1 = {0: [1, 2, 3, 4, 5], 1: [0], 2: [0], 3: [0], 4: [0], 5: [0]} 21 | G_1 = nx.Graph(graph_1) 22 | assert sorted(fast_maximal_independent_set_stabilizer_reduction(G_1)[0]) == [ 23 | 1, 24 | 2, 25 | 3, 26 | 4, 27 | 5, 28 | ] 29 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[python]": { 3 | "editor.defaultFormatter": "ms-python.python", 4 | "editor.formatOnSave": true, 5 | "editor.codeActionsOnSave": { 6 | "source.organizeImports": true 7 | }, 8 | }, 9 | "isort.args": [ 10 | "--profile", 11 | "black" 12 | ], 13 | "python.formatting.provider": "black", 14 | "python.testing.pytestArgs": [ 15 | "tests" 16 | ], 17 | "python.testing.unittestEnabled": false, 18 | "python.testing.pytestEnabled": true, 19 | "files.exclude": { 20 | "**/__pycache__/": true, 21 | "**/.pytest_cache/": true, 22 | "**/.idea/": true 23 | }, 24 | "python.linting.flake8Enabled": true, 25 | "python.linting.enabled": true, 26 | "python.linting.flake8Args": [ 27 | "--max-line-length=102", 28 | "--ignore=E203,E266,F401,W605", 29 | ], 30 | } -------------------------------------------------------------------------------- /src/graph_state_generation/optimizers/stabilizer_measurement_scheduler.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from typing import List, Tuple 3 | 4 | 5 | def greedy_stabilizer_measurement_scheduler( 6 | stabilizer_to_measure: List[Tuple[int, Tuple[int, int]]] 7 | ) -> List[List[Tuple[int, Tuple[int, int]]]]: 8 | """Optimal stabilizer measurement scheduler given that labelling is fixed. Proof to be written.""" 9 | 10 | # stabilizer_to_measure is [(S_i, (min_i, max_i))] 11 | stabilizer_to_measure = stabilizer_to_measure 12 | stabilizer_to_measure.sort(key=lambda x: x[1][1]) 13 | time_step = deque() # stores list of (S_i, (min_i, max_i)) 14 | time_step.append([stabilizer_to_measure[0]]) 15 | for st in stabilizer_to_measure[1:]: 16 | if st[1][0] > time_step[0][-1][1][1]: 17 | time_step[0].append(st) 18 | time_step.append(time_step.popleft()) 19 | else: 20 | time_step.append([st]) 21 | 22 | return list(time_step) 23 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = graph_state_generation 3 | description = "Graph State Generation" 4 | long_description = file: README.md 5 | long_description_content_type = text/markdown; charset=UTF-8 6 | url = https://github.com/sfc-aqua/gosc-graph-state-generation 7 | author = Aqua 8 | author_email = sitong@sfc.wide.ad.jp, 9 | license = MIT 10 | license_file = LICENSE 11 | classifiers = 12 | Programming Language :: Python :: 3 13 | Operating System :: OS Independent 14 | License :: OSI Approved :: Apache Software License 15 | Topic :: Scientific/Engineering 16 | 17 | 18 | [options] 19 | zip_safe = False 20 | include_package_data = True 21 | package_dir = 22 | = src 23 | packages = find_namespace: 24 | python_requires = ~=3.8 25 | 26 | install_requires = 27 | networkx==2.8.7 28 | matplotlib 29 | numpy>=1.20 30 | 31 | 32 | [options.packages.find] 33 | where = src 34 | 35 | [options.extras_require] 36 | dev = 37 | black~=22.3 38 | flake8~=3.9.0 39 | Flake8-pyproject>=0.9.0 40 | isort~=5.9.0 41 | mypy~=0.910 42 | pytest~=6.2 43 | pytest-cov>=2.12 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 sfc-aqua 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/graph_state_generation/optimizers/pre_mapping_optimizer.py: -------------------------------------------------------------------------------- 1 | from typing import Set, Tuple 2 | 3 | import networkx as nx 4 | import numpy as np 5 | 6 | 7 | def approximate_static_stabilizer_reduction(g: nx.Graph) -> Tuple[Set[int], nx.Graph]: 8 | """Uses maximum independent set algorithm to find state initialization such that 9 | most stabilizer generators are already stabilized so the stabilizer measurements 10 | needed are reduced.""" 11 | max_i_set = nx.algorithms.approximation.maximum_independent_set(g) 12 | return (max_i_set, g) 13 | 14 | 15 | def fast_maximal_independent_set_stabilizer_reduction( 16 | g: nx.Graph, 17 | ) -> Tuple[Set[int], nx.Graph]: 18 | """Repeatly run maximal independent set on a random vertex to find 19 | largest maximal independent set.""" 20 | i_set = nx.maximal_independent_set(g) 21 | max_retry = np.log2(g.number_of_nodes()) 22 | retry = max_retry 23 | while retry > 0: 24 | ni_set = nx.maximal_independent_set(g) 25 | retry -= 1 26 | if len(ni_set) > len(i_set): 27 | retry = max_retry 28 | i_set = ni_set 29 | return (i_set, g) 30 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will upload a Python Package using Twine when a release is created 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries 3 | 4 | # This workflow uses actions that are not certified by GitHub. 5 | # They are provided by a third-party and are governed by 6 | # separate terms of service, privacy policy, and support 7 | # documentation. 8 | 9 | name: Upload Python Package 10 | 11 | on: 12 | release: 13 | types: [published] 14 | 15 | jobs: 16 | deploy: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up Python 22 | uses: actions/setup-python@v3 23 | with: 24 | python-version: '3.x' 25 | - name: Install dependencies 26 | run: | 27 | python -m pip install --upgrade pip 28 | pip install build 29 | - name: Build package 30 | run: python -m build 31 | - name: Publish package 32 | uses: pypa/gh-action-pypi-publish@release/v1 33 | with: 34 | password: ${{ secrets.PYPI_API_SECRET }} 35 | -------------------------------------------------------------------------------- /.github/workflows/style.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs automatic code style checks. 2 | 3 | # We need a workflow name to be able to schedule it from Github UI 4 | name: style 5 | 6 | on: 7 | # Triggers the workflow on push to main 8 | push: 9 | branches: 10 | - main 11 | # Triggers the workflow on any PR 12 | pull_request: 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | jobs: 18 | # The job ID has to match repo settings for PR required checks 19 | style: 20 | runs-on: ubuntu-latest 21 | 22 | # Run jobs for a couple of Python versions. 23 | strategy: 24 | matrix: 25 | python: ["3.9"] 26 | 27 | name: Style - Python ${{ matrix.python }} 28 | 29 | steps: 30 | - uses: actions/checkout@v2 31 | 32 | # ------------------------------------------------------------------------ 33 | # Loads private SSH key to the SSH agent. Allows to install dependencies 34 | # from private git repos, but requires setting `secrets.SSH_PRIVATE_KEY` 35 | # in repo settings. 36 | # ------------------------------------------------------------------------ 37 | - uses: ./actions/style 38 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | # This workflow runs tests and reports code coverage. 2 | 3 | # We need a workflow name to be able to schedule it from Github UI 4 | name: TestCoverage 5 | 6 | on: 7 | # Triggers the workflow on push to main 8 | push: 9 | branches: 10 | - main 11 | # Triggers the workflow on any PR 12 | pull_request: 13 | 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | jobs: 18 | # The job ID has to match repo settings for PR required checks 19 | TestCoverage: 20 | runs-on: ubuntu-latest 21 | 22 | # Run jobs for a couple of Python versions. 23 | strategy: 24 | matrix: 25 | python: ["3.9"] 26 | 27 | name: TestCoverage - Python ${{ matrix.python }} 28 | 29 | steps: 30 | - uses: actions/checkout@v2 31 | 32 | # ------------------------------------------------------------------------ 33 | # Loads private SSH key to the SSH agent. Allows to install dependencies 34 | # from private git repos, but requires setting `secrets.SSH_PRIVATE_KEY` 35 | # in repo settings. 36 | # ------------------------------------------------------------------------ 37 | 38 | - uses: ./actions/test 39 | -------------------------------------------------------------------------------- /src/graph_state_generation/visualization_tools/ascii_visualizer.py: -------------------------------------------------------------------------------- 1 | from typing import List, Tuple 2 | 3 | from ..utility.constant import Basis 4 | 5 | 6 | def ascii_instruction_visualization( 7 | node_to_patch_mapping: List[int], 8 | patch_initialized_state: List[Basis], 9 | measurement_steps: List[List[Tuple[int, Tuple[int, int]]]], 10 | ): 11 | n = len(node_to_patch_mapping) 12 | if n > 1000: 13 | raise NotImplementedError( 14 | f"Your graph has {n} nodees. However, we cannot visulize more " 15 | "than 1000 nodes at the moment." 16 | ) 17 | inverse_map = [0] * n 18 | for i in node_to_patch_mapping: 19 | inverse_map[node_to_patch_mapping[i]] = i 20 | 21 | inverse_map_str = [f"{i:03}" for i in inverse_map] 22 | 23 | # printing 24 | label_line = "| " + " | ".join(inverse_map_str) + " |" 25 | patch_state_line = "| " + " | ".join([f" {i.name} " for i in patch_initialized_state]) + " |" 26 | label_border = "|_" + "_|_".join(["___" for _ in inverse_map_str]) + "_|" 27 | 28 | print(label_line) 29 | print(patch_state_line) 30 | print(label_border) 31 | for step in measurement_steps: 32 | curend = 0 33 | line = "" 34 | for (_, (left, right)) in step: 35 | if left > curend: 36 | line = line + (" ") * (left - curend) 37 | line += " |----" 38 | line += "------" * (right - left - 1) 39 | line += "-----|" 40 | curend = right + 1 41 | print(line) 42 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = ["setuptools", "wheel", "setuptools_scm>=6.0"] 3 | 4 | # Including this section is same as 'use_scm_version=True' in setup.py 5 | # See https://github.com/pypa/setuptools_scm for guidance 6 | [tool.setuptools_scm] 7 | 8 | [tool.isort] 9 | profile = "black" 10 | 11 | [tool.mypy] 12 | # Without this we would have to add an empty __init__.py file for every package. 13 | namespace_packages = true 14 | # Without this mypy complains about the same module being imported under different 15 | # names. Note that this also requires setting `mypy_path`. 16 | explicit_package_bases = true 17 | # We usually keep our source code under `src///...`. 18 | # This option tells mypy to look under that directory path. 19 | # If your repo has a different layout you can add the appropriate paths 20 | # by setting MYPYPATH env variable. 21 | mypy_path = "src" 22 | 23 | [[tool.mypy.overrides]] 24 | module = [ 25 | 'scipy.*', 26 | 'networkx.*', 27 | 'matplotlib.*', 28 | 'sympy.*', 29 | 'rapidjson', 30 | ] 31 | ignore_missing_imports = true 32 | 33 | [tool.coverage.run] 34 | omit = ["*/__init__.py"] 35 | 36 | [tool.coverage.report] 37 | exclude_lines = [ 38 | "pragma: no cover", 39 | "def __repr__", 40 | "def __str__", 41 | "raise AssertionError", 42 | "raise NotImplementedError", 43 | "if __name__ == .__main__.:", 44 | ] 45 | 46 | [tool.pytest.ini_options] 47 | addopts = [ 48 | "--import-mode=importlib", 49 | ] 50 | pythonpath = [ 51 | ".", "src", 52 | ] 53 | log_level="INFO" 54 | 55 | [tool.flake8] 56 | ignore = ['E203', 'E266', 'F401', 'W605'] 57 | max-line-length = 102 58 | 59 | [tool.black] 60 | line-length = 102 61 | -------------------------------------------------------------------------------- /usage_example.py: -------------------------------------------------------------------------------- 1 | from timeit import default_timer as timer 2 | 3 | import networkx as nx 4 | 5 | from src.graph_state_generation.optimizers import ( 6 | approximate_static_stabilizer_reduction, 7 | fast_maximal_independent_set_stabilizer_reduction, 8 | greedy_stabilizer_measurement_scheduler, 9 | random_mapper, 10 | ) 11 | from src.graph_state_generation.substrate_scheduler import TwoRowSubstrateScheduler 12 | 13 | 14 | def gen_erdos_renyi_graph_single_component(n, m): 15 | G = nx.gnm_random_graph(n, m) 16 | if nx.number_connected_components(G) == 1: 17 | return G 18 | else: 19 | return gen_erdos_renyi_graph_single_component(n, m) 20 | 21 | 22 | # g = gen_erdos_renyi_graph_single_component(500, 2500) 23 | # g = gen_erdos_renyi_graph_single_component(10, 9) 24 | g = nx.cycle_graph(10) 25 | nothing_compiler = TwoRowSubstrateScheduler(g) 26 | 27 | st = timer() 28 | nx.algorithms.approximation.maximum_independent_set(g) 29 | ed = timer() 30 | print(f"time taken - {ed - st}s") 31 | 32 | nothing_compiler.run() 33 | nothing_compiler.get_summary() 34 | nothing_compiler.visualization() 35 | print("========================================") 36 | 37 | scheduler_only_compiler = TwoRowSubstrateScheduler( 38 | g, stabilizer_scheduler=greedy_stabilizer_measurement_scheduler 39 | ) 40 | scheduler_only_compiler.run() 41 | scheduler_only_compiler.get_summary() 42 | scheduler_only_compiler.visualization() 43 | print("========================================") 44 | 45 | for _ in range(2): 46 | better_compiler = TwoRowSubstrateScheduler( 47 | g, 48 | pre_mapping_optimizer=approximate_static_stabilizer_reduction, 49 | node_to_patch_mapper=random_mapper, 50 | stabilizer_scheduler=greedy_stabilizer_measurement_scheduler, 51 | ) 52 | better_compiler.run() 53 | better_compiler.get_summary() 54 | 55 | print("========================================") 56 | for _ in range(2): 57 | faster_compiler = TwoRowSubstrateScheduler( 58 | g, 59 | pre_mapping_optimizer=fast_maximal_independent_set_stabilizer_reduction, 60 | node_to_patch_mapper=random_mapper, 61 | stabilizer_scheduler=greedy_stabilizer_measurement_scheduler, 62 | ) 63 | faster_compiler.run() 64 | faster_compiler.get_summary() 65 | faster_compiler.visualization() 66 | -------------------------------------------------------------------------------- /data/gen_graphs.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import os 4 | 5 | import math 6 | 7 | import networkx as nx 8 | 9 | 10 | def gen_erdos_renyi_graph_single_component(n, m): 11 | if_connected = 0 12 | for i in range(1000): 13 | G = nx.gnm_random_graph(n, m, seed=51) 14 | if_connected = nx.number_connected_components(G) 15 | if if_connected == 1: 16 | break 17 | if if_connected == 1: 18 | return nx.to_dict_of_lists(G) 19 | else: 20 | return None 21 | 22 | 23 | for i in range(5): 24 | for n_nodes_ran in range(10, 1000, 50): 25 | # minimum number of edges for undirected connected graph is (n-1) edges 26 | # maximal number of edges for undirected connected graph is n(n-1)/2 edges 27 | edge_numbers = n_nodes_ran * (n_nodes_ran - 1) / 2 - n_nodes_ran 28 | 29 | random.seed(10) 30 | 31 | n_edges_very_sparse = random.randint( 32 | n_nodes_ran, math.floor(n_nodes_ran + edge_numbers / 4) 33 | ) 34 | 35 | n_edges_sparse = random.randint( 36 | math.floor(n_nodes_ran + edge_numbers / 4), 37 | math.floor(n_nodes_ran + edge_numbers / 2), 38 | ) 39 | n_edges_dense = random.randint( 40 | math.floor(n_nodes_ran + edge_numbers / 2), 41 | math.floor(n_nodes_ran + edge_numbers * 3 / 4), 42 | ) 43 | n_edges_very_dense = random.randint( 44 | math.floor(n_nodes_ran + edge_numbers * 3 / 4), 45 | math.floor(n_nodes_ran + edge_numbers), 46 | ) 47 | 48 | very_sparse = gen_erdos_renyi_graph_single_component( 49 | n_nodes_ran, n_edges_very_sparse 50 | ) 51 | sparse = gen_erdos_renyi_graph_single_component(n_nodes_ran, n_edges_sparse) 52 | dense = gen_erdos_renyi_graph_single_component(n_nodes_ran, n_edges_dense) 53 | very_dense = gen_erdos_renyi_graph_single_component( 54 | n_nodes_ran, n_edges_very_dense 55 | ) 56 | if not os.path.exists("graph"): 57 | os.makedirs("graph") 58 | with open(f"graph/very_sparse_{i}.json", "w") as json_file: 59 | json.dump(very_sparse, json_file) 60 | with open(f"graph/dense_{i}.json", "w") as json_file: 61 | json.dump(sparse, json_file) 62 | with open(f"graph/dense_{i}.json", "w") as json_file: 63 | json.dump(dense, json_file) 64 | with open(f"graph/very_dense_{i}.json", "w") as json_file: 65 | json.dump(very_dense, json_file) 66 | -------------------------------------------------------------------------------- /src/graph_state_generation/old_code/visualize_result.py: -------------------------------------------------------------------------------- 1 | import math 2 | import numbers 3 | import random 4 | 5 | import matplotlib.pyplot as plt 6 | from matplotlib.patches import Rectangle 7 | from tests.graph_state_generation.create_random_graph import ( 8 | gen_erdos_renyi_graph_single_component, 9 | ) 10 | 11 | from old_version import two_tile_patch_ver as twotile 12 | 13 | n_nodes_ran = random.randint(3, 5) 14 | 15 | row=3 16 | col=2*n_nodes_ran 17 | # generate sparse graph 18 | n_edges_ran = random.randint(n_nodes_ran, math.floor((n_nodes_ran * (n_nodes_ran - 1))/ 2 / 2)) 19 | 20 | adj_list_gen = gen_erdos_renyi_graph_single_component(n_nodes_ran, n_edges_ran) #generate random graph for testing 21 | 22 | if adj_list_gen != None: 23 | # run compilation 24 | num_step_scheduled, num_stablizers, adj_list_gen, stablizer_on_qubits, board_step = twotile.main(adj_list_gen, n_nodes_ran) 25 | print(num_step_scheduled) 26 | print(num_stablizers) 27 | 28 | def saveImage(p,step): 29 | filename = '../code_visualization/' + str(step) + '.png' 30 | p.savefig(filename) 31 | 32 | for step in range(0,num_step_scheduled): 33 | plt.figure(figsize=(10, 5)) 34 | 35 | ax = plt.gca() 36 | ax.set_xlim([0, col]) 37 | ax.set_ylim([0, row]) 38 | 39 | step_of_board = board_step[step] 40 | operation_step = stablizer_on_qubits[step] 41 | for i in range(0, row): 42 | row_board=step_of_board[i] 43 | for j in range(0, col): 44 | if isinstance(row_board[j],numbers.Number): 45 | # for qubits 46 | qubit_id=row_board[j] 47 | if qubit_id in operation_step.keys(): 48 | operation=operation_step[qubit_id] 49 | if operation==['Z']: 50 | rec = Rectangle((j, row - i - 1), width=1, height=1, facecolor='r', edgecolor="gray") 51 | if operation==['X']: 52 | rec = Rectangle((j, row - i - 1), width=1, height=1, facecolor='y', edgecolor="gray") 53 | plt.text(j + 0.5, row - i - 1 + 0.5, (row_board[j],operation),fontweight='bold', fontsize=7.5, verticalalignment="center", 54 | horizontalalignment="center") 55 | else: 56 | rec = Rectangle((j, row - i - 1), width=1, height=1, facecolor='b', edgecolor="gray") 57 | plt.text(j + 0.5, row - i-1 + 0.5, row_board[j], fontsize=7.5, verticalalignment="center", horizontalalignment="center") 58 | ax.add_patch(rec) 59 | 60 | if row_board[j] == None: 61 | # for unassigned tiles 62 | rec = Rectangle((j, row - i-1), width=1, height=1, facecolor='w', edgecolor="gray") 63 | #print(rec) 64 | ax.add_patch(rec) 65 | if row_board[j] == 'anc': 66 | # for ancilla patch 67 | rec = Rectangle((j, row -i-1), width=1, height=1, facecolor='gray', edgecolor="gray") 68 | #print(rec) 69 | ax.add_patch(rec) 70 | 71 | plt.title("step %s / %s" % (step, num_step_scheduled), fontsize='15', fontweight='bold', y=-0.03) 72 | 73 | plt.axis('equal') 74 | plt.axis('off') 75 | plt.tight_layout() 76 | saveImage(plt,step) 77 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # © Copyright 2021-2022 Zapata Computing Inc. 3 | ################################################################################ 4 | TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))) 5 | 6 | # This target will list the possible valid targets available. 7 | default: 8 | @echo -------------------------------------------- 9 | @echo '=> No target chosen: Choose from the following:' 10 | @echo 11 | @grep -E '^\w+(\-default)?:' $(TOP_DIR)/$(firstword $(MAKEFILE_LIST)) \ 12 | | sed -r 's/-default//g; /default/d ; s/(.*)/\t make \1/g ; s/:.*$$//g' 13 | 14 | 15 | export VENV_NAME := my_little_venv 16 | PYTHON := $(shell PATH="${VENV_NAME}/bin:${PATH}" python3 -c 'import sys; print(sys.executable)') 17 | REPO := $(shell git config --get remote.origin.url) 18 | PYTHON_MOD := $(shell find src -maxdepth 3 -mindepth 3 -type d | sed '/.*cache/d; s/src\/python\/// ; s/\//./') 19 | PACKAGE_NAME := "foo" 20 | 21 | ifeq ($(PYTHON),) 22 | $(error "PYTHON=$(PYTHON)") 23 | else 24 | $(info -------------------------------------------------------------------------------) 25 | $(info You are using PYTHON: $(PYTHON)) 26 | $(info Python Version: $(shell $(PYTHON) --version)) 27 | $(info Repository: $(REPO)) 28 | $(info Python Modules Covered: $(PYTHON_MOD)) 29 | $(info -------------------------------------------------------------------------------) 30 | endif 31 | 32 | # Clean out all Pythonic cruft 33 | clean-default: 34 | @find . -regex '^.*\(__pycache__\|\.py[co]\)$$' -delete; 35 | @find . -type d -name __pycache__ -exec rm -r {} \+ 36 | @find . -type d -name '*.egg-info' -exec rm -rf {} + 37 | @find . -type d -name .mypy_cache -exec rm -r {} \+ 38 | @rm -rf .pytest_cache; 39 | @rm -rf tests/.pytest_cache; 40 | @rm -rf dist build 41 | @rm -f .coverage* 42 | @echo Finished cleaning out pythonic cruft... 43 | 44 | install-default: clean 45 | $(PYTHON) -m pip install --upgrade pip && \ 46 | $(PYTHON) -m pip install . 47 | 48 | dev-default: clean 49 | $(PYTHON) -m pip install -e .[dev] 50 | 51 | # Why we want to use `python3` and not `$(PYTHON)` here. 52 | # In order to enable running make commands both from CICD and locally 53 | # we need to use virtualenv when running on GitHub Actions, as otherwise 54 | # we might get into rare and hard to debug edge cases (as we did in the past). 55 | # After this action is executed $(PYTHON) will get resolved to the Python version 56 | # from virtual environment. 57 | # This make task is used to create new virtual environment so we want to use 58 | # `python3` here as it's more explicit, because $(PYTHON) would evaluate to 59 | # something else after executing this task, which might be confusing. 60 | github_actions-default: 61 | python3 -m venv ${VENV_NAME} && \ 62 | ${VENV_NAME}/bin/python3 -m pip install --upgrade pip && \ 63 | ${VENV_NAME}/bin/python3 -m pip install -e '.[dev]' 64 | 65 | # flake8p is a wrapper library that runs flake8 from config in pyproject.toml 66 | # we can now use it instead of flake8 to lint the code 67 | flake8p-default: clean 68 | $(PYTHON) -m flake8p --ignore=E203,E266,F401,W503 --max-line-length=102 src tests 69 | 70 | mypy-default: clean 71 | @echo scanning files with mypy: Please be patient.... 72 | $(PYTHON) -m mypy src tests 73 | 74 | black-default: clean 75 | $(PYTHON) -m black --check src tests 76 | 77 | isort-default: clean 78 | $(PYTHON) -m isort --check src tests 79 | 80 | test-default: 81 | $(PYTHON) -m pytest tests 82 | 83 | style-default: flake8p mypy black isort 84 | @echo This project passes style! 85 | 86 | muster-default: style coverage 87 | @echo This project passes muster! 88 | 89 | build-system-deps-default: 90 | : 91 | 92 | get-next-version-default: github_actions 93 | ${VENV_NAME}/bin/python3 subtrees/z_quantum_actions/bin/get_next_version.py $(PACKAGE_NAME) 94 | 95 | # This is what converts the -default targets into base target names. 96 | # Do not remove!!! 97 | %: %-default 98 | @true 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Python Ignore 2 | 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | cover/ 55 | 56 | # Translations 57 | *.mo 58 | *.pot 59 | 60 | # Django stuff: 61 | *.log 62 | local_settings.py 63 | db.sqlite3 64 | db.sqlite3-journal 65 | 66 | # Flask stuff: 67 | instance/ 68 | .webassets-cache 69 | 70 | # Scrapy stuff: 71 | .scrapy 72 | 73 | # Sphinx documentation 74 | docs/_build/ 75 | 76 | # PyBuilder 77 | .pybuilder/ 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | # For a library or package, you might want to ignore these files since the code is 89 | # intended to run in multiple environments; otherwise, check them in: 90 | # .python-version 91 | 92 | # pipenv 93 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 94 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 95 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 96 | # install all needed dependencies. 97 | #Pipfile.lock 98 | 99 | # poetry 100 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 101 | # This is especially recommended for binary packages to ensure reproducibility, and is more 102 | # commonly ignored for libraries. 103 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 104 | #poetry.lock 105 | 106 | # pdm 107 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 108 | #pdm.lock 109 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 110 | # in version control. 111 | # https://pdm.fming.dev/#use-with-ide 112 | .pdm.toml 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | .idea/ 163 | 164 | 165 | ## Jet Brain Ignore 166 | 167 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 168 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 169 | 170 | # User-specific stuff 171 | .idea/**/workspace.xml 172 | .idea/**/tasks.xml 173 | .idea/**/usage.statistics.xml 174 | .idea/**/dictionaries 175 | .idea/**/shelf 176 | 177 | # AWS User-specific 178 | .idea/**/aws.xml 179 | 180 | # Generated files 181 | .idea/**/contentModel.xml 182 | 183 | # Sensitive or high-churn files 184 | .idea/**/dataSources/ 185 | .idea/**/dataSources.ids 186 | .idea/**/dataSources.local.xml 187 | .idea/**/sqlDataSources.xml 188 | .idea/**/dynamic.xml 189 | .idea/**/uiDesigner.xml 190 | .idea/**/dbnavigator.xml 191 | 192 | # Gradle 193 | .idea/**/gradle.xml 194 | .idea/**/libraries 195 | 196 | # Gradle and Maven with auto-import 197 | # When using Gradle or Maven with auto-import, you should exclude module files, 198 | # since they will be recreated, and may cause churn. Uncomment if using 199 | # auto-import. 200 | # .idea/artifacts 201 | # .idea/compiler.xml 202 | # .idea/jarRepositories.xml 203 | # .idea/modules.xml 204 | # .idea/*.iml 205 | # .idea/modules 206 | # *.iml 207 | # *.ipr 208 | 209 | # CMake 210 | cmake-build-*/ 211 | 212 | # Mongo Explorer plugin 213 | .idea/**/mongoSettings.xml 214 | 215 | # File-based project format 216 | *.iws 217 | 218 | # IntelliJ 219 | out/ 220 | 221 | # mpeltonen/sbt-idea plugin 222 | .idea_modules/ 223 | 224 | # JIRA plugin 225 | atlassian-ide-plugin.xml 226 | 227 | # Cursive Clojure plugin 228 | .idea/replstate.xml 229 | 230 | # SonarLint plugin 231 | .idea/sonarlint/ 232 | 233 | # Crashlytics plugin (for Android Studio and IntelliJ) 234 | com_crashlytics_export_strings.xml 235 | crashlytics.properties 236 | crashlytics-build.properties 237 | fabric.properties 238 | 239 | # Editor-based Rest Client 240 | .idea/httpRequests 241 | 242 | # Android studio 3.1+ serialized cache file 243 | .idea/caches/build_file_checksums.ser 244 | 245 | # maxOS 246 | .DS_Store -------------------------------------------------------------------------------- /src/graph_state_generation/substrate_scheduler/substrate_scheduler_two_row.py: -------------------------------------------------------------------------------- 1 | #! /bin/usr/env python3 2 | from timeit import default_timer as timer 3 | from typing import List, Set, Tuple 4 | 5 | import networkx as nx 6 | 7 | from ..utility import Basis 8 | from ..visualization_tools import ascii_instruction_visualization 9 | 10 | 11 | def default_pre_mapping_optimizer(g: nx.Graph) -> Tuple[Set[int], nx.Graph]: 12 | return (set(), g) 13 | 14 | 15 | def default_node_to_patch_mapper(g: nx.Graph, _: Set[int]) -> List[int]: 16 | return [x for x in range(g.number_of_nodes())] 17 | 18 | 19 | def default_stabilizer_scheduler( 20 | stabilizer_to_measure: List[Tuple[int, Tuple[int, int]]] 21 | ) -> List[List[Tuple[int, Tuple[int, int]]]]: 22 | 23 | return list(map(lambda x: [(x[0], x[1])], stabilizer_to_measure)) 24 | 25 | 26 | class TwoRowSubstrateScheduler: 27 | """The very basic substrate scheduler where all qubits are represented by 2-tile patches 28 | align in a single row and another row is for ancilla to perform multi-Pauli product 29 | measurement for stabilizer generator projection. 30 | 31 | It works in three phases by accepting 3 functions 32 | 1. pre-mapping optimization 33 | 2. node-to-patch mapping 34 | 3. stabilizer measurement scheduling 35 | 36 | If the optimizer functions are not provided, no optimization is done and only one stabilizer 37 | measurement is performed at each time step resulting in O(V) time step 38 | """ 39 | 40 | def __init__( 41 | self, 42 | input_graph: nx.Graph, 43 | # nx.Graph -> Tuple(Set(stabilizer_to_skip), nx.Graph) 44 | pre_mapping_optimizer=default_pre_mapping_optimizer, 45 | # nx.Graph -> List(int) of length V 46 | node_to_patch_mapper=default_node_to_patch_mapper, 47 | # [(S_i, (min_i, max_i))] -> [[(S_i, (min_i, max_i))], [(S_j, (min_j, max_j))]] with length 48 | # equals to number of timestep where S_i, min_i, and max_i are int 49 | stabilizer_scheduler=default_stabilizer_scheduler, 50 | verbose=False, 51 | ): 52 | """pre_mapping_optimizer -""" 53 | 54 | 55 | if input_graph == nx.Graph(): 56 | raise ValueError("Graph is empty. Cannot schedule empty graph.") 57 | 58 | self.input_graph: nx.Graph = input_graph 59 | self.label: List[int] = [] 60 | self.adj_list: List[List[int]] = [] 61 | self.stabilizer: List[ 62 | Tuple[int, List[int]] 63 | ] = [] # Tuple[Pauli_X, List[Pauli_Z]] where it corresponds to the patch label 64 | 65 | self.stabilizer_to_measure: List[ 66 | Tuple[int, Tuple[int, int]] 67 | ] = [] # (G_i, (ancilla_left, ancilla_right)) where G_i denote stabilizer of patch i 68 | self.pre_mapping_optimizer = pre_mapping_optimizer 69 | self.node_to_patch_mapper = node_to_patch_mapper 70 | self.stabilizer_scheduler = stabilizer_scheduler 71 | self.verbose = verbose 72 | 73 | # parse the input graph and set class attributes 74 | self.adj_list = [vs for _, vs in nx.to_dict_of_lists(input_graph).items()] 75 | self.stabilizer_to_measure = [] 76 | 77 | # for reporting instructions and time-space volume analysis 78 | self.patch_state_init: List[Basis] = [Basis.Z] * input_graph.number_of_nodes() 79 | self.measurement_steps: List[List[Tuple[int, Tuple[int, int]]]] = [] 80 | 81 | def visualization(self, fmt="ascii", save=False): 82 | if fmt == "ascii": 83 | visualizer = ascii_instruction_visualization 84 | else: 85 | raise ValueError("unknown format") 86 | 87 | visualizer(self.label, self.patch_state_init, self.measurement_steps) 88 | 89 | def stabilizer_table(self): 90 | """print stabilizer of the input graph state in ascii format 91 | (I is replaced with _ for better readability). 92 | Only support up to 100 vertices.""" 93 | n = self.input_graph.number_of_nodes() 94 | stabilizers_str: List[str] = [""] * n 95 | 96 | def _adj_to_stabilizer(i: int, vs: List[int]) -> str: 97 | barr = bytearray(b"_") * n 98 | barr[i] = 65 + 23 # 'X' 99 | for v in vs: 100 | barr[v] = 65 + 25 # 'Z' 101 | return barr.decode() 102 | 103 | for i in range(n): 104 | stabilizers_str[i] = _adj_to_stabilizer(i, self.adj_list[i]) 105 | 106 | print("\n".join(stabilizers_str)) 107 | 108 | def run(self): 109 | """Execute the three phases on creating the input graph state into 110 | Litinski's GoSC ruleset instructions. 111 | This function will also output the running time of each phase.""" 112 | 113 | # graph optimization 114 | pre_opt_start_time = timer() 115 | stabilizer_to_skip, transformmed_graph = self.pre_mapping_optimizer(self.input_graph) 116 | pre_opt_end_time = timer() 117 | if self.verbose: 118 | print(f"pre-mapping optimization took - {pre_opt_end_time - pre_opt_start_time}s") 119 | 120 | # node to patch mapping / labelling 121 | labelling_start_time = timer() 122 | self.label = self.node_to_patch_mapper(transformmed_graph, set()) 123 | labelling_end_time = timer() 124 | if self.verbose: 125 | print(f"node to patch mapping took - {labelling_end_time - labelling_start_time}s") 126 | 127 | scheduling_start_time = timer() 128 | # from here on we work on patch labelling and not labelling of the input graph 129 | for i, vs in enumerate(self.adj_list): 130 | mi = self.label[i] 131 | mvs = [self.label[v] for v in vs] 132 | 133 | if i in stabilizer_to_skip: 134 | self.patch_state_init[mi] = Basis.X 135 | continue 136 | 137 | self.stabilizer_to_measure.append((mi, (min(mi, min(mvs)), (max(mi, max(mvs)))))) 138 | self.measurement_steps = self.stabilizer_scheduler(self.stabilizer_to_measure) 139 | scheduling_end_time = timer() 140 | if self.verbose: 141 | print(f"measurement scheduler took - {scheduling_end_time - scheduling_start_time}s") 142 | 143 | def get_summary(self): 144 | print(f"reduce from {self.input_graph.number_of_nodes()} to {len(self.measurement_steps)}") 145 | 146 | def get_instructions(self): 147 | # TODO: add labeling phase 148 | return self.measurement_steps 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Substrate Scheduler 2 | 3 | ## About The Project 4 | 5 | `Substrate Scheduler` is a tool with core functionalities to efficiently create the input graph state fault-tolerantly using the rules given by Litinski’s "A Game of Surface Codes: Large-Scale Quantum Computing with Lattice Surgery" (GOSC) paper with the goal of minimizing the space-time volume cost. The space-time volume cost is defined to be the space multiplied by the time where space is defined by the area of patches in units of square tiles and time in units of time step as defined in the GoSC paper or one round of syndrome measurement. 6 | 7 | Substrate Scheduler is a product of the [AQUA](https://aqua.sfc.wide.ad.jp/), [QTS](https://quantumts.org) and [Zapata](https://www.zapatacomputing.com) for the [Bench-Q](https://quantumts.org/bench-q/) project. 8 | 9 | 14 | 15 | ## Features 16 | 17 | `Substrate Scheduler` creates graph states using stabilizer formalism. In the current version, it provides 3 optimzers to reduce the time steps required to create a graph state via stabilizer formalism (note that the time step here is the total number of steps required to create the graph state considering the parallel measurement of the stabilizers) : 18 | 19 | - pre_mapping_optimizer 20 | 21 | From the definition of graph state, we know that we can reduce the number of stabilizer generator to be measured by initializing the qubits in a way that they are already stabilized by some stabilizer generators. For example, initialize node a in X-basis (|+> state) while initializing neighbor nodes of a in Z basis (|0> state). 22 | By converting the problem of finding the optimal reduction to maximum independent set problem, `pre_mapping_optimizer` reduces the maximum number of stabilizer generators before mapping. 23 | 24 | - node_to_patch_mapper 25 | 26 | tbd 27 | 28 | - stabilizer measurement scheduler 29 | 30 | Suppose we have already decided where each node in the graph is mapped to which patch, in order to measure a stabilizer generator in this configuration, we need an ancilla patch that covers all the nodes defined in the stabilizer generator. This means that any stabilizer generator which includes a node between the left-most and the right-most nodes of the stabilizer we are trying to measure cannot be measured at the same time. 31 | 32 | By knowing the start and end positions of each stabilizer generator, `stabilizer_measurement_scheduler` will optimally schedule the stabilizer generators to be measured and reduce the time cost by parallelly measuring as many stabilizers as possible. 33 | 34 | ``` 35 | | 006 | 005 | 003 | 000 | 002 | 008 | 001 | 009 | 004 | 007 | 36 | | X | Z | Z | X | X | X | Z | Z | X | Z | 37 | |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____| 38 | ancilla start |---------------------| ancilla end 39 | One example of the layout after mapping 40 | ``` 41 | 42 | ## Installation 43 | 44 | 46 | 47 | To install `Substrate Scheduler`, you just need to run `git clone https://github.com/sfc-aqua/gosc-graph-state-generation.git` 48 | `Substrate Scheduler`is written is Python, except for a Python3 compiler, you will also need the networkx, matplotlib and numpy packages. 49 | 50 | ## Usage 51 | 66 | For an example of how to use methods from `Substrate Scheduler`, please run `usage_example.py` with the following command. 67 | ``` 68 | python usage_example.py 69 | ``` 70 | The program optimized some graph examples with Substrate Scheduler and returns the following results: 71 | 72 | ``` 73 | pre-mapping optimization took - 0.00037109400000001624s 74 | node to patch mapping took - 3.106400000008058e-05s 75 | measurement scheduler took - 2.7704999999933477e-05s 76 | reduce from 10 to 5 77 | | 006 | 005 | 003 | 000 | 002 | 008 | 001 | 009 | 004 | 007 | 78 | | X | Z | Z | X | X | X | Z | Z | X | Z | 79 | |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____| 80 | |---------------------| 81 | |---------------------------| 82 | |---------------------------------------| 83 | |---------------------------------------------------| 84 | |---------------------------------------------------------| 85 | ``` 86 | It contains: 87 | - Time taken for each optimization process (here is the run time); 88 | - Time step reduced by the optimization; 89 | - An ASCII visualization of the layout (results of the mapping), as well as the starting and ending positions of the stabilizer generator for each step. 90 | 91 | ## Development and Contribution 92 | 93 | 94 | 95 | We use [Google-style](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html) docstring format. If you'd like to specify types please use [PEP 484](https://www.python.org/dev/peps/pep-0484/) type hints instead adding them to docstrings. 96 | 97 | There are codestyle-related [Github Actions](.github/workflows/style.yml) running for PRs. 98 | 99 | - If you'd like to report a bug/issue please create a new issue in this repository. 100 | - If you'd like to contribute, please create a pull request to `main`. 101 | 102 | ### Running tests 103 | 104 | Unit tests for this project can be run using `make coverage` command from the main directory. 105 | Alternatively you can also run `pytest .`. 106 | 107 | ### Style 108 | 109 | We are using automatic tools for style and type checking. In order to make sure the code is compliant with them please run: `make style` from the main directory (this requires `dev` dependencies). 110 | 111 | 117 | 118 | ## In-progress work: 119 | 120 | - [ ] The performance of node_to_patch_mapper needs further testing and improvement. 121 | - [ ] Tests are still being polished. 122 | 123 | See the [open issues](https://github.com/sfc-aqua/gosc-graph-state-generation/issues) for a full list of proposed features (and known issues). 124 | 125 | ## License 126 | Distributed under the MIT License. See `LICENSE` for more information. 127 | --------------------------------------------------------------------------------