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.
- qadence/backend.py +33 -10
- qadence/backends/agpsr_utils.py +96 -0
- qadence/backends/api.py +8 -1
- qadence/backends/horqrux/backend.py +24 -10
- qadence/backends/horqrux/config.py +17 -1
- qadence/backends/horqrux/convert_ops.py +20 -97
- qadence/backends/jax_utils.py +5 -2
- qadence/backends/{gpsr.py → parameter_shift_rules.py} +48 -30
- qadence/backends/pulser/backend.py +16 -9
- qadence/backends/pulser/config.py +18 -0
- qadence/backends/pyqtorch/backend.py +25 -11
- qadence/backends/pyqtorch/config.py +18 -0
- qadence/blocks/embedding.py +10 -1
- qadence/blocks/primitive.py +2 -3
- qadence/blocks/utils.py +33 -24
- qadence/engines/differentiable_backend.py +7 -1
- qadence/engines/jax/differentiable_backend.py +7 -1
- qadence/engines/torch/differentiable_backend.py +12 -9
- qadence/engines/torch/differentiable_expectation.py +12 -11
- qadence/extensions.py +0 -10
- qadence/ml_tools/__init__.py +2 -0
- qadence/ml_tools/callbacks/callbackmanager.py +4 -2
- qadence/ml_tools/constructors.py +264 -4
- qadence/ml_tools/qcnn_model.py +158 -0
- qadence/model.py +113 -8
- qadence/parameters.py +2 -0
- qadence/serialization.py +1 -1
- qadence/transpile/__init__.py +3 -2
- qadence/transpile/block.py +58 -5
- qadence/types.py +2 -4
- qadence/utils.py +39 -8
- {qadence-1.11.1.dist-info → qadence-1.11.3.dist-info}/METADATA +22 -11
- {qadence-1.11.1.dist-info → qadence-1.11.3.dist-info}/RECORD +35 -33
- qadence-1.11.3.dist-info/licenses/LICENSE +13 -0
- qadence-1.11.1.dist-info/licenses/LICENSE +0 -202
- {qadence-1.11.1.dist-info → qadence-1.11.3.dist-info}/WHEEL +0 -0
qadence/ml_tools/constructors.py
CHANGED
@@ -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
|
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,
|
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
|
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
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
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:
|
qadence/transpile/__init__.py
CHANGED
@@ -5,7 +5,8 @@ from .block import (
|
|
5
5
|
chain_single_qubit_ops,
|
6
6
|
repeat,
|
7
7
|
scale_primitive_blocks_only,
|
8
|
-
|
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__ = ["
|
19
|
+
__all__ = ["set_as_variational", "set_as_fixed", "invert_endianness", "set_noise"]
|