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.])
# Full rendezvous: minimize total delta-v (dv1 + dv2)
# Use this when you need to match both position AND velocity at the target
dv1_rend, dv2_rend, dv_total_rend, valid_rend, dv1_vec_rend, dv2_vec_rend = lambert_rs.lambert_izzo_best_dv(
s1, s2, tof,
max_rev=0,
mu=398600.4418,
tol=1e-8,
maxiter=50,
rendezvous=True # Default: minimize dv1 + dv2
)
# Transfer-only: minimize first impulse (dv1 only)
# Use this when you only need to reach the target position (not matching velocity)
dv1_trans, dv2_trans, dv_total_trans, valid_trans, dv1_vec_trans, dv2_vec_trans = lambert_rs.lambert_izzo_best_dv(
s1, s2, tof,
max_rev=0,
mu=398600.4418,
tol=1e-8,
maxiter=50,
rendezvous=False # Minimize dv1 only
)
# Automatically selects best solution (prograde or retrograde)
print(f"Rendezvous total delta-v: {dv_total_rend} km/s")
print(f"Transfer-only total delta-v: {dv1_trans} 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, rendezvous)- Automatically find best solution (prograde or retrograde)
rendezvous=True(default): Minimize total delta-v (dv1 + dv2) for full rendezvousrendezvous=False: Minimize first impulse (dv1) for transfer-only missions- 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:
gridarray 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lambert_rs-0.0.4-cp38-abi3-win_amd64.whl.
File metadata
- Download URL: lambert_rs-0.0.4-cp38-abi3-win_amd64.whl
- Upload date:
- Size: 320.6 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79718920c6b8d5d6b6be59d5ace506395a85b501c0d1c8232a6a33c9da1aea6b
|
|
| MD5 |
79a859d1d812fd4a35bf17055fdd85c2
|
|
| BLAKE2b-256 |
48648c381f6de696739833fc37f4c8d366c849bf58fa4ae4585993416fc22ee7
|
Provenance
The following attestation bundles were made for lambert_rs-0.0.4-cp38-abi3-win_amd64.whl:
Publisher:
publish.yml on rengrub/lambert_rust
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lambert_rs-0.0.4-cp38-abi3-win_amd64.whl -
Subject digest:
79718920c6b8d5d6b6be59d5ace506395a85b501c0d1c8232a6a33c9da1aea6b - Sigstore transparency entry: 770320875
- Sigstore integration time:
-
Permalink:
rengrub/lambert_rust@21c16d3a752e332772d0c083f121ffc9045e7448 -
Branch / Tag:
refs/tags/v0.0.4.dev12 - Owner: https://github.com/rengrub
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@21c16d3a752e332772d0c083f121ffc9045e7448 -
Trigger Event:
push
-
Statement type:
File details
Details for the file lambert_rs-0.0.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: lambert_rs-0.0.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 451.8 kB
- Tags: CPython 3.8+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c2fa784a6b009ab666f0122c2d621e207b2cf89293f2bafd8f247f967165784
|
|
| MD5 |
ee3ecd83aa4a1d94393bc86543d1f2a9
|
|
| BLAKE2b-256 |
671657a77c65cb43f234ede8aeb861264f52dfe5f63fe7e22129a38645676ac2
|
Provenance
The following attestation bundles were made for lambert_rs-0.0.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on rengrub/lambert_rust
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lambert_rs-0.0.4-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
9c2fa784a6b009ab666f0122c2d621e207b2cf89293f2bafd8f247f967165784 - Sigstore transparency entry: 770320958
- Sigstore integration time:
-
Permalink:
rengrub/lambert_rust@21c16d3a752e332772d0c083f121ffc9045e7448 -
Branch / Tag:
refs/tags/v0.0.4.dev12 - Owner: https://github.com/rengrub
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@21c16d3a752e332772d0c083f121ffc9045e7448 -
Trigger Event:
push
-
Statement type:
File details
Details for the file lambert_rs-0.0.4-cp38-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: lambert_rs-0.0.4-cp38-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 438.1 kB
- Tags: CPython 3.8+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
59d864adb94774af27e2beb8c2ae76942fe1fa028c1c9de2c52e88155d30b2fa
|
|
| MD5 |
525dca9a7df40af30e6526f6912ac884
|
|
| BLAKE2b-256 |
779d6de112f20778f477b47563edf25c9dde905237967c37da491726b20b78e0
|
Provenance
The following attestation bundles were made for lambert_rs-0.0.4-cp38-abi3-macosx_11_0_arm64.whl:
Publisher:
publish.yml on rengrub/lambert_rust
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lambert_rs-0.0.4-cp38-abi3-macosx_11_0_arm64.whl -
Subject digest:
59d864adb94774af27e2beb8c2ae76942fe1fa028c1c9de2c52e88155d30b2fa - Sigstore transparency entry: 770320929
- Sigstore integration time:
-
Permalink:
rengrub/lambert_rust@21c16d3a752e332772d0c083f121ffc9045e7448 -
Branch / Tag:
refs/tags/v0.0.4.dev12 - Owner: https://github.com/rengrub
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@21c16d3a752e332772d0c083f121ffc9045e7448 -
Trigger Event:
push
-
Statement type:
File details
Details for the file lambert_rs-0.0.4-cp38-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: lambert_rs-0.0.4-cp38-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 449.7 kB
- Tags: CPython 3.8+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91d29d17c82e4603aed82f1e6acdfbf891b9784ef3b5294c4c8bd0a9d9167604
|
|
| MD5 |
5be624fc167b98f4b938c4d2a5f50288
|
|
| BLAKE2b-256 |
4695de729faa1bc91eebfb55654659ef195e7795eb9f53457aa589a6fd420005
|
Provenance
The following attestation bundles were made for lambert_rs-0.0.4-cp38-abi3-macosx_10_12_x86_64.whl:
Publisher:
publish.yml on rengrub/lambert_rust
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
lambert_rs-0.0.4-cp38-abi3-macosx_10_12_x86_64.whl -
Subject digest:
91d29d17c82e4603aed82f1e6acdfbf891b9784ef3b5294c4c8bd0a9d9167604 - Sigstore transparency entry: 770320905
- Sigstore integration time:
-
Permalink:
rengrub/lambert_rust@21c16d3a752e332772d0c083f121ffc9045e7448 -
Branch / Tag:
refs/tags/v0.0.4.dev12 - Owner: https://github.com/rengrub
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@21c16d3a752e332772d0c083f121ffc9045e7448 -
Trigger Event:
push
-
Statement type: