iqm-client 31.7.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.
@@ -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)