71 my_dY(my_neighbors.size() *
num_dim_),
72 my_uY(my_neighbors.size() *
num_dim_),
73 my_gains(my_neighbors.size() *
num_dim_, 1.0),
74 my_pos_f(my_neighbors.size() *
num_dim_),
75 my_neg_f(my_neighbors.size() *
num_dim_),
76 my_tree(my_neighbors.size(),
options.max_depth),
80 my_parallel_buffer.resize(my_neighbors.size());
89 std::vector<Float_> my_dY, my_uY, my_gains, my_pos_f, my_neg_f;
91 internal::SPTree<num_dim_, Float_> my_tree;
92 std::vector<Float_> my_parallel_buffer;
97 typename decltype(my_tree)::LeafApproxWorkspace my_leaf_workspace;
119 return my_neighbors.size();
150 for(; my_iter <
limit; ++my_iter) {
183 void iterate(Float_* Y, Float_ multiplier, Float_ momentum) {
184 compute_gradient(Y, multiplier);
187 size_t ngains = my_gains.size();
188 for (
size_t i = 0; i < ngains; ++i) {
189 Float_& g = my_gains[i];
190 constexpr Float_ lower_bound = 0.01;
191 constexpr Float_ to_add = 0.2;
192 constexpr Float_ to_mult = 0.8;
193 g = std::max(lower_bound, sign(my_dY[i]) != sign(my_uY[i]) ? (g + to_add) : (g * to_mult));
197 for (
size_t i = 0; i < ngains; ++i) {
198 my_uY[i] = momentum * my_uY[i] - my_options.
eta * my_gains[i] * my_dY[i];
204 for (
int d = 0; d < num_dim_; ++d) {
209 for (
size_t i = 0; i < N; ++i, start += num_dim_) {
215 for (
size_t i = 0; i < N; ++i, start += num_dim_) {
224 void compute_gradient(
const Float_* Y, Float_ multiplier) {
226 compute_edge_forces(Y, multiplier);
229 std::fill(my_neg_f.begin(), my_neg_f.end(), 0);
231 Float_ sum_Q = compute_non_edge_forces();
234 size_t ntotal = N *
static_cast<size_t>(num_dim_);
235 for (
size_t i = 0; i < ntotal; ++i) {
236 my_dY[i] = my_pos_f[i] - (my_neg_f[i] / sum_Q);
240 void compute_edge_forces(
const Float_* Y, Float_ multiplier) {
241 std::fill(my_pos_f.begin(), my_pos_f.end(), 0);
245 for (size_t n = start, end = start + length; n < end; ++n) {
246 const auto& current = my_neighbors[n];
247 size_t offset = n * static_cast<size_t>(num_dim_);
248 const Float_* self = Y + offset;
249 Float_* pos_out = my_pos_f.data() + offset;
251 for (const auto& x : current) {
253 const Float_* neighbor = Y + static_cast<size_t>(x.first) * num_dim_;
254 for (int d = 0; d < num_dim_; ++d) {
255 Float_ delta = self[d] - neighbor[d];
256 sqdist += delta * delta;
259 const Float_ mult = multiplier * x.second / (static_cast<Float_>(1) + sqdist);
260 for (int d = 0; d < num_dim_; ++d) {
261 pos_out[d] += mult * (self[d] - neighbor[d]);
270 Float_ compute_non_edge_forces() {
271 size_t N = num_observations();
274 my_tree.compute_non_edge_forces_for_leaves(my_options.
theta, my_leaf_workspace, my_options.
num_threads);
281 for (size_t n = start, end = start + length; n < end; ++n) {
282 auto neg_ptr = my_neg_f.data() + n * static_cast<size_t>(num_dim_);
283 if (my_options.leaf_approximation) {
284 my_parallel_buffer[n] = my_tree.compute_non_edge_forces_from_leaves(n, neg_ptr, my_leaf_workspace);
286 my_parallel_buffer[n] = my_tree.compute_non_edge_forces(n, my_options.theta, neg_ptr);
290 return std::accumulate(my_parallel_buffer.begin(), my_parallel_buffer.end(),
static_cast<Float_
>(0));
294 for (
size_t n = 0; n < N; ++n) {
295 auto neg_ptr = my_neg_f.data() + n *
static_cast<size_t>(num_dim_);
297 sum_Q += my_tree.compute_non_edge_forces_from_leaves(n, neg_ptr, my_leaf_workspace);
299 sum_Q += my_tree.compute_non_edge_forces(n, my_options.
theta, neg_ptr);