Refactored namespaces
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user