-
Notifications
You must be signed in to change notification settings - Fork 2
Add comprehensive performance visualization for optimization algorithms #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
031165b
Initial plan
Copilot 92b6dfa
feat: add comprehensive performance visualization for optimization al…
Copilot 0fdb8c7
docs: add visualization examples and comprehensive documentation
Copilot af85384
fix: address code review feedback for visualization module
Copilot b4a8892
fix: resolve linting issues and ensure tests pass
Copilot 8934c70
fix: add return type annotations to test functions
Copilot 80f4121
fix: resolve ruff linting issues in examples_visualization.py
Anselmoo f5656b7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,329 @@ | ||
| # Visualization Module | ||
|
|
||
| Comprehensive visualization and stability testing tools for optimization algorithms. | ||
|
|
||
| ## Features | ||
|
|
||
| - **Convergence Curves**: Track best fitness values over iterations | ||
| - **Trajectory Plots**: Visualize search paths through 2D solution spaces | ||
| - **Average Fitness Tracking**: Monitor population fitness with standard deviation bands | ||
| - **Stability Testing**: Run algorithms multiple times with different seeds | ||
| - **Statistical Analysis**: Generate box plots, histograms, and summary statistics | ||
| - **Multi-Optimizer Comparison**: Compare performance and stability across algorithms | ||
|
|
||
| ## Installation | ||
|
|
||
| The visualization module requires matplotlib as an optional dependency: | ||
|
|
||
| ```bash | ||
| pip install useful-optimizer[visualization] | ||
| ``` | ||
|
|
||
| Or install manually: | ||
|
|
||
| ```bash | ||
| pip install matplotlib>=3.7.0 | ||
| ``` | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ### Basic Visualization | ||
|
|
||
| ```python | ||
| from opt.swarm_intelligence.particle_swarm import ParticleSwarm | ||
| from opt.benchmark.functions import shifted_ackley | ||
| from opt.visualization import Visualizer | ||
|
|
||
| # Create optimizer with history tracking enabled | ||
| pso = ParticleSwarm( | ||
| func=shifted_ackley, | ||
| lower_bound=-5, | ||
| upper_bound=5, | ||
| dim=2, | ||
| max_iter=100, | ||
| track_history=True, # Enable history tracking | ||
| population_size=30, | ||
| ) | ||
|
|
||
| # Run optimization | ||
| best_solution, best_fitness = pso.search() | ||
|
|
||
| # Create visualizer and generate plots | ||
| viz = Visualizer(pso) | ||
| viz.plot_convergence() # Show convergence curve | ||
| viz.plot_trajectory() # Show 2D search trajectory | ||
| viz.plot_average_fitness() # Show population fitness evolution | ||
| viz.plot_all() # Generate all plots in one figure | ||
| ``` | ||
|
|
||
| ### Stability Testing | ||
|
|
||
| ```python | ||
| from opt.visualization import run_stability_test | ||
|
|
||
| # Run optimizer multiple times with different seeds | ||
| results = run_stability_test( | ||
| optimizer_class=ParticleSwarm, | ||
| func=shifted_ackley, | ||
| lower_bound=-5, | ||
| upper_bound=5, | ||
| dim=2, | ||
| max_iter=100, | ||
| seeds=[42, 123, 456, 789, 1011], # Specific seeds | ||
| # OR use: n_runs=10 # Random seeds | ||
| ) | ||
|
|
||
| # Print statistical summary | ||
| results.print_summary() | ||
|
|
||
| # Generate visualizations | ||
| results.plot_boxplot() | ||
| results.plot_histogram() | ||
| ``` | ||
|
|
||
| ### Compare Multiple Optimizers | ||
|
|
||
| ```python | ||
| from opt.visualization import compare_optimizers_stability | ||
| from opt.swarm_intelligence.particle_swarm import ParticleSwarm | ||
| from opt.evolutionary.genetic_algorithm import GeneticAlgorithm | ||
|
|
||
| # Compare two or more optimizers | ||
| results_dict, fig = compare_optimizers_stability( | ||
| optimizer_classes=[ParticleSwarm, GeneticAlgorithm], | ||
| func=shifted_ackley, | ||
| lower_bound=-5, | ||
| upper_bound=5, | ||
| dim=2, | ||
| max_iter=100, | ||
| n_runs=10, | ||
| ) | ||
|
|
||
| # Access individual results | ||
| for name, results in results_dict.items(): | ||
| print(f"{name}: {results.summary()}") | ||
| ``` | ||
|
|
||
| ## API Reference | ||
|
|
||
| ### Visualizer Class | ||
|
|
||
| The `Visualizer` class provides visualization methods for a single optimizer run. | ||
|
|
||
| **Constructor:** | ||
| ```python | ||
| Visualizer(optimizer: AbstractOptimizer) | ||
| ``` | ||
|
|
||
| **Methods:** | ||
|
|
||
| - `plot_convergence(log_scale=False, show=True, ax=None)`: Plot best fitness over iterations | ||
| - `plot_trajectory(show=True, ax=None, max_points=1000)`: Plot 2D search trajectory | ||
| - `plot_average_fitness(show_std=True, show=True, ax=None)`: Plot population fitness with std bands | ||
| - `plot_all(save_path=None)`: Generate comprehensive multi-panel visualization | ||
|
|
||
| ### StabilityResults Class | ||
|
|
||
| Stores and analyzes results from multiple optimizer runs. | ||
|
|
||
| **Attributes:** | ||
| - `optimizer_name`: Name of the optimizer | ||
| - `function_name`: Name of the objective function | ||
| - `solutions`: List of best solutions from each run | ||
| - `fitness_values`: Array of best fitness values | ||
| - `seeds`: List of random seeds used | ||
|
|
||
| **Methods:** | ||
|
|
||
| - `summary()`: Get statistical summary (mean, std, min, max, median, quartiles) | ||
| - `print_summary()`: Print formatted summary | ||
| - `plot_boxplot(show=True, save_path=None)`: Generate box plot | ||
| - `plot_histogram(bins=20, show=True, save_path=None)`: Generate histogram | ||
|
|
||
| ### Functions | ||
|
|
||
| **run_stability_test()** | ||
|
|
||
| Run stability test for an optimization algorithm. | ||
|
|
||
| ```python | ||
| run_stability_test( | ||
| optimizer_class: type[AbstractOptimizer], | ||
| func: Callable, | ||
| lower_bound: float, | ||
| upper_bound: float, | ||
| dim: int, | ||
| max_iter: int = 100, | ||
| seeds: Sequence[int] | None = None, | ||
| n_runs: int = 10, | ||
| verbose: bool = True, | ||
| **optimizer_kwargs | ||
| ) -> StabilityResults | ||
| ``` | ||
|
|
||
| **compare_optimizers_stability()** | ||
|
|
||
| Compare stability of multiple optimizers. | ||
|
|
||
| ```python | ||
| compare_optimizers_stability( | ||
| optimizer_classes: list[type[AbstractOptimizer]], | ||
| func: Callable, | ||
| lower_bound: float, | ||
| upper_bound: float, | ||
| dim: int, | ||
| max_iter: int = 100, | ||
| n_runs: int = 10, | ||
| show: bool = True, | ||
| save_path: str | None = None, | ||
| ) -> tuple[dict[str, StabilityResults], Figure] | ||
| ``` | ||
|
|
||
| ## History Tracking | ||
|
|
||
| To use visualization features, optimizers must be run with `track_history=True`: | ||
|
|
||
| ```python | ||
| optimizer = ParticleSwarm( | ||
| func=shifted_ackley, | ||
| lower_bound=-5, | ||
| upper_bound=5, | ||
| dim=2, | ||
| max_iter=100, | ||
| track_history=True, # Required for visualization | ||
| ) | ||
| ``` | ||
|
|
||
| **Note:** History tracking adds memory overhead proportional to `max_iter × population_size`. For very long runs or large populations, consider using it selectively. | ||
|
|
||
| ## Advanced Usage | ||
|
|
||
| ### Custom Matplotlib Integration | ||
|
|
||
| The visualization methods return matplotlib Figure objects and accept axes parameters, allowing full customization: | ||
|
|
||
| ```python | ||
| import matplotlib.pyplot as plt | ||
|
|
||
| # Create custom layout | ||
| fig, axes = plt.subplots(2, 2, figsize=(12, 10)) | ||
|
|
||
| viz = Visualizer(pso) | ||
|
|
||
| # Plot to specific axes | ||
| viz.plot_convergence(show=False, ax=axes[0, 0]) | ||
| viz.plot_trajectory(show=False, ax=axes[0, 1]) | ||
| viz.plot_average_fitness(show=False, ax=axes[1, 0]) | ||
| viz.plot_convergence(log_scale=True, show=False, ax=axes[1, 1]) | ||
|
|
||
| plt.tight_layout() | ||
| plt.savefig("custom_visualization.png", dpi=300) | ||
| ``` | ||
|
|
||
| ### Log Scale Convergence | ||
|
|
||
| For functions with wide fitness ranges, use log scale: | ||
|
|
||
| ```python | ||
| viz.plot_convergence(log_scale=True) | ||
| ``` | ||
|
|
||
| ### Saving Plots | ||
|
|
||
| All plot methods support saving: | ||
|
|
||
| ```python | ||
| # Individual plots | ||
| viz.plot_convergence(show=False) | ||
| plt.savefig("convergence.png", dpi=300, bbox_inches="tight") | ||
|
|
||
| # Or use save_path parameter | ||
| viz.plot_all(save_path="all_plots.png") | ||
| results.plot_boxplot(save_path="stability.png") | ||
| ``` | ||
|
|
||
| ### Reproducible Results | ||
|
|
||
| Use specific seeds for reproducible stability tests: | ||
|
|
||
| ```python | ||
| results = run_stability_test( | ||
| optimizer_class=ParticleSwarm, | ||
| func=shifted_ackley, | ||
| lower_bound=-5, | ||
| upper_bound=5, | ||
| dim=2, | ||
| seeds=[42, 123, 456], # Same seeds = same results | ||
| ) | ||
| ``` | ||
|
|
||
| ## Examples | ||
|
|
||
| See `examples_visualization.py` for complete working examples including: | ||
|
|
||
| 1. Basic visualization workflow | ||
| 2. Stability testing with multiple seeds | ||
| 3. Multi-optimizer comparison | ||
| 4. Log scale convergence plots | ||
| 5. Custom matplotlib integration | ||
|
|
||
| Run the examples: | ||
|
|
||
| ```bash | ||
| python examples_visualization.py | ||
| ``` | ||
|
|
||
| ## Supported Optimizers | ||
|
|
||
| The visualization module works with **all 58+ optimizers** in the package that inherit from `AbstractOptimizer`. This includes: | ||
|
|
||
| - **Swarm Intelligence**: ParticleSwarm, AntColony, FireflyAlgorithm, etc. | ||
| - **Evolutionary**: GeneticAlgorithm, DifferentialEvolution, CMAESAlgorithm, etc. | ||
| - **Gradient-Based**: AdamW, SGDMomentum, BFGS, etc. | ||
| - **Metaheuristic**: SimulatedAnnealing, TabuSearch, HarmonySearch, etc. | ||
| - And many more! | ||
|
|
||
| ## Performance Considerations | ||
|
|
||
| - **History Tracking**: Adds `O(max_iter × population_size)` memory overhead | ||
| - **2D Trajectory**: Only available for 2D problems (dim=2) | ||
| - **Large Runs**: For `max_iter > 10000`, consider using `max_points` parameter in `plot_trajectory()` | ||
| - **Stability Tests**: Running N tests with M iterations each requires `N × M` function evaluations | ||
|
|
||
| ## Tips | ||
|
|
||
| 1. **Start Small**: Test with `max_iter=50-100` before running longer optimizations | ||
| 2. **Use Seeds**: Specify seeds for reproducible results in papers/reports | ||
| 3. **Compare Fairly**: Use same `max_iter`, bounds, and function for comparison | ||
| 4. **Check Convergence**: Use log scale to see if optimizer is still improving | ||
| 5. **Population Diversity**: Use `plot_average_fitness()` to monitor exploration vs exploitation | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| **ValueError: "track_history=True"** | ||
| - Ensure optimizer is created with `track_history=True` | ||
|
|
||
| **ValueError: "2D problems"** | ||
| - Trajectory plotting only works for `dim=2` | ||
| - Other plots work for any dimensionality | ||
|
|
||
| **Memory Issues** | ||
| - Reduce `max_iter` or `population_size` | ||
| - Don't track history for production runs | ||
|
|
||
| **Different Results** | ||
| - Ensure same `seed` value for reproducibility | ||
| - Check that function evaluations are deterministic | ||
|
|
||
| ## Citation | ||
|
|
||
| If you use this visualization module in your research, please cite: | ||
|
|
||
| ```bibtex | ||
| @software{useful_optimizer, | ||
| title = {Useful Optimizer: A Collection of Optimization Algorithms}, | ||
| author = {Hahn, Anselm}, | ||
| year = {2024}, | ||
| url = {https://github.com/Anselmoo/useful-optimizer} | ||
| } | ||
| ``` | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The citation year is listed as 2024, but it's currently December 2024 and nearing 2025. Consider using a more accurate year or a dynamic approach. If the package was first released in 2024, this is correct, but if releases continue into 2025, the citation should reflect the version year rather than a hardcoded value.