Spaces:
Runtime error
Runtime error
Upload 6 files
Browse files- agent.py +48 -0
- app.py +105 -0
- requirements.txt +5 -0
- simulation.py +55 -0
- utils.py +65 -0
- visualization.py +65 -0
agent.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import random
|
| 3 |
+
|
| 4 |
+
class RFTAgent:
|
| 5 |
+
def __init__(self, phi_init, tier, mutation_rate, drift_rate):
|
| 6 |
+
self.phi = phi_init
|
| 7 |
+
self.tau_eff = 0.0 # Initial tau_eff, will be updated
|
| 8 |
+
self.tier = tier
|
| 9 |
+
self.fitness = 0.0 # Initial fitness, will be updated
|
| 10 |
+
self.mutation_rate = mutation_rate
|
| 11 |
+
self.drift_rate = drift_rate
|
| 12 |
+
self.history = {
|
| 13 |
+
'phi': [],
|
| 14 |
+
'tau_eff': [],
|
| 15 |
+
'tier': [],
|
| 16 |
+
'fitness': []
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
def update_tau(self):
|
| 20 |
+
# tau_eff = 0.5*phi + random noise (using uniform for simplicity, can be normal)
|
| 21 |
+
self.tau_eff = 0.5 * self.phi + random.uniform(-0.1, 0.1)
|
| 22 |
+
|
| 23 |
+
def mutate(self):
|
| 24 |
+
# phi = phi + random.normal(0, mutation_rate)
|
| 25 |
+
self.phi += np.random.normal(0, self.mutation_rate)
|
| 26 |
+
|
| 27 |
+
def drift(self):
|
| 28 |
+
# phi = phi + drift_rate * tau_eff
|
| 29 |
+
self.phi += self.drift_rate * self.tau_eff
|
| 30 |
+
|
| 31 |
+
def update_fitness(self):
|
| 32 |
+
# fitness = abs(phi) + abs(tau_eff) + tier*0.1
|
| 33 |
+
self.fitness = abs(self.phi) + abs(self.tau_eff) + self.tier * 0.1
|
| 34 |
+
|
| 35 |
+
def step(self):
|
| 36 |
+
self.mutate()
|
| 37 |
+
self.drift()
|
| 38 |
+
self.update_tau()
|
| 39 |
+
self.update_fitness()
|
| 40 |
+
self._log_history()
|
| 41 |
+
|
| 42 |
+
def _log_history(self):
|
| 43 |
+
self.history['phi'].append(self.phi)
|
| 44 |
+
self.history['tau_eff'].append(self.tau_eff)
|
| 45 |
+
self.history['tier'].append(self.tier)
|
| 46 |
+
self.history['fitness'].append(self.fitness)
|
| 47 |
+
|
| 48 |
+
print("agent.py created successfully.")
|
app.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
import json
|
| 3 |
+
import os
|
| 4 |
+
import matplotlib.pyplot as plt # Kept for potential future gr.Plot use or if figures are handled directly
|
| 5 |
+
from simulation import RFTSimulation
|
| 6 |
+
from utils import export_json, seed_everything
|
| 7 |
+
from IPython.display import Image, display # For Colab dev mode inline image display
|
| 8 |
+
|
| 9 |
+
# Helper function to run simulation and generate plots/export data
|
| 10 |
+
def run_simulation_and_plot(num_agents, steps, mutation_rate, drift_rate, seed=None):
|
| 11 |
+
if seed is not None:
|
| 12 |
+
seed_everything(seed)
|
| 13 |
+
|
| 14 |
+
# Instantiate simulation with save_plots=True, so it saves plots during its run() method
|
| 15 |
+
simulation = RFTSimulation(num_agents, steps, mutation_rate, drift_rate, save_plots=True)
|
| 16 |
+
|
| 17 |
+
# Run the simulation. The internal run method will save plots if save_plots=True.
|
| 18 |
+
# It returns all_agents_history, coherence_list, stability_list.
|
| 19 |
+
all_agents_history, coherence_list, stability_list = simulation.run()
|
| 20 |
+
|
| 21 |
+
# Prepare final agent states for JSON export
|
| 22 |
+
final_agent_states = []
|
| 23 |
+
for i, agent in enumerate(simulation.agents):
|
| 24 |
+
final_agent_states.append({
|
| 25 |
+
'id': i + 1,
|
| 26 |
+
'phi': agent.phi,
|
| 27 |
+
'tau_eff': agent.tau_eff,
|
| 28 |
+
'tier': agent.tier,
|
| 29 |
+
'fitness': agent.fitness
|
| 30 |
+
})
|
| 31 |
+
|
| 32 |
+
# Export JSON
|
| 33 |
+
json_filename = "final_agent_states.json"
|
| 34 |
+
export_json(final_agent_states, json_filename)
|
| 35 |
+
|
| 36 |
+
# Return filenames of the generated plots and the JSON for Gradio or dev mode display
|
| 37 |
+
plot_filenames = [
|
| 38 |
+
'phi_plot.png',
|
| 39 |
+
'tau_plot.png',
|
| 40 |
+
'fitness_plot.png',
|
| 41 |
+
'coherence_plot.png',
|
| 42 |
+
'stability_plot.png'
|
| 43 |
+
]
|
| 44 |
+
|
| 45 |
+
return (*plot_filenames, json_filename)
|
| 46 |
+
|
| 47 |
+
# Gradio Interface setup
|
| 48 |
+
iface = gr.Interface(
|
| 49 |
+
fn=run_simulation_and_plot,
|
| 50 |
+
inputs=[
|
| 51 |
+
gr.Slider(1, 10, value=3, step=1, label="Number of Agents"),
|
| 52 |
+
gr.Slider(10, 500, value=100, step=10, label="Steps"),
|
| 53 |
+
gr.Slider(0.01, 0.5, value=0.05, step=0.01, label="Mutation Rate"),
|
| 54 |
+
gr.Slider(0.01, 0.5, value=0.02, step=0.01, label="Drift Rate"),
|
| 55 |
+
gr.Number(value=42, label="Random Seed (optional, leave empty for random)") # Clarified label
|
| 56 |
+
],
|
| 57 |
+
outputs=[
|
| 58 |
+
gr.Image(label="Phi Plot"), # Changed to gr.Image to display saved PNGs
|
| 59 |
+
gr.Image(label="Tau Effective Plot"),
|
| 60 |
+
gr.Image(label="Fitness Plot"),
|
| 61 |
+
gr.Image(label="Coherence Plot"),
|
| 62 |
+
gr.Image(label="Stability Plot"),
|
| 63 |
+
gr.File(label="Exported Final Agent States (JSON)")
|
| 64 |
+
],
|
| 65 |
+
title="RFT Agent Simulation Engine MVP",
|
| 66 |
+
description="Simulate RFT Agents and visualize their dynamics."
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
# Colab-friendly "dev mode" or Gradio launch logic
|
| 70 |
+
# Check if running in Colab environment (simple heuristic)
|
| 71 |
+
IN_COLAB = 'COLAB_GPU' in os.environ or 'Google' in os.environ.get('COLAB_CLOUD_PLATFORM', '')
|
| 72 |
+
|
| 73 |
+
if IN_COLAB:
|
| 74 |
+
print("Running in Colab development mode (skipping Gradio launch).")
|
| 75 |
+
# Run the test simulation logic as per requirements
|
| 76 |
+
num_agents = 3
|
| 77 |
+
steps = 100
|
| 78 |
+
mutation_rate = 0.05
|
| 79 |
+
drift_rate = 0.02
|
| 80 |
+
seed = 42
|
| 81 |
+
|
| 82 |
+
print("\n--- Running Test Simulation in Dev Mode ---")
|
| 83 |
+
# Call the core simulation function. It will save plots and JSON.
|
| 84 |
+
plot_files_and_json_file = run_simulation_and_plot(num_agents, steps, mutation_rate, drift_rate, seed)
|
| 85 |
+
plot_files = plot_files_and_json_file[:-1]
|
| 86 |
+
json_file = plot_files_and_json_file[-1]
|
| 87 |
+
|
| 88 |
+
print("\nSimulation complete. Displaying plots and exported JSON path:")
|
| 89 |
+
for plot_file in plot_files:
|
| 90 |
+
print(f" - {os.path.abspath(plot_file)}")
|
| 91 |
+
if os.path.exists(plot_file):
|
| 92 |
+
display(Image(filename=plot_file)) # Display inline in Colab
|
| 93 |
+
else:
|
| 94 |
+
print(f"Warning: Plot file {plot_file} not found for inline display.")
|
| 95 |
+
|
| 96 |
+
print(f" - {os.path.abspath(json_file)}")
|
| 97 |
+
print(f"Final agent states exported to {json_file}")
|
| 98 |
+
print("\n--- End of Test Simulation in Dev Mode ---")
|
| 99 |
+
|
| 100 |
+
else:
|
| 101 |
+
print("Running Gradio interface. Call iface.launch() to start it.")
|
| 102 |
+
# For local development, uncomment this to launch Gradio:
|
| 103 |
+
# iface.launch()
|
| 104 |
+
|
| 105 |
+
print("app.py updated successfully.")
|
requirements.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
matplotlib
|
| 2 |
+
numpy
|
| 3 |
+
gradio
|
| 4 |
+
|
| 5 |
+
print("requirements.txt created successfully.")
|
simulation.py
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
from agent import RFTAgent
|
| 3 |
+
import visualization # Added import for visualization
|
| 4 |
+
|
| 5 |
+
class RFTSimulation:
|
| 6 |
+
def __init__(self, num_agents, steps, mutation_rate, drift_rate, save_plots=False): # Added save_plots
|
| 7 |
+
self.agents = []
|
| 8 |
+
self.steps = steps
|
| 9 |
+
self.mutation_rate = mutation_rate
|
| 10 |
+
self.drift_rate = drift_rate
|
| 11 |
+
self.save_plots = save_plots # Stored save_plots
|
| 12 |
+
self.coherence_list = [] # Initialized coherence_list
|
| 13 |
+
self.stability_list = [] # Initialized stability_list
|
| 14 |
+
|
| 15 |
+
for _ in range(num_agents):
|
| 16 |
+
phi_init = np.random.uniform(-1, 1)
|
| 17 |
+
tier = np.random.randint(1, 5)
|
| 18 |
+
agent = RFTAgent(phi_init, tier, mutation_rate, drift_rate)
|
| 19 |
+
self.agents.append(agent)
|
| 20 |
+
|
| 21 |
+
def run(self):
|
| 22 |
+
for _ in range(self.steps):
|
| 23 |
+
for agent in self.agents:
|
| 24 |
+
agent.step()
|
| 25 |
+
# Append current coherence and stability after all agents have stepped
|
| 26 |
+
self.coherence_list.append(self.compute_coherence())
|
| 27 |
+
self.stability_list.append(self.compute_stability())
|
| 28 |
+
|
| 29 |
+
# Conditional plot saving
|
| 30 |
+
if self.save_plots:
|
| 31 |
+
all_agents_history = self.get_history()
|
| 32 |
+
visualization.plot_phi(all_agents_history, filename='phi_plot.png')
|
| 33 |
+
visualization.plot_tau(all_agents_history, filename='tau_plot.png')
|
| 34 |
+
visualization.plot_fitness(all_agents_history, filename='fitness_plot.png')
|
| 35 |
+
visualization.plot_coherence(self.coherence_list, filename='coherence_plot.png')
|
| 36 |
+
visualization.plot_stability(self.stability_list, filename='stability_plot.png')
|
| 37 |
+
|
| 38 |
+
return (self.get_history(), self.coherence_list, self.stability_list) # Modified return value
|
| 39 |
+
|
| 40 |
+
def get_history(self):
|
| 41 |
+
return [agent.history for agent in self.agents]
|
| 42 |
+
|
| 43 |
+
def compute_coherence(self):
|
| 44 |
+
# Average phi across agents at the current step
|
| 45 |
+
if not self.agents:
|
| 46 |
+
return 0.0
|
| 47 |
+
return np.mean([agent.phi for agent in self.agents])
|
| 48 |
+
|
| 49 |
+
def compute_stability(self):
|
| 50 |
+
# Variance of phi across agents at the current step
|
| 51 |
+
if not self.agents:
|
| 52 |
+
return 0.0
|
| 53 |
+
return np.var([agent.phi for agent in self.agents])
|
| 54 |
+
|
| 55 |
+
print("simulation.py updated successfully.")
|
utils.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import numpy as np
|
| 3 |
+
import random
|
| 4 |
+
import os
|
| 5 |
+
import zipfile
|
| 6 |
+
|
| 7 |
+
def export_json(data, filename):
|
| 8 |
+
with open(filename, 'w') as f:
|
| 9 |
+
json.dump(data, f, indent=4)
|
| 10 |
+
print(f"Data exported to {filename}")
|
| 11 |
+
|
| 12 |
+
def seed_everything(seed):
|
| 13 |
+
random.seed(seed)
|
| 14 |
+
np.random.seed(seed)
|
| 15 |
+
print(f"Random seeds set to {seed}")
|
| 16 |
+
|
| 17 |
+
def zip_project(output_name="rft_simulation_engine.zip"):
|
| 18 |
+
files_to_zip = [
|
| 19 |
+
'agent.py',
|
| 20 |
+
'simulation.py',
|
| 21 |
+
'visualization.py',
|
| 22 |
+
'app.py',
|
| 23 |
+
'utils.py',
|
| 24 |
+
'requirements.txt',
|
| 25 |
+
'README.md',
|
| 26 |
+
'test_runner.py',
|
| 27 |
+
'final_agent_states.json',
|
| 28 |
+
'phi_plot.png',
|
| 29 |
+
'tau_plot.png',
|
| 30 |
+
'fitness_plot.png',
|
| 31 |
+
'coherence_plot.png',
|
| 32 |
+
'stability_plot.png'
|
| 33 |
+
]
|
| 34 |
+
|
| 35 |
+
with zipfile.ZipFile(output_name, 'w') as zf:
|
| 36 |
+
for file in files_to_zip:
|
| 37 |
+
if os.path.exists(file):
|
| 38 |
+
zf.write(file)
|
| 39 |
+
print(f"Added {file} to {output_name}")
|
| 40 |
+
else:
|
| 41 |
+
print(f"Warning: {file} not found, skipping.")
|
| 42 |
+
print(f"Successfully created {output_name} containing all specified files.")
|
| 43 |
+
|
| 44 |
+
def zip_huggingface_repo(output_name='rft_hf_repo.zip'):
|
| 45 |
+
"""Creates a zip archive containing only the core project files for Hugging Face deployment."""
|
| 46 |
+
files_to_include = [
|
| 47 |
+
'agent.py',
|
| 48 |
+
'simulation.py',
|
| 49 |
+
'visualization.py',
|
| 50 |
+
'app.py',
|
| 51 |
+
'utils.py',
|
| 52 |
+
'requirements.txt',
|
| 53 |
+
'README.md'
|
| 54 |
+
]
|
| 55 |
+
|
| 56 |
+
with zipfile.ZipFile(output_name, 'w') as zf:
|
| 57 |
+
for file in files_to_include:
|
| 58 |
+
if os.path.exists(file):
|
| 59 |
+
zf.write(file)
|
| 60 |
+
print(f"Added {file} to {output_name}")
|
| 61 |
+
else:
|
| 62 |
+
print(f"Warning: {file} not found, skipping for Hugging Face repo zip.")
|
| 63 |
+
print(f"Successfully created Hugging Face repository zip: {output_name}")
|
| 64 |
+
|
| 65 |
+
print("utils.py updated successfully with new zip_huggingface_repo function.")
|
visualization.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import matplotlib.pyplot as plt
|
| 2 |
+
import numpy as np
|
| 3 |
+
|
| 4 |
+
def plot_phi(all_agents_history, filename=None):
|
| 5 |
+
plt.figure(figsize=(10, 6))
|
| 6 |
+
for i, agent_history in enumerate(all_agents_history):
|
| 7 |
+
plt.plot(agent_history['phi'], label=f'Agent {i+1} Phi')
|
| 8 |
+
plt.xlabel('Step')
|
| 9 |
+
plt.ylabel('Phi')
|
| 10 |
+
plt.title('Agent Phi Over Time')
|
| 11 |
+
plt.legend()
|
| 12 |
+
plt.grid(True)
|
| 13 |
+
if filename is not None:
|
| 14 |
+
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
| 15 |
+
plt.close()
|
| 16 |
+
|
| 17 |
+
def plot_tau(all_agents_history, filename=None):
|
| 18 |
+
plt.figure(figsize=(10, 6))
|
| 19 |
+
for i, agent_history in enumerate(all_agents_history):
|
| 20 |
+
plt.plot(agent_history['tau_eff'], label=f'Agent {i+1} Tau Eff')
|
| 21 |
+
plt.xlabel('Step')
|
| 22 |
+
plt.ylabel('Tau Effective')
|
| 23 |
+
plt.title('Agent Tau Effective Over Time')
|
| 24 |
+
plt.legend()
|
| 25 |
+
plt.grid(True)
|
| 26 |
+
if filename is not None:
|
| 27 |
+
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
| 28 |
+
plt.close()
|
| 29 |
+
|
| 30 |
+
def plot_fitness(all_agents_history, filename=None):
|
| 31 |
+
plt.figure(figsize=(10, 6))
|
| 32 |
+
for i, agent_history in enumerate(all_agents_history):
|
| 33 |
+
plt.plot(agent_history['fitness'], label=f'Agent {i+1} Fitness')
|
| 34 |
+
plt.xlabel('Step')
|
| 35 |
+
plt.ylabel('Fitness')
|
| 36 |
+
plt.title('Agent Fitness Over Time')
|
| 37 |
+
plt.legend()
|
| 38 |
+
plt.grid(True)
|
| 39 |
+
if filename is not None:
|
| 40 |
+
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
| 41 |
+
plt.close()
|
| 42 |
+
|
| 43 |
+
def plot_coherence(coherence_list, filename=None):
|
| 44 |
+
plt.figure(figsize=(10, 6))
|
| 45 |
+
plt.plot(coherence_list)
|
| 46 |
+
plt.xlabel('Step')
|
| 47 |
+
plt.ylabel('Average Phi')
|
| 48 |
+
plt.title('System Coherence Over Time (Average Phi)')
|
| 49 |
+
plt.grid(True)
|
| 50 |
+
if filename is not None:
|
| 51 |
+
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
| 52 |
+
plt.close()
|
| 53 |
+
|
| 54 |
+
def plot_stability(stability_list, filename=None):
|
| 55 |
+
plt.figure(figsize=(10, 6))
|
| 56 |
+
plt.plot(stability_list)
|
| 57 |
+
plt.xlabel('Step')
|
| 58 |
+
plt.ylabel('Variance of Phi')
|
| 59 |
+
plt.title('System Stability Over Time (Variance of Phi)')
|
| 60 |
+
plt.grid(True)
|
| 61 |
+
if filename is not None:
|
| 62 |
+
plt.savefig(filename, dpi=300, bbox_inches='tight')
|
| 63 |
+
plt.close()
|
| 64 |
+
|
| 65 |
+
print("visualization.py updated successfully.")
|