sc: add a hybrid swarm solver alternating the two methods

The swarm solver could run either differential evolution or particle
swarm optimization. Add a third choice that combines them, selected
by the Algorithm property value 2. With the swarm framework in place
the hybrid reuses both engines instead of copying either one.

The hybrid owns one swarm and both engines and alternates them
generation by generation: a particle swarm generation on even counts,
a differential evolution generation that mutates toward the swarm's
best on odd ones. Each generation still costs one fitness evaluation
per particle, the same as either method alone.

Alternating combines the social convergence of particle swarm
optimization with the selection pressure and diversity of
differential evolution. On rugged models with many local optima
it reaches a good solution in less time than particle swarm
optimization alone, and on tightly constrained models it stays
feasible where plain differential evolution does not.

Signed-off-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk>
Change-Id: I01cf454a72a75fe2937b73560b699862fdc3779e
Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/4947
Reviewed-by: Miklos Vajna <vmiklos@collabora.com>
Tested-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
Reviewed-by: Tomaž Vajngerl <tomaz.vajngerl@collabora.com>
(cherry picked from commit 757b50253891e35fd08ef9147431aed5a41ae002)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/209437
Tested-by: Xisco Fauli <xiscofauli@libreoffice.org>
Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org>
This commit is contained in:
Tomaž Vajngerl
2026-09-16 09:44:07 +02:00
committed by Xisco Fauli
parent 7fb5ef1054
commit bdf3a86c44
2 changed files with 78 additions and 0 deletions
+68
View File
@@ -0,0 +1,68 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
*/
#pragma once
#include "Swarm.hxx"
#include "PSOAlgorithm.hxx"
#include "DEAlgorithm.hxx"
#include <vector>
// A hybrid of particle swarm optimization and differential evolution that
// alternates between the two from one generation to the next over a single
// swarm. An even generation runs one full particle swarm generation: inertia
// plus a pull toward each particle's own best and the swarm's best. An odd
// generation runs one full differential evolution generation whose mutant base
// is the swarm's best, kept only when it improves on the particle. Each
// generation costs one fitness evaluation per particle, the same as either
// method alone, so alternating combines the social convergence of particle
// swarm optimization with the selection pressure and diversity of differential
// evolution without doubling the cost.
//
// It owns no algorithm code of its own: it drives the PSOAlgorithm and
// DEAlgorithm engines, both bound to its swarm.
template <typename DataProvider> class DEPSOSolver
{
// differential evolution mutation weight and crossover rate
static constexpr double constDifferentialWeight = 0.6;
static constexpr double constCrossover = 0.9;
Swarm<DataProvider> maSwarm;
PSOAlgorithm<DataProvider> maParticleSwarm;
DEAlgorithm<DataProvider> maDifferential;
public:
DEPSOSolver(DataProvider& rDataProvider, size_t nNumOfParticles)
: maSwarm(rDataProvider, nNumOfParticles)
, maParticleSwarm(maSwarm)
, maDifferential(maSwarm, constDifferentialWeight, constCrossover, DifferentialBound::Clamp,
DifferentialBase::GlobalBest)
{
}
void initialize() { maSwarm.initialize(); }
void restart() { maSwarm.restart(); }
bool next()
{
// Differential evolution needs a difference vector, so fall back to a
// particle swarm generation when there are too few particles.
bool bDifferential = maSwarm.numParticles() >= 3 && (maSwarm.getGeneration() % 2 == 1);
return bDifferential ? maDifferential.next() : maParticleSwarm.next();
}
std::vector<double> const& getResult() const { return maSwarm.getResult(); }
int getGeneration() const { return maSwarm.getGeneration(); }
int getLastChange() const { return maSwarm.getLastChange(); }
double getBestFitness() const { return maSwarm.getBestFitness(); }
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+10
View File
@@ -48,6 +48,7 @@
#include "DifferentialEvolution.hxx"
#include "ParticelSwarmOptimization.hxx"
#include "DEPSOSolver.hxx"
#include "strings.hrc"
@@ -797,6 +798,15 @@ void SAL_CALL SwarmSolver::solve()
aEvolution.setTimeout(mnTimeout);
aSolution = aEvolution.solve();
}
else if (mnAlgorithm == 2)
{
size_t nPopulation = std::clamp<size_t>(10 * nDimensions, 100, 300);
mnSeedCount = nPopulation / 2;
DEPSOSolver<SwarmSolver> aDepso(*this, nPopulation);
SwarmRunner<DEPSOSolver<SwarmSolver>> aRunner(aDepso);
aRunner.setTimeout(mnTimeout);
aSolution = aRunner.solve();
}
else
{
size_t nPopulation = std::clamp<size_t>(10 * nDimensions, 100, 300);