From 5a61e3eb59a6009e6ac7e86787c5915f426f8ff5 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Mon, 27 Jul 2026 11:04:00 +0100 Subject: [PATCH 01/14] Add exact Sage Diophantine research sweep --- research/diophantine_search.sage | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 research/diophantine_search.sage diff --git a/research/diophantine_search.sage b/research/diophantine_search.sage new file mode 100644 index 0000000..515ee74 --- /dev/null +++ b/research/diophantine_search.sage @@ -0,0 +1,116 @@ +#!/usr/bin/env sage +from sage.all import * +import itertools, json, sys, time +proof.all(True) +mode = sys.argv[1] if len(sys.argv) > 1 else 'all' + +def emit(kind, **kw): + print(json.dumps({'kind': kind, **kw}, sort_keys=True), flush=True) + +def verify(x,y,z): + x,y,z=ZZ(x),ZZ(y),ZZ(z) + return z*z+y*y*z+x*x*x-2 == 0 + +def map_e1(P): + if P.is_zero(): return None + X,Y=QQ(P[0]),QQ(P[1]); den=X*(X+2) + if den==0: return None + y=(2*Y+3*X+6)/den + if y.denominator()!=1: return None + y=ZZ(y); x=1-y + w=2+3*y-(X+1)*y*y + if QQ(w).denominator()!=1: return None + w=ZZ(w) + if (w-y*y)%2: return None + z=(w-y*y)//2 + assert verify(x,y,z) + return x,y,z + +def map_e2(P): + if P.is_zero(): return None + U,Y=QQ(P[0]),QQ(P[1]); den=(U-232)*(U-528) + if den==0: return None + u=4*(37*Y+71*U-235068)/den + if u.denominator()!=1: return None + u=ZZ(u); y=u+8; x=3*u-7 + r=(380-U)/148 + w=74+QQ(71,37)*u+r*u*u + if w.denominator()!=1: return None + w=ZZ(w) + if (w-y*y)%2: return None + z=(w-y*y)//2 + assert verify(x,y,z) + return x,y,z + +def lattice(E, gens, bound, mapper, label): + rg=range(-bound,bound+1); r=len(gens); start=time.time(); hits={}; tested=0 + mult=[[n*P for n in rg] for P in gens] + cut=r//2 + left=[] + for cs in itertools.product(*([rg]*cut)): + R=E(0) + for i,c in enumerate(cs): R += mult[i][c+bound] + left.append((cs,R)) + right=[] + for cs in itertools.product(*([rg]*(r-cut))): + R=E(0) + for j,c in enumerate(cs,start=cut): R += mult[j][c+bound] + right.append((cs,R)) + emit('lattice_start',label=label,rank=r,bound=bound,left=len(left),right=len(right),gens=[str(P) for P in gens]) + for lc,LP in left: + for rc,RP in right: + cs=lc+rc + if all(c==0 for c in cs): continue + tested+=1; out=mapper(LP+RP) + if out: + key=tuple(map(int,out)) + if key not in hits: + hits[key]=cs + emit('surface_hit',label=label,coefficients=list(cs),x=str(out[0]),y=str(out[1]),z=str(out[2]),x_digits=len(str(abs(out[0])))) + emit('lattice_done',label=label,tested=tested,hits=len(hits),seconds=time.time()-start) + +def conics(): + E=EllipticCurve([0,0,0,0,-2]); P=E(3,5) + emit('mordell',rank=int(E.rank(proof=True)),gens=[str(Q) for Q in E.gens(proof=True)]) + for n in range(1,31): + Q=n*P; k,v=QQ(Q[0]),QQ(Q[1]); w=abs(v); C=-k**6+40*k**3+32 + Cn=Conic(QQ,[3*k*k*w*w,-4*w**3,-C]); t=time.time() + try: + ok,ans=Cn.has_rational_point(point=True) + emit('conic',multiple=n,soluble=bool(ok),witness=str(ans),seconds=time.time()-t) + except Exception as ex: + emit('conic_error',multiple=n,error=repr(ex),seconds=time.time()-t) + +def e1(): + E=EllipticCurve([0,0,1,-1,6]); t=time.time(); gens=E.gens(proof=True) + emit('curve',label='E1',rank=int(E.rank(proof=True)),gens=[str(P) for P in gens],torsion=[str(P) for P in E.torsion_points()],seconds=time.time()-t) + try: + pts=E.integral_points(both_signs=True) + emit('integral_points',label='E1',count=len(pts),points=[str(P) for P in pts]) + for P in pts: + out=map_e1(P) + if out: emit('integral_surface',label='E1',point=str(P),x=str(out[0]),y=str(out[1]),z=str(out[2])) + except Exception as ex: emit('integral_error',label='E1',error=repr(ex)) + lattice(E,gens,100,map_e1,'E1') + +def e2(): + E=EllipticCurve([0,0,0,-476688,133008912]); t=time.time() + known=[E(132,8508),E(QQ(23796,121),QQ(9112140,1331)),E(QQ(18369,49),QQ(-906975,343)),E(276,-4740),E(QQ(311724,1369),QQ(-305064180,50653))] + try: + gens=E.gens(proof=True); rank=E.rank(proof=True) + emit('curve',label='E2',rank=int(rank),gens=[str(P) for P in gens],torsion=[str(P) for P in E.torsion_points()],seconds=time.time()-t) + except Exception as ex: + emit('curve_error',label='E2',error=repr(ex),seconds=time.time()-t); gens=known + try: + pts=E.integral_points(both_signs=True) + emit('integral_points',label='E2',count=len(pts),points=[str(P) for P in pts]) + for P in pts: + out=map_e2(P) + if out: emit('integral_surface',label='E2',point=str(P),x=str(out[0]),y=str(out[1]),z=str(out[2])) + except Exception as ex: emit('integral_error',label='E2',error=repr(ex)) + lattice(E,known,10,map_e2,'E2-known') + if len(gens)<=6: lattice(E,gens,8 if len(gens)>=5 else 18,map_e2,'E2-basis') + +if mode in ('all','conics'): conics() +if mode in ('all','e1'): e1() +if mode in ('all','e2'): e2() From 9d6347ea71f841e8147cecdb1efd2a8a19373376 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Mon, 27 Jul 2026 11:04:15 +0100 Subject: [PATCH 02/14] Run exact Sage Diophantine jobs --- .github/workflows/diophantine-research.yml | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/diophantine-research.yml diff --git a/.github/workflows/diophantine-research.yml b/.github/workflows/diophantine-research.yml new file mode 100644 index 0000000..a77e1d2 --- /dev/null +++ b/.github/workflows/diophantine-research.yml @@ -0,0 +1,33 @@ +name: Exact Diophantine research + +on: + push: + branches: + - research/diophantine-sage-20260727 + workflow_dispatch: + +jobs: + sage: + runs-on: ubuntu-latest + timeout-minutes: 300 + strategy: + fail-fast: false + matrix: + mode: [conics, e1, e2] + steps: + - uses: actions/checkout@v4 + - name: Pull SageMath + run: docker pull sagemath/sagemath:latest + - name: Run ${{ matrix.mode }} exact search + run: | + set -o pipefail + docker run --rm -v "$PWD:/work" -w /work sagemath/sagemath:latest \ + sage research/diophantine_search.sage "${{ matrix.mode }}" \ + | tee "research/results-${{ matrix.mode }}.jsonl" + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: exact-diophantine-${{ matrix.mode }} + path: research/results-${{ matrix.mode }}.jsonl + if-no-files-found: warn From 7dfaaaade27f49844e0662ee95f9222e6fe0d0e8 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Mon, 27 Jul 2026 11:05:45 +0100 Subject: [PATCH 03/14] Enable exact sweep for pull requests --- .github/workflows/diophantine-research.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/diophantine-research.yml b/.github/workflows/diophantine-research.yml index a77e1d2..c9ed9c9 100644 --- a/.github/workflows/diophantine-research.yml +++ b/.github/workflows/diophantine-research.yml @@ -4,6 +4,10 @@ on: push: branches: - research/diophantine-sage-20260727 + pull_request: + paths: + - 'research/diophantine_search.sage' + - '.github/workflows/diophantine-research.yml' workflow_dispatch: jobs: From 7595262954d576aa6bb49e81a61b643a45cfc287 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Mon, 27 Jul 2026 11:14:32 +0100 Subject: [PATCH 04/14] Add positive-leading norm family search --- research/eplus_search.sage | 60 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 research/eplus_search.sage diff --git a/research/eplus_search.sage b/research/eplus_search.sage new file mode 100644 index 0000000..4d286c3 --- /dev/null +++ b/research/eplus_search.sage @@ -0,0 +1,60 @@ +#!/usr/bin/env sage +from sage.all import * +import itertools, json, sys, time +proof.all(True) +n = Integer(sys.argv[1]) if len(sys.argv)>1 else Integer(1) + +def emit(kind, **kw): print(json.dumps({'kind':kind, **kw}, sort_keys=True), flush=True) +def verify(x,y,z): return z*z+y*y*z+x*x*x-2 == 0 + +E0=EllipticCurve([0,0,0,0,2]); P=E0(-1,1); Q=n*P +k,v=QQ(Q[0]),QQ(Q[1]) +s0=3*(k**4+8*k)/(v**2) +d0=(k**4-16*k)/(v**2) +r0=k**2/v +nq=(k**6+40*k**3-32)/(v**3) +E=EllipticCurve([0,0,0,s0,0]) +emit('family',multiple=int(n),k=str(k),v=str(v),s0=str(s0),d0=str(d0),curve=str(E)) + +def to_surface(R): + if R.is_zero(): return None + T,W=QQ(R[0]),QQ(R[1]) + x=(T*T-d0)/4; y=W/2 + U=(T**3-3*r0*T*T+s0*T-nq)/8 + z=-U + if any(q.denominator()!=1 for q in (x,y,z)): return None + x,y,z=ZZ(x),ZZ(y),ZZ(z) + assert verify(x,y,z) + return x,y,z + +t=time.time() +try: + rank=E.rank(proof=True); gens=E.gens(proof=True); tors=E.torsion_points() + emit('curve_data',multiple=int(n),rank=int(rank),gens=[str(R) for R in gens],torsion=[str(R) for R in tors],seconds=time.time()-t) +except Exception as ex: + emit('curve_error',multiple=int(n),error=repr(ex),seconds=time.time()-t); sys.exit(0) + +try: + pts=E.integral_points(both_signs=True) + emit('integral_points',multiple=int(n),count=len(pts),points=[str(R) for R in pts]) + for R in pts: + out=to_surface(R) + if out: emit('surface_hit',multiple=int(n),source='integral_points',point=str(R),x=str(out[0]),y=str(out[1]),z=str(out[2]),x_digits=len(str(abs(out[0])))) +except Exception as ex: emit('integral_error',multiple=int(n),error=repr(ex)) + +rank=len(gens) +bound=60 if rank<=2 else (18 if rank==3 else (7 if rank==4 else 3)) +rg=range(-bound,bound+1); mult=[[c*G for c in rg] for G in gens] +tors=E.torsion_points(); tested=0; hits={}; start=time.time() +emit('lattice_start',multiple=int(n),rank=rank,bound=bound,total=(2*bound+1)**rank*len(tors)) +for cs in itertools.product(*([rg]*rank)): + R=E(0) + for i,c in enumerate(cs): R += mult[i][c+bound] + for Tors in tors: + S=R+Tors; tested+=1; out=to_surface(S) + if out: + key=tuple(map(int,out)) + if key not in hits: + hits[key]=(cs,str(Tors)) + emit('surface_hit',multiple=int(n),source='lattice',coefficients=list(cs),torsion=str(Tors),x=str(out[0]),y=str(out[1]),z=str(out[2]),x_digits=len(str(abs(out[0])))) +emit('lattice_done',multiple=int(n),tested=tested,hits=len(hits),seconds=time.time()-start) From a4161436659ab77a6ccf660c6cbb4dbb52d15397 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Mon, 27 Jul 2026 11:14:56 +0100 Subject: [PATCH 05/14] Run positive-leading norm family in Sage --- .github/workflows/diophantine-research.yml | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/diophantine-research.yml b/.github/workflows/diophantine-research.yml index c9ed9c9..22ac923 100644 --- a/.github/workflows/diophantine-research.yml +++ b/.github/workflows/diophantine-research.yml @@ -7,6 +7,7 @@ on: pull_request: paths: - 'research/diophantine_search.sage' + - 'research/eplus_search.sage' - '.github/workflows/diophantine-research.yml' workflow_dispatch: @@ -35,3 +36,28 @@ jobs: name: exact-diophantine-${{ matrix.mode }} path: research/results-${{ matrix.mode }}.jsonl if-no-files-found: warn + + positive-norm-family: + runs-on: ubuntu-latest + timeout-minutes: 300 + strategy: + fail-fast: false + matrix: + multiple: [1, 2, 3, 4, 5, 6] + steps: + - uses: actions/checkout@v4 + - name: Pull SageMath + run: docker pull sagemath/sagemath:latest + - name: Search family member ${{ matrix.multiple }} + run: | + set -o pipefail + docker run --rm -v "$PWD:/work" -w /work sagemath/sagemath:latest \ + sage research/eplus_search.sage "${{ matrix.multiple }}" \ + | tee "research/results-eplus-${{ matrix.multiple }}.jsonl" + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: exact-eplus-${{ matrix.multiple }} + path: research/results-eplus-${{ matrix.multiple }}.jsonl + if-no-files-found: warn From 1719f581ee86aca3ced9ded673e5bfbc4ad4f176 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Mon, 27 Jul 2026 11:21:54 +0100 Subject: [PATCH 06/14] Add scaled norm-factor elliptic curve search --- research/lambda_curve_search.sage | 66 +++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 research/lambda_curve_search.sage diff --git a/research/lambda_curve_search.sage b/research/lambda_curve_search.sage new file mode 100644 index 0000000..f5b3b23 --- /dev/null +++ b/research/lambda_curve_search.sage @@ -0,0 +1,66 @@ +#!/usr/bin/env sage +from sage.all import * +import itertools, json, sys, time +proof.all(True) +code=sys.argv[1] if len(sys.argv)>1 else '2' +values={ + '2':QQ(2),'3':QQ(3),'5_3':QQ(5,3),'34_15':QQ(34,15), + '25_41':QQ(25,41),'58_101':QQ(58,101),'87_101':QQ(87,101), + '9129_3943':QQ(9129,3943),'4250453_7007642':QQ(4250453,7007642) +} +lam=values[code] + +def emit(kind,**kw): print(json.dumps({'kind':kind,**kw},sort_keys=True),flush=True) +def verify(x,y,z): return z*z+y*y*z+x*x*x-2==0 +A=lam+1/lam; B=3*(1/lam-lam); D=71*(lam-1/lam) +a=A/8; b=B/8; c=-21*A/8; d=D/8 +# X=a*T and W=a*y gives W^2=X^3+b*X^2+a*c*X+a^2*d. +E=EllipticCurve([0,b,0,a*c,a*a*d]) +emit('family',code=code,lam=str(lam),curve=str(E),ainvs=[str(q) for q in E.ainvs()]) + +def to_surface(P): + if P.is_zero(): return None + X,W=QQ(P[0]),QQ(P[1]); T=X/a; y=W/a + x=(T*T-17)/4 + N1=(T**3-3*T**2-21*T+71)/8 + z=-lam*N1 + if any(q.denominator()!=1 for q in (x,y,z)): return None + x,y,z=ZZ(x),ZZ(y),ZZ(z); assert verify(x,y,z); return x,y,z,T + +t=time.time() +try: + rank=E.rank(proof=True); gens=E.gens(proof=True); tors=E.torsion_points() + emit('curve_data',code=code,rank=int(rank),gens=[str(P) for P in gens],torsion=[str(P) for P in tors],seconds=time.time()-t) +except Exception as ex: + emit('curve_error',code=code,error=repr(ex),seconds=time.time()-t) + try: + rank=E.rank(proof=False); gens=E.gens(proof=False); tors=E.torsion_points() + emit('curve_data_unproved',code=code,rank=int(rank),gens=[str(P) for P in gens],torsion=[str(P) for P in tors]) + except Exception as ex2: emit('curve_error_unproved',code=code,error=repr(ex2)); sys.exit(0) + +try: + Em=E.global_minimal_model(); iso=E.isomorphism_to(Em) + emit('minimal',code=code,curve=str(Em),ainvs=[str(q) for q in Em.ainvs()]) + pts=Em.integral_points(both_signs=True) + emit('minimal_integral_points',code=code,count=len(pts),points=[str(P) for P in pts]) + for Q in pts: + P=iso.inverse()(Q); out=to_surface(P) + if out: emit('surface_hit',code=code,source='minimal_integral',point=str(P),x=str(out[0]),y=str(out[1]),z=str(out[2]),T=str(out[3]),x_digits=len(str(abs(out[0])))) +except Exception as ex: emit('integral_error',code=code,error=repr(ex)) + +# Modest exact Mordell-Weil lattice scan, with every torsion coset. +r=len(gens); bound=100 if r==1 else (25 if r==2 else (8 if r==3 else 3)) +rg=range(-bound,bound+1); mult=[[n*P for n in rg] for P in gens]; hits={}; tested=0; start=time.time() +emit('lattice_start',code=code,rank=r,bound=bound,total=(2*bound+1)**r*len(tors)) +try: + for cs in itertools.product(*([rg]*r)): + R=E(0) + for i,n in enumerate(cs): R += mult[i][n+bound] + for Tor in tors: + tested+=1; out=to_surface(R+Tor) + if out: + key=(int(out[0]),int(out[1]),int(out[2])) + if key not in hits: + hits[key]=(cs,str(Tor)); emit('surface_hit',code=code,source='lattice',coefficients=list(cs),torsion=str(Tor),x=str(out[0]),y=str(out[1]),z=str(out[2]),T=str(out[3]),x_digits=len(str(abs(out[0])))) + emit('lattice_done',code=code,tested=tested,hits=len(hits),seconds=time.time()-start) +except Exception as ex: emit('lattice_error',code=code,tested=tested,error=repr(ex),seconds=time.time()-start) From 112365847e270ec928ccc242d7e73ef7ad88dca7 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Mon, 27 Jul 2026 11:22:44 +0100 Subject: [PATCH 07/14] Search scaled norm-factor elliptic curves --- .github/workflows/diophantine-research.yml | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/diophantine-research.yml b/.github/workflows/diophantine-research.yml index 22ac923..bfdfc72 100644 --- a/.github/workflows/diophantine-research.yml +++ b/.github/workflows/diophantine-research.yml @@ -8,6 +8,7 @@ on: paths: - 'research/diophantine_search.sage' - 'research/eplus_search.sage' + - 'research/lambda_curve_search.sage' - '.github/workflows/diophantine-research.yml' workflow_dispatch: @@ -61,3 +62,28 @@ jobs: name: exact-eplus-${{ matrix.multiple }} path: research/results-eplus-${{ matrix.multiple }}.jsonl if-no-files-found: warn + + scaled-factor-curves: + runs-on: ubuntu-latest + timeout-minutes: 300 + strategy: + fail-fast: false + matrix: + code: ['2', '3', '5_3', '34_15', '25_41', '58_101', '87_101', '9129_3943', '4250453_7007642'] + steps: + - uses: actions/checkout@v4 + - name: Pull SageMath + run: docker pull sagemath/sagemath:latest + - name: Search scaled factor curve ${{ matrix.code }} + run: | + set -o pipefail + docker run --rm -v "$PWD:/work" -w /work sagemath/sagemath:latest \ + sage research/lambda_curve_search.sage "${{ matrix.code }}" \ + | tee "research/results-lambda-${{ matrix.code }}.jsonl" + - name: Upload results + if: always() + uses: actions/upload-artifact@v4 + with: + name: exact-lambda-${{ matrix.code }} + path: research/results-lambda-${{ matrix.code }}.jsonl + if-no-files-found: warn From ed086ef41d09dd91320d694b21d3d80a44715513 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Tue, 28 Jul 2026 17:46:05 +0100 Subject: [PATCH 08/14] Add exact fixed-z Mordell sweep --- research/fixed_z_search.sage | 81 ++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 research/fixed_z_search.sage diff --git a/research/fixed_z_search.sage b/research/fixed_z_search.sage new file mode 100644 index 0000000..18229f5 --- /dev/null +++ b/research/fixed_z_search.sage @@ -0,0 +1,81 @@ +#!/usr/bin/env sage +from sage.all import * +import json, sys, time + +proof.all(False) +start = ZZ(sys.argv[1]) +end = ZZ(sys.argv[2]) + + +def emit(kind, **payload): + print(json.dumps({"kind": kind, **payload}, sort_keys=True), flush=True) + + +def verify(x, y, z): + return z*z + y*y*z + x*x*x - 2 == 0 + + +for d in range(start, end + 1): + if d == 0: + continue + t0 = time.time() + c = d**3 * (d**2 - 2) + E = EllipticCurve([0, 0, 0, 0, c]) + emit("curve_start", d=int(d), constant=str(c)) + try: + rank = E.rank(proof=False) + gens = E.gens(proof=False) + emit("curve_data", d=int(d), rank=int(rank), gens=[str(P) for P in gens], seconds=time.time()-t0) + except Exception as exc: + emit("rank_error", d=int(d), error=repr(exc), seconds=time.time()-t0) + continue + + # A bounded lattice pass catches points even when a complete integral-point + # computation is expensive. Every hit is independently verified. + bound = 150 if len(gens) == 1 else (35 if len(gens) == 2 else (10 if len(gens) == 3 else 4)) + seen = set() + try: + if gens: + ranges = [range(-bound, bound + 1) for _ in gens] + import itertools + for coeffs in itertools.product(*ranges): + if all(n == 0 for n in coeffs): + continue + P = E(0) + for n, G in zip(coeffs, gens): + P += n*G + if P.is_zero(): + continue + X, Y = QQ(P[0]), QQ(P[1]) + if X.denominator() != 1 or Y.denominator() != 1: + continue + X, Y = ZZ(X), ZZ(Y) + if X % d or Y % (d*d): + continue + x, y, z = X//d, Y//(d*d), -d + if verify(x, y, z): + key = (int(x), int(y), int(z)) + if key not in seen: + seen.add(key) + emit("surface_hit", source="lattice", d=int(d), coefficients=list(coeffs), x=str(x), y=str(y), z=str(z), x_digits=len(str(abs(x)))) + except Exception as exc: + emit("lattice_error", d=int(d), error=repr(exc)) + + # Sage's elliptic-logarithm routine gives a complete list when it succeeds. + try: + pts = E.integral_points(both_signs=True) + emit("integral_points", d=int(d), count=len(pts)) + for P in pts: + X, Y = ZZ(P[0]), ZZ(P[1]) + if X % d or Y % (d*d): + continue + x, y, z = X//d, Y//(d*d), -d + assert verify(x, y, z) + key = (int(x), int(y), int(z)) + if key not in seen: + seen.add(key) + emit("surface_hit", source="integral_points", d=int(d), point=str(P), x=str(x), y=str(y), z=str(z), x_digits=len(str(abs(x)))) + except Exception as exc: + emit("integral_error", d=int(d), error=repr(exc)) + + emit("curve_done", d=int(d), hits=len(seen), seconds=time.time()-t0) From a81f0b691554e7487f41a2709946cf9f3cedafab Mon Sep 17 00:00:00 2001 From: teatimedev Date: Tue, 28 Jul 2026 17:46:29 +0100 Subject: [PATCH 09/14] Focus workflow on exact fixed-z Mordell sweep --- .github/workflows/diophantine-research.yml | 81 +++++----------------- 1 file changed, 18 insertions(+), 63 deletions(-) diff --git a/.github/workflows/diophantine-research.yml b/.github/workflows/diophantine-research.yml index bfdfc72..f81436b 100644 --- a/.github/workflows/diophantine-research.yml +++ b/.github/workflows/diophantine-research.yml @@ -1,89 +1,44 @@ -name: Exact Diophantine research +name: Exact fixed-z Diophantine sweep on: push: branches: - research/diophantine-sage-20260727 - pull_request: paths: - - 'research/diophantine_search.sage' - - 'research/eplus_search.sage' - - 'research/lambda_curve_search.sage' + - 'research/fixed_z_search.sage' - '.github/workflows/diophantine-research.yml' workflow_dispatch: jobs: - sage: + fixed-z: runs-on: ubuntu-latest timeout-minutes: 300 strategy: fail-fast: false matrix: - mode: [conics, e1, e2] + include: + - {start: -200, end: -151} + - {start: -150, end: -101} + - {start: -100, end: -51} + - {start: -50, end: -1} + - {start: 1, end: 50} + - {start: 51, end: 100} + - {start: 101, end: 150} + - {start: 151, end: 200} steps: - uses: actions/checkout@v4 - name: Pull SageMath run: docker pull sagemath/sagemath:latest - - name: Run ${{ matrix.mode }} exact search + - name: Run exact fixed-z sweep run: | set -o pipefail docker run --rm -v "$PWD:/work" -w /work sagemath/sagemath:latest \ - sage research/diophantine_search.sage "${{ matrix.mode }}" \ - | tee "research/results-${{ matrix.mode }}.jsonl" - - name: Upload results + sage research/fixed_z_search.sage "${{ matrix.start }}" "${{ matrix.end }}" \ + | tee "research/results-fixed-z-${{ matrix.start }}-${{ matrix.end }}.jsonl" + - name: Upload exact results if: always() uses: actions/upload-artifact@v4 with: - name: exact-diophantine-${{ matrix.mode }} - path: research/results-${{ matrix.mode }}.jsonl - if-no-files-found: warn - - positive-norm-family: - runs-on: ubuntu-latest - timeout-minutes: 300 - strategy: - fail-fast: false - matrix: - multiple: [1, 2, 3, 4, 5, 6] - steps: - - uses: actions/checkout@v4 - - name: Pull SageMath - run: docker pull sagemath/sagemath:latest - - name: Search family member ${{ matrix.multiple }} - run: | - set -o pipefail - docker run --rm -v "$PWD:/work" -w /work sagemath/sagemath:latest \ - sage research/eplus_search.sage "${{ matrix.multiple }}" \ - | tee "research/results-eplus-${{ matrix.multiple }}.jsonl" - - name: Upload results - if: always() - uses: actions/upload-artifact@v4 - with: - name: exact-eplus-${{ matrix.multiple }} - path: research/results-eplus-${{ matrix.multiple }}.jsonl - if-no-files-found: warn - - scaled-factor-curves: - runs-on: ubuntu-latest - timeout-minutes: 300 - strategy: - fail-fast: false - matrix: - code: ['2', '3', '5_3', '34_15', '25_41', '58_101', '87_101', '9129_3943', '4250453_7007642'] - steps: - - uses: actions/checkout@v4 - - name: Pull SageMath - run: docker pull sagemath/sagemath:latest - - name: Search scaled factor curve ${{ matrix.code }} - run: | - set -o pipefail - docker run --rm -v "$PWD:/work" -w /work sagemath/sagemath:latest \ - sage research/lambda_curve_search.sage "${{ matrix.code }}" \ - | tee "research/results-lambda-${{ matrix.code }}.jsonl" - - name: Upload results - if: always() - uses: actions/upload-artifact@v4 - with: - name: exact-lambda-${{ matrix.code }} - path: research/results-lambda-${{ matrix.code }}.jsonl + name: fixed-z-${{ matrix.start }}-${{ matrix.end }} + path: research/results-fixed-z-${{ matrix.start }}-${{ matrix.end }}.jsonl if-no-files-found: warn From 17d3c1c3ce89734dacd1e7c2270cd977abbb6825 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Tue, 28 Jul 2026 17:47:43 +0100 Subject: [PATCH 10/14] Expose fixed-z sweep to draft PR checks --- .github/workflows/diophantine-research.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/diophantine-research.yml b/.github/workflows/diophantine-research.yml index f81436b..60f0700 100644 --- a/.github/workflows/diophantine-research.yml +++ b/.github/workflows/diophantine-research.yml @@ -7,6 +7,10 @@ on: paths: - 'research/fixed_z_search.sage' - '.github/workflows/diophantine-research.yml' + pull_request: + paths: + - 'research/fixed_z_search.sage' + - '.github/workflows/diophantine-research.yml' workflow_dispatch: jobs: From e2e4dd11388141ff97b3472dd5aee7171bfa3461 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Tue, 28 Jul 2026 17:53:25 +0100 Subject: [PATCH 11/14] Add cubic-residue filter to fixed-z sweep --- research/fixed_z_search.sage | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/research/fixed_z_search.sage b/research/fixed_z_search.sage index 18229f5..cbf37a4 100644 --- a/research/fixed_z_search.sage +++ b/research/fixed_z_search.sage @@ -1,6 +1,6 @@ #!/usr/bin/env sage from sage.all import * -import json, sys, time +import itertools, json, sys, time proof.all(False) start = ZZ(sys.argv[1]) @@ -15,9 +15,25 @@ def verify(x, y, z): return z*z + y*y*z + x*x*x - 2 == 0 +def local_obstruction(d): + # From d*y^2 = x^3+d^2-2, every p|d requires x^3=2 (mod p). + for p, _ in factor(abs(d)): + p = ZZ(p) + if p in (2, 3) or p % 3 == 2: + continue + if power_mod(2, (p-1)//3, p) != 1: + return p + return None + + for d in range(start, end + 1): if d == 0: continue + obstruction = local_obstruction(d) + if obstruction is not None: + emit("local_skip", d=int(d), obstruction=int(obstruction)) + continue + t0 = time.time() c = d**3 * (d**2 - 2) E = EllipticCurve([0, 0, 0, 0, c]) @@ -30,14 +46,11 @@ for d in range(start, end + 1): emit("rank_error", d=int(d), error=repr(exc), seconds=time.time()-t0) continue - # A bounded lattice pass catches points even when a complete integral-point - # computation is expensive. Every hit is independently verified. bound = 150 if len(gens) == 1 else (35 if len(gens) == 2 else (10 if len(gens) == 3 else 4)) seen = set() try: if gens: ranges = [range(-bound, bound + 1) for _ in gens] - import itertools for coeffs in itertools.product(*ranges): if all(n == 0 for n in coeffs): continue @@ -61,7 +74,6 @@ for d in range(start, end + 1): except Exception as exc: emit("lattice_error", d=int(d), error=repr(exc)) - # Sage's elliptic-logarithm routine gives a complete list when it succeeds. try: pts = E.integral_points(both_signs=True) emit("integral_points", d=int(d), count=len(pts)) From ee41bbc0780fd72cbb37ac016b53baf83aa13b0e Mon Sep 17 00:00:00 2001 From: teatimedev Date: Tue, 28 Jul 2026 17:55:14 +0100 Subject: [PATCH 12/14] Strengthen local filters and remove duplicate positive-z search --- research/fixed_z_search.sage | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/research/fixed_z_search.sage b/research/fixed_z_search.sage index cbf37a4..bd8692d 100644 --- a/research/fixed_z_search.sage +++ b/research/fixed_z_search.sage @@ -16,9 +16,17 @@ def verify(x, y, z): def local_obstruction(d): - # From d*y^2 = x^3+d^2-2, every p|d requires x^3=2 (mod p). - for p, _ in factor(abs(d)): - p = ZZ(p) + # Every solution has a companion with negative z, so only d=-z>0 is needed. + if d < 0: + return -1 + fd = factor(d) + # x^3=2 modulo 4 or 9 is impossible. + for p, e in fd: + p, e = ZZ(p), ZZ(e) + if p == 2 and e >= 2: + return 4 + if p == 3 and e >= 2: + return 9 if p in (2, 3) or p % 3 == 2: continue if power_mod(2, (p-1)//3, p) != 1: From 85f7c6cd75a84dd548f5b8331f6ef35320110227 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Tue, 28 Jul 2026 18:03:01 +0100 Subject: [PATCH 13/14] Cancel superseded searches and run only negative-z representatives --- .github/workflows/diophantine-research.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/diophantine-research.yml b/.github/workflows/diophantine-research.yml index 60f0700..81274cf 100644 --- a/.github/workflows/diophantine-research.yml +++ b/.github/workflows/diophantine-research.yml @@ -13,6 +13,10 @@ on: - '.github/workflows/diophantine-research.yml' workflow_dispatch: +concurrency: + group: exact-fixed-z-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: fixed-z: runs-on: ubuntu-latest @@ -21,10 +25,6 @@ jobs: fail-fast: false matrix: include: - - {start: -200, end: -151} - - {start: -150, end: -101} - - {start: -100, end: -51} - - {start: -50, end: -1} - {start: 1, end: 50} - {start: 51, end: 100} - {start: 101, end: 150} From 92e1b27f943489a4749065307eb8d723eacedc78 Mon Sep 17 00:00:00 2001 From: teatimedev Date: Tue, 28 Jul 2026 18:21:53 +0100 Subject: [PATCH 14/14] Fix Sage rational constructors and rerun exact E2 arithmetic --- research/diophantine_search.sage | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/research/diophantine_search.sage b/research/diophantine_search.sage index 515ee74..3187121 100644 --- a/research/diophantine_search.sage +++ b/research/diophantine_search.sage @@ -34,7 +34,7 @@ def map_e2(P): if u.denominator()!=1: return None u=ZZ(u); y=u+8; x=3*u-7 r=(380-U)/148 - w=74+QQ(71,37)*u+r*u*u + w=74+(QQ(71)/37)*u+r*u*u if w.denominator()!=1: return None w=ZZ(w) if (w-y*y)%2: return None @@ -71,7 +71,7 @@ def lattice(E, gens, bound, mapper, label): def conics(): E=EllipticCurve([0,0,0,0,-2]); P=E(3,5) - emit('mordell',rank=int(E.rank(proof=True)),gens=[str(Q) for Q in E.gens(proof=True)]) + emit('mordell',rank=int(E.rank()),gens=[str(Q) for Q in E.gens()]) for n in range(1,31): Q=n*P; k,v=QQ(Q[0]),QQ(Q[1]); w=abs(v); C=-k**6+40*k**3+32 Cn=Conic(QQ,[3*k*k*w*w,-4*w**3,-C]); t=time.time() @@ -82,8 +82,8 @@ def conics(): emit('conic_error',multiple=n,error=repr(ex),seconds=time.time()-t) def e1(): - E=EllipticCurve([0,0,1,-1,6]); t=time.time(); gens=E.gens(proof=True) - emit('curve',label='E1',rank=int(E.rank(proof=True)),gens=[str(P) for P in gens],torsion=[str(P) for P in E.torsion_points()],seconds=time.time()-t) + E=EllipticCurve([0,0,1,-1,6]); t=time.time(); gens=E.gens() + emit('curve',label='E1',rank=int(E.rank()),gens=[str(P) for P in gens],torsion=[str(P) for P in E.torsion_points()],seconds=time.time()-t) try: pts=E.integral_points(both_signs=True) emit('integral_points',label='E1',count=len(pts),points=[str(P) for P in pts]) @@ -95,9 +95,9 @@ def e1(): def e2(): E=EllipticCurve([0,0,0,-476688,133008912]); t=time.time() - known=[E(132,8508),E(QQ(23796,121),QQ(9112140,1331)),E(QQ(18369,49),QQ(-906975,343)),E(276,-4740),E(QQ(311724,1369),QQ(-305064180,50653))] + known=[E(132,8508),E(QQ(23796)/121,QQ(9112140)/1331),E(QQ(18369)/49,QQ(-906975)/343),E(276,-4740),E(QQ(311724)/1369,QQ(-305064180)/50653)] try: - gens=E.gens(proof=True); rank=E.rank(proof=True) + gens=E.gens(); rank=E.rank() emit('curve',label='E2',rank=int(rank),gens=[str(P) for P in gens],torsion=[str(P) for P in E.torsion_points()],seconds=time.time()-t) except Exception as ex: emit('curve_error',label='E2',error=repr(ex),seconds=time.time()-t); gens=known