tensorcircuit-nightly 1.2.0.dev20250326__py3-none-any.whl → 1.4.0.dev20251128__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.

Potentially problematic release.


This version of tensorcircuit-nightly might be problematic. Click here for more details.

Files changed (77) hide show
  1. tensorcircuit/__init__.py +5 -1
  2. tensorcircuit/abstractcircuit.py +4 -0
  3. tensorcircuit/analogcircuit.py +413 -0
  4. tensorcircuit/applications/layers.py +1 -1
  5. tensorcircuit/applications/van.py +1 -1
  6. tensorcircuit/backends/abstract_backend.py +312 -5
  7. tensorcircuit/backends/cupy_backend.py +3 -1
  8. tensorcircuit/backends/jax_backend.py +100 -4
  9. tensorcircuit/backends/jax_ops.py +108 -0
  10. tensorcircuit/backends/numpy_backend.py +49 -3
  11. tensorcircuit/backends/pytorch_backend.py +92 -3
  12. tensorcircuit/backends/tensorflow_backend.py +102 -3
  13. tensorcircuit/basecircuit.py +157 -98
  14. tensorcircuit/circuit.py +115 -57
  15. tensorcircuit/cloud/local.py +1 -1
  16. tensorcircuit/cloud/quafu_provider.py +1 -1
  17. tensorcircuit/cloud/tencent.py +1 -1
  18. tensorcircuit/compiler/simple_compiler.py +2 -2
  19. tensorcircuit/cons.py +105 -23
  20. tensorcircuit/densitymatrix.py +16 -11
  21. tensorcircuit/experimental.py +733 -153
  22. tensorcircuit/fgs.py +254 -73
  23. tensorcircuit/gates.py +66 -22
  24. tensorcircuit/interfaces/jax.py +5 -3
  25. tensorcircuit/interfaces/tensortrans.py +6 -2
  26. tensorcircuit/interfaces/torch.py +14 -4
  27. tensorcircuit/keras.py +3 -3
  28. tensorcircuit/mpscircuit.py +154 -65
  29. tensorcircuit/quantum.py +698 -134
  30. tensorcircuit/quditcircuit.py +733 -0
  31. tensorcircuit/quditgates.py +618 -0
  32. tensorcircuit/results/counts.py +131 -18
  33. tensorcircuit/results/readout_mitigation.py +4 -1
  34. tensorcircuit/shadows.py +1 -1
  35. tensorcircuit/simplify.py +3 -1
  36. tensorcircuit/stabilizercircuit.py +29 -17
  37. tensorcircuit/templates/__init__.py +2 -0
  38. tensorcircuit/templates/blocks.py +2 -2
  39. tensorcircuit/templates/hamiltonians.py +174 -0
  40. tensorcircuit/templates/lattice.py +1789 -0
  41. tensorcircuit/timeevol.py +896 -0
  42. tensorcircuit/translation.py +10 -3
  43. tensorcircuit/utils.py +7 -0
  44. {tensorcircuit_nightly-1.2.0.dev20250326.dist-info → tensorcircuit_nightly-1.4.0.dev20251128.dist-info}/METADATA +66 -29
  45. tensorcircuit_nightly-1.4.0.dev20251128.dist-info/RECORD +96 -0
  46. {tensorcircuit_nightly-1.2.0.dev20250326.dist-info → tensorcircuit_nightly-1.4.0.dev20251128.dist-info}/WHEEL +1 -1
  47. {tensorcircuit_nightly-1.2.0.dev20250326.dist-info → tensorcircuit_nightly-1.4.0.dev20251128.dist-info}/top_level.txt +0 -1
  48. tensorcircuit_nightly-1.2.0.dev20250326.dist-info/RECORD +0 -118
  49. tests/__init__.py +0 -0
  50. tests/conftest.py +0 -67
  51. tests/test_backends.py +0 -1035
  52. tests/test_calibrating.py +0 -149
  53. tests/test_channels.py +0 -409
  54. tests/test_circuit.py +0 -1699
  55. tests/test_cloud.py +0 -219
  56. tests/test_compiler.py +0 -147
  57. tests/test_dmcircuit.py +0 -555
  58. tests/test_ensemble.py +0 -72
  59. tests/test_fgs.py +0 -310
  60. tests/test_gates.py +0 -156
  61. tests/test_interfaces.py +0 -562
  62. tests/test_keras.py +0 -160
  63. tests/test_miscs.py +0 -282
  64. tests/test_mpscircuit.py +0 -341
  65. tests/test_noisemodel.py +0 -156
  66. tests/test_qaoa.py +0 -86
  67. tests/test_qem.py +0 -152
  68. tests/test_quantum.py +0 -549
  69. tests/test_quantum_attr.py +0 -42
  70. tests/test_results.py +0 -380
  71. tests/test_shadows.py +0 -160
  72. tests/test_simplify.py +0 -46
  73. tests/test_stabilizer.py +0 -217
  74. tests/test_templates.py +0 -218
  75. tests/test_torchnn.py +0 -99
  76. tests/test_van.py +0 -102
  77. {tensorcircuit_nightly-1.2.0.dev20250326.dist-info → tensorcircuit_nightly-1.4.0.dev20251128.dist-info}/licenses/LICENSE +0 -0
tensorcircuit/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "1.2.0.dev20250326"
1
+ __version__ = "1.4.0.dev20251128"
2
2
  __author__ = "TensorCircuit Authors"
3
3
  __creator__ = "refraction-ray"
4
4
 
@@ -23,8 +23,11 @@ from .cons import (
23
23
  runtime_contractor,
24
24
  ) # prerun of set hooks
25
25
  from . import gates
26
+ from . import quditgates
26
27
  from . import basecircuit
27
28
  from .gates import Gate
29
+ from .quditcircuit import QuditCircuit
30
+ from .analogcircuit import AnalogCircuit
28
31
  from .circuit import Circuit, expectation
29
32
  from .mpscircuit import MPSCircuit
30
33
  from .densitymatrix import DMCircuit as DMCircuit_reference
@@ -52,6 +55,7 @@ from . import compiler
52
55
  from . import cloud
53
56
  from . import fgs
54
57
  from .fgs import FGSSimulator
58
+ from . import timeevol
55
59
 
56
60
  FGSCircuit = FGSSimulator
57
61
 
@@ -53,6 +53,7 @@ vgates = [
53
53
  "any",
54
54
  "exp",
55
55
  "exp1",
56
+ "su4",
56
57
  ]
57
58
  mpogates = ["multicontrol", "mpo"]
58
59
  gate_aliases = [
@@ -65,9 +66,12 @@ gate_aliases = [
65
66
  ["td", "tdg"],
66
67
  ]
67
68
 
69
+ defined_gates = sgates + vgates + mpogates + [alias[1] for alias in gate_aliases]
70
+
68
71
 
69
72
  class AbstractCircuit:
70
73
  _nqubits: int
74
+ _d: int = 2
71
75
  _qir: List[Dict[str, Any]]
72
76
  _extra_qir: List[Dict[str, Any]]
73
77
  inputs: Tensor
@@ -0,0 +1,413 @@
1
+ """
2
+ Analog-Digital Hybrid Circuit class wrapper
3
+ only support jax backend
4
+ """
5
+
6
+ from typing import Any, List, Optional, Callable, Dict, Tuple, Union, Sequence
7
+ from dataclasses import dataclass
8
+ from functools import partial
9
+
10
+ import numpy as np
11
+ import tensornetwork as tn
12
+
13
+ from .cons import backend, rdtypestr
14
+ from .abstractcircuit import defined_gates
15
+ from .circuit import Circuit
16
+ from .quantum import QuOperator
17
+ from .timeevol import ode_evol_global, ode_evol_local
18
+ from .utils import arg_alias
19
+
20
+ Tensor = Any
21
+
22
+
23
+ @dataclass
24
+ class AnalogBlock:
25
+ """
26
+ A data structure to hold information about an analog evolution block.
27
+ """
28
+
29
+ hamiltonian_func: Callable[[Tensor], Tensor]
30
+ time: float
31
+ index: Optional[List[int]] = None
32
+ solver_options: Optional[Dict[str, Any]] = None
33
+
34
+
35
+ class AnalogCircuit:
36
+ """
37
+ A class for hybrid digital-analog quantum simulation with time-dependent Hamiltonians.
38
+ """
39
+
40
+ def __init__(
41
+ self,
42
+ nqubits: int,
43
+ inputs: Optional[Tensor] = None,
44
+ mps_inputs: Optional[QuOperator] = None,
45
+ split: Optional[Dict[str, Any]] = None,
46
+ dim: Optional[int] = None,
47
+ ):
48
+ """
49
+ Initializes the hybrid circuit.
50
+
51
+ :param nqubits: The number of qubits in the circuit.
52
+ :type nqubits: int
53
+ :param dim: The local Hilbert space dimension per site. Qudit is supported for 2 <= d <= 36.
54
+ :type dim: If None, the dimension of the circuit will be `2`, which is a qubit system.
55
+ :param inputs: If not None, the initial state of the circuit is taken as ``inputs``
56
+ instead of :math:`\vert 0 \rangle^n` qubits, defaults to None.
57
+ :type inputs: Optional[Tensor], optional
58
+ :param mps_inputs: QuVector for a MPS like initial wavefunction.
59
+ :type mps_inputs: Optional[QuOperator]
60
+ :param split: dict if two qubit gate is ready for split, including parameters for at least one of
61
+ ``max_singular_values`` and ``max_truncation_err``.
62
+ :type split: Optional[Dict[str, Any]]
63
+ """
64
+ self.num_qubits, self._nqubits = nqubits, nqubits
65
+ self.dim = 2**self.num_qubits
66
+ self.inputs = inputs
67
+ if inputs is None:
68
+ self.inputs = np.zeros([self.dim])
69
+ self.inputs[0] = 1.0
70
+ self.inputs = backend.convert_to_tensor(self.inputs)
71
+
72
+ # List of digital circuits, starting with one empty circuit.
73
+ self.digital_circuits: List[Circuit] = [
74
+ Circuit(self.num_qubits, inputs, mps_inputs, split, dim)
75
+ ]
76
+
77
+ # List of analog blocks, each containing the Hamiltonian function, time, and solver options.
78
+ self.analog_blocks: List[AnalogBlock] = []
79
+ self._effective_circuit: Optional[Circuit] = None
80
+ self._solver_options: Dict[str, Any] = {}
81
+
82
+ def set_solver_options(self, **kws: Any) -> None:
83
+ """
84
+ set solver options globally for this circuit object
85
+ """
86
+ self._solver_options = kws
87
+
88
+ @property
89
+ def effective_circuit(self) -> Circuit:
90
+ """
91
+ Returns the effective circuit after all blocks have been added.
92
+ """
93
+ if self._effective_circuit is None:
94
+ self.state()
95
+ return self._effective_circuit # type: ignore
96
+
97
+ @property
98
+ def current_digital_circuit(self) -> Circuit:
99
+ """
100
+ Returns the last (currently active) digital circuit.
101
+ """
102
+ return self.digital_circuits[-1]
103
+
104
+ def add_analog_block(
105
+ self,
106
+ hamiltonian: Callable[[float], Tensor],
107
+ time: Union[float, List[Tensor]],
108
+ index: Optional[List[int]] = None,
109
+ **solver_options: Any,
110
+ ) -> "AnalogCircuit":
111
+ """
112
+ Adds a time-dependent analog evolution block to the circuit.
113
+
114
+ This finalizes the current digital block and prepares a new one for subsequent gates.
115
+
116
+ :param hamiltonian_func: A function H(t) that takes a time `t` (from 0 to `time`)
117
+ and returns the Hamiltonian matrix at that instant.
118
+ :type hamiltonian_func: Callable[[float], np.ndarray]
119
+ :param time: The total evolution time 'T'.
120
+ :type time: float
121
+ :param index: The indices of the qubits to apply the analog evolution to. Defaults None for
122
+ global application.
123
+ :type index: Optional[List[int]]
124
+ :param solver_options: Keyword arguments passed directly to `tc.timeevol.ode_evolve`
125
+ :type solver_options: Dict[str, Any]
126
+ """
127
+ # Create and store the analog block information
128
+ time = backend.convert_to_tensor(time, dtype=rdtypestr)
129
+ time = backend.reshape(time, [-1])
130
+ if backend.shape_tuple(time)[0] == 1:
131
+ time = backend.stack([0.0, time[0]]) # type: ignore
132
+ elif backend.shape_tuple(time)[0] > 2:
133
+ raise ValueError(
134
+ "Time must be a scalar or a two elements array for the starting and end points."
135
+ )
136
+ combined_solver_options = self._solver_options.copy()
137
+ combined_solver_options.update(solver_options)
138
+ block = AnalogBlock(
139
+ hamiltonian_func=hamiltonian,
140
+ time=time, # type: ignore
141
+ index=index,
142
+ solver_options=combined_solver_options,
143
+ )
144
+ self.analog_blocks.append(block)
145
+
146
+ # After adding an analog block, we start a new digital block.
147
+ self.digital_circuits.append(Circuit(self.num_qubits, inputs=self.inputs))
148
+ self._effective_circuit = None
149
+ return self # Allow for chaining
150
+
151
+ def __getattr__(self, name: str) -> Any:
152
+ """
153
+ Metaprogramming to forward gate calls to the current digital circuit.
154
+ This enables syntax like `analog_circuit.h(0)`.
155
+ """
156
+ gate_method = getattr(self.current_digital_circuit, name, None)
157
+
158
+ if gate_method and callable(gate_method) and name.lower() in defined_gates:
159
+
160
+ def wrapper(*args, **kwargs): # type: ignore
161
+ gate_method(*args, **kwargs)
162
+ self._effective_circuit = None
163
+ return self
164
+
165
+ return wrapper
166
+ else:
167
+ raise AttributeError(
168
+ f"'{type(self).__name__}' object or its underlying '{type(self.current_digital_circuit).__name__}' "
169
+ f"object has no attribute '{name}'."
170
+ )
171
+
172
+ def state(self) -> Tensor:
173
+ """
174
+ Executes the full digital-analog sequence.
175
+
176
+ :return: The final state vector after the full evolution
177
+ :rtype: Tensor
178
+ """
179
+ # Propagate the state through the alternating circuit blocks
180
+ for i, analog_block in enumerate(self.analog_blocks):
181
+ # 1. Apply Digital Block i
182
+ digital_c = self.digital_circuits[i]
183
+ if i > 0:
184
+ digital_c.replace_inputs(psi) # type: ignore
185
+ psi = digital_c.wavefunction()
186
+
187
+ if analog_block.index is None:
188
+ psi = ode_evol_global( # type: ignore
189
+ hamiltonian=analog_block.hamiltonian_func,
190
+ initial_state=psi,
191
+ times=analog_block.time,
192
+ **analog_block.solver_options,
193
+ )
194
+ else:
195
+ psi = ode_evol_local( # type: ignore
196
+ hamiltonian=analog_block.hamiltonian_func,
197
+ initial_state=psi,
198
+ times=analog_block.time,
199
+ index=analog_block.index,
200
+ **analog_block.solver_options,
201
+ )
202
+ psi = psi[-1]
203
+ # TODO(@refraction-ray): support more time evol methods
204
+
205
+ # 3. Apply the final digital circuit
206
+ if self.analog_blocks:
207
+ self.digital_circuits[-1].replace_inputs(psi)
208
+ psi = self.digital_circuits[-1].wavefunction()
209
+ else:
210
+ psi = self.digital_circuits[-1].wavefunction()
211
+ self._effective_circuit = Circuit(self.num_qubits, inputs=psi)
212
+
213
+ return psi
214
+
215
+ wavefunction = state
216
+
217
+ def expectation(
218
+ self,
219
+ *ops: Tuple[tn.Node, List[int]],
220
+ reuse: bool = True,
221
+ enable_lightcone: bool = False,
222
+ nmc: int = 1000,
223
+ **kws: Any,
224
+ ) -> Tensor:
225
+ """
226
+ Compute expectation(s) of local operators.
227
+
228
+ :param ops: Pairs of `(operator_node, [sites])` specifying where each operator acts.
229
+ :type ops: Tuple[tn.Node, List[int]]
230
+ :param reuse: If True, then the wavefunction tensor is cached for further expectation evaluation,
231
+ defaults to be true.
232
+ :type reuse: bool, optional
233
+ :param enable_lightcone: whether enable light cone simplification, defaults to False
234
+ :type enable_lightcone: bool, optional
235
+ :param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
236
+ :type nmc: int, optional
237
+ :return: Tensor with one element
238
+ :rtype: Tensor
239
+ """
240
+ return self.effective_circuit.expectation(
241
+ *ops,
242
+ reuse=reuse,
243
+ enable_lightcone=enable_lightcone,
244
+ noise_conf=None,
245
+ nmc=nmc,
246
+ **kws,
247
+ )
248
+
249
+ def measure_jit(
250
+ self, *index: int, with_prob: bool = False, status: Optional[Tensor] = None
251
+ ) -> Tuple[Tensor, Tensor]:
252
+ """
253
+ Take measurement on the given site indices (computational basis).
254
+ This method is jittable!
255
+
256
+ :param index: Measure on which site (wire) index.
257
+ :type index: int
258
+ :param with_prob: If true, theoretical probability is also returned.
259
+ :type with_prob: bool, optional
260
+ :param status: external randomness, with shape [index], defaults to None
261
+ :type status: Optional[Tensor]
262
+ :return: The sample output and probability (optional) of the quantum line.
263
+ :rtype: Tuple[Tensor, Tensor]
264
+ """
265
+ return self.effective_circuit.measure_jit(
266
+ *index, with_prob=with_prob, status=status
267
+ )
268
+
269
+ measure = measure_jit
270
+
271
+ def amplitude(self, l: Union[str, Tensor]) -> Tensor:
272
+ r"""
273
+ Return the amplitude for a given bitstring `l`.
274
+
275
+ For state simulators, this computes :math:`\langle l \vert \psi \rangle`.
276
+
277
+ :param l: Bitstring in base-`d` using `0-9A-Z`.
278
+ :type l: Union[str, Tensor]
279
+ :return: Complex amplitude.
280
+ :rtype: Tensor
281
+ """
282
+ return self.effective_circuit.amplitude(l)
283
+
284
+ def probability(self) -> Tensor:
285
+ """
286
+ Get the length-`2^n` probability vector over the computational basis.
287
+
288
+ :return: Probability vector of shape `[dim^n]`.
289
+ :rtype: Tensor
290
+ """
291
+ return self.effective_circuit.probability()
292
+
293
+ def expectation_ps(
294
+ self,
295
+ x: Optional[Sequence[int]] = None,
296
+ y: Optional[Sequence[int]] = None,
297
+ z: Optional[Sequence[int]] = None,
298
+ ps: Optional[Sequence[int]] = None,
299
+ reuse: bool = True,
300
+ noise_conf: Optional[Any] = None,
301
+ nmc: int = 1000,
302
+ status: Optional[Tensor] = None,
303
+ **kws: Any,
304
+ ) -> Tensor:
305
+ """
306
+ Shortcut for Pauli string expectation.
307
+ x, y, z list are for X, Y, Z positions
308
+
309
+ :Example:
310
+
311
+ >>> c = tc.Circuit(2)
312
+ >>> c.X(0)
313
+ >>> c.H(1)
314
+ >>> c.expectation_ps(x=[1], z=[0])
315
+ array(-0.99999994+0.j, dtype=complex64)
316
+
317
+ :param x: sites to apply X gate, defaults to None
318
+ :type x: Optional[Sequence[int]], optional
319
+ :param y: sites to apply Y gate, defaults to None
320
+ :type y: Optional[Sequence[int]], optional
321
+ :param z: sites to apply Z gate, defaults to None
322
+ :type z: Optional[Sequence[int]], optional
323
+ :param ps: or one can apply a ps structures instead of ``x``, ``y``, ``z``,
324
+ e.g. [0, 1, 3, 0, 2, 2] for X_1Z_2Y_4Y_5
325
+ defaults to None, ``ps`` can overwrite ``x``, ``y`` and ``z``
326
+ :type ps: Optional[Sequence[int]], optional
327
+ :param reuse: whether to cache and reuse the wavefunction, defaults to True
328
+ :type reuse: bool, optional
329
+ :param noise_conf: Noise Configuration, defaults to None
330
+ :type noise_conf: Optional[NoiseConf], optional
331
+ :param nmc: repetition time for Monte Carlo sampling for noisfy calculation, defaults to 1000
332
+ :type nmc: int, optional
333
+ :param status: external randomness given by tensor uniformly from [0, 1], defaults to None,
334
+ used for noisfy circuit sampling
335
+ :type status: Optional[Tensor], optional
336
+ :return: Expectation value
337
+ :rtype: Tensor
338
+ """
339
+ return self.effective_circuit.expectation_ps(
340
+ x=x,
341
+ y=y,
342
+ z=z,
343
+ ps=ps,
344
+ reuse=reuse,
345
+ noise_conf=noise_conf,
346
+ nmc=nmc,
347
+ status=status,
348
+ **kws,
349
+ )
350
+
351
+ @partial(arg_alias, alias_dict={"format": ["format_"]})
352
+ def sample(
353
+ self,
354
+ batch: Optional[int] = None,
355
+ allow_state: bool = False,
356
+ readout_error: Optional[Sequence[Any]] = None,
357
+ format: Optional[str] = None,
358
+ random_generator: Optional[Any] = None,
359
+ status: Optional[Tensor] = None,
360
+ jittable: bool = True,
361
+ ) -> Any:
362
+ r"""
363
+ Batched sampling from the circuit or final state.
364
+
365
+ :param batch: Number of samples. If `None`, returns a single draw.
366
+ :type batch: Optional[int]
367
+ :param allow_state: If `True`, sample from the final state (when memory allows). Prefer `True` for speed.
368
+ :type allow_state: bool
369
+ :param readout_error: Optional readout error model.
370
+ :type readout_error: Optional[Sequence[Any]]
371
+ :param format: Output format. See :py:meth:`tensorcircuit.quantum.measurement_results`.
372
+ :type format: Optional[str]
373
+ :param random_generator: random generator, defaults to None
374
+ :type random_generator: Optional[Any], optional
375
+ :param status: external randomness given by tensor uniformly from [0, 1],
376
+ if set, can overwrite random_generator, shape [batch] for `allow_state=True`
377
+ and shape [batch, nqudits] for `allow_state=False` using perfect sampling implementation
378
+ :type status: Optional[Tensor]
379
+ :param jittable: when converting to count, whether keep the full size. if false, may be conflict
380
+ external jit, if true, may fail for large scale system with actual limited count results
381
+ :type jittable: bool, defaults true
382
+ :return: List (if batch) of tuple (binary configuration tensor and corresponding probability)
383
+ if the format is None, and consistent with format when given
384
+ :rtype: Any
385
+ """
386
+ return self.effective_circuit.sample(
387
+ batch=batch,
388
+ allow_state=allow_state,
389
+ readout_error=readout_error,
390
+ format=format,
391
+ random_generator=random_generator,
392
+ status=status,
393
+ jittable=jittable,
394
+ )
395
+
396
+ def __repr__(self) -> str:
397
+ s = f"AnalogCircuit(n={self.num_qubits}):\n"
398
+ s += "=" * 40 + "\n"
399
+
400
+ num_stages = len(self.analog_blocks) + 1
401
+
402
+ for i in range(num_stages):
403
+ # Print digital part
404
+ s += f"--- Digital Block {i} ---\n"
405
+
406
+ # Print analog part (if it exists)
407
+ if i < len(self.analog_blocks):
408
+ block = self.analog_blocks[i]
409
+ s += f"--- Analog Block {i} (T={block.time}) ---\n"
410
+ s += f" H(t) function: '{block.hamiltonian_func.__name__}'\n"
411
+
412
+ s += "=" * 40
413
+ return s
@@ -35,7 +35,7 @@ Symbol = Any # sympy.Symbol
35
35
 
36
36
  def _resolve(symbol: Union[Symbol, Tensor], i: int = 0) -> Tensor:
37
37
  """
38
- Make sure the layer is compatible with both multi-param and single param requirements
38
+ Make sure the layer is compatible with both multi-param and single param requirements
39
39
 
40
40
  What could be the input: list/tuple of sympy.symbol, tf.tensor with 1D or 0D shape
41
41
  """
@@ -352,7 +352,7 @@ class NMF(Model): # type: ignore
352
352
  spin_channel: int,
353
353
  *dimensions: int,
354
354
  _dtype: tf.DType = tf.float32,
355
- probamp: Optional[tf.Tensor] = None
355
+ probamp: Optional[tf.Tensor] = None,
356
356
  ):
357
357
  super().__init__()
358
358
  self.w = self.add_weight(