Laurent Series Fitting

The Laurent module implements Stage 3 of the analytic continuation pipeline: fitting a Laurent series map z(zeta) such that the unit circle maps to approximate a Jordan curve.

Mathematical Background

A Laurent series centered at the origin has the form:

\[z(\zeta) = a_0 + \sum_{k=1}^{N} a_k \zeta^k + \sum_{k=1}^{N} b_k \zeta^{-k}\]

When evaluated on the unit circle (|zeta| = 1), this maps to a curve in the complex plane. The fitting process finds coefficients that best approximate a given Jordan curve.

Configuration

class analytic_continuation.LaurentFitConfig[source]

Bases: object

Configuration for Laurent map fitting.

Parameters:
  • N_min (int)

  • N_max (int)

  • m_samples (int)

  • lambda_init (float)

  • lambda_increase_factor (float)

  • max_lambda_tries (int)

  • reparam_iters (int)

  • theta_search_grid (int)

  • theta_refine_iters (int)

  • fit_tol_max_factor (float)

  • prefer_smallest_N (bool)

  • rho_in (float)

  • rho_out (float)

  • check_theta_grid (int)

  • min_abs_deriv_factor (float)

  • min_sep_factor (float)

  • poly_eps_factor (float)

N_min: int = 6
N_max: int = 64
m_samples: int = 2048
lambda_init: float = 1e-06
lambda_increase_factor: float = 10.0
max_lambda_tries: int = 6
reparam_iters: int = 2
theta_search_grid: int = 2048
theta_refine_iters: int = 3
fit_tol_max_factor: float = 0.002
prefer_smallest_N: bool = True
rho_in: float = 0.97
rho_out: float = 1.03
check_theta_grid: int = 4096
min_abs_deriv_factor: float = 1e-08
min_sep_factor: float = 1e-05
poly_eps_factor: float = 0.0005
classmethod from_pipeline_config(cfg)[source]

Create from pipeline_config.json structure.

Parameters:

cfg (dict)

Return type:

LaurentFitConfig

__init__(N_min=6, N_max=64, m_samples=2048, lambda_init=1e-06, lambda_increase_factor=10.0, max_lambda_tries=6, reparam_iters=2, theta_search_grid=2048, theta_refine_iters=3, fit_tol_max_factor=0.002, prefer_smallest_N=True, rho_in=0.97, rho_out=1.03, check_theta_grid=4096, min_abs_deriv_factor=1e-08, min_sep_factor=1e-05, poly_eps_factor=0.0005)
Parameters:
  • N_min (int)

  • N_max (int)

  • m_samples (int)

  • lambda_init (float)

  • lambda_increase_factor (float)

  • max_lambda_tries (int)

  • reparam_iters (int)

  • theta_search_grid (int)

  • theta_refine_iters (int)

  • fit_tol_max_factor (float)

  • prefer_smallest_N (bool)

  • rho_in (float)

  • rho_out (float)

  • check_theta_grid (int)

  • min_abs_deriv_factor (float)

  • min_sep_factor (float)

  • poly_eps_factor (float)

Return type:

None

Key configuration parameters:

  • N_min, N_max: Range of Laurent series degrees to try

  • m_samples: Number of sample points for fitting

  • lambda_init: Initial Tikhonov regularization parameter

  • reparam_iters: Number of reparameterization iterations

  • fit_tol_max_factor: Maximum error tolerance as fraction of curve diameter

Results

class analytic_continuation.LaurentMapResult[source]

Bases: object

Result of Laurent map evaluation.

Parameters:
N: int
a0: complex
a: ndarray
b: ndarray
eval(zeta)[source]

Evaluate z(ζ) = a0 + Σ a_k ζ^k + Σ b_k ζ^{-k}.

Parameters:

zeta (complex)

Return type:

complex

eval_array(zeta)[source]

Vectorized evaluation.

Parameters:

zeta (ndarray)

Return type:

ndarray

deriv(zeta)[source]

Evaluate z’(ζ) = Σ k a_k ζ^{k-1} - Σ k b_k ζ^{-k-1}.

Parameters:

zeta (complex)

Return type:

complex

deriv_array(zeta)[source]

Vectorized derivative.

Parameters:

zeta (ndarray)

Return type:

ndarray

to_laurent_map()[source]

Convert to serializable LaurentMap type.

Return type:

LaurentMap

classmethod from_laurent_map(lm)[source]

Create from serializable LaurentMap type.

Parameters:

lm (LaurentMap)

Return type:

LaurentMapResult

__init__(N, a0, a, b)
Parameters:
Return type:

None

The result object provides methods for evaluating the Laurent map:

import numpy as np

# Evaluate at a single point
z = result.eval(np.exp(1j * 0.5))

# Evaluate at multiple points (vectorized)
thetas = np.linspace(0, 2 * np.pi, 100)
zetas = np.exp(1j * thetas)
curve = result.eval_array(zetas)

# Compute derivative
dz = result.deriv(np.exp(1j * 0.5))
dz_array = result.deriv_array(zetas)
class analytic_continuation.FitResult[source]

Bases: object

Result of fitting a Laurent map.

Parameters:
ok: bool
failure_reason: str | None = None
curve_scale: float = 0.0
polyline_used: List[complex]
laurent_map: LaurentMapResult | None = None
fit_max_err: float = inf
fit_rms_err: float = inf
simple_on_unit_circle: bool = False
min_abs_deriv_unit: float = 0.0
min_sep_unit: float = 0.0
min_sep_in: float = 0.0
min_sep_out: float = 0.0
__init__(ok, failure_reason=None, curve_scale=0.0, polyline_used=<factory>, laurent_map=None, fit_max_err=inf, fit_rms_err=inf, simple_on_unit_circle=False, min_abs_deriv_unit=0.0, min_sep_unit=0.0, min_sep_in=0.0, min_sep_out=0.0)
Parameters:
Return type:

None

Main Fitting Function

analytic_continuation.fit_laurent_map(export, cfg=None)[source]

Fit a Laurent map to a SplineExport curve.

This is the main entry point for Stage 3.

Parameters:
Returns:

The fitting result including the Laurent map if successful

Return type:

FitResult

Basic Usage

from analytic_continuation import fit_laurent_map, LaurentFitConfig, SplineExport

# Load curve data
with open("curve.json") as f:
    export = SplineExport.from_json(f.read())

# Fit with default configuration
result = fit_laurent_map(export)

if result.ok:
    print(f"Fitted with N={result.laurent_map.N}")
    print(f"Max error: {result.fit_max_err}")
    print(f"RMS error: {result.fit_rms_err}")
else:
    print(f"Fitting failed: {result.failure_reason}")

Custom Configuration

config = LaurentFitConfig(
    N_min=8,
    N_max=48,
    m_samples=4096,
    reparam_iters=3,
    fit_tol_max_factor=0.001,  # Tighter tolerance
)

result = fit_laurent_map(export, config)

Utility Functions

analytic_continuation.load_polyline_from_export(export, field_name='adaptivePolyline', drop_duplicate_terminal=True)[source]

Load polyline from SplineExport, converting to complex.

Parameters:
  • export (SplineExport) – The spline export data

  • field_name (str) – Which field to use: ‘adaptivePolyline’, ‘spline’, or ‘controlPoints’

  • drop_duplicate_terminal (bool) – If True, remove trailing points that duplicate the first point

Returns:

The polyline as complex numbers

Return type:

List[complex]

analytic_continuation.estimate_diameter(poly)[source]

Estimate curve diameter as max pairwise distance.

Parameters:

poly (List[complex])

Return type:

float

Quality Checks

The fitting process performs several quality checks:

  1. Simplicity: The mapped curve should not self-intersect

  2. Non-zero derivative: |z'(zeta)| should be bounded away from zero on the unit circle

  3. Separation: Points on the mapped curve should be well-separated

These checks are performed on the unit circle and on inner/outer circles (rho_in, rho_out) to ensure the map is well-behaved on an annulus.