From 53d0007819e86832a6a5017df35b2ea561946ebb Mon Sep 17 00:00:00 2001 From: Pavel Korobov Date: Thu, 19 Jan 2023 16:19:33 +0600 Subject: [PATCH 01/12] Fix incorrect dot_factor usage --- src/annoylib.h | 91 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/src/annoylib.h b/src/annoylib.h index 073d3441..4ce9831d 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -408,6 +408,56 @@ inline void two_means(const vector& nodes, int f, Random& random, bool co } } } + +template +inline void two_means_dot(const vector& nodes, int f, Random& random, Node* p, Node* q) { + /* + This algorithm is a huge heuristic. Empirically it works really well, but I + can't motivate it well. The basic idea is to keep two centroids and assign + points to either one of them. We weight each centroid by the number of points + assigned to it, so to balance it. + */ + static int iteration_steps = 200; + size_t count = nodes.size(); + + size_t i = random.index(count); + size_t j = random.index(count-1); + j += (j >= i); // ensure that i != j + + Distance::template copy_node(p, nodes[i], f); + Distance::template copy_node(q, nodes[j], f); + + Distance::template normalize(p, f); + Distance::template normalize(q, f); + Distance::init_node(p, f); + Distance::init_node(q, f); + + int ic = 1, jc = 1; + for (int l = 0; l < iteration_steps; l++) { + size_t k = random.index(count); + T di = ic * Distance::distance(p, nodes[k], f), + dj = jc * Distance::distance(q, nodes[k], f); + + // if cosine + T norm = sqrt(dot(nodes[k]->v, nodes[k]->v, f) + pow(nodes[k]->dot_factor, 2)); + if (!(norm > T(0))) { + continue; + } + if (di < dj) { + for (int z = 0; z < f; z++) + p->v[z] = (p->v[z] * ic + nodes[k]->v[z] / norm) / (ic + 1); + p->dot_factor = (p->dot_factor * ic + nodes[k]->dot_factor / norm) / (ic + 1); + Distance::init_node(p, f); + ic++; + } else if (dj < di) { + for (int z = 0; z < f; z++) + q->v[z] = (q->v[z] * jc + nodes[k]->v[z] / norm) / (jc + 1); + q->dot_factor = (q->dot_factor * jc + nodes[k]->dot_factor / norm) / (jc + 1); + Distance::init_node(q, f); + jc++; + } + } +} } // namespace struct Base { @@ -486,6 +536,10 @@ struct Angular : Base { return (bool)random.flip(); } template + static inline bool side(const Node* n, const Node* y, int f, Random& random) { + return side(n, y->v, f, random); + } + template static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { Node* p = (Node*)alloca(s); Node* q = (Node*)alloca(s); @@ -538,7 +592,14 @@ struct DotProduct : Angular { } template static inline T distance(const Node* x, const Node* y, int f) { - return -dot(x->v, y->v, f); + // Calculated by analogy with the angular case + T pp = dot(x->v, x->v, f) + x->dot_factor * x->dot_factor; + T qq = dot(y->v, y->v, f) + y->dot_factor * y->dot_factor; + T pq = dot(x->v, y->v, f) + x->dot_factor * y->dot_factor; + T ppqq = pp * qq; + + if (ppqq > 0) return 2.0 - 2.0 * pq / sqrt(ppqq); + else return 2.0; } template @@ -562,7 +623,7 @@ struct DotProduct : Angular { Node* q = (Node*)alloca(s); DotProduct::zero_value(p); DotProduct::zero_value(q); - two_means >(nodes, f, random, true, p, q); + two_means_dot >(nodes, f, random, p, q); for (int z = 0; z < f; z++) n->v[z] = p->v[z] - q->v[z]; n->dot_factor = p->dot_factor - q->dot_factor; @@ -581,7 +642,21 @@ struct DotProduct : Angular { template static inline T margin(const Node* n, const T* y, int f) { - return dot(n->v, y, f) + (n->dot_factor * n->dot_factor); + return dot(n->v, y, f); + } + + template + static inline T margin(const Node* n, const Node* y, int f) { + return dot(n->v, y->v, f) + n->dot_factor * y->dot_factor; + } + + template + static inline bool side(const Node* n, const Node* y, int f, Random& random) { + T dot = margin(n, y, f); + if (dot != 0) + return (dot > 0); + else + return (bool)random.flip(); } template @@ -681,6 +756,10 @@ struct Hamming : Base { return margin(n, y, f); } template + static inline bool side(const Node* n, const Node* y, int f, Random& random) { + return side(n, y->v, f, random); + } + template static inline void create_split(const vector*>& nodes, int f, size_t s, Random& random, Node* n) { size_t cur_size = 0; size_t i = 0; @@ -748,6 +827,10 @@ struct Minkowski : Base { else return (bool)random.flip(); } + template + static inline bool side(const Node* n, const Node* y, int f, Random& random) { + return side(n, y->v, f, random); + } template static inline T pq_distance(T distance, T margin, int child_nr) { if (child_nr == 0) @@ -1290,7 +1373,7 @@ templatev, _f, _random); + bool side = D::side(m, n, _f, _random); children_indices[side].push_back(j); } else { annoylib_showUpdate("No node for index %d?\n", j); From 0c00b062dfd73bea42a789914c0d20a0b50cdb4e Mon Sep 17 00:00:00 2001 From: Pavel Korobov Date: Thu, 19 Jan 2023 17:36:59 +0600 Subject: [PATCH 02/12] Make it the same as in angular --- src/annoylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/annoylib.h b/src/annoylib.h index 4ce9831d..52af785f 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -670,7 +670,7 @@ struct DotProduct : Angular { template static inline T normalized_distance(T distance) { - return -distance; + return sqrt(std::max(distance, T(0))); } template From 66209ad6c13d5155d832771c6065f87956925b01 Mon Sep 17 00:00:00 2001 From: pkorobov Date: Fri, 20 Jan 2023 13:59:13 +0600 Subject: [PATCH 03/12] Remove two_means duplication --- src/annoylib.h | 91 ++++++++++++++++---------------------------------- 1 file changed, 29 insertions(+), 62 deletions(-) diff --git a/src/annoylib.h b/src/annoylib.h index 52af785f..2662445a 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -358,11 +358,6 @@ inline float euclidean_distance(const float* x, const float* y, int f) { #endif - -template -inline T get_norm(T* v, int f) { - return sqrt(dot(v, v, f)); -} template inline void two_means(const vector& nodes, int f, Random& random, bool cosine, Node* p, Node* q) { @@ -391,68 +386,16 @@ inline void two_means(const vector& nodes, int f, Random& random, bool co size_t k = random.index(count); T di = ic * Distance::distance(p, nodes[k], f), dj = jc * Distance::distance(q, nodes[k], f); - T norm = cosine ? get_norm(nodes[k]->v, f) : 1; - if (!(norm > T(0))) { - continue; - } - if (di < dj) { - for (int z = 0; z < f; z++) - p->v[z] = (p->v[z] * ic + nodes[k]->v[z] / norm) / (ic + 1); - Distance::init_node(p, f); - ic++; - } else if (dj < di) { - for (int z = 0; z < f; z++) - q->v[z] = (q->v[z] * jc + nodes[k]->v[z] / norm) / (jc + 1); - Distance::init_node(q, f); - jc++; - } - } -} - -template -inline void two_means_dot(const vector& nodes, int f, Random& random, Node* p, Node* q) { - /* - This algorithm is a huge heuristic. Empirically it works really well, but I - can't motivate it well. The basic idea is to keep two centroids and assign - points to either one of them. We weight each centroid by the number of points - assigned to it, so to balance it. - */ - static int iteration_steps = 200; - size_t count = nodes.size(); - - size_t i = random.index(count); - size_t j = random.index(count-1); - j += (j >= i); // ensure that i != j - - Distance::template copy_node(p, nodes[i], f); - Distance::template copy_node(q, nodes[j], f); - - Distance::template normalize(p, f); - Distance::template normalize(q, f); - Distance::init_node(p, f); - Distance::init_node(q, f); - - int ic = 1, jc = 1; - for (int l = 0; l < iteration_steps; l++) { - size_t k = random.index(count); - T di = ic * Distance::distance(p, nodes[k], f), - dj = jc * Distance::distance(q, nodes[k], f); - - // if cosine - T norm = sqrt(dot(nodes[k]->v, nodes[k]->v, f) + pow(nodes[k]->dot_factor, 2)); + T norm = cosine ? Distance::template get_norm(nodes[k], f) : 1; if (!(norm > T(0))) { continue; } if (di < dj) { - for (int z = 0; z < f; z++) - p->v[z] = (p->v[z] * ic + nodes[k]->v[z] / norm) / (ic + 1); - p->dot_factor = (p->dot_factor * ic + nodes[k]->dot_factor / norm) / (ic + 1); + Distance::update_mean(p, nodes[k], norm, ic, f); Distance::init_node(p, f); ic++; } else if (dj < di) { - for (int z = 0; z < f; z++) - q->v[z] = (q->v[z] * jc + nodes[k]->v[z] / norm) / (jc + 1); - q->dot_factor = (q->dot_factor * jc + nodes[k]->dot_factor / norm) / (jc + 1); + Distance::update_mean(q, nodes[k], norm, jc, f); Distance::init_node(q, f); jc++; } @@ -477,14 +420,25 @@ struct Base { memcpy(dest->v, source->v, f * sizeof(T)); } + template + static inline T get_norm(Node* node, int f) { + return sqrt(dot(node->v, node->v, f)); + } + template static inline void normalize(Node* node, int f) { - T norm = get_norm(node->v, f); + T norm = Base::get_norm(node, f); if (norm > 0) { for (int z = 0; z < f; z++) node->v[z] /= norm; } } + + template + static inline void update_mean(Node* mean, Node* new_node, T norm, int c, int f) { + for (int z = 0; z < f; z++) + mean->v[z] = (mean->v[z] * c + new_node->v[z] / norm) / (c + 1); + } }; struct Angular : Base { @@ -590,6 +544,19 @@ struct DotProduct : Angular { static const char* name() { return "dot"; } + + template + static inline T get_norm(Node* node, int f) { + return sqrt(dot(node->v, node->v, f) + node->dot_factor * node->dot_factor); + } + + template + static inline void update_mean(Node* mean, Node* new_node, T norm, int c, int f) { + for (int z = 0; z < f; z++) + mean->v[z] = (mean->v[z] * c + new_node->v[z] / norm) / (c + 1); + mean->dot_factor = (mean->dot_factor * c + new_node->dot_factor / norm) / (c + 1); + } + template static inline T distance(const Node* x, const Node* y, int f) { // Calculated by analogy with the angular case @@ -623,7 +590,7 @@ struct DotProduct : Angular { Node* q = (Node*)alloca(s); DotProduct::zero_value(p); DotProduct::zero_value(q); - two_means_dot >(nodes, f, random, p, q); + two_means >(nodes, f, random, true, p, q); for (int z = 0; z < f; z++) n->v[z] = p->v[z] - q->v[z]; n->dot_factor = p->dot_factor - q->dot_factor; From 66b74dae45e430b6a3209d686aab24f6749338fe Mon Sep 17 00:00:00 2001 From: pkorobov Date: Thu, 26 Jan 2023 19:18:57 +0600 Subject: [PATCH 04/12] Fix distance --- src/annoylib.h | 38 +++++++++++++++++++++++++++++++++++--- test/dot_index_test.py | 1 + 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/annoylib.h b/src/annoylib.h index 2662445a..630e1068 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -410,6 +410,12 @@ struct Base { // on the entire set of nodes passed into this index. } + template + static inline void postprocess(void* nodes, size_t _s, const S node_count, const int f) { + // Override this in specific metric structs below if you need to do any post-processing + // on the entire set of nodes passed into this index. + } + template static inline void zero_value(Node* dest) { // Initialize any fields that require sane defaults within this node. @@ -538,6 +544,8 @@ struct DotProduct : Angular { S n_descendants; S children[2]; // Will possibly store more than 2 T dot_factor; + T norm; + bool built; T v[ANNOYLIB_V_ARRAY_SIZE]; }; @@ -559,9 +567,15 @@ struct DotProduct : Angular { template static inline T distance(const Node* x, const Node* y, int f) { + if (x->built || y->built) { + // if index is already built we only need order of distances + // thus, we can return dot product itself + return -dot(x->v, y->v, f); + } + // Calculated by analogy with the angular case - T pp = dot(x->v, x->v, f) + x->dot_factor * x->dot_factor; - T qq = dot(y->v, y->v, f) + y->dot_factor * y->dot_factor; + T pp = x->norm ? x->norm : dot(x->v, x->v, f) + x->dot_factor * x->dot_factor; + T qq = y->norm ? y->norm : dot(y->v, y->v, f) + y->dot_factor * y->dot_factor; T pq = dot(x->v, y->v, f) + x->dot_factor * y->dot_factor; T ppqq = pp * qq; @@ -572,10 +586,12 @@ struct DotProduct : Angular { template static inline void zero_value(Node* dest) { dest->dot_factor = 0; + dest->norm = 0; } template static inline void init_node(Node* n, int f) { + n->built = false; } template @@ -637,7 +653,7 @@ struct DotProduct : Angular { template static inline T normalized_distance(T distance) { - return sqrt(std::max(distance, T(0))); + return -distance; } template @@ -651,6 +667,7 @@ struct DotProduct : Angular { T d = dot(node->v, node->v, f); T norm = d < 0 ? 0 : sqrt(d); node->dot_factor = norm; + node->built = false; } // Step two: find the maximum norm @@ -669,9 +686,21 @@ struct DotProduct : Angular { T squared_norm_diff = pow(max_norm, static_cast(2.0)) - pow(node_norm, static_cast(2.0)); T dot_factor = squared_norm_diff < 0 ? 0 : sqrt(squared_norm_diff); + node->norm = pow(max_norm, static_cast(2.0)); node->dot_factor = dot_factor; } } + + template + static inline void postprocess(void* nodes, size_t _s, const S node_count, const int f) { + for (S i = 0; i < node_count; i++) { + Node* node = get_node_ptr(nodes, _s, i); + // we need to remove dot_factor to correctly search by item_id when an index is already built + node->dot_factor = 0; + // when an index is built, we will remember it in index nodes to compute distances differently + node->built = true; + } + } }; struct Hamming : Base { @@ -1037,6 +1066,9 @@ template(_nodes, _s, _n_items, _f); + _built = true; return true; } diff --git a/test/dot_index_test.py b/test/dot_index_test.py index 302a64b4..62762e51 100644 --- a/test/dot_index_test.py +++ b/test/dot_index_test.py @@ -57,6 +57,7 @@ def test_dist(self): i.add_item(0, [0, 1]) i.add_item(1, [1, 1]) i.add_item(2, [0, 0]) + i.build(10) self.assertAlmostEqual(i.get_distance(0, 1), 1.0) self.assertAlmostEqual(i.get_distance(1, 2), 0.0) From e75dfc388bac13151623383a5bd1035817902885 Mon Sep 17 00:00:00 2001 From: pkorobov Date: Thu, 26 Jan 2023 19:19:26 +0600 Subject: [PATCH 05/12] Add extra accuracy tests --- test/accuracy_test.py | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/test/accuracy_test.py b/test/accuracy_test.py index 441e5c28..a2301a0a 100644 --- a/test/accuracy_test.py +++ b/test/accuracy_test.py @@ -27,23 +27,35 @@ from nose.plugins.attrib import attr class AccuracyTest(unittest.TestCase): - def _get_index(self, dataset): - url = 'http://vectors.erikbern.com/%s.hdf5' % dataset + def _get_index(self, dataset, custom_distance=None, custom_dim=None): + url = 'http://ann-benchmarks.com/%s.hdf5' % dataset vectors_fn = os.path.join('test', dataset + '.hdf5') - index_fn = os.path.join('test', dataset + '.annoy') if not os.path.exists(vectors_fn): print('downloading', url, '->', vectors_fn) urlretrieve(url, vectors_fn) dataset_f = h5py.File(vectors_fn, 'r') + distance = dataset_f.attrs['distance'] + if custom_distance: + distance = custom_distance + f = dataset_f['train'].shape[1] + if custom_dim: + f = custom_dim + + if custom_distance: + dataset = dataset.rsplit('-', 2)[0] + "-%d-%s" % (f, custom_distance) + index_fn = os.path.join('test', dataset + '.annoy') + annoy = AnnoyIndex(f, distance) if not os.path.exists(index_fn): print('adding items', distance, f) for i, v in enumerate(dataset_f['train']): + if len(v) > f: + v = v[:f] annoy.add_item(i, v) print('building index') @@ -51,15 +63,17 @@ def _get_index(self, dataset): annoy.save(index_fn) else: annoy.load(index_fn) - return annoy, dataset_f + return annoy, dataset_f, dataset - def _test_index(self, dataset, exp_accuracy): - annoy, dataset_f = self._get_index(dataset) + def _test_index(self, dataset, exp_accuracy, custom_metric=None, custom_dim=None): + annoy, dataset_f, dataset = self._get_index(dataset, custom_metric, custom_dim) n, k = 0, 0 for i, v in enumerate(dataset_f['test']): - js_fast = annoy.get_nns_by_vector(v, 10, 1000) + if custom_dim: + v = v[:custom_dim] + js_fast = annoy.get_nns_by_vector(v, 10, 10000) js_real = dataset_f['neighbors'][i][:10] assert len(js_fast) == 10 assert len(js_real) == 10 @@ -80,3 +94,9 @@ def test_nytimes_16(self): def test_fashion_mnist(self): self._test_index('fashion-mnist-784-euclidean', 90.00) + + def test_lastfm_dot(self): + self._test_index('lastfm-64-dot', 60.00, 'dot', 64) + + def test_lastfm_angular(self): + self._test_index('lastfm-64-dot', 60.00, 'angular', 65) From 7fef46a0bc0b31e940941425aff6f118a458cd85 Mon Sep 17 00:00:00 2001 From: pkorobov Date: Thu, 26 Jan 2023 19:27:24 +0600 Subject: [PATCH 06/12] Add reference --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a686810a..c685223e 100644 --- a/README.rst +++ b/README.rst @@ -122,7 +122,7 @@ We do this k times so that we get a forest of trees. k has to be tuned to your n Hamming distance (contributed by `Martin Aumüller `__) packs the data into 64-bit integers under the hood and uses built-in bit count primitives so it could be quite fast. All splits are axis-aligned. -Dot Product distance (contributed by `Peter Sobot `__) reduces the provided vectors from dot (or "inner-product") space to a more query-friendly cosine space using `a method by Bachrach et al., at Microsoft Research, published in 2014 `__. +Dot Product distance (contributed by `Peter Sobot `__ and `Pavel Korobov `__) reduces the provided vectors from dot (or "inner-product") space to a more query-friendly cosine space using `a method by Bachrach et al., at Microsoft Research, published in 2014 `__. From d68d0252070e368b6f0d81fa1fe0896ada07cd22 Mon Sep 17 00:00:00 2001 From: pkorobov Date: Thu, 26 Jan 2023 23:08:42 +0600 Subject: [PATCH 07/12] Improve some details --- src/annoylib.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/annoylib.h b/src/annoylib.h index 630e1068..107b3d29 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -568,8 +568,8 @@ struct DotProduct : Angular { template static inline T distance(const Node* x, const Node* y, int f) { if (x->built || y->built) { - // if index is already built we only need order of distances - // thus, we can return dot product itself + // When index is already built, we don't need angular distances to retrieve NNs + // Thus, we can return dot product scores itself return -dot(x->v, y->v, f); } @@ -586,12 +586,12 @@ struct DotProduct : Angular { template static inline void zero_value(Node* dest) { dest->dot_factor = 0; - dest->norm = 0; } template static inline void init_node(Node* n, int f) { n->built = false; + n->norm = dot(n->v, n->v, f) + n->dot_factor * n->dot_factor; } template @@ -695,9 +695,7 @@ struct DotProduct : Angular { static inline void postprocess(void* nodes, size_t _s, const S node_count, const int f) { for (S i = 0; i < node_count; i++) { Node* node = get_node_ptr(nodes, _s, i); - // we need to remove dot_factor to correctly search by item_id when an index is already built - node->dot_factor = 0; - // when an index is built, we will remember it in index nodes to compute distances differently + // When an index is built, we will remember it in index item nodes to compute distances differently node->built = true; } } From a436f782f792276662a2fb7cdb2d03672a7e4b87 Mon Sep 17 00:00:00 2001 From: pkorobov Date: Fri, 27 Jan 2023 10:07:03 +0600 Subject: [PATCH 08/12] Remove redundant whitespace --- src/annoylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/annoylib.h b/src/annoylib.h index 107b3d29..d77b2991 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -634,7 +634,7 @@ struct DotProduct : Angular { } template - static inline bool side(const Node* n, const Node* y, int f, Random& random) { + static inline bool side(const Node* n, const Node* y, int f, Random& random) { T dot = margin(n, y, f); if (dot != 0) return (dot > 0); From 359aaa52e9e580b324ae82febce1fef508e244a6 Mon Sep 17 00:00:00 2001 From: pkorobov Date: Mon, 30 Jan 2023 15:33:12 +0600 Subject: [PATCH 09/12] Update comments --- src/annoylib.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/annoylib.h b/src/annoylib.h index d77b2991..42ac9c08 100644 --- a/src/annoylib.h +++ b/src/annoylib.h @@ -539,7 +539,9 @@ struct DotProduct : Angular { template struct Node { /* - * This is an extension of the Angular node with an extra attribute for the scaled norm. + * This is an extension of the Angular node with extra attributes for the DotProduct metric. + * It has dot_factor which is needed to reduce the task to Angular distance metric (see the preprocess method) + * and also a built flag that helps to compute exact dot products when an index is already built. */ S n_descendants; S children[2]; // Will possibly store more than 2 From 7ee2dcecf39e044fa0fb3a2a79a290dd524914ad Mon Sep 17 00:00:00 2001 From: Erik Bernhardsson Date: Fri, 18 Aug 2023 00:24:39 +0200 Subject: [PATCH 10/12] Update accuracy_test.py fix merge resolve typo --- test/accuracy_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/accuracy_test.py b/test/accuracy_test.py index c61bcf2d..ddc8c0c0 100644 --- a/test/accuracy_test.py +++ b/test/accuracy_test.py @@ -65,7 +65,7 @@ def _get_index(dataset, custom_distance=None, custom_dim=None): def _test_index(dataset, exp_accuracy, custom_metric=None, custom_dim=None): - annoy, dataset_f, dataset = self._get_index(dataset, custom_metric, custom_dim) + annoy, dataset_f, dataset = _get_index(dataset, custom_metric, custom_dim) n, k = 0, 0 From 9ae98019512b133a1302c933aabf83953691cc7e Mon Sep 17 00:00:00 2001 From: Erik Bernhardsson Date: Fri, 18 Aug 2023 10:52:21 +0200 Subject: [PATCH 11/12] Fix another merge conflict --- test/accuracy_test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/accuracy_test.py b/test/accuracy_test.py index ddc8c0c0..21233d6b 100644 --- a/test/accuracy_test.py +++ b/test/accuracy_test.py @@ -61,7 +61,7 @@ def _get_index(dataset, custom_distance=None, custom_dim=None): annoy.save(index_fn) else: annoy.load(index_fn) - return annoy, dataset_f + return annoy, dataset_f, dataset def _test_index(dataset, exp_accuracy, custom_metric=None, custom_dim=None): @@ -100,5 +100,6 @@ def test_nytimes_16(): def test_lastfm_dot(self): _test_index('lastfm-64-dot', 60.00, 'dot', 64) + def test_lastfm_angular(self): _test_index('lastfm-64-dot', 60.00, 'angular', 65) From a70dac20ab471491bfa571efde4961b717652491 Mon Sep 17 00:00:00 2001 From: Erik Bernhardsson Date: Fri, 18 Aug 2023 11:09:53 +0200 Subject: [PATCH 12/12] Fix another merge conflict (remove self from arguments) --- test/accuracy_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/accuracy_test.py b/test/accuracy_test.py index 21233d6b..4b3f6530 100644 --- a/test/accuracy_test.py +++ b/test/accuracy_test.py @@ -97,9 +97,9 @@ def test_nytimes_16(): _test_index("nytimes-16-angular", 80.00) -def test_lastfm_dot(self): +def test_lastfm_dot(): _test_index('lastfm-64-dot', 60.00, 'dot', 64) -def test_lastfm_angular(self): +def test_lastfm_angular(): _test_index('lastfm-64-dot', 60.00, 'angular', 65)