Intrinsic Curve Analysis
The intrinsic curve module provides tools for analyzing curves using their intrinsic representations: the Cesaro form (curvature vs arc length) and Whewell form (tangent angle vs arc length).
Mathematical Background
Intrinsic representations describe curves independent of their position and orientation:
Cesaro Form: Expresses curvature kappa as a function of arc length s:
Whewell Form: Expresses tangent angle phi as a function of arc length s:
These representations are related by:
Representations
- class analytic_continuation.CesaroRepresentation[source]
Bases:
objectCesàro intrinsic form: κ(s) - curvature as function of arc length.
- arc_lengths
Cumulative arc length values s[i] at sample points
- Type:
np.ndarray
- curvatures
Curvature κ(s[i]) at each sample point
- Type:
np.ndarray
- class analytic_continuation.WhewellRepresentation[source]
Bases:
objectWhewell intrinsic form: φ(s) - tangent angle as function of arc length.
The tangent angle φ(s) = ∫₀ˢ κ(t) dt + φ₀
- arc_lengths
Cumulative arc length values
- Type:
np.ndarray
- tangent_angles
Tangent angle φ(s[i]) at each sample point (radians)
- Type:
np.ndarray
- winding_number
φ(L) - φ(0) = 2π·n for winding number n (should be 2π for simple curves)
- Type:
- Parameters:
Computation Functions
- analytic_continuation.compute_cesaro_form(log_data)[source]
Convert log bijection data to Cesàro form κ(s).
where z’ = dz/dθ.
- Parameters:
log_data (LogBijectionData) – The log-transformed bijection data
- Returns:
The Cesàro (curvature) representation
- Return type:
- analytic_continuation.compute_whewell_form(cesaro, initial_angle=0.0)[source]
Convert Cesàro form to Whewell form φ(s) by integration.
φ(s) = φ₀ + ∫₀ˢ κ(t) dt
- Parameters:
cesaro (CesaroRepresentation) – The Cesàro (curvature) representation
initial_angle (float) – Initial tangent angle φ(0)
- Returns:
The Whewell (tangent angle) representation
- Return type:
Example:
from analytic_continuation import compute_cesaro_form, compute_whewell_form
import numpy as np
# Circle parameterized by angle
t = np.linspace(0, 2 * np.pi, 1000)
curve = np.exp(1j * t)
# Compute intrinsic forms
cesaro = compute_cesaro_form(curve)
whewell = compute_whewell_form(curve)
# For a circle, curvature should be constant
print(f"Curvature range: {cesaro.kappa.min():.3f} to {cesaro.kappa.max():.3f}")
Log Bijection Analysis
- class analytic_continuation.LogBijectionData[source]
Bases:
objectNatural log of the bijection z(ζ), storing both the original Laurent coefficients and derived intrinsic representations.
- Taking log linearizes the multiplicative structure:
log(z(ζ)) = log|z| + i·arg(z)
- The derivative relationship:
d/dζ[log(z(ζ))] = z’(ζ)/z(ζ)
- Parameters:
- __init__(laurent_N, laurent_a0, laurent_a, laurent_b, curve_scale, theta_samples, log_z_samples, z_samples, log_derivative_samples)
- analytic_continuation.compute_log_bijection(lmap, curve_scale, samples=4096)[source]
Compute the natural log of the bijection z(ζ) on the unit circle.
Uses continuous branch selection for log(z) to avoid discontinuities.
- Parameters:
lmap (LaurentMapResult) – The fitted Laurent map
curve_scale (float) – The diameter/scale of the curve
samples (int) – Number of samples on the unit circle
- Returns:
The log-transformed bijection data
- Return type:
Analyzes the logarithmic structure of a bijection between curves.
Complexity Estimation
- class analytic_continuation.ComplexityEstimates[source]
Bases:
objectComputational complexity estimates derived from intrinsic curve analysis.
These predict the relative difficulty of: - Inverting z(ζ) = z_query (finding ζ given z) - Evaluating the continuation at many points - Achieving a target accuracy
- Parameters:
total_curvature (
float)curvature_variation (
float)max_curvature (
float)mean_curvature (
float)curvature_std (
float)winding_number (
float)total_arc_length (
float)log_deriv_variation (
float)arg_deriv_variation (
float)min_jacobian (
float)max_jacobian (
float)jacobian_ratio (
float)inversion_difficulty (
float)sampling_density_factor (
float)newton_convergence_factor (
float)
- __init__(total_curvature, curvature_variation, max_curvature, mean_curvature, curvature_std, winding_number, total_arc_length, log_deriv_variation, arg_deriv_variation, min_jacobian, max_jacobian, jacobian_ratio, inversion_difficulty, sampling_density_factor, newton_convergence_factor)
- Parameters:
total_curvature (
float)curvature_variation (
float)max_curvature (
float)mean_curvature (
float)curvature_std (
float)winding_number (
float)total_arc_length (
float)log_deriv_variation (
float)arg_deriv_variation (
float)min_jacobian (
float)max_jacobian (
float)jacobian_ratio (
float)inversion_difficulty (
float)sampling_density_factor (
float)newton_convergence_factor (
float)
- Return type:
None
- analytic_continuation.estimate_complexity(log_data, cesaro, whewell)[source]
Compute complexity estimates from intrinsic curve representations.
The estimates predict computational costs for: - Newton iteration for inversion - Sampling density requirements - Overall pipeline complexity
- Parameters:
log_data (LogBijectionData) – The log-transformed bijection
cesaro (CesaroRepresentation) – The Cesàro representation
whewell (WhewellRepresentation) – The Whewell representation
- Returns:
The complexity analysis results
- Return type:
Estimates the complexity of a curve for determining fitting parameters:
from analytic_continuation import estimate_complexity
estimates = estimate_complexity(curve_points)
print(f"Suggested N_min: {estimates.suggested_N_min}")
print(f"Suggested N_max: {estimates.suggested_N_max}")
print(f"Winding number: {estimates.winding_number}")
Inversion Configuration
- analytic_continuation.suggest_inversion_config(complexity, base_theta_grid=256, base_max_iters=40)[source]
Suggest inversion configuration based on complexity analysis.
- Parameters:
complexity (ComplexityEstimates) – The complexity analysis
base_theta_grid (int) – Base number of theta samples
base_max_iters (int) – Base max Newton iterations
- Returns:
Suggested InvertConfig parameters
- Return type:
Suggests configuration parameters based on curve analysis:
from analytic_continuation import suggest_inversion_config
config = suggest_inversion_config(curve_points)
print(f"Suggested tolerance: {config.tol}")
print(f"Suggested max iterations: {config.max_iter}")
Complete Analysis
- class analytic_continuation.IntrinsicCurveAnalysis[source]
Bases:
objectComplete intrinsic curve analysis of a bijection.
Bundles together: - Log bijection data - Cesàro representation - Whewell representation - Complexity estimates
- Parameters:
log_data (
LogBijectionData)cesaro (
CesaroRepresentation)whewell (
WhewellRepresentation)complexity (
ComplexityEstimates)
- log_data: LogBijectionData
- cesaro: CesaroRepresentation
- whewell: WhewellRepresentation
- complexity: ComplexityEstimates
- __init__(log_data, cesaro, whewell, complexity)
- Parameters:
log_data (
LogBijectionData)cesaro (
CesaroRepresentation)whewell (
WhewellRepresentation)complexity (
ComplexityEstimates)
- Return type:
None
- analytic_continuation.analyze_bijection(lmap, curve_scale, samples=4096)[source]
Perform complete intrinsic curve analysis of a Laurent bijection.
This is the main entry point for complexity estimation.
- Parameters:
lmap (LaurentMapResult) – The fitted Laurent map z(ζ)
curve_scale (float) – The diameter/scale of the curve
samples (int) – Number of samples for analysis
- Returns:
Complete analysis including Cesàro, Whewell, and complexity estimates
- Return type:
Example
>>> from analytic_continuation.laurent import fit_laurent_map >>> from analytic_continuation.intrinsic_curve import analyze_bijection >>> >>> # After fitting a Laurent map >>> result = fit_laurent_map(spline_export) >>> if result.ok: ... analysis = analyze_bijection(result.laurent_map, result.curve_scale) ... print(analysis.summary()) ... ... # Use complexity estimates to tune parameters ... if analysis.complexity.inversion_difficulty > 2.0: ... # Increase Newton iterations ... pass
Performs complete intrinsic curve analysis:
from analytic_continuation import analyze_bijection
import numpy as np
# Source and target curves
source = np.exp(1j * np.linspace(0, 2 * np.pi, 1000))
target = 2 * np.exp(1j * np.linspace(0, 2 * np.pi, 1000))
analysis = analyze_bijection(source, target)
print(f"Source total arc length: {analysis.source_cesaro.total_arc_length}")
print(f"Target total arc length: {analysis.target_cesaro.total_arc_length}")
Contour Pre-check (Stage 1)
- class analytic_continuation.ContourPreCheckResult[source]
Bases:
objectResult of quick pre-check on a raw user-drawn contour.
This is a fast “fail early” gate before expensive Laurent fitting.
- Parameters:
ok (
bool)proceed (
bool)is_closed (
bool)is_simple (
bool)has_sufficient_points (
bool)has_reasonable_aspect (
bool)has_reasonable_curvature (
bool)num_points (
int)perimeter (
float)aspect_ratio (
float)estimated_diameter (
float)min_segment_length (
float)max_segment_length (
float)max_turning_angle (
float)estimated_difficulty (
str)
- __init__(ok, proceed, is_closed, is_simple, has_sufficient_points, has_reasonable_aspect, has_reasonable_curvature, num_points, perimeter, bounding_box, aspect_ratio, estimated_diameter, min_segment_length, max_segment_length, max_turning_angle, warnings, errors, estimated_difficulty, estimated_fit_time_seconds=None)
- Parameters:
ok (
bool)proceed (
bool)is_closed (
bool)is_simple (
bool)has_sufficient_points (
bool)has_reasonable_aspect (
bool)has_reasonable_curvature (
bool)num_points (
int)perimeter (
float)aspect_ratio (
float)estimated_diameter (
float)min_segment_length (
float)max_segment_length (
float)max_turning_angle (
float)estimated_difficulty (
str)
- Return type:
None
- analytic_continuation.precheck_contour(points, closed=True, min_points=8, max_aspect_ratio=20.0, max_turning_angle_deg=170.0, min_segment_ratio=0.001)[source]
Quick pre-check on a raw user-drawn contour before Laurent fitting.
This is a fast “fail early” gate at Stage 1 to catch obviously bad contours before wasting time on expensive computations.
- Parameters:
points (List[Tuple[float, float]]) – The contour points (x, y) from user input or adaptive polyline
closed (bool) – Whether the contour should be treated as closed
min_points (int) – Minimum number of points required
max_aspect_ratio (float) – Maximum allowed aspect ratio (rejects very thin curves)
max_turning_angle_deg (float) – Maximum turning angle in degrees (rejects near-cusps)
min_segment_ratio (float) – Minimum segment length as fraction of perimeter
- Returns:
Pre-check results with pass/fail and diagnostics
- Return type:
Quick validation before proceeding with full analysis:
from analytic_continuation import precheck_contour
result = precheck_contour(curve_points)
if result.ok:
print("Contour is suitable for analysis")
else:
print(f"Pre-check failed: {result.failure_reason}")
print(f"Winding number: {result.winding_number}")
- analytic_continuation.precheck_contour_from_spline_export(control_points, adaptive_polyline=None, closed=True)[source]
Pre-check a contour from SplineExport data.
Uses adaptive polyline if available (more accurate), otherwise control points.
Convenience function for SplineExport data:
from analytic_continuation import precheck_contour_from_spline_export, SplineExport
export = SplineExport.from_json(json_data)
result = precheck_contour_from_spline_export(export)
Workflow Integration
The intrinsic curve analysis is typically used in the early stages of the pipeline:
from analytic_continuation import (
precheck_contour,
estimate_complexity,
suggest_inversion_config,
LaurentFitConfig,
fit_laurent_map,
)
# Stage 1: Quick pre-check
precheck = precheck_contour(curve_points)
if not precheck.ok:
raise ValueError(f"Contour failed pre-check: {precheck.failure_reason}")
# Stage 2: Analyze complexity
complexity = estimate_complexity(curve_points)
# Configure fitting based on analysis
fit_config = LaurentFitConfig(
N_min=complexity.suggested_N_min,
N_max=complexity.suggested_N_max,
)
# Stage 3: Fit Laurent map
result = fit_laurent_map(export, fit_config)