iqm-client 31.8.0__py3-none-any.whl → 32.0.0__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.
- iqm/iqm_client/authentication.py +0 -3
- iqm/qiskit_iqm/examples/bell_measure.py +42 -17
- {iqm_client-31.8.0.dist-info → iqm_client-32.0.0.dist-info}/METADATA +1 -1
- {iqm_client-31.8.0.dist-info → iqm_client-32.0.0.dist-info}/RECORD +9 -15
- iqm/iqm_client/cli/__init__.py +0 -14
- iqm/iqm_client/cli/auth.py +0 -168
- iqm/iqm_client/cli/cli.py +0 -798
- iqm/iqm_client/cli/models.py +0 -40
- iqm/iqm_client/cli/token_manager.py +0 -196
- iqm/qiskit_iqm/examples/resonance_example.py +0 -83
- {iqm_client-31.8.0.dist-info → iqm_client-32.0.0.dist-info}/AUTHORS.rst +0 -0
- {iqm_client-31.8.0.dist-info → iqm_client-32.0.0.dist-info}/LICENSE.txt +0 -0
- {iqm_client-31.8.0.dist-info → iqm_client-32.0.0.dist-info}/WHEEL +0 -0
- {iqm_client-31.8.0.dist-info → iqm_client-32.0.0.dist-info}/entry_points.txt +0 -0
- {iqm_client-31.8.0.dist-info → iqm_client-32.0.0.dist-info}/top_level.txt +0 -0
iqm/iqm_client/cli/models.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# Copyright 2021-2023 IQM client developers
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
"""Pydantic models for IQM Client CLI files."""
|
|
15
|
-
|
|
16
|
-
from datetime import datetime
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
|
|
19
|
-
from pydantic import AnyUrl, BaseModel
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class ConfigFile(BaseModel):
|
|
23
|
-
"""Model of configuration file, used for validating JSON."""
|
|
24
|
-
|
|
25
|
-
auth_server_url: AnyUrl
|
|
26
|
-
realm: str
|
|
27
|
-
client_id: str
|
|
28
|
-
username: str | None = None
|
|
29
|
-
tokens_file: Path
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
class TokensFile(BaseModel):
|
|
33
|
-
"""Model of tokens file, used for validating JSON."""
|
|
34
|
-
|
|
35
|
-
pid: int | None = None
|
|
36
|
-
timestamp: datetime
|
|
37
|
-
access_token: str
|
|
38
|
-
refresh_token: str
|
|
39
|
-
refresh_status: str | None = None
|
|
40
|
-
auth_server_url: AnyUrl
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
# Copyright 2021-2023 IQM client developers
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
"""Token manager for authentication and authorization to IQM's quantum computers. Part of IQM Client CLI."""
|
|
15
|
-
|
|
16
|
-
from datetime import datetime
|
|
17
|
-
import json
|
|
18
|
-
import os
|
|
19
|
-
from pathlib import Path
|
|
20
|
-
import time
|
|
21
|
-
|
|
22
|
-
from iqm.iqm_client.cli.auth import AUTH_REQUESTS_TIMEOUT, ClientAuthenticationError, refresh_request
|
|
23
|
-
from iqm.iqm_client.cli.models import ConfigFile
|
|
24
|
-
from psutil import pid_exists
|
|
25
|
-
from requests.exceptions import ConnectionError, Timeout
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def daemonize_token_manager(cycle: int, config: ConfigFile, logfile: str | None = None) -> None:
|
|
29
|
-
"""Start a daemon process.
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
cycle: refresh cycle in seconds
|
|
33
|
-
config: IQM Client CLI configuration
|
|
34
|
-
logfile: path to file for writing errors
|
|
35
|
-
|
|
36
|
-
"""
|
|
37
|
-
import daemon
|
|
38
|
-
|
|
39
|
-
logfile = (
|
|
40
|
-
logfile
|
|
41
|
-
if logfile is not None
|
|
42
|
-
else f"{os.environ.get('XDG_STATE_HOME', f'{Path.home()}/.local/state')}/iqm-client-cli/token_manager.log"
|
|
43
|
-
)
|
|
44
|
-
os.makedirs(os.path.dirname(logfile), exist_ok=True)
|
|
45
|
-
with open(logfile, "w", encoding="UTF-8") as output:
|
|
46
|
-
with daemon.DaemonContext(stdout=output, stderr=output):
|
|
47
|
-
start_token_manager(cycle, config)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def start_token_manager(cycle: int, config: ConfigFile, single_run: bool = False) -> None:
|
|
51
|
-
"""Refresh tokens periodically.
|
|
52
|
-
|
|
53
|
-
For each refresh cycle new tokens are requested from auth server.
|
|
54
|
-
|
|
55
|
-
- If refresh is successful next refresh is attempted in the next cycle.
|
|
56
|
-
- If auth server does not respond refresh is attempted repeatedly until it succeeds or
|
|
57
|
-
the existing refresh token expires.
|
|
58
|
-
- If auth server responds but returns an error code or invalid tokens token manager is stopped.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
cycle: refresh cycle in seconds
|
|
62
|
-
config: IQM Client CLI configuration
|
|
63
|
-
single_run: if True, refresh tokens only once and exit; otherwise repeat refreshing indefinitely
|
|
64
|
-
|
|
65
|
-
"""
|
|
66
|
-
while True:
|
|
67
|
-
tokens_file = str(config.tokens_file)
|
|
68
|
-
tokens = read_tokens(tokens_file)
|
|
69
|
-
|
|
70
|
-
new_tokens, status, sleep_time = refresh_tokens(config, tokens, cycle)
|
|
71
|
-
if new_tokens is None:
|
|
72
|
-
break
|
|
73
|
-
|
|
74
|
-
write_tokens(tokens_file, str(config.auth_server_url), status, **new_tokens)
|
|
75
|
-
|
|
76
|
-
if single_run:
|
|
77
|
-
break
|
|
78
|
-
|
|
79
|
-
time.sleep(sleep_time)
|
|
80
|
-
|
|
81
|
-
print(f"{datetime.now().strftime('%m/%d/%Y %H:%M:%S')}: Token manager stopped")
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def read_tokens(path_to_tokens_file: str) -> dict:
|
|
85
|
-
"""Read current tokens from the tokens file.
|
|
86
|
-
|
|
87
|
-
Args:
|
|
88
|
-
path_to_tokens_file: path to the tokens file
|
|
89
|
-
|
|
90
|
-
Returns:
|
|
91
|
-
dict containing the tokens
|
|
92
|
-
|
|
93
|
-
"""
|
|
94
|
-
with open(path_to_tokens_file, "r", encoding="utf-8") as file:
|
|
95
|
-
tokens = json.load(file)
|
|
96
|
-
return tokens
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
def refresh_tokens(config: ConfigFile, current_tokens: dict, cycle: int) -> tuple[dict | None, bool, int]:
|
|
100
|
-
"""Request new tokens from auth server.
|
|
101
|
-
|
|
102
|
-
Args:
|
|
103
|
-
config: IQM Client CLI configuration
|
|
104
|
-
current_tokens: dict containing the current tokens from the tokens file
|
|
105
|
-
cycle: refresh cycle length in seconds
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
* dict containing new tokens or current tokens if auth server could not be connected or
|
|
109
|
-
None if auth server refused to provide new tokens
|
|
110
|
-
* bool, True if tokens were refreshed successfully, False otherwise
|
|
111
|
-
* time to sleep before next refresh attempt
|
|
112
|
-
|
|
113
|
-
"""
|
|
114
|
-
access_token = current_tokens.get("access_token", "")
|
|
115
|
-
refresh_token = current_tokens.get("refresh_token", "")
|
|
116
|
-
try:
|
|
117
|
-
tokens = refresh_request(str(config.auth_server_url), config.realm, config.client_id, refresh_token)
|
|
118
|
-
status = True
|
|
119
|
-
sleep_time = cycle
|
|
120
|
-
log_timestamp = datetime.now().strftime("%m/%d/%Y %H:%M:%S")
|
|
121
|
-
print(f"{log_timestamp}: Tokens refreshed successfully.")
|
|
122
|
-
except (Timeout, ConnectionError) as ex:
|
|
123
|
-
# No connection to auth server or auth server did not respond, keep current tokens
|
|
124
|
-
tokens = {"access_token": access_token, "refresh_token": refresh_token}
|
|
125
|
-
status = False
|
|
126
|
-
if isinstance(ex, ConnectionError):
|
|
127
|
-
sleep_time = AUTH_REQUESTS_TIMEOUT
|
|
128
|
-
else:
|
|
129
|
-
sleep_time = 1
|
|
130
|
-
log_timestamp = datetime.now().strftime("%m/%d/%Y %H:%M:%S")
|
|
131
|
-
print(f"{log_timestamp}: No response from auth server: {ex}")
|
|
132
|
-
except ClientAuthenticationError as ex:
|
|
133
|
-
# Auth server responded but no valid tokens were received
|
|
134
|
-
tokens = None
|
|
135
|
-
status = False
|
|
136
|
-
sleep_time = cycle
|
|
137
|
-
log_timestamp = datetime.now().strftime("%m/%d/%Y %H:%M:%S")
|
|
138
|
-
print(f"{log_timestamp}: Failed to authenticate with auth server: {ex}")
|
|
139
|
-
|
|
140
|
-
return tokens, status, sleep_time
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def write_tokens(
|
|
144
|
-
path_to_tokens_file: str,
|
|
145
|
-
auth_server_url: str,
|
|
146
|
-
status: bool,
|
|
147
|
-
*,
|
|
148
|
-
access_token: str = "",
|
|
149
|
-
refresh_token: str = "",
|
|
150
|
-
) -> None:
|
|
151
|
-
"""Write new tokens into the tokens file.
|
|
152
|
-
|
|
153
|
-
Args:
|
|
154
|
-
path_to_tokens_file: path to the tokens file
|
|
155
|
-
auth_server_url: base URL of the auth server
|
|
156
|
-
status: refresh status, True when successful, False otherwise
|
|
157
|
-
access_token: new access token
|
|
158
|
-
refresh_token: new refresh token
|
|
159
|
-
|
|
160
|
-
"""
|
|
161
|
-
tokens_json = json.dumps(
|
|
162
|
-
{
|
|
163
|
-
"pid": os.getpid(),
|
|
164
|
-
"timestamp": datetime.now().isoformat(),
|
|
165
|
-
"refresh_status": "SUCCESS" if status else "FAILED",
|
|
166
|
-
"access_token": access_token,
|
|
167
|
-
"refresh_token": refresh_token,
|
|
168
|
-
"auth_server_url": auth_server_url,
|
|
169
|
-
}
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
try:
|
|
173
|
-
Path(path_to_tokens_file).parent.mkdir(parents=True, exist_ok=True)
|
|
174
|
-
with open(Path(path_to_tokens_file), "w", encoding="UTF-8") as file:
|
|
175
|
-
file.write(tokens_json)
|
|
176
|
-
except OSError as error:
|
|
177
|
-
print("Error writing tokens file", error)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def check_token_manager(tokens_file: str) -> int | None:
|
|
181
|
-
"""Check whether a token manager related to the given tokens_file is running.
|
|
182
|
-
|
|
183
|
-
Args:
|
|
184
|
-
tokens_file: Path to a tokens JSON file.
|
|
185
|
-
|
|
186
|
-
Returns:
|
|
187
|
-
Optional[int]: PID of the process if process is running, None otherwise.
|
|
188
|
-
|
|
189
|
-
"""
|
|
190
|
-
with open(tokens_file, "r", encoding="utf-8") as file:
|
|
191
|
-
tokens_data = json.load(file)
|
|
192
|
-
pid = tokens_data["pid"] if "pid" in tokens_data else None
|
|
193
|
-
|
|
194
|
-
if pid and pid_exists(pid):
|
|
195
|
-
return pid
|
|
196
|
-
return None
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# Copyright 2024 Qiskit on IQM developers
|
|
2
|
-
#
|
|
3
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
# you may not use this file except in compliance with the License.
|
|
5
|
-
# You may obtain a copy of the License at
|
|
6
|
-
#
|
|
7
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
#
|
|
9
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
# See the License for the specific language governing permissions and
|
|
13
|
-
# limitations under the License.
|
|
14
|
-
"""This file is an example of using Qiskit on IQM to run a simple but non-trivial quantum circuit on
|
|
15
|
-
Resonance, the IQM quantum cloud service.
|
|
16
|
-
See the Qiskit on IQM user guide for instructions:
|
|
17
|
-
https://docs.meetiqm.com/iqm-client/user_guide_qiskit.html
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
import argparse
|
|
21
|
-
|
|
22
|
-
from iqm.qiskit_iqm import IQMProvider
|
|
23
|
-
from qiskit import QuantumCircuit, transpile
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def resonance_example(server_url: str, api_token: str | None) -> dict[str, int]:
|
|
27
|
-
"""Run a circuit via IQM Resonance.
|
|
28
|
-
|
|
29
|
-
Args:
|
|
30
|
-
server_url: URL of the IQM Resonance QC used for execution
|
|
31
|
-
api_token: IQM Resonance API token for authentication
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
a mapping of bitstrings representing qubit measurement results to counts for each result
|
|
35
|
-
|
|
36
|
-
"""
|
|
37
|
-
SHOTS = 1000
|
|
38
|
-
|
|
39
|
-
# Initialize a backend without metrics as IQMClient._get_calibration_quality_metrics is not supported by resonance
|
|
40
|
-
backend = IQMProvider(server_url, token=api_token).get_backend()
|
|
41
|
-
|
|
42
|
-
# Just to make sure that "get_static_quantum_architecture" method works
|
|
43
|
-
static_quantum_architecture = backend.client.get_static_quantum_architecture()
|
|
44
|
-
print(f"static_quantum_architecture={static_quantum_architecture}")
|
|
45
|
-
|
|
46
|
-
# Define a quantum circuit
|
|
47
|
-
num_qb = min(backend.num_qubits, 5) # use at most 5 qubits
|
|
48
|
-
qc = QuantumCircuit(num_qb)
|
|
49
|
-
|
|
50
|
-
qc.h(0)
|
|
51
|
-
for qb in range(1, num_qb):
|
|
52
|
-
qc.cx(0, qb)
|
|
53
|
-
qc.barrier()
|
|
54
|
-
qc.measure_all()
|
|
55
|
-
|
|
56
|
-
# Transpile the circuit
|
|
57
|
-
qc_transpiled = transpile(qc, backend)
|
|
58
|
-
print(qc_transpiled.draw(output="text"))
|
|
59
|
-
|
|
60
|
-
# Run the circuit
|
|
61
|
-
job = backend.run(qc_transpiled, shots=SHOTS)
|
|
62
|
-
return job.result().get_counts()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if __name__ == "__main__":
|
|
66
|
-
argparser = argparse.ArgumentParser()
|
|
67
|
-
argparser.add_argument(
|
|
68
|
-
"--url",
|
|
69
|
-
help="URL of the IQM Resonance QC",
|
|
70
|
-
# For example https://cocos.resonance.meetiqm.com/garnet
|
|
71
|
-
default="https://cocos.resonance.meetiqm.com/<RESONANCE QC NAME>",
|
|
72
|
-
)
|
|
73
|
-
argparser.add_argument(
|
|
74
|
-
"--token",
|
|
75
|
-
help="IQM Resonance access token",
|
|
76
|
-
# Provide the API token explicitly or set it as an environment variable
|
|
77
|
-
# following the https://docs.meetiqm.com/iqm-client/user_guide_qiskit.html#authentication
|
|
78
|
-
default="<INSERT YOUR TOKEN>",
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
args = argparser.parse_args()
|
|
82
|
-
results = resonance_example(args.url, args.token)
|
|
83
|
-
print(results)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|