| """ |
| EXPERIMENT 1c: Robust root finding for F(z) = sum z^n/n^n |
| Using continuation method from known roots. |
| """ |
| import numpy as np |
| from scipy.optimize import fsolve |
| import json |
|
|
| def F_vec(zv, N=150): |
| z = complex(zv[0], zv[1]) |
| result = 0.0 + 0.0j |
| for n in range(1, N+1): |
| try: |
| term = z**n / n**n |
| except (OverflowError, ZeroDivisionError): |
| break |
| if abs(term) > 1e100 or np.isnan(abs(term)) or np.isinf(abs(term)): |
| break |
| result += term |
| return [result.real, result.imag] |
|
|
| |
| known_roots = [ |
| (-4.305, 14.945), |
| (-5.386, 32.038), |
| (-5.996, 49.129), |
| (-6.421, 66.215), |
| ] |
|
|
| roots = [] |
| for kr in known_roots: |
| r = complex(kr[0], kr[1]) |
| val = complex(*F_vec([r.real, r.imag])) |
| if abs(val) < 1e-3: |
| |
| refined = fsolve(F_vec, [r.real, r.imag], full_output=False) |
| r = complex(refined[0], refined[1]) |
| roots.append(r) |
|
|
| |
| if len(roots) > 0: |
| last_root = roots[-1] |
| |
| if len(roots) >= 2: |
| im_step = roots[-1].imag - roots[-2].imag |
| re_step = roots[-1].real - roots[-2].real |
| else: |
| im_step = 17.08 |
| re_step = -0.4 |
| |
| |
| for k in range(30): |
| re_guess = last_root.real + re_step |
| im_guess = last_root.imag + im_step |
| try: |
| refined = fsolve(F_vec, [re_guess, im_guess], full_output=False) |
| r = complex(refined[0], refined[1]) |
| val = complex(*F_vec([r.real, r.imag])) |
| if abs(val) < 1e-3 and r.imag > 0 and r.real < 0: |
| |
| is_dup = any(abs(r - ex) < 1.0 for ex in roots) |
| if not is_dup: |
| roots.append(r) |
| last_root = r |
| |
| if len(roots) >= 2: |
| im_step = roots[-1].imag - roots[-2].imag |
| re_step = roots[-1].real - roots[-2].real |
| except: |
| pass |
|
|
| |
| if len(roots) > 0: |
| first_root = roots[0] |
| if len(roots) >= 2: |
| im_step = roots[1].imag - roots[0].imag |
| re_step = roots[1].real - roots[0].real |
| else: |
| im_step = 17.08 |
| re_step = 0.4 |
| |
| for k in range(5): |
| re_guess = first_root.real - re_step |
| im_guess = first_root.imag - im_step |
| if im_guess < 5: |
| break |
| try: |
| refined = fsolve(F_vec, [re_guess, im_guess], full_output=False) |
| r = complex(refined[0], refined[1]) |
| val = complex(*F_vec([r.real, r.imag])) |
| if abs(val) < 1e-3 and r.imag > 0 and r.real < 0: |
| is_dup = any(abs(r - ex) < 1.0 for ex in roots) |
| if not is_dup: |
| roots.insert(0, r) |
| first_root = r |
| except: |
| pass |
|
|
| roots.sort(key=lambda r: r.imag) |
| print(f"Found {len(roots)} roots\n") |
|
|
| if len(roots) >= 2: |
| im_spacings = [roots[i].imag - roots[i-1].imag for i in range(1, len(roots))] |
| re_shifts = [roots[i].real - roots[i-1].real for i in range(1, len(roots))] |
| |
| print(f"{'#':>3s} {'Re':>10s} {'Im':>10s} {'Im-gap':>10s} {'Re-shift':>10s}") |
| for i, r in enumerate(roots): |
| im_gap = f"{im_spacings[i-1]:10.4f}" if i > 0 else f"{'---':>10s}" |
| re_shift = f"{re_shifts[i-1]:10.4f}" if i > 0 else f"{'---':>10s}" |
| print(f"{i:3d} {r.real:10.4f} {r.imag:10.4f} {im_gap} {re_shift}") |
| |
| mean_im = np.mean(im_spacings) |
| std_im = np.std(im_spacings) |
| |
| print(f"\nIm-spacing: mean={mean_im:.6f}, std={std_im:.6f}") |
| print(f"2*pi*e: {2*np.pi*np.e:.6f}") |
| print(f"Ratio: {mean_im/(2*np.pi*np.e):.6f}") |
| print(f"Re-shift: mean={np.mean(re_shifts):.6f}") |
| |
| |
| results = { |
| 'roots_re': [r.real for r in roots], |
| 'roots_im': [r.imag for r in roots], |
| 'im_spacings': im_spacings, |
| 're_shifts': re_shifts, |
| 'two_pi_e': float(2*np.pi*np.e), |
| 'mean_im_spacing': float(mean_im), |
| 'std_im_spacing': float(std_im), |
| } |
| with open('/app/exp1_results.json', 'w') as f: |
| json.dump(results, f, indent=2) |
| print("\nSaved to /app/exp1_results.json") |
|
|