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
@@ -21,14 +21,14 @@
#include <memory>
namespace Database
namespace lms::db
{
class Db;
}
namespace Recommendation
namespace lms::recommendation
{
class IEngine;
std::unique_ptr<IEngine> createClustersEngine(Database::Db& db);
std::unique_ptr<IEngine> createClustersEngine(db::Db& db);
}
@@ -22,13 +22,13 @@
#include <memory>
#include "IEngine.hpp"
namespace Database
namespace lms::db
{
class Db;
}
namespace Recommendation
namespace lms::recommendation
{
std::unique_ptr<IEngine> createFeaturesEngine(Database::Db& db);
std::unique_ptr<IEngine> createFeaturesEngine(db::Db& db);
}
@@ -23,14 +23,14 @@
#include "database/Types.hpp"
#include "database/TrackListId.hpp"
#include "services/recommendation/Types.hpp"
#include "utils/EnumSet.hpp"
#include "core/EnumSet.hpp"
namespace Database
namespace lms::db
{
class Db;
}
namespace Recommendation
namespace lms::recommendation
{
class IEngine
{
@@ -40,13 +40,13 @@ namespace Recommendation
virtual void load(bool forceReload, const ProgressCallback& progressCallback = {}) = 0;
virtual void requestCancelLoad() = 0;
virtual TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const = 0;
virtual TrackContainer findSimilarTracks(const std::vector<Database::TrackId>& tracksId, std::size_t maxCount) const = 0;
virtual ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const = 0;
virtual ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet<Database::TrackArtistLinkType> linkTypes, std::size_t maxCount) const = 0;
virtual TrackContainer findSimilarTracksFromTrackList(db::TrackListId tracklistId, std::size_t maxCount) const = 0;
virtual TrackContainer findSimilarTracks(const std::vector<db::TrackId>& tracksId, std::size_t maxCount) const = 0;
virtual ReleaseContainer getSimilarReleases(db::ReleaseId releaseId, std::size_t maxCount) const = 0;
virtual ArtistContainer getSimilarArtists(db::ArtistId artistId, core::EnumSet<db::TrackArtistLinkType> linkTypes, std::size_t maxCount) const = 0;
};
std::unique_ptr<IEngine> createEngine(Database::Db& db);
std::unique_ptr<IEngine> createEngine(db::Db& db);
} // ns Recommendation
@@ -26,18 +26,18 @@
#include "playlist-constraints/ConsecutiveArtists.hpp"
#include "playlist-constraints/ConsecutiveReleases.hpp"
#include "playlist-constraints/DuplicateTracks.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
namespace Recommendation
namespace lms::recommendation
{
using namespace Database;
using namespace db;
std::unique_ptr<IPlaylistGeneratorService> createPlaylistGeneratorService(Db& db, Recommendation::IRecommendationService& recommendationService)
std::unique_ptr<IPlaylistGeneratorService> createPlaylistGeneratorService(Db& db, IRecommendationService& recommendationService)
{
return std::make_unique<PlaylistGeneratorService>(db, recommendationService);
}
PlaylistGeneratorService::PlaylistGeneratorService(Db& db, Recommendation::IRecommendationService& recommendationService)
PlaylistGeneratorService::PlaylistGeneratorService(Db& db, IRecommendationService& recommendationService)
: _db{ db }
, _recommendationService{ recommendationService }
{
@@ -69,7 +69,7 @@ namespace Recommendation
// select the similar track that has the best score
for (std::size_t trackIndex{}; trackIndex < similarTracks.size(); ++trackIndex)
{
using namespace Database::Debug;
using namespace db::Debug;
finalResult.push_back(similarTracks[trackIndex]);
@@ -95,7 +95,7 @@ namespace Recommendation
return std::vector(std::cbegin(finalResult) + startingTracks.size(), std::cend(finalResult));
}
TrackContainer PlaylistGeneratorService::getTracksFromTrackList(Database::TrackListId tracklistId) const
TrackContainer PlaylistGeneratorService::getTracksFromTrackList(db::TrackListId tracklistId) const
{
TrackContainer tracks;
@@ -23,20 +23,20 @@
#include "services/recommendation/IRecommendationService.hpp"
#include "playlist-constraints/IConstraint.hpp"
namespace Recommendation
namespace lms::recommendation
{
class PlaylistGeneratorService : public IPlaylistGeneratorService
{
public:
PlaylistGeneratorService(Database::Db& db, Recommendation::IRecommendationService& recommendationService);
PlaylistGeneratorService(db::Db& db, IRecommendationService& recommendationService);
private:
TrackContainer extendPlaylist(Database::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer extendPlaylist(db::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer getTracksFromTrackList(Database::TrackListId tracklistId) const;
TrackContainer getTracksFromTrackList(db::TrackListId tracklistId) const;
Database::Db& _db;
Recommendation::IRecommendationService& _recommendationService;
db::Db& _db;
IRecommendationService& _recommendationService;
std::vector<std::unique_ptr<PlaylistGeneratorConstraint::IConstraint>> _constraints;
};
} // namespace Radio
@@ -28,33 +28,33 @@
#include "database/Db.hpp"
#include "database/Session.hpp"
#include "database/ScanSettings.hpp"
#include "utils/Exception.hpp"
#include "utils/ILogger.hpp"
#include "core/Exception.hpp"
#include "core/ILogger.hpp"
namespace Recommendation
namespace lms::recommendation
{
namespace
{
Database::ScanSettings::SimilarityEngineType getSimilarityEngineType(Database::Session& session)
db::ScanSettings::SimilarityEngineType getSimilarityEngineType(db::Session& session)
{
auto transaction{ session.createReadTransaction() };
return Database::ScanSettings::get(session)->getSimilarityEngineType();
return db::ScanSettings::get(session)->getSimilarityEngineType();
}
}
std::unique_ptr<IRecommendationService> createRecommendationService(Database::Db& db)
std::unique_ptr<IRecommendationService> createRecommendationService(db::Db& db)
{
return std::make_unique<RecommendationService>(db);
}
RecommendationService::RecommendationService(Database::Db& db)
RecommendationService::RecommendationService(db::Db& db)
: _db{ db }
{
load();
}
TrackContainer RecommendationService::findSimilarTracks(Database::TrackListId trackListId, std::size_t maxCount) const
TrackContainer RecommendationService::findSimilarTracks(db::TrackListId trackListId, std::size_t maxCount) const
{
TrackContainer res;
@@ -64,7 +64,7 @@ namespace Recommendation
return _engine->findSimilarTracksFromTrackList(trackListId, maxCount);
}
TrackContainer RecommendationService::findSimilarTracks(const std::vector<Database::TrackId>& trackIds, std::size_t maxCount) const
TrackContainer RecommendationService::findSimilarTracks(const std::vector<db::TrackId>& trackIds, std::size_t maxCount) const
{
TrackContainer res;
@@ -74,7 +74,7 @@ namespace Recommendation
return _engine->findSimilarTracks(trackIds, maxCount);
}
ReleaseContainer RecommendationService::getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const
ReleaseContainer RecommendationService::getSimilarReleases(db::ReleaseId releaseId, std::size_t maxCount) const
{
ReleaseContainer res;
@@ -84,7 +84,7 @@ namespace Recommendation
return _engine->getSimilarReleases(releaseId, maxCount);;
}
ArtistContainer RecommendationService::getSimilarArtists(Database::ArtistId artistId, EnumSet<Database::TrackArtistLinkType> linkTypes, std::size_t maxCount) const
ArtistContainer RecommendationService::getSimilarArtists(db::ArtistId artistId, core::EnumSet<db::TrackArtistLinkType> linkTypes, std::size_t maxCount) const
{
ArtistContainer res;
@@ -98,7 +98,7 @@ namespace Recommendation
void RecommendationService::load()
{
using namespace Database;
using namespace db;
switch (getSimilarityEngineType(_db.getTLSSession()))
{
@@ -24,12 +24,12 @@
#include "services/recommendation/IRecommendationService.hpp"
#include "IEngine.hpp"
namespace Database
namespace lms::db
{
class Db;
}
namespace Recommendation
namespace lms::recommendation
{
enum class EngineType
{
@@ -40,7 +40,7 @@ namespace Recommendation
class RecommendationService : public IRecommendationService
{
public:
RecommendationService(Database::Db& db);
RecommendationService(db::Db& db);
~RecommendationService() = default;
RecommendationService(const RecommendationService&) = delete;
@@ -49,16 +49,16 @@ namespace Recommendation
private:
void load() override;
TrackContainer findSimilarTracks(Database::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer findSimilarTracks(const std::vector<Database::TrackId>& tracksId, std::size_t maxCount) const override;
ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override;
ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet<Database::TrackArtistLinkType> linkTypes, std::size_t maxCount) const override;
TrackContainer findSimilarTracks(db::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer findSimilarTracks(const std::vector<db::TrackId>& tracksId, std::size_t maxCount) const override;
ReleaseContainer getSimilarReleases(db::ReleaseId releaseId, std::size_t maxCount) const override;
ArtistContainer getSimilarArtists(db::ArtistId artistId, core::EnumSet<db::TrackArtistLinkType> linkTypes, std::size_t maxCount) const override;
void setEnginePriorities(const std::vector<EngineType>& engineTypes);
void clearEngines();
void loadPendingEngine(EngineType engineType, std::unique_ptr<IEngine> engine, bool forceReload, const ProgressCallback& progressCallback);
Database::Db& _db;
db::Db& _db;
std::optional<EngineType> _engineType;
std::unique_ptr<IEngine> _engine;
};
@@ -27,9 +27,9 @@
#include "database/Track.hpp"
#include "database/TrackList.hpp"
namespace Recommendation {
namespace lms::recommendation {
using namespace Database;
using namespace db;
std::unique_ptr<IEngine> createClustersEngine(Db& db)
{
@@ -92,7 +92,7 @@ namespace Recommendation {
return res;
}
ArtistContainer ClusterEngine::getSimilarArtists(ArtistId artistId, EnumSet<TrackArtistLinkType> artistLinkTypes, std::size_t maxCount) const
ArtistContainer ClusterEngine::getSimilarArtists(ArtistId artistId, core::EnumSet<TrackArtistLinkType> artistLinkTypes, std::size_t maxCount) const
{
if (maxCount == 0)
return {};
@@ -108,4 +108,4 @@ namespace Recommendation {
return std::move(similarArtistIds.results);
}
} // namespace Recommendation
} // namespace lms::recommendation
@@ -21,13 +21,13 @@
#include "IEngine.hpp"
namespace Recommendation
namespace lms::recommendation
{
class ClusterEngine : public IEngine
{
public:
ClusterEngine(Database::Db& db) : _db {db} {}
ClusterEngine(db::Db& db) : _db {db} {}
ClusterEngine(const ClusterEngine&) = delete;
ClusterEngine(ClusterEngine&&) = delete;
@@ -38,13 +38,13 @@ namespace Recommendation
void load(bool, const ProgressCallback&) override {}
void requestCancelLoad() override {}
TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer findSimilarTracks(const std::vector<Database::TrackId>& tracksId, std::size_t maxCount) const override;
ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override;
ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet<Database::TrackArtistLinkType> linkTypes, std::size_t maxCount) const override;
TrackContainer findSimilarTracksFromTrackList(db::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer findSimilarTracks(const std::vector<db::TrackId>& tracksId, std::size_t maxCount) const override;
ReleaseContainer getSimilarReleases(db::ReleaseId releaseId, std::size_t maxCount) const override;
ArtistContainer getSimilarArtists(db::ArtistId artistId, core::EnumSet<db::TrackArtistLinkType> linkTypes, std::size_t maxCount) const override;
Database::Db& _db;
db::Db& _db;
};
} // namespace Recommendation
} // namespace lms::recommendation
@@ -22,9 +22,9 @@
#include <algorithm>
#include <iterator>
#include "utils/Exception.hpp"
#include "core/Exception.hpp"
namespace Recommendation {
namespace lms::recommendation {
static const std::unordered_map<FeatureName, FeatureDef> featureDefinitions
{
@@ -373,7 +373,7 @@ getFeatureDef(const FeatureName& featureName)
{
auto it {featureDefinitions.find(featureName)};
if (it == std::cend(featureDefinitions))
throw LmsException {"Unhandled requested feature '" + featureName + "'"};
throw core::LmsException {"Unhandled requested feature '" + featureName + "'"};
return it->second;
}
@@ -389,5 +389,5 @@ getFeatureNames()
return res;
}
} // namespace Recommendation
} // namespace lms::recommendation
@@ -24,7 +24,7 @@
#include <unordered_set>
#include <vector>
namespace Recommendation {
namespace lms::recommendation {
using FeatureName = std::string;
using FeatureNames = std::unordered_set<FeatureName>;
@@ -46,4 +46,4 @@ struct FeatureSettings
};
using FeatureSettingsMap = std::unordered_map<FeatureName, FeatureSettings>;
} // namespace Recommendation
} // namespace lms::recommendation
@@ -30,12 +30,12 @@
#include "database/TrackFeatures.hpp"
#include "database/TrackList.hpp"
#include "som/DataNormalizer.hpp"
#include "utils/ILogger.hpp"
#include "utils/Random.hpp"
#include "core/ILogger.hpp"
#include "core/Random.hpp"
namespace Recommendation
namespace lms::recommendation
{
using namespace Database;
using namespace db;
std::unique_ptr<IEngine> createFeaturesEngine(Db& db)
{
@@ -44,10 +44,10 @@ namespace Recommendation
namespace
{
std::optional<SOM::InputVector> convertFeatureValuesMapToInputVector(const FeatureValuesMap& featureValuesMap, std::size_t nbDimensions)
std::optional<som::InputVector> convertFeatureValuesMapToInputVector(const FeatureValuesMap& featureValuesMap, std::size_t nbDimensions)
{
std::size_t i{};
std::optional<SOM::InputVector> res{ SOM::InputVector {nbDimensions} };
std::optional<som::InputVector> res{ som::InputVector {nbDimensions} };
for (const auto& [featureName, values] : featureValuesMap)
{
if (values.size() != getFeatureDef(featureName).nbDimensions)
@@ -64,9 +64,9 @@ namespace Recommendation
return res;
}
SOM::InputVector getInputVectorWeights(const FeatureSettingsMap& featureSettingsMap, std::size_t nbDimensions)
som::InputVector getInputVectorWeights(const FeatureSettingsMap& featureSettingsMap, std::size_t nbDimensions)
{
SOM::InputVector weights{ nbDimensions };
som::InputVector weights{ nbDimensions };
std::size_t index{};
for (const auto& [featureName, featureSettings] : featureSettingsMap)
{
@@ -120,7 +120,7 @@ namespace Recommendation
LMS_LOG(RECOMMENDATION, DEBUG, "Getting Track features DONE (found " << trackFeaturesIds.results.size() << " track features)");
}
std::vector<SOM::InputVector> samples;
std::vector<som::InputVector> samples;
std::vector<TrackId> samplesTrackIds;
samples.reserve(trackFeaturesIds.results.size());
@@ -143,7 +143,7 @@ namespace Recommendation
if (featureValuesMap.empty())
continue;
std::optional<SOM::InputVector> inputVector{ convertFeatureValuesMapToInputVector(featureValuesMap, nbDimensions) };
std::optional<som::InputVector> inputVector{ convertFeatureValuesMapToInputVector(featureValuesMap, nbDimensions) };
if (!inputVector)
continue;
@@ -159,13 +159,13 @@ namespace Recommendation
}
LMS_LOG(RECOMMENDATION, DEBUG, "Normalizing data...");
SOM::DataNormalizer dataNormalizer{ nbDimensions };
som::DataNormalizer dataNormalizer{ nbDimensions };
dataNormalizer.computeNormalizationFactors(samples);
for (auto& sample : samples)
dataNormalizer.normalizeData(sample);
SOM::Coordinate size{ static_cast<SOM::Coordinate>(std::sqrt(samples.size() / trainSettings.sampleCountPerNeuron)) };
som::Coordinate size{ static_cast<som::Coordinate>(std::sqrt(samples.size() / trainSettings.sampleCountPerNeuron)) };
if (size < 2)
{
LMS_LOG(RECOMMENDATION, WARNING, "Very few tracks (" << samples.size() << ") are being used by the features engine, expect bad behaviors");
@@ -173,12 +173,12 @@ namespace Recommendation
}
LMS_LOG(RECOMMENDATION, INFO, "Found " << samples.size() << " tracks, constructing a " << size << "*" << size << " network");
SOM::Network network{ size, size, nbDimensions };
som::Network network{ size, size, nbDimensions };
SOM::InputVector weights{ getInputVectorWeights(trainSettings.featureSettingsMap, nbDimensions) };
som::InputVector weights{ getInputVectorWeights(trainSettings.featureSettingsMap, nbDimensions) };
network.setDataWeights(weights);
auto somProgressCallback{ [&](const SOM::Network::CurrentIteration& iter)
auto somProgressCallback{ [&](const som::Network::CurrentIteration& iter)
{
LMS_LOG(RECOMMENDATION, DEBUG, "Current pass = " << iter.idIteration << " / " << iter.iterationCount);
progressCallback(Progress {iter.idIteration, iter.iterationCount});
@@ -186,7 +186,7 @@ namespace Recommendation
LMS_LOG(RECOMMENDATION, DEBUG, "Training network...");
network.train(samples, trainSettings.iterationCount,
progressCallback ? somProgressCallback : SOM::Network::ProgressCallback{},
progressCallback ? somProgressCallback : som::Network::ProgressCallback{},
[this] { return _loadCancelled; });
LMS_LOG(RECOMMENDATION, DEBUG, "Training network DONE");
@@ -197,7 +197,7 @@ namespace Recommendation
if (_loadCancelled)
return;
const SOM::Position position{ network.getClosestRefVectorPosition(samples[i]) };
const som::Position position{ network.getClosestRefVectorPosition(samples[i]) };
trackPositions[samplesTrackIds[i]].push_back(position);
}
@@ -275,7 +275,7 @@ namespace Recommendation
return similarReleaseIds;
}
ArtistContainer FeaturesEngine::getSimilarArtists(ArtistId artistId, EnumSet<TrackArtistLinkType> linkTypes, std::size_t maxCount) const
ArtistContainer FeaturesEngine::getSimilarArtists(ArtistId artistId, core::EnumSet<TrackArtistLinkType> linkTypes, std::size_t maxCount) const
{
auto getSimilarArtistIdsForLinkType{ [&](TrackArtistLinkType linkType)
{
@@ -313,7 +313,7 @@ namespace Recommendation
}
while (res.size() > maxCount)
res.erase(Random::pickRandom(res));
res.erase(core::random::pickRandom(res));
return res;
}
@@ -349,15 +349,15 @@ namespace Recommendation
_loadCancelled = true;
}
void FeaturesEngine::load(const SOM::Network& network, const TrackPositions& trackPositions)
void FeaturesEngine::load(const som::Network& network, const TrackPositions& trackPositions)
{
using namespace Database;
using namespace db;
_networkRefVectorsDistanceMedian = network.computeRefVectorsDistanceMedian();
LMS_LOG(RECOMMENDATION, DEBUG, "Median distance betweend ref vectors = " << _networkRefVectorsDistanceMedian);
const SOM::Coordinate width{ network.getWidth() };
const SOM::Coordinate height{ network.getHeight() };
const som::Coordinate width{ network.getWidth() };
const som::Coordinate height{ network.getHeight() };
_releaseMatrix = ReleaseMatrix{ width, height };
_trackMatrix = TrackMatrix{ width, height };
@@ -377,22 +377,22 @@ namespace Recommendation
if (!track)
continue;
for (const SOM::Position& position : positions)
for (const som::Position& position : positions)
{
Utils::push_back_if_not_present(_trackPositions[trackId], position);
Utils::push_back_if_not_present(_trackMatrix[position], trackId);
core::utils::push_back_if_not_present(_trackPositions[trackId], position);
core::utils::push_back_if_not_present(_trackMatrix[position], trackId);
if (Release::pointer release{ track->getRelease() })
{
const ReleaseId releaseId{ release->getId() };
Utils::push_back_if_not_present(_releasePositions[releaseId], position);
Utils::push_back_if_not_present(_releaseMatrix[position], releaseId);
core::utils::push_back_if_not_present(_releasePositions[releaseId], position);
core::utils::push_back_if_not_present(_releaseMatrix[position], releaseId);
}
for (const TrackArtistLink::pointer& artistLink : track->getArtistLinks())
{
const ArtistId artistId{ artistLink->getArtist()->getId() };
Utils::push_back_if_not_present(_artistPositions[artistId], position);
core::utils::push_back_if_not_present(_artistPositions[artistId], position);
auto itArtists{ _artistMatrix.find(artistLink->getType()) };
if (itArtists == std::cend(_artistMatrix))
{
@@ -400,12 +400,12 @@ namespace Recommendation
assert(inserted);
itArtists = it;
}
Utils::push_back_if_not_present(itArtists->second[position], artistId);
core::utils::push_back_if_not_present(itArtists->second[position], artistId);
}
}
}
_network = std::make_unique<SOM::Network>(network);
_network = std::make_unique<som::Network>(network);
LMS_LOG(RECOMMENDATION, INFO, "Classifier successfully loaded!");
}
@@ -28,178 +28,174 @@
#include "som/DataNormalizer.hpp"
#include "som/Network.hpp"
#include "utils/Utils.hpp"
#include "core/Utils.hpp"
#include "IEngine.hpp"
#include "FeaturesEngineCache.hpp"
#include "FeaturesDefs.hpp"
namespace Database
namespace lms::db
{
class Session;
class Session;
}
namespace Recommendation {
using FeatureWeight = double;
class FeaturesEngine : public IEngine
namespace lms::recommendation
{
public:
FeaturesEngine(Database::Db& db) : _db {db} {}
using FeatureWeight = double;
FeaturesEngine(const FeaturesEngine&) = delete;
FeaturesEngine(FeaturesEngine&&) = delete;
FeaturesEngine& operator=(const FeaturesEngine&) = delete;
FeaturesEngine& operator=(FeaturesEngine&&) = delete;
class FeaturesEngine : public IEngine
{
public:
FeaturesEngine(db::Db& db) : _db{ db } {}
static const FeatureSettingsMap& getDefaultTrainFeatureSettings();
FeaturesEngine(const FeaturesEngine&) = delete;
FeaturesEngine(FeaturesEngine&&) = delete;
FeaturesEngine& operator=(const FeaturesEngine&) = delete;
FeaturesEngine& operator=(FeaturesEngine&&) = delete;
private:
void load(bool forceReload, const ProgressCallback& progressCallback) override;
void requestCancelLoad() override;
static const FeatureSettingsMap& getDefaultTrainFeatureSettings();
TrackContainer findSimilarTracksFromTrackList(Database::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer findSimilarTracks(const std::vector<Database::TrackId>& tracksId, std::size_t maxCount) const override;
ReleaseContainer getSimilarReleases(Database::ReleaseId releaseId, std::size_t maxCount) const override;
ArtistContainer getSimilarArtists(Database::ArtistId artistId, EnumSet<Database::TrackArtistLinkType> linkTypes, std::size_t maxCount) const override;
private:
void load(bool forceReload, const ProgressCallback& progressCallback) override;
void requestCancelLoad() override;
void loadFromCache(FeaturesEngineCache&& cache);
TrackContainer findSimilarTracksFromTrackList(db::TrackListId tracklistId, std::size_t maxCount) const override;
TrackContainer findSimilarTracks(const std::vector<db::TrackId>& tracksId, std::size_t maxCount) const override;
ReleaseContainer getSimilarReleases(db::ReleaseId releaseId, std::size_t maxCount) const override;
ArtistContainer getSimilarArtists(db::ArtistId artistId, core::EnumSet<db::TrackArtistLinkType> linkTypes, std::size_t maxCount) const override;
// Use training (may be very slow)
struct TrainSettings
{
std::size_t iterationCount {10};
float sampleCountPerNeuron {4};
FeatureSettingsMap featureSettingsMap;
};
void loadFromTraining(const TrainSettings& trainSettings, const ProgressCallback& progressCallback);
void loadFromCache(FeaturesEngineCache&& cache);
template <typename IdType>
using ObjectPositions = std::unordered_map<IdType, std::vector<SOM::Position>>;
// Use training (may be very slow)
struct TrainSettings
{
std::size_t iterationCount{ 10 };
float sampleCountPerNeuron{ 4 };
FeatureSettingsMap featureSettingsMap;
};
void loadFromTraining(const TrainSettings& trainSettings, const ProgressCallback& progressCallback);
using ArtistPositions = ObjectPositions<Database::ArtistId>;
using ReleasePositions = ObjectPositions<Database::ReleaseId>;
using TrackPositions = ObjectPositions<Database::TrackId>;
template <typename IdType>
using ObjectPositions = std::unordered_map<IdType, std::vector<som::Position>>;
template <typename IdType>
using ObjectMatrix = SOM::Matrix<std::vector<IdType>>;
using ArtistMatrix = ObjectMatrix<Database::ArtistId>;
using ReleaseMatrix = ObjectMatrix<Database::ReleaseId>;
using TrackMatrix = ObjectMatrix<Database::TrackId>;
using ArtistPositions = ObjectPositions<db::ArtistId>;
using ReleasePositions = ObjectPositions<db::ReleaseId>;
using TrackPositions = ObjectPositions<db::TrackId>;
void load(const SOM::Network& network, const TrackPositions& tracksPosition);
template <typename IdType>
using ObjectMatrix = som::Matrix<std::vector<IdType>>;
using ArtistMatrix = ObjectMatrix<db::ArtistId>;
using ReleaseMatrix = ObjectMatrix<db::ReleaseId>;
using TrackMatrix = ObjectMatrix<db::TrackId>;
FeaturesEngineCache toCache() const;
void load(const som::Network& network, const TrackPositions& tracksPosition);
template <typename IdType>
static std::vector<SOM::Position> getMatchingRefVectorsPosition(const std::vector<IdType>& ids, const ObjectPositions<IdType>& objectPositions);
FeaturesEngineCache toCache() const;
template <typename IdType>
static std::vector<IdType> getObjectsIds(const std::vector<SOM::Position>& positions, const ObjectMatrix<IdType>& objectsMatrix);
template <typename IdType>
static std::vector<som::Position> getMatchingRefVectorsPosition(const std::vector<IdType>& ids, const ObjectPositions<IdType>& objectPositions);
template <typename IdType>
std::vector<IdType> getSimilarObjects(const std::vector<IdType>& ids,
const ObjectMatrix<IdType>& objectMatrix,
const ObjectPositions<IdType>& objectPositions,
std::size_t maxCount) const;
template <typename IdType>
static std::vector<IdType> getObjectsIds(const std::vector<som::Position>& positions, const ObjectMatrix<IdType>& objectsMatrix);
Database::Db& _db;
bool _loadCancelled {};
std::unique_ptr<SOM::Network> _network;
double _networkRefVectorsDistanceMedian {};
template <typename IdType>
std::vector<IdType> getSimilarObjects(const std::vector<IdType>& ids,
const ObjectMatrix<IdType>& objectMatrix,
const ObjectPositions<IdType>& objectPositions,
std::size_t maxCount) const;
ArtistPositions _artistPositions;
std::unordered_map<Database::TrackArtistLinkType, ArtistMatrix> _artistMatrix;
db::Db& _db;
bool _loadCancelled{};
std::unique_ptr<som::Network> _network;
double _networkRefVectorsDistanceMedian{};
ReleasePositions _releasePositions;
ReleaseMatrix _releaseMatrix;
ArtistPositions _artistPositions;
std::unordered_map<db::TrackArtistLinkType, ArtistMatrix> _artistMatrix;
TrackPositions _trackPositions;
TrackMatrix _trackMatrix;
};
ReleasePositions _releasePositions;
ReleaseMatrix _releaseMatrix;
template <typename IdType>
std::vector<SOM::Position>
FeaturesEngine::getMatchingRefVectorsPosition(const std::vector<IdType>& ids, const ObjectPositions<IdType>& objectPositions)
{
std::vector<SOM::Position> res;
TrackPositions _trackPositions;
TrackMatrix _trackMatrix;
};
if (ids.empty())
return res;
template <typename IdType>
std::vector<som::Position> FeaturesEngine::getMatchingRefVectorsPosition(const std::vector<IdType>& ids, const ObjectPositions<IdType>& objectPositions)
{
std::vector<som::Position> res;
for (const IdType id : ids)
{
auto it = objectPositions.find(id);
if (it == objectPositions.end())
continue;
if (ids.empty())
return res;
for (const SOM::Position& position : it->second)
Utils::push_back_if_not_present(res, position);
}
for (const IdType id : ids)
{
auto it = objectPositions.find(id);
if (it == objectPositions.end())
continue;
return res;
for (const som::Position& position : it->second)
core::utils::push_back_if_not_present(res, position);
}
return res;
}
template <typename IdType>
std::vector<IdType> FeaturesEngine::getObjectsIds(const std::vector<som::Position>& positions, const ObjectMatrix<IdType>& objectMatrix)
{
std::vector<IdType> res;
for (const som::Position& position : positions)
{
for (const IdType id : objectMatrix.get(position))
core::utils::push_back_if_not_present(res, id);
}
return res;
}
template <typename IdType>
std::vector<IdType> FeaturesEngine::getSimilarObjects(const std::vector<IdType>& ids,
const ObjectMatrix<IdType>& objectMatrix,
const ObjectPositions<IdType>& objectPositions,
std::size_t maxCount) const
{
std::vector<IdType> res;
std::vector<som::Position> searchedRefVectorsPosition{ getMatchingRefVectorsPosition(ids, objectPositions) };
if (searchedRefVectorsPosition.empty())
return res;
while (1)
{
std::vector<IdType> closestObjectIds{ getObjectsIds(searchedRefVectorsPosition, objectMatrix) };
// Remove objects that are already in input or already reported
closestObjectIds.erase(std::remove_if(std::begin(closestObjectIds), std::end(closestObjectIds),
[&](IdType id)
{
return std::find(std::cbegin(ids), std::cend(ids), id) != std::cend(ids);
})
, std::end(closestObjectIds));
for (IdType id : closestObjectIds)
{
if (res.size() == maxCount)
break;
core::utils::push_back_if_not_present(res, id);
}
if (res.size() == maxCount)
break;
// If there is not enough objects, try again with closest neighbour until there is too much distance
const std::optional<som::Position> closestRefVectorPosition{ _network->getClosestRefVectorPosition(searchedRefVectorsPosition, _networkRefVectorsDistanceMedian * 0.75) };
if (!closestRefVectorPosition)
break;
core::utils::push_back_if_not_present(searchedRefVectorsPosition, closestRefVectorPosition.value());
}
return res;
}
}
template <typename IdType>
std::vector<IdType>
FeaturesEngine::getObjectsIds(const std::vector<SOM::Position>& positions, const ObjectMatrix<IdType>& objectMatrix)
{
std::vector<IdType> res;
for (const SOM::Position& position : positions)
{
for (const IdType id : objectMatrix.get(position))
Utils::push_back_if_not_present(res, id);
}
return res;
}
template <typename IdType>
std::vector<IdType>
FeaturesEngine::getSimilarObjects(const std::vector<IdType>& ids,
const ObjectMatrix<IdType>& objectMatrix,
const ObjectPositions<IdType>& objectPositions,
std::size_t maxCount) const
{
std::vector<IdType> res;
std::vector<SOM::Position> searchedRefVectorsPosition {getMatchingRefVectorsPosition(ids, objectPositions)};
if (searchedRefVectorsPosition.empty())
return res;
while (1)
{
std::vector<IdType> closestObjectIds {getObjectsIds(searchedRefVectorsPosition, objectMatrix)};
// Remove objects that are already in input or already reported
closestObjectIds.erase(std::remove_if(std::begin(closestObjectIds), std::end(closestObjectIds),
[&](IdType id)
{
return std::find(std::cbegin(ids), std::cend(ids), id) != std::cend(ids);
})
, std::end(closestObjectIds));
for (IdType id : closestObjectIds)
{
if (res.size() == maxCount)
break;
Utils::push_back_if_not_present(res, id);
}
if (res.size() == maxCount)
break;
// If there is not enough objects, try again with closest neighbour until there is too much distance
const std::optional<SOM::Position> closestRefVectorPosition {_network->getClosestRefVectorPosition(searchedRefVectorsPosition, _networkRefVectorsDistanceMedian * 0.75)};
if (!closestRefVectorPosition)
break;
Utils::push_back_if_not_present(searchedRefVectorsPosition, closestRefVectorPosition.value());
}
return res;
}
} // ns Recommendation
@@ -22,17 +22,17 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include "utils/IConfig.hpp"
#include "utils/ILogger.hpp"
#include "utils/Service.hpp"
#include "core/IConfig.hpp"
#include "core/ILogger.hpp"
#include "core/Service.hpp"
namespace Recommendation
namespace lms::recommendation
{
namespace
{
std::filesystem::path getCacheDirectory()
{
return Service<IConfig>::get()->getPath("working-dir") / "cache" / "features";
return core::Service<core::IConfig>::get()->getPath("working-dir") / "cache" / "features";
}
std::filesystem::path getCacheNetworkFilePath()
@@ -45,7 +45,7 @@ namespace Recommendation
return getCacheDirectory() / "track_positions";
}
bool networkToCacheFile(const SOM::Network& network, std::filesystem::path path)
bool networkToCacheFile(const som::Network& network, std::filesystem::path path)
{
try
{
@@ -55,12 +55,12 @@ namespace Recommendation
root.put("height", network.getHeight());
root.put("dim_count", network.getInputDimCount());
for (SOM::InputVector::value_type weight : network.getDataWeights())
for (som::InputVector::value_type weight : network.getDataWeights())
root.add("weights.weight", weight);
for (SOM::Coordinate x = 0; x < network.getWidth(); ++x)
for (som::Coordinate x = 0; x < network.getWidth(); ++x)
{
for (SOM::Coordinate y = 0; y < network.getWidth(); ++y)
for (som::Coordinate y = 0; y < network.getWidth(); ++y)
{
const auto& refVector = network.getRefVector({ x, y });
@@ -88,7 +88,7 @@ namespace Recommendation
}
}
std::optional<SOM::Network> FeaturesEngineCache::createNetworkFromCacheFile(const std::filesystem::path& path)
std::optional<som::Network> FeaturesEngineCache::createNetworkFromCacheFile(const std::filesystem::path& path)
{
if (!std::filesystem::exists(path))
return std::nullopt;
@@ -101,14 +101,14 @@ namespace Recommendation
boost::property_tree::read_xml(path.string(), root);
SOM::Coordinate width{ root.get<SOM::Coordinate>("width") };
SOM::Coordinate height{ root.get<SOM::Coordinate>("height") };
som::Coordinate width{ root.get<som::Coordinate>("width") };
som::Coordinate height{ root.get<som::Coordinate>("height") };
std::size_t dimCount{ root.get<std::size_t>("dim_count") };
SOM::Network res{ width, height, dimCount };
som::Network res{ width, height, dimCount };
{
SOM::InputVector weights{ dimCount };
som::InputVector weights{ dimCount };
std::size_t i{};
for (const auto& val : root.get_child("weights"))
weights[i++] = val.second.get_value<double>();
@@ -118,13 +118,13 @@ namespace Recommendation
for (const auto& node : root.get_child("ref_vectors"))
{
SOM::Coordinate x{ node.second.get<SOM::Coordinate>("coord_x") };
SOM::Coordinate y{ node.second.get<SOM::Coordinate>("coord_y") };
som::Coordinate x{ node.second.get<som::Coordinate>("coord_x") };
som::Coordinate y{ node.second.get<som::Coordinate>("coord_y") };
SOM::InputVector refVector{ dimCount };
som::InputVector refVector{ dimCount };
std::size_t i{};
for (const auto& val : node.second.get_child("values"))
refVector[i++] = val.second.get_value<SOM::InputVector::value_type>();
refVector[i++] = val.second.get_value<som::InputVector::value_type>();
res.setRefVector({ x, y }, refVector);
}
@@ -152,7 +152,7 @@ namespace Recommendation
node.put("id", id.getValue());
for (const SOM::Position& position : positions)
for (const som::Position& position : positions)
{
boost::property_tree::ptree positionNode;
positionNode.put("x", position.x);
@@ -188,11 +188,11 @@ namespace Recommendation
for (const auto& object : root.get_child("objects"))
{
const Database::TrackId id{ object.second.get<Database::IdType::ValueType>("id") };
const db::TrackId id{ object.second.get<db::IdType::ValueType>("id") };
for (const auto& position : object.second.get_child("position"))
{
auto x = position.second.get<SOM::Coordinate>("x");
auto y = position.second.get<SOM::Coordinate>("y");
auto x = position.second.get<som::Coordinate>("x");
auto y = position.second.get<som::Coordinate>("y");
res[id].push_back({ x, y });
}
@@ -230,7 +230,7 @@ namespace Recommendation
void FeaturesEngineCache::write() const
{
std::filesystem::create_directories(Service<IConfig>::get()->getPath("working-dir") / "cache" / "features");
std::filesystem::create_directories(core::Service<core::IConfig>::get()->getPath("working-dir") / "cache" / "features");
if (!networkToCacheFile(_network, getCacheNetworkFilePath())
|| !objectPositionToCacheFile(_trackPositions, getCacheTrackPositionsFilePath()))
@@ -239,10 +239,10 @@ namespace Recommendation
}
}
FeaturesEngineCache::FeaturesEngineCache(SOM::Network network, TrackPositions trackPositions)
FeaturesEngineCache::FeaturesEngineCache(som::Network network, TrackPositions trackPositions)
: _network{ std::move(network) },
_trackPositions{ std::move(trackPositions) }
{
}
} // namespace Recommendation
} // namespace lms::recommendation
@@ -25,29 +25,30 @@
#include "database/TrackId.hpp"
#include "som/Network.hpp"
namespace Recommendation {
class FeaturesEngineCache
namespace lms::recommendation
{
public:
static void invalidate();
static std::optional<FeaturesEngineCache> read();
void write() const;
class FeaturesEngineCache
{
public:
static void invalidate();
private:
using TrackPositions = std::unordered_map<Database::TrackId, std::vector<SOM::Position>>;
static std::optional<FeaturesEngineCache> read();
void write() const;
FeaturesEngineCache(SOM::Network network, TrackPositions trackPositions);
private:
using TrackPositions = std::unordered_map<db::TrackId, std::vector<som::Position>>;
static std::optional<SOM::Network> createNetworkFromCacheFile(const std::filesystem::path& path);
static std::optional<TrackPositions> createObjectPositionsFromCacheFile(const std::filesystem::path& path);
static bool objectPositionToCacheFile(const TrackPositions& trackPositions, const std::filesystem::path& path);
FeaturesEngineCache(som::Network network, TrackPositions trackPositions);
friend class FeaturesEngine;
static std::optional<som::Network> createNetworkFromCacheFile(const std::filesystem::path& path);
static std::optional<TrackPositions> createObjectPositionsFromCacheFile(const std::filesystem::path& path);
static bool objectPositionToCacheFile(const TrackPositions& trackPositions, const std::filesystem::path& path);
SOM::Network _network;
TrackPositions _trackPositions;
};
friend class FeaturesEngine;
} // namespace Recommendation
som::Network _network;
TrackPositions _trackPositions;
};
} // namespace lms::recommendation
@@ -25,9 +25,9 @@
#include "database/Release.hpp"
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
namespace Recommendation::PlaylistGeneratorConstraint
namespace lms::recommendation::PlaylistGeneratorConstraint
{
namespace
{
@@ -44,12 +44,12 @@ namespace Recommendation::PlaylistGeneratorConstraint
}
}
ConsecutiveArtists::ConsecutiveArtists(Database::Db& db)
ConsecutiveArtists::ConsecutiveArtists(db::Db& db)
: _db {db}
{}
float
ConsecutiveArtists::computeScore(const std::vector<Database::TrackId>& trackIds, std::size_t trackIndex)
ConsecutiveArtists::computeScore(const std::vector<db::TrackId>& trackIds, std::size_t trackIndex)
{
assert(!trackIds.empty());
assert(trackIndex <= trackIds.size() - 1);
@@ -73,9 +73,9 @@ namespace Recommendation::PlaylistGeneratorConstraint
}
ArtistContainer
ConsecutiveArtists::getArtists(Database::TrackId trackId)
ConsecutiveArtists::getArtists(db::TrackId trackId)
{
using namespace Database;
using namespace db;
ArtistContainer res;
@@ -93,5 +93,5 @@ namespace Recommendation::PlaylistGeneratorConstraint
}
} // namespace Recommendation
} // namespace lms::recommendation
@@ -23,23 +23,23 @@
#include "database/ReleaseId.hpp"
namespace Database
namespace lms::db
{
class Db;
}
namespace Recommendation::PlaylistGeneratorConstraint
namespace lms::recommendation::PlaylistGeneratorConstraint
{
class ConsecutiveArtists : public IConstraint
{
public:
ConsecutiveArtists(Database::Db& db);
ConsecutiveArtists(db::Db& db);
private:
float computeScore(const TrackContainer& trackIds, std::size_t trackIndex) override;
ArtistContainer getArtists(Database::TrackId trackId);
ArtistContainer getArtists(db::TrackId trackId);
Database::Db& _db;
db::Db& _db;
};
} // namespace Recommendation::PlaylistGeneratorConstraint
} // namespace lms::recommendation::PlaylistGeneratorConstraint
@@ -23,21 +23,21 @@
#include "database/Release.hpp"
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
namespace Recommendation::PlaylistGeneratorConstraint
namespace lms::recommendation::PlaylistGeneratorConstraint
{
ConsecutiveReleases::ConsecutiveReleases(Database::Db& db)
ConsecutiveReleases::ConsecutiveReleases(db::Db& db)
: _db {db}
{}
float
ConsecutiveReleases::computeScore(const std::vector<Database::TrackId>& trackIds, std::size_t trackIndex)
ConsecutiveReleases::computeScore(const std::vector<db::TrackId>& trackIds, std::size_t trackIndex)
{
assert(!trackIds.empty());
assert(trackIndex <= trackIds.size() - 1);
const Database::ReleaseId releaseId {getReleaseId(trackIds[trackIndex])};
const db::ReleaseId releaseId {getReleaseId(trackIds[trackIndex])};
constexpr std::size_t rangeSize{ 3 }; // check up to rangeSize tracks before/after the target track
static_assert(rangeSize > 0);
@@ -55,10 +55,10 @@ namespace Recommendation::PlaylistGeneratorConstraint
return score;
}
Database::ReleaseId
ConsecutiveReleases::getReleaseId(Database::TrackId trackId)
db::ReleaseId
ConsecutiveReleases::getReleaseId(db::TrackId trackId)
{
using namespace Database;
using namespace db;
Session& dbSession {_db.getTLSSession()};
auto transaction {dbSession.createReadTransaction()};
@@ -73,5 +73,5 @@ namespace Recommendation::PlaylistGeneratorConstraint
return release->getId();
}
} // namespace Recommendation
} // namespace lms::recommendation
@@ -23,24 +23,24 @@
#include "database/ReleaseId.hpp"
namespace Database
namespace lms::db
{
class Db;
}
namespace Recommendation::PlaylistGeneratorConstraint
namespace lms::recommendation::PlaylistGeneratorConstraint
{
class ConsecutiveReleases : public IConstraint
{
public:
ConsecutiveReleases(Database::Db& db);
ConsecutiveReleases(db::Db& db);
private:
float computeScore(const std::vector<Database::TrackId>& trackIds, std::size_t trackIndex) override;
float computeScore(const std::vector<db::TrackId>& trackIds, std::size_t trackIndex) override;
Database::ReleaseId getReleaseId(Database::TrackId trackId);
db::ReleaseId getReleaseId(db::TrackId trackId);
Database::Db& _db;
db::Db& _db;
};
} // namespace Recommendation
} // namespace lms::recommendation
@@ -21,13 +21,13 @@
#include <algorithm>
namespace Recommendation::PlaylistGeneratorConstraint
namespace lms::recommendation::PlaylistGeneratorConstraint
{
float
DuplicateTracks::computeScore(const std::vector<Database::TrackId>& trackIds, std::size_t trackIndex)
DuplicateTracks::computeScore(const std::vector<db::TrackId>& trackIds, std::size_t trackIndex)
{
const auto count {std::count(std::cbegin(trackIds), std::cend(trackIds), trackIds[trackIndex])};
return count == 1 ? 0 : 1000;
}
} // namespace Recommendation
} // namespace lms::recommendation
@@ -21,12 +21,12 @@
#include "IConstraint.hpp"
namespace Recommendation::PlaylistGeneratorConstraint
namespace lms::recommendation::PlaylistGeneratorConstraint
{
class DuplicateTracks : public IConstraint
{
private:
float computeScore(const std::vector<Database::TrackId>& trackIds, std::size_t trackIndex) override;
float computeScore(const std::vector<db::TrackId>& trackIds, std::size_t trackIndex) override;
};
} // namespace Recommendation::PlaylistGeneratorConstraints
} // namespace lms::recommendation::PlaylistGeneratorConstraints
@@ -23,7 +23,7 @@
#include "services/recommendation/Types.hpp"
namespace Recommendation::PlaylistGeneratorConstraint
namespace lms::recommendation::PlaylistGeneratorConstraint
{
class IConstraint
{
@@ -36,4 +36,4 @@ namespace Recommendation::PlaylistGeneratorConstraint
// > 1 : violation
virtual float computeScore(const TrackContainer& trackIds, std::size_t trackIndex) = 0;
};
} // namespace Recommendation
} // namespace lms::recommendation