def test_construct_multivariate_dirac_delta(self): """Tests the construct of the multivariate dirac delta.""" modulus = 3 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # Let's generate the dirac delta at x=0, y=0, z=0 values = [mod7(0), mod7(0), mod7(0)] dirac = construct_multivariate_dirac_delta(mod7, values, n) # The dirac delta should be 1 at x=0, y=0, z=0 assert dirac((0, 0, 0)) == 1 # It should be 0 elsewhere assert dirac((1, 0, 0)) == 0 assert dirac((0, 1, 0)) == 0 assert dirac((0, 0, 1)) == 0 # Let's generate the dirac delta at x=1, y=1, z=1 values = [mod7(1), mod7(1), mod7(1)] dirac = construct_multivariate_dirac_delta(mod7, values, n) # The dirac delta should be 1 at x=1, y=1, z=1 assert dirac((1, 1, 1)) == 1 # It should be 0 elsewehre assert dirac((1, 0, 0)) == 0 assert dirac((0, 1, 0)) == 0 assert dirac((0, 0, 1)) == 0
def test_project_multivar(self): """Test the multivariate projection.""" modulus = 7 field = IntegersModP(modulus) width = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(field, width).factory # This should equal xy xy_poly = multi({(1, 1, 0): 1}) x_poly = project_to_univariate(xy_poly, 0, field, width)
def __init__(self, air: AIR, zero_knowledge_expansion=5): """Initiates the APR object using an AIR object. Implements the AIR->APR transform from the STARKs paper. """ self.air = air modulus = air.field.p self.width = air.width # field = (Z/2[g]/h(g)) self.field = air.field # base_field = Z/2 base_field = self.field.base_field self.basePolys = polynomials_over(base_field) # h(g) h = self.field.ideal_generator # g g = self.basePolys([0, 1]) # Some constants T = air.steps self.t = int(math.log(T, 2)) # chosen so deg(C) <= 2^d # Setting to arbitrary value for now. self.d = int(math.log(self.air.CDegree, 2))+1 # TODO(rbharath): How should this constant be set correctly? self.R = 5 # This is the zero-knowledge expansion self.k = zero_knowledge_expansion self.polysOver = polynomials_over(self.field).factory self.Tau = list(range(self.width)) # Element of (Z/2[g]/h(g)) self.zeta = generate_primitive_polynomial(modulus, self.width) # Neighbors self.Nbrs = self.construct_neighbors(self.Tau, self.zeta, g, self.polysOver) # Define the affine spaces self.H = AffineSpace(base_field, [g**k for k in range(self.t)]) self.H0 = AffineSpace(base_field, [g**k for k in range(self.t-1)]) self.H1 = AffineSpace(base_field, [g**k for k in range(self.t-1)], g**(self.t-1)) self.L = self.construct_L(g) self.Lcmp = self.construct_L_cmp(g) self.Z_boundaries = self.construct_Z_boundaries(air.B) self.Eps_boundaries = self.construct_Eps_boundaries(air.B) self.rho_js = self.compute_rho_js(self.Z_boundaries, self.L) self.rho_cmp = self.compute_rho_cmp(self.Lcmp) self.comp = air # X_loc + {X_N}_{n in Nbrs} num_Phi_vars = 1 + len(self.Nbrs) PhiPolys = multivariates_over(self.field, num_Phi_vars).factory self.Phi = self.construct_Phi_polynomials(air, PhiPolys, g, self.zeta, modulus) # Witness reductio self.w = self.generate_witness()
def project_to_univariate(multivar_poly: MultiVarPoly, i: int, field: Field, width: int) -> Poly: """Projects a multivariate polynomial to a univariate polynomial over one var.""" multivars = multivariates_over(field, width - 1) multiVarsOver = multivars.factory polysOver = polynomials_over(multivars).factory X = polysOver([0, 1]) out = 0 for (term, coeff) in multivar_poly: # Remove i-th variable term_minus_i = term[:i] + term[i + 1:] coeff = multiVarsOver({term_minus_i: coeff}) out += polysOver([coeff]) * X**term[i] return out
def test_cross_mul(self): """Test multiplication with different types.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory three = mod7(3) # This should equal y y_poly = multi({(0, 1, 0): mod7(1)}) three_y_poly = multi({(0, 1, 0): mod7(3)}) assert three_y_poly == three * y_poly
def test_basic_construction(self): """"Test construction.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # This should equal y y_poly = multi({(0, 1, 0): 1}) # Test construction of a constant polynomial zero_poly = multi(0) zero_poly_2 = multi({}) assert zero_poly == zero_poly_2
def test_add(self): """Test multivariate polynomial addition.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # This should equal 0 zero_poly = multi({}) assert zero_poly == zero_poly + zero_poly # This should equal y y_poly = multi({(0, 1, 0): 1}) assert y_poly == y_poly + zero_poly
def test_composition(self): """Test that multivariate polynomials can compose properly.""" modulus = 2**256 - 2**32 * 351 + 1 field = IntegersModP(modulus) width = 2 multi = multivariates_over(field, width).factory [X_1, X_2] = generate_Xi_s(field, width) step_polys = [X_2, X_1 + 2*X_2**2] # This should equal y + 1 state_polys = [X_1, X_2] for step_poly in step_polys: next_step = step_poly(state_polys) # Since we start from the original polynomials the "next step" poly is # just the transition poly. assert next_step == step_poly
def test_neg(self): """Tests negation of multivariate polynomials.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # This should equal 0 zero_poly = multi({}) assert zero_poly == -zero_poly # This should equal y y_poly = multi({(0, 1, 0): 1}) # This should equal -y neg_y_poly = multi({(0, 1, 0): -1}) assert neg_y_poly == - y_poly
def make_multivar(poly: Poly, i: int, field: Field, width: int) -> MultiVarPoly: """Converts a univariate polynomial into multivariate. Suppose poly = x^2 + 3 Suppose that width = 5, i = 2. Then returns x_2^2 + 3 """ polysOver = multivariates_over(field, width).factory pre = (0, ) * i post = (0, ) * (width - (i + 1)) index = pre + (1, ) + post X_i = polysOver({index: field(1)}) up_poly = 0 for degree, coeff in enumerate(poly.coefficients): up_poly += coeff * X_i**degree return up_poly
def test_construct_multivariate_coefficients(self): """Tests the "compilation" of a function into a polynomial.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # Our test function def f(x): if isinstance(x, tuple) or isinstance(x, list): x = x[0] return x + 1 poly = construct_multivariate_coefficients(mod7, f, n) # This should equal x + 1 x_plus_one_poly = multi({(0, 0, 0): mod7(1), (1, 0, 0): mod7(1)}) assert poly == x_plus_one_poly
def test_div(self): """Test multiplication of multivariate polynomials.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # This should equal y y_poly = multi({(0, 1, 0): mod7(1)}) # This is y^2 y_sq_poly = multi({(0, 2, 0): mod7(1)}) assert y_sq_poly / y_poly == y_poly # This should equal x + y x_y_poly = multi({(1, 0, 0): mod7(1), (0, 1, 0): mod7(1)}) # This should equal x^2 + 2xy + y^2 sq_x_y_poly = multi({(2, 0, 0): mod7(1), (1, 1, 0): mod7(2), (0, 2, 0): mod7(1)}) prod = x_y_poly*x_y_poly assert sq_x_y_poly / x_y_poly == x_y_poly
def test_call(self): """Test calling the multivariate polynomial like a function.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # This should equal 0 zero_poly = multi({}) # Evaluate with x=1, y=1, z=1. This should equal 0 assert zero_poly((1, 1, 1)) == 0 # This should equal y y_poly = multi({(0, 1, 0): mod7(1)}) # Evaluate with x=1, y=1, z=1. This should equal 1 assert y_poly((1, 1, 1)) == 1 # This should equal x^2 + 2xy + y^2 sq_x_y_poly = multi({(2, 0, 0): mod7(1), (1, 1, 0): mod7(2), (0, 2, 0): mod7(1)}) # Evaluate with x=1, y=1, z=1. This should equal 4 assert sq_x_y_poly((1, 1, 1)) == 4
def construct_multivariate_dirac_delta(field: Field, values: List[FieldElement], n: int) -> MultiVarPoly: """Constructs the multivariate dirac delta polynomial at 0. 1_0(x) = \prod_{i=1}^n (1 - x_i^{q-1}) This can be generalized into the the dirac polynomial at y as follows. 1_y(x)\prod_{i=1}^n (1 - (x_i - y_i)^{q-1}) """ multi = multivariates_over(field, n).factory q = field.field_size base = field(1) for i, val in enumerate(values): # x_i_term = (0,...1,...0) with the 1 in the ith-term x_i_term = [0] * n x_i_term[i] = 1 term = multi({(0, ) * n: -values[i], tuple(x_i_term): 1}) term = field(1) - term**(q - 1) base = base * term return base
def test_finitefield(self): """Test multivariates over finite fields.""" # This finite field is of size 2^17 p = 2 m = 17 Zp = IntegersModP(p) polysOver = polynomials_over(Zp) #field = FiniteField(p, m) #x^17 + x^3 + 1 is primitive coefficients = [Zp(0)] * 18 coefficients[0] = Zp(1) coefficients[3] = Zp(1) coefficients[17] = Zp(1) poly = polysOver(coefficients) field = FiniteField(p, m, polynomialModulus=poly) width = 2 inp = [field(0), field(1)] polysOver = multivariates_over(field, width).factory [X_1, X_2] = generate_Xi_s(field, width) step_poly = X_2 poly = step_poly([X_1, X_2]) assert poly == X_2
def test_degree(self): """Test computation of multivariate degree""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # This should equal y y_poly = multi({(0, 1, 0): 1}) assert y_poly.degree() == 1 # This should equal x + y + z sum_poly = multi({(1, 0, 0): 1, (0, 1, 0): 1, (0, 0, 1): 1}) assert sum_poly.degree() == 1 # This should equal x^2 + y^2 + z^2 sq_sum_poly = multi({(2, 0, 0): 1, (0, 2, 0): 1, (0, 0, 2): 1}) assert sq_sum_poly.degree() == 2 # This should equal xy + yz + zx multi_poly = multi({(1, 1, 0): 1, (0, 1, 1): 1, (1, 0, 1): 1}) assert multi_poly.degree() == 2
def construct_multivariate_coefficients( field: Field, step_fn: Callable, n: int) -> Dict[Tuple[int, ...], FieldElement]: """Transforms a function over vector of finite fields into a polynomial. Every function f: F_q^n -> F_q is a polynomial if F is a finite field of size q. (See Lemma 7 of http://math.uga.edu/~pete/4400ChevalleyWarning.pdf). The key trick used in this transformation is the creation of a "dirac-delta" multivariate polynomial which is 1 iff all n of its inputs are 0. 1_0(x) = \prod_{i=1}^n (1 - x_i^{q-1}) Why does this make sense? For any non-zero element x in F_q, x^{q-1} = 1. How can we convert an aribtrary function using these dirac-delta polynomials? P_f(x) = \sum_{y \in F_q^n} f(y) \prod_{i=1}^n (1 - (x_i - y_i)^{q-1}) The idea is that we construct the polynomial term-wise. """ multi = multivariates_over(field, n).factory poly = multi(0) field_size = field.field_size # Finite field case if field.__name__[:2] == "F_": p = field.p m = field.m elif field.__name__[:2] == "Z/": p = field.p m = 1 else: raise ValueError # Iterate over field indices field_indices = itertools.product(*[range(p) for _ in range(m)]) for index in field_indices: index = [field(ind) for ind in list(index)] term = construct_multivariate_dirac_delta(field, index, n) poly += step_fn(index) * term return poly
def test_exponentiation(self): """Tests exponentiation of multivariate polynomials.""" modulus = 7 mod7 = IntegersModP(modulus) n = 3 # Let's make polynomials in (Z/7)[x, y, z] multi = multivariates_over(mod7, n).factory # This should equal 0 zero_poly = multi({}) assert zero_poly == zero_poly**2 # This should equal y y_poly = multi({(0, 1, 0): mod7(1)}) # This is y^2 y_sq_poly = multi({(0, 2, 0): mod7(1)}) assert y_sq_poly == y_poly**2 # This should equal x + y x_y_poly = multi({(1, 0, 0): mod7(1), (0, 1, 0): mod7(1)}) # This should equal x^2 + 2xy + y^2 sq_x_y_poly = multi({(2, 0, 0): mod7(1), (1, 1, 0): mod7(2), (0, 2, 0): mod7(1)}) assert sq_x_y_poly == x_y_poly**2