qadence 1.7.0__py3-none-any.whl → 1.7.2__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.
qadence/model.py CHANGED
@@ -37,6 +37,36 @@ class QuantumModel(nn.Module):
37
37
  This class should be used as base class for any new quantum model supported in the qadence
38
38
  framework for information on the implementation of custom models see
39
39
  [here](../tutorials/advanced_tutorials/custom-models.md).
40
+
41
+ Example:
42
+ ```python exec="on" source="material-block" result="json"
43
+ import torch
44
+ from qadence import QuantumModel, QuantumCircuit, RX, RY, Z, PI, chain, kron
45
+ from qadence import FeatureParameter, VariationalParameter
46
+
47
+ theta = VariationalParameter("theta")
48
+ phi = FeatureParameter("phi")
49
+
50
+ block = chain(
51
+ kron(RX(0, theta), RY(1, theta)),
52
+ kron(RX(0, phi), RY(1, phi)),
53
+ )
54
+
55
+ circuit = QuantumCircuit(2, block)
56
+
57
+ observable = Z(0) + Z(1)
58
+
59
+ model = QuantumModel(circuit, observable)
60
+ values = {"phi": torch.tensor([PI, PI/2]), "theta": torch.tensor([PI, PI/2])}
61
+
62
+ wf = model.run(values)
63
+ xs = model.sample(values, n_shots=100)
64
+ ex = model.expectation(values)
65
+ print(wf)
66
+ print(xs)
67
+ print(ex)
68
+ ```
69
+ ```
40
70
  """
41
71
 
42
72
  backend: Backend | DifferentiableBackend
@@ -120,6 +150,7 @@ class QuantumModel(nn.Module):
120
150
 
121
151
  @property
122
152
  def vparams(self) -> OrderedDict:
153
+ """Variational parameters."""
123
154
  return OrderedDict({k: v.data for k, v in self._params.items() if v.requires_grad})
124
155
 
125
156
  @property
@@ -145,9 +176,26 @@ class QuantumModel(nn.Module):
145
176
  return len(self.vals_vparams)
146
177
 
147
178
  def circuit(self, circuit: QuantumCircuit) -> ConvertedCircuit:
179
+ """Get backend-converted circuit.
180
+
181
+ Args:
182
+ circuit: QuantumCircuit instance.
183
+
184
+ Returns:
185
+ Backend circuit.
186
+ """
148
187
  return self.backend.circuit(circuit)
149
188
 
150
189
  def observable(self, observable: AbstractBlock, n_qubits: int) -> Any:
190
+ """Get backend observable.
191
+
192
+ Args:
193
+ observable: Observable block.
194
+ n_qubits: Number of qubits
195
+
196
+ Returns:
197
+ Backend observable.
198
+ """
151
199
  return self.backend.observable(observable, n_qubits)
152
200
 
153
201
  def reset_vparams(self, values: Sequence) -> None:
@@ -161,6 +209,11 @@ class QuantumModel(nn.Module):
161
209
  current_vparams[k].data = torch.tensor([values[i]])
162
210
 
163
211
  def forward(self, *args: Any, **kwargs: Any) -> Tensor:
212
+ """Calls run method with arguments.
213
+
214
+ Returns:
215
+ Tensor: A torch.Tensor representing output.
216
+ """
164
217
  return self.run(*args, **kwargs)
165
218
 
166
219
  def run(
@@ -169,9 +222,26 @@ class QuantumModel(nn.Module):
169
222
  state: Tensor | None = None,
170
223
  endianness: Endianness = Endianness.BIG,
171
224
  ) -> Tensor:
225
+ r"""Run model.
226
+
227
+ Given an input state $| \psi_0 \rangle$,
228
+ a set of variational parameters $\vec{\theta}$
229
+ and the unitary representation of the model $U(\vec{\theta})$
230
+ we return $U(\vec{\theta}) | \psi_0 \rangle$.
231
+
232
+ Arguments:
233
+ values: Values dict which contains values for the parameters.
234
+ state: Optional input state to apply model on.
235
+ endianness: Storage convention for binary information.
236
+
237
+ Returns:
238
+ A torch.Tensor representing output.
239
+ """
172
240
  if values is None:
173
241
  values = {}
242
+
174
243
  params = self.embedding_fn(self._params, values)
244
+
175
245
  return self.backend.run(self._circuit, params, state=state, endianness=endianness)
176
246
 
177
247
  def sample(
@@ -183,6 +253,19 @@ class QuantumModel(nn.Module):
183
253
  mitigation: Mitigations | None = None,
184
254
  endianness: Endianness = Endianness.BIG,
185
255
  ) -> list[Counter]:
256
+ """Obtain samples from model.
257
+
258
+ Arguments:
259
+ values: Values dict which contains values for the parameters.
260
+ n_shots: Observable part of the expectation.
261
+ state: Optional input state to apply model on.
262
+ noise: A noise model to use.
263
+ mitigation: A mitigation protocol to use.
264
+ endianness: Storage convention for binary information.
265
+
266
+ Returns:
267
+ A list of Counter instances with the sample results.
268
+ """
186
269
  params = self.embedding_fn(self._params, values)
187
270
  if noise is None:
188
271
  noise = self._noise
@@ -208,7 +291,27 @@ class QuantumModel(nn.Module):
208
291
  mitigation: Mitigations | None = None,
209
292
  endianness: Endianness = Endianness.BIG,
210
293
  ) -> Tensor:
211
- """Compute expectation using the given backend.
294
+ r"""Compute expectation using the given backend.
295
+
296
+
297
+
298
+ Given an input state $|\psi_0 \rangle$,
299
+ a set of variational parameters $\vec{\theta}$
300
+ and the unitary representation of the model $U(\vec{\theta})$
301
+ we return $\langle \psi_0 | U(\vec{\theta}) | \psi_0 \rangle$.
302
+
303
+ Arguments:
304
+ values: Values dict which contains values for the parameters.
305
+ observable: Observable part of the expectation.
306
+ state: Optional input state.
307
+ measurement: Optional measurement protocol. If None, use
308
+ exact expectation value with a statevector simulator.
309
+ noise: A noise model to use.
310
+ mitigation: A mitigation protocol to use.
311
+ endianness: Storage convention for binary information.
312
+
313
+ Raises:
314
+ ValueError: when no observable is set.
212
315
 
213
316
  Returns:
214
317
  A torch.Tensor of shape n_batches x n_obs
@@ -243,9 +346,22 @@ class QuantumModel(nn.Module):
243
346
  )
244
347
 
245
348
  def overlap(self) -> Tensor:
349
+ """Overlap of model.
350
+
351
+ Raises:
352
+ NotImplementedError: The overlap method is not implemented for this model.
353
+ """
246
354
  raise NotImplementedError("The overlap method is not implemented for this model.")
247
355
 
248
356
  def _to_dict(self, save_params: bool = False) -> dict[str, Any]:
357
+ """Convert QuantumModel to a dictionary for serialization.
358
+
359
+ Arguments:
360
+ save_params: Optionally save parameters. Defaults to False.
361
+
362
+ Returns:
363
+ The dictionary
364
+ """
249
365
  d = dict()
250
366
  try:
251
367
  if isinstance(self._observable, list):
@@ -275,6 +391,15 @@ class QuantumModel(nn.Module):
275
391
 
276
392
  @classmethod
277
393
  def _from_dict(cls, d: dict, as_torch: bool = False) -> QuantumModel:
394
+ """Initialize instance of QuantumModel from dictionary.
395
+
396
+ Args:
397
+ d: Dictionary.
398
+ as_torch: Load parameters as torch tensors. Defaults to False.
399
+
400
+ Returns:
401
+ QuantumModel instance
402
+ """
278
403
  from qadence.serialization import deserialize
279
404
 
280
405
  qm: QuantumModel
@@ -310,6 +435,16 @@ class QuantumModel(nn.Module):
310
435
  def save(
311
436
  self, folder: str | Path, file_name: str = "quantum_model.pt", save_params: bool = True
312
437
  ) -> None:
438
+ """Save model.
439
+
440
+ Arguments:
441
+ folder: Folder where model is saved.
442
+ file_name: File name for saving model. Defaults to "quantum_model.pt".
443
+ save_params: Save parameters if True. Defaults to True.
444
+
445
+ Raises:
446
+ FileNotFoundError: If folder is not a directory.
447
+ """
313
448
  if not os.path.isdir(folder):
314
449
  raise FileNotFoundError
315
450
  try:
@@ -321,6 +456,16 @@ class QuantumModel(nn.Module):
321
456
  def load(
322
457
  cls, file_path: str | Path, as_torch: bool = False, map_location: str | torch.device = "cpu"
323
458
  ) -> QuantumModel:
459
+ """Load QuantumModel.
460
+
461
+ Arguments:
462
+ file_path: File path to load model from.
463
+ as_torch: Load parameters as torch tensor. Defaults to False.
464
+ map_location (str | torch.device, optional): Location for loading. Defaults to "cpu".
465
+
466
+ Returns:
467
+ QuantumModel from file_path.
468
+ """
324
469
  qm_pt = {}
325
470
  if isinstance(file_path, str):
326
471
  file_path = Path(file_path)
@@ -336,11 +481,23 @@ class QuantumModel(nn.Module):
336
481
  return cls._from_dict(qm_pt, as_torch)
337
482
 
338
483
  def assign_parameters(self, values: dict[str, Tensor]) -> Any:
339
- """Return the final, assigned circuit that is used in e.g. `backend.run`."""
484
+ """Return the final, assigned circuit that is used in e.g. `backend.run`.
485
+
486
+ Arguments:
487
+ values: Values dict which contains values for the parameters.
488
+
489
+ Returns:
490
+ Final, assigned circuit that is used in e.g. `backend.run`
491
+ """
340
492
  params = self.embedding_fn(self._params, values)
341
493
  return self.backend.assign_parameters(self._circuit, params)
342
494
 
343
495
  def to(self, *args: Any, **kwargs: Any) -> QuantumModel:
496
+ """Conversion method for device or types.
497
+
498
+ Returns:
499
+ QuantumModel with conversions.
500
+ """
344
501
  from pyqtorch import QuantumCircuit as PyQCircuit
345
502
 
346
503
  try:
@@ -369,6 +526,11 @@ class QuantumModel(nn.Module):
369
526
 
370
527
  @property
371
528
  def device(self) -> torch.device:
529
+ """Get device.
530
+
531
+ Returns:
532
+ torch.device
533
+ """
372
534
  return (
373
535
  self._circuit.native.device
374
536
  if self.backend.backend.name == "pyqtorch" # type: ignore[union-attr]
@@ -43,6 +43,7 @@ class HamEvo(TimeEvolutionBlock):
43
43
  generator: Either a AbstractBlock, torch.Tensor or numpy.ndarray.
44
44
  parameter: A scalar or vector of numeric or torch.Tensor type.
45
45
  qubit_support: The qubits on which the evolution will be performed on.
46
+ duration: duration of evolution in case of time-dependent generator
46
47
 
47
48
  Examples:
48
49
 
@@ -66,6 +67,7 @@ class HamEvo(TimeEvolutionBlock):
66
67
  generator: Union[TGenerator, AbstractBlock],
67
68
  parameter: TParameter,
68
69
  qubit_support: tuple[int, ...] = None,
70
+ duration: float | None = None,
69
71
  ):
70
72
  gen_exprs = {}
71
73
  if qubit_support is None and not isinstance(generator, AbstractBlock):
@@ -75,6 +77,10 @@ class HamEvo(TimeEvolutionBlock):
75
77
  qubit_support = generator.qubit_support
76
78
  if generator.is_parametric:
77
79
  gen_exprs = {str(e): e for e in expressions(generator)}
80
+
81
+ if generator.is_time_dependent and duration is None:
82
+ raise ValueError("For time-dependent generators, a duration must be specified.")
83
+
78
84
  elif isinstance(generator, torch.Tensor):
79
85
  msg = "Please provide a square generator."
80
86
  if len(generator.shape) == 2:
@@ -99,6 +105,7 @@ class HamEvo(TimeEvolutionBlock):
99
105
  ps = {"parameter": Parameter(parameter), **gen_exprs}
100
106
  self.parameters = ParamMap(**ps)
101
107
  self.generator = generator
108
+ self.duration = duration
102
109
 
103
110
  @classmethod
104
111
  def num_parameters(cls) -> int:
@@ -197,3 +204,6 @@ class HamEvo(TimeEvolutionBlock):
197
204
  raise NotImplementedError(
198
205
  "The current digital decomposition can be applied only to Pauli Hamiltonians."
199
206
  )
207
+
208
+ def __matmul__(self, other: AbstractBlock) -> AbstractBlock:
209
+ return super().__matmul__(other)
qadence/parameters.py CHANGED
@@ -16,7 +16,7 @@ from torch import Tensor, heaviside, no_grad, rand, tensor
16
16
  from qadence.types import DifferentiableExpression, Engine, TNumber
17
17
 
18
18
  # Modules to be automatically added to the qadence namespace
19
- __all__ = ["FeatureParameter", "Parameter", "VariationalParameter", "ParamMap"]
19
+ __all__ = ["FeatureParameter", "Parameter", "VariationalParameter", "ParamMap", "TimeParameter"]
20
20
 
21
21
  logger = getLogger(__name__)
22
22
 
@@ -61,6 +61,8 @@ class Parameter(Symbol):
61
61
  value: TNumber
62
62
  """(Initial) value of the parameter."""
63
63
 
64
+ is_time: bool
65
+
64
66
  def __new__(
65
67
  cls, name: str | TNumber | Tensor | Basic | Parameter, **assumptions: Any
66
68
  ) -> Parameter | Basic | Expr | Array:
@@ -111,6 +113,7 @@ class Parameter(Symbol):
111
113
  p.name = name.name
112
114
  p.trainable = name.trainable
113
115
  p.value = name.value
116
+ p.is_time = name.is_time
114
117
  return p
115
118
  elif isinstance(name, (Basic, Expr)):
116
119
  if name.is_number:
@@ -120,6 +123,7 @@ class Parameter(Symbol):
120
123
  p = super().__new__(cls, name, **assumptions)
121
124
  p.trainable = assumptions.get("trainable", True)
122
125
  p.value = assumptions.get("value", None)
126
+ p.is_time = assumptions.get("is_time", False)
123
127
  if p.value is None:
124
128
  p.value = rand(1).item()
125
129
  return p
@@ -178,6 +182,11 @@ def VariationalParameter(name: str, **kwargs: Any) -> Parameter:
178
182
  return Parameter(name, trainable=True, **kwargs)
179
183
 
180
184
 
185
+ def TimeParameter(name: str) -> Parameter:
186
+ """Shorthand for `Parameter(..., trainable=False, is_time=True)`."""
187
+ return Parameter(name, trainable=False, is_time=True)
188
+
189
+
181
190
  def extract_original_param_entry(
182
191
  param: Expr,
183
192
  ) -> TNumber | Tensor | Expr:
qadence/register.py CHANGED
@@ -38,28 +38,31 @@ class Register:
38
38
  device_specs: RydbergDevice = DEFAULT_DEVICE,
39
39
  ):
40
40
  """
41
- A 2D register of qubits which includes their coordinates.
41
+ A register of qubits including 2D coordinates.
42
42
 
43
- It is needed for e.g. analog computing.
44
- The coordinates are ignored in backends that don't need them. The easiest
45
- way to construct a register is via its classmethods like `Register.triangular_lattice`.
43
+ Instantiating the Register class directly is only recommended for building custom registers.
44
+ For most uses where a predefined lattice is desired it is recommended to use the various
45
+ class methods available, e.g. `Register.triangular_lattice`.
46
46
 
47
47
  Arguments:
48
- support: A graph or number of qubits. Nodes can include a `"pos"` attribute
48
+ support: A NetworkX graph or number of qubits. Nodes can include a `"pos"` attribute
49
49
  such that e.g.: `graph.nodes = {0: {"pos": (2,3)}, 1: {"pos": (0,0)}, ...}` which
50
- will be used in backends that need qubit coordinates.
51
- See the classmethods for simple construction of some predefined lattices if you
52
- don't want to build a graph manually.
53
- If you pass an integer the resulting register is the same as
54
- `Register.all_to_all(n_qubits)`.
55
- spacing: Value set as the distance between the two closest qubits.
50
+ will be used in backends that need qubit coordinates. Passing a number of qubits
51
+ calls `Register.all_to_all(n_qubits)`.
52
+ spacing: Value set as the distance between the two closest qubits. The spacing
53
+ argument is also available for all the class method constructors.
56
54
 
57
55
  Examples:
58
- ```python exec="on" source="material-block" result="json"
56
+ ```python exec="on" source="material-block"
59
57
  from qadence import Register
60
58
 
61
- reg = Register.honeycomb_lattice(2,3)
62
- reg.draw()
59
+ reg_all = Register.all_to_all(n_qubits = 4)
60
+ reg_line = Register.line(n_qubits = 4)
61
+ reg_circle = Register.circle(n_qubits = 4)
62
+ reg_squre = Register.square(qubits_side = 2)
63
+ reg_rect = Register.rectangular_lattice(qubits_row = 2, qubits_col = 2)
64
+ reg_triang = Register.triangular_lattice(n_cells_row = 2, n_cells_col = 2)
65
+ reg_honey = Register.honeycomb_lattice(n_cells_row = 2, n_cells_col = 2)
63
66
  ```
64
67
  """
65
68
  if device_specs is not None and not isinstance(device_specs, RydbergDevice):
@@ -74,6 +77,7 @@ class Register:
74
77
 
75
78
  @property
76
79
  def n_qubits(self) -> int:
80
+ """Total number of qubits in the register."""
77
81
  return len(self.graph)
78
82
 
79
83
  @classmethod
@@ -84,6 +88,15 @@ class Register:
84
88
  spacing: float | None = None,
85
89
  device_specs: RydbergDevice = DEFAULT_DEVICE,
86
90
  ) -> Register:
91
+ """
92
+ Build a register from a list of qubit coordinates.
93
+
94
+ Each node is added to the underlying
95
+ graph with the respective coordinates, but the edges are left empty.
96
+
97
+ Arguments:
98
+ coords: List of qubit coordinate tuples.
99
+ """
87
100
  graph = nx.Graph()
88
101
  for i, pos in enumerate(coords):
89
102
  graph.add_node(i, pos=pos)
@@ -96,6 +109,12 @@ class Register:
96
109
  spacing: float = 1.0,
97
110
  device_specs: RydbergDevice = DEFAULT_DEVICE,
98
111
  ) -> Register:
112
+ """
113
+ Build a line register.
114
+
115
+ Arguments:
116
+ n_qubits: Total number of qubits.
117
+ """
99
118
  return cls(line_graph(n_qubits), spacing, device_specs)
100
119
 
101
120
  @classmethod
@@ -105,6 +124,12 @@ class Register:
105
124
  spacing: float = 1.0,
106
125
  device_specs: RydbergDevice = DEFAULT_DEVICE,
107
126
  ) -> Register:
127
+ """
128
+ Build a circle register.
129
+
130
+ Arguments:
131
+ n_qubits: Total number of qubits.
132
+ """
108
133
  graph = nx.grid_2d_graph(n_qubits, 1, periodic=True)
109
134
  graph = nx.relabel_nodes(graph, {(i, 0): i for i in range(n_qubits)})
110
135
  coords = nx.circular_layout(graph)
@@ -119,6 +144,12 @@ class Register:
119
144
  spacing: float = 1.0,
120
145
  device_specs: RydbergDevice = DEFAULT_DEVICE,
121
146
  ) -> Register:
147
+ """
148
+ Build a square register.
149
+
150
+ Arguments:
151
+ qubits_side: Number of qubits on one side of the square.
152
+ """
122
153
  n_points = 4 * (qubits_side - 1)
123
154
 
124
155
  def gen_points() -> np.ndarray:
@@ -152,6 +183,15 @@ class Register:
152
183
  spacing: float = 1.0,
153
184
  device_specs: RydbergDevice = DEFAULT_DEVICE,
154
185
  ) -> Register:
186
+ """
187
+ Build a register with an all-to-all connectivity graph.
188
+
189
+ The graph is projected
190
+ onto a 2D space and the qubit coordinates are set using a spring layout algorithm.
191
+
192
+ Arguments:
193
+ n_qubits: Total number of qubits.
194
+ """
155
195
  return cls(alltoall_graph(n_qubits), spacing, device_specs)
156
196
 
157
197
  @classmethod
@@ -166,6 +206,13 @@ class Register:
166
206
  values = {i: {"pos": node} for (i, node) in enumerate(graph.nodes)}
167
207
  graph = nx.relabel_nodes(graph, {(i, j): k for k, (i, j) in enumerate(graph.nodes)})
168
208
  nx.set_node_attributes(graph, values)
209
+ """
210
+ Build a rectangular lattice register.
211
+
212
+ Arguments:
213
+ qubits_row: Number of qubits in each row.
214
+ qubits_col: Number of qubits in each column.
215
+ """
169
216
  return cls(graph, spacing, device_specs)
170
217
 
171
218
  @classmethod
@@ -176,6 +223,15 @@ class Register:
176
223
  spacing: float = 1.0,
177
224
  device_specs: RydbergDevice = DEFAULT_DEVICE,
178
225
  ) -> Register:
226
+ """
227
+ Build a triangular lattice register.
228
+
229
+ Each cell is a triangle made up of three qubits.
230
+
231
+ Arguments:
232
+ n_cells_row: Number of cells in each row.
233
+ n_cells_col: Number of cells in each column.
234
+ """
179
235
  return cls(triangular_lattice_graph(n_cells_row, n_cells_col), spacing, device_specs)
180
236
 
181
237
  @classmethod
@@ -186,6 +242,15 @@ class Register:
186
242
  spacing: float = 1.0,
187
243
  device_specs: RydbergDevice = DEFAULT_DEVICE,
188
244
  ) -> Register:
245
+ """
246
+ Build a honeycomb lattice register.
247
+
248
+ Each cell is an hexagon made up of six qubits.
249
+
250
+ Arguments:
251
+ n_cells_row: Number of cells in each row.
252
+ n_cells_col: Number of cells in each column.
253
+ """
189
254
  graph = nx.hexagonal_lattice_graph(n_cells_row, n_cells_col)
190
255
  graph = nx.relabel_nodes(graph, {(i, j): k for k, (i, j) in enumerate(graph.nodes)})
191
256
  return cls(graph, spacing, device_specs)
@@ -195,6 +260,7 @@ class Register:
195
260
  return getattr(cls, topology)(*args, **kwargs) # type: ignore[no-any-return]
196
261
 
197
262
  def draw(self, show: bool = True) -> None:
263
+ """Draw the underlying NetworkX graph representing the register."""
198
264
  coords = {i: n["pos"] for i, n in self.graph.nodes.items()}
199
265
  nx.draw(self.graph, with_labels=True, pos=coords)
200
266
  if show:
@@ -205,41 +271,59 @@ class Register:
205
271
 
206
272
  @property
207
273
  def nodes(self) -> NodeView:
274
+ """Return the NodeView of the underlying NetworkX graph."""
208
275
  return self.graph.nodes
209
276
 
210
277
  @property
211
278
  def edges(self) -> EdgeView:
279
+ """Return the EdgeView of the underlying NetworkX graph."""
212
280
  return self.graph.edges
213
281
 
214
282
  @property
215
283
  def support(self) -> set:
284
+ """Return the set of qubits in the register."""
216
285
  return set(self.nodes)
217
286
 
218
287
  @property
219
288
  def coords(self) -> dict:
289
+ """Return the dictionary of qubit coordinates."""
220
290
  return {i: tuple(node.get("pos", ())) for i, node in self.nodes.items()}
221
291
 
222
292
  @property
223
293
  def all_node_pairs(self) -> EdgeView:
294
+ """Return a list of all possible qubit pairs in the register."""
224
295
  return list(filter(lambda x: x[0] < x[1], product(self.support, self.support)))
225
296
 
226
297
  @property
227
298
  def distances(self) -> dict:
299
+ """Return a dictionary of distances for all qubit pairs in the register."""
228
300
  coords = self.coords
229
301
  return {edge: dist(coords[edge[0]], coords[edge[1]]) for edge in self.all_node_pairs}
230
302
 
231
303
  @property
232
304
  def edge_distances(self) -> dict:
305
+ """
306
+ Return a dictionary of distances for the qubit pairs that are.
307
+
308
+ connected by an edge in the underlying NetworkX graph.
309
+ """
233
310
  coords = self.coords
234
311
  return {edge: dist(coords[edge[0]], coords[edge[1]]) for edge in self.edges}
235
312
 
236
313
  @property
237
314
  def min_distance(self) -> float:
315
+ """Return the minimum distance between two qubts in the register."""
238
316
  distances = self.distances
239
317
  value: float = min(self.distances.values()) if len(distances) > 0 else 0.0
240
318
  return value
241
319
 
242
320
  def rescale_coords(self, scaling: float) -> Register:
321
+ """
322
+ Rescale the coordinates of all qubits in the register.
323
+
324
+ Arguments:
325
+ scaling: Scaling value.
326
+ """
243
327
  g = deepcopy(self.graph)
244
328
  _scale_node_positions(g, min_distance=1.0, spacing=scaling)
245
329
  return Register(g, spacing=None, device_specs=self.device_specs)
@@ -272,14 +356,6 @@ class Register:
272
356
 
273
357
 
274
358
  def line_graph(n_qubits: int) -> nx.Graph:
275
- """Create graph representing linear lattice.
276
-
277
- Args:
278
- n_qubits (int): number of nodes in the graph
279
-
280
- Returns:
281
- graph instance
282
- """
283
359
  graph = nx.Graph()
284
360
  for i in range(n_qubits):
285
361
  graph.add_node(i, pos=(i, 0.0))
qadence/types.py CHANGED
@@ -7,6 +7,7 @@ from typing import Callable, Iterable, Tuple, Union
7
7
  import numpy as np
8
8
  import sympy
9
9
  from numpy.typing import ArrayLike
10
+ from pyqtorch.utils import SolverType
10
11
  from torch import Tensor, pi
11
12
 
12
13
  TNumber = Union[int, float, complex, np.int64, np.float64]
@@ -47,6 +48,7 @@ __all__ = [
47
48
  "AlgoHEvo",
48
49
  "SerializationFormat",
49
50
  "PI",
51
+ "SolverType",
50
52
  ] # type: ignore
51
53
 
52
54
 
qadence/utils.py CHANGED
@@ -234,18 +234,12 @@ def is_qadence_shape(state: ArrayLike, n_qubits: int) -> bool:
234
234
  return state.shape[1] == 2**n_qubits # type: ignore[no-any-return]
235
235
 
236
236
 
237
- def infer_batchsize(param_values: dict[str, Tensor] = None) -> int:
238
- """Infer the batch_size through the length of the parameter tensors."""
239
- try:
240
- return max([len(tensor) for tensor in param_values.values()]) if param_values else 1
241
- except Exception:
242
- return 1
243
-
244
-
245
237
  def validate_values_and_state(
246
238
  state: ArrayLike | None, n_qubits: int, param_values: dict[str, Tensor] = None
247
239
  ) -> None:
248
240
  if state is not None:
241
+ from qadence.backends.utils import infer_batchsize
242
+
249
243
  if isinstance(state, Tensor):
250
244
  if state is not None:
251
245
  batch_size_state = (
@@ -1,8 +1,8 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: qadence
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Pasqal interface for circuit-based quantum computing SDKs
5
- Author-email: Aleksander Wennersteen <aleksander.wennersteen@pasqal.com>, Gert-Jan Both <gert-jan.both@pasqal.com>, Niklas Heim <niklas.heim@pasqal.com>, Mario Dagrada <mario.dagrada@pasqal.com>, Vincent Elfving <vincent.elfving@pasqal.com>, Dominik Seitz <dominik.seitz@pasqal.com>, Roland Guichard <roland.guichard@pasqal.com>, "Joao P. Moutinho" <joao.moutinho@pasqal.com>, Vytautas Abramavicius <vytautas.abramavicius@pasqal.com>, Gergana Velikova <gergana.velikova@pasqal.com>, Eduardo Maschio <eduardo.maschio@pasqal.com>, Smit Chaudhary <smit.chaudhary@pasqal.com>
5
+ Author-email: Aleksander Wennersteen <aleksander.wennersteen@pasqal.com>, Gert-Jan Both <gert-jan.both@pasqal.com>, Niklas Heim <niklas.heim@pasqal.com>, Mario Dagrada <mario.dagrada@pasqal.com>, Vincent Elfving <vincent.elfving@pasqal.com>, Dominik Seitz <dominik.seitz@pasqal.com>, Roland Guichard <roland.guichard@pasqal.com>, "Joao P. Moutinho" <joao.moutinho@pasqal.com>, Vytautas Abramavicius <vytautas.abramavicius@pasqal.com>, Gergana Velikova <gergana.velikova@pasqal.com>, Eduardo Maschio <eduardo.maschio@pasqal.com>, Smit Chaudhary <smit.chaudhary@pasqal.com>, Ignacio Fernández Graña <ignacio.fernandez-grana@pasqal.com>, Charles Moussa <charles.moussa@pasqal.com>
6
6
  License: Apache 2.0
7
7
  License-File: LICENSE
8
8
  Classifier: License :: OSI Approved :: Apache Software License
@@ -22,10 +22,11 @@ Requires-Dist: matplotlib
22
22
  Requires-Dist: nevergrad
23
23
  Requires-Dist: numpy
24
24
  Requires-Dist: openfermion
25
- Requires-Dist: pyqtorch==1.2.1
25
+ Requires-Dist: pyqtorch==1.2.5
26
26
  Requires-Dist: pyyaml
27
27
  Requires-Dist: rich
28
28
  Requires-Dist: scipy
29
+ Requires-Dist: sympy<1.13
29
30
  Requires-Dist: sympytorch>=0.1.2
30
31
  Requires-Dist: tensorboard>=2.12.0
31
32
  Requires-Dist: torch
@@ -53,9 +54,9 @@ Requires-Dist: qadence-libs; extra == 'libs'
53
54
  Provides-Extra: protocols
54
55
  Requires-Dist: qadence-protocols; extra == 'protocols'
55
56
  Provides-Extra: pulser
56
- Requires-Dist: pasqal-cloud==0.10.1; extra == 'pulser'
57
- Requires-Dist: pulser-core==0.18.1; extra == 'pulser'
58
- Requires-Dist: pulser-simulation==0.18.1; extra == 'pulser'
57
+ Requires-Dist: pasqal-cloud==0.11.1; extra == 'pulser'
58
+ Requires-Dist: pulser-core==0.19.0; extra == 'pulser'
59
+ Requires-Dist: pulser-simulation==0.19.0; extra == 'pulser'
59
60
  Provides-Extra: visualization
60
61
  Requires-Dist: graphviz; extra == 'visualization'
61
62
  Description-Content-Type: text/markdown