history/graph.py (135 lines of code) (raw):
#!/usr/bin/env python3
# Copyright 2024 -l
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import matplotlib.pyplot as plt
import numpy as np
# Define the hockey-stick function (piecewise cubic with transition at x = 10)
def hockey_stick(x):
return np.piecewise(
x,
[x < 10, x >= 10],
[
lambda x: 0.05 * x,
lambda x: 0.005 * (x - 10) ** 3 + 0.5,
], # Transition at x = 10
)
# Define the fixed-point (logistic growth) function
def fixed_point(x):
return 1 / (
1 + np.exp(-0.6 * (x - 5))
) # Logistic growth centered around x = 5
# Define the decay function (damped sine wave)
def decay(x):
return np.sin(0.5 * x) * np.exp(-0.1 * x) # Damped sine wave
# Introduce larger noise (jitter) for better differentiation between metrics
noise_factor = 0.05 # Increased noise
# Use a fixed X-axis range for all plots (e.g., 0 to 20)
time_range = np.linspace(0, 20, 500)
# Function to adjust label placement dynamically
def get_label_offset(x, y, ax):
# Get the current axis limits
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# Determine where to place the label based on the position in the graph
if x < (xlim[0] + xlim[1]) / 2:
x_offset = 1 # Right
else:
x_offset = -2 # Left
if y < (ylim[0] + ylim[1]) / 2:
y_offset = 0.1 # Above
else:
y_offset = -0.1 # Below
return x_offset, y_offset
# Create plots for each function (hockey-stick, fixed-point, decay)
for performance_function, title, file_name, custom_labels in [
(
hockey_stick,
"Hockey-Stick Growth",
"hockey-stick.svg",
[("Inflection Point", 12.5)],
),
(
fixed_point,
"Saturation (Fixed-Point) Growth",
"fixed-point.svg",
[("Saturation", 10)],
),
(
decay,
"Decay Over Time",
"decay.svg",
[
("Initial Growth", 0),
("Decline", 2.5),
("Recovery", 8.75),
("Saturation", 15.0),
],
),
]:
# Generate performance metrics with noise
accuracy = performance_function(
time_range
) + noise_factor * np.random.normal(size=len(time_range))
generalization = (
performance_function(time_range)
+ noise_factor * np.random.normal(size=len(time_range))
+ 0.1
)
coherence = (
performance_function(time_range)
+ noise_factor * np.random.normal(size=len(time_range))
+ 0.15
)
creativity = (
performance_function(time_range)
+ noise_factor * np.random.normal(size=len(time_range))
+ 0.2
)
efficiency = (
performance_function(time_range)
+ noise_factor * np.random.normal(size=len(time_range))
+ 0.25
)
# Plot the time-series data with noise to differentiate the lines
fig, ax = plt.subplots(figsize=(10, 6))
ax.plot(time_range, accuracy, label="Accuracy", color="blue", linewidth=2)
ax.plot(
time_range,
generalization,
label="Generalization",
color="green",
linewidth=2,
)
ax.plot(
time_range, coherence, label="Coherence", color="orange", linewidth=2
)
ax.plot(
time_range, creativity, label="Creativity", color="red", linewidth=2
)
ax.plot(
time_range, efficiency, label="Efficiency", color="purple", linewidth=2
)
# Add custom labels for each graph with dynamically adjusted annotations
for label, x in custom_labels:
y = performance_function(np.array([x]))[0]
# Add a black dot with a white edge
ax.scatter(
x,
y,
color="black",
zorder=5,
edgecolor="white", # White edge
linewidth=2, # Thickness of the edge
s=100, # Increase size of the dot for better visibility
)
# Get dynamically calculated offset
x_offset, y_offset = get_label_offset(x, y, ax)
# Add a smaller black arrow on top of the white one
ax.annotate(
label,
xy=(x, y), # The point to annotate
xytext=(x + x_offset, y + y_offset), # Dynamic offset
arrowprops=dict(
facecolor="white",
arrowstyle="wedge",
connectionstyle="arc3,rad=0.3",
shrinkB=7,
),
bbox=dict(
boxstyle="round,pad=0.3", edgecolor="black", facecolor="white"
), # Label box styling
fontsize=10,
color="black", # Label text color
)
# Add labels and title
ax.set_xlabel("Time")
ax.set_ylabel("Performance")
ax.set_title(title)
ax.legend(loc="upper left")
# Display the plot
ax.grid(True)
plt.tight_layout()
file_name = f"slides/public/{file_name}"
# Save the plot as an SVG file
plt.savefig(file_name, format="svg")
print(f"Saved plot as {file_name}")
plt.show()