Path: blob/master/shared/small_roots/coron_direct.py
2589 views
import logging12from sage.all import ZZ3from sage.all import matrix45from shared import small_roots6from shared.polynomial import max_norm789def integer_bivariate(p, k, X, Y, echelon_algorithm="default", roots_method="groebner"):10"""11Computes small integer roots of a bivariate polynomial.12More information: Coron J., "Finding Small Roots of Bivariate Integer Polynomial Equations: a Direct Approach"13:param p: the polynomial14:param k: the amount of shifts to use15:param X: an approximate bound on the x roots16:param Y: an approximate bound on the y roots17:param echelon_algorithm: the algorithm to use to calculate the Echelon form of L (default: "default")18:param roots_method: the method to use to find roots (default: "groebner")19:return: a generator generating small roots (tuples of x and y roots) of the polynomial20"""21pr = p.parent()22x, y = pr.gens()23delta = max(p.degrees())2425(i0, j0), W = max_norm(p(x * X, y * Y))2627logging.debug("Calculating n...")28S = matrix(ZZ, k ** 2, k ** 2)29for a in range(k):30for b in range(k):31s = x ** a * y ** b * p32for i in range(k):33for j in range(k):34S[a * k + b, i * k + j] = s.coefficient([i0 + i, j0 + j])3536n = abs(S.det())37logging.debug(f"Found {n = }")3839# Monomials are collected in "left" and "right" lists, which determine where the columns are in relation to each other.40# This partition ensures the Echelon form will set desired monomial coefficients to zero.41logging.debug("Generating monomials...")42left_monomials = []43right_monomials = []44for i in range(k + delta):45for j in range(k + delta):46if 0 <= i - i0 < k and 0 <= j - j0 < k:47left_monomials.append(x ** i * y ** j)48else:49right_monomials.append(x ** i * y ** j)5051assert len(left_monomials) == k ** 252monomials = left_monomials + right_monomials5354logging.debug("Generating shifts...")5556shifts = []57for a in range(k):58for b in range(k):59s = x ** a * y ** b * p60shifts.append(s)6162for monomial in monomials:63r = monomial * n64shifts.append(r)6566logging.debug(f"Filling the lattice ({len(shifts)} x {len(monomials)})...")67L = matrix(ZZ, len(shifts), len(monomials))68for row, shift in enumerate(shifts):69for col, monomial in enumerate(monomials):70L[row, col] = shift.monomial_coefficient(monomial) * monomial(X, Y)7172logging.debug("Generating Echelon form...")73L = L.echelon_form(algorithm=echelon_algorithm)7475L2 = L.submatrix(k ** 2, k ** 2, (k + delta) ** 2 - k ** 2)76L2 = small_roots.reduce_lattice(L2)77# Only use right monomials now (corresponding the the sublattice).78polynomials = small_roots.reconstruct_polynomials(L2, p, n, right_monomials, [X, Y])79for roots in small_roots.find_roots(pr, [p] + polynomials, method=roots_method):80yield roots[x], roots[y]818283