Analysis Guide¶
This guide explains how to analyze LONs and interpret the computed metrics.
Computing Metrics¶
Both LON and CMLON provide a compute_metrics() method:
from lonkit import compute_lon, BasinHoppingSamplerConfig
lon = compute_lon(func, dim=2, lower_bound=-5, upper_bound=5,
config=BasinHoppingSamplerConfig(n_runs=30))
# LON metrics
lon_metrics = lon.compute_metrics()
print(lon_metrics)
# CMLON metrics
cmlon = lon.to_cmlon()
cmlon_metrics = cmlon.compute_metrics()
print(cmlon_metrics)
Empty Networks¶
An empty LON() (or a graph with all vertices removed) returns zero for every
network metric. to_cmlon() returns an empty CMLON with the same metadata and
source LON; its network metrics are also zero. Both support blank 2D and 3D plots.
Performance metrics use the stored sampling results independently of the graph.
When these results are absent or empty, or no best fitness is available,
success and deviation are both 0.0. These defaults indicate missing data,
not successful optimization. A CMLON without a source LON returns an empty
performance dictionary, as usual.
LON.from_trace_data() still rejects an empty DataFrame with
ValueError("Trace is empty.").
LON Metrics¶
n_optima¶
Number of local optima discovered.
Interpretation:
- Higher values indicate more multimodal landscapes
- Compare across functions to assess relative complexity
- Depends on sampling thoroughness and coordinate_precision
n_funnels¶
Number of sink nodes (nodes with no outgoing edges).
Interpretation:
- 1 funnel = single-funnel landscape (easier to optimize)
- Multiple funnels = competing basins (harder to optimize)
- Sinks are "endpoints" where Basin-Hopping cannot escape
n_global_funnels¶
Number of sinks at the global optimum fitness.
Interpretation:
- Ideally equals n_funnels (all paths lead to global)
- If n_global < n_funnels, some searches get trapped
- Multiple global funnels may indicate symmetry in the landscape
neutral¶
Proportion of nodes with equal-fitness connections.
Interpretation:
- 0% = No flat regions, all transitions change fitness
- High % = Many plateaus or degenerate optima
- Affects how CMLON compression works
global_strength¶
Proportion of global optima incoming strength to total incoming strength of all nodes.
Interpretation:
- 100% = All transitions flow toward global optimum
- 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.
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.
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.
Interpretation:
- 0.0 = All runs converged to the global optimum
- Higher values indicate runs ending far from the global optimum
- Complements
successby measuring "how far off" unsuccessful runs are
Separating Network and Performance Metrics¶
You can also compute them separately:
# 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:
global_funnel_proportion¶
Proportion of nodes that can reach a global optimum.
Interpretation:
- 100% = Every optimum can reach the global (easy landscape)
- Low % = Many optima trapped in local funnels
- Better measure of "escapability" than n_funnels
neutral (in CMLON)¶
For CMLON, neutral measures compression ratio:
Comparing LON vs CMLON¶
print("=== LON ===")
print(f"Nodes: {lon.n_vertices}")
print(f"Edges: {lon.n_edges}")
print("\n=== CMLON ===")
print(f"Nodes: {cmlon.n_vertices}")
print(f"Edges: {cmlon.n_edges}")
print(f"\nCompression: {1 - cmlon.n_vertices/lon.n_vertices:.1%}")
Accessing Graph Properties¶
Vertex Properties¶
# All vertex fitness values
fitnesses = lon.vertex_fitness
# Best fitness found
best = lon.best_fitness
# Vertex visit counts
counts = lon.vertex_count
# Vertex names (hash strings)
names = lon.vertex_names
Finding Special Nodes¶
# Sink nodes (no outgoing edges)
sinks = lon.get_sinks()
print(f"Sink indices: {sinks}")
# For CMLON: separate global and local sinks
global_sinks = cmlon.get_global_sinks()
local_sinks = cmlon.get_local_sinks()
Working with the Graph¶
The underlying graph is accessible via the graph attribute (python-igraph):
import igraph as ig
# Get the igraph Graph object
g = lon.graph
# Graph properties
print(f"Vertices: {g.vcount()}")
print(f"Edges: {g.ecount()}")
print(f"Directed: {g.is_directed()}")
# Edge list
edges = g.get_edgelist()
# Vertex attributes
all_fitness = g.vs["Fitness"]
all_counts = g.vs["Count"]
# Edge attributes
edge_weights = g.es["Count"]
# Compute standard graph metrics
print(f"Density: {g.density():.4f}")
print(f"Components: {len(g.components(mode='weak'))}")
Using Known Global Optimum¶
If you know the true global optimum, pass it to compute_metrics():
# Rastrigin global optimum is 0
metrics = lon.compute_metrics(known_best=0)
# Check if we found it
if lon.best_fitness == 0:
print("Global optimum found!")
else:
print(f"Best found: {lon.best_fitness} (gap: {lon.best_fitness - 0})")
Landscape Classification¶
Use metrics to classify landscape difficulty:
def classify_landscape(lon):
metrics = lon.compute_metrics()
cmlon = lon.to_cmlon()
cmlon_metrics = cmlon.compute_metrics()
if metrics['n_funnels'] == 1:
return "Easy: Single-funnel landscape"
elif cmlon_metrics['global_funnel_proportion'] > 0.8:
return "Moderate: Multiple funnels but well-connected"
elif metrics['global_strength'] > 0.5:
return "Moderate: Good flow to global optimum"
else:
return "Hard: Multiple competing funnels"
print(classify_landscape(lon))
Comparing Multiple Functions¶
import numpy as np
from lonkit import compute_lon, BasinHoppingSamplerConfig
def sphere(x):
return np.sum(x**2)
def rastrigin(x):
return 10 * len(x) + np.sum(x**2 - 10 * np.cos(2 * np.pi * x))
def rosenbrock(x):
return sum(100*(x[i+1]-x[i]**2)**2 + (1-x[i])**2 for i in range(len(x)-1))
functions = {
"Sphere": (sphere, -5, 5),
"Rastrigin": (rastrigin, -5.12, 5.12),
"Rosenbrock": (rosenbrock, -5, 10),
}
results = {}
for name, (func, lb, ub) in functions.items():
lon = compute_lon(func, dim=2, lower_bound=lb, upper_bound=ub,
config=BasinHoppingSamplerConfig(n_runs=30, seed=42))
results[name] = lon.compute_metrics()
results[name]['n_vertices'] = lon.n_vertices
# Compare
import pandas as pd
df = pd.DataFrame(results).T
print(df)
Next Steps¶
- Visualization Guide - Create plots of your analysis
- Examples - Complete analysis workflows
- API Reference - Full LON/CMLON API