71 my_neighbors(std::move(neighbors)),
72 my_tree(my_neighbors.size(), options.
max_depth),
73 my_options(std::move(options))
75 std::size_t full_size =
static_cast<std::size_t
>(my_neighbors.size()) * num_dim_;
76 my_dY.resize(full_size);
77 my_uY.resize(full_size);
78 my_gains.resize(full_size,
static_cast<Float_
>(1));
79 my_pos_f.resize(full_size);
80 my_neg_f.resize(full_size);
83 my_parallel_buffer.resize(my_neighbors.size());
92 std::vector<Float_> my_dY, my_uY, my_gains, my_pos_f, my_neg_f;
94 internal::SPTree<num_dim_, Float_> my_tree;
95 std::vector<Float_> my_parallel_buffer;
100 typename decltype(my_tree)::LeafApproxWorkspace my_leaf_workspace;
122 return my_neighbors.size();
129 const auto& get_neighbors()
const {
149 void run(Float_* Y,
int limit) {
153 for(; my_iter < limit; ++my_iter) {
162 iterate(Y, multiplier, momentum);
180 static Float_ sign(Float_ x) {
181 constexpr Float_ zero = 0;
182 constexpr Float_ one = 1;
183 return (x == zero ? zero : (x < zero ? -one : one));
186 void iterate(Float_* Y, Float_ multiplier, Float_ momentum) {
187 compute_gradient(Y, multiplier);
190 auto ngains = my_gains.size();
191 for (
decltype(ngains) i = 0; i < ngains; ++i) {
192 Float_& g = my_gains[i];
193 constexpr Float_ lower_bound = 0.01;
194 constexpr Float_ to_add = 0.2;
195 constexpr Float_ to_mult = 0.8;
196 g = std::max(lower_bound, sign(my_dY[i]) != sign(my_uY[i]) ? (g + to_add) : (g * to_mult));
200 for (
decltype(ngains) i = 0; i < ngains; ++i) {
201 my_uY[i] = momentum * my_uY[i] - my_options.
eta * my_gains[i] * my_dY[i];
206 std::array<Float_, num_dim_> means{};
208 for (std::size_t i = 0; i < num_obs; ++i) {
209 for (std::size_t d = 0; d < num_dim_; ++d) {
210 means[d] += Y[d + i * num_dim_];
213 for (std::size_t d = 0; d < num_dim_; ++d) {
216 for (std::size_t i = 0; i < num_obs; ++i) {
217 for (std::size_t d = 0; d < num_dim_; ++d) {
218 Y[d + i * num_dim_] -= means[d];
226 void compute_gradient(
const Float_* Y, Float_ multiplier) {
228 compute_edge_forces(Y, multiplier);
231 std::fill(my_neg_f.begin(), my_neg_f.end(), 0);
232 Float_ sum_Q = compute_non_edge_forces();
235 std::size_t ntotal = num_obs * num_dim_;
236 for (std::size_t i = 0; i < ntotal; ++i) {
237 my_dY[i] = my_pos_f[i] - (my_neg_f[i] / sum_Q);
241 void compute_edge_forces(
const Float_* Y, Float_ multiplier) {
242 std::fill(my_pos_f.begin(), my_pos_f.end(), 0);
246 for (std::size_t i = start, end = start + length; i < end; ++i) {
247 const auto& current = my_neighbors[i];
248 size_t offset = i * num_dim_;
249 const Float_* self = Y + offset;
250 Float_* pos_out = my_pos_f.data() + offset;
252 for (const auto& x : current) {
254 const Float_* neighbor = Y + static_cast<std::size_t>(x.first) * num_dim_;
255 for (std::size_t d = 0; d < num_dim_; ++d) {
256 Float_ delta = self[d] - neighbor[d];
257 sqdist += delta * delta;
260 const Float_ mult = multiplier * x.second / (static_cast<Float_>(1) + sqdist);
261 for (std::size_t d = 0; d < num_dim_; ++d) {
262 pos_out[d] += mult * (self[d] - neighbor[d]);
271 Float_ compute_non_edge_forces() {
273 my_tree.compute_non_edge_forces_for_leaves(my_options.
theta, my_leaf_workspace, my_options.
num_threads);
276 std::size_t num_obs = num_observations();
281 for (std::size_t i = start, end = start + length; i < end; ++i) {
282 auto neg_ptr = my_neg_f.data() + i * num_dim_;
283 if (my_options.leaf_approximation) {
284 my_parallel_buffer[i] = my_tree.compute_non_edge_forces_from_leaves(i, neg_ptr, my_leaf_workspace);
286 my_parallel_buffer[i] = my_tree.compute_non_edge_forces(i, my_options.theta, neg_ptr);
290 return std::accumulate(my_parallel_buffer.begin(), my_parallel_buffer.end(),
static_cast<Float_
>(0));
294 for (std::size_t i = 0; i < num_obs; ++i) {
295 auto neg_ptr = my_neg_f.data() + i * num_dim_;
297 sum_Q += my_tree.compute_non_edge_forces_from_leaves(i, neg_ptr, my_leaf_workspace);
299 sum_Q += my_tree.compute_non_edge_forces(i, my_options.
theta, neg_ptr);