--[[ An implementation of `DE` (Differential Evolution), ARGS: -`opfunc` : a function that takes a single input (X), the point of evaluation, and returns f(X) and df/dX. Note that df/dX is not used -`x` : the initial point -`state.popsize`: population size. If this is left empty, 10*d will be used -`state.scaleFactor`: float, usually between 0.4 and 1 -`state.crossoverRate`: float, usually between 0.1 and 0.9 -`state.maxEval`: int, maximal number of function evaluations RETURN: - `x*` : the new `x` vector, at the optimal point - `f` : a table of all function values: `f[1]` is the value of the function before any optimization and `f[#f]` is the final fully optimized value, at `x*` ]] require 'torch' function optim.de(opfunc, x, config, state) -- process input parameters local config = config or {} local state = state local popsize = config.popsize -- population size local scaleFactor = config.scaleFactor -- scale factor local crossoverRate = config.crossoverRate -- crossover rate local maxFEs = tonumber(config.maxFEs) -- maximal number of function evaluations local maxRegion = config.maxRegion -- upper bound of search region local minRegion = config.minRegion -- lower bound of search region local xmean = x:clone():view(-1) -- distribution mean, a flattened copy local D = xmean:size(1) -- number of objective variables/problem dimension if config.popsize == nil then popsize = 10 * D end if config.maxRegion == nil then maxRegion = 30 end if config.minRegion == nil then minRegion = -30 end -- Initialize population local fx = x.new(maxFEs) local pop = x.new(popsize, D) local children = x.new(popsize, D) local fitness = x.new(popsize) local children_fitness = x.new(popsize) local fes = 1 -- number of function evaluations local best_fitness local best_solution = x.new(D) -- Initialize population and evaluate the its fitness value local gen = torch.Generator() torch.manualSeed(gen, 1) pop:uniform(gen, minRegion, maxRegion) for i = 1, popsize do fitness[i] = opfunc(pop[i]) fx[fes] = fitness[i] fes = fes + 1 end -- Find the best solution local index best_fitness, index = fitness:max(1) best_fitness = best_fitness[1] index = index[1] best_solution:copy(pop[index]) -- Main loop while fes < maxFEs do local r1, r2 for i = 1, popsize do repeat r1 = torch.random(gen, 1, popsize) until(r1 ~= i) repeat r2 = torch.random(gen, 1, popsize) until(r2 ~= r1 and r2 ~= i) local jrand = torch.random(gen, 1, D) for j = 1, D do if torch.uniform(gen, 0, 1) < crossoverRate or i == jrand then children[i][j] = best_solution[j] + scaleFactor * (pop[r1][j] - pop[r2][j]) else children[i][j] = pop[i][j] end end children_fitness[i] = opfunc(children[i]) fx[fes] = children_fitness[i] fes = fes + 1 end for i = 1, popsize do if children_fitness[i] <= fitness[i] then pop[i]:copy(children[i]) fitness[i] = children_fitness[i] if fitness[i] < best_fitness then best_fitness = fitness[i] best_solution:copy(children[i]) end end end end return best_solution, fx end