Skip to main content

Rust + PyO3 implementation of the Izzo Lambert solver

Project description

lambert_rs

High-performance Rust + PyO3 implementation of the Izzo Lambert solver with advanced optimization capabilities.

Installation

From PyPI

pip install lambert-rs

Features

  • Fast Lambert Problem Solving: Vectorized batch processing with parallel execution
  • Multiple Solution Methods: Single solutions, grid search, and Nelder-Mead optimization
  • Delta-V Calculations: Automatic computation of transfer impulses
  • Two-Body Propagation: Universal variable formulation for orbit propagation
  • Transfer Optimization: Find optimal departure/arrival times minimizing delta-v
  • Rendezvous Optimization: Optimize for full rendezvous (two burns) or transfer only (one burn)

Quick Start

Basic Lambert Problem

Solve a single Lambert problem:

import numpy as np
import lambert_rs

# Initial and final positions (km)
r1 = np.array([7000., 0., 0.])
r2 = np.array([0., -8000., 0.])

# Time of flight (seconds)
tof = 3600.0

# Solve Lambert problem
v1_list, v2_list, valid = lambert_rs.lambert_izzo_single(
    r1, r2, tof,
    max_rev=0,        # Maximum number of revolutions
    retrograde=False, # Prograde transfer
    mu=398600.4418,  # Earth's gravitational parameter (km^3/s^2)
    tol=1e-8,        # Tolerance
    maxiter=50       # Maximum iterations
)

# Get the first valid solution
if valid[0]:
    v1 = v1_list[0]
    v2 = v2_list[0]
    print(f"v1: {v1} km/s")
    print(f"v2: {v2} km/s")

Batch Processing

Process multiple Lambert problems in parallel:

import numpy as np
import lambert_rs

# Multiple position pairs: shape (N, 3)
r1_batch = np.array([
    [7000., 0., 0.],
    [8000., 0., 0.],
    [9000., 0., 0.],
])

r2_batch = np.array([
    [0., -8000., 0.],
    [0., -9000., 0.],
    [0., -10000., 0.],
])

# Multiple time-of-flight values
tof_array = np.array([3600., 7200., 10800.])

# Solve all combinations
v1_all, v2_all, valid_all = lambert_rs.lambert_izzo_vec(
    r1_batch, r2_batch, tof_array,
    max_rev=0,
    retrograde=False,
    mu=398600.4418,
    tol=1e-8,
    maxiter=50
)

# Results shape: (N, num_tof, num_solutions, 3)
print(f"v1_all shape: {v1_all.shape}")
print(f"v2_all shape: {v2_all.shape}")

Delta-V Calculations

Calculate transfer impulses for rendezvous:

import numpy as np
import lambert_rs

# Initial and target states: [x, y, z, vx, vy, vz]
s1 = np.array([7000., 0., 0., 0., 7.5, 0.])  # Initial state
s2 = np.array([0., -8000., 0., 0., 8.0, 0.])  # Target state

# Time of flight
tof = 3600.0

# Calculate delta-v for all solutions
dv1_mag, dv2_mag, dv_total, valid, dv1_vec, dv2_vec = lambert_rs.lambert_izzo_vec_dv(
    s1, s2, tof,
    max_rev=0,
    retrograde=False,
    mu=398600.4418,
    tol=1e-8,
    maxiter=50
)

# Find best solution (minimum total delta-v)
if np.any(valid):
    best_idx = np.nanargmin(dv_total[valid])
    print(f"Best delta-v: {dv_total[best_idx]:.3f} km/s")
    print(f"First burn: {dv1_vec[best_idx]} km/s")
    print(f"Second burn: {dv2_vec[best_idx]} km/s")

Find Best Delta-V Solution

Automatically find the best solution (prograde or retrograde):

import numpy as np
import lambert_rs

# States: shape (..., 6) for batch processing
s1 = np.array([7000., 0., 0., 0., 7.5, 0.])
s2 = np.array([0., -8000., 0., 0., 8.0, 0.])

# Time of flight (can be scalar or array)
tof = np.array([3600., 7200., 10800.])

dv1_mag, dv2_mag, dv_total, valid, dv1_vec, dv2_vec = lambert_rs.lambert_izzo_best_dv(
    s1, s2, tof,
    max_rev=0,
    mu=398600.4418,
    tol=1e-8,
    maxiter=50
)

# Automatically selects best solution (prograde or retrograde)
print(f"Best total delta-v: {dv_total} km/s")

Two-Body Orbit Propagation

Propagate an orbit using universal variable formulation:

import numpy as np
import lambert_rs

# Initial state: [x, y, z, vx, vy, vz] (km, km/s)
initial_state = np.array([7000., 0., 0., 0., 7.5, 0.])

# Propagation time (seconds)
dt = 3600.0

# Propagate
r_final, v_final = lambert_rs.kepler_universal_2body_py(initial_state, dt)

print(f"Final position: {r_final} km")
print(f"Final velocity: {v_final} km/s")

Transfer Optimization (Grid Search)

Find optimal departure and arrival times using grid search:

import numpy as np
import lambert_rs

# Source and target initial states
source_state = np.array([7000., 0., 0., 0., 7.5, 0.])
target_state = np.array([0., -8000., 0., 0., 8.0, 0.])

# Time bounds: (min_departure, max_arrival) in seconds
time_bounds = (0., 86400.)  # 24 hours
dt_step = 3600.0  # 1 hour steps

# Find optimal transfer
best_t0, best_tf, best_dv = lambert_rs.optimize_lambert_transfer_py(
    source_state, target_state,
    time_bounds, dt_step,
    mu=398600.4418,
    retrograde=False
)

print(f"Optimal departure time: {best_t0:.1f} s")
print(f"Optimal arrival time: {best_tf:.1f} s")
print(f"Minimum delta-v: {best_dv:.3f} km/s")

Transfer Grid (All Solutions)

Get all transfer solutions in a time window:

import numpy as np
import lambert_rs

source_state = np.array([7000., 0., 0., 0., 7.5, 0.])
target_state = np.array([0., -8000., 0., 0., 8.0, 0.])

time_bounds = (0., 86400.)
dt_step = 3600.0

# Get all solutions: shape (N, 3) where columns are [t0, tf, delta_v]
grid = lambert_rs.optimize_lambert_transfer_grid_py(
    source_state, target_state,
    time_bounds, dt_step,
    mu=398600.4418,
    retrograde=False
)

# Plot or analyze all solutions
import matplotlib.pyplot as plt
plt.scatter(grid[:, 0], grid[:, 1], c=grid[:, 2], cmap='viridis')
plt.colorbar(label='Delta-V (km/s)')
plt.xlabel('Departure Time (s)')
plt.ylabel('Arrival Time (s)')
plt.show()

Nelder-Mead Optimization

Use gradient-free optimization to find optimal transfer:

import numpy as np
import lambert_rs

source_state = np.array([7000., 0., 0., 0., 7.5, 0.])
target_state = np.array([0., -8000., 0., 0., 8.0, 0.])

time_bounds = (0., 86400.)

# Optimize for transfer (single burn)
times, cost, dv1, dv2 = lambert_rs.optimize_lambert_nm_py(
    source_state, target_state,
    time_bounds,
    mu=398600.4418,
    retrograde=False,
    max_rev=0,
    optimize_rendezvous=False,  # Transfer only (one burn)
    max_iters=1000,
    initial_guess=None  # Optional: (t0, tf) guess
)

print(f"Optimal times: t0={times[0]:.1f} s, tf={times[1]:.1f} s")
print(f"Cost (delta-v): {cost[0]:.3f} km/s")
print(f"First burn: {dv1} km/s")

# Optimize for rendezvous (two burns)
times, cost, dv1, dv2 = lambert_rs.optimize_lambert_nm_py(
    source_state, target_state,
    time_bounds,
    mu=398600.4418,
    retrograde=False,
    max_rev=0,
    optimize_rendezvous=True,  # Full rendezvous (two burns)
    max_iters=1000
)

print(f"Rendezvous cost: {cost[0]:.3f} km/s")
print(f"First burn: {dv1} km/s")
print(f"Second burn: {dv2} km/s")

Multi-Start Optimization

Find multiple local optima using parallel multi-start optimization:

import numpy as np
import lambert_rs

source_state = np.array([7000., 0., 0., 0., 7.5, 0.])
target_state = np.array([0., -8000., 0., 0., 8.0, 0.])

time_bounds = (0., 86400.)

# Run multiple optimizers in parallel
times, costs, dv1_all, dv2_all = lambert_rs.optimize_lambert_nm_multi_py(
    source_state, target_state,
    time_bounds,
    mu=398600.4418,
    retrograde=False,
    max_rev=0,
    optimize_rendezvous=True,
    num_solvers=10,        # Number of parallel optimizers
    return_best_only=False, # Return all solutions
    remove_duplicates=True, # Remove duplicate solutions
    max_iters=1000
)

# Analyze all solutions
print(f"Found {len(costs)} solutions")
print(f"Best cost: {np.min(costs):.3f} km/s")
print(f"Worst cost: {np.max(costs):.3f} km/s")

# Get the best solution
best_idx = np.argmin(costs)
print(f"\nBest solution:")
print(f"  Times: t0={times[best_idx, 0]:.1f} s, tf={times[best_idx, 1]:.1f} s")
print(f"  Cost: {costs[best_idx]:.3f} km/s")
print(f"  First burn: {dv1_all[best_idx]} km/s")
print(f"  Second burn: {dv2_all[best_idx]} km/s")

API Reference

Core Functions

  • lambert_izzo_single(r1, r2, tof, max_rev, retrograde, mu, tol, maxiter)

    • Solve a single Lambert problem
    • Returns: (v1_list, v2_list, valid)
  • lambert_izzo_vec(r1, r2, tof, max_rev, retrograde, mu, tol, maxiter)

    • Vectorized batch processing
    • Returns: (v1_all, v2_all, valid_all)
  • lambert_izzo_vec_dv(s1, s2, tof, max_rev, retrograde, mu, tol, maxiter)

    • Calculate delta-v for transfers
    • Returns: (dv1_mag, dv2_mag, dv_total, valid, dv1_vec, dv2_vec)
  • lambert_izzo_best_dv(s1, s2, tof, max_rev, mu, tol, maxiter)

    • Automatically find best solution (prograde or retrograde)
    • Returns: (dv1_mag, dv2_mag, dv_total, valid, dv1_vec, dv2_vec)

Propagation

  • kepler_universal_2body_py(init_state_vec, dt_sec)
    • Two-body orbit propagation using universal variables
    • Returns: (r_final, v_final)

Optimization

  • optimize_lambert_transfer_py(source_state, target_state, time_bounds, dt_step, mu, retrograde)

    • Grid search for optimal transfer
    • Returns: (best_t0, best_tf, best_dv)
  • optimize_lambert_transfer_grid_py(source_state, target_state, time_bounds, dt_step, mu, retrograde)

    • Get all grid search solutions
    • Returns: grid array with columns [t0, tf, delta_v]
  • optimize_lambert_nm_py(source_state, target_state, time_bounds, mu, retrograde, max_rev, optimize_rendezvous, max_iters, initial_guess)

    • Nelder-Mead optimization
    • Returns: (times, cost, dv1, dv2)
  • optimize_lambert_nm_multi_py(source_state, target_state, time_bounds, mu, retrograde, max_rev, optimize_rendezvous, num_solvers, return_best_only, remove_duplicates, max_iters)

    • Multi-start parallel optimization
    • Returns: (times, costs, dv1_all, dv2_all)

Performance

  • Parallel Processing: Automatically uses N-1 CPU cores for batch operations
  • Vectorized Operations: Efficient NumPy array operations
  • Rust Performance: Core algorithms implemented in Rust for maximum speed

Units

  • Positions: kilometers (km)
  • Velocities: kilometers per second (km/s)
  • Time: seconds (s)
  • Gravitational Parameter (mu): km³/s²
    • Earth: 398600.4418 km³/s²

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

lambert_rs-0.0.3-cp38-abi3-win_amd64.whl (320.4 kB view details)

Uploaded CPython 3.8+Windows x86-64

lambert_rs-0.0.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (451.5 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

lambert_rs-0.0.3-cp38-abi3-macosx_11_0_arm64.whl (437.8 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

lambert_rs-0.0.3-cp38-abi3-macosx_10_12_x86_64.whl (449.6 kB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file lambert_rs-0.0.3-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: lambert_rs-0.0.3-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 320.4 kB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for lambert_rs-0.0.3-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 508f5dd3e7f4b57fc67848f54737532bcd91cee10aa66d88fb22d8a0741bf87a
MD5 749395b4a79de1e2711ce6b5527de439
BLAKE2b-256 2edfee4b04ed0cc6c3ec0b7a9af9295e74ad073bfb38f28dc9507d5c29cd1993

See more details on using hashes here.

Provenance

The following attestation bundles were made for lambert_rs-0.0.3-cp38-abi3-win_amd64.whl:

Publisher: publish.yml on rengrub/lambert_rust

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lambert_rs-0.0.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for lambert_rs-0.0.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 608178337b530ad64c6ea6e0da0efaab4408bd40653e24d3b42e42b81cb20471
MD5 0d0d9e188d13ac0302cf76c2d419e5dc
BLAKE2b-256 6187ac5bf94058f2ff17d2a6a6bece9cc712f3afcfa143f83ccbe3f55415f2ff

See more details on using hashes here.

Provenance

The following attestation bundles were made for lambert_rs-0.0.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: publish.yml on rengrub/lambert_rust

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lambert_rs-0.0.3-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for lambert_rs-0.0.3-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 3800f4a14c15bd9e2d8395bcef4a3267f431a5a1f5cf4c4640d672a75a2909d5
MD5 cf4281fd6384e88b5350f62174a2be0f
BLAKE2b-256 b475a03fefc0236efc6754c3fda3b11395319ed12c824d70b271e4cce9b630cc

See more details on using hashes here.

Provenance

The following attestation bundles were made for lambert_rs-0.0.3-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: publish.yml on rengrub/lambert_rust

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lambert_rs-0.0.3-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for lambert_rs-0.0.3-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 c155278dbc4aa2f0e86f53a789330d6d9918cbe5aa7a3e4a6de484f729a423ad
MD5 4793cf615ea6ab1d06498fbaad258778
BLAKE2b-256 d51137c47934ccd9ecc1dfd2cfc8c97f8353650f0127d54d6d944ee466de256f

See more details on using hashes here.

Provenance

The following attestation bundles were made for lambert_rs-0.0.3-cp38-abi3-macosx_10_12_x86_64.whl:

Publisher: publish.yml on rengrub/lambert_rust

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page