azure-quantum 2.5.0.dev0__py3-none-any.whl → 2.5.0.dev2__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.
@@ -10,6 +10,7 @@ from .job import MicrosoftElementsDftJob
10
10
  from pathlib import Path
11
11
  import copy
12
12
  import json
13
+ from collections import defaultdict
13
14
 
14
15
 
15
16
  class MicrosoftElementsDft(Target):
@@ -78,22 +79,31 @@ class MicrosoftElementsDft(Target):
78
79
 
79
80
  if isinstance(input_data, list):
80
81
 
81
- qcschema_data = self._assemble_qcshema_from_files(input_data, input_params)
82
-
83
- qcschema_blobs = {}
84
- for i in range(len(qcschema_data)):
85
- qcschema_blobs[f"inputData_{i}"] = self._encode_input_data(qcschema_data[i])
86
-
87
- toc_str = self._create_table_of_contents(input_data, list(qcschema_blobs.keys()))
82
+ if all(isinstance(task,str) for task in input_data):
83
+ qcschema_data = self.assemble_qcschema_from_files(input_data, input_params)
84
+
85
+ qcschema_blobs = {}
86
+ for i in range(len(qcschema_data)):
87
+ qcschema_blobs[f"inputData_{i}"] = self._encode_input_data(qcschema_data[i])
88
+
89
+ toc_str = self._create_table_of_contents(input_data, list(qcschema_blobs.keys()))
90
+ elif all(isinstance(task,dict) for task in input_data):
91
+ qcschema_blobs = {}
92
+ for i in range(len(input_data)):
93
+ qcschema_blobs[f"inputData_{i}"] = self._encode_input_data(input_data[i])
94
+ toc_str = '{"description": "QcSchema Objects were given for input."}'
95
+ else:
96
+ raise ValueError(f"Unsupported batch submission. Please use List[str] or List[dict].")
88
97
  toc = self._encode_input_data(toc_str)
89
98
 
99
+ input_params = {} if input_params is None else input_params
90
100
  return self._get_job_class().from_input_data_container(
91
101
  workspace=self.workspace,
92
102
  name=name,
93
103
  target=self.name,
94
104
  input_data=toc,
95
105
  batch_input_blobs=qcschema_blobs,
96
- input_params={ 'numberOfFiles': len(qcschema_data), "inputFiles": list(qcschema_blobs.keys()), **input_params },
106
+ input_params={ 'numberOfFiles': len(input_data), "inputFiles": list(qcschema_blobs.keys()), **input_params },
97
107
  content_type=kwargs.pop('content_type', self.content_type),
98
108
  encoding=kwargs.pop('encoding', self.encoding),
99
109
  provider_id=self.provider_id,
@@ -114,16 +124,22 @@ class MicrosoftElementsDft(Target):
114
124
 
115
125
 
116
126
  @classmethod
117
- def _assemble_qcshema_from_files(self, input_data: List[str], input_params: Dict) -> str:
127
+ def assemble_qcschema_from_files(self, input_data: Union[List[str]], input_params: Dict) -> List[Dict]:
118
128
  """
119
- Convert a list of files to a list of qcshema objects serialized in json.
129
+ Convert a list of files to a list of QcSchema objects that are ready for submission.
130
+
131
+ :param input_data: Input data
132
+ :type input_data: List[str]
133
+ :param input_params: Input parameters
134
+ :type input_params: Dict[str, Any]
135
+ :rtype: List[Dict]
120
136
  """
121
137
 
138
+ self._check_file_paths(input_data)
139
+
122
140
  qcshema_objects = []
123
141
  for file in input_data:
124
142
  file_path = Path(file)
125
- if not file_path.exists():
126
- raise FileNotFoundError(f"File {file} does not exist.")
127
143
 
128
144
  file_data = file_path.read_text()
129
145
  if file_path.suffix == '.xyz':
@@ -136,17 +152,44 @@ class MicrosoftElementsDft(Target):
136
152
  with open(file_path, 'r') as f:
137
153
  qcshema_objects.append( json.load(f) )
138
154
  else:
139
- raise ValueError(f"File type '{file_path.suffix}' for file '{file_path}' is not supported. Please use xyz or QcSchema file formats.")
155
+ raise ValueError(f"File type '{file_path.suffix}' for file '{file_path}' is not supported.")
140
156
 
141
157
  return qcshema_objects
142
158
 
159
+ @classmethod
160
+ def _check_file_paths( self, input_data: List[str]):
161
+ """Check the file types and make sure they are supported by our parsers."""
162
+
163
+ warn_task_count = 1000
164
+ if len(input_data) >= warn_task_count:
165
+ warnings.warn(f'Number of tasks is greater than {warn_task_count}.')
166
+
167
+ supported_ext = ['.xyz', '.json']
168
+ prev_ext = None
169
+ for path_str in input_data:
170
+ path = Path(path_str)
171
+
172
+ if not path.exists():
173
+ raise FileNotFoundError(f"File {path_str} does not exist.")
174
+
175
+ if path.suffix not in supported_ext:
176
+ raise ValueError(f"'{path.suffix}' file type is not supported. Please use one of {supported_ext}.")
177
+
178
+ if prev_ext is not None and prev_ext != path.suffix:
179
+ raise ValueError(f"Multiple file types were provided ('{path.suffix}', '{prev_ext}'). Please submit only one file type.")
180
+ else:
181
+ prev_ext = path.suffix
182
+
183
+
143
184
  @classmethod
144
185
  def _new_qcshema( self, input_params: Dict[str,Any], mol: Dict[str,Any], ) -> Dict[str, Any]:
145
186
  """
146
187
  Create a new default qcshema object.
147
188
  """
148
189
 
149
- if input_params.get("driver") == "go":
190
+ self._sanity_check_params(input_params, mol)
191
+
192
+ if input_params.get("driver").lower() == "go":
150
193
  copy_input_params = copy.deepcopy(input_params)
151
194
  copy_input_params["driver"] = "gradient"
152
195
  new_object = {
@@ -154,11 +197,11 @@ class MicrosoftElementsDft(Target):
154
197
  "schema_version": 1,
155
198
  "initial_molecule": mol,
156
199
  }
157
- if copy_input_params.get("keywords") and copy_input_params["keywords"].get("geometryOptimization"):
158
- new_object["keywords"] = copy_input_params["keywords"].pop("geometryOptimization")
200
+ if copy_input_params.get("go_keywords"):
201
+ new_object["keywords"] = copy_input_params.pop("go_keywords")
159
202
  new_object["input_specification"] = copy_input_params
160
203
  return new_object
161
- elif input_params.get("driver") == "bomd":
204
+ elif input_params.get("driver").lower() == "bomd":
162
205
  copy_input_params = copy.deepcopy(input_params)
163
206
  copy_input_params["driver"] = "gradient"
164
207
  new_object = {
@@ -166,8 +209,8 @@ class MicrosoftElementsDft(Target):
166
209
  "schema_version": 1,
167
210
  "initial_molecule": mol,
168
211
  }
169
- if copy_input_params.get("keywords") and copy_input_params["keywords"].get("molecularDynamics"):
170
- new_object["keywords"] = copy_input_params["keywords"].pop("molecularDynamics")
212
+ if copy_input_params.get("bomd_keywords"):
213
+ new_object["keywords"] = copy_input_params.pop("bomd_keywords")
171
214
  new_object["input_specification"] = copy_input_params
172
215
  return new_object
173
216
  else:
@@ -178,7 +221,34 @@ class MicrosoftElementsDft(Target):
178
221
  "molecule": mol,
179
222
  })
180
223
  return new_object
181
-
224
+
225
+ @classmethod
226
+ def _sanity_check_params(self, input_params, mol):
227
+
228
+ # QM/MM is not supported for GO, BOMD and Hessian.
229
+ driver = input_params.get("driver",'').lower()
230
+ if driver in ["go", "bomd", "hessian"]:
231
+ if "extras" in mol and "mm_charges" in mol["extras"]:
232
+ raise ValueError(f"'{driver}' does not support QM/MM.")
233
+
234
+ # Top level params
235
+ self._check_dict_for_required_keys(input_params, 'input_params', ['driver', 'model'])
236
+
237
+ # Check Model params
238
+ self._check_dict_for_required_keys(input_params['model'], 'input_params["model"]', ['method', 'basis'])
239
+
240
+ supported_drivers = ['energy', 'gradient', 'hessian', 'go', 'bomd']
241
+ if input_params['driver'] not in supported_drivers:
242
+ raise ValueError(f"Driver ({input_params['driver']}) is not supported. Please use one of {supported_drivers}.")
243
+
244
+
245
+ @classmethod
246
+ def _check_dict_for_required_keys(self, input_params: dict, dict_name: str, required_keys: list[str]):
247
+ """Check dictionary for required keys and if it doesn't have then raise ValueError."""
248
+
249
+ for required_key in required_keys:
250
+ if required_key not in input_params.keys():
251
+ raise ValueError(f"Required key ({required_key}) was not provided in {dict_name}.")
182
252
 
183
253
  @classmethod
184
254
  def _xyz_to_qcschema_mol(self, file_data: str ) -> Dict[str, Any]:
@@ -191,23 +261,33 @@ class MicrosoftElementsDft(Target):
191
261
  raise ValueError("Invalid xyz format.")
192
262
  n_atoms = int(lines.pop(0))
193
263
  comment = lines.pop(0)
194
- mol = {
195
- "geometry": [],
196
- "symbols": [],
197
- }
264
+ mol = defaultdict(list)
265
+ mol['extras'] = defaultdict(list)
198
266
  bohr_to_angstrom = 0.52917721092
199
267
  for line in lines:
200
268
  if line:
201
269
  elements = line.split()
202
- if len(elements) < 4:
270
+ if len(elements) == 4:
271
+ symbol, x, y, z = elements
272
+ mol["symbols"].append(symbol)
273
+ mol["geometry"] += [float(x)/bohr_to_angstrom, float(y)/bohr_to_angstrom, float(z)/bohr_to_angstrom]
274
+ elif len(elements) == 5:
275
+ symbol, x, y, z, q = elements
276
+ if symbol[0] != '-':
277
+ raise ValueError("Invalid xyz format. Molecular Mechanics atoms requires '-' at the beginning of the atom type.")
278
+ mol["extras"]["mm_symbols"].append(symbol.replace('-', ''))
279
+ mol["extras"]["mm_geometry"] += [float(x)/bohr_to_angstrom, float(y)/bohr_to_angstrom, float(z)/bohr_to_angstrom]
280
+ mol["extras"]["mm_charges"].append(float(q))
281
+ else:
203
282
  raise ValueError("Invalid xyz format.")
204
- symbol, x, y, z = elements
205
- mol["symbols"].append(symbol)
206
- mol["geometry"] += [float(x)/bohr_to_angstrom, float(y)/bohr_to_angstrom, float(z)/bohr_to_angstrom]
207
283
  else:
208
284
  break
285
+
286
+ # Convert defaultdict to dict
287
+ mol = dict(mol)
288
+ mol["extras"] = dict(mol["extras"])
209
289
 
210
- if len(mol["symbols"]) != n_atoms:
290
+ if len(mol["symbols"])+len(mol["extras"].get("mm_symbols",[])) != n_atoms:
211
291
  raise ValueError("Number of inputs does not match the number of atoms in xyz file.")
212
292
 
213
293
  return mol
@@ -32,8 +32,6 @@ class RigettiTarget(str, Enum):
32
32
 
33
33
  ANKAA_3 = "rigetti.qpu.ankaa-3"
34
34
 
35
- ANKAA_9Q_3 = "rigetti.qpu.ankaa-9q-3"
36
-
37
35
  def simulators() -> List[str]:
38
36
  """Returns a list of simulator targets"""
39
37
  return [
@@ -44,7 +42,6 @@ class RigettiTarget(str, Enum):
44
42
  """Returns a list of QPU targets"""
45
43
  return [
46
44
  RigettiTarget.ANKAA_3.value,
47
- RigettiTarget.ANKAA_9Q_3.value,
48
45
  ]
49
46
 
50
47
  def num_qubits(target_name) -> int:
@@ -54,8 +51,6 @@ class RigettiTarget(str, Enum):
54
51
  return 20
55
52
  elif target_name == RigettiTarget.ANKAA_3.value:
56
53
  return 84
57
- elif target_name == RigettiTarget.ANKAA_9Q_3.value:
58
- return 9
59
54
  else:
60
55
  raise ValueError(f"Unknown target {target_name}")
61
56
 
azure/quantum/version.py CHANGED
@@ -5,4 +5,4 @@
5
5
  # Copyright (c) Microsoft Corporation. All rights reserved.
6
6
  # Licensed under the MIT License.
7
7
  ##
8
- __version__ = "2.5.0.dev0"
8
+ __version__ = "2.5.0.dev2"