Coverage for qml_essentials / ansaetze.py: 94%
291 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-30 11:43 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-30 11:43 +0000
1from abc import ABC, abstractmethod
2from typing import Any, Optional, List, Union, Callable, Tuple
3import jax.numpy as np
4import logging
5import warnings
7from qml_essentials.gates import Gates, PulseInformation
8from qml_essentials.topologies import Topology
10log = logging.getLogger(__name__)
13class Circuit(ABC):
14 """Abstract base class for quantum circuit ansätze."""
16 def __init__(self) -> None:
17 """Initialize the circuit."""
18 pass
20 @abstractmethod
21 def n_params_per_layer(self, n_qubits: int) -> int:
22 """
23 Get the number of parameters per circuit layer.
25 Args:
26 n_qubits (int): Number of qubits in the circuit.
28 Returns:
29 int: Number of parameters required per layer.
31 Raises:
32 NotImplementedError: Must be implemented by subclasses.
33 """
34 raise NotImplementedError("n_params_per_layer method is not implemented")
36 def n_pulse_params_per_layer(self, n_qubits: int) -> int:
37 """
38 Get the number of pulse parameters per circuit layer.
40 Subclasses that do not use pulse-level simulation do not need to
41 override this method.
43 Args:
44 n_qubits (int): Number of qubits in the circuit.
46 Returns:
47 int: Number of pulse parameters required per layer.
49 Raises:
50 NotImplementedError: If called but not overridden by subclass.
51 """
52 raise NotImplementedError("n_pulse_params_per_layer method is not implemented")
54 @abstractmethod
55 def get_control_indices(self, n_qubits: int) -> Optional[List[int]]:
56 """
57 Get indices for controlled rotation gates in one layer.
59 Returns slice indices [start:stop:step] for extracting controlled
60 gate parameters from a full parameter array for one layer.
62 Args:
63 n_qubits (int): Number of qubits in the circuit.
65 Returns:
66 Optional[List[int]]: List of three integers [start, stop, step]
67 for slicing, or None if the circuit contains no controlled
68 rotation gates.
70 Raises:
71 NotImplementedError: Must be implemented by subclasses.
72 """
73 raise NotImplementedError("get_control_indices method is not implemented")
75 def get_control_angles(self, w: np.ndarray, n_qubits: int) -> Optional[np.ndarray]:
76 """
77 Extract angles for controlled rotation gates from parameter array.
79 Args:
80 w (np.ndarray): Parameter array for one layer.
81 n_qubits (int): Number of qubits in the circuit.
83 Returns:
84 Optional[np.ndarray]: Array of controlled gate parameters,
85 or empty array if circuit contains no controlled gates.
86 """
87 indices = self.get_control_indices(n_qubits)
88 if indices is None:
89 return np.array([])
91 if len(indices) == 3 and None in indices:
92 return w[indices[0] : indices[1] : indices[2]]
93 else:
94 return w.take(np.array(indices))
96 def _build(self, w: np.ndarray, n_qubits: int, **kwargs) -> Any:
97 """
98 Build one layer of the circuit using unitary or pulse-level parameters.
100 Internal method that handles pulse parameter validation and context
101 management before delegating to the build() method.
103 Args:
104 w (np.ndarray): Parameter array for the current layer.
105 n_qubits (int): Number of qubits in the circuit.
106 **kwargs: Additional keyword arguments:
107 - gate_mode (str): "unitary" (default) or "pulse" for
108 pulse-level simulation.
109 - pulse_params (np.ndarray): Pulse parameters if gate_mode="pulse".
110 - noise_params (Dict): Noise parameters dictionary.
112 Returns:
113 Any: Result from the build() method.
115 Raises:
116 ValueError: If pulse_params length doesn't match expected count.
117 """
118 gate_mode = kwargs.get("gate_mode", "unitary")
120 if gate_mode == "pulse" and "pulse_params" in kwargs:
121 pulse_params_per_layer = self.n_pulse_params_per_layer(n_qubits)
123 if len(kwargs["pulse_params"]) != pulse_params_per_layer:
124 raise ValueError(
125 f"Pulse params length {len(kwargs['pulse_params'])} "
126 f"does not match expected {pulse_params_per_layer} "
127 f"for {n_qubits} qubits"
128 )
130 with Gates.pulse_manager_context(kwargs["pulse_params"]):
131 return self.build(w, n_qubits, **kwargs)
132 else:
133 return self.build(w, n_qubits, **kwargs)
135 @abstractmethod
136 def build(self, w: np.ndarray, n_qubits: int, **kwargs) -> Any:
137 """
138 Build one layer of the quantum circuit.
140 Args:
141 w (np.ndarray): Parameter array for the current layer.
142 n_qubits (int): Number of qubits in the circuit.
143 **kwargs: Additional keyword arguments passed from _build.
145 Returns:
146 Any: Circuit construction result.
148 Raises:
149 NotImplementedError: Must be implemented by subclasses.
150 """
151 raise NotImplementedError("build method is not implemented")
153 def __call__(self, *args: Any, **kwds: Any) -> Any:
154 """Call the _build method with provided arguments."""
155 self._build(*args, **kwds)
158class DeclarativeCircuit(Circuit):
159 """
160 A circuit defined entirely by a sequence of Block descriptors.
162 Subclasses only need to set the class attribute `structure` — a tuple of
164 All of `n_params_per_layer`, `n_pulse_params_per_layer`,
165 `get_control_indices`, and `build` are derived automatically.
166 """
168 @staticmethod
169 def structure() -> Tuple[Any, ...]:
170 """Override in subclass to return the structure tuple."""
171 raise NotImplementedError
173 @classmethod
174 def n_params_per_layer(cls, n_qubits: int) -> int:
175 return sum(block.n_params(n_qubits) for block in cls.structure())
177 @classmethod
178 def n_pulse_params_per_layer(cls, n_qubits: int) -> int:
179 return sum(block.n_pulse_params(n_qubits) for block in cls.structure())
181 @classmethod
182 def get_control_indices(cls, n_qubits: int) -> Optional[List]:
183 """
184 Computes parameter indices for controlled rotation Gates.
185 Scans the structure for Block with
186 [start, stop, step] into the flat parameter vector, or None.
187 """
188 structure = cls.structure()
189 total_params = sum(block.n_params(n_qubits) for block in structure)
191 # Collect which parameter indices correspond to controlled rotations
192 controlled_indices = []
193 offset = 0
194 for block in structure:
195 n = block.n_params(n_qubits)
196 if block.is_controlled_rotation:
197 controlled_indices.extend(range(offset, offset + n))
198 offset += n
200 # FIXME: this last part should be reworked
202 if not controlled_indices:
203 return None
205 # Check if indices form a contiguous tail (the common case)
206 # This preserves backwards compatibility with the [start, None, None] format
207 if controlled_indices == list(
208 range(total_params - len(controlled_indices), total_params)
209 ):
210 return [-len(controlled_indices), None, None]
212 # Fallback: return raw indices (future-proof)
213 return controlled_indices
215 @classmethod
216 def build(cls, w: np.ndarray, n_qubits: int, **kwargs) -> None:
217 structure = cls.structure()
218 w_idx = 0
219 for block in structure:
220 w_idx = block.apply(n_qubits, w, w_idx, **kwargs)
221 Gates.Barrier(wires=list(range(n_qubits)), **kwargs)
224class Block:
225 def __init__(
226 self,
227 gate: str,
228 topology: Any = None,
229 **kwargs,
230 ):
231 """
232 Initialize a Block object; the atoms of Ansatzes.
234 Args:
235 gate (str): Name of the Gate class to use.
236 topology (Any, optional): Topology of the gate for entangling gates.
237 Defaults to None.
238 kwargs: Additional keyword arguments passed to the topology function.
239 """
240 if isinstance(gate, str):
241 self.gate = getattr(Gates, gate)
242 else:
243 self.gate = gate
245 if self.is_entangling:
246 assert (
247 topology is not None
248 ), "Topology must be specified for entangling gates"
250 self.topology = topology
251 self.kwargs = kwargs
253 def __repr__(self):
254 if self.topology is None:
255 return f"{self.__class__.__name__}({self.gate.__name__})"
256 else:
257 return (
258 f"{self.__class__.__name__}"
259 f"({self.topology.__name__}[{self.gate.__name__}])"
260 )
262 @property
263 def is_entangling(self):
264 return Gates.is_entangling(self.gate)
266 @property
267 def is_rotational(self):
268 return Gates.is_rotational(self.gate)
270 @property
271 def is_controlled_rotation(self):
272 return self.is_entangling and self.is_rotational
274 def enough_qubits(self, n_qubits):
275 if self.is_entangling:
276 # NOTE This must be adjusted if default values
277 # in Topology change
278 span = self.kwargs.get("span", 1)
279 if callable(span):
280 span = span(n_qubits)
282 return (n_qubits >= 2) and (n_qubits > span)
284 return n_qubits >= 1
286 def n_params(self, n_qubits: int) -> int:
287 assert n_qubits > 0, "Number of qubits must be positive"
289 if self.is_rotational:
290 if self.is_entangling:
291 if not self.enough_qubits(n_qubits):
292 warnings.warn(
293 f"Skipping {self.topology.__name__} with n_qubits={n_qubits} "
294 f"as there are not enough qubits"
295 f"for this topology."
296 )
297 return 0
298 else:
299 return len(self.topology(n_qubits=n_qubits, **self.kwargs))
300 else:
301 return n_qubits if self.gate.__name__ != "Rot" else 3 * n_qubits
303 return 0
305 def n_pulse_params(self, n_qubits: int) -> int:
306 assert n_qubits > 0, "Number of qubits must be positive"
308 n_pulse_params = PulseInformation.num_params(self.gate)
309 if self.is_entangling:
310 if not self.enough_qubits(n_qubits):
311 warnings.warn(
312 f"Skipping {self.topology.__name__} with n_qubits={n_qubits} "
313 f"as there are not enough qubits"
314 f"for this topology."
315 )
316 return 0
317 else:
318 return n_pulse_params * len(
319 self.topology(n_qubits=n_qubits, **self.kwargs)
320 )
321 return n_pulse_params * n_qubits
323 def apply(
324 self, n_qubits: int, w: np.ndarray = None, w_idx: int = None, **kwargs
325 ) -> int:
326 """
327 Applies the block to the given circuit.
329 Args:
330 n_qubits (int): Number of qubits, the block is applied to.
331 w (np.ndarray, optional): Weights to use for rotational gates.
332 Defaults to None.
333 w_idx (int, optional): Index of weights to use for rotational gates.
334 Defaults to None.
335 **kwargs: Keyword arguments passed to the gate.
337 Returns:
338 int: The new index of weights after applying the block.
339 """
340 assert n_qubits > 0, "Number of qubits must be positive"
342 iterator = (
343 self.topology(n_qubits=n_qubits, **self.kwargs)
344 if self.is_entangling
345 else range(n_qubits)
346 )
348 for wires in iterator:
349 if self.is_entangling and not self.enough_qubits(n_qubits):
350 warnings.warn(
351 f"Skipping {self.topology.__name__} with n_qubits={n_qubits} "
352 f"as there are not enough qubits"
353 f"for this topology."
354 )
355 continue
357 if self.is_rotational:
358 assert w is not None, "w must be provided for rotational gates"
359 assert w_idx is not None, "w_idx must be provided for rotational gates"
361 if self.gate.__name__ == "Rot":
362 self.gate(
363 w[w_idx], w[w_idx + 1], w[w_idx + 2], wires=wires, **kwargs
364 )
365 w_idx += 3
366 else:
367 self.gate(w[w_idx], wires=wires, **kwargs)
368 w_idx += 1
369 else:
370 self.gate(wires=wires, **kwargs)
371 return w_idx
374class Ansaetze:
375 def get_available(parameterized_only=False):
376 # list of parameterized ansaetze
377 ansaetze = [
378 Ansaetze.Circuit_1,
379 Ansaetze.Circuit_2,
380 Ansaetze.Circuit_3,
381 Ansaetze.Circuit_4,
382 Ansaetze.Circuit_5,
383 Ansaetze.Circuit_6,
384 Ansaetze.Circuit_7,
385 Ansaetze.Circuit_8,
386 Ansaetze.Circuit_9,
387 Ansaetze.Circuit_10,
388 Ansaetze.Circuit_13,
389 Ansaetze.Circuit_14,
390 Ansaetze.Circuit_15,
391 Ansaetze.Circuit_16,
392 Ansaetze.Circuit_17,
393 Ansaetze.Circuit_18,
394 Ansaetze.Circuit_19,
395 Ansaetze.Circuit_20,
396 Ansaetze.No_Entangling,
397 Ansaetze.Strongly_Entangling,
398 Ansaetze.Hardware_Efficient,
399 ]
401 # extend by the non-parameterized ones
402 if not parameterized_only:
403 ansaetze += [
404 Ansaetze.No_Ansatz,
405 Ansaetze.GHZ,
406 ]
408 return ansaetze
410 class No_Ansatz(DeclarativeCircuit):
411 @staticmethod
412 def structure():
413 return ()
415 class GHZ(DeclarativeCircuit):
416 @staticmethod
417 def structure():
418 return (
419 Block(gate=Gates.H),
420 Block(gate=Gates.CX, topology=Topology.stairs, reverse=True),
421 )
423 @staticmethod
424 def build(w: np.ndarray, n_qubits: int, **kwargs):
425 Gates.H(wires=0, **kwargs)
426 for q in range(n_qubits - 1):
427 Gates.CX(wires=[q, q + 1], **kwargs)
429 @staticmethod
430 def n_pulse_params_per_layer(n_qubits: int) -> int:
431 n_params = PulseInformation.num_params("H") # only 1 H
432 n_params += (n_qubits - 1) * PulseInformation.num_params(Gates.CX)
433 return n_params
435 class Circuit_1(DeclarativeCircuit):
436 @staticmethod
437 def structure():
438 return (
439 Block(gate=Gates.RX),
440 Block(gate=Gates.RZ),
441 )
443 class Circuit_2(DeclarativeCircuit):
444 @staticmethod
445 def structure():
446 return (
447 Block(gate=Gates.RX),
448 Block(gate=Gates.RZ),
449 Block(
450 gate=Gates.CX,
451 topology=Topology.stairs,
452 ),
453 )
455 class Circuit_3(DeclarativeCircuit):
456 @staticmethod
457 def structure():
458 return (
459 Block(gate=Gates.RX),
460 Block(gate=Gates.RZ),
461 Block(gate=Gates.CRZ, topology=Topology.stairs),
462 )
464 class Circuit_4(DeclarativeCircuit):
465 @staticmethod
466 def structure():
467 return (
468 Block(gate=Gates.RX),
469 Block(gate=Gates.RZ),
470 Block(gate=Gates.CRX, topology=Topology.stairs),
471 )
473 class Circuit_5(DeclarativeCircuit):
474 @staticmethod
475 def structure():
476 return (
477 Block(gate=Gates.RX),
478 Block(gate=Gates.RZ),
479 Block(gate=Gates.CRZ, topology=Topology.all_to_all),
480 Block(gate=Gates.RX),
481 Block(gate=Gates.RZ),
482 )
484 class Circuit_6(DeclarativeCircuit):
485 @staticmethod
486 def structure():
487 return (
488 Block(gate=Gates.RX),
489 Block(gate=Gates.RZ),
490 Block(gate=Gates.CRX, topology=Topology.all_to_all),
491 Block(gate=Gates.RX),
492 Block(gate=Gates.RZ),
493 )
495 class Circuit_7(DeclarativeCircuit):
496 @staticmethod
497 def structure():
498 return (
499 Block(gate=Gates.RX),
500 Block(gate=Gates.RZ),
501 Block(
502 gate=Gates.CRZ,
503 topology=Topology.bricks,
504 ),
505 Block(gate=Gates.RX),
506 Block(gate=Gates.RZ),
507 Block(
508 gate=Gates.CRZ,
509 topology=Topology.bricks,
510 offset=1,
511 ),
512 )
514 class Circuit_8(DeclarativeCircuit):
515 @staticmethod
516 def structure():
517 return (
518 Block(gate=Gates.RX),
519 Block(gate=Gates.RZ),
520 Block(
521 gate=Gates.CRX,
522 topology=Topology.bricks,
523 ),
524 Block(gate=Gates.RX),
525 Block(gate=Gates.RZ),
526 Block(
527 gate=Gates.CRX,
528 topology=Topology.bricks,
529 offset=1,
530 ),
531 )
533 class Circuit_9(DeclarativeCircuit):
534 @staticmethod
535 def structure():
536 return (
537 Block(gate=Gates.H),
538 Block(gate="CZ", topology=Topology.stairs),
539 Block(gate=Gates.RX),
540 )
542 class Circuit_10(DeclarativeCircuit):
543 @staticmethod
544 def structure():
545 return (
546 Block(gate=Gates.RY),
547 Block(gate="CZ", topology=Topology.stairs, offset=-1, wrap=True),
548 Block(gate=Gates.RY),
549 )
551 class Circuit_13(DeclarativeCircuit):
552 @staticmethod
553 def structure():
554 return (
555 Block(gate=Gates.RY),
556 Block(
557 gate=Gates.CRZ,
558 topology=Topology.stairs,
559 wrap=True,
560 reverse=True,
561 mirror=False,
562 ),
563 Block(gate=Gates.RY),
564 Block(
565 gate=Gates.CRZ,
566 topology=Topology.stairs,
567 reverse=False,
568 mirror=False,
569 offset=lambda n: n - 1,
570 span=3,
571 wrap=True,
572 ),
573 )
575 class Circuit_14(DeclarativeCircuit):
576 @staticmethod
577 def structure():
578 return (
579 Block(gate=Gates.RY),
580 Block(
581 gate=Gates.CRX,
582 topology=Topology.stairs,
583 wrap=True,
584 reverse=True,
585 mirror=False,
586 ),
587 Block(gate=Gates.RY),
588 Block(
589 gate=Gates.CRX,
590 topology=Topology.stairs,
591 reverse=False,
592 mirror=False,
593 offset=lambda n: n - 1,
594 span=3,
595 wrap=True,
596 ),
597 )
599 class Circuit_15(DeclarativeCircuit):
600 @staticmethod
601 def structure():
602 return (
603 Block(gate=Gates.RY),
604 Block(
605 gate=Gates.CX,
606 topology=Topology.stairs,
607 wrap=True,
608 reverse=True,
609 mirror=False,
610 ),
611 Block(gate=Gates.RY),
612 Block(
613 gate=Gates.CX,
614 topology=Topology.stairs,
615 reverse=False,
616 mirror=False,
617 offset=lambda n: n - 1,
618 span=3,
619 wrap=True,
620 ),
621 )
623 class Circuit_16(DeclarativeCircuit):
624 @staticmethod
625 def structure():
626 return (
627 Block(gate=Gates.RX),
628 Block(gate=Gates.RZ),
629 Block(
630 gate=Gates.CRZ,
631 topology=Topology.bricks,
632 ),
633 Block(
634 gate=Gates.CRZ,
635 topology=Topology.bricks,
636 offset=1,
637 ),
638 )
640 class Circuit_17(DeclarativeCircuit):
641 @staticmethod
642 def structure():
643 return (
644 Block(gate=Gates.RX),
645 Block(gate=Gates.RZ),
646 Block(
647 gate=Gates.CRX,
648 topology=Topology.bricks,
649 ),
650 Block(
651 gate=Gates.CRX,
652 topology=Topology.bricks,
653 offset=1,
654 ),
655 )
657 class Circuit_18(DeclarativeCircuit):
658 @staticmethod
659 def structure():
660 return (
661 Block(gate=Gates.RX),
662 Block(gate=Gates.RZ),
663 Block(
664 gate=Gates.CRZ,
665 topology=Topology.stairs,
666 wrap=True,
667 mirror=False,
668 ),
669 )
671 class Circuit_19(DeclarativeCircuit):
672 @staticmethod
673 def structure():
674 return (
675 Block(gate=Gates.RX),
676 Block(gate=Gates.RZ),
677 Block(
678 gate=Gates.CRX,
679 topology=Topology.stairs,
680 wrap=True,
681 mirror=False,
682 ),
683 )
685 class Circuit_20(DeclarativeCircuit):
686 @staticmethod
687 def structure():
688 return (
689 Block(gate=Gates.RY),
690 Block(
691 gate=Gates.CX,
692 topology=Topology.stairs,
693 wrap=True,
694 reverse=True,
695 mirror=False,
696 ),
697 Block(gate=Gates.RY),
698 Block(
699 gate=Gates.CX,
700 topology=Topology.stairs,
701 reverse=False,
702 offset=lambda n: n - 2,
703 span=1,
704 wrap=True,
705 ),
706 )
708 class No_Entangling(DeclarativeCircuit):
709 @staticmethod
710 def structure():
711 return (Block(gate=Gates.Rot),)
713 class Hardware_Efficient(DeclarativeCircuit):
714 @staticmethod
715 def structure():
716 return (
717 Block(gate=Gates.RY),
718 Block(gate=Gates.RZ),
719 Block(gate=Gates.RY),
720 Block(
721 gate=Gates.CX,
722 topology=Topology.bricks,
723 mirror=False,
724 ),
725 Block(
726 gate=Gates.CX,
727 topology=Topology.bricks,
728 offset=-1,
729 modulo=True,
730 wrap=True,
731 mirror=False,
732 ),
733 )
735 class Strongly_Entangling(DeclarativeCircuit):
736 @staticmethod
737 def structure():
738 return (
739 Block(gate=Gates.Rot),
740 Block(
741 gate=Gates.CX,
742 topology=Topology.stairs,
743 wrap=True,
744 reverse=False,
745 mirror=False,
746 ),
747 Block(gate=Gates.Rot),
748 Block(
749 gate=Gates.CX,
750 topology=Topology.stairs,
751 reverse=False,
752 span=lambda n: n // 2,
753 wrap=True,
754 mirror=False,
755 ),
756 )
759class Encoding:
760 def __init__(
761 self, strategy: str, gates: Union[str, Callable, List[Union[str, Callable]]]
762 ):
763 """
764 Initializes an Encoding object.
766 Implementations closely follow https://doi.org/10.22331/q-2023-12-20-1210
768 Parameters
769 ----------
770 strategy : str
771 The encoding strategy to use. Available options:
772 ['hamming', 'binary', 'ternary']
773 gates : Union[str, Callable, List[Union[str, Callable]]]
774 The gates to use for encoding. Can be a string, a callable or a list
775 of strings or callables.
777 Returns
778 -------
779 None
781 Raises
782 -------
783 ValueError
784 If the encoding strategy is not implemented.
785 ValueError
786 If there is an error parsing the Gates.
787 """
788 if strategy not in ["hamming", "binary", "ternary"]:
789 raise ValueError(
790 f"Encoding strategy {strategy} not implemented. "
791 "Available options: ['hamming', 'binary', 'ternary']"
792 )
793 self._strategy = strategy
794 strategy = getattr(self, strategy)
796 log.debug(f"Using encoding strategy: '{strategy.__name__}'")
798 try:
799 self._gates = Gates.parse_gates(gates, Gates)
800 except ValueError as e:
801 raise ValueError(f"Error parsing encodings: {e}")
803 self.callable = [strategy(g) for g in self._gates]
805 def __len__(self):
806 return len(self.callable)
808 def __getitem__(self, idx):
809 return self.callable[idx]
811 def get_n_freqs(self, omegas):
812 """
813 Returns the number of frequencies required for the encoding strategy.
814 This includes positive and negative side.
816 Parameters
817 ----------
818 omegas : int
819 The number of frequencies to encode.
821 Returns
822 -------
823 int
824 The number of frequencies required for the encoding strategy.
825 """
826 if self._strategy == "hamming":
827 return int(2 * omegas + 1)
828 elif self._strategy == "binary":
829 return int(2 ** (omegas + 1) - 1)
830 elif self._strategy == "ternary":
831 return int(3 ** (omegas))
832 else:
833 raise NotImplementedError
835 def get_spectrum(self, omegas):
836 """
837 Spectrum for one of the following encoding strategies:
839 Hamming: {-n_q -(n_q-1), ..., n_q}
840 Binary: {-2^{n_q}+1, ..., 2^{n_q}-1}
841 Ternary: {-floor(3^{n_q}/2), ..., floor(3^(n_q)/2)}
843 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
845 Parameters
846 ----------
847 omegas : int
848 The number of frequencies to encode.
850 Returns
851 -------
852 np.ndarray
853 The spectrum of the encoding strategy.
854 """
855 if self._strategy == "hamming":
856 return np.arange(-omegas, omegas + 1)
857 elif self._strategy == "binary":
858 return np.arange(-(2**omegas) + 1, 2**omegas)
859 elif self._strategy == "ternary":
860 limit = int(np.floor(3**omegas / 2))
861 return np.arange(-limit, limit + 1)
862 else:
863 raise NotImplementedError
865 def hamming(self, enc):
866 """
867 Hamming encoding strategy.
869 Returns an encoding function that uses the Hamming encoding strategy
870 which uses 2 * omegas + 1 frequencies for the encoding.
871 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
873 Parameters
874 ----------
875 enc : Callable
876 The encoding function to be wrapped.
878 Returns
879 -------
880 Callable
881 The wrapped encoding function.
882 """
883 return enc
885 def binary(self, enc):
886 """
887 Binary encoding strategy.
889 Returns an encoding function that scales the input by a factor of 2^wires.
891 Binary encoding uses 2^(omegas + 1) - 1 frequencies for the encoding.
892 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
894 Parameters
895 ----------
896 enc : Callable
897 The encoding function to be wrapped.
899 Returns
900 -------
901 Callable
902 The wrapped encoding function.
903 """
905 def _enc(inputs, wires, **kwargs):
906 return enc(inputs * (2**wires), wires, **kwargs)
908 return _enc
910 def ternary(self, enc):
911 """
912 Ternary encoding strategy.
914 Returns an encoding function that scales the input by a factor of 3^wires.
916 Ternary encoding uses 3^(omegas + 1) - 1 frequencies for the encoding.
917 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
919 Parameters
920 ----------
921 enc : Callable
922 The encoding function to be wrapped.
924 Returns
925 -------
926 Callable
927 The wrapped encoding function.
928 """
930 def _enc(inputs, wires, **kwargs):
931 return enc(inputs * (3**wires), wires, **kwargs)
933 return _enc
935 def golomb(self, enc):
936 raise NotImplementedError