|
|
import sys |
|
|
import numpy as np |
|
|
import matplotlib.pyplot as plt |
|
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas |
|
|
from matplotlib.figure import Figure |
|
|
from mpl_toolkits.mplot3d import Axes3D |
|
|
import random |
|
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, |
|
|
QWidget, QComboBox, QPushButton, QLabel, QSpinBox, |
|
|
QDoubleSpinBox, QGroupBox, QGridLayout, QTextEdit, |
|
|
QSplitter, QProgressBar) |
|
|
from PyQt5.QtCore import QTimer, Qt |
|
|
from PyQt5.QtGui import QFont |
|
|
|
|
|
class Particle: |
|
|
def __init__(self, dim, bounds): |
|
|
self.dim = dim |
|
|
self.position = np.array([random.uniform(bounds[i][0], bounds[i][1]) for i in range(dim)]) |
|
|
self.velocity = np.array([random.uniform(-1, 1) for _ in range(dim)]) |
|
|
self.best_position = self.position.copy() |
|
|
self.best_value = float('inf') |
|
|
self.bounds = bounds |
|
|
|
|
|
def update_velocity(self, global_best_position, w, c1, c2): |
|
|
for i in range(self.dim): |
|
|
r1, r2 = random.random(), random.random() |
|
|
cognitive = c1 * r1 * (self.best_position[i] - self.position[i]) |
|
|
social = c2 * r2 * (global_best_position[i] - self.position[i]) |
|
|
self.velocity[i] = w * self.velocity[i] + cognitive + social |
|
|
|
|
|
def update_position(self): |
|
|
self.position += self.velocity |
|
|
|
|
|
for i in range(self.dim): |
|
|
if self.position[i] < self.bounds[i][0]: |
|
|
self.position[i] = self.bounds[i][0] |
|
|
self.velocity[i] *= -0.5 |
|
|
elif self.position[i] > self.bounds[i][1]: |
|
|
self.position[i] = self.bounds[i][1] |
|
|
self.velocity[i] *= -0.5 |
|
|
|
|
|
class PSO: |
|
|
def __init__(self, objective_func, dim, bounds, num_particles=30, w=0.7, c1=1.4, c2=1.4): |
|
|
self.objective_func = objective_func |
|
|
self.dim = dim |
|
|
self.bounds = bounds |
|
|
self.num_particles = num_particles |
|
|
self.w = w |
|
|
self.c1 = c1 |
|
|
self.c2 = c2 |
|
|
|
|
|
self.particles = [Particle(dim, bounds) for _ in range(num_particles)] |
|
|
self.global_best_position = np.array([random.uniform(bounds[i][0], bounds[i][1]) for i in range(dim)]) |
|
|
self.global_best_value = float('inf') |
|
|
self.history = [] |
|
|
|
|
|
def optimize(self, max_iterations): |
|
|
for iteration in range(max_iterations): |
|
|
for particle in self.particles: |
|
|
|
|
|
value = self.objective_func(particle.position) |
|
|
|
|
|
|
|
|
if value < particle.best_value: |
|
|
particle.best_value = value |
|
|
particle.best_position = particle.position.copy() |
|
|
|
|
|
|
|
|
if value < self.global_best_value: |
|
|
self.global_best_value = value |
|
|
self.global_best_position = particle.position.copy() |
|
|
|
|
|
|
|
|
for particle in self.particles: |
|
|
particle.update_velocity(self.global_best_position, self.w, self.c1, self.c2) |
|
|
particle.update_position() |
|
|
|
|
|
|
|
|
self.history.append({ |
|
|
'positions': [p.position.copy() for p in self.particles], |
|
|
'global_best': self.global_best_position.copy(), |
|
|
'global_best_value': self.global_best_value, |
|
|
'iteration': iteration |
|
|
}) |
|
|
|
|
|
return self.global_best_position, self.global_best_value |
|
|
|
|
|
class EquationDefinitions: |
|
|
@staticmethod |
|
|
def get_equations(): |
|
|
equations = { |
|
|
|
|
|
"Sphere Function": { |
|
|
"func": lambda x: sum(xi**2 for xi in x), |
|
|
"dim": 2, |
|
|
"bounds": [(-5.12, 5.12), (-5.12, 5.12)], |
|
|
"description": "f(x,y) = x² + y²\nMinimum at (0,0)" |
|
|
}, |
|
|
"Rosenbrock Function": { |
|
|
"func": lambda x: 100*(x[1]-x[0]**2)**2 + (1-x[0])**2, |
|
|
"dim": 2, |
|
|
"bounds": [(-2, 2), (-1, 3)], |
|
|
"description": "f(x,y) = 100(y-x²)² + (1-x)²\nMinimum at (1,1)" |
|
|
}, |
|
|
"Rastrigin Function": { |
|
|
"func": lambda x: 20 + sum(xi**2 - 10*np.cos(2*np.pi*xi) for xi in x), |
|
|
"dim": 2, |
|
|
"bounds": [(-5.12, 5.12), (-5.12, 5.12)], |
|
|
"description": "f(x,y) = 20 + x²+y² -10(cos(2πx)+cos(2πy))\nMinimum at (0,0)" |
|
|
}, |
|
|
"Ackley Function": { |
|
|
"func": lambda x: -20*np.exp(-0.2*np.sqrt(0.5*sum(xi**2 for xi in x))) - |
|
|
np.exp(0.5*sum(np.cos(2*np.pi*xi) for xi in x)) + 20 + np.e, |
|
|
"dim": 2, |
|
|
"bounds": [(-5, 5), (-5, 5)], |
|
|
"description": "Complex function with many local minima\nMinimum at (0,0)" |
|
|
}, |
|
|
"Matyas Function": { |
|
|
"func": lambda x: 0.26*(x[0]**2 + x[1]**2) - 0.48*x[0]*x[1], |
|
|
"dim": 2, |
|
|
"bounds": [(-10, 10), (-10, 10)], |
|
|
"description": "f(x,y) = 0.26(x²+y²) - 0.48xy\nMinimum at (0,0)" |
|
|
}, |
|
|
"Himmelblau's Function": { |
|
|
"func": lambda x: (x[0]**2 + x[1] - 11)**2 + (x[0] + x[1]**2 - 7)**2, |
|
|
"dim": 2, |
|
|
"bounds": [(-5, 5), (-5, 5)], |
|
|
"description": "f(x,y) = (x²+y-11)² + (x+y²-7)²\n4 minima at (3,2), (-2.8,3.1), (-3.8,-3.3), (3.6,-1.8)" |
|
|
}, |
|
|
"Three-Hump Camel": { |
|
|
"func": lambda x: 2*x[0]**2 - 1.05*x[0]**4 + x[0]**6/6 + x[0]*x[1] + x[1]**2, |
|
|
"dim": 2, |
|
|
"bounds": [(-5, 5), (-5, 5)], |
|
|
"description": "f(x,y) = 2x² -1.05x⁴ + x⁶/6 + xy + y²\nMinimum at (0,0)" |
|
|
}, |
|
|
"Easom Function": { |
|
|
"func": lambda x: -np.cos(x[0])*np.cos(x[1])*np.exp(-((x[0]-np.pi)**2 + (x[1]-np.pi)**2)), |
|
|
"dim": 2, |
|
|
"bounds": [(-10, 10), (-10, 10)], |
|
|
"description": "f(x,y) = -cos(x)cos(y)exp(-((x-π)²+(y-π)²))\nMinimum at (π,π)" |
|
|
}, |
|
|
"Cross-in-Tray": { |
|
|
"func": lambda x: -0.0001*(abs(np.sin(x[0])*np.sin(x[1])*np.exp(abs(100-np.sqrt(x[0]**2+x[1]**2)/np.pi))) + 1)**0.1, |
|
|
"dim": 2, |
|
|
"bounds": [(-10, 10), (-10, 10)], |
|
|
"description": "Multiple global minima in cross pattern" |
|
|
}, |
|
|
"Holder Table": { |
|
|
"func": lambda x: -abs(np.sin(x[0])*np.cos(x[1])*np.exp(abs(1-np.sqrt(x[0]**2+x[1]**2)/np.pi))), |
|
|
"dim": 2, |
|
|
"bounds": [(-10, 10), (-10, 10)], |
|
|
"description": "Multiple minima in table-like pattern" |
|
|
}, |
|
|
|
|
|
|
|
|
"Sphere 3D": { |
|
|
"func": lambda x: sum(xi**2 for xi in x), |
|
|
"dim": 3, |
|
|
"bounds": [(-5.12, 5.12), (-5.12, 5.12), (-5.12, 5.12)], |
|
|
"description": "f(x,y,z) = x² + y² + z²\nMinimum at (0,0,0)" |
|
|
}, |
|
|
"Rosenbrock 3D": { |
|
|
"func": lambda x: sum(100*(x[i+1]-x[i]**2)**2 + (1-x[i])**2 for i in range(len(x)-1)), |
|
|
"dim": 3, |
|
|
"bounds": [(-2, 2), (-2, 2), (-2, 2)], |
|
|
"description": "3D extension of Rosenbrock\nMinimum at (1,1,1)" |
|
|
}, |
|
|
"Rastrigin 3D": { |
|
|
"func": lambda x: 30 + sum(xi**2 - 10*np.cos(2*np.pi*xi) for xi in x), |
|
|
"dim": 3, |
|
|
"bounds": [(-5.12, 5.12), (-5.12, 5.12), (-5.12, 5.12)], |
|
|
"description": "3D Rastrigin function\nMinimum at (0,0,0)" |
|
|
}, |
|
|
"Ackley 3D": { |
|
|
"func": lambda x: -20*np.exp(-0.2*np.sqrt(1/3*sum(xi**2 for xi in x))) - |
|
|
np.exp(1/3*sum(np.cos(2*np.pi*xi) for xi in x)) + 20 + np.e, |
|
|
"dim": 3, |
|
|
"bounds": [(-5, 5), (-5, 5), (-5, 5)], |
|
|
"description": "3D Ackley function\nMinimum at (0,0,0)" |
|
|
}, |
|
|
"Sum of Different Powers": { |
|
|
"func": lambda x: sum(abs(xi)**(i+2) for i, xi in enumerate(x)), |
|
|
"dim": 3, |
|
|
"bounds": [(-1, 1), (-1, 1), (-1, 1)], |
|
|
"description": "f(x,y,z) = |x|² + |y|³ + |z|⁴\nMinimum at (0,0,0)" |
|
|
}, |
|
|
"Rotated Hyper-Ellipsoid": { |
|
|
"func": lambda x: sum(sum(x[j]**2 for j in range(i+1)) for i in range(len(x))), |
|
|
"dim": 3, |
|
|
"bounds": [(-5.12, 5.12), (-5.12, 5.12), (-5.12, 5.12)], |
|
|
"description": "f(x,y,z) = x² + (x²+y²) + (x²+y²+z²)\nMinimum at (0,0,0)" |
|
|
}, |
|
|
"Zakharov 3D": { |
|
|
"func": lambda x: sum(xi**2 for xi in x) + (0.5*sum(i*xi for i, xi in enumerate(x, 1)))**2 + (0.5*sum(i*xi for i, xi in enumerate(x, 1)))**4, |
|
|
"dim": 3, |
|
|
"bounds": [(-5, 10), (-5, 10), (-5, 10)], |
|
|
"description": "Zakharov function in 3D\nMinimum at (0,0,0)" |
|
|
}, |
|
|
"Dixon-Price": { |
|
|
"func": lambda x: (x[0]-1)**2 + sum(i*(2*x[i]**2 - x[i-1])**2 for i in range(1, len(x))), |
|
|
"dim": 3, |
|
|
"bounds": [(-10, 10), (-10, 10), (-10, 10)], |
|
|
"description": "Dixon-Price function\nMinimum depends on dimension" |
|
|
}, |
|
|
"Levy 3D": { |
|
|
"func": lambda x: ( |
|
|
np.sin(np.pi * (1 + (x[0] - 1) / 4))**2 + |
|
|
sum( |
|
|
((1 + (x[i] - 1) / 4 - 1)**2 * |
|
|
(1 + 10 * np.sin(np.pi * (1 + (x[i] - 1) / 4) + 1)**2)) |
|
|
for i in range(len(x) - 1) |
|
|
) + |
|
|
((1 + (x[-1] - 1) / 4 - 1)**2 * |
|
|
(1 + np.sin(2 * np.pi * (1 + (x[-1] - 1) / 4))**2)) |
|
|
), |
|
|
"dim": 3, |
|
|
"bounds": [(-10, 10), (-10, 10), (-10, 10)], |
|
|
"description": "Levy function in 3D\nMinimum at (1,1,1)" |
|
|
}, |
|
|
"Michalewicz 3D": { |
|
|
"func": lambda x: -sum(np.sin(x[i]) * np.sin((i+1)*x[i]**2/np.pi)**20 for i in range(len(x))), |
|
|
"dim": 3, |
|
|
"bounds": [(0, np.pi), (0, np.pi), (0, np.pi)], |
|
|
"description": "Michalewicz function\nMany local minima, hard global optimization" |
|
|
} |
|
|
} |
|
|
return equations |
|
|
|
|
|
class PlotCanvas(FigureCanvas): |
|
|
def __init__(self, parent=None, width=5, height=4, dpi=100, is_3d=False): |
|
|
self.fig = Figure(figsize=(width, height), dpi=dpi) |
|
|
super().__init__(self.fig) |
|
|
self.setParent(parent) |
|
|
self.is_3d = is_3d |
|
|
|
|
|
if is_3d: |
|
|
self.ax = self.fig.add_subplot(111, projection='3d') |
|
|
else: |
|
|
self.ax = self.fig.add_subplot(111) |
|
|
|
|
|
self.ax.grid(True, alpha=0.3) |
|
|
|
|
|
def plot_optimization(self, equation_info, particles_history, current_iteration): |
|
|
self.ax.clear() |
|
|
|
|
|
if current_iteration >= len(particles_history): |
|
|
return |
|
|
|
|
|
current_data = particles_history[current_iteration] |
|
|
positions = current_data['positions'] |
|
|
|
|
|
if equation_info['dim'] == 2: |
|
|
self._plot_2d(equation_info, positions, current_data) |
|
|
else: |
|
|
if self.is_3d: |
|
|
self._plot_3d(equation_info, positions, current_data) |
|
|
else: |
|
|
self._plot_3d_projection(equation_info, positions, current_data) |
|
|
|
|
|
self.ax.set_title(f'Iteration {current_iteration + 1}\nBest Value: {current_data["global_best_value"]:.6f}') |
|
|
self.draw() |
|
|
|
|
|
def _plot_2d(self, equation_info, positions, current_data): |
|
|
|
|
|
bounds = equation_info['bounds'] |
|
|
x = np.linspace(bounds[0][0], bounds[0][1], 100) |
|
|
y = np.linspace(bounds[1][0], bounds[1][1], 100) |
|
|
X, Y = np.meshgrid(x, y) |
|
|
Z = np.array([[equation_info['func']([xi, yi]) for xi in x] for yi in y]) |
|
|
|
|
|
|
|
|
contour = self.ax.contour(X, Y, Z, levels=20, alpha=0.6) |
|
|
self.ax.clabel(contour, inline=True, fontsize=8) |
|
|
|
|
|
|
|
|
particle_x = [p[0] for p in positions] |
|
|
particle_y = [p[1] for p in positions] |
|
|
self.ax.scatter(particle_x, particle_y, c='red', s=30, alpha=0.7, label='Particles') |
|
|
|
|
|
|
|
|
self.ax.scatter(current_data['global_best'][0], current_data['global_best'][1], |
|
|
c='green', s=100, marker='*', label='Global Best') |
|
|
|
|
|
self.ax.set_xlabel('X') |
|
|
self.ax.set_ylabel('Y') |
|
|
self.ax.legend() |
|
|
|
|
|
def _plot_3d(self, equation_info, positions, current_data): |
|
|
bounds = equation_info['bounds'] |
|
|
x = np.linspace(bounds[0][0], bounds[0][1], 30) |
|
|
y = np.linspace(bounds[1][0], bounds[1][1], 30) |
|
|
X, Y = np.meshgrid(x, y) |
|
|
|
|
|
|
|
|
if len(positions[0]) == 3: |
|
|
fixed_z = current_data['global_best'][2] |
|
|
Z = np.array([[equation_info['func']([xi, yi, fixed_z]) for xi in x] for yi in y]) |
|
|
|
|
|
|
|
|
self.ax.plot_surface(X, Y, Z, cmap='viridis', alpha=0.6) |
|
|
|
|
|
|
|
|
particle_x = [p[0] for p in positions] |
|
|
particle_y = [p[1] for p in positions] |
|
|
particle_z = [equation_info['func']([p[0], p[1], fixed_z]) for p in positions] |
|
|
self.ax.scatter(particle_x, particle_y, particle_z, c='red', s=30, alpha=0.7, label='Particles') |
|
|
|
|
|
|
|
|
best_x, best_y = current_data['global_best'][0], current_data['global_best'][1] |
|
|
best_z = equation_info['func']([best_x, best_y, fixed_z]) |
|
|
self.ax.scatter([best_x], [best_y], [best_z], c='green', s=100, marker='*', label='Global Best') |
|
|
|
|
|
self.ax.set_xlabel('X') |
|
|
self.ax.set_ylabel('Y') |
|
|
self.ax.set_zlabel('f(X,Y)') |
|
|
|
|
|
self.ax.legend() |
|
|
|
|
|
def _plot_3d_projection(self, equation_info, positions, current_data): |
|
|
"""2D projection of 3D function by fixing one dimension""" |
|
|
bounds = equation_info['bounds'] |
|
|
|
|
|
|
|
|
best_pos = current_data['global_best'] |
|
|
|
|
|
|
|
|
x = np.linspace(bounds[0][0], bounds[0][1], 100) |
|
|
y = np.linspace(bounds[1][0], bounds[1][1], 100) |
|
|
X, Y = np.meshgrid(x, y) |
|
|
|
|
|
|
|
|
fixed_z = best_pos[2] if len(best_pos) > 2 else 0 |
|
|
Z = np.array([[equation_info['func']([xi, yi, fixed_z]) for xi in x] for yi in y]) |
|
|
|
|
|
|
|
|
contour = self.ax.contour(X, Y, Z, levels=20, alpha=0.6) |
|
|
self.ax.clabel(contour, inline=True, fontsize=8) |
|
|
|
|
|
|
|
|
particle_x = [p[0] for p in positions] |
|
|
particle_y = [p[1] for p in positions] |
|
|
self.ax.scatter(particle_x, particle_y, c='red', s=30, alpha=0.7, label='Particles') |
|
|
|
|
|
|
|
|
self.ax.scatter(best_pos[0], best_pos[1], c='green', s=100, marker='*', label='Global Best') |
|
|
|
|
|
self.ax.set_xlabel('X') |
|
|
self.ax.set_ylabel('Y') |
|
|
self.ax.set_title(f'3D Function Projection (Z fixed at {fixed_z:.3f})') |
|
|
self.ax.legend() |
|
|
|
|
|
class PSOApp(QMainWindow): |
|
|
def __init__(self): |
|
|
super().__init__() |
|
|
self.equations = EquationDefinitions.get_equations() |
|
|
self.current_pso = None |
|
|
self.current_iteration = 0 |
|
|
self.timer = QTimer() |
|
|
self.timer.timeout.connect(self.update_visualization) |
|
|
|
|
|
self.init_ui() |
|
|
|
|
|
def init_ui(self): |
|
|
self.setWindowTitle("Particle Swarm Optimization - 20 Equations Solver") |
|
|
self.setGeometry(100, 100, 1600, 1000) |
|
|
|
|
|
|
|
|
central_widget = QWidget() |
|
|
self.setCentralWidget(central_widget) |
|
|
|
|
|
|
|
|
main_layout = QHBoxLayout(central_widget) |
|
|
|
|
|
|
|
|
left_panel = QWidget() |
|
|
left_panel.setMaximumWidth(400) |
|
|
left_layout = QVBoxLayout(left_panel) |
|
|
|
|
|
|
|
|
equation_group = QGroupBox("Equation Selection") |
|
|
equation_layout = QVBoxLayout(equation_group) |
|
|
|
|
|
self.equation_combo = QComboBox() |
|
|
self.equation_combo.addItems(self.equations.keys()) |
|
|
self.equation_combo.currentTextChanged.connect(self.on_equation_changed) |
|
|
equation_layout.addWidget(QLabel("Select Equation:")) |
|
|
equation_layout.addWidget(self.equation_combo) |
|
|
|
|
|
self.equation_desc = QTextEdit() |
|
|
self.equation_desc.setMaximumHeight(100) |
|
|
self.equation_desc.setReadOnly(True) |
|
|
equation_layout.addWidget(QLabel("Description:")) |
|
|
equation_layout.addWidget(self.equation_desc) |
|
|
|
|
|
left_layout.addWidget(equation_group) |
|
|
|
|
|
|
|
|
params_group = QGroupBox("PSO Parameters") |
|
|
params_layout = QGridLayout(params_group) |
|
|
|
|
|
params_layout.addWidget(QLabel("Particles:"), 0, 0) |
|
|
self.particles_spin = QSpinBox() |
|
|
self.particles_spin.setRange(10, 100) |
|
|
self.particles_spin.setValue(30) |
|
|
params_layout.addWidget(self.particles_spin, 0, 1) |
|
|
|
|
|
params_layout.addWidget(QLabel("Iterations:"), 1, 0) |
|
|
self.iterations_spin = QSpinBox() |
|
|
self.iterations_spin.setRange(10, 500) |
|
|
self.iterations_spin.setValue(100) |
|
|
params_layout.addWidget(self.iterations_spin, 1, 1) |
|
|
|
|
|
params_layout.addWidget(QLabel("Inertia (w):"), 2, 0) |
|
|
self.w_spin = QDoubleSpinBox() |
|
|
self.w_spin.setRange(0.1, 1.0) |
|
|
self.w_spin.setSingleStep(0.1) |
|
|
self.w_spin.setValue(0.7) |
|
|
params_layout.addWidget(self.w_spin, 2, 1) |
|
|
|
|
|
params_layout.addWidget(QLabel("Cognitive (c1):"), 3, 0) |
|
|
self.c1_spin = QDoubleSpinBox() |
|
|
self.c1_spin.setRange(0.1, 2.0) |
|
|
self.c1_spin.setSingleStep(0.1) |
|
|
self.c1_spin.setValue(1.4) |
|
|
params_layout.addWidget(self.c1_spin, 3, 1) |
|
|
|
|
|
params_layout.addWidget(QLabel("Social (c2):"), 4, 0) |
|
|
self.c2_spin = QDoubleSpinBox() |
|
|
self.c2_spin.setRange(0.1, 2.0) |
|
|
self.c2_spin.setSingleStep(0.1) |
|
|
self.c2_spin.setValue(1.4) |
|
|
params_layout.addWidget(self.c2_spin, 4, 1) |
|
|
|
|
|
left_layout.addWidget(params_group) |
|
|
|
|
|
|
|
|
control_group = QGroupBox("Controls") |
|
|
control_layout = QVBoxLayout(control_group) |
|
|
|
|
|
self.run_button = QPushButton("Run PSO") |
|
|
self.run_button.clicked.connect(self.run_pso) |
|
|
control_layout.addWidget(self.run_button) |
|
|
|
|
|
self.pause_button = QPushButton("Pause") |
|
|
self.pause_button.clicked.connect(self.toggle_pause) |
|
|
self.pause_button.setEnabled(False) |
|
|
control_layout.addWidget(self.pause_button) |
|
|
|
|
|
self.step_button = QPushButton("Step") |
|
|
self.step_button.clicked.connect(self.step_forward) |
|
|
self.step_button.setEnabled(False) |
|
|
control_layout.addWidget(self.step_button) |
|
|
|
|
|
self.reset_button = QPushButton("Reset") |
|
|
self.reset_button.clicked.connect(self.reset) |
|
|
control_layout.addWidget(self.reset_button) |
|
|
|
|
|
left_layout.addWidget(control_group) |
|
|
|
|
|
|
|
|
progress_group = QGroupBox("Progress") |
|
|
progress_layout = QVBoxLayout(progress_group) |
|
|
|
|
|
self.progress_bar = QProgressBar() |
|
|
self.progress_bar.setValue(0) |
|
|
progress_layout.addWidget(self.progress_bar) |
|
|
|
|
|
self.status_label = QLabel("Ready to optimize") |
|
|
progress_layout.addWidget(self.status_label) |
|
|
|
|
|
self.results_text = QTextEdit() |
|
|
self.results_text.setMaximumHeight(150) |
|
|
self.results_text.setReadOnly(True) |
|
|
progress_layout.addWidget(self.results_text) |
|
|
|
|
|
left_layout.addWidget(progress_group) |
|
|
left_layout.addStretch() |
|
|
|
|
|
|
|
|
right_panel = QWidget() |
|
|
right_layout = QVBoxLayout(right_panel) |
|
|
|
|
|
|
|
|
splitter = QSplitter(Qt.Vertical) |
|
|
|
|
|
self.plot_2d = PlotCanvas(self, width=8, height=6, dpi=100, is_3d=False) |
|
|
self.plot_3d = PlotCanvas(self, width=8, height=6, dpi=100, is_3d=True) |
|
|
|
|
|
splitter.addWidget(self.plot_2d) |
|
|
splitter.addWidget(self.plot_3d) |
|
|
splitter.setSizes([500, 500]) |
|
|
|
|
|
right_layout.addWidget(splitter) |
|
|
|
|
|
|
|
|
main_layout.addWidget(left_panel) |
|
|
main_layout.addWidget(right_panel) |
|
|
|
|
|
|
|
|
self.on_equation_changed(self.equation_combo.currentText()) |
|
|
|
|
|
def on_equation_changed(self, equation_name): |
|
|
equation_info = self.equations[equation_name] |
|
|
self.equation_desc.setText(equation_info['description']) |
|
|
|
|
|
def run_pso(self): |
|
|
try: |
|
|
equation_name = self.equation_combo.currentText() |
|
|
equation_info = self.equations[equation_name] |
|
|
|
|
|
|
|
|
num_particles = self.particles_spin.value() |
|
|
max_iterations = self.iterations_spin.value() |
|
|
w = self.w_spin.value() |
|
|
c1 = self.c1_spin.value() |
|
|
c2 = self.c2_spin.value() |
|
|
|
|
|
|
|
|
self.current_pso = PSO( |
|
|
objective_func=equation_info['func'], |
|
|
dim=equation_info['dim'], |
|
|
bounds=equation_info['bounds'], |
|
|
num_particles=num_particles, |
|
|
w=w, c1=c1, c2=c2 |
|
|
) |
|
|
|
|
|
|
|
|
best_position, best_value = self.current_pso.optimize(max_iterations) |
|
|
|
|
|
|
|
|
self.results_text.setText( |
|
|
f"Optimization Complete!\n" |
|
|
f"Best Position: {[f'{x:.6f}' for x in best_position]}\n" |
|
|
f"Best Value: {best_value:.10f}\n" |
|
|
f"Equation: {equation_name}" |
|
|
) |
|
|
|
|
|
|
|
|
self.current_iteration = 0 |
|
|
self.progress_bar.setMaximum(max_iterations - 1) |
|
|
self.update_visualization() |
|
|
|
|
|
|
|
|
self.pause_button.setEnabled(True) |
|
|
self.step_button.setEnabled(True) |
|
|
self.run_button.setEnabled(False) |
|
|
|
|
|
|
|
|
self.timer.start(100) |
|
|
|
|
|
except Exception as e: |
|
|
self.results_text.setText(f"Error during optimization: {str(e)}") |
|
|
|
|
|
def toggle_pause(self): |
|
|
if self.timer.isActive(): |
|
|
self.timer.stop() |
|
|
self.pause_button.setText("Resume") |
|
|
else: |
|
|
self.timer.start(100) |
|
|
self.pause_button.setText("Pause") |
|
|
|
|
|
def step_forward(self): |
|
|
if self.current_pso and self.current_iteration < len(self.current_pso.history) - 1: |
|
|
self.current_iteration += 1 |
|
|
self.update_visualization() |
|
|
|
|
|
def reset(self): |
|
|
self.timer.stop() |
|
|
self.current_pso = None |
|
|
self.current_iteration = 0 |
|
|
self.progress_bar.setValue(0) |
|
|
self.status_label.setText("Ready to optimize") |
|
|
self.results_text.clear() |
|
|
self.pause_button.setEnabled(False) |
|
|
self.step_button.setEnabled(False) |
|
|
self.run_button.setEnabled(True) |
|
|
self.pause_button.setText("Pause") |
|
|
|
|
|
|
|
|
self.plot_2d.ax.clear() |
|
|
self.plot_3d.ax.clear() |
|
|
self.plot_2d.draw() |
|
|
self.plot_3d.draw() |
|
|
|
|
|
def update_visualization(self): |
|
|
if not self.current_pso or self.current_iteration >= len(self.current_pso.history): |
|
|
self.timer.stop() |
|
|
self.status_label.setText("Optimization Complete!") |
|
|
return |
|
|
|
|
|
equation_name = self.equation_combo.currentText() |
|
|
equation_info = self.equations[equation_name] |
|
|
|
|
|
try: |
|
|
|
|
|
self.plot_2d.plot_optimization(equation_info, self.current_pso.history, self.current_iteration) |
|
|
|
|
|
|
|
|
self.plot_3d.plot_optimization(equation_info, self.current_pso.history, self.current_iteration) |
|
|
|
|
|
|
|
|
self.progress_bar.setValue(self.current_iteration) |
|
|
self.status_label.setText(f"Iteration {self.current_iteration + 1}/{len(self.current_pso.history)}") |
|
|
|
|
|
self.current_iteration += 1 |
|
|
|
|
|
if self.current_iteration >= len(self.current_pso.history): |
|
|
self.timer.stop() |
|
|
self.status_label.setText("Optimization Complete!") |
|
|
|
|
|
except Exception as e: |
|
|
self.status_label.setText(f"Visualization error: {str(e)}") |
|
|
self.timer.stop() |
|
|
|
|
|
def main(): |
|
|
app = QApplication(sys.argv) |
|
|
app.setStyle('Fusion') |
|
|
|
|
|
|
|
|
font = QFont("Arial", 10) |
|
|
app.setFont(font) |
|
|
|
|
|
window = PSOApp() |
|
|
window.show() |
|
|
|
|
|
sys.exit(app.exec_()) |
|
|
|
|
|
if __name__ == '__main__': |
|
|
main() |