DiSMEC++
pointwise.h
Go to the documentation of this file.
1 // Copyright (c) 2021, Aalto University, developed by Erik Schultheis
2 // All rights reserved.
3 //
4 // SPDX-License-Identifier: MIT
5 
6 #ifndef DISMEC_POINTWISE_H
7 #define DISMEC_POINTWISE_H
8 
9 #include "objective.h"
10 #include "utils/hash_vector.h"
11 #include "utils/throw_error.h"
12 
13 namespace dismec::objective {
38  template<class CRTP>
40  public:
44  explicit PointWiseRegularizer(real_t scale = 1, bool ignore_bias = false);
45 
47  [[nodiscard]] long num_variables() const noexcept final { return -1; }
48 
49  [[nodiscard]] real_t value_unchecked(const HashVector& location) override;
50 
52  const DenseRealVector& direction,
53  Eigen::Ref<DenseRealVector> target) override;
54 
55  void gradient_unchecked(const HashVector& location, Eigen::Ref<DenseRealVector> target) override;
56 
57  void gradient_at_zero_unchecked(Eigen::Ref<DenseRealVector> target) override;
58 
59  void diag_preconditioner_unchecked(const HashVector& location, Eigen::Ref<DenseRealVector> target) override;
60 
61  void project_to_line_unchecked(const HashVector& location, const DenseRealVector& direction) override;
63 
66  [[nodiscard]] bool dont_regularize_bias() const { return m_LastWeightIsBias; }
67 
69  [[nodiscard]] real_t scale() const { return m_Scale; }
70 
71  private:
72  bool m_LastWeightIsBias = false;
73  real_t m_Scale = 1.0;
74 
81 
83  [[nodiscard]] real_t point_wise_value_(real_t x) const {
84  return static_cast<const CRTP*>(this)->point_wise_value(x);
85  }
86 
88  [[nodiscard]] real_t point_wise_grad_(real_t x) const {
89  return static_cast<const CRTP*>(this)->point_wise_grad(x);
90  }
91 
93  [[nodiscard]] real_t point_wise_quad_(real_t x) const {
94  return static_cast<const CRTP*>(this)->point_wise_quad(x);
95  }
96 
97  [[nodiscard]] long get_loop_bound(const HashVector& location) const {
98  return dont_regularize_bias() ? location->size() - 1u : location->size();
99  }
100  };
101 
102  template<class T>
104  m_LastWeightIsBias(ignore_bias), m_Scale(scale) {
105  if(m_Scale < 0) {
106  THROW_EXCEPTION(std::logic_error, "Scale must be non-negative");
107  }
108  }
109 
110 
111  template<class T>
113  /*
114  * Here, we just add up the pointwise values, and perform a single rescaling at the end.
115  */
116  real_t result = 0.0;
117  long loop_bound = get_loop_bound(location);
118  for (long i = 0; i < loop_bound; ++i) {
119  result += point_wise_value_(location->coeff(i));
120  }
121  return m_Scale * result;
122  }
123 
124  template<class T>
126  const HashVector& location,
127  const DenseRealVector& direction,
128  Eigen::Ref<DenseRealVector> target) {
129 
130  // the hessian / quadratic approximation is diagonal, so for the matrix-vector product
131  // we have to multiply each diagonal component with the corresponding coefficient of the direction.
132  long loop_bound = get_loop_bound(location);
133  for (long i = 0; i < loop_bound; ++i) {
134  target.coeffRef(i) = m_Scale * point_wise_quad_(location->coeff(i)) * direction.coeff(i);
135  }
136 
137  // if the last weight is interpreted as bias, we make sure that the corresponding target value is set to zero.
138  if (dont_regularize_bias())
139  target.coeffRef(loop_bound) = real_t{0};
140  }
141 
142  template<class T>
143  void PointWiseRegularizer<T>::gradient_unchecked(const HashVector& location, Eigen::Ref<DenseRealVector> target) {
144  long loop_bound = get_loop_bound(location);
145 
146  // calculate and fill in the pointwise gradient
147  for (long i = 0; i < loop_bound; ++i) {
148  target.coeffRef(i) = m_Scale * point_wise_grad_(location->coeff(i));
149  }
150 
151  // if the last weight is interpreted as bias, we make sure that the corresponding target value is set to zero.
152  if (dont_regularize_bias())
153  target.coeffRef(target.size() - 1) = real_t{0};
154  }
155 
156  template<class T>
157  void PointWiseRegularizer<T>::gradient_at_zero_unchecked(Eigen::Ref<DenseRealVector> target) {
158  // for gradient at zero, we only need to calculate the point-wise gradient once, and can then
159  // copy this over the entire vector.
160  real_t grad_at_zero = point_wise_grad_(real_t{0});
161  target.setConstant(grad_at_zero * m_Scale);
162 
163  // if the last weight is interpreted as bias, we make sure that the corresponding target value is set to zero.
164  if (dont_regularize_bias())
165  target.coeffRef(target.size() - 1) = real_t{0};
166  }
167 
168  template<class T>
170  const HashVector& location,
171  Eigen::Ref<DenseRealVector> target) {
172  // same as hessian_times_direction_unchecked, only now we don't multiply by a direction vector
173  long loop_bound = get_loop_bound(location);
174  for (long i = 0; i < loop_bound; ++i) {
175  target.coeffRef(i) = m_Scale * point_wise_quad_(location->coeff(i));
176  }
177 
178  if (dont_regularize_bias())
179  target.coeffRef(target.size() - 1) = real_t{0};
180  }
181 
182  template<class T>
184  // projecting to line just saves the location and direction for use with the `lookup_on_line` function.
185  m_LineStart = location.get();
186  m_LineDirection = direction;
187  }
188 
189  template<class T>
191  // The same as value, only using the interpolated positions
192  real_t result = 0.0;
193  // make sure we sum over the correct subset.
194  long loop_bound = dont_regularize_bias() ? m_LineStart.size() - 1u: m_LineStart.size();
195  for(long i = 0; i < loop_bound; ++i) {
196  result += point_wise_value_(m_LineStart.coeff(i) + a * m_LineDirection.coeff(i));
197  }
198  return m_Scale * result;
199  }
200 
201 }
202 
203 #endif //DISMEC_POINTWISE_H
An Eigen vector with versioning information, to implement simple caching of results.
Definition: hash_vector.h:43
const DenseRealVector & get() const
Gets a constant reference to the data of this vector.
Definition: hash_vector.h:57
Class that models an optimization objective.
Definition: objective.h:41
Base class for pointwise regularization functions.
Definition: pointwise.h:39
void gradient_at_zero_unchecked(Eigen::Ref< DenseRealVector > target) override
Definition: pointwise.h:157
void hessian_times_direction_unchecked(const HashVector &location, const DenseRealVector &direction, Eigen::Ref< DenseRealVector > target) override
Definition: pointwise.h:125
PointWiseRegularizer(real_t scale=1, bool ignore_bias=false)
Definition: pointwise.h:103
real_t point_wise_grad_(real_t x) const
calls point_wise_grad() of the implementing class
Definition: pointwise.h:88
real_t value_unchecked(const HashVector &location) override
Definition: pointwise.h:112
long get_loop_bound(const HashVector &location) const
Definition: pointwise.h:97
void diag_preconditioner_unchecked(const HashVector &location, Eigen::Ref< DenseRealVector > target) override
Definition: pointwise.h:169
void gradient_unchecked(const HashVector &location, Eigen::Ref< DenseRealVector > target) override
Definition: pointwise.h:143
real_t point_wise_value_(real_t x) const
calls point_wise_value() of the implementing class
Definition: pointwise.h:83
real_t scale() const
Returns the common scale factor for the entire regularizer.
Definition: pointwise.h:69
real_t lookup_on_line(real_t a) override
Looks up the value of the objective on the line defined by the last call to project_to_line().
Definition: pointwise.h:190
void project_to_line_unchecked(const HashVector &location, const DenseRealVector &direction) override
Definition: pointwise.h:183
long num_variables() const noexcept final
The pointwise regularizer can act on arbitrarily sized vectors, so num_variables() == -1.
Definition: pointwise.h:47
real_t point_wise_quad_(real_t x) const
calls point_wise_quad() of the implementing class
Definition: pointwise.h:93
types::DenseVector< real_t > DenseRealVector
Any dense, real values vector.
Definition: matrix_types.h:40
float real_t
The default type for floating point values.
Definition: config.h:17
#define THROW_EXCEPTION(exception_type,...)
Definition: throw_error.h:16