Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ lon = compute_lon(
lower_bound=-5.12,
upper_bound=5.12,
n_runs=20,
n_iterations=500,
max_perturbations_without_improvement=500,
seed=42
)

Expand Down Expand Up @@ -81,12 +81,13 @@ cmlon_metrics = cmlon.compute_metrics()
from lonpy import BasinHoppingSampler, BasinHoppingSamplerConfig

config = BasinHoppingSamplerConfig(
n_runs=50, # Number of independent runs
n_iterations=1000, # Iterations per run
step_size=0.05, # Perturbation size
step_mode="per", # "per" (percentage) or "fix" (fixed)
hash_digits=4, # Precision for identifying optima
seed=42 # For reproducibility
n_runs=50, # Number of independent runs
max_perturbations_without_improvement=1000, # Stop after this many consecutive non-improving perturbations
step_size=0.05, # Perturbation size
step_mode="percentage", # "percentage" or "fixed"
coordinate_precision=4, # Precision for identifying optima
fitness_precision=None, # Precision for fitness values (None = full double)
seed=42 # For reproducibility
)

sampler = BasinHoppingSampler(config)
Expand Down
14 changes: 1 addition & 13 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Data structures for Local Optima Networks.

- [`LON`](lon.md#lonpy.lon.LON) - Local Optima Network representation
- [`CMLON`](lon.md#lonpy.lon.CMLON) - Compressed Monotonic LON
- [`LONConfig`](lon.md#lonpy.lon.LONConfig) - Configuration for LON construction

### [Sampling Module](sampling.md)

Expand Down Expand Up @@ -80,16 +81,3 @@ viz.create_rotation_gif(lon, output_path="lon.gif")
# All visualizations
viz.visualize_all(lon, output_folder="./output")
```

## Dependencies

lonpy depends on:

- `numpy` - Numerical computations
- `scipy` - Optimization
- `pandas` - Data handling
- `igraph` - Graph operations
- `matplotlib` - 2D plotting
- `plotly` - 3D plotting
- `imageio` - GIF creation
- `kaleido` - Static image export
11 changes: 10 additions & 1 deletion docs/api/lon.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
- vertex_fitness
- vertex_count
- get_sinks
- get_global_optima_indices
- compute_metrics
- to_cmlon

Expand All @@ -30,3 +29,13 @@
- get_global_sinks
- get_local_sinks
- compute_metrics

::: lonpy.lon.LONConfig
options:
show_root_heading: true
show_source: true
members:
- fitness_aggregation
- warn_on_duplicates
- max_fitness_deviation
- eq_atol
11 changes: 0 additions & 11 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,6 @@ mkdocs build
6. Push to your fork
7. Open a Pull Request

### Commit Messages

Use clear, descriptive commit messages:

```
feat: add support for custom perturbation functions
fix: correct edge weight calculation in CMLON
docs: update visualization examples
test: add tests for edge cases in sampling
```

## Reporting Issues

When reporting bugs, please include:
Expand Down
20 changes: 13 additions & 7 deletions docs/getting-started/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,16 @@ lonpy constructs LONs by:

### Node Identification

Two solutions are considered the same local optimum if their coordinates match after rounding to `hash_digits` decimal places:
Two solutions are considered the same local optimum if their coordinates match after rounding to `coordinate_precision` decimal places:

```python
# With hash_digits=4:
# x1 = [1.23456, 2.34567] → "1.2346_2.3457"
# x2 = [1.23458, 2.34569] → "1.2346_2.3457"
# With coordinate_precision=5 (default):
# x1 = [1.234561, 2.345671] → "1.23456_2.34567"
# x2 = [1.234564, 2.345674] → "1.23456_2.34567"
# Same node!

# With coordinate_precision=None (full double precision):
# No rounding — only exact matches are the same node
```

## LON Metrics
Expand All @@ -86,20 +89,23 @@ lonpy computes several metrics to characterize fitness landscapes:
| `n_funnels` | Number of sinks (no outgoing edges) | Distinct attraction basins |
| `n_global_funnels` | Sinks at global optimum | How many paths lead to success |
| `neutral` | Proportion of equal-fitness connections | Landscape flatness |
| `strength` | Incoming flow to global optima | Global optimum accessibility |
| `global_strength` | Incoming flow to global optima relative to all nodes | Global optimum accessibility |
| `sink_strength` | Incoming flow to global sinks relative to all sinks | Global sink dominance |
| `success` | Proportion of runs reaching global optimum | Search algorithm effectiveness |
| `deviation` | Mean absolute deviation from global optimum | Solution quality across runs |

### Interpreting Metrics

**Easy landscape** (single funnel):

- Few funnels (ideally 1)
- High strength (most flow reaches global)
- High global_strength and sink_strength (most flow reaches global)
- All paths converge to global optimum

**Hard landscape** (multiple funnels):

- Many funnels competing for flow
- Low strength (flow diverted to local sinks)
- Low global_strength and sink_strength (flow diverted to local sinks)
- Search easily gets trapped

## CMLON (Compressed Monotonic LON)
Expand Down
15 changes: 0 additions & 15 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,6 @@ cd lonpy
pip install -e .
```

## Dependencies

lonpy automatically installs the following dependencies:

| Package | Version | Purpose |
|---------|---------|---------|
| numpy | >= 1.24.0 | Numerical computations |
| scipy | >= 1.10.0 | Optimization algorithms |
| pandas | >= 2.0.0 | Data manipulation |
| igraph | >= 0.11.0 | Graph operations |
| matplotlib | >= 3.7.0 | 2D plotting |
| plotly | >= 5.15.0 | Interactive 3D plots |
| kaleido | >= 0.2.1 | Static image export |
| imageio | >= 2.31.0 | GIF creation |

## Development Installation

To install lonpy with development dependencies for contributing:
Expand Down
13 changes: 8 additions & 5 deletions docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ lon = compute_lon(
lower_bound=-5.12, # Search space lower bound
upper_bound=5.12, # Search space upper bound
n_runs=20, # Number of Basin-Hopping runs
n_iterations=500, # Iterations per run
max_perturbations_without_improvement=500, # Stop after this many consecutive non-improving perturbations
seed=42 # For reproducibility
)

Expand All @@ -43,7 +43,8 @@ print(f"Number of optima: {metrics['n_optima']}")
print(f"Number of funnels: {metrics['n_funnels']}")
print(f"Global funnels: {metrics['n_global_funnels']}")
print(f"Neutrality: {metrics['neutral']:.1%}")
print(f"Strength to global: {metrics['strength']:.1%}")
print(f"Global strength: {metrics['global_strength']:.1%}")
print(f"Sink strength: {metrics['sink_strength']:.1%}")
```

**What do these metrics mean?**
Expand All @@ -54,7 +55,10 @@ print(f"Strength to global: {metrics['strength']:.1%}")
| `n_funnels` | Number of sink nodes (basins of attraction) |
| `n_global_funnels` | Funnels leading to the global optimum |
| `neutral` | Proportion of nodes with equal-fitness neighbors |
| `strength` | Proportion of flow directed toward global optima |
| `global_strength` | Proportion of global optima incoming strength to total incoming strength |
| `sink_strength` | Proportion of global sinks incoming strength to all sinks incoming strength |
| `success` | Proportion of runs that reached the global optimum |
| `deviation` | Mean absolute deviation from the global optimum |

## Visualizing the Network

Expand Down Expand Up @@ -136,7 +140,7 @@ lon = compute_lon(
lower_bound=-5.12,
upper_bound=5.12,
n_runs=30,
n_iterations=500,
max_perturbations_without_improvement=500,
seed=42
)

Expand All @@ -154,7 +158,6 @@ viz = LONVisualizer()
outputs = viz.visualize_all(
lon,
output_folder="./output",
create_gifs=True,
seed=42
)

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Local Optima Networks (LONs) are graph-based models that capture the global stru

---

Compute landscape metrics including funnel analysis, neutrality measures, and global optima strength
Compute landscape metrics including funnel analysis, neutrality measures, and strength metrics (global and sink)

- **Beautiful Visualizations**

Expand Down
82 changes: 71 additions & 11 deletions docs/user-guide/analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ print(f"Found {n_optima} local optima")

- Higher values indicate more multimodal landscapes
- Compare across functions to assess relative complexity
- Depends on sampling thoroughness and hash_digits precision
- Depends on sampling thoroughness and coordinate_precision

### n_funnels

Expand Down Expand Up @@ -83,21 +83,85 @@ print(f"Neutrality: {neutral:.1%}")
- High % = Many plateaus or degenerate optima
- Affects how CMLON compression works

### strength
### global_strength

**Proportion of incoming edge weight to global optima.**
**Proportion of global optima incoming strength to total incoming strength of all nodes.**

```python
strength = lon_metrics['strength']
print(f"Global strength: {strength:.1%}")
global_strength = lon_metrics['global_strength']
print(f"Global strength: {global_strength:.1%}")
```

**Interpretation:**

- 100% = All transitions flow toward global optimum
- Low % = Most flow diverted to suboptimal sinks
- Low % = Most flow diverted to suboptimal nodes
- Key indicator of optimization difficulty

### sink_strength

**Proportion of global sinks incoming strength to incoming strength of all sink nodes.**

```python
sink_strength = lon_metrics['sink_strength']
print(f"Sink strength: {sink_strength:.1%}")
```

**Interpretation:**

- 100% = All sink-directed flow reaches global sinks
- Low % = Most sink flow goes to local (suboptimal) sinks
- Focuses specifically on competition between sinks

## Performance Metrics

In addition to network topology metrics, `compute_metrics()` returns performance metrics based on the sampling runs:

### success

**Proportion of sampling runs that reached the global optimum.**

```python
success = lon_metrics['success']
print(f"Success rate: {success:.1%}")
```

**Interpretation:**

- 100% = Every run found the global optimum
- Low % = Many runs got trapped in local optima
- Useful for comparing algorithm effectiveness across landscapes

### deviation

**Mean absolute deviation from the global optimum across all runs.**

```python
deviation = lon_metrics['deviation']
print(f"Mean deviation: {deviation:.6f}")
```

**Interpretation:**

- 0.0 = All runs converged to the global optimum
- Higher values indicate runs ending far from the global optimum
- Complements `success` by measuring "how far off" unsuccessful runs are

### Separating Network and Performance Metrics

You can also compute them separately:

```python
# Only network topology metrics (n_optima, n_funnels, etc.)
network_metrics = lon.compute_network_metrics()

# Only performance metrics (success, deviation)
performance_metrics = lon.compute_performance_metrics()

# Both combined (equivalent to compute_metrics())
all_metrics = lon.compute_metrics()
```

## CMLON-Specific Metrics

CMLON computes additional metrics:
Expand Down Expand Up @@ -165,10 +229,6 @@ names = lon.vertex_names
sinks = lon.get_sinks()
print(f"Sink indices: {sinks}")

# Global optima
global_idx = lon.get_global_optima_indices()
print(f"Global optimum indices: {global_idx}")

# For CMLON: separate global and local sinks
global_sinks = cmlon.get_global_sinks()
local_sinks = cmlon.get_local_sinks()
Expand Down Expand Up @@ -233,7 +293,7 @@ def classify_landscape(lon):
return "Easy: Single-funnel landscape"
elif cmlon_metrics['global_funnel_proportion'] > 0.8:
return "Moderate: Multiple funnels but well-connected"
elif metrics['strength'] > 0.5:
elif metrics['global_strength'] > 0.5:
return "Moderate: Good flow to global optimum"
else:
return "Hard: Multiple competing funnels"
Expand Down
Loading