Coverage for qml_essentials / ansaetze.py: 94%
288 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-17 20:45 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-17 20:45 +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(w, w_idx, n_qubits, **kwargs)
223class Block:
224 def __init__(
225 self,
226 gate: str,
227 topology: Any = None,
228 **kwargs,
229 ):
230 """
231 Initialize a Block object; the atoms of Ansatzes.
233 Args:
234 gate (str): Name of the Gate class to use.
235 topology (Any, optional): Topology of the gate for entangling gates.
236 Defaults to None.
237 kwargs: Additional keyword arguments passed to the topology function.
238 """
239 if isinstance(gate, str):
240 self.gate = getattr(Gates, gate)
241 else:
242 self.gate = gate
244 if self.is_entangling:
245 assert (
246 topology is not None
247 ), "Topology must be specified for entangling gates"
249 self.topology = topology
250 self.kwargs = kwargs
252 def __repr__(self):
253 if self.topology is None:
254 return f"{self.__class__.__name__}({self.gate.__name__})"
255 else:
256 return (
257 f"{self.__class__.__name__}"
258 f"({self.topology.__name__}[{self.gate.__name__}])"
259 )
261 @property
262 def is_entangling(self):
263 return Gates.is_entangling(self.gate)
265 @property
266 def is_rotational(self):
267 return Gates.is_rotational(self.gate)
269 @property
270 def is_controlled_rotation(self):
271 return self.is_entangling and self.is_rotational
273 def enough_qubits(self, n_qubits):
274 if self.is_entangling:
275 # NOTE This must be adjusted if default values
276 # in Topology change
277 span = self.kwargs.get("span", 1)
278 if callable(span):
279 span = span(n_qubits)
281 return (n_qubits >= 2) and (n_qubits > span)
283 return n_qubits >= 1
285 def n_params(self, n_qubits: int) -> int:
286 assert n_qubits > 0, "Number of qubits must be positive"
288 if self.is_rotational:
289 if self.is_entangling:
290 if not self.enough_qubits(n_qubits):
291 warnings.warn(
292 f"Skipping {self.topology.__name__} with n_qubits={n_qubits} "
293 f"as there are not enough qubits"
294 f"for this topology."
295 )
296 return 0
297 else:
298 return len(self.topology(n_qubits=n_qubits, **self.kwargs))
299 else:
300 return n_qubits if self.gate.__name__ != "Rot" else 3 * n_qubits
302 return 0
304 def n_pulse_params(self, n_qubits: int) -> int:
305 assert n_qubits > 0, "Number of qubits must be positive"
307 n_pulse_params = PulseInformation.num_params(self.gate)
308 if self.is_entangling:
309 if not self.enough_qubits(n_qubits):
310 warnings.warn(
311 f"Skipping {self.topology.__name__} with n_qubits={n_qubits} "
312 f"as there are not enough qubits"
313 f"for this topology."
314 )
315 return 0
316 else:
317 return n_pulse_params * len(
318 self.topology(n_qubits=n_qubits, **self.kwargs)
319 )
320 return n_pulse_params * n_qubits
322 def apply(self, w: np.ndarray, w_idx: int, n_qubits: int, **kwargs) -> int:
323 assert n_qubits > 0, "Number of qubits must be positive"
325 iterator = (
326 self.topology(n_qubits=n_qubits, **self.kwargs)
327 if self.is_entangling
328 else range(n_qubits)
329 )
331 for wires in iterator:
332 if self.is_entangling and not self.enough_qubits(n_qubits):
333 warnings.warn(
334 f"Skipping {self.topology.__name__} with n_qubits={n_qubits} "
335 f"as there are not enough qubits"
336 f"for this topology."
337 )
338 continue
340 if self.is_rotational:
341 if self.gate.__name__ == "Rot":
342 self.gate(
343 w[w_idx], w[w_idx + 1], w[w_idx + 2], wires=wires, **kwargs
344 )
345 w_idx += 3
346 else:
347 self.gate(w[w_idx], wires=wires, **kwargs)
348 w_idx += 1
349 else:
350 self.gate(wires=wires, **kwargs)
351 return w_idx
354class Ansaetze:
355 def get_available(parameterized_only=False):
356 # list of parameterized ansaetze
357 ansaetze = [
358 Ansaetze.Circuit_1,
359 Ansaetze.Circuit_2,
360 Ansaetze.Circuit_3,
361 Ansaetze.Circuit_4,
362 Ansaetze.Circuit_5,
363 Ansaetze.Circuit_6,
364 Ansaetze.Circuit_7,
365 Ansaetze.Circuit_8,
366 Ansaetze.Circuit_9,
367 Ansaetze.Circuit_10,
368 Ansaetze.Circuit_13,
369 Ansaetze.Circuit_14,
370 Ansaetze.Circuit_15,
371 Ansaetze.Circuit_16,
372 Ansaetze.Circuit_17,
373 Ansaetze.Circuit_18,
374 Ansaetze.Circuit_19,
375 Ansaetze.Circuit_20,
376 Ansaetze.No_Entangling,
377 Ansaetze.Strongly_Entangling,
378 Ansaetze.Hardware_Efficient,
379 ]
381 # extend by the non-parameterized ones
382 if not parameterized_only:
383 ansaetze += [
384 Ansaetze.No_Ansatz,
385 Ansaetze.GHZ,
386 ]
388 return ansaetze
390 class No_Ansatz(DeclarativeCircuit):
391 @staticmethod
392 def structure():
393 return ()
395 class GHZ(DeclarativeCircuit):
396 @staticmethod
397 def structure():
398 return (
399 Block(gate=Gates.H),
400 Block(gate=Gates.CX, topology=Topology.stairs, reverse=True),
401 )
403 @staticmethod
404 def build(w: np.ndarray, n_qubits: int, **kwargs):
405 Gates.H(wires=0, **kwargs)
406 for q in range(n_qubits - 1):
407 Gates.CX(wires=[q, q + 1], **kwargs)
409 @staticmethod
410 def n_pulse_params_per_layer(n_qubits: int) -> int:
411 n_params = PulseInformation.num_params("H") # only 1 H
412 n_params += (n_qubits - 1) * PulseInformation.num_params(Gates.CX)
413 return n_params
415 class Circuit_1(DeclarativeCircuit):
416 @staticmethod
417 def structure():
418 return (
419 Block(gate=Gates.RX),
420 Block(gate=Gates.RZ),
421 )
423 class Circuit_2(DeclarativeCircuit):
424 @staticmethod
425 def structure():
426 return (
427 Block(gate=Gates.RX),
428 Block(gate=Gates.RZ),
429 Block(
430 gate=Gates.CX,
431 topology=Topology.stairs,
432 ),
433 )
435 class Circuit_3(DeclarativeCircuit):
436 @staticmethod
437 def structure():
438 return (
439 Block(gate=Gates.RX),
440 Block(gate=Gates.RZ),
441 Block(gate=Gates.CRZ, topology=Topology.stairs),
442 )
444 class Circuit_4(DeclarativeCircuit):
445 @staticmethod
446 def structure():
447 return (
448 Block(gate=Gates.RX),
449 Block(gate=Gates.RZ),
450 Block(gate=Gates.CRX, topology=Topology.stairs),
451 )
453 class Circuit_5(DeclarativeCircuit):
454 @staticmethod
455 def structure():
456 return (
457 Block(gate=Gates.RX),
458 Block(gate=Gates.RZ),
459 Block(gate=Gates.CRZ, topology=Topology.all_to_all),
460 Block(gate=Gates.RX),
461 Block(gate=Gates.RZ),
462 )
464 class Circuit_6(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.all_to_all),
471 Block(gate=Gates.RX),
472 Block(gate=Gates.RZ),
473 )
475 class Circuit_7(DeclarativeCircuit):
476 @staticmethod
477 def structure():
478 return (
479 Block(gate=Gates.RX),
480 Block(gate=Gates.RZ),
481 Block(
482 gate=Gates.CRZ,
483 topology=Topology.bricks,
484 ),
485 Block(gate=Gates.RX),
486 Block(gate=Gates.RZ),
487 Block(
488 gate=Gates.CRZ,
489 topology=Topology.bricks,
490 offset=1,
491 ),
492 )
494 class Circuit_8(DeclarativeCircuit):
495 @staticmethod
496 def structure():
497 return (
498 Block(gate=Gates.RX),
499 Block(gate=Gates.RZ),
500 Block(
501 gate=Gates.CRX,
502 topology=Topology.bricks,
503 ),
504 Block(gate=Gates.RX),
505 Block(gate=Gates.RZ),
506 Block(
507 gate=Gates.CRX,
508 topology=Topology.bricks,
509 offset=1,
510 ),
511 )
513 class Circuit_9(DeclarativeCircuit):
514 @staticmethod
515 def structure():
516 return (
517 Block(gate=Gates.H),
518 Block(gate="CZ", topology=Topology.stairs),
519 Block(gate=Gates.RX),
520 )
522 class Circuit_10(DeclarativeCircuit):
523 @staticmethod
524 def structure():
525 return (
526 Block(gate=Gates.RY),
527 Block(gate="CZ", topology=Topology.stairs, offset=-1, wrap=True),
528 Block(gate=Gates.RY),
529 )
531 class Circuit_13(DeclarativeCircuit):
532 @staticmethod
533 def structure():
534 return (
535 Block(gate=Gates.RY),
536 Block(
537 gate=Gates.CRZ,
538 topology=Topology.stairs,
539 wrap=True,
540 reverse=True,
541 mirror=False,
542 ),
543 Block(gate=Gates.RY),
544 Block(
545 gate=Gates.CRZ,
546 topology=Topology.stairs,
547 reverse=False,
548 mirror=False,
549 offset=lambda n: n - 1,
550 span=3,
551 wrap=True,
552 ),
553 )
555 class Circuit_14(DeclarativeCircuit):
556 @staticmethod
557 def structure():
558 return (
559 Block(gate=Gates.RY),
560 Block(
561 gate=Gates.CRX,
562 topology=Topology.stairs,
563 wrap=True,
564 reverse=True,
565 mirror=False,
566 ),
567 Block(gate=Gates.RY),
568 Block(
569 gate=Gates.CRX,
570 topology=Topology.stairs,
571 reverse=False,
572 mirror=False,
573 offset=lambda n: n - 1,
574 span=3,
575 wrap=True,
576 ),
577 )
579 class Circuit_15(DeclarativeCircuit):
580 @staticmethod
581 def structure():
582 return (
583 Block(gate=Gates.RY),
584 Block(
585 gate=Gates.CX,
586 topology=Topology.stairs,
587 wrap=True,
588 reverse=True,
589 mirror=False,
590 ),
591 Block(gate=Gates.RY),
592 Block(
593 gate=Gates.CX,
594 topology=Topology.stairs,
595 reverse=False,
596 mirror=False,
597 offset=lambda n: n - 1,
598 span=3,
599 wrap=True,
600 ),
601 )
603 class Circuit_16(DeclarativeCircuit):
604 @staticmethod
605 def structure():
606 return (
607 Block(gate=Gates.RX),
608 Block(gate=Gates.RZ),
609 Block(
610 gate=Gates.CRZ,
611 topology=Topology.bricks,
612 ),
613 Block(
614 gate=Gates.CRZ,
615 topology=Topology.bricks,
616 offset=1,
617 ),
618 )
620 class Circuit_17(DeclarativeCircuit):
621 @staticmethod
622 def structure():
623 return (
624 Block(gate=Gates.RX),
625 Block(gate=Gates.RZ),
626 Block(
627 gate=Gates.CRX,
628 topology=Topology.bricks,
629 ),
630 Block(
631 gate=Gates.CRX,
632 topology=Topology.bricks,
633 offset=1,
634 ),
635 )
637 class Circuit_18(DeclarativeCircuit):
638 @staticmethod
639 def structure():
640 return (
641 Block(gate=Gates.RX),
642 Block(gate=Gates.RZ),
643 Block(
644 gate=Gates.CRZ,
645 topology=Topology.stairs,
646 wrap=True,
647 mirror=False,
648 ),
649 )
651 class Circuit_19(DeclarativeCircuit):
652 @staticmethod
653 def structure():
654 return (
655 Block(gate=Gates.RX),
656 Block(gate=Gates.RZ),
657 Block(
658 gate=Gates.CRX,
659 topology=Topology.stairs,
660 wrap=True,
661 mirror=False,
662 ),
663 )
665 class Circuit_20(DeclarativeCircuit):
666 @staticmethod
667 def structure():
668 return (
669 Block(gate=Gates.RY),
670 Block(
671 gate=Gates.CX,
672 topology=Topology.stairs,
673 wrap=True,
674 reverse=True,
675 mirror=False,
676 ),
677 Block(gate=Gates.RY),
678 Block(
679 gate=Gates.CX,
680 topology=Topology.stairs,
681 reverse=False,
682 offset=lambda n: n - 2,
683 span=1,
684 wrap=True,
685 ),
686 )
688 class No_Entangling(DeclarativeCircuit):
689 @staticmethod
690 def structure():
691 return (Block(gate=Gates.Rot),)
693 class Hardware_Efficient(DeclarativeCircuit):
694 @staticmethod
695 def structure():
696 return (
697 Block(gate=Gates.RY),
698 Block(gate=Gates.RZ),
699 Block(gate=Gates.RY),
700 Block(
701 gate=Gates.CX,
702 topology=Topology.bricks,
703 mirror=False,
704 ),
705 Block(
706 gate=Gates.CX,
707 topology=Topology.bricks,
708 offset=-1,
709 modulo=True,
710 wrap=True,
711 mirror=False,
712 ),
713 )
715 class Strongly_Entangling(DeclarativeCircuit):
716 @staticmethod
717 def structure():
718 return (
719 Block(gate=Gates.Rot),
720 Block(
721 gate=Gates.CX,
722 topology=Topology.stairs,
723 wrap=True,
724 reverse=False,
725 mirror=False,
726 ),
727 Block(gate=Gates.Rot),
728 Block(
729 gate=Gates.CX,
730 topology=Topology.stairs,
731 reverse=False,
732 span=lambda n: n // 2,
733 wrap=True,
734 mirror=False,
735 ),
736 )
739class Encoding:
740 def __init__(
741 self, strategy: str, gates: Union[str, Callable, List[Union[str, Callable]]]
742 ):
743 """
744 Initializes an Encoding object.
746 Implementations closely follow https://doi.org/10.22331/q-2023-12-20-1210
748 Parameters
749 ----------
750 strategy : str
751 The encoding strategy to use. Available options:
752 ['hamming', 'binary', 'ternary']
753 gates : Union[str, Callable, List[Union[str, Callable]]]
754 The gates to use for encoding. Can be a string, a callable or a list
755 of strings or callables.
757 Returns
758 -------
759 None
761 Raises
762 -------
763 ValueError
764 If the encoding strategy is not implemented.
765 ValueError
766 If there is an error parsing the Gates.
767 """
768 if strategy not in ["hamming", "binary", "ternary"]:
769 raise ValueError(
770 f"Encoding strategy {strategy} not implemented. "
771 "Available options: ['hamming', 'binary', 'ternary']"
772 )
773 self._strategy = strategy
774 strategy = getattr(self, strategy)
776 log.debug(f"Using encoding strategy: '{strategy.__name__}'")
778 try:
779 self._gates = Gates.parse_gates(gates, Gates)
780 except ValueError as e:
781 raise ValueError(f"Error parsing encodings: {e}")
783 self.callable = [strategy(g) for g in self._gates]
785 def __len__(self):
786 return len(self.callable)
788 def __getitem__(self, idx):
789 return self.callable[idx]
791 def get_n_freqs(self, omegas):
792 """
793 Returns the number of frequencies required for the encoding strategy.
794 This includes positive and negative side.
796 Parameters
797 ----------
798 omegas : int
799 The number of frequencies to encode.
801 Returns
802 -------
803 int
804 The number of frequencies required for the encoding strategy.
805 """
806 if self._strategy == "hamming":
807 return int(2 * omegas + 1)
808 elif self._strategy == "binary":
809 return int(2 ** (omegas + 1) - 1)
810 elif self._strategy == "ternary":
811 return int(3 ** (omegas))
812 else:
813 raise NotImplementedError
815 def get_spectrum(self, omegas):
816 """
817 Spectrum for one of the following encoding strategies:
819 Hamming: {-n_q -(n_q-1), ..., n_q}
820 Binary: {-2^{n_q}+1, ..., 2^{n_q}-1}
821 Ternary: {-floor(3^{n_q}/2), ..., floor(3^(n_q)/2)}
823 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
825 Parameters
826 ----------
827 omegas : int
828 The number of frequencies to encode.
830 Returns
831 -------
832 np.ndarray
833 The spectrum of the encoding strategy.
834 """
835 if self._strategy == "hamming":
836 return np.arange(-omegas, omegas + 1)
837 elif self._strategy == "binary":
838 return np.arange(-(2**omegas) + 1, 2**omegas)
839 elif self._strategy == "ternary":
840 limit = int(np.floor(3**omegas / 2))
841 return np.arange(-limit, limit + 1)
842 else:
843 raise NotImplementedError
845 def hamming(self, enc):
846 """
847 Hamming encoding strategy.
849 Returns an encoding function that uses the Hamming encoding strategy
850 which uses 2 * omegas + 1 frequencies for the encoding.
851 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
853 Parameters
854 ----------
855 enc : Callable
856 The encoding function to be wrapped.
858 Returns
859 -------
860 Callable
861 The wrapped encoding function.
862 """
863 return enc
865 def binary(self, enc):
866 """
867 Binary encoding strategy.
869 Returns an encoding function that scales the input by a factor of 2^wires.
871 Binary encoding uses 2^(omegas + 1) - 1 frequencies for the encoding.
872 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
874 Parameters
875 ----------
876 enc : Callable
877 The encoding function to be wrapped.
879 Returns
880 -------
881 Callable
882 The wrapped encoding function.
883 """
885 def _enc(inputs, wires, **kwargs):
886 return enc(inputs * (2**wires), wires, **kwargs)
888 return _enc
890 def ternary(self, enc):
891 """
892 Ternary encoding strategy.
894 Returns an encoding function that scales the input by a factor of 3^wires.
896 Ternary encoding uses 3^(omegas + 1) - 1 frequencies for the encoding.
897 See https://doi.org/10.22331/q-2023-12-20-1210 for more details.
899 Parameters
900 ----------
901 enc : Callable
902 The encoding function to be wrapped.
904 Returns
905 -------
906 Callable
907 The wrapped encoding function.
908 """
910 def _enc(inputs, wires, **kwargs):
911 return enc(inputs * (3**wires), wires, **kwargs)
913 return _enc
915 def golomb(self, enc):
916 raise NotImplementedError