Audio similarity engine: optimized release and artists matching

This commit is contained in:
emeric
2026-06-04 23:13:38 +02:00
parent 7ef19e4c06
commit 59b710ea37
4 changed files with 135 additions and 119 deletions
+32 -52
View File
@@ -38,30 +38,49 @@ namespace lms::math
using value_type = typename VectorType::value_type;
using size_type = std::size_t;
// Add a single vector, returning its index
size_type add(const VectorType& value)
// provided value must be accessible until finalize() is called
void add(const VectorType& value)
{
_vectors.push_back(value);
return _vectors.size() - 1;
_pointers.push_back(&value);
}
// Compute the medoid: the vector with minimum sum of squared distances to all others
// Returns the index of the medoid in the added vectors
size_type findMedoidIndex() const
// Returns a pointer to the medoid among the added vectors.
// The pointer remains valid as long as the original vectors are alive.
const VectorType* finalize() const
{
assert(!empty());
return _pointers[findMedoidIndex()];
}
bool empty() const
{
return _pointers.empty();
}
size_type count() const
{
return _pointers.size();
}
void clear()
{
_pointers.clear();
}
private:
size_type findMedoidIndex() const
{
size_type medoidIndex{};
value_type minTotalDistance{ std::numeric_limits<value_type>::max() };
for (size_type i{}; i < _vectors.size(); ++i)
for (size_type i{}; i < _pointers.size(); ++i)
{
value_type totalDistance{};
const SquaredEuclideanDistance distFunc{ _vectors[i] };
for (size_type j{}; j < _vectors.size(); ++j)
const SquaredEuclideanDistance distFunc{ *_pointers[i] };
for (size_type j{}; j < _pointers.size(); ++j)
{
if (i != j)
totalDistance += distFunc(_vectors[j]);
totalDistance += distFunc(*_pointers[j]);
}
if (totalDistance < minTotalDistance)
@@ -74,37 +93,7 @@ namespace lms::math
return medoidIndex;
}
// Compute the medoid vector itself
VectorType finalize() const
{
return _vectors[findMedoidIndex()];
}
// Get a specific vector by index
const VectorType& getVector(size_type index) const
{
assert(index < _vectors.size());
return _vectors[index];
}
// Query methods
bool empty() const
{
return _vectors.empty();
}
size_type count() const
{
return _vectors.size();
}
void clear()
{
_vectors.clear();
}
private:
std::vector<VectorType> _vectors;
std::vector<const VectorType*> _pointers;
};
template<typename VectorType>
@@ -113,15 +102,6 @@ namespace lms::math
MedoidCalculator<VectorType> calculator;
for (const auto& value : values)
calculator.add(value);
return calculator.finalize();
}
template<typename VectorType>
VectorType computeNormalizedMedoid(std::span<const VectorType> values)
{
MedoidCalculator<VectorType> calculator;
for (const auto& value : values)
calculator.add(value);
return calculator.finalizeNormalized();
return *calculator.finalize();
}
} // namespace lms::math
+13 -34
View File
@@ -40,12 +40,7 @@ namespace lms::math::medoidCalculatorTests
EXPECT_FALSE(calculator.empty());
EXPECT_EQ(calculator.count(), 1U);
EXPECT_EQ(calculator.findMedoidIndex(), 0U);
const Vector<3, float> result = calculator.finalize();
EXPECT_EQ(result[0], 1.0F);
EXPECT_EQ(result[1], 2.0F);
EXPECT_EQ(result[2], 3.0F);
EXPECT_EQ(calculator.finalize(), &vec);
}
TEST(MedoidCalculator, twoVectors)
@@ -58,9 +53,9 @@ namespace lms::math::medoidCalculatorTests
calculator.add(v2);
EXPECT_EQ(calculator.count(), 2U);
// Both have equal distance to the other, but first one is returned
const std::size_t medoidIndex = calculator.findMedoidIndex();
EXPECT_TRUE(medoidIndex == 0 || medoidIndex == 1);
// Both have equal distance to the other; result must be one of the two
const Vector<2, float>* medoid = calculator.finalize();
EXPECT_TRUE(medoid == &v1 || medoid == &v2);
}
TEST(MedoidCalculator, threeDifferentVectors)
@@ -68,16 +63,14 @@ namespace lms::math::medoidCalculatorTests
MedoidCalculator<Vector<2, float>> calculator;
// Three points: (0,0), (1,0), (10,0)
// Medoid should be (1,0) as it's closest to the others
calculator.add(Vector<2, float>{ 0.0F, 0.0F });
calculator.add(Vector<2, float>{ 1.0F, 0.0F });
calculator.add(Vector<2, float>{ 10.0F, 0.0F });
const Vector<2, float> v0{ 0.0F, 0.0F };
const Vector<2, float> v1{ 1.0F, 0.0F };
const Vector<2, float> v2{ 10.0F, 0.0F };
calculator.add(v0);
calculator.add(v1);
calculator.add(v2);
const std::size_t medoidIndex = calculator.findMedoidIndex();
EXPECT_EQ(medoidIndex, 1U); // The middle point (1,0) is the medoid
const Vector<2, float> result = calculator.finalize();
EXPECT_FLOAT_EQ(result[0], 1.0F);
EXPECT_FLOAT_EQ(result[1], 0.0F);
EXPECT_EQ(calculator.finalize(), &v1);
}
TEST(MedoidCalculator, computeMedoidSpan)
@@ -94,25 +87,11 @@ namespace lms::math::medoidCalculatorTests
EXPECT_FLOAT_EQ(result[1], 0.0F);
}
TEST(MedoidCalculator, getVector)
{
MedoidCalculator<Vector<2, float>> calculator;
calculator.add(Vector<2, float>{ 1.0F, 2.0F });
calculator.add(Vector<2, float>{ 3.0F, 4.0F });
const Vector<2, float>& v0 = calculator.getVector(0U);
const Vector<2, float>& v1 = calculator.getVector(1U);
EXPECT_FLOAT_EQ(v0[0], 1.0F);
EXPECT_FLOAT_EQ(v0[1], 2.0F);
EXPECT_FLOAT_EQ(v1[0], 3.0F);
EXPECT_FLOAT_EQ(v1[1], 4.0F);
}
TEST(MedoidCalculator, clear)
{
MedoidCalculator<Vector<2, float>> calculator;
calculator.add(Vector<2, float>{ 1.0F, 2.0F });
const Vector<2, float> v{ 1.0F, 2.0F };
calculator.add(v);
EXPECT_EQ(calculator.count(), 1);
calculator.clear();
EXPECT_EQ(calculator.count(), 0);