qec 0.3.0__py3-none-any.whl → 0.3.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,142 @@
1
+ import json
2
+ from typing import Union
3
+ import inspect
4
+ from pathlib import Path
5
+ from qec.utils.sparse_binary_utils import dict_to_binary_csr_matrix
6
+ import qec.code_constructions
7
+
8
+
9
+ def load_code(filepath: Union[str, Path]):
10
+ """
11
+ Loads a quantum error correction code from a JSON file.
12
+
13
+ Parameters
14
+ ----------
15
+ filepath : Union[str, Path]
16
+ Path to JSON file containing code data
17
+
18
+ Returns
19
+ -------
20
+ object
21
+ An instance of the quantum error correction code class.
22
+
23
+ Raises
24
+ ------
25
+ FileNotFoundError
26
+ If the specified file does not exist.
27
+ ValueError
28
+ If the input file is missing the 'class_name' key.
29
+ AttributeError
30
+ If the specified class does not exist in qec.code_constructions.
31
+ """
32
+
33
+ file_path = Path(filepath)
34
+ if not file_path.exists():
35
+ raise FileNotFoundError(
36
+ f"Error: No file found at the specified path: {file_path}"
37
+ )
38
+
39
+ with open(file_path, "r") as file:
40
+ code_data = json.load(file)
41
+
42
+ if "class_name" not in code_data:
43
+ raise ValueError(
44
+ "Error: The input JSON file must contain a 'class_name' key specifying the class to instantiate."
45
+ )
46
+
47
+ try:
48
+ class_reference = eval(
49
+ f"qec.code_constructions.{code_data['class_name']}", {"qec": qec}
50
+ )
51
+ except AttributeError:
52
+ raise AttributeError(
53
+ f"Error: The specified class '{code_data['class_name']}' does not exist in qec.code_constructions."
54
+ )
55
+
56
+ constructor_parameters = inspect.signature(
57
+ class_reference.__init__
58
+ ).parameters.keys()
59
+ filtered_input_parameters = {}
60
+
61
+ for key, value in code_data.items():
62
+ if key in constructor_parameters:
63
+ if isinstance(value, dict) and all(
64
+ k in value for k in ["indices", "indptr", "shape"]
65
+ ):
66
+ filtered_input_parameters[key] = dict_to_binary_csr_matrix(
67
+ value
68
+ ) # Convert sparse matrix
69
+ else:
70
+ filtered_input_parameters[key] = value # Keep as-is if not a matrix
71
+
72
+ # Instantiate the class
73
+ code_instance = class_reference(**filtered_input_parameters)
74
+
75
+ # Add extra attributes from JSON that are valid class attributes but not constructor parameters
76
+ class_attributes = dir(code_instance)
77
+ for key, value in code_data.items():
78
+ if key not in constructor_parameters and key in class_attributes:
79
+ if isinstance(value, dict) and all(
80
+ k in value for k in ["indices", "indptr", "shape"]
81
+ ):
82
+ value = dict_to_binary_csr_matrix(value) # Convert sparse matrix
83
+ else:
84
+ pass
85
+
86
+ if (not isinstance(value, str) or value != "?") and value is not None:
87
+ setattr(code_instance, key, value)
88
+
89
+ return code_instance
90
+
91
+
92
+ def load_code_from_id(code_id: int):
93
+ """
94
+ Load a quantum error correction code from a JSON file based on its ID from the package data.
95
+
96
+ The code files are packaged as data in the directory:
97
+ qec/code_instances/saved_codes
98
+ and are named as f"{code_id}.json".
99
+
100
+ Parameters
101
+ ----------
102
+ code_id : int
103
+ The identifier of the saved code.
104
+
105
+ Returns
106
+ -------
107
+ object
108
+ An instance of the quantum error correction code class loaded from the JSON data.
109
+
110
+ Raises
111
+ ------
112
+ FileNotFoundError
113
+ If the JSON file corresponding to code_id is not found.
114
+ """
115
+ from importlib.resources import files, as_file
116
+ from importlib import import_module
117
+ from pathlib import Path
118
+
119
+ filename = f"{code_id}.json"
120
+ package = "qec.code_instances.saved_codes"
121
+ try:
122
+ pkg = import_module(package)
123
+ # If the package spec lacks an origin, fall back.
124
+ if pkg.__spec__ is None or pkg.__spec__.origin is None:
125
+ raise ImportError("Package spec origin not found; fallback to filesystem.")
126
+ resource = files(pkg).joinpath(filename)
127
+ except Exception:
128
+ # Fallback: construct the path relative to this file.
129
+ resource = (
130
+ Path(__file__).parent.parent / "code_instances" / "saved_codes" / filename
131
+ )
132
+
133
+ if not resource.is_file():
134
+ raise FileNotFoundError(
135
+ f"File '{filename}' does not exist in package data at '{package}' "
136
+ f"or via fallback path '{resource}'."
137
+ )
138
+
139
+ with as_file(resource) as resource_path:
140
+ if not resource_path.exists():
141
+ raise FileNotFoundError(f"File '{resource_path}' does not exist on disk.")
142
+ return load_code(resource_path)
@@ -0,0 +1,80 @@
1
+ import numpy.typing
2
+ import numpy as np
3
+ import scipy.sparse
4
+
5
+
6
+ def convert_to_binary_scipy_sparse(
7
+ matrix: numpy.typing.ArrayLike,
8
+ ) -> scipy.sparse.csr_matrix:
9
+ """
10
+ Convert and validate a matrix as a sparse binary matrix in CSR format.
11
+
12
+ This function checks whether all elements of the input matrix are binary (0 or 1).
13
+ If the input is not already a sparse matrix, it converts it to a CSR (Compressed Sparse Row) matrix.
14
+
15
+ Parameters
16
+ ----------
17
+ matrix : array-like
18
+ Input matrix of shape (M, N). Can be a dense array-like or any SciPy sparse matrix format.
19
+
20
+ Returns
21
+ -------
22
+ scipy.sparse.csr_matrix
23
+ Binary sparse matrix in CSR format, of shape (M, N).
24
+
25
+ Raises
26
+ ------
27
+ ValueError
28
+ If the input matrix has elements outside {0, 1}.
29
+ TypeError
30
+ If the input is not array-like.
31
+
32
+ Examples
33
+ --------
34
+ >>> import numpy as np
35
+ >>> from scipy.sparse import csr_matrix
36
+ >>> from qec.utils.sparse_binary_utils import convert_to_binary_scipy_sparse
37
+ >>> mat = np.array([[0, 1], [1, 0]])
38
+ >>> convert_to_binary_scipy_sparse(mat).toarray()
39
+ array([[0, 1],
40
+ [1, 0]])
41
+
42
+ >>> mat = csr_matrix([[0, 1], [1, 0]])
43
+ >>> convert_to_binary_scipy_sparse(mat).toarray()
44
+ array([[0, 1],
45
+ [1, 0]])
46
+
47
+ >>> mat = np.array([[0, 2], [1, 0]])
48
+ >>> convert_to_binary_scipy_sparse(mat)
49
+ Traceback (most recent call last):
50
+ ...
51
+ ValueError: All elements of the input matrix must be binary.
52
+ """
53
+ if not isinstance(matrix, (np.ndarray, list, scipy.sparse.spmatrix)):
54
+ raise TypeError("Input must be array-like.")
55
+
56
+ if not isinstance(matrix, scipy.sparse.csr_matrix):
57
+ matrix = scipy.sparse.csr_matrix(matrix, dtype=np.uint8)
58
+
59
+ if not matrix.dtype == np.uint8:
60
+ matrix = matrix.astype(np.uint8)
61
+
62
+ if not np.all(np.isin(matrix.data, [0, 1])):
63
+ raise ValueError("All elements of the input matrix must be binary.")
64
+
65
+ return matrix
66
+
67
+
68
+ def binary_csr_matrix_to_dict(matrix: scipy.sparse.spmatrix):
69
+ return {
70
+ "indices": matrix.indices.tolist(),
71
+ "indptr": matrix.indptr.tolist(),
72
+ "shape": matrix.shape,
73
+ }
74
+
75
+
76
+ def dict_to_binary_csr_matrix(csr_dict: dict):
77
+ return scipy.sparse.csr_matrix(
78
+ ([1] * len(csr_dict["indices"]), csr_dict["indices"], csr_dict["indptr"]),
79
+ shape=csr_dict["shape"],
80
+ )
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: qec
3
- Version: 0.3.0
3
+ Version: 0.3.3
4
4
  Summary: Python Tools for Quantum Error Correction
5
5
  Author-email: Joschka Roffe <joschka@roffe.eu>, Tamas Noszko <T.K.Noszko@sms.ed.ac.uk>, Liam Veeder-Sweeney <L.D.Veeder-Sweeney@sms.ed.ac.uk>
6
6
  License: MIT License
@@ -34,13 +34,12 @@ Requires-Dist: numpy>=1.24.0
34
34
  Requires-Dist: scipy>=1.9.3
35
35
  Requires-Dist: requests
36
36
  Requires-Dist: beautifulsoup4
37
+ Dynamic: license-file
38
+
39
+ ![qec_banner](https://github.com/user-attachments/assets/9f54ed76-f606-44ed-aa15-275695e45ea0)
37
40
 
38
- <h1>
39
- <p align="center">
40
- <br>QEC
41
- </h1>
42
41
  <p align="center">
43
- Python Tools for Quantum Error Correction
42
+ QEC: Python Tools for Quantum Error Correction
44
43
  <br />
45
44
  <a href="https://qec.codes/">Website</a>
46
45
  ·
@@ -54,8 +53,8 @@ Requires-Dist: beautifulsoup4
54
53
 
55
54
  # Features
56
55
 
57
- ## Code constructions and fundamental analysis
58
- We currently provide the construction methods for the stabilizer generators of the QEC code families below, along with optimized methods to obtain their fundamental properties. All the classes allow to:
56
+ ## Base functionality: code constructions and "fundamental" analysis
57
+ We currently provide the construction methods for the stabilizer generators of the QEC code families below, along with optimized methods to obtain their fundamental properties. All the classes allow you to:
59
58
 
60
59
  - obtain the physical qubit count
61
60
  - obtain the logical qubit count
@@ -63,11 +62,18 @@ We currently provide the construction methods for the stabilizer generators of t
63
62
  - estimate the minimum code distance
64
63
  - obtain a logical basis
65
64
 
66
- The classes (along with their extre methods) are:
65
+ The classes (along with their extra methods) are:
67
66
 
68
67
  - Hyperprgarph Product (HGP) codes, with methods:
69
68
  - construct the x and z stabiliser matrices from the seed codes
70
69
  - obtain the canonical basis (work in progress)
70
+
71
+ - Surface codes
72
+ - Unrotated Surface code
73
+ - Periodic Surface XZZX code
74
+ - Rotated Surface XZZX code
75
+
76
+ - Toric code
71
77
 
72
78
  - Calderbank-Shor-Steane (CSS) codes
73
79
  - check that the seed codes satisfy the CSS criteria
@@ -75,8 +81,24 @@ The classes (along with their extre methods) are:
75
81
  - Stabiliser codes
76
82
  - check that the input stabiliser matrix is valid
77
83
 
78
- ## Circuit compilation
79
- Work in progress.
84
+ ## Circuit compilation
85
+
86
+ >
87
+ > _Note:_ this functionality is still work in progress. The corresponding code is not part of the `main` branch - you can find it on the `circuit_compilation` branch to play around.
88
+ >
89
+
90
+ Currently we only support circuit compilation for memory experiments of:
91
+
92
+ - HGP codes with:
93
+ - "coloration circuit" stabilizer schedule (twice the depth of the most optimal "cardinal circuit" method)
94
+ - _under development:_ "cardinal circuit" stabilizer schedule
95
+
96
+ One can either compile noisy or noisless circuits (for further compilation to one's own needs). To create a nosiy circuit one needs to construct their own noise model, or use one of the available presets:
97
+ - `uniform_depolarizing_noise`
98
+ - `non_uniform_depolarizing_noise` under development
99
+ - `phenomenological_noise`
100
+
101
+ For a more detailed example please see the `demo.ipynb` [notebook](https://github.com/qec-codes/qec/blob/circuit_compilation/demo/demo.ipynb), inside the `demo/` folder of the `circuit_compilation` branch.
80
102
 
81
103
  # Installation
82
104
 
@@ -98,18 +120,10 @@ Finally, install the package:
98
120
  pip install -e .
99
121
  ```
100
122
 
101
- You are all set! To import the package use:
102
-
103
- ```python
104
- In [1]: import qec
105
-
106
- In [2]: qec.__version__
107
- Out [2]: '0.1.0'
108
-
109
- ```
110
-
111
123
  # Examples
112
124
 
125
+ ## Base functionality:
126
+
113
127
  In this example we are going to create the Steane code, and obtain its fundamental code properties. We start by initialising its seed matrices (the [7, 4, 3] Hamming code):
114
128
  ```python
115
129
  In [1]: import numpy as np
@@ -125,11 +139,13 @@ as the Steane code is part of the CSS code family, we can use the `CSSCode` clas
125
139
  In [4]: steane_code = CSSCode(x_stabilizer_matrix = hamming_code,
126
140
  z_stabilizer_matrix = hamming_code,
127
141
  name = "Steane")
128
- In [5]: print(steane_code)
129
- Out [6]: Steane Code: [[N=7, K=1, dx<=3, dz<=3]]
142
+ In [5]: steane_code.compute_exact_code_distance()
143
+ In [6]: print(steane_code)
144
+
145
+ Out [7]: Steane Code: [[N=7, K=1, dx<=3, dz<=3]]
130
146
  ```
131
147
 
132
- we can see that all the fundamental properties (N - physical qubit number, K - logical qubit number, dx - X distance, dz - Z distance) are pre-calculated for us. We can continue our analysis by taking a look at the x and z logical basis of the code:
148
+ we can see that all the fundamental properties (N - physical qubit number, K - logical qubit number, dx - X distance, dz - Z distance). We can continue our analysis by taking a look at the x and z logical basis of the code:
133
149
 
134
150
  ```python
135
151
  In [7]: print(steane_code.x_logical_operator_basis.toarray())
@@ -138,6 +154,21 @@ In [9]: print(steane_code.z_logical_operator_basis.toarray())
138
154
  Out [10]: [[1 1 0 1 0 0 0]]
139
155
  ```
140
156
 
157
+ ## Circuit compilation:
158
+
159
+ ```python
160
+ In [10]: from qec.code_constructions import HypergraphProductCode
161
+ In [11]: from qec.circuit_compilation import MemoryExperiment
162
+
163
+ In [12]: hgp_example_code = HypergraphProductCode(hamming_code, hamming_code)
164
+
165
+ In [12]: hgp_memory_example = MemoryExperiment(hgp_example_code)
166
+
167
+ In [13]: hgp_X_mem_circuit = hgp_memory_example.circuit(basis = 'X', rounds = 1, noise = False)
168
+ ```
169
+
170
+ To see the output circuit and a more detailed example (including a simulation) see the `demo.ipynb` [notebook](https://github.com/qec-codes/qec/blob/circuit_compilation/demo/demo.ipynb), inside the `demo/` folder of the `circuit_compilation` branch.
171
+
141
172
 
142
173
 
143
174
 
@@ -0,0 +1,27 @@
1
+ qec/__init__.py,sha256=daEdpEyAJIa8b2VkCqSKcw8PaExcB6Qro80XNes_sHA,2
2
+ qec/code_constructions/__init__.py,sha256=Jg7aUTYaTuFnLGcpqPmc7iSQeM6Kwi_p9eUQBLNvePQ,600
3
+ qec/code_constructions/css_code.py,sha256=e4X6QMnZ_xHdZENsaqrfFe-tl0XgEHvm7Fw2yBM3low,38614
4
+ qec/code_constructions/hgp_code.py,sha256=FqghbZ6mQIBlQIJwhaPU_Zb1LjnmHBS_PGtQVNPl_mA,12310
5
+ qec/code_constructions/periodic_surface_xzzx.py,sha256=iF95s-py85ivELlCiL5oEWe87OUVaKI3r4mJjaKy_-o,3551
6
+ qec/code_constructions/rotated_xzzx.py,sha256=wSWH9H1AaNn_Yyhnr_NSvZdlo_sV9HW5JHFcBrjAnQY,4400
7
+ qec/code_constructions/stabilizer_code.py,sha256=InPbSrqzMxueMQaFbUt-ytDHCWIzgIddnlAwYegpRFg,24258
8
+ qec/code_constructions/surface_code.py,sha256=s49uyMZPRjOwBqpfLeB5Dl38O8w7BP8Y1tBemFhyDr0,2561
9
+ qec/code_constructions/toric_code.py,sha256=Q8f3jU80gwsjqRVtp2tBuO6yRltQI5KeD49xQm8vuAw,1952
10
+ qec/code_instances/__init__.py,sha256=5T_Qw26-cQc5BC6RM23_oDAYRdB3UPU5MeCGPxsoSRs,79
11
+ qec/code_instances/five_qubit_code.py,sha256=ZHyusTEnnnmUv4QxPLXQSKbEQv2QOoBqn2V9N4SgrQE,2177
12
+ qec/code_instances/saved_codes/1.json,sha256=PEErxvbeDupmZ4woHxC6EGAaRxfZ34fb4cOpBkfHvrs,850
13
+ qec/code_instances/saved_codes/2.json,sha256=Dwp5XT65s9Wu51WFJjzNzkP5WYLcVPkVoIpfLb92PMM,2920
14
+ qec/code_instances/saved_codes/3.json,sha256=3FeCj54tl708TeEsaChb_qqr92kw4_9S1wf4VASN-5U,4687
15
+ qec/code_instances/saved_codes/4.json,sha256=H4ktJy02VGPxqSAZkjam1HMlPex2KjmbcG9CBdP4NhI,5452
16
+ qec/codetables_de/__init__.py,sha256=_BQq4cIgZHbGPsA6qsJnMJ-B5eHqiYvUKbSubmJP8jA,68
17
+ qec/codetables_de/codetables_de.py,sha256=4tkoHtr4VXHm0qY9r397EUhxFXR1Mp6DqqtfblGyzbw,3725
18
+ qec/utils/__init__.py,sha256=lSkUeUQhk2KU8EgH63jSehwAMHQAJ3XBDmmRIKXRuN8,123
19
+ qec/utils/binary_pauli_utils.py,sha256=Vtqw-wWlOmrj2CDDhjjNmgUZHe5TJ2bU5-c_pV_Q4ks,13280
20
+ qec/utils/codetables_de_utils.py,sha256=Qbk2y-4sMufSrgQyuC6pDBOteeC3By_BiykVz4mIL7o,9467
21
+ qec/utils/load_code_util.py,sha256=w4icqtSKvEiA9JuFAc1AdkCt31dDC-SYK4-d4G4YfAY,4642
22
+ qec/utils/sparse_binary_utils.py,sha256=02qxK1iC_8jFIeuH1vjSd_3OwuKT5wWsVBshklc2IIA,2397
23
+ qec-0.3.3.dist-info/licenses/LICENSE,sha256=1b_xwNz1znYBfEaCL6pN2gNBAn8pQIjDRs_UhDp1EJI,1066
24
+ qec-0.3.3.dist-info/METADATA,sha256=r6ZBxOp3OYOhSOvjah90naZqy93BQcnHUpqIgfJpLoU,6392
25
+ qec-0.3.3.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
26
+ qec-0.3.3.dist-info/top_level.txt,sha256=d8l_7pJ5u9uWdviNp0FUK-j8VPZqywkDek7qa4NDank,4
27
+ qec-0.3.3.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,34 +0,0 @@
1
- {
2
- "class_name": "CSSCode",
3
- "name": "Steane",
4
- "physical_qubit_count": 7,
5
- "logical_qubit_count": 1,
6
- "code_distance": 3,
7
- "x_code_distance": 3,
8
- "z_code_distance": 3,
9
- "x_stabilizer_matrix": {
10
- "data": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
11
- "indices": [0, 3, 5, 6, 1, 3, 4, 6, 2, 4, 5, 6],
12
- "indptr": [0, 4, 8, 12],
13
- "shape": [3, 7]
14
- },
15
- "z_stabilizer_matrix": {
16
- "data": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
17
- "indices": [0, 3, 5, 6, 1, 3, 4, 6, 2, 4, 5, 6],
18
- "indptr": [0, 4, 8, 12],
19
- "shape": [3, 7]
20
- },
21
- "x_logical_operator_basis": {
22
- "data": [1, 1, 1],
23
- "indices": [0, 1, 3],
24
- "indptr": [0, 3],
25
- "shape": [1, 7]
26
- },
27
- "z_logical_operator_basis": {
28
- "data": [1, 1, 1],
29
- "indices": [0, 1, 3],
30
- "indptr": [0, 3],
31
- "shape": [1, 7]
32
- },
33
- "notes": "this is a test note"
34
- }
@@ -1 +0,0 @@
1
- {"hello": "world"}
@@ -1,9 +0,0 @@
1
- qec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- qec/code_instances/saved_codes/1.json,sha256=whH698pYdPARAVomZOxoYerwW0IzaCOavL5KMIFuB9Y,1616
3
- qec/code_instances/saved_codes/steane.json,sha256=Hx6TqeE8KHOL189fJG4eIekiuQvVLcltjbOCsN1mEPQ,930
4
- qec/code_instances/saved_codes/test.json,sha256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE,18
5
- qec-0.3.0.dist-info/LICENSE,sha256=1b_xwNz1znYBfEaCL6pN2gNBAn8pQIjDRs_UhDp1EJI,1066
6
- qec-0.3.0.dist-info/METADATA,sha256=FTk3YvEROBX7QuNFBAwjVkUVBs6mhED0j71y0W2jQMQ,4611
7
- qec-0.3.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
8
- qec-0.3.0.dist-info/top_level.txt,sha256=d8l_7pJ5u9uWdviNp0FUK-j8VPZqywkDek7qa4NDank,4
9
- qec-0.3.0.dist-info/RECORD,,