@@ -1025,6 +1025,108 @@ def __str__(self):
10251025 return f'QGF({ self .characteristic } **{ self .degree } )'
10261026
10271027
1028+ @attrs .frozen
1029+ class QGFPoly (QDType ):
1030+ r"""Univariate Polynomials with coefficients in a Galois Field GF($p^m$).
1031+
1032+ This data type represents a degree-$n$ univariate polynomials
1033+ $f(x)=\sum_{i=0}^{n} a_i x^{i}$ where the coefficients $a_{i}$ of the polynomial
1034+ belong to a Galois Field $GF(p^{m})$.
1035+
1036+ The data type uses the [Galois library](https://mhostetter.github.io/galois/latest/) to
1037+ perform arithmetic over polynomials defined over Galois Fields using the
1038+ [galois.Poly](https://mhostetter.github.io/galois/latest/api/galois.Poly/).
1039+
1040+ Attributes:
1041+ degree: The degree $n$ of the univariate polynomial $f(x)$ represented by this type.
1042+ qgf: An instance of `QGF` that represents the galois field $GF(p^m)$ over which the
1043+ univariate polynomial $f(x)$ is defined.
1044+
1045+ References
1046+ [Polynomials over finite fields](https://mhostetter.github.io/galois/latest/api/galois.Poly/)
1047+
1048+ [Polynomial Arithmetic](https://mhostetter.github.io/galois/latest/basic-usage/poly-arithmetic/)
1049+ """
1050+
1051+ degree : SymbolicInt
1052+ qgf : QGF
1053+
1054+ @cached_property
1055+ def bitsize (self ) -> SymbolicInt :
1056+ """Bitsize of qubit register required to represent a single instance of this data type."""
1057+ return self .qgf .bitsize * (self .degree + 1 )
1058+
1059+ @cached_property
1060+ def num_qubits (self ) -> SymbolicInt :
1061+ """Number of qubits required to represent a single instance of this data type."""
1062+ return self .bitsize
1063+
1064+ def get_classical_domain (self ) -> Iterable [Any ]:
1065+ """Yields all possible classical (computational basis state) values representable
1066+ by this type."""
1067+ import itertools
1068+
1069+ from galois import Poly
1070+
1071+ for it in itertools .product (self .qgf .gf_type .elements , repeat = (self .degree + 1 )):
1072+ yield Poly (self .qgf .gf_type (it ), field = self .qgf .gf_type )
1073+
1074+ @cached_property
1075+ def _quint_equivalent (self ) -> QUInt :
1076+ return QUInt (self .num_qubits )
1077+
1078+ def to_gf_coefficients (self , f_x : galois .Poly ) -> galois .Array :
1079+ """Returns a big-endian array of coefficients of the polynomial f(x)."""
1080+ f_x_coeffs = self .qgf .gf_type .Zeros (self .degree + 1 )
1081+ f_x_coeffs [self .degree - f_x .degree :] = f_x .coeffs
1082+ return f_x_coeffs
1083+
1084+ def from_gf_coefficients (self , f_x : galois .Array ) -> galois .Poly :
1085+ """Expects a big-endian array of coefficients that represent a polynomial f(x)."""
1086+ return galois .Poly (f_x , field = self .qgf .gf_type )
1087+
1088+ def to_bits (self , x ) -> List [int ]:
1089+ """Returns individual bits corresponding to binary representation of x"""
1090+ self .assert_valid_classical_val (x )
1091+ assert isinstance (x , galois .Poly )
1092+ return self .qgf .to_bits_array (self .to_gf_coefficients (x )).reshape (- 1 ).tolist ()
1093+
1094+ def from_bits (self , bits : Sequence [int ]):
1095+ """Combine individual bits to form x"""
1096+ reshaped_bits = np .array (bits ).reshape ((int (self .degree ) + 1 , int (self .qgf .bitsize )))
1097+ return self .from_gf_coefficients (self .qgf .from_bits_array (reshaped_bits ))
1098+
1099+ def assert_valid_classical_val (self , val : Any , debug_str : str = 'val' ):
1100+ """Raises an exception if `val` is not a valid classical value for this type.
1101+
1102+ Args:
1103+ val: A classical value that should be in the domain of this QDType.
1104+ debug_str: Optional debugging information to use in exception messages.
1105+ """
1106+ if not isinstance (val , galois .Poly ):
1107+ raise ValueError (f"{ debug_str } should be a { galois .Poly } , not { val !r} " )
1108+ if val .field is not self .qgf .gf_type :
1109+ raise ValueError (
1110+ f"{ debug_str } should be defined over { self .qgf .gf_type } , not { val .field } "
1111+ )
1112+ if val .degree > self .degree :
1113+ raise ValueError (f"{ debug_str } should have a degree <= { self .degree } , not { val .degree } " )
1114+
1115+ def is_symbolic (self ) -> bool :
1116+ """Returns True if this qdtype is parameterized with symbolic objects."""
1117+ return is_symbolic (self .degree , self .qgf )
1118+
1119+ def iteration_length_or_zero (self ) -> SymbolicInt :
1120+ """Safe version of iteration length.
1121+
1122+ Returns the iteration_length if the type has it or else zero.
1123+ """
1124+ return self .qgf .order
1125+
1126+ def __str__ (self ):
1127+ return f'QGFPoly({ self .degree } , { self .qgf !s} )'
1128+
1129+
10281130QAnyInt = (QInt , QUInt , BQUInt , QMontgomeryUInt )
10291131QAnyUInt = (QUInt , BQUInt , QMontgomeryUInt , QGF )
10301132
0 commit comments