-
Notifications
You must be signed in to change notification settings - Fork 239
/
exponential_barycenter.py
181 lines (153 loc) · 5.1 KB
/
exponential_barycenter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
"""Exponential barycenter.
Lead author: Nicolas Guigui.
"""
import logging
from sklearn.base import BaseEstimator
import geomstats.backend as gs
from geomstats.geometry.euclidean import Euclidean
from geomstats.learning.frechet_mean import linear_mean
EPSILON = 1e-6
def _default_gradient_descent(
group,
points,
weights=None,
max_iter=32,
init_step_size=1.0,
epsilon=EPSILON,
verbose=False,
):
"""Compute the (weighted) group exponential barycenter of `points`.
Parameters
----------
group : LieGroup
Instance of the class LieGroup.
points : array-like, shape=[..., [n,n]]
Input points lying in the Lie Group.
weights : array-like, shape=[...,]
Weights associated to the points.
Optional, defaults to 1 for each point if None.
max_iter : int
Maximum number of iterations to perform in the gradient descent.
Optional, default: 32.
epsilon : float
Tolerance to reach convergence. The exstrinsic norm of the
gradient is used as criterion.
Optional, default: 1e-6.
init_step_size : float
Learning rate in the gradient descent.
Optional, default: 1.
verbose : bool
Level of verbosity to inform about convergence.
Optional, default: False.
Returns
-------
exp_bar : array-like, shape=[n,n]
Exponential_barycenter of the input points.
"""
ndim = 2 if group.default_point_type == "vector" else 3
if gs.ndim(gs.array(points)) < ndim or len(points) == 1:
return points[0] if len(points) == 1 else points
n_points = points.shape[0]
if weights is None:
weights = gs.ones((n_points,))
weights = gs.cast(weights, gs.float32)
sum_weights = gs.sum(weights)
mean = points[0]
sq_dists_between_iterates = []
iteration = 0
grad_norm = 0.0
while iteration < max_iter:
if not (grad_norm > epsilon or iteration == 0):
break
inv_mean = group.inverse(mean)
centered_points = group.compose(inv_mean, points)
logs = group.log(point=centered_points)
tangent_mean = init_step_size * gs.einsum(
"n, nk...->k...", weights / sum_weights, logs
)
mean_next = group.compose(mean, group.exp(tangent_vec=tangent_mean))
grad_norm = gs.linalg.norm(tangent_mean)
sq_dists_between_iterates.append(grad_norm)
mean = mean_next
iteration += 1
if iteration == max_iter:
logging.warning(
"Maximum number of iterations {} reached. "
"The mean may be inaccurate".format(max_iter)
)
if verbose:
logging.info("n_iter: {}, final gradient norm: {}".format(iteration, grad_norm))
return mean
class ExponentialBarycenter(BaseEstimator):
"""Empirical Exponential Barycenter for Matrix groups.
Parameters
----------
group : LieGroup
Lie group instance on which the data lie.
max_iter : int
Maximum number of iterations to perform in the gradient descent.
Optional, default: 32.
epsilon : float
Tolerance to reach convergence. The exstrinsic norm of the
gradient is used as criterion.
Optional, default: 1e-6.
init_step_size : float
Learning rate in the gradient descent.
Optional, default: 1.
verbose : bool
Level of verbosity to inform about convergence.
Optional, default: 1.
Attributes
----------
estimate_ : array-like, shape=[dim, dim]
"""
def __init__(
self,
group,
max_iter=32,
epsilon=EPSILON,
init_step_size=1.0,
point_type=None,
verbose=False,
):
self.group = group
self.max_iter = max_iter
self.epsilon = epsilon
self.verbose = verbose
self.init_step_size = init_step_size
self.point_type = point_type
self.estimate_ = None
def fit(self, X, y=None, weights=None):
"""Compute the empirical Exponential Barycenter mean.
Parameters
----------
X : {array-like, sparse matrix}, shape=[..., n_features]
Training input samples.
y : array-like, shape=[...,] or [..., n_outputs]
Target values (class labels in classification, real numbers in
regression).
Ignored.
weights : array-like, shape=[...,]
Weights associated to the points.
Optional, default: None.
Returns
-------
self : object
Returns self.
"""
if isinstance(self.group, Euclidean):
mean = linear_mean(points=X, weights=weights)
# TODO (nguigs): use closed form expression for special euclidean
# group as before PR #537
else:
mean = _default_gradient_descent(
group=self.group,
points=X,
weights=weights,
max_iter=self.max_iter,
init_step_size=self.init_step_size,
epsilon=self.epsilon,
verbose=self.verbose,
)
self.estimate_ = mean
return self