azure-quantum 1.0.1__py3-none-any.whl → 1.1.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,393 +0,0 @@
1
- ##
2
- # Copyright (c) Microsoft Corporation. All rights reserved.
3
- # Licensed under the MIT License.
4
- ##
5
-
6
- from __future__ import annotations
7
- import logging
8
- import io
9
- import gzip
10
- import json
11
- import numpy
12
- import os
13
- import tarfile
14
-
15
-
16
- from typing import List, Tuple, Union, Dict, Optional, TYPE_CHECKING
17
- from enum import Enum
18
- from azure.quantum.optimization import TermBase, Term
19
- from azure.quantum.storage import (
20
- ContainerClient,
21
- download_blob,
22
- BlobClient,
23
- download_blob_metadata,
24
- download_blob_properties
25
- )
26
- from azure.quantum.job.base_job import ContentType
27
- from azure.quantum.job.job import Job
28
- from azure.quantum.target.target import Target
29
-
30
- logger = logging.getLogger(__name__)
31
-
32
- __all__ = ["Problem", "ProblemType"]
33
-
34
- if TYPE_CHECKING:
35
- from azure.quantum.workspace import Workspace
36
-
37
- class ProblemType(str, Enum):
38
- pubo = 0
39
- ising = 1
40
-
41
-
42
- class Problem:
43
- """Problem to submit to the service.
44
-
45
- :param name: Problem name
46
- :type name: str
47
- When passing None, it will default to "Optimization problem"
48
- :param terms: Problem terms, depending on solver.
49
- Defaults to None
50
- :type terms: Optional[List[TermBase]], optional
51
- :param init_config: Optional configuration details, depending on solver.
52
- Defaults to None
53
- :type init_config: Optional[Dict[str,int]], optional
54
- :param problem_type: Problem type (ProblemType.pubo or
55
- ProblemType.ising), defaults to ProblemType.ising
56
- :type problem_type: ProblemType, optional
57
- :param content_type: Content type, eg: application/json. Default is application/json
58
- :type content_type: ContentType, optional
59
- """
60
-
61
- def __init__(
62
- self,
63
- name: str,
64
- terms: Optional[List[TermBase]] = None,
65
- init_config: Optional[Dict[str, int]] = None,
66
- problem_type: ProblemType = ProblemType.ising,
67
- content_type: Optional[ContentType] = ContentType.json
68
- ):
69
- self.name = name or "Optimization problem"
70
- self.problem_type = problem_type
71
- self.init_config = init_config
72
- self.uploaded_blob_uri = None
73
- self.uploaded_blob_params = None
74
- self.content_type = content_type
75
-
76
- # each type of term has its own section for quicker serialization
77
- self.terms = []
78
-
79
- # set the terms
80
- if terms:
81
- for term in terms:
82
- self.terms.append(term)
83
-
84
- """
85
- Constant thresholds used to determine if a problem is "large".
86
- """
87
- NUM_VARIABLES_LARGE = 2500
88
- NUM_TERMS_LARGE = 1e6
89
-
90
-
91
- def serialize(self) -> Union[str, list]:
92
- """Wrapper function for serializing to json
93
- """
94
- return self.to_json()
95
-
96
- def to_json(self) -> str:
97
- """Serializes the problem to a JSON string"""
98
- result = {
99
- "metadata": {
100
- "name": self.name,
101
- },
102
- "cost_function": {
103
- "version": "1.1" if self.init_config else "1.0",
104
- "type": self.problem_type.name,
105
- "terms": [term.to_dict() for term in self.terms]
106
- }
107
- }
108
-
109
- if self.init_config:
110
- result["cost_function"]["initial_configuration"] = self.init_config
111
-
112
- return json.dumps(result)
113
-
114
-
115
- @classmethod
116
- def from_json(
117
- cls,
118
- input_problem: str,
119
- name: Optional[str] = None
120
- ) -> Problem:
121
- """Deserializes the problem from a
122
- json serialized with Problem.serialize()
123
-
124
- :param input_problem:
125
- the json string to be deserialized to a `Problem` instance
126
- :type problem_msgs: str
127
- :param
128
- :param name:
129
- The name of the problem is optional, since it will try
130
- to read the serialized name from the json payload.
131
- If this parameter is not empty, it will use it as the
132
- problem name ignoring the serialized value.
133
- :type name: Optional[str]
134
- """
135
- result = json.loads(input_problem)
136
-
137
- if name is None:
138
- metadata = result.get("metadata")
139
- if metadata is not None:
140
- name = metadata.get("name")
141
-
142
- terms = [Term.from_dict(t) for t in result["cost_function"]["terms"]] if "terms" in result["cost_function"] else []
143
-
144
- problem = cls(
145
- name=name,
146
- terms=terms,
147
- problem_type=ProblemType[result["cost_function"]["type"]],
148
- )
149
-
150
- if "initial_configuration" in result["cost_function"]:
151
- problem.init_config = result["cost_function"]["initial_configuration"]
152
-
153
- return problem
154
-
155
- @classmethod
156
- def deserialize(
157
- cls,
158
- input_problem: Union[str, list],
159
- name: Optional[str] = None,
160
- content_type: Optional[ContentType] = None) -> Problem:
161
- """Deserializes the problem from a
162
- JSON string serialized with Problem.serialize()
163
- Also used to deserialize the messages downloaded from the blob
164
-
165
- :param input_problem:
166
- The json string
167
- :type input_problem: Union[str,list]
168
- :param
169
- :param name:
170
- The name of the problem is optional, since it will try
171
- to read the serialized name from the json payload.
172
- If this parameter is not empty, it will use it as the
173
- problem name ignoring the serialized value.
174
- :type name: Optional[str]
175
- :param content_type: The content type of the input problem data
176
- :type: Optional, ContentType
177
- """
178
- return cls.from_json(input_problem, name)
179
-
180
- def add_term(self, c: Union[int, float], indices: List[int]):
181
- """Adds a single monomial term to the `Problem` representation
182
-
183
- :param c: The cost or weight of this term
184
- :type c: int, float
185
- :param indices: The variable indices that are in this term
186
- :type indices: List[int]
187
- """
188
- self.terms.append(Term(indices=indices, c=c))
189
- self.uploaded_blob_uri = None
190
-
191
- def add_terms(
192
- self,
193
- terms: List[Term]
194
- ):
195
- """Adds a list of monomial terms to the `Problem` representation
196
-
197
- :param terms: The list of terms to add to the problem
198
- """
199
- self.terms += terms
200
- self.uploaded_blob_uri = None
201
-
202
-
203
- def to_blob(self) -> bytes:
204
- """Convert problem data to a binary blob.
205
-
206
- :return: Blob data
207
- :rtype: bytes
208
- """
209
- input_problem = self.serialize()
210
- debug_input_string = input_problem if type(input_problem) is str else b''.join( input_problem).decode('latin-1')
211
- logger.debug("Input Problem: " + debug_input_string)
212
- data = io.BytesIO()
213
- with gzip.GzipFile(fileobj=data, mode="w") as fo:
214
- fo.write(input_problem.encode())
215
-
216
- return data.getvalue()
217
-
218
- def _blob_name(self):
219
- import uuid
220
- return "{}-{}".format(self.name, uuid.uuid1())
221
-
222
- def upload(
223
- self,
224
- workspace: "Workspace",
225
- container_name: str = "optimization-problems",
226
- blob_name: str = "inputData",
227
- container_uri: str = None,
228
- ):
229
- """Uploads an optimization problem instance to
230
- the cloud storage linked with the Workspace.
231
-
232
- :param workspace: interaction terms of the problem.
233
- :type workspace: Workspace
234
- :param container_name: Container name, defaults to "optimization-problems"
235
- :type container_name: str, optional
236
- :param blob_name: Blob name, defaults to None
237
- :type blob_name: str, optional
238
- :param container_uri: Optional container URI
239
- :type container_uri: str
240
- :return: uri of the uploaded problem
241
- :rtype: str
242
- """
243
- blob_params = [workspace, container_name, blob_name]
244
- if self.uploaded_blob_uri and self.uploaded_blob_params == blob_params:
245
- return self.uploaded_blob_uri
246
-
247
- if blob_name is None:
248
- blob_name = self._blob_name()
249
-
250
- encoding = "gzip"
251
- content_type = self.content_type
252
-
253
- blob = self.to_blob()
254
- if container_uri is None:
255
- container_uri = workspace.get_container_uri(
256
- container_name=container_name
257
- )
258
- input_data_uri = Job.upload_input_data(
259
- input_data=blob,
260
- blob_name=blob_name,
261
- container_uri=container_uri,
262
- encoding=encoding,
263
- content_type= content_type
264
- )
265
- self.uploaded_blob_params = blob_params
266
- self.uploaded_blob_uri = input_data_uri
267
- return input_data_uri
268
-
269
- def set_fixed_variables(
270
- self, fixed_variables: Union[Dict[int, int], Dict[str, int]]
271
- ) -> Problem:
272
- """Transforms the current problem with a set of fixed
273
- variables and returns the new modified problem.
274
- The original Problem instance is untouched.
275
-
276
- :param fixed_variables:
277
- The dictionary of variable ids and their fixed state
278
- """
279
- if len(fixed_variables) == 0:
280
- raise RuntimeError(
281
- "Error: fixed_variables is empty - \
282
- please specify at least one fixed variable"
283
- )
284
-
285
- fixed_transformed = {
286
- int(k): fixed_variables[k] for k in fixed_variables
287
- } # if ids are given in string form, convert them to int
288
- new_terms = []
289
-
290
- constant = 0
291
- for term in self.terms:
292
- reduced_term = term.reduce_by_variable_state(fixed_transformed)
293
- if reduced_term:
294
- if not isinstance(reduced_term, Term) or len(reduced_term.ids) > 0:
295
- new_terms.append(reduced_term)
296
- else:
297
- # reduced to a constant term
298
- constant += reduced_term.c
299
-
300
- if constant:
301
- new_terms.append(Term(c=constant, indices=[]))
302
-
303
- new_init_config = None
304
- if self.init_config:
305
- new_init_config = {
306
- k: self.init_config[k]
307
- for k in self.init_config
308
- if int(k) not in fixed_transformed
309
- }
310
-
311
- return Problem(
312
- self.name,
313
- terms=new_terms,
314
- init_config=new_init_config,
315
- problem_type=self.problem_type,
316
- )
317
-
318
- def _evaluate(self, configuration, term_list):
319
- total = 0
320
- if term_list:
321
- for term in term_list:
322
- total += term.evaluate(configuration)
323
- return total
324
-
325
- def evaluate(self, configuration: Union[Dict[int, int], Dict[str, int]]) -> float:
326
- """Given a configuration/variable assignment,
327
- return the cost function value of this problem.
328
-
329
- :param configuration: The dictionary of
330
- variable ids to their assigned value
331
- """
332
- configuration_transformed = {
333
- int(k): configuration[k] for k in configuration
334
- } # if ids are given in string form, convert them to int
335
-
336
- total_cost = 0
337
- for terms in self.terms:
338
- total_cost += self._evaluate(configuration_transformed, terms)
339
-
340
- return total_cost
341
-
342
- def is_large(self) -> bool:
343
- """Determines if the current problem is large.
344
- "large" is an arbitrary threshold and can be easily changed.
345
- Based on usage data, we have defined a
346
- large problem to be NUM_VARIABLES_LARGE+
347
- variables AND NUM_TERMS_LARGE+ terms.
348
- """
349
-
350
- set_vars = set()
351
- total_term_count = 0
352
- for term in self.terms:
353
- if isinstance(term, Term):
354
- set_vars.update(term.ids)
355
- total_term_count += 1
356
-
357
- return (
358
- len(set_vars) >= Problem.NUM_VARIABLES_LARGE
359
- and total_term_count >= Problem.NUM_TERMS_LARGE
360
- )
361
-
362
- def download(self, workspace: "Workspace"):
363
- """Downloads the uploaded problem as an instance of `Problem`"""
364
- if not self.uploaded_blob_uri:
365
- raise Exception("Problem may not be downloaded before it is uploaded")
366
- blob_client = BlobClient.from_blob_url(self.uploaded_blob_uri)
367
- container_client = ContainerClient.from_container_url(
368
- workspace._get_linked_storage_sas_uri(blob_client.container_name)
369
- )
370
- blob_name = blob_client.blob_name
371
- blob = container_client.get_blob_client(blob_name)
372
- contents = download_blob(blob.url)
373
- blob_properties = download_blob_properties(blob.url)
374
- content_type = blob_properties.content_type
375
- return Problem.deserialize(contents, self.name, content_type)
376
-
377
- def get_terms(self, id: int) -> List[TermBase]:
378
- """Given an index the function will return
379
- a list of terms with that index
380
- """
381
- terms = []
382
- if self.terms != []:
383
- for term in self.terms:
384
- if isinstance(term, Term):
385
- if id in term.ids:
386
- terms.append(term)
387
- return terms
388
- else:
389
- raise Exception(
390
- "There are currently no terms in this problem. \
391
- Please download the problem on the client or add terms to the \
392
- problem to perform this operation"
393
- )
@@ -1,10 +0,0 @@
1
- # coding=utf-8
2
- ##
3
- # Copyright (c) Microsoft Corporation. All rights reserved.
4
- # Licensed under the MIT License.
5
- ##
6
- import warnings
7
- warnings.warn("The azure.quantum.optimization.solvers namespace will be deprecated. \
8
- Please use azure.quantum.target instead.")
9
-
10
- from azure.quantum.target.solvers import Solver