Refactored namespaces

This commit is contained in:
emeric
2024-03-12 08:32:08 +01:00
parent 487960b413
commit 4b7c4295ec
501 changed files with 12605 additions and 12631 deletions
@@ -19,48 +19,48 @@
#include <numeric>
#include "utils/Random.hpp"
#include "core/Random.hpp"
#include "ParallelFor.hpp"
template<typename Individual>
class GeneticAlgorithm
{
public:
using Score = float;
public:
using Score = float;
using BreedFunction = std::function<Individual(const Individual&, const Individual&)>;
using MutateFunction = std::function<void(Individual&)>;
using ScoreFunction = std::function<Score(const Individual&)>;
using BreedFunction = std::function<Individual(const Individual&, const Individual&)>;
using MutateFunction = std::function<void(Individual&)>;
using ScoreFunction = std::function<Score(const Individual&)>;
struct Params
{
std::size_t nbWorkers {1};
std::size_t nbGenerations;
float crossoverRatio {0.5};
float mutationProbability {0.05};
BreedFunction breedFunction;
MutateFunction mutateFunction;
ScoreFunction scoreFunction;
};
struct Params
{
std::size_t nbWorkers {1};
std::size_t nbGenerations;
float crossoverRatio {0.5};
float mutationProbability {0.05};
BreedFunction breedFunction;
MutateFunction mutateFunction;
ScoreFunction scoreFunction;
};
GeneticAlgorithm(const Params& params);
GeneticAlgorithm(const Params& params);
// Returns the individual that has the maximum score after processing the requested generations
Individual simulate(const std::vector<Individual>& initialPopulation);
// Returns the individual that has the maximum score after processing the requested generations
Individual simulate(const std::vector<Individual>& initialPopulation);
private:
struct ScoredIndividual
{
Individual individual;
std::optional<Score> score {};
};
private:
struct ScoredIndividual
{
Individual individual;
std::optional<Score> score {};
};
void scoreAndSortPopulation(std::vector<ScoredIndividual>& population);
Score getTotalScore(const std::vector<ScoredIndividual>& population) const;
typename std::vector<ScoredIndividual>::const_iterator pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population, Score totalScore);
void scoreAndSortPopulation(std::vector<ScoredIndividual>& population);
Score getTotalScore(const std::vector<ScoredIndividual>& population) const;
typename std::vector<ScoredIndividual>::const_iterator pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population, Score totalScore);
Params _params;
Params _params;
};
template<typename Individual>
@@ -74,60 +74,60 @@ template<typename Individual>
Individual
GeneticAlgorithm<Individual>::simulate(const std::vector<Individual>& initialPopulation)
{
const std::size_t childrenCountPerGeneration {static_cast<std::size_t>(initialPopulation.size() * _params.crossoverRatio)};
if (initialPopulation.size() < 10)
throw std::runtime_error("Initial population must has at least 10 elements");
const std::size_t childrenCountPerGeneration {static_cast<std::size_t>(initialPopulation.size() * _params.crossoverRatio)};
if (initialPopulation.size() < 10)
throw std::runtime_error("Initial population must has at least 10 elements");
std::vector<ScoredIndividual> scoredPopulation;
scoredPopulation.reserve(initialPopulation.size());
std::vector<ScoredIndividual> scoredPopulation;
scoredPopulation.reserve(initialPopulation.size());
std::transform(std::cbegin(initialPopulation), std::cend(initialPopulation), std::back_inserter(scoredPopulation ),
[](const Individual& individual) { return ScoredIndividual {individual};});
std::transform(std::cbegin(initialPopulation), std::cend(initialPopulation), std::back_inserter(scoredPopulation ),
[](const Individual& individual) { return ScoredIndividual {individual};});
scoreAndSortPopulation(scoredPopulation);
scoreAndSortPopulation(scoredPopulation);
for (std::size_t currentGeneration {}; currentGeneration < _params.nbGenerations; ++currentGeneration)
{
assert(scoredPopulation.size() == initialPopulation.size());
std::cout << "Processing generation " << currentGeneration << "..." << std::endl;
std::cout << "Need to create " << childrenCountPerGeneration << " new children" << std::endl;
for (std::size_t currentGeneration {}; currentGeneration < _params.nbGenerations; ++currentGeneration)
{
assert(scoredPopulation.size() == initialPopulation.size());
std::cout << "Processing generation " << currentGeneration << "..." << std::endl;
std::cout << "Need to create " << childrenCountPerGeneration << " new children" << std::endl;
// breed
const Score populationTotalScore {getTotalScore(scoredPopulation)};
std::vector<ScoredIndividual> children;
children.reserve(childrenCountPerGeneration);
// breed
const Score populationTotalScore {getTotalScore(scoredPopulation)};
std::vector<ScoredIndividual> children;
children.reserve(childrenCountPerGeneration);
while (children.size() < childrenCountPerGeneration)
{
// Select two random parents using their score as weight
const auto itParent1 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
const auto itParent2 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
while (children.size() < childrenCountPerGeneration)
{
// Select two random parents using their score as weight
const auto itParent1 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
const auto itParent2 {pickRandomRouletteWheel(scoredPopulation, populationTotalScore)};
if (itParent1 == itParent2)
continue;
if (itParent1 == itParent2)
continue;
ScoredIndividual child {_params.breedFunction(itParent1->individual, itParent2->individual)};
if (Random::getRealRandom(float {}, float {1}) <= _params.mutationProbability)
_params.mutateFunction(child.individual);
ScoredIndividual child {_params.breedFunction(itParent1->individual, itParent2->individual)};
if (core::random::getRealRandom(float {}, float {1}) <= _params.mutationProbability)
_params.mutateFunction(child.individual);
children.emplace_back(std::move(child));
}
children.emplace_back(std::move(child));
}
// Elitist selection
scoredPopulation.resize(initialPopulation.size() - childrenCountPerGeneration);
// Elitist selection
scoredPopulation.resize(initialPopulation.size() - childrenCountPerGeneration);
scoredPopulation.insert(std::end(scoredPopulation), std::make_move_iterator(std::begin(children)), std::make_move_iterator(std::end(children)));
assert(scoredPopulation.size() == initialPopulation.size());
scoredPopulation.insert(std::end(scoredPopulation), std::make_move_iterator(std::begin(children)), std::make_move_iterator(std::end(children)));
assert(scoredPopulation.size() == initialPopulation.size());
scoreAndSortPopulation(scoredPopulation);
scoreAndSortPopulation(scoredPopulation);
std::cout << "Mean score = " << getTotalScore(scoredPopulation) / scoredPopulation.size() << std::endl;
std::cout << "Current best score = " << *scoredPopulation.front().score << std::endl;
}
std::cout << "Mean score = " << getTotalScore(scoredPopulation) / scoredPopulation.size() << std::endl;
std::cout << "Current best score = " << *scoredPopulation.front().score << std::endl;
}
std::cout << "Best score = " << *scoredPopulation.front().score << std::endl;
return scoredPopulation.front().individual;
std::cout << "Best score = " << *scoredPopulation.front().score << std::endl;
return scoredPopulation.front().individual;
}
@@ -135,39 +135,37 @@ template<typename Individual>
void
GeneticAlgorithm<Individual>::scoreAndSortPopulation(std::vector<ScoredIndividual>& scoredPopulation)
{
parallel_foreach(_params.nbWorkers, std::begin(scoredPopulation), std::end(scoredPopulation),
[&](ScoredIndividual& scoredIndividual)
{
if (!scoredIndividual.score)
scoredIndividual.score = _params.scoreFunction(scoredIndividual.individual);
});
parallel_foreach(_params.nbWorkers, std::begin(scoredPopulation), std::end(scoredPopulation),
[&](ScoredIndividual& scoredIndividual)
{
if (!scoredIndividual.score)
scoredIndividual.score = _params.scoreFunction(scoredIndividual.individual);
});
std::sort(std::begin(scoredPopulation), std::end(scoredPopulation), [](const ScoredIndividual& a, const ScoredIndividual& b) { return a.score > b.score; });
std::sort(std::begin(scoredPopulation), std::end(scoredPopulation), [](const ScoredIndividual& a, const ScoredIndividual& b) { return a.score > b.score; });
}
template<typename Individual>
typename GeneticAlgorithm<Individual>::Score
GeneticAlgorithm<Individual>::getTotalScore(const std::vector<ScoredIndividual>& scoredPopulation) const
{
return std::accumulate(std::cbegin(scoredPopulation), std::cend(scoredPopulation), Score {}, [](Score score, const ScoredIndividual& individual) { return score + *individual.score; });
return std::accumulate(std::cbegin(scoredPopulation), std::cend(scoredPopulation), Score {}, [](Score score, const ScoredIndividual& individual) { return score + *individual.score; });
}
template<typename Individual>
typename std::vector<typename GeneticAlgorithm<Individual>::ScoredIndividual>::const_iterator
GeneticAlgorithm<Individual>::pickRandomRouletteWheel(const std::vector<ScoredIndividual>& population, Score totalScore)
{
const Score randomScore {Random::getRealRandom(Score {}, totalScore)};
const Score randomScore {core::random::getRealRandom(Score {}, totalScore)};
Score curScore{};
for (auto itScoredIndividual {std::cbegin(population)}; itScoredIndividual != std::cend(population); ++itScoredIndividual )
{
if (curScore + *itScoredIndividual->score > randomScore)
return itScoredIndividual;
Score curScore{};
for (auto itScoredIndividual {std::cbegin(population)}; itScoredIndividual != std::cend(population); ++itScoredIndividual )
{
if (curScore + *itScoredIndividual->score > randomScore)
return itScoredIndividual;
curScore += *itScoredIndividual->score;
}
curScore += *itScoredIndividual->score;
}
throw std::runtime_error("bad random or empty population");
throw std::runtime_error("bad random or empty population");
}
@@ -29,9 +29,9 @@
#include "database/Track.hpp"
#include "database/TrackFeatures.hpp"
#include "similarity/features/SimilarityFeaturesSearcher.hpp"
#include "utils/Config.hpp"
#include "utils/Service.hpp"
#include "utils/StreamLogger.hpp"
#include "core/Config.hpp"
#include "core/Service.hpp"
#include "core/StreamLogger.hpp"
#include "GeneticAlgorithm.hpp"
@@ -42,446 +42,446 @@ using SimilarityScore = GeneticAlgorithm<FeatureSettingsMap>::Score;
// The goal is to get the FeatureSettingsMap that maximize the score
const FeatureSettingsMap featuresSettings
{
{ "lowlevel.average_loudness", {1}},
{ "lowlevel.barkbands.mean", {1}},
{ "lowlevel.barkbands.median", {1}},
{ "lowlevel.barkbands.var", {1}},
{ "lowlevel.barkbands_crest.mean", {1}},
{ "lowlevel.barkbands_crest.median", {1}},
{ "lowlevel.barkbands_crest.var", {1}},
{ "lowlevel.barkbands_flatness_db.mean", {1}},
{ "lowlevel.barkbands_flatness_db.median", {1}},
{ "lowlevel.barkbands_flatness_db.var", {1}},
{ "lowlevel.barkbands_kurtosis.mean", {1}},
{ "lowlevel.barkbands_kurtosis.median", {1}},
{ "lowlevel.barkbands_kurtosis.var", {1}},
{ "lowlevel.barkbands_skewness.mean", {1}},
{ "lowlevel.barkbands_skewness.median", {1}},
{ "lowlevel.barkbands_skewness.var", {1}},
{ "lowlevel.barkbands_spread.mean", {1}},
{ "lowlevel.barkbands_spread.median", {1}},
{ "lowlevel.barkbands_spread.var", {1}},
{ "lowlevel.dissonance.mean", {1}},
{ "lowlevel.dissonance.median", {1}},
{ "lowlevel.dissonance.var", {1}},
{ "lowlevel.dynamic_complexity", {1}},
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
{ "lowlevel.erbbands.mean", {1}},
{ "lowlevel.erbbands.median", {1}},
{ "lowlevel.erbbands.var", {1}},
{ "lowlevel.gfcc.mean", {1}},
{ "lowlevel.hfc.mean", {1}},
{ "lowlevel.hfc.median", {1}},
{ "lowlevel.hfc.var", {1}},
{ "tonal.hpcp.median", {1}},
{ "lowlevel.melbands.mean", {1}},
{ "lowlevel.melbands.median", {1}},
{ "lowlevel.melbands.var", {1}},
{ "lowlevel.melbands_crest.mean", {1}},
{ "lowlevel.melbands_crest.median", {1}},
{ "lowlevel.melbands_crest.var", {1}},
{ "lowlevel.melbands_flatness_db.mean", {1}},
{ "lowlevel.melbands_flatness_db.median", {1}},
{ "lowlevel.melbands_flatness_db.var", {1}},
{ "lowlevel.melbands_kurtosis.mean", {1}},
{ "lowlevel.melbands_kurtosis.median", {1}},
{ "lowlevel.melbands_kurtosis.var", {1}},
{ "lowlevel.melbands_skewness.mean", {1}},
{ "lowlevel.melbands_skewness.median", {1}},
{ "lowlevel.melbands_skewness.var", {1}},
{ "lowlevel.melbands_spread.mean", {1}},
{ "lowlevel.melbands_spread.median", {1}},
{ "lowlevel.melbands_spread.var", {1}},
{ "lowlevel.mfcc.mean", {1}},
{ "lowlevel.pitch_salience.mean", {1}},
{ "lowlevel.pitch_salience.median", {1}},
{ "lowlevel.pitch_salience.var", {1}},
{ "lowlevel.silence_rate_30dB.mean", {1}},
{ "lowlevel.silence_rate_30dB.median", {1}},
{ "lowlevel.silence_rate_30dB.var", {1}},
{ "lowlevel.silence_rate_60dB.mean", {1}},
{ "lowlevel.silence_rate_60dB.median", {1}},
{ "lowlevel.silence_rate_60dB.var", {1}},
{ "lowlevel.spectral_centroid.mean", {1}},
{ "lowlevel.spectral_centroid.median", {1}},
{ "lowlevel.spectral_centroid.var", {1}},
{ "lowlevel.spectral_complexity.mean", {1}},
{ "lowlevel.spectral_complexity.median", {1}},
{ "lowlevel.spectral_complexity.var", {1}},
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
{ "lowlevel.spectral_contrast_valleys.mean", {1}},
{ "lowlevel.spectral_contrast_valleys.median", {1}},
{ "lowlevel.spectral_contrast_valleys.var", {1}},
{ "lowlevel.spectral_decrease.mean", {1}},
{ "lowlevel.spectral_decrease.median", {1}},
{ "lowlevel.spectral_decrease.var", {1}},
{ "lowlevel.spectral_energy.mean", {1}},
{ "lowlevel.spectral_energy.median", {1}},
{ "lowlevel.spectral_energy.var", {1}},
{ "lowlevel.spectral_energyband_high.mean", {1}},
{ "lowlevel.spectral_energyband_high.median", {1}},
{ "lowlevel.spectral_energyband_high.var", {1}},
{ "lowlevel.spectral_energyband_low.mean", {1}},
{ "lowlevel.spectral_energyband_low.median", {1}},
{ "lowlevel.spectral_energyband_low.var", {1}},
{ "lowlevel.spectral_energyband_middle_high.mean", {1}},
{ "lowlevel.spectral_energyband_middle_high.median", {1}},
{ "lowlevel.spectral_energyband_middle_high.var", {1}},
{ "lowlevel.spectral_energyband_middle_low.mean", {1}},
{ "lowlevel.spectral_energyband_middle_low.median", {1}},
{ "lowlevel.spectral_energyband_middle_low.var", {1}},
{ "lowlevel.spectral_entropy.mean", {1}},
{ "lowlevel.spectral_entropy.median", {1}},
{ "lowlevel.spectral_entropy.var", {1}},
{ "lowlevel.spectral_flux.mean", {1}},
{ "lowlevel.spectral_flux.median", {1}},
{ "lowlevel.spectral_flux.var", {1}},
{ "lowlevel.spectral_kurtosis.mean", {1}},
{ "lowlevel.spectral_kurtosis.median", {1}},
{ "lowlevel.spectral_kurtosis.var", {1}},
{ "lowlevel.spectral_rms.mean", {1}},
{ "lowlevel.spectral_rms.median", {1}},
{ "lowlevel.spectral_rms.var", {1}},
{ "lowlevel.spectral_rolloff.mean", {1}},
{ "lowlevel.spectral_rolloff.median", {1}},
{ "lowlevel.spectral_rolloff.var", {1}},
{ "lowlevel.spectral_skewness.mean", {1}},
{ "lowlevel.spectral_skewness.median", {1}},
{ "lowlevel.spectral_skewness.var", {1}},
{ "lowlevel.spectral_spread.mean", {1}},
{ "lowlevel.spectral_spread.median", {1}},
{ "lowlevel.spectral_spread.var", {1}},
{ "lowlevel.zerocrossingrate.mean", {1}},
{ "lowlevel.zerocrossingrate.median", {1}},
{ "lowlevel.zerocrossingrate.var", {1}},
{ "lowlevel.average_loudness", {1}},
{ "lowlevel.barkbands.mean", {1}},
{ "lowlevel.barkbands.median", {1}},
{ "lowlevel.barkbands.var", {1}},
{ "lowlevel.barkbands_crest.mean", {1}},
{ "lowlevel.barkbands_crest.median", {1}},
{ "lowlevel.barkbands_crest.var", {1}},
{ "lowlevel.barkbands_flatness_db.mean", {1}},
{ "lowlevel.barkbands_flatness_db.median", {1}},
{ "lowlevel.barkbands_flatness_db.var", {1}},
{ "lowlevel.barkbands_kurtosis.mean", {1}},
{ "lowlevel.barkbands_kurtosis.median", {1}},
{ "lowlevel.barkbands_kurtosis.var", {1}},
{ "lowlevel.barkbands_skewness.mean", {1}},
{ "lowlevel.barkbands_skewness.median", {1}},
{ "lowlevel.barkbands_skewness.var", {1}},
{ "lowlevel.barkbands_spread.mean", {1}},
{ "lowlevel.barkbands_spread.median", {1}},
{ "lowlevel.barkbands_spread.var", {1}},
{ "lowlevel.dissonance.mean", {1}},
{ "lowlevel.dissonance.median", {1}},
{ "lowlevel.dissonance.var", {1}},
{ "lowlevel.dynamic_complexity", {1}},
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
{ "lowlevel.erbbands.mean", {1}},
{ "lowlevel.erbbands.median", {1}},
{ "lowlevel.erbbands.var", {1}},
{ "lowlevel.gfcc.mean", {1}},
{ "lowlevel.hfc.mean", {1}},
{ "lowlevel.hfc.median", {1}},
{ "lowlevel.hfc.var", {1}},
{ "tonal.hpcp.median", {1}},
{ "lowlevel.melbands.mean", {1}},
{ "lowlevel.melbands.median", {1}},
{ "lowlevel.melbands.var", {1}},
{ "lowlevel.melbands_crest.mean", {1}},
{ "lowlevel.melbands_crest.median", {1}},
{ "lowlevel.melbands_crest.var", {1}},
{ "lowlevel.melbands_flatness_db.mean", {1}},
{ "lowlevel.melbands_flatness_db.median", {1}},
{ "lowlevel.melbands_flatness_db.var", {1}},
{ "lowlevel.melbands_kurtosis.mean", {1}},
{ "lowlevel.melbands_kurtosis.median", {1}},
{ "lowlevel.melbands_kurtosis.var", {1}},
{ "lowlevel.melbands_skewness.mean", {1}},
{ "lowlevel.melbands_skewness.median", {1}},
{ "lowlevel.melbands_skewness.var", {1}},
{ "lowlevel.melbands_spread.mean", {1}},
{ "lowlevel.melbands_spread.median", {1}},
{ "lowlevel.melbands_spread.var", {1}},
{ "lowlevel.mfcc.mean", {1}},
{ "lowlevel.pitch_salience.mean", {1}},
{ "lowlevel.pitch_salience.median", {1}},
{ "lowlevel.pitch_salience.var", {1}},
{ "lowlevel.silence_rate_30dB.mean", {1}},
{ "lowlevel.silence_rate_30dB.median", {1}},
{ "lowlevel.silence_rate_30dB.var", {1}},
{ "lowlevel.silence_rate_60dB.mean", {1}},
{ "lowlevel.silence_rate_60dB.median", {1}},
{ "lowlevel.silence_rate_60dB.var", {1}},
{ "lowlevel.spectral_centroid.mean", {1}},
{ "lowlevel.spectral_centroid.median", {1}},
{ "lowlevel.spectral_centroid.var", {1}},
{ "lowlevel.spectral_complexity.mean", {1}},
{ "lowlevel.spectral_complexity.median", {1}},
{ "lowlevel.spectral_complexity.var", {1}},
{ "lowlevel.spectral_contrast_coeffs.mean", {1}},
{ "lowlevel.spectral_contrast_coeffs.median", {1}},
{ "lowlevel.spectral_contrast_coeffs.var", {1}},
{ "lowlevel.spectral_contrast_valleys.mean", {1}},
{ "lowlevel.spectral_contrast_valleys.median", {1}},
{ "lowlevel.spectral_contrast_valleys.var", {1}},
{ "lowlevel.spectral_decrease.mean", {1}},
{ "lowlevel.spectral_decrease.median", {1}},
{ "lowlevel.spectral_decrease.var", {1}},
{ "lowlevel.spectral_energy.mean", {1}},
{ "lowlevel.spectral_energy.median", {1}},
{ "lowlevel.spectral_energy.var", {1}},
{ "lowlevel.spectral_energyband_high.mean", {1}},
{ "lowlevel.spectral_energyband_high.median", {1}},
{ "lowlevel.spectral_energyband_high.var", {1}},
{ "lowlevel.spectral_energyband_low.mean", {1}},
{ "lowlevel.spectral_energyband_low.median", {1}},
{ "lowlevel.spectral_energyband_low.var", {1}},
{ "lowlevel.spectral_energyband_middle_high.mean", {1}},
{ "lowlevel.spectral_energyband_middle_high.median", {1}},
{ "lowlevel.spectral_energyband_middle_high.var", {1}},
{ "lowlevel.spectral_energyband_middle_low.mean", {1}},
{ "lowlevel.spectral_energyband_middle_low.median", {1}},
{ "lowlevel.spectral_energyband_middle_low.var", {1}},
{ "lowlevel.spectral_entropy.mean", {1}},
{ "lowlevel.spectral_entropy.median", {1}},
{ "lowlevel.spectral_entropy.var", {1}},
{ "lowlevel.spectral_flux.mean", {1}},
{ "lowlevel.spectral_flux.median", {1}},
{ "lowlevel.spectral_flux.var", {1}},
{ "lowlevel.spectral_kurtosis.mean", {1}},
{ "lowlevel.spectral_kurtosis.median", {1}},
{ "lowlevel.spectral_kurtosis.var", {1}},
{ "lowlevel.spectral_rms.mean", {1}},
{ "lowlevel.spectral_rms.median", {1}},
{ "lowlevel.spectral_rms.var", {1}},
{ "lowlevel.spectral_rolloff.mean", {1}},
{ "lowlevel.spectral_rolloff.median", {1}},
{ "lowlevel.spectral_rolloff.var", {1}},
{ "lowlevel.spectral_skewness.mean", {1}},
{ "lowlevel.spectral_skewness.median", {1}},
{ "lowlevel.spectral_skewness.var", {1}},
{ "lowlevel.spectral_spread.mean", {1}},
{ "lowlevel.spectral_spread.median", {1}},
{ "lowlevel.spectral_spread.var", {1}},
{ "lowlevel.zerocrossingrate.mean", {1}},
{ "lowlevel.zerocrossingrate.median", {1}},
{ "lowlevel.zerocrossingrate.var", {1}},
};
static
std::unordered_map<Database::IdType, FeatureValuesMap>
constructFeaturesCache(Database::Session& session, const FeatureSettingsMap& featureSettings)
std::unordered_map<db::IdType, FeatureValuesMap>
constructFeaturesCache(db::Session& session, const FeatureSettingsMap& featureSettings)
{
std::unordered_map<Database::IdType, FeatureValuesMap> cache;
std::unordered_map<db::IdType, FeatureValuesMap> cache;
std::unordered_set<FeatureName> names;
std::transform(std::cbegin(featureSettings), std::cend(featureSettings), std::inserter(names, std::begin(names)),
[](const auto& itFeature) { return itFeature.first; });
std::unordered_set<FeatureName> names;
std::transform(std::cbegin(featureSettings), std::cend(featureSettings), std::inserter(names, std::begin(names)),
[](const auto& itFeature) { return itFeature.first; });
auto transaction {session.createReadTransaction()};
auto transaction{ session.createReadTransaction() };
for (auto trackId : Database::Track::getAllIdsWithFeatures(session))
{
const Database::Track::pointer track {Database::Track::getById(session, trackId)};
const Database::TrackFeatures::pointer trackFeatures {track->getTrackFeatures()};
for (auto trackId : db::Track::getAllIdsWithFeatures(session))
{
const db::Track::pointer track{ db::Track::getById(session, trackId) };
const db::TrackFeatures::pointer trackFeatures{ track->getTrackFeatures() };
cache[trackId] = trackFeatures->getFeatureValuesMap(names);
}
cache[trackId] = trackFeatures->getFeatureValuesMap(names);
}
return cache;
return cache;
}
static
std::optional<FeatureValuesMap>
getFeaturesFromCache(const std::unordered_map<Database::IdType, FeatureValuesMap>& cache, Database::IdType trackId, const FeatureNames& names)
getFeaturesFromCache(const std::unordered_map<db::IdType, FeatureValuesMap>& cache, db::IdType trackId, const FeatureNames& names)
{
std::optional<FeatureValuesMap> res;
std::optional<FeatureValuesMap> res;
auto it {cache.find(trackId)};
if (it == std::cend(cache))
return res;
auto it{ cache.find(trackId) };
if (it == std::cend(cache))
return res;
res = FeatureValuesMap{};
res = FeatureValuesMap{};
const FeatureValuesMap& trackFeatures {it->second};
for (const FeatureName& name : names)
{
auto itFeatures {trackFeatures.find(name)};
if (itFeatures == std::cend(trackFeatures))
{
res.reset();
break;
}
const FeatureValuesMap& trackFeatures{ it->second };
for (const FeatureName& name : names)
{
auto itFeatures{ trackFeatures.find(name) };
if (itFeatures == std::cend(trackFeatures))
{
res.reset();
break;
}
res->emplace(name, itFeatures ->second);
}
res->emplace(name, itFeatures->second);
}
return res;
return res;
}
static
void
printFeatureSettingsMap(const FeatureSettingsMap& featureSettings)
{
std::cout << "FeatureSettingsMap: (" << featureSettings.size() << " features)" << std::endl;
for (const auto& [name, settings] : featureSettings)
std::cout << "\t" << name << std::endl;
std::cout << "FeatureSettingsMap: (" << featureSettings.size() << " features)" << std::endl;
for (const auto& [name, settings] : featureSettings)
std::cout << "\t" << name << std::endl;
}
static
std::string
trackToString(Database::Session& session, Database::IdType trackId)
trackToString(db::Session& session, db::IdType trackId)
{
std::string res;
auto transaction {session.createReadTransaction()};
Database::Track::pointer track {Database::Track::getById(session, trackId)};
std::string res;
auto transaction{ session.createReadTransaction() };
db::Track::pointer track{ db::Track::getById(session, trackId) };
res += track->getName();
if (track->getRelease())
res += " [" + track->getRelease()->getName() + "]";
for (auto artist : track->getArtists())
res += " - " + artist->getName();
for (auto cluster : track->getClusters())
res += " {" + cluster->getType()->getName() + "-"+ cluster->getName() + "}";
res += track->getName();
if (track->getRelease())
res += " [" + track->getRelease()->getName() + "]";
for (auto artist : track->getArtists())
res += " - " + artist->getName();
for (auto cluster : track->getClusters())
res += " {" + cluster->getType()->getName() + "-" + cluster->getName() + "}";
return res;
return res;
}
static
SimilarityScore
computeTrackScore(Database::Session& session, Database::IdType track1Id, Database::IdType track2Id)
computeTrackScore(db::Session& session, db::IdType track1Id, db::IdType track2Id)
{
SimilarityScore score {};
SimilarityScore score{};
auto transaction {session.createReadTransaction()};
auto transaction{ session.createReadTransaction() };
auto track1 {Database::Track::getById(session, track1Id)};
auto track2 {Database::Track::getById(session, track2Id)};
auto track1{ db::Track::getById(session, track1Id) };
auto track2{ db::Track::getById(session, track2Id) };
if (track1->getRelease() == track2->getRelease())
score += 1;
if (track1->getRelease() == track2->getRelease())
score += 1;
// Artists in common
{
auto track1ArtistIds {track1->getArtistIds()};
auto track2ArtistIds {track2->getArtistIds()};
// Artists in common
{
auto track1ArtistIds{ track1->getArtistIds() };
auto track2ArtistIds{ track2->getArtistIds() };
std::vector<Database::IdType> commonArtistIds;
std::set_intersection(std::cbegin(track1ArtistIds), std::cend(track1ArtistIds),
std::cbegin(track2ArtistIds), std::cend(track2ArtistIds),
std::back_inserter(commonArtistIds));
std::vector<db::IdType> commonArtistIds;
std::set_intersection(std::cbegin(track1ArtistIds), std::cend(track1ArtistIds),
std::cbegin(track2ArtistIds), std::cend(track2ArtistIds),
std::back_inserter(commonArtistIds));
score += commonArtistIds.size();
}
score += commonArtistIds.size();
}
// Clusters in common
{
auto track1ClusterIds {track1->getClusterIds()};
auto track2ClusterIds {track2->getClusterIds()};
// Clusters in common
{
auto track1ClusterIds{ track1->getClusterIds() };
auto track2ClusterIds{ track2->getClusterIds() };
std::vector<Database::IdType> commonClusterIds;
std::set_intersection(std::cbegin(track1ClusterIds), std::cend(track1ClusterIds),
std::cbegin(track2ClusterIds), std::cend(track2ClusterIds),
std::back_inserter(commonClusterIds));
std::vector<db::IdType> commonClusterIds;
std::set_intersection(std::cbegin(track1ClusterIds), std::cend(track1ClusterIds),
std::cbegin(track2ClusterIds), std::cend(track2ClusterIds),
std::back_inserter(commonClusterIds));
score += commonClusterIds.size();
}
score += commonClusterIds.size();
}
return score;
return score;
}
static
SimilarityScore
computeSimilarityScore(Database::Session& session, FeaturesSearcher::TrainSettings trainSettings)
computeSimilarityScore(db::Session& session, FeaturesSearcher::TrainSettings trainSettings)
{
std::cout << "Compute score of: ";
printFeatureSettingsMap(trainSettings.featureSettingsMap);
std::cout << std::endl;
std::cout << "Compute score of: ";
printFeatureSettingsMap(trainSettings.featureSettingsMap);
std::cout << std::endl;
FeaturesSearcher searcher {session, trainSettings};
FeaturesSearcher searcher{ session, trainSettings };
const std::vector<Database::IdType> trackIds = std::invoke([&]()
{
auto transaction {session.createReadTransaction()};
return Database::Track::getAllIdsWithFeatures(session);
});
const std::vector<db::IdType> trackIds = std::invoke([&]()
{
auto transaction{ session.createReadTransaction() };
return db::Track::getAllIdsWithFeatures(session);
});
SimilarityScore score {};
for (Database::IdType trackId : trackIds)
{
constexpr std::size_t nbSimilarTracks {3};
// std::cout << "Processing track '" << trackToString(session, trackId) << "'" << std::endl;
SimilarityScore factor {1};
for (Database::IdType similarTrackId : searcher.getSimilarTracks({trackId}, nbSimilarTracks))
{
SimilarityScore trackScore {computeTrackScore(session, trackId, similarTrackId)};
// std::cout << "\tScore = " << trackScore << " (*" << factor << ") with track '" << trackToString(session, similarTrackId) << "'" << std::endl;
trackScore *= factor;
score += trackScore;
SimilarityScore score{};
for (db::IdType trackId : trackIds)
{
constexpr std::size_t nbSimilarTracks{ 3 };
// std::cout << "Processing track '" << trackToString(session, trackId) << "'" << std::endl;
SimilarityScore factor{ 1 };
for (db::IdType similarTrackId : searcher.getSimilarTracks({ trackId }, nbSimilarTracks))
{
SimilarityScore trackScore{ computeTrackScore(session, trackId, similarTrackId) };
// std::cout << "\tScore = " << trackScore << " (*" << factor << ") with track '" << trackToString(session, similarTrackId) << "'" << std::endl;
trackScore *= factor;
score += trackScore;
factor -= (SimilarityScore {1}/nbSimilarTracks );
}
}
factor -= (SimilarityScore{ 1 } / nbSimilarTracks);
}
}
std::cout << "Total score = " << score << std::endl;
std::cout << "Total score = " << score << std::endl;
return score;
return score;
}
static
void
printBadlyClassifiedTracks(Database::Session& session, FeaturesSearcher::TrainSettings trainSettings)
printBadlyClassifiedTracks(db::Session& session, FeaturesSearcher::TrainSettings trainSettings)
{
FeaturesSearcher searcher {session, trainSettings};
FeaturesSearcher searcher{ session, trainSettings };
const std::vector<Database::IdType> trackIds = std::invoke([&]()
{
auto transaction {session.createReadTransaction()};
return Database::Track::getAllIdsWithFeatures(session);
});
const std::vector<db::IdType> trackIds = std::invoke([&]()
{
auto transaction{ session.createReadTransaction() };
return db::Track::getAllIdsWithFeatures(session);
});
for (Database::IdType trackId : trackIds)
{
constexpr std::size_t nbSimilarTracks {3};
for (Database::IdType similarTrackId : searcher.getSimilarTracks({trackId}, nbSimilarTracks))
{
SimilarityScore trackScore {computeTrackScore(session, trackId, similarTrackId)};
if (trackScore == 0)
std::cout << "Badly classified tracks: '" << trackToString(session, trackId) << "'\n\twith track '" << trackToString(session, similarTrackId) << "'" <<std::endl;
}
}
for (db::IdType trackId : trackIds)
{
constexpr std::size_t nbSimilarTracks{ 3 };
for (db::IdType similarTrackId : searcher.getSimilarTracks({ trackId }, nbSimilarTracks))
{
SimilarityScore trackScore{ computeTrackScore(session, trackId, similarTrackId) };
if (trackScore == 0)
std::cout << "Badly classified tracks: '" << trackToString(session, trackId) << "'\n\twith track '" << trackToString(session, similarTrackId) << "'" << std::endl;
}
}
}
static
FeatureSettingsMap
FeatureSettingsMap
breedFeatureSettingsMap(const FeatureSettingsMap& a, const FeatureSettingsMap& b)
{
FeatureSettingsMap res;
FeatureSettingsMap res;
res.insert(std::cbegin(a), std::cend(a));
res.insert(std::cbegin(b), std::cend(b));
res.insert(std::cbegin(a), std::cend(a));
res.insert(std::cbegin(b), std::cend(b));
// just kill random elements until size is good
while (res.size() > a.size())
{
const auto itFeature {Random::pickRandom(res)};
res.erase(itFeature);
}
// just kill random elements until size is good
while (res.size() > a.size())
{
const auto itFeature{ core::random::pickRandom(res) };
res.erase(itFeature);
}
return res;
return res;
}
static
void
mutateFeatureSettingsMap(FeatureSettingsMap& a)
{
const std::size_t size {a.size()};
// Replace one of the feature with another one, random
a.erase(Random::pickRandom(a));
const std::size_t size{ a.size() };
// Replace one of the feature with another one, random
a.erase(core::random::pickRandom(a));
while (a.size() != size)
{
const auto itFeatureSetting {Random::pickRandom(featuresSettings)};
a.emplace(itFeatureSetting->first, itFeatureSetting->second);
}
while (a.size() != size)
{
const auto itFeatureSetting{ core::random::pickRandom(featuresSettings) };
a.emplace(itFeatureSetting->first, itFeatureSetting->second);
}
}
int main(int argc, char *argv[])
int main(int argc, char* argv[])
{
try
{
// log to stdout
try
{
// log to stdout
// ServiceProvider<Logger>::create<StreamLogger>(std::cout);
if (argc != 3)
{
std::cerr << "usage: <lms_conf_file> <nb_workers>" << std::endl;
return EXIT_FAILURE;
}
if (argc != 3)
{
std::cerr << "usage: <lms_conf_file> <nb_workers>" << std::endl;
return EXIT_FAILURE;
}
const std::filesystem::path configFilePath {std::string(argv[1], 0, 256)};
const std::size_t nbWorkers = atoi(argv[2]);
const std::filesystem::path configFilePath{ std::string(argv[1], 0, 256) };
const std::size_t nbWorkers = atoi(argv[2]);
ServiceProvider<Config>::create(configFilePath);
ServiceProvider<Config>::create(configFilePath);
Database::Db db {ServiceProvider<Config>::get()->getPath("working-dir") / "lms.db"};
Database::SessionPool sessionPool {db, nbWorkers};
db::Db db{ ServiceProvider<Config>::get()->getPath("working-dir") / "lms.db" };
db::SessionPool sessionPool{ db, nbWorkers };
std::cout << "Caching all features..." << std::endl;
// Cache all the features of all the music in order to speed up the multiple trainings
const auto cachedFeatures { constructFeaturesCache(Database::SessionPool::ScopedSession {sessionPool}.get(), featuresSettings) };
std::cout << "Caching all features DONE" << std::endl;
std::cout << "Caching all features..." << std::endl;
// Cache all the features of all the music in order to speed up the multiple trainings
const auto cachedFeatures{ constructFeaturesCache(db::SessionPool::ScopedSession {sessionPool}.get(), featuresSettings) };
std::cout << "Caching all features DONE" << std::endl;
FeaturesSearcher::setFeaturesFetchFunc(
[&](Database::IdType trackId, const FeatureNames& featureNames)
{
return getFeaturesFromCache(cachedFeatures, trackId, featureNames);
});
FeaturesSearcher::setFeaturesFetchFunc(
[&](db::IdType trackId, const FeatureNames& featureNames)
{
return getFeaturesFromCache(cachedFeatures, trackId, featureNames);
});
// Create some random settings (i.e random population)
std::vector<FeatureSettingsMap> initialPopulation;
// Create some random settings (i.e random population)
std::vector<FeatureSettingsMap> initialPopulation;
constexpr std::size_t populationSize {200};
constexpr std::size_t nbFeatures {5};
constexpr std::size_t populationSize{ 200 };
constexpr std::size_t nbFeatures{ 5 };
for (std::size_t i {}; i < populationSize; ++i)
{
FeatureSettingsMap settings;
for (std::size_t i{}; i < populationSize; ++i)
{
FeatureSettingsMap settings;
while (settings.size() < nbFeatures)
{
const auto itFeatureSetting {Random::pickRandom(featuresSettings)};
settings.emplace(itFeatureSetting->first, itFeatureSetting->second);
}
while (settings.size() < nbFeatures)
{
const auto itFeatureSetting{ core::random::pickRandom(featuresSettings) };
settings.emplace(itFeatureSetting->first, itFeatureSetting->second);
}
initialPopulation.emplace_back(std::move(settings));
}
initialPopulation.emplace_back(std::move(settings));
}
FeaturesSearcher::TrainSettings trainSettings;
trainSettings.iterationCount = 8;
trainSettings.sampleCountPerNeuron = 1.5;
FeaturesSearcher::TrainSettings trainSettings;
trainSettings.iterationCount = 8;
trainSettings.sampleCountPerNeuron = 1.5;
GeneticAlgorithm<FeatureSettingsMap>::Params params;
params.nbWorkers = nbWorkers;
params.nbGenerations = 1;
params.crossoverRatio = 0.78;
params.mutationProbability = 0.2;
params.breedFunction = breedFeatureSettingsMap;
params.mutateFunction = mutateFeatureSettingsMap;
params.scoreFunction =
[&](const FeatureSettingsMap& featureSettings)
{
FeaturesSearcher::TrainSettings settings {trainSettings};
settings.featureSettingsMap = featureSettings;
GeneticAlgorithm<FeatureSettingsMap>::Params params;
params.nbWorkers = nbWorkers;
params.nbGenerations = 1;
params.crossoverRatio = 0.78;
params.mutationProbability = 0.2;
params.breedFunction = breedFeatureSettingsMap;
params.mutateFunction = mutateFeatureSettingsMap;
params.scoreFunction =
[&](const FeatureSettingsMap& featureSettings)
{
FeaturesSearcher::TrainSettings settings{ trainSettings };
settings.featureSettingsMap = featureSettings;
Database::SessionPool::ScopedSession scopedSession {sessionPool};
return computeSimilarityScore(scopedSession.get(), settings);
};
db::SessionPool::ScopedSession scopedSession{ sessionPool };
return computeSimilarityScore(scopedSession.get(), settings);
};
GeneticAlgorithm<FeatureSettingsMap> geneticAlgorithm {params};
GeneticAlgorithm<FeatureSettingsMap> geneticAlgorithm{ params };
std::cout << "Parameters:\n"
<< "\tnb total settings = "<< featuresSettings.size() << "\n"
<< "\tnb generations = " << params.nbGenerations << "\n"
<< "\tpopulationSize = " << populationSize << "\n"
<< "\tnbFeatures = " << nbFeatures << "\n"
<< "\tcrossoverRatio = " << params.crossoverRatio << "\n"
<< "\tmutationProbability = " << params.mutationProbability << "\n"
<< std::endl;
std::cout << "Starting simulation..." << std::endl;
const FeatureSettingsMap selectedSettings {geneticAlgorithm.simulate(initialPopulation)};
std::cout << "Simulation complete! Best result:" << std::endl;
printFeatureSettingsMap(selectedSettings);
std::cout << "Parameters:\n"
<< "\tnb total settings = " << featuresSettings.size() << "\n"
<< "\tnb generations = " << params.nbGenerations << "\n"
<< "\tpopulationSize = " << populationSize << "\n"
<< "\tnbFeatures = " << nbFeatures << "\n"
<< "\tcrossoverRatio = " << params.crossoverRatio << "\n"
<< "\tmutationProbability = " << params.mutationProbability << "\n"
<< std::endl;
// print all badly classified tracks
{
FeaturesSearcher::TrainSettings settings {trainSettings};
settings.featureSettingsMap = selectedSettings;
std::cout << "Starting simulation..." << std::endl;
const FeatureSettingsMap selectedSettings{ geneticAlgorithm.simulate(initialPopulation) };
std::cout << "Simulation complete! Best result:" << std::endl;
printFeatureSettingsMap(selectedSettings);
Database::SessionPool::ScopedSession scopedSession {sessionPool};
printBadlyClassifiedTracks(scopedSession.get(), settings);
}
}
catch (std::exception& e)
{
std::cerr << "Caught exception: " << e.what() << std::endl;
}
// print all badly classified tracks
{
FeaturesSearcher::TrainSettings settings{ trainSettings };
settings.featureSettingsMap = selectedSettings;
return EXIT_SUCCESS;
db::SessionPool::ScopedSession scopedSession{ sessionPool };
printBadlyClassifiedTracks(scopedSession.get(), settings);
}
}
catch (std::exception& e)
{
std::cerr << "Caught exception: " << e.what() << std::endl;
}
return EXIT_SUCCESS;
}