parityos 0.0.6__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.
Potentially problematic release.
This version of parityos might be problematic. Click here for more details.
- parityos/__init__.py +15 -0
- parityos/api_interface/__init__.py +0 -0
- parityos/api_interface/authentication.py +87 -0
- parityos/api_interface/compiler_run.py +113 -0
- parityos/api_interface/connection.py +139 -0
- parityos/api_interface/exceptions.py +36 -0
- parityos/base/__init__.py +48 -0
- parityos/base/circuit.py +219 -0
- parityos/base/constraints.py +123 -0
- parityos/base/exceptions.py +20 -0
- parityos/base/gates.py +704 -0
- parityos/base/problem_representation.py +322 -0
- parityos/base/qubits.py +83 -0
- parityos/base/utils.py +124 -0
- parityos/compiler_client.py +146 -0
- parityos/device_model.py +181 -0
- parityos/encodings/__init__.py +0 -0
- parityos/encodings/mappings.py +110 -0
- parityos/encodings/parity_decoder.py +268 -0
- parityos/encodings/parity_encoder.py +39 -0
- parityos/encodings/parityos_output.py +248 -0
- parityos-0.0.6.dist-info/License.txt +37 -0
- parityos-0.0.6.dist-info/METADATA +34 -0
- parityos-0.0.6.dist-info/RECORD +146 -0
- parityos-0.0.6.dist-info/WHEEL +5 -0
- parityos-0.0.6.dist-info/top_level.txt +2 -0
- parityos_addons/benchmarking/__init__.py +11 -0
- parityos_addons/benchmarking/benchmark_compiler_runs.py +79 -0
- parityos_addons/benchmarking/benchmark_parityos_outputs.py +48 -0
- parityos_addons/benchmarking/dataframe_construction/__init__.py +0 -0
- parityos_addons/benchmarking/dataframe_construction/add_column.py +38 -0
- parityos_addons/benchmarking/dataframe_construction/benchmark_circuits.py +47 -0
- parityos_addons/benchmarking/dataframe_construction/benchmark_compiler_runs.py +46 -0
- parityos_addons/benchmarking/dataframe_construction/benchmark_problem_representations.py +52 -0
- parityos_addons/benchmarking/metrics/__init__.py +27 -0
- parityos_addons/benchmarking/metrics/circuit_metrics.py +125 -0
- parityos_addons/benchmarking/metrics/circuit_utils.py +37 -0
- parityos_addons/benchmarking/metrics/compilation_metrics.py +75 -0
- parityos_addons/documentation/__init__.py +0 -0
- parityos_addons/documentation/html/_images/hypergraph_solution_tutorial.png +0 -0
- parityos_addons/documentation/html/_images/hypergraph_tutorial.png +0 -0
- parityos_addons/documentation/html/_sources/api_reference/api_interface.rst.txt +6 -0
- parityos_addons/documentation/html/_sources/api_reference/base.rst.txt +14 -0
- parityos_addons/documentation/html/_sources/api_reference/encodings.rst.txt +11 -0
- parityos_addons/documentation/html/_sources/api_reference/index.rst.txt +11 -0
- parityos_addons/documentation/html/_sources/api_reference/parityos.rst.txt +6 -0
- parityos_addons/documentation/html/_sources/api_reference/parityos_addons.rst.txt +17 -0
- parityos_addons/documentation/html/_sources/asynchronous_submission.rst.txt +67 -0
- parityos_addons/documentation/html/_sources/benchmarking.rst.txt +132 -0
- parityos_addons/documentation/html/_sources/custom_device_models.rst.txt +221 -0
- parityos_addons/documentation/html/_sources/index.rst.txt +15 -0
- parityos_addons/documentation/html/_sources/installation.rst.txt +33 -0
- parityos_addons/documentation/html/_sources/parity_decoder.rst.txt +69 -0
- parityos_addons/documentation/html/_sources/quickstart.rst.txt +199 -0
- parityos_addons/documentation/html/_sources/tutorial.rst.txt +449 -0
- parityos_addons/documentation/html/_static/_sphinx_javascript_frameworks_compat.js +123 -0
- parityos_addons/documentation/html/_static/basic.css +914 -0
- parityos_addons/documentation/html/_static/css/badge_only.css +1 -0
- parityos_addons/documentation/html/_static/css/fonts/Roboto-Slab-Bold.woff +0 -0
- parityos_addons/documentation/html/_static/css/fonts/Roboto-Slab-Bold.woff2 +0 -0
- parityos_addons/documentation/html/_static/css/fonts/Roboto-Slab-Regular.woff +0 -0
- parityos_addons/documentation/html/_static/css/fonts/Roboto-Slab-Regular.woff2 +0 -0
- parityos_addons/documentation/html/_static/css/fonts/fontawesome-webfont.eot +0 -0
- parityos_addons/documentation/html/_static/css/fonts/fontawesome-webfont.svg +2671 -0
- parityos_addons/documentation/html/_static/css/fonts/fontawesome-webfont.ttf +0 -0
- parityos_addons/documentation/html/_static/css/fonts/fontawesome-webfont.woff +0 -0
- parityos_addons/documentation/html/_static/css/fonts/fontawesome-webfont.woff2 +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-bold-italic.woff +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-bold-italic.woff2 +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-bold.woff +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-bold.woff2 +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-normal-italic.woff +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-normal-italic.woff2 +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-normal.woff +0 -0
- parityos_addons/documentation/html/_static/css/fonts/lato-normal.woff2 +0 -0
- parityos_addons/documentation/html/_static/css/theme.css +4 -0
- parityos_addons/documentation/html/_static/doctools.js +149 -0
- parityos_addons/documentation/html/_static/documentation_options.js +13 -0
- parityos_addons/documentation/html/_static/file.png +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bold.eot +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bold.ttf +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bold.woff +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bold.woff2 +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bolditalic.eot +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bolditalic.ttf +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bolditalic.woff +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-bolditalic.woff2 +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-italic.eot +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-italic.ttf +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-italic.woff +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-italic.woff2 +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-regular.eot +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-regular.ttf +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-regular.woff +0 -0
- parityos_addons/documentation/html/_static/fonts/Lato/lato-regular.woff2 +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.eot +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.ttf +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-bold.woff2 +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.eot +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.ttf +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff +0 -0
- parityos_addons/documentation/html/_static/fonts/RobotoSlab/roboto-slab-v7-regular.woff2 +0 -0
- parityos_addons/documentation/html/_static/jquery.js +2 -0
- parityos_addons/documentation/html/_static/js/badge_only.js +1 -0
- parityos_addons/documentation/html/_static/js/theme.js +1 -0
- parityos_addons/documentation/html/_static/js/versions.js +228 -0
- parityos_addons/documentation/html/_static/language_data.js +192 -0
- parityos_addons/documentation/html/_static/minus.png +0 -0
- parityos_addons/documentation/html/_static/plus.png +0 -0
- parityos_addons/documentation/html/_static/pygments.css +75 -0
- parityos_addons/documentation/html/_static/searchtools.js +632 -0
- parityos_addons/documentation/html/_static/sphinx_highlight.js +154 -0
- parityos_addons/documentation/html/api_reference/api_interface.html +124 -0
- parityos_addons/documentation/html/api_reference/base.html +1262 -0
- parityos_addons/documentation/html/api_reference/encodings.html +352 -0
- parityos_addons/documentation/html/api_reference/index.html +133 -0
- parityos_addons/documentation/html/api_reference/parityos.html +704 -0
- parityos_addons/documentation/html/api_reference/parityos_addons.html +382 -0
- parityos_addons/documentation/html/asynchronous_submission.html +185 -0
- parityos_addons/documentation/html/benchmarking.html +251 -0
- parityos_addons/documentation/html/custom_device_models.html +337 -0
- parityos_addons/documentation/html/genindex.html +768 -0
- parityos_addons/documentation/html/index.html +128 -0
- parityos_addons/documentation/html/installation.html +142 -0
- parityos_addons/documentation/html/objects.inv +0 -0
- parityos_addons/documentation/html/parity_decoder.html +189 -0
- parityos_addons/documentation/html/py-modindex.html +193 -0
- parityos_addons/documentation/html/quickstart.html +324 -0
- parityos_addons/documentation/html/search.html +127 -0
- parityos_addons/documentation/html/searchindex.js +1 -0
- parityos_addons/documentation/html/tutorial.html +530 -0
- parityos_addons/documentation/show.py +24 -0
- parityos_addons/examples/__init__.py +0 -0
- parityos_addons/examples/example1.py +60 -0
- parityos_addons/examples/example2.py +54 -0
- parityos_addons/examples/example3.py +58 -0
- parityos_addons/examples/example4.py +70 -0
- parityos_addons/examples/runner.py +33 -0
- parityos_addons/interfaces/__init__.py +22 -0
- parityos_addons/interfaces/cirq_exporter.py +184 -0
- parityos_addons/interfaces/openqasm_exporter.py +426 -0
- parityos_addons/interfaces/qiskit_exporter.py +251 -0
- parityos_addons/qaoa/__init__.py +12 -0
- parityos_addons/qaoa/qaoa_scheduler.py +105 -0
- parityos_addons/spin_hamiltonians.py +102 -0
parityos/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parity Quantum Computing GmbH
|
|
3
|
+
Rennweg 1 Top 314
|
|
4
|
+
6020 Innsbruck, Austria
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2020-2024.
|
|
7
|
+
All rights reserved.
|
|
8
|
+
|
|
9
|
+
Tools to use the ParityOS cloud services.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .base import Hamiltonian, ProblemRepresentation, ParityOSException, ParityOSImportError, Qubit
|
|
13
|
+
from .device_model import RectangularAnalogDevice, RectangularDigitalDevice
|
|
14
|
+
from .encodings.parityos_output import ParityOSOutput
|
|
15
|
+
from .compiler_client import CompilerClient
|
|
File without changes
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parity Quantum Computing GmbH
|
|
3
|
+
Rennweg 1 Top 314
|
|
4
|
+
6020 Innsbruck, Austria
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2020-2023.
|
|
7
|
+
All rights reserved.
|
|
8
|
+
|
|
9
|
+
Tools to authenticate with the ParityOS cloud services.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from collections.abc import Iterator
|
|
13
|
+
import getpass
|
|
14
|
+
import os
|
|
15
|
+
import warnings
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def generate_credentials(
|
|
19
|
+
username: str = None,
|
|
20
|
+
username_variable: str = "",
|
|
21
|
+
password_variable: str = "",
|
|
22
|
+
intents: int = 3,
|
|
23
|
+
) -> Iterator[dict[str, str]]:
|
|
24
|
+
"""
|
|
25
|
+
Generates a series of JSON compatible dictionaries with a username and a password.
|
|
26
|
+
The username can be provided as an argument. Otherwise, it is taken from the given system
|
|
27
|
+
variable (this is tried only once), or requested from the user on the prompt.
|
|
28
|
+
For security reasons, the password can not be provided as an argument. It is either taken
|
|
29
|
+
from the given system variable (this is tried only once), or requested from the user on the
|
|
30
|
+
prompt.
|
|
31
|
+
The argument `intents` sets the maximum number of times to ask for a username or password,
|
|
32
|
+
either from the environment or from the prompt.
|
|
33
|
+
To make this generator fully non-interactive, set intents to 1 and set the correct system
|
|
34
|
+
variables.
|
|
35
|
+
|
|
36
|
+
:param str username: The username for the account on the server.
|
|
37
|
+
:param str username_variable: System variable to query for the username
|
|
38
|
+
:param str password_variable: System variable to query for the password
|
|
39
|
+
:param int intents: Maximum number of times to ask for a username or password.
|
|
40
|
+
"""
|
|
41
|
+
# The credentials object allows for a number of intents to provide the password.
|
|
42
|
+
# First intent: get data from the environment variables or else from prompt.
|
|
43
|
+
username = username or _get_username(username, username_variable)
|
|
44
|
+
password = _get_password(password_variable=password_variable)
|
|
45
|
+
yield {"username": username, "password": password}
|
|
46
|
+
|
|
47
|
+
# The next intents are always interactive.
|
|
48
|
+
for intent in range(1, intents):
|
|
49
|
+
print("Login failed. Please provide the correct credentials.")
|
|
50
|
+
username = _get_username(username)
|
|
51
|
+
password = _get_password(password)
|
|
52
|
+
yield {"username": username, "password": password}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _get_username(username: str = "", username_variable: str = "") -> str:
|
|
56
|
+
"""
|
|
57
|
+
Ask for a username, either from the provided environment variable or on the prompt.
|
|
58
|
+
If a username argument is given, then this is shown as a suggestion on the prompt
|
|
59
|
+
and returned as the result if the user only types return.
|
|
60
|
+
"""
|
|
61
|
+
if username_variable and not username:
|
|
62
|
+
username = os.getenv(username_variable)
|
|
63
|
+
if username:
|
|
64
|
+
return username
|
|
65
|
+
|
|
66
|
+
suggestion = f" [{username}]" if username else ""
|
|
67
|
+
username = input(f"ParityOS Username{suggestion}: ") or username
|
|
68
|
+
return username
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _get_password(password: str = "", password_variable: str = "") -> str:
|
|
72
|
+
"""
|
|
73
|
+
Ask for a password, either from the provided environment variable or on the prompt.
|
|
74
|
+
If a password argument is given, then "***" is shown as a suggestion on the prompt
|
|
75
|
+
and the given value is returned as the result if the user only types return.
|
|
76
|
+
"""
|
|
77
|
+
if password_variable and not password:
|
|
78
|
+
with warnings.catch_warnings():
|
|
79
|
+
warnings.simplefilter("ignore")
|
|
80
|
+
password = os.getenv(password_variable)
|
|
81
|
+
|
|
82
|
+
if password:
|
|
83
|
+
return password
|
|
84
|
+
|
|
85
|
+
suggestion = " [***]" if password else ""
|
|
86
|
+
password = getpass.getpass(f"ParityOS Password{suggestion}: ") or password
|
|
87
|
+
return password
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parity Quantum Computing GmbH
|
|
3
|
+
Rennweg 1 Top 314
|
|
4
|
+
6020 Innsbruck, Austria
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2020-2024.
|
|
7
|
+
All rights reserved.
|
|
8
|
+
|
|
9
|
+
Tools to process the results from the ParityOS cloud service.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from typing import Optional, Union
|
|
16
|
+
|
|
17
|
+
from parityos.base import JSONMappingType
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
DateStr = Union[datetime, str] # type: TypeAlias
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CompilerRunStatus(Enum):
|
|
24
|
+
"""
|
|
25
|
+
Enum for compiler run status.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
SUBMITTED = "S"
|
|
29
|
+
RUNNING = "R"
|
|
30
|
+
COMPLETED = "C"
|
|
31
|
+
FAILED = "F"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class CompilerRun:
|
|
36
|
+
"""
|
|
37
|
+
Encapsulates a compiler run; has attributes which describe relevant times
|
|
38
|
+
at which they were submitted, started, and eventually finished or failed
|
|
39
|
+
(in which case, a reason for failure is also given).
|
|
40
|
+
|
|
41
|
+
:param id: The UUID4 id of the compiler run in ParityOS cloud database.
|
|
42
|
+
:param submission_id: The UUID4 id of the submission which triggered compile run.
|
|
43
|
+
:param status: The status of the submission; see the `CompilerRunStatus` enum.
|
|
44
|
+
:param submitted_at: Time at which the run was queued for execution, as `datetime` object.
|
|
45
|
+
:param started_at: Time at which the run started being executed, as `datetime` object.
|
|
46
|
+
:param finished_at: Time at which the run was completed, as `datetime` object.
|
|
47
|
+
:param failed_at: time at which run failed
|
|
48
|
+
:param failure_reason: reason for which run failed
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
id: str
|
|
52
|
+
submission_id: str
|
|
53
|
+
status: Union[CompilerRunStatus, str]
|
|
54
|
+
submitted_at: Optional[DateStr] = None
|
|
55
|
+
started_at: Optional[DateStr] = None
|
|
56
|
+
finished_at: Optional[DateStr] = None
|
|
57
|
+
failed_at: Optional[DateStr] = None
|
|
58
|
+
failure_reason: Optional[str] = ""
|
|
59
|
+
|
|
60
|
+
def __post_init__(self):
|
|
61
|
+
"""Convert attributes to their intended type if they were provided as strings."""
|
|
62
|
+
# Map the status to the corresponding Enum instance.
|
|
63
|
+
self.status = CompilerRunStatus(self.status)
|
|
64
|
+
# Map date times in string format to datetime instances
|
|
65
|
+
self.submitted_at = _str_to_datetime(self.submitted_at)
|
|
66
|
+
self.started_at = _str_to_datetime(self.started_at)
|
|
67
|
+
self.finished_at = _str_to_datetime(self.finished_at)
|
|
68
|
+
self.failed_at = _str_to_datetime(self.failed_at)
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def from_json(cls, data: JSONMappingType) -> "Self":
|
|
72
|
+
"""
|
|
73
|
+
Creates a CompilerRun object from a JSON-like data dictionary.
|
|
74
|
+
:return: a CompilerRun object
|
|
75
|
+
"""
|
|
76
|
+
return cls(**data)
|
|
77
|
+
|
|
78
|
+
def __str__(self) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Return useful information about the compiler run in string format.
|
|
81
|
+
"""
|
|
82
|
+
info_lines = [f"Compiler run {self.id}. ", f"Status: {self.status}"]
|
|
83
|
+
if self.submitted_at:
|
|
84
|
+
info_lines.append(f"The compiler run was submitted at {self.submitted_at.ctime()}.")
|
|
85
|
+
|
|
86
|
+
if self.started_at:
|
|
87
|
+
info_lines.append(f"The compilation started at {self.started_at.ctime()}.")
|
|
88
|
+
if self.finished_at:
|
|
89
|
+
duration_in_seconds = (self.finished_at - self.started_at).total_seconds()
|
|
90
|
+
info_lines.append(f"The compilation took {duration_in_seconds:0.2f} seconds.")
|
|
91
|
+
|
|
92
|
+
if self.failed_at:
|
|
93
|
+
if self.started_at:
|
|
94
|
+
duration_in_seconds = (self.failed_at - self.started_at).total_seconds()
|
|
95
|
+
info_lines.append(
|
|
96
|
+
f"The compilation was aborted after {duration_in_seconds:0.2f} seconds."
|
|
97
|
+
)
|
|
98
|
+
else:
|
|
99
|
+
info_lines.append(f"An error occurred at {self.failed_at.ctime()}.")
|
|
100
|
+
|
|
101
|
+
if self.failure_reason:
|
|
102
|
+
info_lines.append(self.failure_reason)
|
|
103
|
+
|
|
104
|
+
return "\n".join(info_lines)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _str_to_datetime(date: Union[datetime, str]) -> datetime:
|
|
108
|
+
"""Helper method to convert dates in string format to datetime instances."""
|
|
109
|
+
return (
|
|
110
|
+
datetime.fromisoformat(date.replace("Z", "+00:00"))
|
|
111
|
+
if date and isinstance(date, str)
|
|
112
|
+
else date
|
|
113
|
+
)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parity Quantum Computing GmbH
|
|
3
|
+
Rennweg 1 Top 314
|
|
4
|
+
6020 Innsbruck, Austria
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2020-2022.
|
|
7
|
+
All rights reserved.
|
|
8
|
+
|
|
9
|
+
Tools to connect to the ParityOS cloud services.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from json.decoder import JSONDecodeError
|
|
13
|
+
|
|
14
|
+
from requests import Session, Response
|
|
15
|
+
from requests.adapters import HTTPAdapter
|
|
16
|
+
from requests.packages.urllib3.util.retry import Retry
|
|
17
|
+
|
|
18
|
+
from parityos.api_interface.authentication import generate_credentials
|
|
19
|
+
from parityos.api_interface.exceptions import ParityOSAuthError, ParityOSRequestError
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
DEFAULT_HOST = "api.parityqc.com"
|
|
23
|
+
|
|
24
|
+
# Define environment variables that can hold the credentials for the default host.
|
|
25
|
+
DEFAULT_ENVIRONMENT = dict(
|
|
26
|
+
username_variable="PARITYOS_USER",
|
|
27
|
+
password_variable="PARITYOS_PASS",
|
|
28
|
+
)
|
|
29
|
+
# Define the environment variables that can hold the credentials for alternative hosts.
|
|
30
|
+
ALTERNATIVE_ENVIRONMENT = dict(
|
|
31
|
+
username_variable="PARITYOS_ALTUSER",
|
|
32
|
+
password_variable="PARITYOS_ALTPASS",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ClientBase:
|
|
37
|
+
"""
|
|
38
|
+
Base class that sets up HTTP parameters (authentication, API endpoints, and so on).
|
|
39
|
+
It should not be used directly unless you are debugging HTTP requests,
|
|
40
|
+
use parityos.CompilerClient instead.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
username: str = None,
|
|
46
|
+
host: str = DEFAULT_HOST,
|
|
47
|
+
url_prefix: str = "v1",
|
|
48
|
+
http_retries: int = 3,
|
|
49
|
+
http_timeout: int = 10,
|
|
50
|
+
http_backoff_factor: float = 0.02,
|
|
51
|
+
intents: int = 3,
|
|
52
|
+
):
|
|
53
|
+
"""
|
|
54
|
+
:param str username: ParityOS id of the user.
|
|
55
|
+
:param str host: ParityAPI host name.
|
|
56
|
+
:param str url_prefix: Prefix to REST API paths, used mostly for versioning.
|
|
57
|
+
:param int http_retries: Number of http retries.
|
|
58
|
+
:param int http_timeout: Maximum time in seconds to wait for a http response.
|
|
59
|
+
:param int http_backoff_factor: Http exponential backoff factor.
|
|
60
|
+
:param int intents: Maximum number of intents with different passwords.
|
|
61
|
+
"""
|
|
62
|
+
self.username = username
|
|
63
|
+
self.host = host
|
|
64
|
+
self.url_prefix = url_prefix
|
|
65
|
+
self.base_url = f"https://{self.host}/{self.url_prefix}"
|
|
66
|
+
self.http_timeout = http_timeout
|
|
67
|
+
self.http_retries = http_retries
|
|
68
|
+
self.http_backoff_factor = http_backoff_factor
|
|
69
|
+
self.intents = intents
|
|
70
|
+
self._setup_connection()
|
|
71
|
+
|
|
72
|
+
def _setup_connection(self):
|
|
73
|
+
"""
|
|
74
|
+
Connects to the ParityAPI server, logs the user in and stores the session token.
|
|
75
|
+
"""
|
|
76
|
+
# Credentials are instantiated here so that the password information
|
|
77
|
+
# is garbage collected as soon as this method terminates.
|
|
78
|
+
self.http = Session()
|
|
79
|
+
adapter = HTTPAdapter(
|
|
80
|
+
max_retries=Retry(total=self.http_retries, backoff_factor=self.http_backoff_factor)
|
|
81
|
+
)
|
|
82
|
+
self.http.mount("https://", adapter)
|
|
83
|
+
self.username, token = self._get_authentication()
|
|
84
|
+
# Only the username is stored in self so that the password information
|
|
85
|
+
# can be garbage collected.
|
|
86
|
+
self.http.headers.update({"Authorization": f"Token {token}"})
|
|
87
|
+
|
|
88
|
+
def _get_authentication(self) -> tuple[str, str]:
|
|
89
|
+
"""
|
|
90
|
+
Log in on the server to obtain an authentication token for future requests.
|
|
91
|
+
"""
|
|
92
|
+
# The credentials object allows for a number of intents to provide the password.
|
|
93
|
+
environment_variables = (
|
|
94
|
+
DEFAULT_ENVIRONMENT if self.host == DEFAULT_HOST else ALTERNATIVE_ENVIRONMENT
|
|
95
|
+
)
|
|
96
|
+
authentication_intents = generate_credentials(
|
|
97
|
+
self.username,
|
|
98
|
+
**environment_variables,
|
|
99
|
+
intents=self.intents,
|
|
100
|
+
)
|
|
101
|
+
username = self.username
|
|
102
|
+
for credentials_data in authentication_intents:
|
|
103
|
+
response = self.http.post(f"{self.base_url}/auth", data=credentials_data)
|
|
104
|
+
data = response.json()
|
|
105
|
+
username = credentials_data["username"]
|
|
106
|
+
if "token" in data:
|
|
107
|
+
return username, data["token"]
|
|
108
|
+
|
|
109
|
+
# No authentication intents left after failed logins
|
|
110
|
+
raise ParityOSAuthError(f"Failed login for user {username} on server {self.host}.")
|
|
111
|
+
|
|
112
|
+
def _send_request(self, method: str, url: str, data: dict = None) -> Response:
|
|
113
|
+
"""
|
|
114
|
+
Send a http request
|
|
115
|
+
|
|
116
|
+
:param method: request method to use ('GET', 'POST', 'PUT', ... )
|
|
117
|
+
:param url: target url
|
|
118
|
+
:param data: payload data as a dictionary
|
|
119
|
+
:return: requests.Response object
|
|
120
|
+
"""
|
|
121
|
+
try:
|
|
122
|
+
response = self.http.request(method, url=url, json=data, timeout=self.http_timeout)
|
|
123
|
+
except Exception as err:
|
|
124
|
+
message = f"Failed to receive valid response after at most {self.http_retries} retries."
|
|
125
|
+
err.args = (*err.args, message)
|
|
126
|
+
raise err
|
|
127
|
+
|
|
128
|
+
if response.ok:
|
|
129
|
+
return response
|
|
130
|
+
else:
|
|
131
|
+
try:
|
|
132
|
+
error_data = response.json()
|
|
133
|
+
except JSONDecodeError:
|
|
134
|
+
error_data = "None"
|
|
135
|
+
|
|
136
|
+
raise ParityOSRequestError(
|
|
137
|
+
f"{method} on {url}: {response.status_code}, {error_data}",
|
|
138
|
+
response=response,
|
|
139
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parity Quantum Computing GmbH
|
|
3
|
+
Rennweg 1 Top 314
|
|
4
|
+
6020 Innsbruck, Austria
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2020-2024.
|
|
7
|
+
All rights reserved.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from requests.exceptions import RequestException
|
|
11
|
+
|
|
12
|
+
from parityos.base.exceptions import ParityOSException
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ParityOSAuthError(RequestException, ValueError):
|
|
16
|
+
"""
|
|
17
|
+
Exception for failed logins to the ParityAPI server.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ParityOSRequestError(RequestException):
|
|
22
|
+
"""
|
|
23
|
+
Exception for failed logins to the ParityAPI server.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ParityOSCompilerError(ParityOSException):
|
|
28
|
+
"""
|
|
29
|
+
Exception resulting from errors in the compiler on the ParityOS cloud server.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ParityOSTimeoutException(ParityOSException):
|
|
34
|
+
"""
|
|
35
|
+
Exception resulting from time-outs waiting for a response from the ParityOS cloud server.
|
|
36
|
+
"""
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parity Quantum Computing GmbH
|
|
3
|
+
Rennweg 1 Top 314
|
|
4
|
+
6020 Innsbruck, Austria
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2020-2024.
|
|
7
|
+
All rights reserved.
|
|
8
|
+
|
|
9
|
+
Basic data structures to describe gates, circuits and optimization problems.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .circuit import Circuit
|
|
13
|
+
from .constraints import ConfigurationType, EqualityConstraint, evaluate_parity, ParityConstraint
|
|
14
|
+
from .exceptions import ParityOSException, ParityOSImportError
|
|
15
|
+
from .gates import (
|
|
16
|
+
CCNOT,
|
|
17
|
+
CH,
|
|
18
|
+
CNOT,
|
|
19
|
+
CP,
|
|
20
|
+
CRx,
|
|
21
|
+
CRy,
|
|
22
|
+
CRz,
|
|
23
|
+
CY,
|
|
24
|
+
CZ,
|
|
25
|
+
DEFAULT_PARAMETER_NAME,
|
|
26
|
+
Gate,
|
|
27
|
+
H,
|
|
28
|
+
ISwap,
|
|
29
|
+
MultiControlledH,
|
|
30
|
+
MultiControlledRx,
|
|
31
|
+
MultiControlledRy,
|
|
32
|
+
MultiControlledRz,
|
|
33
|
+
Rx,
|
|
34
|
+
Ry,
|
|
35
|
+
Rz,
|
|
36
|
+
Rxx,
|
|
37
|
+
Ryy,
|
|
38
|
+
Rzz,
|
|
39
|
+
Rzzzz,
|
|
40
|
+
Swap,
|
|
41
|
+
SX,
|
|
42
|
+
X,
|
|
43
|
+
Y,
|
|
44
|
+
Z,
|
|
45
|
+
)
|
|
46
|
+
from .problem_representation import Hamiltonian, ProblemRepresentation
|
|
47
|
+
from .qubits import Qubit, QubitLabel
|
|
48
|
+
from .utils import json_wrap, dict_filter, JSONType, JSONMappingType, JSONLoadSaveMixin
|
parityos/base/circuit.py
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Parity Quantum Computing GmbH
|
|
3
|
+
Rennweg 1 Top 314
|
|
4
|
+
6020 Innsbruck, Austria
|
|
5
|
+
|
|
6
|
+
Copyright (c) 2020-2024.
|
|
7
|
+
All rights reserved.
|
|
8
|
+
|
|
9
|
+
Classes that store information on sequences of quantum gates
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
13
|
+
from math import pi
|
|
14
|
+
from typing import Union
|
|
15
|
+
|
|
16
|
+
from parityos.base.exceptions import ParityOSException
|
|
17
|
+
from parityos.base.gates import Gate, CNOT, RMixin, Rx, Ry, Rz, Rzz
|
|
18
|
+
from parityos.base.qubits import Qubit
|
|
19
|
+
from parityos.base.utils import json_wrap, JSONLoadSaveMixin, JSONType
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Circuit(JSONLoadSaveMixin, list):
|
|
23
|
+
"""
|
|
24
|
+
A sequence of Gate and/or Circuit objects.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def qubits(self) -> set[Qubit]:
|
|
29
|
+
"""
|
|
30
|
+
:return: All qubits from the elements in the circuit
|
|
31
|
+
"""
|
|
32
|
+
return set().union(*(element.qubits for element in self))
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def parameters(self) -> set[str]:
|
|
36
|
+
"""
|
|
37
|
+
:return: the set of parameters (strings)
|
|
38
|
+
"""
|
|
39
|
+
parameters = set()
|
|
40
|
+
for gate_or_circuit_element in self:
|
|
41
|
+
if hasattr(gate_or_circuit_element, "parameters"):
|
|
42
|
+
parameters.update(gate_or_circuit_element.parameters)
|
|
43
|
+
|
|
44
|
+
return parameters
|
|
45
|
+
|
|
46
|
+
def get_hermitian_conjugate(self) -> "Self":
|
|
47
|
+
"""
|
|
48
|
+
Returns the Hermitian conjugate (inverse) of the circuit
|
|
49
|
+
:return: a new Circuit instance
|
|
50
|
+
"""
|
|
51
|
+
return type(self)(element.get_hermitian_conjugate() for element in reversed(self))
|
|
52
|
+
|
|
53
|
+
def generate_flat_gate_sequence(self) -> Iterator[Gate]:
|
|
54
|
+
"""
|
|
55
|
+
Generates a sequence of all gates in the circuit and its subcircuits.
|
|
56
|
+
|
|
57
|
+
:returns: A generator that loops over all gates in the circuit and its subcircuits.
|
|
58
|
+
"""
|
|
59
|
+
for element in self:
|
|
60
|
+
if isinstance(element, Gate):
|
|
61
|
+
yield element
|
|
62
|
+
else:
|
|
63
|
+
yield from element.generate_flat_gate_sequence()
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def from_json(cls, data: Sequence[Sequence[JSONType]]) -> "Self":
|
|
67
|
+
"""
|
|
68
|
+
Creates a Circuit from a list of elements in json
|
|
69
|
+
|
|
70
|
+
:param data: a list of elements in json format
|
|
71
|
+
:return: a Circuit object
|
|
72
|
+
"""
|
|
73
|
+
args = (
|
|
74
|
+
(
|
|
75
|
+
Gate.from_json(element_data)
|
|
76
|
+
if isinstance(element_data[0], str)
|
|
77
|
+
else cls.from_json(element_data)
|
|
78
|
+
)
|
|
79
|
+
for element_data in data
|
|
80
|
+
if element_data
|
|
81
|
+
)
|
|
82
|
+
return cls(args)
|
|
83
|
+
|
|
84
|
+
def to_json(self) -> list[JSONType]:
|
|
85
|
+
"""
|
|
86
|
+
Converts the Container to json
|
|
87
|
+
|
|
88
|
+
:return: a list with the elements of the circuit in json format
|
|
89
|
+
"""
|
|
90
|
+
return [json_wrap(element) for element in self]
|
|
91
|
+
|
|
92
|
+
def remap(self, context: Mapping = None, **kwargs) -> "Circuit":
|
|
93
|
+
"""
|
|
94
|
+
Creates a copy of the circuit where the remap has been applied to all parametrized gates
|
|
95
|
+
in the circuit (see `gates.RMixin.remap` for details).
|
|
96
|
+
|
|
97
|
+
:param context: a mapping of parameter names (strings) to parameter values (number-like
|
|
98
|
+
objects) or to new parameter names (strings).
|
|
99
|
+
"""
|
|
100
|
+
return Circuit((element.remap(context=context, **kwargs) for element in self))
|
|
101
|
+
|
|
102
|
+
def modify_angle(
|
|
103
|
+
self,
|
|
104
|
+
angle_map: Mapping[frozenset[Qubit], float],
|
|
105
|
+
gate_type: type[RMixin] = None,
|
|
106
|
+
parameter_name: str = None,
|
|
107
|
+
) -> "Self":
|
|
108
|
+
"""
|
|
109
|
+
Create a new circuit with the same subcircuits and gates as this one, except for the
|
|
110
|
+
rotation gates of the given gate type and with the given parameter_name as parameter,
|
|
111
|
+
for which the angle will be changed to the values given by the angle map, in function
|
|
112
|
+
of the qubit(s) on which the gate acts. If the qubit(s) are not included in the angle map,
|
|
113
|
+
then the angle is left unchanged.
|
|
114
|
+
|
|
115
|
+
If the gate type is not specified, then all rotation gates might be affected. If the
|
|
116
|
+
parameter name is not specified, then also gates without parameters might be affected.
|
|
117
|
+
|
|
118
|
+
:param angle_map: A mapping that provides a new angle for each of the qubit sets on which
|
|
119
|
+
the gates might act.
|
|
120
|
+
:param gate_type: Optional. If given, then only gates of this type will be updated. Other
|
|
121
|
+
gates will be copied into the new circuit without changes. Default is
|
|
122
|
+
None.
|
|
123
|
+
:param parameter_name: Optional. If given, then only gates with this parameter name will be
|
|
124
|
+
updated. Other gates will be copied into the new circuit without
|
|
125
|
+
changes. Default is None.
|
|
126
|
+
|
|
127
|
+
:returns: A new circuit with all the gates copied over from the current circuit, except for
|
|
128
|
+
selected rotation gates for which the rotation angle will have been modified.
|
|
129
|
+
Default is None.
|
|
130
|
+
|
|
131
|
+
"""
|
|
132
|
+
return type(self)(
|
|
133
|
+
element.modify_angle(angle_map, gate_type=gate_type, parameter_name=parameter_name)
|
|
134
|
+
for element in self
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
def __repr__(self):
|
|
138
|
+
args = ", ".join(repr(element) for element in self)
|
|
139
|
+
return f"{self.__class__.__name__}([{args}])"
|
|
140
|
+
|
|
141
|
+
def __add__(self, other):
|
|
142
|
+
return type(self)(list.__add__(self, other))
|
|
143
|
+
|
|
144
|
+
def __mul__(self, other):
|
|
145
|
+
return type(self)(list.__mul__(self, other))
|
|
146
|
+
|
|
147
|
+
def __rmul__(self, other):
|
|
148
|
+
return type(self)(list.__rmul__(self, other))
|
|
149
|
+
|
|
150
|
+
def __getitem__(self, key):
|
|
151
|
+
# Convert slices of circuits to again to Circuits
|
|
152
|
+
item = list.__getitem__(self, key)
|
|
153
|
+
return type(self)(item) if isinstance(key, slice) else item
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
CircuitElement = Union[Gate, Circuit]
|
|
157
|
+
HALF_PI = 0.5 * pi
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def convert_cnots_to_rzzs(circuit: Circuit) -> Circuit:
|
|
161
|
+
"""
|
|
162
|
+
ZZ rotations instead of CNOTs.
|
|
163
|
+
|
|
164
|
+
Replaces the standards CNOTs on the optimized circuit with an equivalent implementation based on
|
|
165
|
+
ZZ and local rotations. The resulting circuit will contain additional subcircuits to account
|
|
166
|
+
for the necessary Rx, Ry and Rz rotations.
|
|
167
|
+
|
|
168
|
+
:param circuit: a circuit containing moments with CNOT gates.
|
|
169
|
+
:type circuit: Circuit
|
|
170
|
+
:return: a new circuit where all CNOTs have been replaced by ZZ and local rotations.
|
|
171
|
+
:rtype: Circuit
|
|
172
|
+
"""
|
|
173
|
+
new_circuit = Circuit()
|
|
174
|
+
for subcircuit in circuit:
|
|
175
|
+
if not isinstance(subcircuit, Circuit):
|
|
176
|
+
# This situation can occur when gates and subcircuits were mixed together at the same
|
|
177
|
+
# level. We raise an error in this case, because this method expects gates to be grouped
|
|
178
|
+
# in moments (i.e. parallelizable subcircuits).
|
|
179
|
+
raise ParityOSException(
|
|
180
|
+
"Unexpected mix of gates and subcircuits encountered. Please "
|
|
181
|
+
"organize gates in moments (parallelizable subcircuits)."
|
|
182
|
+
)
|
|
183
|
+
elif all(isinstance(element, Gate) for element in subcircuit):
|
|
184
|
+
new_circuit.extend(_convert_moment(subcircuit))
|
|
185
|
+
else:
|
|
186
|
+
new_circuit.append(convert_cnots_to_rzzs(subcircuit))
|
|
187
|
+
|
|
188
|
+
return new_circuit
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _convert_moment(circuit) -> Iterator[Circuit]:
|
|
192
|
+
"""
|
|
193
|
+
A helper function for convert_cnots_to_rzzs.
|
|
194
|
+
Lowest level circuits are expanded and have cnots turned into rzzs. This results in additional
|
|
195
|
+
circuits to be added before and after the moment circuit to account for the necessary Rx, Ry
|
|
196
|
+
and Rz rotations.
|
|
197
|
+
|
|
198
|
+
:return: a list of circuits representing the original circuit where cnots have been converted to
|
|
199
|
+
rzz gates.
|
|
200
|
+
"""
|
|
201
|
+
before, moment, after1, after2 = Circuit(), Circuit(), Circuit(), Circuit()
|
|
202
|
+
# Representing a Cnot as a ZZ interaction requires the injection of a moment before and two
|
|
203
|
+
# moments after the original moment.
|
|
204
|
+
for gate in circuit:
|
|
205
|
+
if not isinstance(gate, CNOT):
|
|
206
|
+
# Copy the other gates directly to the new moment.
|
|
207
|
+
moment.append(gate)
|
|
208
|
+
else:
|
|
209
|
+
control, target = gate.make_args()
|
|
210
|
+
# Add the necessary rotation before the ZZ interaction.
|
|
211
|
+
before.append(Ry(target, -HALF_PI))
|
|
212
|
+
# Replace the CNOT with a ZZ rotation.
|
|
213
|
+
moment.append(Rzz(control, target, -HALF_PI))
|
|
214
|
+
# Add the remaining rotations after the ZZ interaction.
|
|
215
|
+
after1.append(Rx(target, HALF_PI))
|
|
216
|
+
after2.append(Rz(control, HALF_PI))
|
|
217
|
+
after2.append(Rz(target, HALF_PI))
|
|
218
|
+
|
|
219
|
+
return (subcircuit for subcircuit in [before, moment, after1, after2] if subcircuit)
|