Coverage for jaxquantum/circuits/gates.py: 90%
73 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-27 22:28 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-27 22:28 +0000
1"""Gates."""
3from copy import deepcopy
4from flax import struct
5from jax import Array, config
6from typing import List, Dict, Any, Optional, Callable, Union
7import jax.numpy as jnp
10from jaxquantum.core.qarray import Qarray, concatenate
12config.update("jax_enable_x64", True)
15@struct.dataclass
16class Gate:
17 dims: List[int] = struct.field(pytree_node=False)
18 _U: Optional[Array] # Unitary
19 _Ht: Optional[Array] # Hamiltonian
20 _KM: Optional[Qarray] # Kraus map
21 _c_ops: Optional[Qarray]
22 _params: Dict[str, Any]
23 _ts: Array
24 _name: str = struct.field(pytree_node=False)
25 num_modes: int = struct.field(pytree_node=False)
26 _gen_KM: Optional[Callable] = struct.field(pytree_node=False, default=None)
27 _channel_apply: Optional[Callable] = struct.field(
28 pytree_node=False, default=None
29 )
31 @classmethod
32 def create(
33 cls,
34 dims: Union[int, List[int]],
35 name: str = "Gate",
36 params: Optional[Dict[str, Any]] = None,
37 ts: Optional[Array] = None,
38 gen_U: Optional[Callable[[Dict[str, Any]], Qarray]] = None,
39 gen_Ht: Optional[Callable[[Dict[str, Any]], Qarray]] = None,
40 gen_c_ops: Optional[Callable[[Dict[str, Any]], Qarray]] = None,
41 gen_KM: Optional[Callable[[Dict[str, Any]], List[Qarray]]] = None,
42 channel_apply: Optional[Callable[[Array, Dict[str, Any]], Array]] = None,
43 lazy_kraus: bool = False,
44 num_modes: int = 1,
45 ):
46 """Create a gate.
48 Args:
49 dims: Dimensions of the gate.
50 name: Name of the gate.
51 params: Parameters of the gate.
52 ts: Times of the gate.
53 gen_U: Function to generate the unitary of the gate.
54 gen_Ht: Function to generate a function Ht(t) that takes in a time t and outputs a Hamiltonian Qarray.
55 gen_KM: Function to generate the Kraus map of the gate.
56 channel_apply: Optional direct density-matrix channel kernel.
57 lazy_kraus: Generate Kraus operators only when ``KM`` is accessed.
58 num_modes: Number of modes of the gate.
59 """
61 # TODO: add params to device?
63 if isinstance(dims, int):
64 dims = [dims]
66 assert len(dims) == num_modes, (
67 "Number of dimensions must match number of modes."
68 )
70 # Unitary
71 _U = gen_U(params) if gen_U is not None else None
72 _Ht = gen_Ht(params) if gen_Ht is not None else None
73 _c_ops = gen_c_ops(params) if gen_c_ops is not None else Qarray.from_list([])
75 _KM = gen_KM(params) if gen_KM is not None and not lazy_kraus else None
77 return Gate(
78 dims = dims,
79 _U = _U,
80 _Ht = _Ht,
81 _KM = _KM,
82 _c_ops = _c_ops,
83 _gen_KM=gen_KM if lazy_kraus else None,
84 _channel_apply=channel_apply,
85 _params = params if params is not None else {},
86 _ts=ts if ts is not None else jnp.array([]),
87 _name=name,
88 num_modes=num_modes,
89 )
91 def __str__(self):
92 return self._name
94 def __repr__(self):
95 return self._name
97 @property
98 def name(self):
99 return self._name
101 @property
102 def U(self):
103 return self._U
105 @property
106 def Ht(self):
107 return self._Ht
109 @property
110 def KM(self):
111 if self._KM is not None:
112 return self._KM
113 if self._gen_KM is not None:
114 return self._gen_KM(self.params)
115 if self._U is None:
116 return Qarray.from_list([])
117 impl = type(self._U._impl).from_data(self._U.data[None])
118 return Qarray._from_impl(impl, self._U._qdims)
120 @property
121 def c_ops(self):
122 return self._c_ops
124 @property
125 def channel_apply(self):
126 """Direct density-matrix kernel, if defined."""
127 return self._channel_apply
129 @property
130 def params(self):
131 return self._params
133 @property
134 def ts(self):
135 return self._ts
137 def add_Ht(self, Ht: Callable[[float], Qarray]):
138 """Add a Hamiltonian function to the gate."""
139 def new_Ht(t):
140 return Ht(t) + self.Ht(t) if self.Ht is not None else Ht(t)
142 return Gate(
143 dims = self.dims,
144 _U = self.U,
145 _Ht = new_Ht,
146 _KM = self._KM,
147 _c_ops = self.c_ops,
148 _gen_KM=self._gen_KM,
149 _channel_apply=self._channel_apply,
150 _params = self.params,
151 _ts = self.ts,
152 _name = self.name,
153 num_modes = self.num_modes,
154 )
156 def add_c_ops(self, c_ops: Qarray):
157 """Add a c_ops to the gate."""
158 return Gate(
159 dims = self.dims,
160 _U = self.U,
161 _Ht = self.Ht,
162 _KM = self._KM,
163 _c_ops = concatenate([self.c_ops, c_ops]),
164 _gen_KM=self._gen_KM,
165 _channel_apply=self._channel_apply,
166 _params = self.params,
167 _ts = self.ts,
168 _name = self.name,
169 num_modes = self.num_modes,
170 )
172 def copy(self):
173 """Return a copy of the gate."""
174 return Gate(
175 dims = deepcopy(self.dims),
176 _U = self.U,
177 _Ht = deepcopy(self.Ht),
178 _KM = self._KM,
179 _c_ops = self.c_ops,
180 _gen_KM=self._gen_KM,
181 _channel_apply=self._channel_apply,
182 _params = deepcopy(self.params),
183 _ts = self.ts,
184 _name = self.name,
185 num_modes = self.num_modes,
186 )