| | 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_()) |