|
35 | 35 | try:
|
36 | 36 | from .lightning_qubit_ops import (
|
37 | 37 | StateVectorC64,
|
38 |
| - ObsStructC64, |
39 | 38 | StateVectorC128,
|
40 |
| - ObsStructC128, |
| 39 | + ) |
| 40 | + from .lightning_qubit_ops.adjoint_diff import ( |
| 41 | + NamedObsC64, |
| 42 | + NamedObsC128, |
| 43 | + HermitianObsC64, |
| 44 | + HermitianObsC128, |
| 45 | + TensorProdObsC64, |
| 46 | + TensorProdObsC128, |
| 47 | + HamiltonianC64, |
| 48 | + HamiltonianC128, |
| 49 | + OpsStructC64, |
| 50 | + OpsStructC128, |
41 | 51 | )
|
42 | 52 | except ImportError:
|
43 | 53 | pass
|
44 | 54 |
|
45 | 55 |
|
46 |
| -def _obs_has_kernel(obs: Observable) -> bool: |
| 56 | +def _obs_has_kernel(ob: Observable) -> bool: |
47 | 57 | """Returns True if the input observable has a supported kernel in the C++ backend.
|
48 | 58 |
|
49 | 59 | Args:
|
50 |
| - obs (Observable): the input observable |
| 60 | + ob (Observable): the input observable |
51 | 61 |
|
52 | 62 | Returns:
|
53 | 63 | bool: indicating whether ``obs`` has a dedicated kernel in the backend
|
54 | 64 | """
|
55 |
| - if is_pauli_word(obs): |
| 65 | + if is_pauli_word(ob): |
56 | 66 | return True
|
57 |
| - if isinstance(obs, (Hadamard, Projector)): |
| 67 | + if isinstance(ob, (Hadamard, Projector)): |
58 | 68 | return True
|
59 |
| - if isinstance(obs, Tensor): |
60 |
| - return all(_obs_has_kernel(o) for o in obs.obs) |
| 69 | + if isinstance(ob, Tensor): |
| 70 | + return all(_obs_has_kernel(o) for o in ob.obs) |
61 | 71 | return False
|
62 | 72 |
|
63 | 73 |
|
64 |
| -def _serialize_obs(tape: QuantumTape, wires_map: dict, use_csingle: bool = False) -> List: |
65 |
| - """Serializes the observables of an input tape. |
66 |
| -
|
67 |
| - Args: |
68 |
| - tape (QuantumTape): the input quantum tape |
69 |
| - wires_map (dict): a dictionary mapping input wires to the device's backend wires |
70 |
| - use_csingle (bool): whether to use np.complex64 instead of np.complex128 |
71 |
| -
|
72 |
| - Returns: |
73 |
| - list(ObsStructC128 or ObsStructC64): A list of observable objects compatible with the C++ backend |
74 |
| - """ |
75 |
| - obs = [] |
| 74 | +def _serialize_named_hermitian_ob(o, wires_map: dict, use_csingle: bool): |
| 75 | + """Serializes an observable (Named or Hermitian)""" |
| 76 | + assert not isinstance(o, Tensor) |
76 | 77 |
|
77 | 78 | if use_csingle:
|
78 | 79 | ctype = np.complex64
|
79 |
| - obs_py = ObsStructC64 |
| 80 | + named_obs = NamedObsC64 |
| 81 | + hermitian_obs = HermitianObsC64 |
80 | 82 | else:
|
81 | 83 | ctype = np.complex128
|
82 |
| - obs_py = ObsStructC128 |
| 84 | + named_obs = NamedObsC128 |
| 85 | + hermitian_obs = HermitianObsC128 |
83 | 86 |
|
84 |
| - for o in tape.observables: |
85 |
| - is_tensor = isinstance(o, Tensor) |
| 87 | + wires_list = o.wires.tolist() |
| 88 | + wires = [wires_map[w] for w in wires_list] |
| 89 | + if _obs_has_kernel(o): |
| 90 | + return named_obs(o.name, wires) |
| 91 | + return hermitian_obs(qml.matrix(o).ravel().astype(ctype), wires) |
86 | 92 |
|
87 |
| - wires = [] |
88 | 93 |
|
89 |
| - if is_tensor: |
90 |
| - for o_ in o.obs: |
91 |
| - wires_list = o_.wires.tolist() |
92 |
| - w = [wires_map[w] for w in wires_list] |
93 |
| - wires.append(w) |
94 |
| - else: |
95 |
| - wires_list = o.wires.tolist() |
96 |
| - w = [wires_map[w] for w in wires_list] |
97 |
| - wires.append(w) |
| 94 | +def _serialize_tensor_ob(ob, wires_map: dict, use_csingle: bool): |
| 95 | + """Serialize a tensor observable""" |
| 96 | + assert isinstance(ob, Tensor) |
98 | 97 |
|
99 |
| - name = o.name if is_tensor else [o.name] |
| 98 | + if use_csingle: |
| 99 | + tensor_obs = TensorProdObsC64 |
| 100 | + else: |
| 101 | + tensor_obs = TensorProdObsC128 |
100 | 102 |
|
101 |
| - params = [] |
| 103 | + return tensor_obs([_serialize_ob(o, wires_map, use_csingle) for o in ob.obs]) |
102 | 104 |
|
103 |
| - if not _obs_has_kernel(o): |
104 |
| - if is_tensor: |
105 |
| - for o_ in o.obs: |
106 |
| - if not _obs_has_kernel(o_): |
107 |
| - params.append(qml.matrix(o_).ravel().astype(ctype)) |
108 |
| - else: |
109 |
| - params.append([]) |
110 |
| - else: |
111 |
| - params.append(qml.matrix(o).ravel().astype(ctype)) |
112 | 105 |
|
113 |
| - ob = obs_py(name, params, wires) |
114 |
| - obs.append(ob) |
| 106 | +def _serialize_hamiltonian(ob, wires_map: dict, use_csingle: bool): |
| 107 | + if use_csingle: |
| 108 | + rtype = np.float32 |
| 109 | + hamiltonian_obs = HamiltonianC64 |
| 110 | + else: |
| 111 | + rtype = np.float64 |
| 112 | + hamiltonian_obs = HamiltonianC128 |
| 113 | + |
| 114 | + coeffs = np.array(ob.coeffs).astype(rtype) |
| 115 | + terms = [_serialize_ob(t, wires_map, use_csingle) for t in ob.ops] |
| 116 | + return hamiltonian_obs(coeffs, terms) |
| 117 | + |
| 118 | + |
| 119 | +def _serialize_ob(ob, wires_map, use_csingle): |
| 120 | + if isinstance(ob, Tensor): |
| 121 | + return _serialize_tensor_ob(ob, wires_map, use_csingle) |
| 122 | + elif ob.name == "Hamiltonian": |
| 123 | + return _serialize_hamiltonian(ob, wires_map, use_csingle) |
| 124 | + else: |
| 125 | + return _serialize_named_hermitian_ob(ob, wires_map, use_csingle) |
| 126 | + |
| 127 | + |
| 128 | +def _serialize_observables(tape: QuantumTape, wires_map: dict, use_csingle: bool = False) -> List: |
| 129 | + """Serializes the observables of an input tape. |
| 130 | +
|
| 131 | + Args: |
| 132 | + tape (QuantumTape): the input quantum tape |
| 133 | + wires_map (dict): a dictionary mapping input wires to the device's backend wires |
| 134 | + use_csingle (bool): whether to use np.complex64 instead of np.complex128 |
| 135 | +
|
| 136 | + Returns: |
| 137 | + list(ObsStructC128 or ObsStructC64): A list of observable objects compatible with the C++ backend |
| 138 | + """ |
115 | 139 |
|
116 |
| - return obs |
| 140 | + return [_serialize_ob(ob, wires_map, use_csingle) for ob in tape.observables] |
117 | 141 |
|
118 | 142 |
|
119 | 143 | def _serialize_ops(
|
|
0 commit comments