qadence 1.9.1__py3-none-any.whl → 1.10.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,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import math
4
+ from logging import getLogger
3
5
  from typing import Any, Callable
4
6
 
5
7
  from qadence.ml_tools.callbacks.saveload import load_checkpoint, write_checkpoint
@@ -12,6 +14,8 @@ from qadence.ml_tools.stages import TrainingStage
12
14
  CallbackFunction = Callable[..., Any]
13
15
  CallbackConditionFunction = Callable[..., bool]
14
16
 
17
+ logger = getLogger("ml_tools")
18
+
15
19
 
16
20
  class Callback:
17
21
  """Base class for defining various training callbacks.
@@ -258,7 +262,7 @@ class WriteMetrics(Callback):
258
262
  writer (BaseWriter ): The writer object for logging.
259
263
  """
260
264
  opt_result = trainer.opt_result
261
- writer.write(opt_result)
265
+ writer.write(opt_result.iteration, opt_result.metrics)
262
266
 
263
267
 
264
268
  class PlotMetrics(Callback):
@@ -449,3 +453,323 @@ class LogModelTracker(Callback):
449
453
  writer.log_model(
450
454
  model, trainer.train_dataloader, trainer.val_dataloader, trainer.test_dataloader
451
455
  )
456
+
457
+
458
+ class LRSchedulerStepDecay(Callback):
459
+ """
460
+ Reduces the learning rate by a factor at regular intervals.
461
+
462
+ This callback adjusts the learning rate by multiplying it with a decay factor
463
+ after a specified number of iterations. The learning rate is updated as:
464
+ lr = lr * gamma
465
+
466
+ Example Usage in `TrainConfig`:
467
+ To use `LRSchedulerStepDecay`, include it in the `callbacks` list when setting
468
+ up your `TrainConfig`:
469
+ ```python exec="on" source="material-block" result="json"
470
+ from qadence.ml_tools import TrainConfig
471
+ from qadence.ml_tools.callbacks import LRSchedulerStepDecay
472
+
473
+ # Create an instance of the LRSchedulerStepDecay callback
474
+ lr_step_decay = LRSchedulerStepDecay(on="train_epoch_end",
475
+ called_every=100,
476
+ gamma=0.5)
477
+
478
+ config = TrainConfig(
479
+ max_iter=10000,
480
+ # Print metrics every 1000 training epochs
481
+ print_every=1000,
482
+ # Add the custom callback
483
+ callbacks=[lr_step_decay]
484
+ )
485
+ ```
486
+ """
487
+
488
+ def __init__(self, on: str, called_every: int, gamma: float = 0.5):
489
+ """Initializes the LRSchedulerStepDecay callback.
490
+
491
+ Args:
492
+ on (str): The event to trigger the callback.
493
+ called_every (int): Frequency of callback calls in terms of iterations.
494
+ gamma (float, optional): The decay factor applied to the learning rate.
495
+ A value < 1 reduces the learning rate over time. Default is 0.5.
496
+ """
497
+ super().__init__(on=on, called_every=called_every)
498
+ self.gamma = gamma
499
+
500
+ def run_callback(self, trainer: Any, config: TrainConfig, writer: BaseWriter) -> None:
501
+ """
502
+ Runs the callback to apply step decay to the learning rate.
503
+
504
+ Args:
505
+ trainer (Any): The training object.
506
+ config (TrainConfig): The configuration object.
507
+ writer (BaseWriter): The writer object for logging.
508
+ """
509
+ for param_group in trainer.optimizer.param_groups:
510
+ param_group["lr"] *= self.gamma
511
+
512
+
513
+ class LRSchedulerCyclic(Callback):
514
+ """
515
+ Applies a cyclic learning rate schedule during training.
516
+
517
+ This callback oscillates the learning rate between a minimum (base_lr)
518
+ and a maximum (max_lr) over a defined cycle length (step_size). The learning
519
+ rate follows a triangular wave pattern.
520
+
521
+ Example Usage in `TrainConfig`:
522
+ To use `LRSchedulerCyclic`, include it in the `callbacks` list when setting
523
+ up your `TrainConfig`:
524
+ ```python exec="on" source="material-block" result="json"
525
+ from qadence.ml_tools import TrainConfig
526
+ from qadence.ml_tools.callbacks import LRSchedulerCyclic
527
+
528
+ # Create an instance of the LRSchedulerCyclic callback
529
+ lr_cyclic = LRSchedulerCyclic(on="train_batch_end",
530
+ called_every=1,
531
+ base_lr=0.001,
532
+ max_lr=0.01,
533
+ step_size=2000)
534
+
535
+ config = TrainConfig(
536
+ max_iter=10000,
537
+ # Print metrics every 1000 training epochs
538
+ print_every=1000,
539
+ # Add the custom callback
540
+ callbacks=[lr_cyclic]
541
+ )
542
+ ```
543
+ """
544
+
545
+ def __init__(self, on: str, called_every: int, base_lr: float, max_lr: float, step_size: int):
546
+ """Initializes the LRSchedulerCyclic callback.
547
+
548
+ Args:
549
+ on (str): The event to trigger the callback.
550
+ called_every (int): Frequency of callback calls in terms of iterations.
551
+ base_lr (float): The minimum learning rate.
552
+ max_lr (float): The maximum learning rate.
553
+ step_size (int): Number of iterations for half a cycle.
554
+ """
555
+ super().__init__(on=on, called_every=called_every)
556
+ self.base_lr = base_lr
557
+ self.max_lr = max_lr
558
+ self.step_size = step_size
559
+
560
+ def run_callback(self, trainer: Any, config: TrainConfig, writer: BaseWriter) -> None:
561
+ """
562
+ Adjusts the learning rate cyclically.
563
+
564
+ Args:
565
+ trainer (Any): The training object.
566
+ config (TrainConfig): The configuration object.
567
+ writer (BaseWriter): The writer object for logging.
568
+ """
569
+ cycle = trainer.opt_result.iteration // (2 * self.step_size)
570
+ x = abs(trainer.opt_result.iteration / self.step_size - 2 * cycle - 1)
571
+ scale = max(0, (1 - x))
572
+ new_lr = self.base_lr + (self.max_lr - self.base_lr) * scale
573
+ for param_group in trainer.optimizer.param_groups:
574
+ param_group["lr"] = new_lr
575
+
576
+
577
+ class LRSchedulerCosineAnnealing(Callback):
578
+ """
579
+ Applies cosine annealing to the learning rate during training.
580
+
581
+ This callback decreases the learning rate following a cosine curve,
582
+ starting from the initial learning rate and annealing to a minimum (min_lr).
583
+
584
+ Example Usage in `TrainConfig`:
585
+ To use `LRSchedulerCosineAnnealing`, include it in the `callbacks` list
586
+ when setting up your `TrainConfig`:
587
+ ```python exec="on" source="material-block" result="json"
588
+ from qadence.ml_tools import TrainConfig
589
+ from qadence.ml_tools.callbacks import LRSchedulerCosineAnnealing
590
+
591
+ # Create an instance of the LRSchedulerCosineAnnealing callback
592
+ lr_cosine = LRSchedulerCosineAnnealing(on="train_batch_end",
593
+ called_every=1,
594
+ t_max=5000,
595
+ min_lr=1e-6)
596
+
597
+ config = TrainConfig(
598
+ max_iter=10000,
599
+ # Print metrics every 1000 training epochs
600
+ print_every=1000,
601
+ # Add the custom callback
602
+ callbacks=[lr_cosine]
603
+ )
604
+ ```
605
+ """
606
+
607
+ def __init__(self, on: str, called_every: int, t_max: int, min_lr: float = 0.0):
608
+ """Initializes the LRSchedulerCosineAnnealing callback.
609
+
610
+ Args:
611
+ on (str): The event to trigger the callback.
612
+ called_every (int): Frequency of callback calls in terms of iterations.
613
+ t_max (int): The total number of iterations for one annealing cycle.
614
+ min_lr (float, optional): The minimum learning rate. Default is 0.0.
615
+ """
616
+ super().__init__(on=on, called_every=called_every)
617
+ self.t_max = t_max
618
+ self.min_lr = min_lr
619
+
620
+ def run_callback(self, trainer: Any, config: TrainConfig, writer: BaseWriter) -> None:
621
+ """
622
+ Adjusts the learning rate using cosine annealing.
623
+
624
+ Args:
625
+ trainer (Any): The training object.
626
+ config (TrainConfig): The configuration object.
627
+ writer (BaseWriter): The writer object for logging.
628
+ """
629
+ for param_group in trainer.optimizer.param_groups:
630
+ max_lr = param_group["lr"]
631
+ new_lr = (
632
+ self.min_lr
633
+ + (max_lr - self.min_lr)
634
+ * (1 + math.cos(math.pi * trainer.opt_result.iteration / self.t_max))
635
+ / 2
636
+ )
637
+ param_group["lr"] = new_lr
638
+
639
+
640
+ class EarlyStopping(Callback):
641
+ """
642
+ Stops training when a monitored metric has not improved for a specified number of epochs.
643
+
644
+ This callback monitors a specified metric (e.g., validation loss or accuracy). If the metric
645
+ does not improve for a given patience period, training is stopped.
646
+
647
+ Example Usage in `TrainConfig`:
648
+ To use `EarlyStopping`, include it in the `callbacks` list when setting up your `TrainConfig`:
649
+ ```python exec="on" source="material-block" result="json"
650
+ from qadence.ml_tools import TrainConfig
651
+ from qadence.ml_tools.callbacks import EarlyStopping
652
+
653
+ # Create an instance of the EarlyStopping callback
654
+ early_stopping = EarlyStopping(on="val_epoch_end",
655
+ called_every=1,
656
+ monitor="val_loss",
657
+ patience=5,
658
+ mode="min")
659
+
660
+ config = TrainConfig(
661
+ max_iter=10000,
662
+ print_every=1000,
663
+ callbacks=[early_stopping]
664
+ )
665
+ ```
666
+ """
667
+
668
+ def __init__(
669
+ self, on: str, called_every: int, monitor: str, patience: int = 5, mode: str = "min"
670
+ ):
671
+ """Initializes the EarlyStopping callback.
672
+
673
+ Args:
674
+ on (str): The event to trigger the callback (e.g., "val_epoch_end").
675
+ called_every (int): Frequency of callback calls in terms of iterations.
676
+ monitor (str): The metric to monitor (e.g., "val_loss" or "train_loss").
677
+ All metrics returned by optimize step are available to monitor.
678
+ Please add "val_" and "train_" strings at the start of the metric name.
679
+ patience (int, optional): Number of iterations to wait for improvement. Default is 5.
680
+ mode (str, optional): Whether to minimize ("min") or maximize ("max") the metric.
681
+ Default is "min".
682
+ """
683
+ super().__init__(on=on, called_every=called_every)
684
+ self.monitor = monitor
685
+ self.patience = patience
686
+ self.mode = mode
687
+ self.best_value = float("inf") if mode == "min" else -float("inf")
688
+ self.counter = 0
689
+
690
+ def run_callback(self, trainer: Any, config: TrainConfig, writer: BaseWriter) -> None:
691
+ """
692
+ Monitors the metric and stops training if no improvement is observed.
693
+
694
+ Args:
695
+ trainer (Any): The training object.
696
+ config (TrainConfig): The configuration object.
697
+ writer (BaseWriter): The writer object for logging.
698
+ """
699
+ current_value = trainer.opt_result.metrics.get(self.monitor)
700
+ if current_value is None:
701
+ raise ValueError(f"Metric '{self.monitor}' is not available in the trainer's metrics.")
702
+
703
+ if (self.mode == "min" and current_value < self.best_value) or (
704
+ self.mode == "max" and current_value > self.best_value
705
+ ):
706
+ self.best_value = current_value
707
+ self.counter = 0
708
+ else:
709
+ self.counter += 1
710
+
711
+ if self.counter >= self.patience:
712
+ logger.info(
713
+ f"EarlyStopping: No improvement in '{self.monitor}' for {self.patience} epochs. "
714
+ "Stopping training."
715
+ )
716
+ trainer.stop_training = True
717
+
718
+
719
+ class GradientMonitoring(Callback):
720
+ """
721
+ Logs gradient statistics (e.g., mean, standard deviation, max) during training.
722
+
723
+ This callback monitors and logs statistics about the gradients of the model parameters
724
+ to help debug or optimize the training process.
725
+
726
+ Example Usage in `TrainConfig`:
727
+ To use `GradientMonitoring`, include it in the `callbacks` list when
728
+ setting up your `TrainConfig`:
729
+ ```python exec="on" source="material-block" result="json"
730
+ from qadence.ml_tools import TrainConfig
731
+ from qadence.ml_tools.callbacks import GradientMonitoring
732
+
733
+ # Create an instance of the GradientMonitoring callback
734
+ gradient_monitoring = GradientMonitoring(on="train_batch_end", called_every=10)
735
+
736
+ config = TrainConfig(
737
+ max_iter=10000,
738
+ print_every=1000,
739
+ callbacks=[gradient_monitoring]
740
+ )
741
+ ```
742
+ """
743
+
744
+ def __init__(self, on: str, called_every: int = 1):
745
+ """Initializes the GradientMonitoring callback.
746
+
747
+ Args:
748
+ on (str): The event to trigger the callback (e.g., "train_batch_end").
749
+ called_every (int): Frequency of callback calls in terms of iterations.
750
+ """
751
+ super().__init__(on=on, called_every=called_every)
752
+
753
+ def run_callback(self, trainer: Any, config: TrainConfig, writer: BaseWriter) -> None:
754
+ """
755
+ Logs gradient statistics.
756
+
757
+ Args:
758
+ trainer (Any): The training object.
759
+ config (TrainConfig): The configuration object.
760
+ writer (BaseWriter): The writer object for logging.
761
+ """
762
+ gradient_stats = {}
763
+ for name, param in trainer.model.named_parameters():
764
+ if param.grad is not None:
765
+ grad = param.grad
766
+ gradient_stats.update(
767
+ {
768
+ name + "_mean": grad.mean().item(),
769
+ name + "_std": grad.std().item(),
770
+ name + "_max": grad.max().item(),
771
+ name + "_min": grad.min().item(),
772
+ }
773
+ )
774
+
775
+ writer.write(trainer.opt_result.iteration, gradient_stats)
@@ -60,12 +60,14 @@ class BaseWriter(ABC):
60
60
  raise NotImplementedError("Writers must implement a close method.")
61
61
 
62
62
  @abstractmethod
63
- def write(self, result: OptimizeResult) -> None:
63
+ def write(self, iteration: int, metrics: dict) -> None:
64
64
  """
65
65
  Logs the results of the current iteration.
66
66
 
67
67
  Args:
68
- result (OptimizeResult): The optimization results to log.
68
+ iteration (int): The current training iteration.
69
+ metrics (dict): A dictionary of metrics to log, where keys are metric names
70
+ and values are the corresponding metric values.
69
71
  """
70
72
  raise NotImplementedError("Writers must implement a write method.")
71
73
 
@@ -166,23 +168,22 @@ class TensorBoardWriter(BaseWriter):
166
168
  if self.writer:
167
169
  self.writer.close()
168
170
 
169
- def write(self, result: OptimizeResult) -> None:
171
+ def write(self, iteration: int, metrics: dict) -> None:
170
172
  """
171
173
  Logs the results of the current iteration to TensorBoard.
172
174
 
173
175
  Args:
174
- result (OptimizeResult): The optimization results to log.
176
+ iteration (int): The current training iteration.
177
+ metrics (dict): A dictionary of metrics to log, where keys are metric names
178
+ and values are the corresponding metric values.
175
179
  """
176
- # Not writing loss as loss is available in the metrics
177
- # if result.loss is not None:
178
- # self.writer.add_scalar("loss", float(result.loss), result.iteration)
179
180
  if self.writer:
180
- for key, value in result.metrics.items():
181
- self.writer.add_scalar(key, value, result.iteration)
181
+ for key, value in metrics.items():
182
+ self.writer.add_scalar(key, value, iteration)
182
183
  else:
183
184
  raise RuntimeError(
184
185
  "The writer is not initialized."
185
- "Please call the 'writer.open()' method before writing"
186
+ "Please call the 'writer.open()' method before writing."
186
187
  )
187
188
 
188
189
  def log_hyperparams(self, hyperparams: dict) -> None:
@@ -305,22 +306,21 @@ class MLFlowWriter(BaseWriter):
305
306
  if self.run:
306
307
  self.mlflow.end_run()
307
308
 
308
- def write(self, result: OptimizeResult) -> None:
309
+ def write(self, iteration: int, metrics: dict) -> None:
309
310
  """
310
311
  Logs the results of the current iteration to MLflow.
311
312
 
312
313
  Args:
313
- result (OptimizeResult): The optimization results to log.
314
+ iteration (int): The current training iteration.
315
+ metrics (dict): A dictionary of metrics to log, where keys are metric names
316
+ and values are the corresponding metric values.
314
317
  """
315
- # Not writing loss as loss is available in the metrics
316
- # if result.loss is not None:
317
- # self.mlflow.log_metric("loss", float(result.loss), step=result.iteration)
318
318
  if self.mlflow:
319
- self.mlflow.log_metrics(result.metrics, step=result.iteration)
319
+ self.mlflow.log_metrics(metrics, step=iteration)
320
320
  else:
321
321
  raise RuntimeError(
322
322
  "The writer is not initialized."
323
- "Please call the 'writer.open()' method before writing"
323
+ "Please call the 'writer.open()' method before writing."
324
324
  )
325
325
 
326
326
  def log_hyperparams(self, hyperparams: dict) -> None:
@@ -13,13 +13,13 @@ from qadence.constructors import (
13
13
  analog_feature_map,
14
14
  feature_map,
15
15
  hamiltonian_factory,
16
- identity_initialized_ansatz,
16
+ iia,
17
17
  rydberg_feature_map,
18
18
  rydberg_hea,
19
19
  rydberg_tower_feature_map,
20
20
  )
21
- from qadence.constructors.ansatze import hea_digital, hea_sDAQC
22
21
  from qadence.constructors.hamiltonians import ObservableConfig, TDetuning
22
+ from qadence.constructors.hea import hea_digital, hea_sDAQC
23
23
  from qadence.measurements import Measurements
24
24
  from qadence.noise import NoiseHandler
25
25
  from qadence.operations import CNOT, RX, RY, I, N, Z
@@ -107,7 +107,8 @@ def _encode_features_series_digital(
107
107
  )
108
108
 
109
109
  support_arrays = {
110
- key: support for key, support in zip(config.inputs, support_arrays_list) # type: ignore[union-attr, arg-type]
110
+ key: support
111
+ for key, support in zip(config.inputs, support_arrays_list) # type: ignore[union-attr, arg-type]
111
112
  }
112
113
 
113
114
  num_uploads = {key: value + 1 for key, value in config.num_repeats.items()} # type: ignore[union-attr]
@@ -163,7 +164,8 @@ def _encode_features_parallel_digital(
163
164
  )
164
165
 
165
166
  support_arrays = {
166
- key: support for key, support in zip(config.inputs, support_arrays_list) # type: ignore[union-attr, arg-type]
167
+ key: support
168
+ for key, support in zip(config.inputs, support_arrays_list) # type: ignore[union-attr, arg-type]
167
169
  }
168
170
 
169
171
  num_uploads = {key: value + 1 for key, value in config.num_repeats.items()} # type: ignore[union-attr]
@@ -412,7 +414,7 @@ def _create_iia_digital(
412
414
  entangler = config.strategy_args.get("entangler", CNOT)
413
415
  periodic = config.strategy_args.get("periodic", False)
414
416
 
415
- return identity_initialized_ansatz(
417
+ return iia(
416
418
  n_qubits=num_qubits,
417
419
  depth=config.depth,
418
420
  param_prefix=config.param_prefix,
@@ -441,7 +443,7 @@ def _create_iia_sdaqc(
441
443
  entangler = config.strategy_args.get("entangler", CNOT)
442
444
  periodic = config.strategy_args.get("periodic", False)
443
445
 
444
- return identity_initialized_ansatz(
446
+ return iia(
445
447
  n_qubits=num_qubits,
446
448
  depth=config.depth,
447
449
  param_prefix=config.param_prefix,
@@ -281,6 +281,7 @@ class Trainer(BaseTrainer):
281
281
  self.device: torch_device | None = device
282
282
  self.dtype: torch_dtype | None = dtype
283
283
  self.data_dtype: torch_dtype | None = None
284
+ self.stop_training: bool = False
284
285
  if self.dtype:
285
286
  self.data_dtype = float64 if (self.dtype == complex128) else float32
286
287
 
@@ -321,6 +322,7 @@ class Trainer(BaseTrainer):
321
322
  The callback_manager.start_training takes care of loading checkpoint,
322
323
  and setting up the writer.
323
324
  """
325
+ self.stop_training = False
324
326
  self.config_manager.initialize_config()
325
327
  self.callback_manager.start_training(trainer=self)
326
328
 
@@ -377,25 +379,26 @@ class Trainer(BaseTrainer):
377
379
  for epoch in range(
378
380
  self.global_step, self.global_step + self.config_manager.config.max_iter + 1
379
381
  ):
380
- try:
381
- self.current_epoch = epoch
382
- self.on_train_epoch_start()
383
- train_epoch_loss_metrics = self.run_training(self.train_dataloader)
384
- train_losses.append(train_epoch_loss_metrics)
385
- self.on_train_epoch_end(train_epoch_loss_metrics)
386
-
387
- # Run validation periodically if specified
388
- if self.perform_val and self.current_epoch % self.config.val_every == 0:
389
- self.on_val_epoch_start()
390
- val_epoch_loss_metrics = self.run_validation(self.val_dataloader)
391
- val_losses.append(val_epoch_loss_metrics)
392
- self.on_val_epoch_end(val_epoch_loss_metrics)
393
- self.progress.update(val_task, advance=1)
394
-
395
- self.progress.update(train_task, advance=1)
396
- except KeyboardInterrupt:
397
- logger.info("Terminating training gracefully after the current iteration.")
398
- break
382
+ if not self.stop_training:
383
+ try:
384
+ self.current_epoch = epoch
385
+ self.on_train_epoch_start()
386
+ train_epoch_loss_metrics = self.run_training(self.train_dataloader)
387
+ train_losses.append(train_epoch_loss_metrics)
388
+ self.on_train_epoch_end(train_epoch_loss_metrics)
389
+
390
+ # Run validation periodically if specified
391
+ if self.perform_val and self.current_epoch % self.config.val_every == 0:
392
+ self.on_val_epoch_start()
393
+ val_epoch_loss_metrics = self.run_validation(self.val_dataloader)
394
+ val_losses.append(val_epoch_loss_metrics)
395
+ self.on_val_epoch_end(val_epoch_loss_metrics)
396
+ self.progress.update(val_task, advance=1)
397
+
398
+ self.progress.update(train_task, advance=1)
399
+ except KeyboardInterrupt:
400
+ logger.info("Terminating training gracefully after the current iteration.")
401
+ break
399
402
 
400
403
  self.on_train_end(train_losses, val_losses)
401
404
  return train_losses
@@ -629,10 +632,12 @@ class Trainer(BaseTrainer):
629
632
 
630
633
  def build_optimize_result(
631
634
  self,
632
- result: None
633
- | tuple[torch.Tensor, dict[Any, Any]]
634
- | list[tuple[torch.Tensor, dict[Any, Any]]]
635
- | list[list[tuple[torch.Tensor, dict[Any, Any]]]],
635
+ result: (
636
+ None
637
+ | tuple[torch.Tensor, dict[Any, Any]]
638
+ | list[tuple[torch.Tensor, dict[Any, Any]]]
639
+ | list[list[tuple[torch.Tensor, dict[Any, Any]]]]
640
+ ),
636
641
  ) -> None:
637
642
  """
638
643
  Builds and stores the optimization result by calculating the average loss and metrics.
@@ -119,9 +119,7 @@ class MCRX(ParametricControlBlock):
119
119
 
120
120
  @property
121
121
  def eigenvalues_generator(self) -> Tensor:
122
- return torch.cat(
123
- (torch.zeros(2**self.n_qubits - 2), torch.tensor([1, -1], dtype=cdouble))
124
- )
122
+ return torch.cat((torch.zeros(2**self.n_qubits - 2), torch.tensor([1, -1], dtype=cdouble)))
125
123
 
126
124
  @property
127
125
  def eigenvalues(self) -> Tensor:
@@ -164,9 +162,7 @@ class MCRY(ParametricControlBlock):
164
162
 
165
163
  @property
166
164
  def eigenvalues_generator(self) -> Tensor:
167
- return torch.cat(
168
- (torch.zeros(2**self.n_qubits - 2), torch.tensor([1, -1], dtype=cdouble))
169
- )
165
+ return torch.cat((torch.zeros(2**self.n_qubits - 2), torch.tensor([1, -1], dtype=cdouble)))
170
166
 
171
167
  @property
172
168
  def eigenvalues(self) -> Tensor:
@@ -205,9 +201,7 @@ class MCRZ(ParametricControlBlock):
205
201
 
206
202
  @property
207
203
  def eigenvalues_generator(self) -> Tensor:
208
- return torch.cat(
209
- (torch.zeros(2**self.n_qubits - 2), torch.tensor([1, -1], dtype=cdouble))
210
- )
204
+ return torch.cat((torch.zeros(2**self.n_qubits - 2), torch.tensor([1, -1], dtype=cdouble)))
211
205
 
212
206
  @property
213
207
  def eigenvalues(self) -> Tensor: