def field_isomorphism_pslq(a, b): """Construct field isomorphism using PSLQ algorithm.""" if not all(_.domain.is_RationalField and _.ext.is_real for _ in (a, b)): raise NotImplementedError("PSLQ doesn't support complex coefficients") f = a.minpoly x = f.gen g = b.minpoly.replace(x) m = g.degree() a, b = a.ext, b.ext for n in mpmath.libmp.libintmath.giant_steps(32, 256): # pragma: no branch with mpmath.workdps(n): A, B = lambdify((), [a, b], 'mpmath')() basis = [B**i for i in range(m)] + [A] coeffs = mpmath.pslq(basis, maxcoeff=10**10, maxsteps=10**3) if coeffs: assert coeffs[-1] # basis[:-1] elements are linearly independent h = -Poly(coeffs[:-1], x, field=True).quo_ground(coeffs[-1]) if f.compose(h).rem(g).is_zero: return h.rep.all_coeffs() else: break
def nsimplify_real(x): orig = mpmath.mp.dps xv = x._to_mpmath(bprec) try: # We'll be happy with low precision if a simple fraction if not (tolerance or full): mpmath.mp.dps = 15 rat = mpmath.pslq([xv, 1]) if rat is not None: return Rational(-int(rat[1]), int(rat[0])) mpmath.mp.dps = prec newexpr = mpmath.identify(xv, constants=constants_dict, tol=tolerance, full=full) if not newexpr: raise ValueError if full: newexpr = newexpr[0] expr = sympify(newexpr) if x and not expr: # don't let x become 0 raise ValueError if expr.is_finite is False and not xv in [mpmath.inf, mpmath.ninf]: raise ValueError return expr finally: # even though there are returns above, this is executed # before leaving mpmath.mp.dps = orig
def field_isomorphism_pslq(a, b): """Construct field isomorphism using PSLQ algorithm. """ if not all(_.domain.is_RationalField and _.ext.is_real for _ in (a, b)): raise NotImplementedError("PSLQ doesn't support complex coefficients") f = a.minpoly g = b.minpoly.replace(f.gen) m = b.minpoly.degree() for n in mpmath.libmp.libintmath.giant_steps(32, 256): # pragma: no branch with mpmath.workdps(n): A = lambdify((), a.ext, "mpmath")() B = lambdify((), b.ext, "mpmath")() basis = [B**i for i in range(m)] + [A] coeffs = mpmath.pslq(basis, maxcoeff=int(1e10), maxsteps=1000) if coeffs is None: break coeffs = [QQ(c, coeffs[-1]) for c in coeffs[:-1]] while not coeffs[-1]: coeffs.pop() coeffs.reverse() h = Poly(coeffs, f.gen, domain='QQ') if f.compose(h).rem(g).is_zero or f.compose(-h).rem(g).is_zero: return [-c for c in coeffs]
def _improve_results_precision(self, intermediate_results, verbose=True): """ Calculates GCFs to a higher depth using RelativeGCFEnumerator's implementation. We then feed those results and the constant given to a PSLQ, that tries to find a suitable LHS. Notice- The second part of this function (PSLQ), logically belongs to the next step of the algorithm - the result refinement part. It is implemented here, because the next function is not parallelized over different processes or clients, and we want the PSLQ to be parallelized as well. """ precise_intermediate_results = super()._improve_results_precision(intermediate_results, verbose) for i in precise_intermediate_results: print(i) pslq_results = [] const = self.constants_generator[0]() # using only one constant for now. for match, val, precision in precise_intermediate_results: mpf_val = mpmath.mpf(val) print(match) try: pslq_res = mpmath.pslq( [1, const, const**2, -mpf_val, -const * mpf_val, -(const**2) * mpf_val], tol=10 ** (1 - precision)) except Exception as e: import ipdb ipdb.set_trace() if pslq_res: # Sometimes, PSLQ can find several results for the same value (e.g. z(3)/(z(3)^2) = 1/z(3)) # we'll reduce fraction found to get uniform results reduced_num, reduced_denom = get_reduced_fraction(pslq_res[:3], pslq_res[3:], 2) print(reduced_num, reduced_denom) pslq_results.append(RefinedMatch(*match, val, reduced_num, reduced_denom, precision)) else: pslq_results.append(RefinedMatch(*match, val, None, None, precision)) return pslq_results
def real_to_simple_algebraic_maybe(x, **param): ''' Tries to turn a float into a rational combination of roots. If it succeeds, returns a sympy number If it fails, returns directly the float We return a number, a certificate, and a latex string It would be nice to also take into account other constants like pi? NB : this is very expensive. Can it be improved? ''' list_roots = nonsquare_int(param['root_max']) L = [np.sqrt(n) for n in list_roots] L.append(x) coeff = pslq(L, tol=param['tol']) # TOO EXPENSIVE #coeff = [1,2,3,5,6,7,-1] if coeff is None: # we found no algebraic combination return x, False, '' denominator = -coeff[-1] if denominator == 0: # this shouldn't happen, but who knows.. return x, False, '' fracs = [Fraction(c, denominator) for c in coeff[:-1]] for frac in fracs: if frac.denominator > param['denominator_max']: return x, False, '' approx = 0 latex = '' for k in range(len(fracs)): if fracs[k] != 0: approx = approx + fracs[k] * sympy.sqrt(list_roots[ k]) # it is expensive to use sympy.sqrt. Maybe hard code it? latex = latex + fraction_x_root_to_latex( (0, fracs[k], list_roots[k]), **param) + '+' return approx, True, latex[:-1]
def field_isomorphism_pslq(a, b): """Construct field isomorphism using PSLQ algorithm.""" if not all(_.domain.is_RationalField and _.ext.is_real for _ in (a, b)): raise NotImplementedError("PSLQ doesn't support complex coefficients") f = a.minpoly g = b.minpoly.replace(f.gen) m = b.minpoly.degree() for n in mpmath.libmp.libintmath.giant_steps(32, 256): # pragma: no branch with mpmath.workdps(n): A = lambdify((), a.ext, "mpmath")() B = lambdify((), b.ext, "mpmath")() basis = [B**i for i in range(m)] + [A] coeffs = mpmath.pslq(basis, maxcoeff=int(1e10), maxsteps=1000) if coeffs is None: break coeffs = [QQ(c, coeffs[-1]) for c in coeffs[:-1]] while not coeffs[-1]: coeffs.pop() coeffs.reverse() h = Poly(coeffs, f.gen, domain='QQ') if f.compose(h).rem(g).is_zero or f.compose(-h).rem(g).is_zero: return [-c for c in coeffs]
def find_simple_recurrence_vector(v, maxcoeff=1024): """ This function is used internally by other functions from the sympy.concrete.guess module. While most users may want to rather use the function find_simple_recurrence when looking for recurrence relations among rational numbers, the current function may still be useful when some post-processing has to be done. The function returns a vector of length n when a recurrence relation of order n is detected in the sequence of rational numbers v. If the returned vector has a length 1, then the returned value is always the list [0], which means that no relation has been found. While the functions is intended to be used with rational numbers, it should work for other kinds of real numbers except for some cases involving quadratic numbers; for that reason it should be used with some caution when the argument is not a list of rational numbers. Examples ======== >>> from sympy.concrete.guess import find_simple_recurrence_vector >>> from sympy import fibonacci >>> find_simple_recurrence_vector([fibonacci(k) for k in range(12)]) [1, -1, -1] See also ======== See the function sympy.concrete.guess.find_simple_recurrence which is more user-friendly. """ l = len(v) >> 1 previous = mp.prec # save current precision mp.prec = 128 b = [ sum(sqrt((l >> 1)**2 + k) * v[-1 - k - i] for k in range(l)) for i in range(l) ] p = pslq(b, maxcoeff=maxcoeff, maxsteps=128 + 4 * l) mp.prec = previous # restore current precision if p == None: return [0] first, last = 0, l - 1 while p[first] == 0: first += 1 while p[last] == 0: last -= 1 if first == last: return [0] # TODO: probably never occuring return p[first:last + 1]
def find_simple_recurrence_vector(v, maxcoeff=1024): """ This function is used internally by other functions from the sympy.concrete.guess module. While most users may want to rather use the function find_simple_recurrence when looking for recurrence relations among rational numbers, the current function may still be useful when some post-processing has to be done. The function returns a vector of length n when a recurrence relation of order n is detected in the sequence of rational numbers v. If the returned vector has a length 1, then the returned value is always the list [0], which means that no relation has been found. While the functions is intended to be used with rational numbers, it should work for other kinds of real numbers except for some cases involving quadratic numbers; for that reason it should be used with some caution when the argument is not a list of rational numbers. Examples ======== >>> from sympy.concrete.guess import find_simple_recurrence_vector >>> from sympy import fibonacci >>> find_simple_recurrence_vector([fibonacci(k) for k in range(12)]) [1, -1, -1] See also ======== See the function sympy.concrete.guess.find_simple_recurrence which is more user-friendly. """ l = len(v)>>1 previous = mp.prec # save current precision mp.prec = 128 b = [sum(sqrt((l>>1)**2 + k)*v[-1-k-i] for k in range(l)) for i in range(l)] p = pslq(b, maxcoeff = maxcoeff, maxsteps = 128 + 4*l) mp.prec = previous # restore current precision if p == None: return [0] first, last = 0, l-1 while p[first]==0: first += 1 while p[last]==0: last -= 1 if first == last: return [0] # TODO: probably never occuring return p[first:last+1]
def field_isomorphism_pslq(a, b): """Construct field isomorphism using PSLQ algorithm. """ if not a.root.is_real or not b.root.is_real: raise NotImplementedError("PSLQ doesn't support complex coefficients") f = a.minpoly g = b.minpoly.replace(f.gen) n, m, prev = 100, b.minpoly.degree(), None for i in range(1, 5): A = a.root.evalf(n) B = b.root.evalf(n) basis = [1, B] + [B**i for i in range(2, m)] + [A] dps, mp.dps = mp.dps, n coeffs = pslq(basis, maxcoeff=int(1e10), maxsteps=1000) mp.dps = dps if coeffs is None: break if coeffs != prev: prev = coeffs else: break coeffs = [S(c) / coeffs[-1] for c in coeffs[:-1]] while not coeffs[-1]: coeffs.pop() coeffs = list(reversed(coeffs)) h = Poly(coeffs, f.gen, domain='QQ') if f.compose(h).rem(g).is_zero: d, approx = len(coeffs) - 1, 0 for i, coeff in enumerate(coeffs): approx += coeff * B**(d - i) if A * approx < 0: return [-c for c in coeffs] else: return coeffs elif f.compose(-h).rem(g).is_zero: return [-c for c in coeffs] else: n *= 2 return None
def field_isomorphism_pslq(a, b): """Construct field isomorphism using PSLQ algorithm. """ if not a.root.is_real or not b.root.is_real: raise NotImplementedError("PSLQ doesn't support complex coefficients") f = a.minpoly g = b.minpoly.replace(f.gen) n, m, prev = 100, b.minpoly.degree(), None for i in range(1, 5): A = a.root.evalf(n) B = b.root.evalf(n) basis = [1, B] + [ B**i for i in range(2, m) ] + [A] dps, mp.dps = mp.dps, n coeffs = pslq(basis, maxcoeff=int(1e10), maxsteps=1000) mp.dps = dps if coeffs is None: break if coeffs != prev: prev = coeffs else: break coeffs = [S(c)/coeffs[-1] for c in coeffs[:-1]] while not coeffs[-1]: coeffs.pop() coeffs = list(reversed(coeffs)) h = Poly(coeffs, f.gen, domain='QQ') if f.compose(h).rem(g).is_zero: d, approx = len(coeffs) - 1, 0 for i, coeff in enumerate(coeffs): approx += coeff*B**(d - i) if A*approx < 0: return [ -c for c in coeffs ] else: return coeffs elif f.compose(-h).rem(g).is_zero: return [ -c for c in coeffs ] else: n *= 2 return None