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
+24 -23
View File
@@ -29,39 +29,40 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "services/cover/ICoverService.hpp"
#include "utils/IConfig.hpp"
#include "utils/ILogger.hpp"
#include "utils/Service.hpp"
#include "utils/StreamLogger.hpp"
#include "core/IConfig.hpp"
#include "core/ILogger.hpp"
#include "core/Service.hpp"
#include "core/StreamLogger.hpp"
static
void
dumpTrackCovers(Database::Session& session, Image::ImageSize width)
namespace lms
{
using namespace Database;
RangeResults<Database::TrackId> trackIds;
void dumpTrackCovers(db::Session& session, image::ImageSize width)
{
auto transaction{ session.createReadTransaction() };
trackIds = Database::Track::findIds(session, Database::Track::FindParameters{});
}
using namespace db;
for (const Database::TrackId trackId : trackIds.results)
{
std::cout << "Getting cover for track id " << trackId.toString() << std::endl;
Service<Cover::ICoverService>::get()->getFromTrack(trackId, width);
RangeResults<db::TrackId> trackIds;
{
auto transaction{ session.createReadTransaction() };
trackIds = db::Track::findIds(session, db::Track::FindParameters{});
}
for (const db::TrackId trackId : trackIds.results)
{
std::cout << "Getting cover for track id " << trackId.toString() << std::endl;
core::Service<cover::ICoverService>::get()->getFromTrack(trackId, width);
}
}
}
int main(int argc, char* argv[])
{
try
{
using namespace lms;
namespace po = boost::program_options;
// log to stdout
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout) };
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout) };
po::options_description desc{ "Allowed options" };
desc.add_options()
@@ -82,13 +83,13 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS;
}
Service<IConfig> config{ createConfig(vm["conf"].as<std::string>()) };
Database::Db db{ config->getPath("working-dir") / "lms.db" };
Service<Cover::ICoverService> coverArtService{ Cover::createCoverService(db, argv[0], vm["default-cover"].as<std::string>()) };
core::Service<core::IConfig> config{ core::createConfig(vm["conf"].as<std::string>()) };
db::Db db{ config->getPath("working-dir") / "lms.db" };
core::Service<cover::ICoverService> coverArtService{ cover::createCoverService(db, argv[0], vm["default-cover"].as<std::string>()) };
coverArtService->setJpegQuality(config->getULong("cover-jpeg-quality", vm["quality"].as<unsigned>()));
Database::Session session{ db };
db::Session session{ db };
if (vm.count("tracks"))
dumpTrackCovers(session, vm["size"].as<unsigned>());
+1 -1
View File
@@ -5,6 +5,6 @@ add_executable(lms-db-generator
target_link_libraries(lms-db-generator PRIVATE
lmsdatabase
lmsutils
lmscore
Boost::program_options
)
+54 -53
View File
@@ -37,12 +37,12 @@
#include "database/Track.hpp"
#include "database/TrackArtistLink.hpp"
#include "utils/IConfig.hpp"
#include "utils/Random.hpp"
#include "utils/StreamLogger.hpp"
#include "utils/Service.hpp"
#include "core/IConfig.hpp"
#include "core/Random.hpp"
#include "core/StreamLogger.hpp"
#include "core/Service.hpp"
namespace
namespace lms
{
struct GeneratorParameters
{
@@ -60,32 +60,32 @@ namespace
struct GenerationContext
{
Database::Session& session;
std::vector<Database::MediaLibrary::pointer> mediaLibraries;
std::vector<Database::Cluster::pointer> genres;
std::vector<Database::Cluster::pointer> moods;
GenerationContext(Database::Session& _session) : session{ _session } {}
db::Session& session;
std::vector<db::MediaLibrary::pointer> mediaLibraries;
std::vector<db::Cluster::pointer> genres;
std::vector<db::Cluster::pointer> moods;
GenerationContext(db::Session& _session) : session{ _session } {}
};
Database::Cluster::pointer generateCluster(Database::Session& session, Database::ClusterType::pointer clusterType)
db::Cluster::pointer generateCluster(db::Session& session, db::ClusterType::pointer clusterType)
{
const std::string clusterName{ std::string{ clusterType->getName() } + "-" + std::string{ UUID::generate().getAsString() } };
return session.create<Database::Cluster>(clusterType, clusterName);
const std::string clusterName{ std::string{ clusterType->getName() } + "-" + std::string{ core::UUID::generate().getAsString() } };
return session.create<db::Cluster>(clusterType, clusterName);
}
Database::Artist::pointer generateArtist(Database::Session& session)
db::Artist::pointer generateArtist(db::Session& session)
{
const UUID artistMBID{ UUID::generate() };
const std::string artistName{ "Artist-" + std::string{ UUID::generate().getAsString() } };
return session.create<Database::Artist>(artistName, artistMBID);
const core::UUID artistMBID{ core::UUID::generate() };
const std::string artistName{ "Artist-" + std::string{ core::UUID::generate().getAsString() } };
return session.create<db::Artist>(artistName, artistMBID);
}
void generateRelease(const GeneratorParameters& params, GenerationContext& context)
{
using namespace Database;
using namespace db;
const UUID releaseMBID{ UUID::generate() };
const std::string releaseName{ "Release-" + std::string{ UUID::generate().getAsString() } };
const core::UUID releaseMBID{ core::UUID::generate() };
const std::string releaseName{ "Release-" + std::string{ core::UUID::generate().getAsString() } };
Release::pointer release{ context.session.create<Release>(releaseName, releaseMBID) };
Artist::pointer artist{ generateArtist(context.session) };
@@ -94,25 +94,25 @@ namespace
{
Track::pointer track{ context.session.create<Track>(params.trackPath) };
track.modify()->setName("Track-" + std::string{ UUID::generate().getAsString() });
track.modify()->setName("Track-" + std::string{ core::UUID::generate().getAsString() });
track.modify()->setDiscNumber(1);
track.modify()->setTrackNumber(i);
track.modify()->setDuration(std::chrono::seconds{ Random::getRandom(30, 300) });
track.modify()->setDuration(std::chrono::seconds{ core::random::getRandom(30, 300) });
track.modify()->setRelease(release);
track.modify()->setTrackMBID(UUID::generate());
track.modify()->setRecordingMBID(UUID::generate());
track.modify()->setTrackMBID(core::UUID::generate());
track.modify()->setRecordingMBID(core::UUID::generate());
track.modify()->setTotalTrack(params.trackCountPerRelease);
if (!context.mediaLibraries.empty())
track.modify()->setMediaLibrary(*Random::pickRandom(context.mediaLibraries));
track.modify()->setMediaLibrary(*core::random::pickRandom(context.mediaLibraries));
TrackArtistLink::create(context.session, track, artist, TrackArtistLinkType::Artist);
TrackArtistLink::create(context.session, track, artist, TrackArtistLinkType::ReleaseArtist);
std::vector<ObjectPtr<Cluster>> clusters;
if (!context.genres.empty())
clusters.push_back(*Random::pickRandom(context.genres));
clusters.push_back(*core::random::pickRandom(context.genres));
if (!context.moods.empty())
clusters.push_back(*Random::pickRandom(context.moods));
clusters.push_back(*core::random::pickRandom(context.moods));
track.modify()->setClusters(clusters);
}
}
@@ -137,22 +137,22 @@ namespace
// create some random media libraries
for (std::size_t i{}; i < params.mediaLibraryCount; ++i)
context.mediaLibraries.push_back(context.session.create<Database::MediaLibrary>());
context.mediaLibraries.push_back(context.session.create<db::MediaLibrary>());
// create some random genres/moods
{
Database::ClusterType::pointer genre{ Database::ClusterType::find(context.session, "GENRE") };
db::ClusterType::pointer genre{ db::ClusterType::find(context.session, "GENRE") };
if (!genre)
genre = context.session.create<Database::ClusterType>("GENRE");
genre = context.session.create<db::ClusterType>("GENRE");
for (std::size_t i{}; i < params.genreCount; ++i)
context.genres.push_back(generateCluster(context.session, genre));
}
{
Database::ClusterType::pointer mood{ Database::ClusterType::find(context.session, "MOOD") };
db::ClusterType::pointer mood{ db::ClusterType::find(context.session, "MOOD") };
if (!mood)
mood = context.session.create<Database::ClusterType>("MOOD");
mood = context.session.create<db::ClusterType>("MOOD");
for (std::size_t i{}; i < params.moodCount; ++i)
context.moods.push_back(generateCluster(context.session, mood));
@@ -164,31 +164,32 @@ int main(int argc, char* argv[])
{
try
{
// log to stdout
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout) };
using namespace lms;
namespace program_options = boost::program_options;
namespace po = boost::program_options;
// log to stdout
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout) };
const GeneratorParameters defaultParams;
po::options_description options{ "Options" };
program_options::options_description options{ "Options" };
options.add_options()
("conf,c", po::value<std::string>()->default_value("/etc/lms.conf"), "lms config file")
("media-library-count", po::value<unsigned>()->default_value(defaultParams.mediaLibraryCount), "Number of media libraries to use")
("release-count-per-batch", po::value<unsigned>()->default_value(defaultParams.releaseCountPerBatch), "Number of releases to generate before committing transaction")
("release-count", po::value<unsigned>()->default_value(defaultParams.releaseCount), "Number of releases to generate")
("track-count-per-release", po::value<unsigned>()->default_value(defaultParams.trackCountPerRelease), "Number of tracks per release")
("compilation-ratio", po::value<float>()->default_value(defaultParams.compilationRatio), "Compilation ratio (compilation means all tracks have a different artist)")
("track-path", po::value<std::string>()->required(), "Path of a valid track file, that will be used for all generated tracks")
("genre-count", po::value<unsigned>()->default_value(defaultParams.genreCount), "Number of genres to generate")
("genre-count-per-track", po::value<unsigned>()->default_value(defaultParams.genreCountPerTrack), "Number of genres to assign to each track")
("mood-count", po::value<unsigned>()->default_value(defaultParams.moodCount), "Number of moods to generate")
("mood-count-per-track", po::value<unsigned>()->default_value(defaultParams.moodCountPerTrack), "Number of moods to assign to each track")
("conf,c", program_options::value<std::string>()->default_value("/etc/lms.conf"), "lms config file")
("media-library-count", program_options::value<unsigned>()->default_value(defaultParams.mediaLibraryCount), "Number of media libraries to use")
("release-count-per-batch", program_options::value<unsigned>()->default_value(defaultParams.releaseCountPerBatch), "Number of releases to generate before committing transaction")
("release-count", program_options::value<unsigned>()->default_value(defaultParams.releaseCount), "Number of releases to generate")
("track-count-per-release", program_options::value<unsigned>()->default_value(defaultParams.trackCountPerRelease), "Number of tracks per release")
("compilation-ratio", program_options::value<float>()->default_value(defaultParams.compilationRatio), "Compilation ratio (compilation means all tracks have a different artist)")
("track-path", program_options::value<std::string>()->required(), "Path of a valid track file, that will be used for all generated tracks")
("genre-count", program_options::value<unsigned>()->default_value(defaultParams.genreCount), "Number of genres to generate")
("genre-count-per-track", program_options::value<unsigned>()->default_value(defaultParams.genreCountPerTrack), "Number of genres to assign to each track")
("mood-count", program_options::value<unsigned>()->default_value(defaultParams.moodCount), "Number of moods to generate")
("mood-count-per-track", program_options::value<unsigned>()->default_value(defaultParams.moodCountPerTrack), "Number of moods to assign to each track")
("help,h", "produce help message")
;
po::variables_map vm;
po::store(po::parse_command_line(argc, argv, options), vm);
program_options::variables_map vm;
program_options::store(program_options::parse_command_line(argc, argv, options), vm);
if (vm.count("help")) {
std::cout << options << "\n";
@@ -196,7 +197,7 @@ int main(int argc, char* argv[])
}
// notify required params
po::notify(vm);
program_options::notify(vm);
GeneratorParameters genParams;
genParams.mediaLibraryCount = vm["media-library-count"].as<unsigned>();
@@ -209,9 +210,9 @@ int main(int argc, char* argv[])
if (!std::filesystem::exists(genParams.trackPath))
throw std::runtime_error{ "File '" + genParams.trackPath.string() + "' does not exist!" };
Service<IConfig> config{ createConfig(vm["conf"].as<std::string>()) };
Database::Db db{ config->getPath("working-dir") / "lms.db" };
Database::Session session{ db };
core::Service<core::IConfig> config{ core::createConfig(vm["conf"].as<std::string>()) };
db::Db db{ config->getPath("working-dir") / "lms.db" };
db::Session session{ db };
std::cout << "Starting generation..." << std::endl;
GenerationContext genContext{ session };
+1 -1
View File
@@ -5,7 +5,7 @@ add_executable(lms-metadata
target_link_libraries(lms-metadata PRIVATE
lmsmetadata
lmsutils
lmscore
)
install(TARGETS lms-metadata DESTINATION bin)
+15 -13
View File
@@ -28,11 +28,11 @@
#include "metadata/Exception.hpp"
#include "metadata/IParser.hpp"
#include "utils/StreamLogger.hpp"
#include "core/StreamLogger.hpp"
namespace
namespace lms::metadata
{
std::ostream& operator<<(std::ostream& os, const MetaData::Artist& artist)
std::ostream& operator<<(std::ostream& os, const Artist& artist)
{
os << artist.name;
@@ -45,7 +45,7 @@ namespace
return os;
}
std::ostream& operator<<(std::ostream& os, const MetaData::Release& release)
std::ostream& operator<<(std::ostream& os, const Release& release)
{
os << release.name;
if (!release.sortName.empty())
@@ -64,7 +64,7 @@ namespace
if (!release.artistDisplayName.empty())
std::cout << "\tDisplay artist: " << release.artistDisplayName << std::endl;
for (const MetaData::Artist& artist : release.artists)
for (const Artist& artist : release.artists)
std::cout << "\tRelease artist: " << artist << std::endl;
for (std::string_view releaseType : release.releaseTypes)
@@ -73,7 +73,7 @@ namespace
return os;
}
std::ostream& operator<<(std::ostream& os, const MetaData::Medium& medium)
std::ostream& operator<<(std::ostream& os, const Medium& medium)
{
if (!medium.name.empty())
os << medium.name;
@@ -97,9 +97,9 @@ namespace
return os;
}
void parse(MetaData::IParser& parser, const std::filesystem::path& file)
void parse(IParser& parser, const std::filesystem::path& file)
{
using namespace MetaData;
using namespace metadata;
const auto start{ std::chrono::steady_clock::now() };
std::unique_ptr<Track> track{ parser.parse(file, true) };
@@ -222,8 +222,10 @@ int main(int argc, char* argv[])
try
{
using namespace lms;
// log to stdout
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout, StreamLogger::allSeverities) };
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout, core::logging::StreamLogger::allSeverities) };
for (std::size_t i{}; i < static_cast<std::size_t>(argc - 1); ++i)
{
@@ -234,10 +236,10 @@ int main(int argc, char* argv[])
try
{
std::cout << "Using av:" << std::endl;
auto parser{ MetaData::createParser(MetaData::ParserBackend::AvFormat, MetaData::ParserReadStyle::Accurate) };
auto parser{ metadata::createParser(metadata::ParserBackend::AvFormat, metadata::ParserReadStyle::Accurate) };
parse(*parser, file);
}
catch (MetaData::Exception& e)
catch (metadata::Exception& e)
{
std::cerr << "Parsing failed: " << e.what() << std::endl;
}
@@ -245,10 +247,10 @@ int main(int argc, char* argv[])
try
{
std::cout << "Using TagLib:" << std::endl;
auto parser{ MetaData::createParser(MetaData::ParserBackend::TagLib, MetaData::ParserReadStyle::Accurate) };
auto parser{ metadata::createParser(metadata::ParserBackend::TagLib, metadata::ParserReadStyle::Accurate) };
parse(*parser, file);
}
catch (MetaData::Exception& e)
catch (metadata::Exception& e)
{
std::cerr << "Parsing failed: " << e.what() << std::endl;
}
+74 -70
View File
@@ -32,94 +32,97 @@
#include "database/Track.hpp"
#include "database/Types.hpp"
#include "services/recommendation/IRecommendationService.hpp"
#include "utils/IConfig.hpp"
#include "utils/Service.hpp"
#include "utils/StreamLogger.hpp"
#include "core/IConfig.hpp"
#include "core/Service.hpp"
#include "core/StreamLogger.hpp"
using namespace Database;
static void dumpTracksRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
namespace lms
{
const RangeResults<TrackId> trackIds{ [&]
{
auto transaction {session.createReadTransaction()};
return Track::findIds(session, Track::FindParameters{});
}() };
using namespace db;
std::cout << "*** Tracks (" << trackIds.results.size() << ") ***" << std::endl;
for (const TrackId trackId : trackIds.results)
void dumpTracksRecommendation(Session session, recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
{
auto trackToString = [&](const TrackId trackId)
const RangeResults<TrackId> trackIds{ [&]
{
std::string res;
auto transaction{ session.createReadTransaction() };
const Track::pointer track{ Track::find(session, trackId) };
auto transaction {session.createReadTransaction()};
return Track::findIds(session, Track::FindParameters{});
}() };
res += track->getName();
if (track->getRelease())
res += " [" + std::string{ track->getRelease()->getName() } + "]";
for (auto artist : track->getArtists({ TrackArtistLinkType::Artist }))
res += " - " + artist->getName();
for (auto cluster : track->getClusters())
res += " {" + std::string{ cluster->getType()->getName() } + "-" + std::string{ cluster->getName() } + "}";
std::cout << "*** Tracks (" << trackIds.results.size() << ") ***" << std::endl;
for (const TrackId trackId : trackIds.results)
{
auto trackToString = [&](const TrackId trackId)
{
std::string res;
auto transaction{ session.createReadTransaction() };
const Track::pointer track{ Track::find(session, trackId) };
return res;
};
res += track->getName();
if (track->getRelease())
res += " [" + std::string{ track->getRelease()->getName() } + "]";
for (auto artist : track->getArtists({ TrackArtistLinkType::Artist }))
res += " - " + artist->getName();
for (auto cluster : track->getClusters())
res += " {" + std::string{ cluster->getType()->getName() } + "-" + std::string{ cluster->getName() } + "}";
std::cout << "Processing track '" << trackToString(trackId) << std::endl;
for (TrackId similarTrackId : recommendationService.findSimilarTracks({ trackId }, maxSimilarityCount))
std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl;
return res;
};
std::cout << "Processing track '" << trackToString(trackId) << std::endl;
for (TrackId similarTrackId : recommendationService.findSimilarTracks({ trackId }, maxSimilarityCount))
std::cout << "\t- Similar track '" << trackToString(similarTrackId) << std::endl;
}
}
}
static void dumpReleasesRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
{
const RangeResults<ReleaseId> releaseIds{ std::invoke([&]
{
auto transaction {session.createReadTransaction()};
return Release::findIds(session, Release::FindParameters {});
}) };
std::cout << "*** Releases ***" << std::endl;
for (const ReleaseId releaseId : releaseIds.results)
void dumpReleasesRecommendation(Session session, recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
{
auto releaseToString = [&](ReleaseId releaseId) -> std::string
const RangeResults<ReleaseId> releaseIds{ std::invoke([&]
{
auto transaction{ session.createReadTransaction() };
auto transaction {session.createReadTransaction()};
return Release::findIds(session, Release::FindParameters {});
}) };
Release::pointer release{ Release::find(session, releaseId) };
return std::string{ release->getName() };
};
std::cout << "*** Releases ***" << std::endl;
for (const ReleaseId releaseId : releaseIds.results)
{
auto releaseToString = [&](ReleaseId releaseId) -> std::string
{
auto transaction{ session.createReadTransaction() };
std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl;
for (const ReleaseId similarReleaseId : recommendationService.getSimilarReleases(releaseId, maxSimilarityCount))
std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl;
Release::pointer release{ Release::find(session, releaseId) };
return std::string{ release->getName() };
};
std::cout << "Processing release '" << releaseToString(releaseId) << "'" << std::endl;
for (const ReleaseId similarReleaseId : recommendationService.getSimilarReleases(releaseId, maxSimilarityCount))
std::cout << "\t- Similar release '" << releaseToString(similarReleaseId) << "'" << std::endl;
}
}
}
static void dumpArtistsRecommendation(Session session, Recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
{
const RangeResults<ArtistId> artistIds = std::invoke([&]()
{
auto transaction{ session.createReadTransaction() };
return Artist::findIds(session, Artist::FindParameters{});
});
std::cout << "*** Artists ***" << std::endl;
for (ArtistId artistId : artistIds.results)
void dumpArtistsRecommendation(Session session, recommendation::IRecommendationService& recommendationService, unsigned maxSimilarityCount)
{
auto artistToString = [&](ArtistId artistId)
const RangeResults<ArtistId> artistIds = std::invoke([&]()
{
auto transaction{ session.createReadTransaction() };
return Artist::findIds(session, Artist::FindParameters{});
});
Artist::pointer artist{ Artist::find(session, artistId) };
return artist->getName();
};
std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl;
for (ArtistId similarArtistId : recommendationService.getSimilarArtists(artistId, { TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist }, maxSimilarityCount))
std::cout << "*** Artists ***" << std::endl;
for (ArtistId artistId : artistIds.results)
{
std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl;
auto artistToString = [&](ArtistId artistId)
{
auto transaction{ session.createReadTransaction() };
Artist::pointer artist{ Artist::find(session, artistId) };
return artist->getName();
};
std::cout << "Processing artist '" << artistToString(artistId) << "'" << std::endl;
for (ArtistId similarArtistId : recommendationService.getSimilarArtists(artistId, { TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist }, maxSimilarityCount))
{
std::cout << "\t- Similar artist '" << artistToString(similarArtistId) << "'" << std::endl;
}
}
}
}
@@ -128,10 +131,11 @@ int main(int argc, char* argv[])
{
try
{
using namespace lms;
namespace po = boost::program_options;
// log to stdout
Service<ILogger> logger{ std::make_unique<StreamLogger>(std::cout) };
core::Service<core::logging::ILogger> logger{ std::make_unique<core::logging::StreamLogger>(std::cout) };
po::options_description desc{ "Allowed options" };
desc.add_options()
@@ -152,13 +156,13 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS;
}
Service<IConfig> config{ createConfig(vm["conf"].as<std::string>()) };
core::Service<core::IConfig> config{ core::createConfig(vm["conf"].as<std::string>()) };
Db db{ config->getPath("working-dir") / "lms.db" };
Session session{ db };
std::cout << "Creating recommendation service..." << std::endl;
const auto recommendationService{ Recommendation::createRecommendationService(db) };
const auto recommendationService{ recommendation::createRecommendationService(db) };
std::cout << "Recommendation service created!" << std::endl;
std::cout << "Loading recommendation service..." << std::endl;
@@ -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;
}