import itertools import numpy as np import pickle # V: y^2 = x^3 + 4x # P = (2, 4) # f = y - 2x # V = span{1, x, x^2, y, xy, x^2 y} # multiply polynomials # perform reduction K = 11 dim_x = 3 dim_y = 2 n = dim_x*dim_y P = Px, Py = 2, 4 V = [ [0, 7, 0, 10, 0, 0], [0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0] ] coeffs = [list(range(K))]*dim_x x_vals = list(itertools.product(*coeffs)) y_vals = x_vals[:] ring = list(itertools.product(x_vals, y_vals)) #for idx in range(0, len(ring), 10): # print(ring[idx:idx + 10]) def call(poly, point): x, y = point result = 0 for j, row in enumerate(poly): for i, a in enumerate(row): v = a * x**i * y**j #print(f'{a} x^{i} y^{j} = {a} {x**i} {y**j} = {v}') result += v return result % K p = [[2, 7, 0], [6, 7, 0]] assert call(p, (2, 4)) == 8 p = [[1, 2, 0], [0, 0, 3]] assert call(p, (6, 3)) == 7 def sub(poly_a, poly_b): c = [] for row_a, row_b in zip(poly_a, poly_b): c_row = [(a - b) % K for (a, b) in zip(row_a, row_b)] c.append(c_row) return c def add(poly_a, poly_b): c = [] for row_a, row_b in zip(poly_a, poly_b): c_row = [(a + b) % K for (a, b) in zip(row_a, row_b)] c.append(c_row) return c a = [[4, 2, 3], [6, 0, 2]] b = [[2, 1, 2], [9, 3, 4]] assert sub(a, b) == [[2, 1, 1], [8, 8, 9]] def _expand_double(poly): assert len(poly) > 0 x_size = len(poly[0]) return [[0]*2*x_size]*2*len(poly) def _copy_double(poly): assert len(poly) > 0 x_size = len(poly[0]) return [row[:] + [0]*x_size for row in poly] + [[0]*2*x_size]*len(poly) def _mul_x(poly): p = [] for row in poly: p.append([0] + row[:-1]) return p def _mul_y(p): assert len(p) > 0 x_size = len(p[0]) return [[0]*x_size] + p[:-1] def _mul_const(p, c): return [[p*c % K for p in row] for row in p] def mul(a, b): result = _expand_double(a) for j, row in enumerate(b): for i, c in enumerate(row): # c x^i y^j v = _copy_double(a) v = _mul_const(v, c) for _ in range(i): v = _mul_x(v) for _ in range(j): v = _mul_y(v) result = add(result, v) return result #print(_expand_double(a)) #print(_mul_x(_expand_double(a))) #print(_mul_y(_expand_double(a))) #print(_mul_const(_expand_double(a), 2)) a = [[4, 2, 3], [6, 0, 2]] b = [[2, 1, 2], [9, 3, 4]] ab = mul(a, b) assert ab == [ [8, 8, 5, 7, 6, 0], [4, 3, 10, 8, 5, 0], [10, 7, 9, 6, 8, 0], [0, 0, 0, 0, 0, 0] ] def max_monomial(p): degree = 0 pos = 0, 0 coeff = 0 for j, row in enumerate(p): for i, c in enumerate(row): if c == 0: continue current_deg = i + j if current_deg > degree: degree = current_deg pos = i, j coeff = c return coeff, pos def deg(p): _, pos = max_monomial(p) return sum(pos) assert deg([[0, 0, 0], [0, 0, 0]]) == 0 assert deg([[1, 0, 0], [0, 0, 0]]) == 0 assert deg([[0, 1, 0], [0, 0, 0]]) == 1 assert deg([[0, 0, 0], [1, 0, 0]]) == 1 assert deg([[0, 0, 1], [1, 0, 0]]) == 2 assert deg([[0, 0, 0], [0, 0, 1]]) == 3 assert deg([[0, 0, 0], [0, 0, 1], [0, 0, 1]]) == 4 def invert(b): n = 11 (x0, x1, y0, y1) = (1, 0, 0, 1) while n != 0: q = b // n b = n n = b % n (x0, x1) = (x1, x0 - q * x1) (y0, y1) = (y1, y0 - q * y1) return b, x0, y0 def _extended_euclid(a, b): if a == 0 : return 0, 1 x1, y1 = _extended_euclid(b % a, a) # Update x and y using results of recursive call x = y1 - (b//a) * x1 y = x1 return x, y def invert(x): return _extended_euclid(x, K)[0] % K assert 5 * invert(5) % K == 1 assert 3 * invert(3) % K == 1 assert 7 * invert(7) % K == 1 def __reduce(p, q): q_deg_x, q_deg_y = deg_x(q), deg_y(q) r = [row[:] for row in p] for j in range(len(r) - 1, -1, -1): row = r[j] for i in range(len(row)): c = row[i] if c == 0: continue term = [[0]*len(row) for _ in r] # TODO return r #print(reduce(ab, V)) #ring_valid_denoms = list(filter(lambda g: call(g, P) != 0, ring)) # #local_ring = [] #for f1 in ring: # for g1 in ring_valid_denoms: # is_unique = True # for (f2, g2) in local_ring: # # Test: f1 g2 - f2 g1 in I # f1g2 = mul(f1, g2) # f2g1 = mul(f2, g1) # fg_fg = sub(f1g2, f2g1)