| import sys |
| import numpy as np |
| import random |
| from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, |
| QHBoxLayout, QPushButton, QTextEdit, QLabel, |
| QTabWidget, QTableWidget, QTableWidgetItem, |
| QHeaderView, QGroupBox, QSpinBox, QDoubleSpinBox, |
| QFormLayout) |
| from PyQt5.QtCore import Qt, QThread, pyqtSignal |
| import matplotlib.pyplot as plt |
| from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas |
| from matplotlib.figure import Figure |
|
|
| class Particle: |
| def __init__(self, dim, bounds): |
| 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.value = float('inf') |
| |
| def update_velocity(self, global_best_position, w=0.5, c1=1.5, c2=1.5): |
| r1, r2 = random.random(), random.random() |
| cognitive = c1 * r1 * (self.best_position - self.position) |
| social = c2 * r2 * (global_best_position - self.position) |
| self.velocity = w * self.velocity + cognitive + social |
| |
| def update_position(self, bounds): |
| self.position += self.velocity |
| |
| for i in range(len(self.position)): |
| if self.position[i] < bounds[i][0]: |
| self.position[i] = bounds[i][0] |
| elif self.position[i] > bounds[i][1]: |
| self.position[i] = bounds[i][1] |
| |
| def evaluate(self, cost_function): |
| self.value = cost_function(self.position) |
| if self.value < self.best_value: |
| self.best_value = self.value |
| self.best_position = self.position.copy() |
|
|
| class PSOThread(QThread): |
| update_signal = pyqtSignal(str, int, float, list) |
| finished_signal = pyqtSignal(list, list) |
| |
| def __init__(self, cost_function, bounds, num_particles=30, max_iter=100): |
| super().__init__() |
| self.cost_function = cost_function |
| self.bounds = bounds |
| self.num_particles = num_particles |
| self.max_iter = max_iter |
| self.dim = len(bounds) |
| self.running = True |
| |
| def run(self): |
| |
| particles = [Particle(self.dim, self.bounds) for _ in range(self.num_particles)] |
| global_best_position = particles[0].position.copy() |
| global_best_value = float('inf') |
| |
| |
| for particle in particles: |
| particle.evaluate(self.cost_function) |
| if particle.best_value < global_best_value: |
| global_best_value = particle.best_value |
| global_best_position = particle.best_position.copy() |
| |
| |
| iteration_data = [] |
| position_data = [] |
| |
| for iteration in range(self.max_iter): |
| if not self.running: |
| break |
| |
| for particle in particles: |
| particle.update_velocity(global_best_position) |
| particle.update_position(self.bounds) |
| particle.evaluate(self.cost_function) |
| |
| if particle.best_value < global_best_value: |
| global_best_value = particle.best_value |
| global_best_position = particle.best_position.copy() |
| |
| |
| iteration_data.append(iteration + 1) |
| position_data.append(global_best_position.copy()) |
| |
| |
| self.update_signal.emit( |
| f"Iteration {iteration+1}/{self.max_iter}", |
| iteration+1, |
| global_best_value, |
| global_best_position.tolist() |
| ) |
| |
| self.finished_signal.emit(iteration_data, position_data) |
| |
| def stop(self): |
| self.running = False |
|
|
| class CircuitExample: |
| def __init__(self, example_num): |
| self.example_num = example_num |
| self.R1 = example_num |
| self.V_out = example_num |
| self.C1 = self.C2 = 1/(example_num + 1) |
| self.L1 = self.L2 = 0.5 + 0.1 * example_num |
| self.V1 = self.V2 = example_num |
| self.alpha = self.R1 |
| |
| def get_description(self): |
| return f""" |
| Example {self.example_num}: |
| - R1 = {self.R1} Ω |
| - V_out(s) = {self.V_out} Ω |
| - C1 = C2 = {self.C1:.3f}/s Ω |
| - L1 = L2 = {self.L2:.3f}s Ω |
| - V1 = V2 = {self.V1} V |
| - α = {self.alpha} |
| """ |
| |
| def theoretical_impedance(self, s): |
| |
| return self.alpha * s |
| |
| def cost_function(self, x): |
| |
| |
| s_values = [0.1, 0.5, 1.0, 2.0, 5.0] |
| error = 0 |
| for s in s_values: |
| theoretical = self.theoretical_impedance(s) |
| estimated = x[0] * s |
| error += (theoretical - estimated) ** 2 |
| return error |
|
|
| class MplCanvas(FigureCanvas): |
| def __init__(self, parent=None, width=5, height=4, dpi=100): |
| self.fig = Figure(figsize=(width, height), dpi=dpi) |
| super().__init__(self.fig) |
| self.setParent(parent) |
| |
| def plot_convergence(self, iterations, best_values): |
| self.fig.clear() |
| ax = self.fig.add_subplot(111) |
| ax.plot(iterations, best_values, 'b-', linewidth=2) |
| ax.set_xlabel('Iteration') |
| ax.set_ylabel('Best Cost Value') |
| ax.set_title('PSO Convergence') |
| ax.grid(True) |
| self.draw() |
| |
| def plot_parameter_evolution(self, iterations, parameters): |
| self.fig.clear() |
| ax = self.fig.add_subplot(111) |
| for i in range(len(parameters[0])): |
| param_values = [p[i] for p in parameters] |
| ax.plot(iterations, param_values, label=f'Parameter {i+1}') |
| ax.set_xlabel('Iteration') |
| ax.set_ylabel('Parameter Value') |
| ax.set_title('Parameter Evolution') |
| ax.legend() |
| ax.grid(True) |
| self.draw() |
|
|
| class PSOCircuitApp(QMainWindow): |
| def __init__(self): |
| super().__init__() |
| self.examples = [CircuitExample(i+1) for i in range(10)] |
| self.current_example = 0 |
| self.pso_thread = None |
| self.init_ui() |
| |
| def init_ui(self): |
| self.setWindowTitle("PSO Circuit Analysis") |
| self.setGeometry(100, 100, 1200, 800) |
| |
| |
| central_widget = QWidget() |
| self.setCentralWidget(central_widget) |
| main_layout = QHBoxLayout(central_widget) |
| |
| |
| left_panel = QVBoxLayout() |
| |
| |
| example_group = QGroupBox("Circuit Examples") |
| example_layout = QVBoxLayout() |
| |
| self.example_combo = QSpinBox() |
| self.example_combo.setMinimum(1) |
| self.example_combo.setMaximum(10) |
| self.example_combo.valueChanged.connect(self.change_example) |
| |
| self.example_info = QTextEdit() |
| self.example_info.setMaximumHeight(200) |
| self.example_info.setReadOnly(True) |
| |
| example_layout.addWidget(QLabel("Select Example:")) |
| example_layout.addWidget(self.example_combo) |
| example_layout.addWidget(QLabel("Circuit Parameters:")) |
| example_layout.addWidget(self.example_info) |
| example_group.setLayout(example_layout) |
| left_panel.addWidget(example_group) |
| |
| |
| pso_group = QGroupBox("PSO Parameters") |
| pso_layout = QFormLayout() |
| |
| self.num_particles_spin = QSpinBox() |
| self.num_particles_spin.setMinimum(10) |
| self.num_particles_spin.setMaximum(100) |
| self.num_particles_spin.setValue(30) |
| |
| self.max_iter_spin = QSpinBox() |
| self.max_iter_spin.setMinimum(10) |
| self.max_iter_spin.setMaximum(500) |
| self.max_iter_spin.setValue(100) |
| |
| self.w_spin = QDoubleSpinBox() |
| self.w_spin.setMinimum(0.1) |
| self.w_spin.setMaximum(2.0) |
| self.w_spin.setValue(0.5) |
| self.w_spin.setSingleStep(0.1) |
| |
| self.c1_spin = QDoubleSpinBox() |
| self.c1_spin.setMinimum(0.1) |
| self.c1_spin.setMaximum(3.0) |
| self.c1_spin.setValue(1.5) |
| self.c1_spin.setSingleStep(0.1) |
| |
| self.c2_spin = QDoubleSpinBox() |
| self.c2_spin.setMinimum(0.1) |
| self.c2_spin.setMaximum(3.0) |
| self.c2_spin.setValue(1.5) |
| self.c2_spin.setSingleStep(0.1) |
| |
| pso_layout.addRow("Number of Particles:", self.num_particles_spin) |
| pso_layout.addRow("Maximum Iterations:", self.max_iter_spin) |
| pso_layout.addRow("Inertia Weight (w):", self.w_spin) |
| pso_layout.addRow("Cognitive Parameter (c1):", self.c1_spin) |
| pso_layout.addRow("Social Parameter (c2):", self.c2_spin) |
| |
| pso_group.setLayout(pso_layout) |
| left_panel.addWidget(pso_group) |
| |
| |
| self.run_button = QPushButton("Run PSO") |
| self.run_button.clicked.connect(self.run_pso) |
| |
| self.stop_button = QPushButton("Stop PSO") |
| self.stop_button.clicked.connect(self.stop_pso) |
| self.stop_button.setEnabled(False) |
| |
| left_panel.addWidget(self.run_button) |
| left_panel.addWidget(self.stop_button) |
| |
| |
| results_group = QGroupBox("Results") |
| results_layout = QVBoxLayout() |
| |
| self.results_text = QTextEdit() |
| self.results_text.setMaximumHeight(150) |
| self.results_text.setReadOnly(True) |
| |
| results_layout.addWidget(self.results_text) |
| results_group.setLayout(results_layout) |
| left_panel.addWidget(results_group) |
| |
| |
| main_layout.addLayout(left_panel, 1) |
| |
| |
| right_panel = QVBoxLayout() |
| |
| |
| self.plot_tabs = QTabWidget() |
| |
| |
| self.convergence_canvas = MplCanvas(self, width=5, height=4, dpi=100) |
| self.plot_tabs.addTab(self.convergence_canvas, "Convergence") |
| |
| |
| self.param_canvas = MplCanvas(self, width=5, height=4, dpi=100) |
| self.plot_tabs.addTab(self.param_canvas, "Parameter Evolution") |
| |
| right_panel.addWidget(self.plot_tabs) |
| |
| |
| main_layout.addLayout(right_panel, 2) |
| |
| |
| self.change_example(1) |
| |
| def change_example(self, value): |
| self.current_example = value - 1 |
| example = self.examples[self.current_example] |
| self.example_info.setText(example.get_description()) |
| self.results_text.clear() |
| |
| def run_pso(self): |
| example = self.examples[self.current_example] |
| |
| |
| bounds = [(0.1, 2 * example.alpha)] |
| |
| |
| self.pso_thread = PSOThread( |
| example.cost_function, |
| bounds, |
| self.num_particles_spin.value(), |
| self.max_iter_spin.value() |
| ) |
| |
| |
| self.pso_thread.update_signal.connect(self.update_progress) |
| self.pso_thread.finished_signal.connect(self.pso_finished) |
| |
| |
| self.run_button.setEnabled(False) |
| self.stop_button.setEnabled(True) |
| self.results_text.clear() |
| self.results_text.append("Running PSO...") |
| |
| |
| self.pso_thread.start() |
| |
| def stop_pso(self): |
| if self.pso_thread and self.pso_thread.isRunning(): |
| self.pso_thread.stop() |
| self.pso_thread.wait() |
| self.results_text.append("PSO stopped by user.") |
| self.run_button.setEnabled(True) |
| self.stop_button.setEnabled(False) |
| |
| def update_progress(self, status, iteration, best_value, best_position): |
| example = self.examples[self.current_example] |
| self.results_text.clear() |
| self.results_text.append(f"Status: {status}") |
| self.results_text.append(f"Best Cost: {best_value:.6f}") |
| self.results_text.append(f"Estimated α: {best_position[0]:.4f}") |
| self.results_text.append(f"Theoretical α: {example.alpha}") |
| self.results_text.append(f"Error: {abs(best_position[0] - example.alpha):.4f}") |
| |
| def pso_finished(self, iterations, positions): |
| example = self.examples[self.current_example] |
| best_alpha = positions[-1][0] |
| |
| self.results_text.append("\n--- PSO Completed ---") |
| self.results_text.append(f"Final Estimated α: {best_alpha:.4f}") |
| self.results_text.append(f"Theoretical α: {example.alpha}") |
| self.results_text.append(f"Absolute Error: {abs(best_alpha - example.alpha):.4f}") |
| self.results_text.append(f"Relative Error: {abs(best_alpha - example.alpha)/example.alpha*100:.2f}%") |
| |
| |
| best_values = [example.cost_function(p) for p in positions] |
| self.convergence_canvas.plot_convergence(iterations, best_values) |
| |
| |
| self.param_canvas.plot_parameter_evolution(iterations, positions) |
| |
| |
| self.run_button.setEnabled(True) |
| self.stop_button.setEnabled(False) |
|
|
| if __name__ == "__main__": |
| app = QApplication(sys.argv) |
| window = PSOCircuitApp() |
| window.show() |
| sys.exit(app.exec_()) |