| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- import numpy as np
- from finite_fields import finitefield
- class Variable:
- def __init__(self, name, fp):
- self.name = name
- self.fp = fp
- def __pow__(self, n):
- expr = MultiplyExpression(self.fp)
- expr.set_symbol(self.name, n)
- return expr
- def __eq__(self, other):
- return self.name == other.name
- def __hash__(self):
- return hash(self.name)
- def termify(self):
- expr = MultiplyExpression(self.fp)
- expr.set_symbol(self.name, 1)
- return expr
- class MultiplyExpression:
- def __init__(self, fp):
- self.coeff = fp(1)
- self.symbols = {}
- self.fp = fp
- def copy(self):
- result = MultiplyExpression(self.fp)
- result.coeff = self.coeff
- result.symbols = self.symbols.copy()
- return result
- def clean(self):
- for symbol in list(self.symbols.keys()):
- if self.symbols[symbol] == 0:
- del self.symbols[symbol]
- def matches(self, other):
- return self.symbols == other.symbols
- def set_symbol(self, var_name, power):
- self.symbols[var_name] = power
- def __eq__(self, other):
- return (self.coeff == other.coeff and
- self.symbols == other.symbols)
- def __neg__(self):
- result = self.copy()
- result.coeff *= -1
- return result
- def __mul__(self, expr):
- result = MultiplyExpression(self.fp)
- result.coeff = self.coeff
- result.symbols = self.symbols.copy()
- if isinstance(expr, np.int64) or isinstance(expr, int):
- expr = self.fp(int(expr))
- if hasattr(expr, "field"):
- result.coeff *= expr
- return result
- if isinstance(expr, Variable):
- expr = expr.termify()
- for var_name, power in expr.symbols.items():
- if var_name in result.symbols:
- result.symbols[var_name] += power
- else:
- result.symbols[var_name] = power
- # Remember to multiply the coefficients
- result.coeff *= expr.coeff
- return result
- def __add__(self, expr):
- if isinstance(expr, Variable):
- expr = expr.termify()
- if self.matches(expr):
- result = self.copy()
- result.coeff += expr.coeff
- return result
- return MultivariatePolynomial([self, expr])
- def __sub__(self, expr):
- expr = -expr
- return self + expr
- def evaluate(self, symbol_map):
- result = MultiplyExpression(self.fp)
- for symbol, power in self.symbols.items():
- if symbol in symbol_map:
- value = symbol_map[symbol]
- result *= value**power
- else:
- result *= Variable(symbol, self.fp)**power
- return result
- def __str__(self):
- repr = ""
- first = True
- if self.coeff != 1:
- repr += str(self.coeff)
- first = False
- for var_name, power in self.symbols.items():
- if first:
- first = False
- else:
- repr += " "
- if power == 1:
- repr += var_name
- else:
- repr += var_name + "^" + str(power)
- return repr
- class MultivariatePolynomial:
- def __init__(self, terms=[]):
- self.terms = terms
- def copy(self):
- terms = [term.copy() for term in self.terms]
- return MultivariatePolynomial(terms)
- # Operations can accept Variables and constants
- # so we make sure to convert them to MultiplyExpression types
- def _convert_term(self, term):
- if isinstance(term, Variable):
- term = term.termify()
- if hasattr(term, "field"):
- expr = MultiplyExpression(term.field)
- expr.coeff = term
- term = expr
- return term
- def __bool__(self):
- return bool(self.terms)
- def __eq__(self, other):
- return self.terms == other.terms
- def __neg__(self):
- terms = [-term for term in self.terms]
- return MultivariatePolynomial(terms)
- def __add__(self, term):
- term = self._convert_term(term)
- if isinstance(term, MultivariatePolynomial):
- # Recursively apply addition operation
- result = self.copy()
- for other_term in term.terms:
- result += other_term
- return result
- assert isinstance(term, MultiplyExpression)
- # Delete ^0 variables
- term.clean()
- # Skip terms where the coeff is 0
- if term.coeff == 0:
- return self
- result = self.copy()
- result_term = result._find(term)
- if result_term is None:
- result.terms.append(term)
- else:
- result_term.coeff += term.coeff
- return result
- def __sub__(self, term):
- term = -term
- return self + term
- def __mul__(self, term):
- term = self._convert_term(term)
- if isinstance(term, MultivariatePolynomial):
- # Recursively apply addition operation
- result = MultivariatePolynomial()
- for other_term in term.terms:
- result += self * other_term
- return result
- assert isinstance(term, MultiplyExpression)
- # Delete ^0 variables
- term.clean()
- # Skip terms where the coeff is 0
- if term.coeff == 0:
- return self
- terms = [self_term * term for self_term in self.terms]
- result = MultivariatePolynomial(terms)
- return result
- def divmod(self, poly):
- assert isinstance(poly, MultivariatePolynomial)
- # https://www.win.tue.nl/~aeb/2WF02/groebner.pdf
- def _find(self, other):
- for term in self.terms:
- if term.matches(other):
- return term
- return None
- def evaluate(self, variable_map):
- p = MultivariatePolynomial()
- for term in self.terms:
- assert isinstance(term, MultiplyExpression)
- p += term.evaluate(variable_map)
- return p
- def _assert_unique_terms(self):
- for i, term1 in enumerate(self.terms):
- for q, term2 in enumerate(self.terms):
- if i == q:
- continue
- assert not term1.matches(term2)
- def filter(self, variables):
- p = MultivariatePolynomial()
- for term in self.terms:
- assert isinstance(term, MultiplyExpression)
- skip = False
- for variable in variables:
- symbol = variable.name
- if symbol in term.symbols:
- skip = True
- if not skip:
- p += term
- return p
- def __str__(self):
- if not self.terms:
- return "0"
- repr = ""
- first = True
- for term in self.terms:
- if first:
- first = False
- else:
- repr += " + "
- repr += str(term)
- return repr
- if __name__ == "__main__":
- from finite_fields import finitefield
- p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
- fp = finitefield.IntegersModP(p)
- x = Variable("X")
- y = Variable("Y")
- z = Variable("Z")
- print(y**2 + y**2)
- p = x**3 * y**2 * x**2 * fp(5) * fp(2) + x**3 * y + z + fp(6)
- q = x**3 * y * fp(3) + y
- print(p)
- print(q)
- print(p + q)
- print(p * q)
- print(-q)
- print(p - q)
|