从MATLAB到PyTorch和EvoX的转换#
本文档旨在指导MATLAB用户过渡到PyTorch和EvoX进行演化计算。我们将重点介绍MATLAB和PyTorch在语法、数据结构和工作流方面的核心差异。然后,我们将通过在MATLAB和PyTorch中使用粒子群优化(PSO)示例来说明这些差异。
语法差异#
数组创建和索引#
MATLAB#
使用从1开始的索引。
向量和矩阵使用方括号和分号声明(例如,
[1 2 3; 4 5 6]
)。使用rand()
进行随机初始化时,返回的值在区间 \([0, 1)\) 内。切片使用
(start:end)
语法进行,并使用从1开始的索引。
PyTorch#
使用从0开始的索引。
数组(张量)通常使用构造函数创建,例如
torch.rand()
、torch.zeros()
,或将 Python 列表转换为张量的 `torch.tensor()切片使用
[start:end]
,索引从0开始。
矩阵计算#
MATLAB#
通过
*
执行线性代数矩阵乘法。使用
.*
来逐元素相乘相同大小的矩阵。/
表示矩阵右除法。.^
表示元素级幂。张量中长度为1的尾部和头部维度将被忽略。
自动查找元素级操作的可广播维度,并执行隐式维度扩展。
PyTorch#
执行线性代数矩阵乘法通过
@
或 `torch.matmul()直接使用
*
来乘以相同形状或可广播形状的张量的对应元素。/
表示逐元素除法。**
表示元素级幂。张量长度为1的维度被保留并视为广播维度。
防止大多数隐式维度扩展,通常需要广播维度。
函数和定义#
MATLAB#
一个函数由
function
关键字定义。一个文件可以包含多个函数,但通常主要函数与文件名相同。
匿名函数(例如,
@(x) sum(x.^2)
)用于简短的内联计算。
PyTorch#
函数使用
def
关键字定义,通常在一个.py
文件或模块中。类用于以面向对象的方式封装数据和方法。
Lambdas 作为简短的匿名函数使用(
lambda x: x.sum()
),但不允许使用多行 lambdas。
控制流#
MATLAB#
i = 1:N
循环使用1为基准的索引。条件语句如
if
、elseif
和 `else
PyTorch#
使用
for i in range(N):
,采用从0开始的索引。缩进在循环和条件语句中对于确定作用域是重要的(没有
end
关键字)。
抱歉,我需要更多的上下文或具体的文本内容来进行翻译。请提供需要翻译的具体文本。#
MATLAB#
使用
fprintf()
函数进行格式化输出。使用
%
进行单行注释。
PyTorch#
使用
print
和 f-strings 进行格式化输出。使用
#
进行单行注释。
抱歉,我需要更多详细内容才能进行翻译。请提供具体的多行代码或完整的文本内容。#
MATLAB#
使用
...
在行尾表示下一行应视为与当前行相同的一行。
Python#
使用
\
在行尾表示下一行应被视为与当前行相同的一行。如果多行内容在括号内,则不需要特定的结尾符号。
如何通过EvoX编写演化计算算法?#
MATLAB#
以下是PSO算法的MATLAB代码示例:
function [] = example_pso()
pso = init_pso(100, [-10, -10], [10, 10], 0.6, 2.5, 0.8);
test_fn = @(x) (sum(x .* x, 2));
for i = 1:20
pso = step_pso(pso, test_fn);
fprintf("Iteration = %d, global best = %f\n", i, pso.global_best_fitness);
end
end
function [self] = init_pso(pop_size, lb, ub, w, phi_p, phi_g)
self = struct();
self.pop_size = pop_size;
self.dim = length(lb);
self.w = w;
self.phi_p = phi_p;
self.phi_g = phi_g;
% setup
range = ub - lb;
population = rand(self.pop_size, self.dim);
population = range .* population + lb;
velocity = rand(self.pop_size, self.dim);
velocity = 2 .* range .* velocity - range;
self.lb = lb;
self.ub = ub;
% mutable
self.population = population;
self.velocity = velocity;
self.local_best_location = population;
self.local_best_fitness = Inf(self.pop_size, 1);
self.global_best_location = population(1, :);
self.global_best_fitness = Inf;
end
function [self] = step_pso(self, evaluate)
% Evaluate
fitness = evaluate(self.population);
% Update the local best
compare = find(self.local_best_fitness > fitness);
self.local_best_location(compare, :) = self.population(compare, :);
self.local_best_fitness(compare) = fitness(compare);
% Update the global best
values = [self.global_best_location; self.population];
keys = [self.global_best_fitness; fitness];
[min_val, min_index] = min(keys);
self.global_best_location = values(min_index, :);
self.global_best_fitness = min_val;
% Update velocity and position
rg = rand(self.pop_size, self.dim);
rp = rand(self.pop_size, self.dim);
velocity = self.w .* self.velocity ...
+ self.phi_p .* rp .* (self.local_best_location - self.population) ...
+ self.phi_g .* rg .* (self.global_best_location - self.population);
population = self.population + velocity;
self.population = min(max(population, self.lb), self.ub);
self.velocity = min(max(velocity, self.lb), self.ub);
end
在 MATLAB 中,函数 init_pso()
初始化算法,单独的函数 step_pso()
执行一个迭代步骤,主要函数 example_pso()
组织循环。
EvoX#
在EvoX中,你可以通过以下方式构建PSO算法:
首先,建议从 EvoX 和 PyTorch 导入必要的模块和函数。
import torch
from evox.core import *
from evox.utils import *
from evox.workflows import *
from evox.problems.numerical import Sphere
然后,您可以根据“语法差异”部分将MATLAB代码相应地转换为Python代码。
def main():
pso = PSO(pop_size=10, lb=torch.tensor([-10.0, -10.0]), ub=torch.tensor([10.0, 10.0]))
wf = StdWorkflow()
wf.setup(algorithm=pso, problem=Sphere())
for i in range(1, 21):
wf.step()
print(f"Iteration = {i}, global best = {wf.algorithm.global_best_fitness}")
@jit_class
class PSO(Algorithm):
def __init__(self, pop_size, lb, ub, w=0.6, phi_p=2.5, phi_g=0.8):
super().__init__()
self.pop_size = pop_size
self.dim = lb.shape[0]
self.w = w
self.phi_p = phi_p
self.phi_g = phi_g
# setup
lb = lb.unsqueeze(0)
ub = ub.unsqueeze(0)
range = ub - lb
population = torch.rand(self.pop_size, self.dim)
population = range * population + lb
velocity = torch.rand(self.pop_size, self.dim)
velocity = 2 * range * velocity - range
self.lb = lb
self.ub = ub
# mutable
self.population = population
self.velocity = velocity
self.local_best_location = population
self.local_best_fitness = torch.full((self.pop_size,), fill_value=torch.inf)
self.global_best_location = population[0, :]
self.global_best_fitness = torch.tensor(torch.inf)
def step(self):
# Evaluate
fitness = self.evaluate(self.population)
# Update the local best
compare = self.local_best_fitness > fitness
self.local_best_location = torch.where(compare.unsqueeze(1), self.population, self.local_best_location)
self.local_best_fitness = torch.where(compare, fitness, self.local_best_fitness)
# Update the global best
values = torch.cat([self.global_best_location.unsqueeze(0), self.population], dim=0)
keys = torch.cat([self.global_best_fitness.unsqueeze(0), fitness], dim=0)
min_index = torch.argmin(keys)
self.global_best_location = values[min_index]
self.global_best_fitness = keys[min_index]
# Update velocity and position
rg = torch.rand(self.pop_size, self.dim)
rp = torch.rand(self.pop_size, self.dim)
velocity = (
self.w * self.velocity
+ self.phi_p * rp * (self.local_best_location - self.population)
+ self.phi_g * rg * (self.global_best_location - self.population)
)
population = self.population + velocity
self.population = clamp(population, self.lb, self.ub)
self.velocity = clamp(velocity, self.lb, self.ub)
# Run the main function
if __name__ == "__main__":
main()
备注
值得注意的是,我们在MATLAB中使用[]
与;
和,
来沿特定维度连接矩阵和向量;然而,在EvoX中,必须调用torch.cat
并使用参数dim
来指示连接的维度。此外,在PyTorch中,要连接的张量必须具有相同的维数;因此,额外的XXX.unsqueeze(0)
被应用于在第一个维度之前添加一个长度为1的新维度。
在 EvoX 中,PSO 逻辑被封装在一个继承自 Algorithm
的类中。这种面向对象的设计简化了状态管理和迭代,并引入了以下优点:
继承的
evaluate()
方法 你可以简单地调用self.evaluate(self.population)
来计算适应度值,而不是在每次迭代中手动传递你的目标函数。内置工作流集成
通过扩展 Algorithm
,__init__()
在标准的 Python 类构造函数中设置所有主要的 PSO 组件(种群、速度、局部/全局最优等)。