From 05deb8c276f04065ab01731c1f03eeac240dc87b Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Wed, 13 Nov 2019 22:41:49 +0100 Subject: [PATCH 01/13] chunks return a map of index to chunk --- metric_learn/constraints.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index b71c9b96..e7e23444 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -23,6 +23,7 @@ def __init__(self, partial_labels): self.num_points, = partial_labels.shape self.known_label_idx, = np.where(partial_labels >= 0) self.known_labels = partial_labels[self.known_label_idx] + self.partial_labels = partial_labels def adjacency_matrix(self, num_constraints, random_state=None): random_state = check_random_state(random_state) @@ -76,9 +77,11 @@ def chunks(self, num_chunks=100, chunk_size=2, random_state=None): the random state object to be passed must be a numpy random seed """ random_state = check_random_state(random_state) - chunks = -np.ones_like(self.known_label_idx, dtype=int) - uniq, lookup = np.unique(self.known_labels, return_inverse=True) - all_inds = [set(np.where(lookup == c)[0]) for c in xrange(len(uniq))] + chunks = -np.ones_like(self.partial_labels, dtype=int) + uniq, lookup = np.unique(self.partial_labels, return_inverse=True) + if self.num_points != len(self.known_labels): + uniq = uniq[uniq >= 0] + all_inds = [set(np.where(lookup == c)[0]) for c in uniq] max_chunks = int(np.sum([len(s) // chunk_size for s in all_inds])) if max_chunks < num_chunks: raise ValueError(('Not enough possible chunks of %d elements in each' From 7a6f3027bd3f5e13bf8f76917e41618ff934e925 Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Wed, 13 Nov 2019 23:04:36 +0100 Subject: [PATCH 02/13] maj --- metric_learn/constraints.py | 6 ++++-- test.py | 8 ++++++++ test/test_constraints.py | 4 ++-- test_constraints.py | 7 +++++++ 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 test.py create mode 100644 test_constraints.py diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index e7e23444..523f1ed5 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -80,9 +80,11 @@ def chunks(self, num_chunks=100, chunk_size=2, random_state=None): chunks = -np.ones_like(self.partial_labels, dtype=int) uniq, lookup = np.unique(self.partial_labels, return_inverse=True) if self.num_points != len(self.known_labels): - uniq = uniq[uniq >= 0] - all_inds = [set(np.where(lookup == c)[0]) for c in uniq] + unknown_uniq = np.where(uniq < 0)[0] + all_inds = [set(np.where(lookup == c)[0]) for c in xrange(len(uniq)) + if c not in unknown_uniq] max_chunks = int(np.sum([len(s) // chunk_size for s in all_inds])) + print(max_chunks) if max_chunks < num_chunks: raise ValueError(('Not enough possible chunks of %d elements in each' ' class to form expected %d chunks - maximum number' diff --git a/test.py b/test.py new file mode 100644 index 00000000..1c9099e8 --- /dev/null +++ b/test.py @@ -0,0 +1,8 @@ +from metric_learn import RCA_Supervised +import numpy as np + +X = np.random.rand(5, 2) +y = [1, 1, -1, 2, 2] + +rca = RCA_Supervised(num_chunks=2) +rca.fit(X, y) diff --git a/test/test_constraints.py b/test/test_constraints.py index a135985e..3547349b 100644 --- a/test/test_constraints.py +++ b/test/test_constraints.py @@ -34,7 +34,7 @@ def test_exact_num_points_for_chunks(num_chunks, chunk_size): chunks = constraints.chunks(num_chunks=num_chunks, chunk_size=chunk_size, random_state=SEED) - chunk_no, size_each_chunk = np.unique(chunks, return_counts=True) + chunk_no, size_each_chunk = np.unique(chunks[chunks > 0], return_counts=True) np.testing.assert_array_equal(size_each_chunk, chunk_size) assert chunk_no.shape[0] == num_chunks @@ -60,4 +60,4 @@ def test_chunk_case_one_miss_point(num_chunks, chunk_size): if __name__ == '__main__': - unittest.main() + test_exact_num_points_for_chunks(5, 10) diff --git a/test_constraints.py b/test_constraints.py new file mode 100644 index 00000000..feaeb275 --- /dev/null +++ b/test_constraints.py @@ -0,0 +1,7 @@ +from metric_learn.constraints import Constraints +partial_labels = [1, 2, 2, 1, -1, 3, 3] +cons = Constraints(partial_labels) +cons.chunks(num_chunks=3, chunk_size=2) +chunks = cons.chunks(num_chunks=3, chunk_size=2) +expected_chunk = [0, 1, 1, 0, -1, 2, 2] +print(chunks, expected_chunk) From d08c2d85a904e29d1520924c96b4b4cb5b0e8180 Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Thu, 14 Nov 2019 17:29:02 +0100 Subject: [PATCH 03/13] maj --- metric_learn/constraints.py | 4 +--- test.py | 8 -------- test/test_constraints.py | 8 ++------ test_constraints.py | 7 ------- 4 files changed, 3 insertions(+), 24 deletions(-) delete mode 100644 test.py delete mode 100644 test_constraints.py diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index 523f1ed5..e95923e2 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -79,12 +79,10 @@ def chunks(self, num_chunks=100, chunk_size=2, random_state=None): random_state = check_random_state(random_state) chunks = -np.ones_like(self.partial_labels, dtype=int) uniq, lookup = np.unique(self.partial_labels, return_inverse=True) - if self.num_points != len(self.known_labels): - unknown_uniq = np.where(uniq < 0)[0] + unknown_uniq = np.where(uniq < 0)[0] all_inds = [set(np.where(lookup == c)[0]) for c in xrange(len(uniq)) if c not in unknown_uniq] max_chunks = int(np.sum([len(s) // chunk_size for s in all_inds])) - print(max_chunks) if max_chunks < num_chunks: raise ValueError(('Not enough possible chunks of %d elements in each' ' class to form expected %d chunks - maximum number' diff --git a/test.py b/test.py deleted file mode 100644 index 1c9099e8..00000000 --- a/test.py +++ /dev/null @@ -1,8 +0,0 @@ -from metric_learn import RCA_Supervised -import numpy as np - -X = np.random.rand(5, 2) -y = [1, 1, -1, 2, 2] - -rca = RCA_Supervised(num_chunks=2) -rca.fit(X, y) diff --git a/test/test_constraints.py b/test/test_constraints.py index 3547349b..7e6ac51c 100644 --- a/test/test_constraints.py +++ b/test/test_constraints.py @@ -1,4 +1,3 @@ -import unittest import pytest import numpy as np from sklearn.utils import shuffle @@ -34,7 +33,8 @@ def test_exact_num_points_for_chunks(num_chunks, chunk_size): chunks = constraints.chunks(num_chunks=num_chunks, chunk_size=chunk_size, random_state=SEED) - chunk_no, size_each_chunk = np.unique(chunks[chunks > 0], return_counts=True) + chunk_no, size_each_chunk = np.unique(chunks[chunks >= 0], + return_counts=True) np.testing.assert_array_equal(size_each_chunk, chunk_size) assert chunk_no.shape[0] == num_chunks @@ -57,7 +57,3 @@ def test_chunk_case_one_miss_point(num_chunks, chunk_size): ) % (chunk_size, num_chunks, num_chunks - 1)) assert str(e.value) == expected_message - - -if __name__ == '__main__': - test_exact_num_points_for_chunks(5, 10) diff --git a/test_constraints.py b/test_constraints.py deleted file mode 100644 index feaeb275..00000000 --- a/test_constraints.py +++ /dev/null @@ -1,7 +0,0 @@ -from metric_learn.constraints import Constraints -partial_labels = [1, 2, 2, 1, -1, 3, 3] -cons = Constraints(partial_labels) -cons.chunks(num_chunks=3, chunk_size=2) -chunks = cons.chunks(num_chunks=3, chunk_size=2) -expected_chunk = [0, 1, 1, 0, -1, 2, 2] -print(chunks, expected_chunk) From 6c4ba21d376f1e02909272d3889004afd73123b3 Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Thu, 21 Nov 2019 15:46:33 +0100 Subject: [PATCH 04/13] remove storing of known labels --- metric_learn/constraints.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index e95923e2..e8688cb5 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -20,10 +20,8 @@ class Constraints(object): def __init__(self, partial_labels): '''partial_labels : int arraylike, -1 indicating unknown label''' partial_labels = np.asanyarray(partial_labels, dtype=int) - self.num_points, = partial_labels.shape - self.known_label_idx, = np.where(partial_labels >= 0) - self.known_labels = partial_labels[self.known_label_idx] self.partial_labels = partial_labels + self.num_points, = partial_labels.shape def adjacency_matrix(self, num_constraints, random_state=None): random_state = check_random_state(random_state) @@ -51,17 +49,19 @@ def positive_negative_pairs(self, num_constraints, same_length=False, def _pairs(self, num_constraints, same_label=True, max_iter=10, random_state=np.random): - num_labels = len(self.known_labels) + known_label_idx, = np.where(self.partial_labels >= 0) + known_labels = self.partial_labels[known_label_idx] + num_labels = len(known_labels) ab = set() it = 0 while it < max_iter and len(ab) < num_constraints: nc = num_constraints - len(ab) for aidx in random_state.randint(num_labels, size=nc): if same_label: - mask = self.known_labels[aidx] == self.known_labels + mask = known_labels[aidx] == known_labels mask[aidx] = False # avoid identity pairs else: - mask = self.known_labels[aidx] != self.known_labels + mask = known_labels[aidx] != known_labels b_choices, = np.where(mask) if len(b_choices) > 0: ab.add((aidx, random_state.choice(b_choices))) From 2b1308d0d91e6208170715d0f0323d68443fa3c6 Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Thu, 21 Nov 2019 16:37:23 +0100 Subject: [PATCH 05/13] typo --- metric_learn/constraints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index e8688cb5..e112fdc8 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -70,7 +70,7 @@ def _pairs(self, num_constraints, same_label=True, max_iter=10, warnings.warn("Only generated %d %s constraints (requested %d)" % ( len(ab), 'positive' if same_label else 'negative', num_constraints)) ab = np.array(list(ab)[:num_constraints], dtype=int) - return self.known_label_idx[ab.T] + return known_label_idx[ab.T] def chunks(self, num_chunks=100, chunk_size=2, random_state=None): """ From 8003c0d63de36c0df65cabb9f6473c0fe524e5aa Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Thu, 21 Nov 2019 16:42:34 +0100 Subject: [PATCH 06/13] no self.num_points --- metric_learn/constraints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index e112fdc8..a5c3ad59 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -21,7 +21,6 @@ def __init__(self, partial_labels): '''partial_labels : int arraylike, -1 indicating unknown label''' partial_labels = np.asanyarray(partial_labels, dtype=int) self.partial_labels = partial_labels - self.num_points, = partial_labels.shape def adjacency_matrix(self, num_constraints, random_state=None): random_state = check_random_state(random_state) @@ -31,7 +30,8 @@ def adjacency_matrix(self, num_constraints, random_state=None): col = np.concatenate((b, d)) data = np.ones_like(row, dtype=int) data[len(a):] = -1 - adj = coo_matrix((data, (row, col)), shape=(self.num_points,) * 2) + num_points = self.partial_labels.shape[0] + adj = coo_matrix((data, (row, col)), shape=(num_points,) * 2) # symmetrize return adj + adj.T From 57b559cbf05566696fb6d02af8b6a632c8f04d0d Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Thu, 28 Nov 2019 16:06:53 +0100 Subject: [PATCH 07/13] tests for unlabeled, repairs chunk generation --- metric_learn/constraints.py | 13 ------------- test/metric_learn_test.py | 13 +++++++++++++ test/test_constraints.py | 12 ++++++++++++ 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index a5c3ad59..49eac338 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -22,19 +22,6 @@ def __init__(self, partial_labels): partial_labels = np.asanyarray(partial_labels, dtype=int) self.partial_labels = partial_labels - def adjacency_matrix(self, num_constraints, random_state=None): - random_state = check_random_state(random_state) - a, b, c, d = self.positive_negative_pairs(num_constraints, - random_state=random_state) - row = np.concatenate((a, c)) - col = np.concatenate((b, d)) - data = np.ones_like(row, dtype=int) - data[len(a):] = -1 - num_points = self.partial_labels.shape[0] - adj = coo_matrix((data, (row, col)), shape=(num_points,) * 2) - # symmetrize - return adj + adj.T - def positive_negative_pairs(self, num_constraints, same_length=False, random_state=None): random_state = check_random_state(random_state) diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index f713a059..4b89fb57 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -1136,6 +1136,19 @@ def test_changed_behaviour_warning_random_state(self): rca_supervised.fit(X, y) assert any(msg == str(wrn.message) for wrn in raised_warning) + def test_unknown_labels(self): + n = 100 + X, y = make_classification(random_state=42, n_samples=2 * n) + y2 = np.concatenate((y[:n], np.ones(n))) + + rca = RCA_Supervised(num_chunks=2) + rca.fit(X[:n], y[:n]) + + rca2 = RCA_Supervised(num_chunks=2) + rca2.fit(X, y2) + + np.testing.assert_array_equal(rca.components_, rca2.components_) + @pytest.mark.parametrize('num_dims', [None, 2]) def test_deprecation_num_dims_rca(num_dims): diff --git a/test/test_constraints.py b/test/test_constraints.py index 7e6ac51c..243028f6 100644 --- a/test/test_constraints.py +++ b/test/test_constraints.py @@ -57,3 +57,15 @@ def test_chunk_case_one_miss_point(num_chunks, chunk_size): ) % (chunk_size, num_chunks, num_chunks - 1)) assert str(e.value) == expected_message + + +@pytest.mark.parametrize("num_chunks, chunk_size", [(5, 10), (10, 50)]) +def test_unknown_labels_not_in_chunks(num_chunks, chunk_size): + """Checks that unknown labels are not assigned to any chunk.""" + labels = gen_labels_for_chunks(num_chunks, chunk_size) + + constraints = Constraints(labels) + chunks = constraints.chunks(num_chunks=num_chunks, chunk_size=chunk_size, + random_state=SEED) + + assert np.all(chunks[labels < 0] < 0) From 72a9ee53d848d5ae0058d8a281e3531c8e2f0627 Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Thu, 28 Nov 2019 16:20:59 +0100 Subject: [PATCH 08/13] maj --- metric_learn/constraints.py | 1 - 1 file changed, 1 deletion(-) diff --git a/metric_learn/constraints.py b/metric_learn/constraints.py index 49eac338..752ca6e0 100644 --- a/metric_learn/constraints.py +++ b/metric_learn/constraints.py @@ -5,7 +5,6 @@ import numpy as np import warnings from six.moves import xrange -from scipy.sparse import coo_matrix from sklearn.utils import check_random_state __all__ = ['Constraints'] From 0c98432ea3b1a5d9b55c71e615df6455d827ca4a Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Tue, 10 Dec 2019 13:08:48 +0100 Subject: [PATCH 09/13] testing diff features --- test/metric_learn_test.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index 4b89fb57..eff18ca3 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -1138,8 +1138,10 @@ def test_changed_behaviour_warning_random_state(self): def test_unknown_labels(self): n = 100 - X, y = make_classification(random_state=42, n_samples=2 * n) - y2 = np.concatenate((y[:n], np.ones(n))) + X, y = make_classification(random_state=42, n_samples=2 * n, + n_features=3, n_redundant=0, + n_informative=3) + y2 = np.concatenate((y[:n], -np.ones(n))) rca = RCA_Supervised(num_chunks=2) rca.fit(X[:n], y[:n]) @@ -1147,6 +1149,9 @@ def test_unknown_labels(self): rca2 = RCA_Supervised(num_chunks=2) rca2.fit(X, y2) + assert not np.any(np.isnan(rca.components_)) + assert not np.any(np.isnan(rca2.components_)) + np.testing.assert_array_equal(rca.components_, rca2.components_) From 02168eb034fdce449e2529817faba801257e172d Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Tue, 10 Dec 2019 18:49:12 +0100 Subject: [PATCH 10/13] corrected test --- test/metric_learn_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index eff18ca3..d10acf47 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -1137,16 +1137,16 @@ def test_changed_behaviour_warning_random_state(self): assert any(msg == str(wrn.message) for wrn in raised_warning) def test_unknown_labels(self): - n = 100 + n = 200 + num_chunks = 50 X, y = make_classification(random_state=42, n_samples=2 * n, - n_features=3, n_redundant=0, - n_informative=3) + n_features=6, n_informative=6, n_redundant=0) y2 = np.concatenate((y[:n], -np.ones(n))) - rca = RCA_Supervised(num_chunks=2) + rca = RCA_Supervised(num_chunks=num_chunks, random_state=42) rca.fit(X[:n], y[:n]) - rca2 = RCA_Supervised(num_chunks=2) + rca2 = RCA_Supervised(num_chunks=num_chunks, random_state=42) rca2.fit(X, y2) assert not np.any(np.isnan(rca.components_)) From 03ad650ebe6c91c1cd08e30bb0cbc89daeb55cfc Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Tue, 10 Dec 2019 19:12:47 +0100 Subject: [PATCH 11/13] diff warning --- metric_learn/rca.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/metric_learn/rca.py b/metric_learn/rca.py index 2a9ab1e8..38373044 100644 --- a/metric_learn/rca.py +++ b/metric_learn/rca.py @@ -96,7 +96,8 @@ def _check_dimension(self, rank, X): if rank < d: warnings.warn('The inner covariance matrix is not invertible, ' 'so the transformation matrix may contain Nan values. ' - 'You should reduce the dimensionality of your input,' + 'You should remove any linearly dependent features and/or' + 'reduce the dimensionality of your input,' 'for instance using `sklearn.decomposition.PCA` as a ' 'preprocessing step.') From b93c1152b18c0d6e394c1ffdc6960841562df463 Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Fri, 20 Dec 2019 11:38:19 +0100 Subject: [PATCH 12/13] maj --- metric_learn/rca.py | 4 ++-- test/metric_learn_test.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/metric_learn/rca.py b/metric_learn/rca.py index 38373044..591d0384 100644 --- a/metric_learn/rca.py +++ b/metric_learn/rca.py @@ -96,8 +96,8 @@ def _check_dimension(self, rank, X): if rank < d: warnings.warn('The inner covariance matrix is not invertible, ' 'so the transformation matrix may contain Nan values. ' - 'You should remove any linearly dependent features and/or' - 'reduce the dimensionality of your input,' + 'You should remove any linearly dependent features and/or ' + 'reduce the dimensionality of your input, ' 'for instance using `sklearn.decomposition.PCA` as a ' 'preprocessing step.') diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index d10acf47..ebd1ec5b 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -1100,9 +1100,11 @@ def test_rank_deficient_returns_warning(self): rca = RCA() msg = ('The inner covariance matrix is not invertible, ' 'so the transformation matrix may contain Nan values. ' - 'You should reduce the dimensionality of your input,' + 'You should remove any linearly dependent features and/or ' + 'reduce the dimensionality of your input, ' 'for instance using `sklearn.decomposition.PCA` as a ' 'preprocessing step.') + with pytest.warns(None) as raised_warnings: rca.fit(X, y) assert any(str(w.message) == msg for w in raised_warnings) From 5289e6659eb9f86282dfce9f45f3bc0b0cc46da3 Mon Sep 17 00:00:00 2001 From: RobinVogel Date: Mon, 23 Dec 2019 10:30:16 +0100 Subject: [PATCH 13/13] added parameter bound test --- metric_learn/rca.py | 10 ++++++++++ test/metric_learn_test.py | 17 +++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/metric_learn/rca.py b/metric_learn/rca.py index 591d0384..204bd360 100644 --- a/metric_learn/rca.py +++ b/metric_learn/rca.py @@ -93,6 +93,7 @@ def __init__(self, n_components=None, num_dims='deprecated', def _check_dimension(self, rank, X): d = X.shape[1] + if rank < d: warnings.warn('The inner covariance matrix is not invertible, ' 'so the transformation matrix may contain Nan values. ' @@ -242,4 +243,13 @@ def fit(self, X, y, random_state='deprecated'): chunks = Constraints(y).chunks(num_chunks=self.num_chunks, chunk_size=self.chunk_size, random_state=self.random_state) + + if self.num_chunks * (self.chunk_size - 1) < X.shape[1]: + warnings.warn('Due to the parameters of RCA_Supervised, ' + 'the inner covariance matrix is not invertible, ' + 'so the transformation matrix will contain Nan values. ' + 'Increase the number or size of the chunks to correct ' + 'this problem.' + ) + return RCA.fit(self, X, chunks) diff --git a/test/metric_learn_test.py b/test/metric_learn_test.py index ebd1ec5b..5a271890 100644 --- a/test/metric_learn_test.py +++ b/test/metric_learn_test.py @@ -1156,6 +1156,23 @@ def test_unknown_labels(self): np.testing.assert_array_equal(rca.components_, rca2.components_) + def test_bad_parameters(self): + n = 200 + num_chunks = 3 + X, y = make_classification(random_state=42, n_samples=n, + n_features=6, n_informative=6, n_redundant=0) + + rca = RCA_Supervised(num_chunks=num_chunks, random_state=42) + msg = ('Due to the parameters of RCA_Supervised, ' + 'the inner covariance matrix is not invertible, ' + 'so the transformation matrix will contain Nan values. ' + 'Increase the number or size of the chunks to correct ' + 'this problem.' + ) + with pytest.warns(None) as raised_warning: + rca.fit(X, y) + assert any(str(w.message) == msg for w in raised_warning) + @pytest.mark.parametrize('num_dims', [None, 2]) def test_deprecation_num_dims_rca(num_dims):