qadence 1.11.1__py3-none-any.whl → 1.11.3__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.
Files changed (36) hide show
  1. qadence/backend.py +33 -10
  2. qadence/backends/agpsr_utils.py +96 -0
  3. qadence/backends/api.py +8 -1
  4. qadence/backends/horqrux/backend.py +24 -10
  5. qadence/backends/horqrux/config.py +17 -1
  6. qadence/backends/horqrux/convert_ops.py +20 -97
  7. qadence/backends/jax_utils.py +5 -2
  8. qadence/backends/{gpsr.py → parameter_shift_rules.py} +48 -30
  9. qadence/backends/pulser/backend.py +16 -9
  10. qadence/backends/pulser/config.py +18 -0
  11. qadence/backends/pyqtorch/backend.py +25 -11
  12. qadence/backends/pyqtorch/config.py +18 -0
  13. qadence/blocks/embedding.py +10 -1
  14. qadence/blocks/primitive.py +2 -3
  15. qadence/blocks/utils.py +33 -24
  16. qadence/engines/differentiable_backend.py +7 -1
  17. qadence/engines/jax/differentiable_backend.py +7 -1
  18. qadence/engines/torch/differentiable_backend.py +12 -9
  19. qadence/engines/torch/differentiable_expectation.py +12 -11
  20. qadence/extensions.py +0 -10
  21. qadence/ml_tools/__init__.py +2 -0
  22. qadence/ml_tools/callbacks/callbackmanager.py +4 -2
  23. qadence/ml_tools/constructors.py +264 -4
  24. qadence/ml_tools/qcnn_model.py +158 -0
  25. qadence/model.py +113 -8
  26. qadence/parameters.py +2 -0
  27. qadence/serialization.py +1 -1
  28. qadence/transpile/__init__.py +3 -2
  29. qadence/transpile/block.py +58 -5
  30. qadence/types.py +2 -4
  31. qadence/utils.py +39 -8
  32. {qadence-1.11.1.dist-info → qadence-1.11.3.dist-info}/METADATA +22 -11
  33. {qadence-1.11.1.dist-info → qadence-1.11.3.dist-info}/RECORD +35 -33
  34. qadence-1.11.3.dist-info/licenses/LICENSE +13 -0
  35. qadence-1.11.1.dist-info/licenses/LICENSE +0 -202
  36. {qadence-1.11.1.dist-info → qadence-1.11.3.dist-info}/WHEEL +0 -0
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Callable
3
+ from typing import Any, Callable
4
4
  import numpy as np
5
5
  from sympy import Basic
6
6
 
@@ -8,7 +8,8 @@ from qadence.backend import BackendConfiguration
8
8
  from qadence.blocks import chain, kron
9
9
  from qadence.blocks.abstract import AbstractBlock
10
10
  from qadence.blocks.composite import ChainBlock, KronBlock
11
- from qadence.blocks.utils import add, tag
11
+ from qadence.blocks.utils import tag, add
12
+ from qadence.parameters import Parameter
12
13
  from qadence.circuit import QuantumCircuit
13
14
  from qadence.constructors import (
14
15
  analog_feature_map,
@@ -24,7 +25,7 @@ from qadence.constructors.hea import hea_digital, hea_sDAQC
24
25
  from qadence.constructors.iia import iia
25
26
  from qadence.measurements import Measurements
26
27
  from qadence.noise import NoiseHandler
27
- from qadence.operations import CNOT, RX, RY, I, N, Z
28
+ from qadence.operations import CNOT, RX, RY, RZ, I
28
29
  from qadence.register import Register
29
30
  from qadence.types import (
30
31
  AnsatzType,
@@ -733,7 +734,6 @@ def create_observable(
733
734
  interaction=config.interaction,
734
735
  detuning=config.detuning,
735
736
  )
736
-
737
737
  obs: AbstractBlock = add(shifting_term, detuning_hamiltonian)
738
738
 
739
739
  if isinstance(config.tag, str):
@@ -801,6 +801,7 @@ def build_qnn_from_configs(
801
801
  if isinstance(observable_config, list)
802
802
  else create_observable(register=register, config=observable_config)
803
803
  )
804
+
804
805
  ufa = QNN(
805
806
  circ,
806
807
  observable,
@@ -814,3 +815,262 @@ def build_qnn_from_configs(
814
815
  )
815
816
 
816
817
  return ufa
818
+
819
+
820
+ def _create_feature_map_qcnn(
821
+ n_qubits: int,
822
+ n_inputs: int,
823
+ fm_type: str = "Fourier",
824
+ op: Any = RX,
825
+ ) -> Any:
826
+ """
827
+ Creates a feature map (FM) by dividing qubits among inputs and applying.
828
+
829
+ the specified feature map type.
830
+
831
+ Args:
832
+ n_qubits (int): Total number of qubits.
833
+ n_inputs (int): Number of inputs.
834
+ fm_type (str): Type of feature map to use (e.g., "Fourier").
835
+ op (Any): Quantum operation to use in the feature map (e.g., RX).
836
+
837
+ Returns:
838
+ Any: The combined feature map as a kronecker product
839
+ of individual feature maps.
840
+ """
841
+ fm_temp = []
842
+ qubits_per_input = n_qubits // n_inputs # Base number of qubits per input
843
+ exceeding_qubits = n_qubits % n_inputs # Number of exceeding qubits
844
+ start = 0 # Track current qubit index
845
+
846
+ for i in range(n_inputs):
847
+ # Assign base qubits + 1 extra if input has exceeding qubits
848
+ num_qubits = qubits_per_input + 1 if i < exceeding_qubits else qubits_per_input
849
+ end = start + num_qubits
850
+
851
+ # Create FM for this input
852
+ fm_temp.append(
853
+ feature_map(
854
+ n_qubits=num_qubits,
855
+ param=f"\u03C6_{i}", # Use phi_i as the parameter
856
+ op=op,
857
+ fm_type=fm_type,
858
+ support=tuple(range(start, end)),
859
+ )
860
+ )
861
+ start = end # Update starting index for next FM
862
+
863
+ # Combine all feature maps using kronecker product
864
+ return kron(*fm_temp)
865
+
866
+
867
+ def _get_block_params(
868
+ params: dict,
869
+ layer: int,
870
+ rep: int,
871
+ pos: int,
872
+ is_corr: bool = False,
873
+ ) -> Any:
874
+ """
875
+ Retrieves the parameter for a given operation.
876
+
877
+ Args:
878
+ params (dict): Dictionary to store and retrieve parameters.
879
+ layer (int): The index of the current layer.
880
+ rep (int): The index of the current repetition in the layer.
881
+ pos (int): Position of the qubit in the layer.
882
+ is_corr (bool): If True, uses correlated parameters for corresponding gates in W^opt_ij.
883
+
884
+ Returns:
885
+ Parameter: the retrieved parameter.
886
+ """
887
+ if is_corr:
888
+ # Cycle pos from 0 to 8
889
+ key = f"θ_{layer}_{pos % 9}"
890
+ else:
891
+ key = f"θ_{layer}_{rep}_{pos}"
892
+
893
+ if key not in params:
894
+ params[key] = Parameter(key)
895
+ return params[key]
896
+
897
+
898
+ def _create_single_W(
899
+ params: dict,
900
+ operations: list[Any],
901
+ entangler: Any,
902
+ layer: int,
903
+ rep: int,
904
+ max_reps: int,
905
+ control: int,
906
+ target: int,
907
+ spacing: int,
908
+ n_qubits: int,
909
+ is_corr: bool = False,
910
+ ) -> ChainBlock:
911
+ """Creates a single convolutional cell W_ij."""
912
+ pad = [
913
+ I(q)
914
+ for q in range(control - spacing, control + spacing + 1)
915
+ if q != control and q != target and 0 <= q < n_qubits
916
+ ]
917
+ gates = []
918
+
919
+ # Track per-layer repetition index for proper parameter continuity
920
+ key_param_counter = f"param_index_{layer}_{rep}"
921
+ if key_param_counter not in params:
922
+ params[key_param_counter] = 0 # Initialize if first time
923
+
924
+ param_index = params[key_param_counter] # Load index
925
+ single_params = {} # Store params for single RZ/RY gates
926
+
927
+ # Apply the first sequence of operations
928
+ for _, op in enumerate(operations):
929
+ param_control = _get_block_params(params, layer, rep, param_index, is_corr)
930
+ param_index += 1
931
+ param_target = _get_block_params(params, layer, rep, param_index, is_corr)
932
+ param_index += 1
933
+ gates.append(
934
+ kron(
935
+ *pad,
936
+ op(control, param_control),
937
+ op(target, param_target),
938
+ )
939
+ )
940
+ # entangling gate
941
+ gates.append(entangler(target, control))
942
+
943
+ # Apply RZ, RY and entangling gates for intermediate step
944
+ single_params["control_rz"] = _get_block_params(params, layer, rep, param_index, is_corr)
945
+ param_index += 1
946
+ single_params["target_ry"] = _get_block_params(params, layer, rep, param_index, is_corr)
947
+ param_index += 1
948
+ gates.append(
949
+ kron(
950
+ *pad,
951
+ RZ(control, single_params["control_rz"]),
952
+ RY(target, single_params["target_ry"]),
953
+ )
954
+ )
955
+ # entangling gate
956
+ gates.append(entangler(control, target))
957
+
958
+ intermediate_ry = _get_block_params(params, layer, rep, param_index, is_corr)
959
+ param_index += 1
960
+ gates.append(
961
+ kron(
962
+ *pad,
963
+ I(control),
964
+ RY(target, intermediate_ry),
965
+ )
966
+ )
967
+ # entangling gate
968
+ gates.append(entangler(target, control))
969
+
970
+ # Apply the first sequence of operations
971
+ for _, op in enumerate(operations):
972
+ param_control = _get_block_params(params, layer, rep, param_index, is_corr)
973
+ param_index += 1
974
+ param_target = _get_block_params(params, layer, rep, param_index, is_corr)
975
+ param_index += 1
976
+ gates.append(
977
+ kron(
978
+ *pad,
979
+ op(control, param_control),
980
+ op(target, param_target),
981
+ )
982
+ )
983
+ # Add final entangling gate (control -> target)
984
+ if rep == int(max_reps - 1):
985
+ gates.append(entangler(control, target))
986
+
987
+ # Update params dict with the last used index
988
+ params[key_param_counter] = param_index
989
+
990
+ return chain(*gates)
991
+
992
+
993
+ def _create_conv_layer(
994
+ layer_index: int,
995
+ max_reps: int,
996
+ current_indices: list[int],
997
+ params: dict,
998
+ operations: list[Any],
999
+ entangler: Any,
1000
+ n_qubits: int,
1001
+ is_corr: bool,
1002
+ ) -> tuple[AbstractBlock, list[int]]:
1003
+ """
1004
+ Function to create a single convolutional layer.
1005
+
1006
+ Args:
1007
+ layer_index (int): The index of the current layer.
1008
+ reps (int): Number of repetitions for this layer.
1009
+ current_indices (List[int]): Indices of qubits for the current layer.
1010
+ params (dict): Dictionary to store and retrieve parameters.
1011
+ operations (List[Any]): List of quantum operations to apply in the gates.
1012
+ entangler (Any): Entangling operation, such as CZ.
1013
+ n_qubits (int): Total number of qubits.
1014
+
1015
+ Returns:
1016
+ Tuple[AbstractBlock, List[int]]: A tuple containing the quantum block
1017
+ for the layer and the target indices for the next layer.
1018
+ """
1019
+ current_layer = []
1020
+ next_indices = []
1021
+ spacing = layer_index
1022
+
1023
+ if layer_index in [0, 1]: # Special behavior for first two layers
1024
+ layer_reps = []
1025
+ for rep in range(max_reps):
1026
+ rep_kron = []
1027
+ # Define qubit pairs based on odd/even repetition
1028
+ if rep % 2 == 0: # Even d: regular behavior
1029
+ pairs = zip(current_indices[::2], current_indices[1::2])
1030
+ else: # Odd d: shift downward, leaving qubits 0 and 7 free
1031
+ pairs = zip(current_indices[1:-1:2], current_indices[2:-1:2])
1032
+
1033
+ # Build the gate sequence for each pair
1034
+ for control, target in pairs:
1035
+ W_pairs = _create_single_W(
1036
+ params,
1037
+ operations,
1038
+ entangler,
1039
+ layer_index,
1040
+ rep,
1041
+ max_reps,
1042
+ control,
1043
+ target,
1044
+ spacing,
1045
+ n_qubits,
1046
+ is_corr,
1047
+ )
1048
+ tag(W_pairs, f"W{control,target}")
1049
+ rep_kron.append(W_pairs)
1050
+
1051
+ layer_reps.append(kron(*rep_kron))
1052
+
1053
+ # Combine all repetitions using `chain`
1054
+ current_layer.append(chain(*layer_reps))
1055
+
1056
+ else: # Original behavior for other layers
1057
+ for rep in range(max_reps):
1058
+ for control, target in zip(current_indices[::2], current_indices[1::2]):
1059
+ W_pairs = _create_single_W(
1060
+ params,
1061
+ operations,
1062
+ entangler,
1063
+ layer_index,
1064
+ rep,
1065
+ max_reps,
1066
+ control,
1067
+ target,
1068
+ spacing,
1069
+ n_qubits,
1070
+ is_corr,
1071
+ )
1072
+ current_layer.append(W_pairs)
1073
+
1074
+ # Update `next_indices` with the **targets** of the current layer
1075
+ next_indices = current_indices[1::2]
1076
+ return chain(*current_layer), next_indices
@@ -0,0 +1,158 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Callable
4
+ from qadence.blocks import chain
5
+ from qadence.parameters import Parameter
6
+ from qadence.blocks.utils import add, tag
7
+ from qadence.operations import Z, RX, CZ
8
+ from qadence.circuit import QuantumCircuit
9
+ from qadence.types import BackendName, DiffMode
10
+ from qadence.blocks.abstract import AbstractBlock
11
+
12
+ from .models import QNN
13
+ from qadence.ml_tools.constructors import _create_conv_layer, _create_feature_map_qcnn
14
+
15
+
16
+ class QCNN(QNN):
17
+ def __init__(
18
+ self,
19
+ n_inputs: int,
20
+ n_qubits: int,
21
+ depth: list[int],
22
+ operations: list[Any],
23
+ entangler: Any = CZ,
24
+ random_meas: bool = True,
25
+ fm_basis: str = "Fourier",
26
+ fm_gate: Any = RX,
27
+ is_corr: bool = False,
28
+ **kwargs: Any,
29
+ ) -> None:
30
+ """
31
+ Creates a QCNN model.
32
+
33
+ Args:
34
+ n_inputs (int): Number of input features.
35
+ n_qubits (int): Total number of qubits.
36
+ depth (list[int]): List defining the depth (repetitions) of each layer.
37
+ operations (list[Any]): List of quantum operations to apply
38
+ in the gates (e.g., [RX, RZ]).
39
+ entangler (Any): Entangling operation, such as CZ.
40
+ random_meas (bool): If True, applies random weighted measurements.
41
+ fm_basis (str): feature map basis.
42
+ fm_gate (Any): gate employed in the fm, such as.
43
+ **kwargs (Any): Additional keyword arguments for the parent QNN class.
44
+ """
45
+ self.n_inputs = n_inputs
46
+ self.n_qubits = n_qubits
47
+ self.depth = depth
48
+ self.operations = operations
49
+ self.entangler = entangler
50
+ self.random_meas = random_meas
51
+ self.fm_basis = fm_basis
52
+ self.fm_gate = fm_gate
53
+ self.is_corr = is_corr
54
+
55
+ circuit = self.qcnn_circuit(
56
+ self.n_inputs,
57
+ self.n_qubits,
58
+ self.depth,
59
+ self.operations,
60
+ self.entangler,
61
+ self.fm_basis,
62
+ self.fm_gate,
63
+ self.is_corr,
64
+ )
65
+
66
+ obs = self.qcnn_deferred_obs(self.n_qubits, self.random_meas)
67
+
68
+ super().__init__(
69
+ circuit=circuit,
70
+ observable=obs,
71
+ backend=BackendName.PYQTORCH,
72
+ diff_mode=DiffMode.AD,
73
+ inputs=[f"\u03C6_{i}" for i in range(self.n_inputs)],
74
+ **kwargs,
75
+ )
76
+
77
+ def qcnn_circuit(
78
+ self,
79
+ n_inputs: int,
80
+ n_qubits: int,
81
+ depth: list[int],
82
+ operations: list[Any],
83
+ entangler: AbstractBlock,
84
+ fm_basis: str,
85
+ fm_gate: AbstractBlock,
86
+ is_corr: bool,
87
+ ) -> QuantumCircuit:
88
+ """Defines the QCNN circuit."""
89
+ # Validate qubit count
90
+ if n_qubits < 4:
91
+ raise ValueError(
92
+ f"Invalid number of qubits: {n_qubits}. " "At least 4 qubits are required."
93
+ )
94
+ if n_qubits % 2 != 0:
95
+ raise ValueError(
96
+ f"Invalid number of qubits: {n_qubits}. " "The number of qubits must be even."
97
+ )
98
+
99
+ # Validate that all values in `depth` are odd
100
+ even_depths = [d for d in depth if d % 2 == 0]
101
+ if even_depths:
102
+ raise ValueError(
103
+ f"Invalid depth values: '{even_depths[0]}'. " "All the conv layer 'r's must be odd."
104
+ )
105
+
106
+ # Feature map (FM)
107
+ fm = _create_feature_map_qcnn(n_qubits, n_inputs, fm_basis, fm_gate)
108
+ tag(fm, "FM")
109
+
110
+ # Conv and Pool layer definition
111
+ conv_layers = []
112
+ params: dict[str, Parameter] = {}
113
+
114
+ # Define layer all the 2-qubit patterns based on depth
115
+ layer_patterns = [(2**layer_index, depth[layer_index]) for layer_index in range(len(depth))]
116
+
117
+ # Initialize all qubits for the current layer
118
+ current_indices = list(range(n_qubits))
119
+
120
+ # Build the circuit layer by layer using the helper
121
+ for layer_index, (_, reps) in enumerate(layer_patterns):
122
+ if reps == 0:
123
+ raise ValueError(f"Invalid layer {layer_index}: zero repetitions (reps = {reps}).")
124
+ if len(current_indices) < 2:
125
+ raise RuntimeError(
126
+ f"Layer {layer_index} requires at least 2 qubits, "
127
+ f"but found {len(current_indices)}."
128
+ )
129
+
130
+ layer_block, next_indices = _create_conv_layer(
131
+ layer_index, reps, current_indices, params, operations, entangler, n_qubits, is_corr
132
+ )
133
+ tag(layer_block, f"C+P layer {layer_index}")
134
+ conv_layers.append(layer_block)
135
+
136
+ # Update `current_indices` for the next layer
137
+ current_indices = next_indices
138
+
139
+ # Combine all layers for the final ansatz
140
+ ansatz = chain(*conv_layers)
141
+
142
+ return QuantumCircuit(n_qubits, fm, ansatz)
143
+
144
+ def qcnn_deferred_obs(
145
+ self, n_qubits: int, random_meas: bool
146
+ ) -> AbstractBlock | list[AbstractBlock]:
147
+ """
148
+ Defines the measurements to be performedthe traced out.
149
+
150
+ and remaining qubits.
151
+ """
152
+ if random_meas:
153
+ w1 = [Parameter(f"w{i}") for i in range(n_qubits)]
154
+ obs = add(Z(i) * w for i, w in zip(range(n_qubits), w1))
155
+ else:
156
+ obs = add(Z(i) for i in range(n_qubits))
157
+
158
+ return obs
qadence/model.py CHANGED
@@ -27,7 +27,12 @@ from qadence.mitigations import Mitigations
27
27
  from qadence.noise import NoiseHandler
28
28
  from qadence.parameters import Parameter
29
29
  from qadence.types import DiffMode, Endianness
30
- from qadence.utils import block_to_mathematical_expression
30
+ from qadence.utils import (
31
+ block_to_mathematical_expression,
32
+ check_param_dict_values,
33
+ merge_separate_params,
34
+ )
35
+ from qadence.transpile import set_as_variational, set_as_fixed
31
36
 
32
37
  logger = getLogger(__name__)
33
38
 
@@ -142,18 +147,31 @@ class QuantumModel(nn.Module):
142
147
  self._measurement = measurement
143
148
  self._noise = noise
144
149
  self._mitigation = mitigation
145
- self._params = nn.ParameterDict(
146
- {
147
- str(key): nn.Parameter(val, requires_grad=val.requires_grad)
148
- for key, val in conv.params.items()
149
- }
150
- )
150
+ if check_param_dict_values(conv.params):
151
+ self._params = nn.ParameterDict(
152
+ {
153
+ str(key): nn.Parameter(val, requires_grad=val.requires_grad) # type: ignore[union-attr]
154
+ for key, val in conv.params.items()
155
+ }
156
+ )
157
+ else:
158
+ self._params = nn.ParameterDict(
159
+ {
160
+ str(key): nn.Parameter(val, requires_grad=val.requires_grad) # type: ignore[union-attr]
161
+ for key, val in merge_separate_params(conv.params).items()
162
+ }
163
+ )
151
164
 
152
165
  @property
153
166
  def vparams(self) -> OrderedDict:
154
167
  """Variational parameters."""
155
168
  return OrderedDict({k: v.data for k, v in self._params.items() if v.requires_grad})
156
169
 
170
+ @property
171
+ def params(self) -> OrderedDict:
172
+ """All parameters."""
173
+ return OrderedDict({k: v.data for k, v in self._params.items()})
174
+
157
175
  @property
158
176
  def vals_vparams(self) -> Tensor:
159
177
  """Dictionary with parameters which are actually updated during optimization."""
@@ -176,6 +194,93 @@ class QuantumModel(nn.Module):
176
194
  """The number of variational parameters."""
177
195
  return len(self.vals_vparams)
178
196
 
197
+ @property
198
+ def show_config(self) -> str:
199
+ """Attain current quantum model configurations."""
200
+ if isinstance(self.backend, DifferentiableBackend):
201
+ current_config = self.backend.backend.config
202
+ return BackendConfiguration.available_options(current_config)
203
+
204
+ def change_config(self, new_config: dict) -> None:
205
+ """Change configuration with the input."""
206
+ if isinstance(self.backend, DifferentiableBackend):
207
+ current_config = self.backend.backend.config
208
+ BackendConfiguration.change_config(current_config, new_config)
209
+
210
+ def set_as_variational(self, params: list[str] = list()) -> None:
211
+ """Set as variational the list of names in `params`.
212
+
213
+ Args:
214
+ params (list[str], optional): List of parameters to fix. Defaults to list().
215
+ """
216
+ circuit: QuantumCircuit = self._circuit.original
217
+ if self._observable is not None:
218
+ if isinstance(self._observable, list):
219
+ for obs in self._observable:
220
+ set_as_variational(obs.original, params)
221
+ observable = [obs.original for obs in self._observable]
222
+ else:
223
+ set_as_variational(self._observable.original, params)
224
+ observable = [self._observable.original]
225
+ else:
226
+ observable = self._observable # type: ignore[assignment]
227
+ set_as_variational(circuit.block, params)
228
+ conv = self.backend.convert(circuit, observable)
229
+ self.embedding_fn = conv.embedding_fn
230
+ self._circuit = conv.circuit
231
+ self._observable = conv.observable
232
+ if check_param_dict_values(conv.params):
233
+ self._params = nn.ParameterDict(
234
+ {
235
+ str(key): nn.Parameter(val, requires_grad=val.requires_grad) # type: ignore[union-attr]
236
+ for key, val in conv.params.items()
237
+ }
238
+ )
239
+ else:
240
+ self._params = nn.ParameterDict(
241
+ {
242
+ str(key): nn.Parameter(val, requires_grad=val.requires_grad) # type: ignore[union-attr]
243
+ for key, val in merge_separate_params(conv.params).items()
244
+ }
245
+ )
246
+
247
+ def set_as_fixed(self, params: list[str] = list()) -> None:
248
+ """Set as fixed the list of names in `params`.
249
+
250
+ Args:
251
+ params (list[str], optional): List of parameters to fix. Defaults to list().
252
+ """
253
+ circuit: QuantumCircuit = self._circuit.original
254
+ if self._observable is not None:
255
+ if isinstance(self._observable, list):
256
+ for obs in self._observable:
257
+ set_as_fixed(obs.original, params)
258
+ observable = [obs.original for obs in self._observable]
259
+ else:
260
+ set_as_fixed(self._observable.original, params)
261
+ observable = [self._observable.original]
262
+ else:
263
+ observable = self._observable # type: ignore[assignment]
264
+ set_as_fixed(circuit.block, params)
265
+ conv = self.backend.convert(circuit, observable)
266
+ self.embedding_fn = conv.embedding_fn
267
+ self._circuit = conv.circuit
268
+ self._observable = conv.observable
269
+ if check_param_dict_values(conv.params):
270
+ self._params = nn.ParameterDict(
271
+ {
272
+ str(key): nn.Parameter(val, requires_grad=val.requires_grad) # type: ignore[union-attr]
273
+ for key, val in conv.params.items()
274
+ }
275
+ )
276
+ else:
277
+ self._params = nn.ParameterDict(
278
+ {
279
+ str(key): nn.Parameter(val, requires_grad=val.requires_grad) # type: ignore[union-attr]
280
+ for key, val in merge_separate_params(conv.params).items()
281
+ }
282
+ )
283
+
179
284
  def circuit(self, circuit: QuantumCircuit) -> ConvertedCircuit:
180
285
  """Get backend-converted circuit.
181
286
 
@@ -520,7 +625,7 @@ class QuantumModel(nn.Module):
520
625
  file_path = file_path / get_latest_checkpoint_name(file_path, "model")
521
626
 
522
627
  try:
523
- qm_pt = torch.load(file_path, map_location=map_location)
628
+ qm_pt = torch.load(file_path, map_location=map_location, weights_only=False)
524
629
  except Exception as e:
525
630
  logger.error(f"Unable to load QuantumModel due to {e}")
526
631
  return cls._from_dict(qm_pt, as_torch)
qadence/parameters.py CHANGED
@@ -225,6 +225,8 @@ def sympy_to_numeric(expr: Basic) -> TNumber:
225
225
  if expr.as_real_imag()[1] != 0:
226
226
  return complex(expr)
227
227
  else:
228
+ if expr.is_Integer:
229
+ return int(expr)
228
230
  return float(expr)
229
231
 
230
232
 
qadence/serialization.py CHANGED
@@ -226,7 +226,7 @@ def save_json(d: dict, file_path: str | Path) -> None:
226
226
 
227
227
 
228
228
  def load_pt(file_path: str | Path, map_location: str) -> Any:
229
- return torch.load(file_path, map_location=map_location)
229
+ return torch.load(file_path, map_location=map_location, weights_only=False)
230
230
 
231
231
 
232
232
  def load_json(file_path: str | Path, map_location: str) -> Any:
@@ -5,7 +5,8 @@ from .block import (
5
5
  chain_single_qubit_ops,
6
6
  repeat,
7
7
  scale_primitive_blocks_only,
8
- set_trainable,
8
+ set_as_variational,
9
+ set_as_fixed,
9
10
  validate,
10
11
  )
11
12
  from .circuit import fill_identities
@@ -15,4 +16,4 @@ from .invert import invert_endianness, reassign
15
16
  from .noise import set_noise
16
17
  from .transpile import blockfn_to_circfn, transpile
17
18
 
18
- __all__ = ["set_trainable", "invert_endianness", "set_noise"]
19
+ __all__ = ["set_as_variational", "set_as_fixed", "invert_endianness", "set_noise"]