Repository: LMFDB/lmfdb

Issue:
Error in euler factors of Artin representations under sage-9.0
After seeing this appear in travis test runs, @edgarcosta has found a way to reliably reproduce the problem.  While running the LMFDB under sage-9.0, go to 
[localhost/2.2e3_17.4t3.4c1](http://localhost:37777/L/ArtinRepresentation/2.2e3_17.4t3.4c1/)
This will
* take a very long time to load compared to [beta/2.2e3_17.4t3.4c1](beta.lmfdb.org/L/ArtinRepresentation/2.2e3_17.4t3.4c1/),
* the zeros on the critical line will never appear,
* and you will see the following error on the command line:
```
Traceback (most recent call last):
...
  File ".../lmfdb/artin_representations/math_classes.py", line 550, in good_factor
    return self.from_conjugacy_class_index_to_polynomial(self.nf().from_prime_to_conjugacy_class_index(p))
  File ".../lmfdb/artin_representations/math_classes.py", line 868, in from_prime_to_conjugacy_class_index
    return self.from_cycle_type_to_conjugacy_class_index(self.frobenius_cycle_type(p), p)
  File ".../lmfdb/artin_representations/math_classes.py", line 865, in from_cycle_type_to_conjugacy_class_index
    return fn_to_use(p)
  File ".../lmfdb/artin_representations/cyc_alt_res_engine.py", line 191, in RES_from_cycle_type_to_conjugacy_class_index
    return polynomial_conjugacy_class_matcher(alpha)
  File ".../lmfdb/artin_representations/cyc_alt_res_engine.py", line 62, in polynomial_conjugacy_class_matcher
    raise AssertionError("alpha = %s is supposed to be root of one of %s" % (alpha, input))
AssertionError: alpha = 8*a + 36 is supposed to be root of one of [{'RootOf': ['-2', '1'], 'ConjugacyClass': 2}, {'RootOf': ['14', '8', '1'], 'ConjugacyClass': 3}]
```


Discussion:
I traced this to where we call sage to get roots of a polynomial over a finite field, and sometimes sage is producing something which is not a root.

This sometimes happens for p=29 over F_{29^2} for x^4 + 28*x^3 + 27*x^2 + 28*x + 1.  Sometimes it returns 20 as a root, and it isn't.
A little more information.  If I just repeatedly call the roots function, I don't get an error.   I have to call it rotating through different primes.

Here is a small bit of code to generate an error:

def tst(p):
  Fq= FiniteField(p**2,"a")
  Fq_X = PolynomialRing(Fq, "x")
  pol = Fq_X([1, -1, -2, -1, 1])
  roots = pol.roots(multiplicities=False)
  print(p, " ", roots)
  for r in roots:
    try:
      assert pol(r) == 0
    except:
      print("Caught ", p, " and ", r)
      raise ValueError 

def domany(cnt=500):
  for j in range(cnt):
    [tst(5), tst(13), tst(29)]

Then call domany()

The three primes are chosen because the polynomial factors as a product of two quadratics for each, so they will have their roots in F_{p^2}.
Thanks, that's very helpful.  I will try to debug this in Sage.
I'm building 9.1 beta6 to see if the error is still present
This is a bug in pari, which Sage is using to do the factorization:

```python
def tst(p):
     Fq= FiniteField(p**2,"a")
     Fq_X = PolynomialRing(Fq, "x")
     pol = Fq_X([1, -1, -2, -1, 1])
     v = [z.__pari__("a") for z in pol.list()]
     f = pari(v).Polrev()
     G = list(f.factor())
     roots = [-Fq_X(F)[0] for F in G[0]]
     print(p, " ", roots)
     for r in roots:
         assert pol(r) == 0
```
also causes the error.  @jwj61 do you want to submit a gp/pari bug report, or should I?
This seems to be fixed in 9.1beta9.
I assume this means it is already fixed in a more recent version of gp
I'm building a new sage for travis.
> I assume this means it is already fixed in a more recent version of gp

Yes, I would expect so.  So there's no need to submit a bug report to pari.

Resolving patch:
diff --git a/lmfdb/utils/__init__.py b/lmfdb/utils/__init__.py
--- a/lmfdb/utils/__init__.py
+++ b/lmfdb/utils/__init__.py
@@ -17,7 +17,7 @@
            'polyquo_knowl', 'web_latex_poly', 'list_to_latex_matrix',
            'code_snippet_knowl',
            'Pagination',
-           'debug', 'flash_error', 'cached',
+           'debug', 'flash_error',
            'ajax_url',
            'image_callback', 'encode_plot',
            'KeyedDefaultDict', 'make_tuple', 'range_formatter',
@@ -60,7 +60,7 @@
     bigpoly_knowl, factor_base_factor, factor_base_factorization_latex,
     polyquo_knowl, web_latex_poly, list_to_latex_matrix, code_snippet_knowl,
     Pagination,
-    debug, flash_error, cached,
+    debug, flash_error, 
     ajax_url,  # try to eliminate?
     image_callback, encode_plot,
     KeyedDefaultDict, make_tuple, range_formatter,
diff --git a/lmfdb/utils/utilities.py b/lmfdb/utils/utilities.py
--- a/lmfdb/utils/utilities.py
+++ b/lmfdb/utils/utilities.py
@@ -1,8 +1,4 @@
 # -*- encoding: utf-8 -*-
-# this is the caching wrapper, use it like this:
-# @app.route(....)
-# @cached()
-# def func(): ...
 from six.moves import range
 from six import integer_types as six_integers
 from six import string_types
@@ -17,16 +13,13 @@
 import time
 from collections import defaultdict
 from copy import copy
-from functools import wraps
 from itertools import islice
 from types import GeneratorType
 from six.moves.urllib_parse import urlencode
 from six import PY3
 
-from flask import request, make_response, flash, url_for, current_app
+from flask import make_response, flash, url_for, current_app
 from markupsafe import Markup, escape
-# DeprecationWarning: 'werkzeug.contrib.cache' is deprecated as of version 0.15 and will be removed in version 1.0. It has moved to https://github.com/pallets/cachelib.
-from werkzeug.contrib.cache import SimpleCache
 from werkzeug.utils import cached_property
 from sage.all import (CC, CBF, CDF,
                       Factorization, NumberField,
@@ -1052,23 +1045,6 @@ def flash_error(errmsg, *args):
     flash(Markup("Error: " + (errmsg % tuple("<span style='color:black'>%s</span>" % escape(x) for x in args))), "error")
 
 
-cache = SimpleCache()
-def cached(timeout=15 * 60, key='cache::%s::%s'):
-    def decorator(f):
-        @wraps(f)
-        def decorated_function(*args, **kwargs):
-            cache_key = key % (request.path, request.args)
-            rv = cache.get(cache_key)
-            if rv is None:
-                rv = f(*args, **kwargs)
-                cache.set(cache_key, rv, timeout=timeout)
-            ret = make_response(rv)
-            # this header basically says that any cache can store this infinitely long
-            # set this down to 600, because we have pagespeed now (hsy)
-            ret.headers['Cache-Control'] = 'max-age=600, public'
-            return ret
-        return decorated_function
-    return decorator
 
 ################################################################################
 #  Ajax utilities


Test patch: