Skip to content

Commit fbc373e

Browse files
committed
Plotting script
<very ugly>
1 parent 1422fec commit fbc373e

File tree

1 file changed

+212
-45
lines changed

1 file changed

+212
-45
lines changed

plot.py

Lines changed: 212 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,214 @@
1-
import sys
1+
# code stolen & generated by claude
2+
23
import numpy as np
34
import matplotlib.pyplot as plt
4-
import pandas as pd
5-
6-
# Read the data
7-
data = pd.read_csv(sys.argv[1], header=None, names=["x", "y"])
8-
9-
# Create figure and axis
10-
plt.figure(figsize=(10, 6))
11-
12-
# Plot the actual data points
13-
plt.loglog(data["x"], data["y"], "bo-", markersize=8)
14-
15-
# Generate points for the theoretical line
16-
theory_exp_2d = 5 / 4
17-
theory_exp_3d = 1.624
18-
theory_exp_4d = 2
19-
20-
first_x = data["x"].iloc[0]
21-
first_y = data["y"].iloc[0]
22-
scaling_factor_2d = first_y / (first_x**theory_exp_2d)
23-
scaling_factor_3d = first_y / (first_x**theory_exp_3d)
24-
scaling_factor_4d = first_y / (first_x**theory_exp_4d)
25-
x_theory = np.logspace(np.log10(data["x"].min()), np.log10(data["x"].max()), 100)
26-
y_theory_2d = scaling_factor_2d * x_theory**theory_exp_2d
27-
y_theory_3d = scaling_factor_3d * x_theory**theory_exp_3d
28-
y_theory_4d = scaling_factor_4d * x_theory**theory_exp_4d
29-
30-
# Plot the theoretical line
31-
plt.loglog(x_theory, y_theory_2d, "g--", label="x^(5/4)")
32-
plt.loglog(x_theory, y_theory_3d, "g--", label="x^(1.624)")
33-
plt.loglog(x_theory, y_theory_4d, "g--", label="x^2")
34-
35-
# Customize the plot
36-
plt.grid(True, which="both", ls="-", alpha=0.2)
37-
plt.xlabel("D", fontsize=12)
38-
plt.ylabel("L", fontsize=12)
39-
plt.title("LERW Distance-Length comparison", fontsize=14)
40-
plt.legend(fontsize=10)
41-
42-
# Ensure tight layout
43-
plt.tight_layout()
44-
45-
# Save the plot
46-
plt.savefig("scaling.png", dpi=300, bbox_inches="tight")
47-
plt.show()
5+
from typing import List, Dict, Union
6+
from numpy.typing import NDArray
7+
from itertools import product
8+
9+
from interface import Norm, get_walk_lengths
10+
11+
12+
def estimate_exponent_with_errors(
13+
R_values: List[int], avg_lengths: List[float], n_bootstrap: int = 1000
14+
) -> Dict[str, Union[float, NDArray[np.float64]]]:
15+
"""
16+
Estimate the scaling exponent D and its uncertainty using robust bootstrap methods.
17+
18+
Args:
19+
R_values: List of system sizes
20+
avg_lengths: List of average path lengths
21+
n_bootstrap: Number of bootstrap samples for error estimation
22+
"""
23+
# Convert to numpy arrays and take logs
24+
log_R = np.log(np.array(R_values, dtype=np.float64))
25+
log_L = np.log(np.array(avg_lengths, dtype=np.float64))
26+
n_points = len(R_values)
27+
28+
# Perform main regression on original data
29+
main_slope, main_intercept = np.polyfit(log_R, log_L, 1)
30+
31+
# Initialize bootstrap arrays
32+
bootstrap_slopes = np.zeros(n_bootstrap)
33+
bootstrap_intercepts = np.zeros(n_bootstrap)
34+
35+
# Compute residuals from main fit
36+
y_fit = main_slope * log_R + main_intercept
37+
residuals = log_L - y_fit
38+
39+
# Perform parametric bootstrap using residuals
40+
for i in range(n_bootstrap):
41+
# Resample residuals
42+
bootstrap_residuals = np.random.choice(residuals, size=n_points, replace=True)
43+
# Generate new y values by adding resampled residuals to fitted line
44+
bootstrap_y = y_fit + bootstrap_residuals
45+
46+
# Fit on bootstrap sample
47+
try:
48+
slope, intercept = np.polyfit(log_R, bootstrap_y, 1)
49+
bootstrap_slopes[i] = slope
50+
bootstrap_intercepts[i] = intercept
51+
except np.linalg.LinAlgError:
52+
# If fitting fails, use the main fit values
53+
bootstrap_slopes[i] = main_slope
54+
bootstrap_intercepts[i] = main_intercept
55+
56+
# Calculate confidence intervals
57+
ci_slope = np.percentile(
58+
bootstrap_slopes[np.isfinite(bootstrap_slopes)], [2.5, 97.5]
59+
)
60+
ci_intercept = np.percentile(
61+
bootstrap_intercepts[np.isfinite(bootstrap_intercepts)], [2.5, 97.5]
62+
)
63+
64+
# Calculate standard errors
65+
slope_stderr = np.std(bootstrap_slopes[np.isfinite(bootstrap_slopes)])
66+
intercept_stderr = np.std(bootstrap_intercepts[np.isfinite(bootstrap_intercepts)])
67+
68+
# Calculate R-squared
69+
r_squared = 1 - (np.sum(residuals**2) / np.sum((log_L - np.mean(log_L)) ** 2))
70+
71+
return {
72+
"D": float(main_slope),
73+
"a": float(main_intercept),
74+
"std_error_slope": float(slope_stderr),
75+
"std_error_intercept": float(intercept_stderr),
76+
"bootstrap_ci_slope": ci_slope,
77+
"bootstrap_ci_intercept": ci_intercept,
78+
"r_squared": float(r_squared),
79+
"bootstrap_std_slope": float(slope_stderr),
80+
"bootstrap_std_intercept": float(intercept_stderr),
81+
"residual_std": float(np.std(residuals)),
82+
}
83+
84+
85+
def analyze_results(
86+
dim: int,
87+
norm: Norm,
88+
R_values: List[int],
89+
alpha: float,
90+
trials: int,
91+
) -> Dict[str, Union[float, NDArray[np.float64]]]:
92+
avg_lengths = [
93+
np.mean(get_walk_lengths(dim, R, trials, alpha, norm)) for R in R_values
94+
]
95+
96+
# Perform analysis
97+
results = estimate_exponent_with_errors(R_values, avg_lengths)
98+
99+
print(f"\nResults for {dim}D LERW (α={alpha:.3f}):")
100+
print(f"D (slope) = {results['D']:.6f} ± {results['std_error_slope']:.6f}")
101+
print(f"a (intercept) = {results['a']:.6f} ± {results['std_error_intercept']:.6f}")
102+
print(
103+
f"D 95% Bootstrap CI: [{results['bootstrap_ci_slope'][0]:.6f}, "
104+
f"{results['bootstrap_ci_slope'][1]:.6f}]"
105+
)
106+
print(f"R² = {results['r_squared']:.6f}")
107+
108+
return results
109+
110+
111+
def plot_alpha_dependence(
112+
results: Dict[str, List[float]], dimension: int, norm: Norm
113+
) -> None:
114+
"""Create a plot showing how D varies with alpha"""
115+
116+
alpha = np.array(results["alpha"])
117+
D = np.array(results["D"])
118+
D_ci_lower = np.array(results["D_ci_lower"])
119+
D_ci_upper = np.array(results["D_ci_upper"])
120+
121+
plt.errorbar(
122+
alpha,
123+
D,
124+
yerr=[D - D_ci_lower, D_ci_upper - D],
125+
fmt="o-",
126+
capsize=5,
127+
label=f"{dimension}D LERW, {norm}",
128+
)
129+
130+
plt.fill_between(alpha, D_ci_lower, D_ci_upper, alpha=0.2, label="95% Bootstrap CI")
131+
132+
plt.xlabel("α")
133+
plt.ylabel("D")
134+
plt.title(f"Fractal Dimension vs Alpha in {dimension}D LERW")
135+
plt.grid(True, alpha=0.3)
136+
plt.legend(loc="lower right")
137+
138+
stats_text = (
139+
f"Dimension: {dimension}D\n"
140+
f"α range: [{min(alpha):.2f}, {max(alpha):.2f}]\n"
141+
f"D range: [{min(D):.2f}, {max(D):.2f}]\n"
142+
f"CI width: [{min(D_ci_upper - D_ci_lower):.3f}, {max(D_ci_upper - D_ci_lower):.3f}]"
143+
)
144+
plt.text(
145+
0.05,
146+
0.95,
147+
stats_text,
148+
transform=plt.gca().transAxes,
149+
verticalalignment="top",
150+
bbox=dict(facecolor="white", alpha=0.8),
151+
)
152+
153+
plt.ylim(min(D_ci_lower) - 0.1, max(D_ci_upper) + 0.1)
154+
155+
plt.savefig(f"lerw_alpha_dependence_{dimension}d.png", dpi=300, bbox_inches="tight")
156+
157+
158+
def show_alpha_dependence(
159+
dim: int,
160+
norm: Norm,
161+
R_values: List[int],
162+
alpha_values: List[int],
163+
trials: int,
164+
):
165+
results = {"alpha": [], "D": [], "D_err": [], "D_ci_lower": [], "D_ci_upper": []}
166+
167+
for alpha in alpha_values:
168+
analysis_results = analyze_results(dim, norm, R_values, alpha, trials)
169+
170+
results["alpha"].append(alpha)
171+
results["D"].append(analysis_results["D"])
172+
results["D_err"].append(analysis_results["std_error_slope"])
173+
results["D_ci_lower"].append(analysis_results["bootstrap_ci_slope"][0])
174+
results["D_ci_upper"].append(analysis_results["bootstrap_ci_slope"][1])
175+
176+
plot_alpha_dependence(results, dim, norm)
177+
178+
179+
def generate_R_values(min_size: int, max_size: int, num_sizes: int) -> List[int]:
180+
"""Generate system sizes in geometric progression"""
181+
return [
182+
int(min_size * (max_size / min_size) ** (i / (num_sizes - 1)))
183+
for i in range(num_sizes)
184+
]
185+
186+
187+
def main():
188+
config = {
189+
"min_size": 500,
190+
"max_size": 2_000,
191+
"num_sizes": 4,
192+
"alpha_min": 0.2,
193+
"alpha_max": 2.0,
194+
"alpha_steps": 10,
195+
}
196+
197+
# Generate parameter arrays
198+
R_values = generate_R_values(
199+
config["min_size"], config["max_size"], config["num_sizes"]
200+
)
201+
alpha_values = np.linspace(
202+
config["alpha_min"], config["alpha_max"], config["alpha_steps"]
203+
)
204+
205+
plt.figure(figsize=(10, 6))
206+
x_range = np.array([min(alpha_values), max(alpha_values)])
207+
plt.plot(x_range, x_range, "--", color="gray", alpha=0.7, label="α = D")
208+
for dim, norm in product([2, 3], [Norm.L2, Norm.LINF]):
209+
show_alpha_dependence(dim, norm, R_values, alpha_values, 1000)
210+
plt.show()
211+
212+
213+
if __name__ == "__main__":
214+
main()

0 commit comments

Comments
 (0)