A toolkit for working with phylogenetic data.
v0.20.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
norm_boundary.hpp
Go to the documentation of this file.
1 #ifndef GENESIS_UTILS_TOOLS_COLOR_NORM_BOUNDARY_H_
2 #define GENESIS_UTILS_TOOLS_COLOR_NORM_BOUNDARY_H_
3 
4 /*
5  Genesis - A toolkit for working with phylogenetic data.
6  Copyright (C) 2014-2018 Lucas Czech and HITS gGmbH
7 
8  This program is free software: you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program. If not, see <http://www.gnu.org/licenses/>.
20 
21  Contact:
22  Lucas Czech <lucas.czech@h-its.org>
23  Exelixis Lab, Heidelberg Institute for Theoretical Studies
24  Schloss-Wolfsbrunnenweg 35, D-69118 Heidelberg, Germany
25 */
26 
39 
40 #include <algorithm>
41 #include <cassert>
42 #include <cmath>
43 #include <limits>
44 #include <map>
45 #include <stdexcept>
46 #include <string>
47 #include <vector>
48 
49 namespace genesis {
50 namespace utils {
51 
52 // =================================================================================================
53 // Color Normalization Boundary
54 // =================================================================================================
55 
76  : public ColorNormalization
77 {
78 public:
79 
80  // -------------------------------------------------------------------------
81  // Constructors and Rule of Five
82  // -------------------------------------------------------------------------
83 
87  ColorNormalizationBoundary() = default;
88 
92  ColorNormalizationBoundary( std::vector<double> const& boundaries )
93  : boundaries_( boundaries )
94  {
96  }
97 
98  ColorNormalizationBoundary( double min, double max, size_t intervals )
99  {
100  scale( min, max, intervals );
101  }
102 
103  virtual ~ColorNormalizationBoundary() = default;
104 
107 
110 
111  // -------------------------------------------------------------------------
112  // Accessors
113  // -------------------------------------------------------------------------
114 
118  std::vector<double> const& boundaries() const
119  {
120  return boundaries_;
121  }
122 
126  int interval( double value ) const
127  {
128  // Make sure that the norm is set up correctly.
129  // This is now done while setting, so no need to do this here.
130  // is_valid_or_throw_();
131 
132  // Extreme cases: value is not within the boundaries.
133  // For the upper limit, we return size-1, as this is the first interval index
134  // that is not reachable with valid (within-boundary) values.
135  assert( boundaries_.size() >= 3 );
136  if( value < boundaries_.front() ) {
137  return -1;
138  }
139  if( value > boundaries_.back() ) {
140  return boundaries_.size() - 1;
141  }
142 
143  // Get an iterator to the boundary, and turn it into an interval index upon return.
144  // The index calculation has a special case if the value hits exaclty the highest
145  // boundary value. In that case, the iterator points to the end of the boundary vector.
146  // In all other cases, the iterator points to the end of interval boundary,
147  // so we subtract one to get the interval index.
148  // The first assertion checks this special case.
149  // The second assertion holds because we already checked that the value is not less than
150  // the first boundary.
151  auto const it = std::upper_bound( boundaries_.begin(), boundaries_.end(), value );
152  assert(( it != boundaries_.end() ) xor ( value == boundaries_.back() ));
153  assert( it != boundaries_.begin() );
154 
155  if( it == boundaries_.end() ) {
156  return boundaries_.size() - 2;
157  } else {
158  return std::distance( boundaries_.begin(), it ) - 1;
159  }
160  }
161 
162  // -------------------------------------------------------------------------
163  // Modificators
164  // -------------------------------------------------------------------------
165 
169  ColorNormalizationBoundary& boundaries( std::vector<double> const& values )
170  {
171  boundaries_ = values;
173  return *this;
174  }
175 
180  ColorNormalizationBoundary& scale( double min, double max, size_t intervals )
181  {
182  // Make evenly distributed boundary intervals between min and max.
183  // We add max separately to increase precision.
184  boundaries_.clear();
185  double const interval = ( max - min ) / static_cast<double>( intervals );
186  for( size_t i = 0; i < intervals; ++i ) {
187  boundaries_.push_back( min + static_cast<double>(i) * interval );
188  }
189  boundaries_.push_back( max );
191 
192  return *this;
193  }
194 
199  ColorNormalizationBoundary& autoscale( std::vector<double> const& values, size_t intervals )
200  {
201  return autoscale( values.begin(), values.end(), intervals );
202  }
203 
208  template <class ForwardIterator>
209  ColorNormalizationBoundary& autoscale( ForwardIterator first, ForwardIterator last, size_t intervals )
210  {
211  // New values, so that we first do not override the current ones.
212  auto min = std::numeric_limits<double>::max();
213  auto max = std::numeric_limits<double>::lowest();
214 
215  size_t cnt = 0;
216  while( first != last ) {
217  if( ! std::isfinite( *first ) || *first == mask_value() ) {
218  ++first;
219  continue;
220  }
221  if( *first < min ) {
222  min = *first;
223  }
224  if( *first > max ) {
225  max = *first;
226  }
227 
228  ++cnt;
229  ++first;
230  }
231 
232  // Only update if we found values.
233  if( cnt == 0 ) {
234  return *this;
235  }
236 
237  scale( min, max, intervals );
238  return *this;
239  }
240 
241  // -------------------------------------------------------------------------
242  // (Pure) Virtual Functions
243  // -------------------------------------------------------------------------
244 
248  virtual bool is_valid_() const override
249  {
250  return boundaries_.size() >= 3 && std::is_sorted( boundaries_.begin(), boundaries_.end() );
251  }
252 
253 protected:
254 
258  virtual void is_valid_or_throw_() const
259  {
260  if( boundaries_.size() < 3 ) {
261  throw std::runtime_error( "Invalid Color Normalization with less than three boundaries." );
262  }
263  if( ! std::is_sorted( boundaries_.begin(), boundaries_.end() ) ) {
264  throw std::runtime_error( "Invalid Color Normalization with unsorted boundaries." );
265  }
266  }
267 
271  virtual double normalize_( double value ) const override
272  {
273  auto const idx = interval( value );
274 
275  // Special cases: the value is outside of the boundaries.
276  if( idx < 0 ) {
277  return -1.0;
278  }
279  if( idx >= static_cast<int>( boundaries_.size() - 1 )) {
280  return 2.0;
281  }
282 
283  // If we are inside the boundaries, calculate a fixed position depending on the interval.
284  // As the index returned for too large values is size-1, we need to use size-2 here
285  // to normalize properly so that the max value corresponds to a normalized value of 1.0.
286  assert( boundaries_.size() >= 3 );
287  return static_cast<double>( idx ) / static_cast<double>( boundaries_.size() - 2 );
288  }
289 
290  // -------------------------------------------------------------------------
291  // Data Members
292  // -------------------------------------------------------------------------
293 
294 private:
295 
296  std::vector<double> boundaries_;
297 
298 };
299 
300 } // namespace utils
301 } // namespace genesis
302 
303 #endif // include guardmask_value_
ColorNormalizationBoundary & autoscale(std::vector< double > const &values, size_t intervals)
Set the boundaries similar to scale(), but use the given vector of values to determine min and max fi...
ColorNormalizationBoundary(double min, double max, size_t intervals)
Header of Color class.
virtual double normalize_(double value) const override
Normalization function.
ColorNormalizationBoundary & boundaries(std::vector< double > const &values)
Explicitly set the boundaries to the given values. Have to be sorted.
ColorNormalizationBoundary & scale(double min, double max, size_t intervals)
Set the boundaries to equal-sizes intervals between min and max, using interval many steps...
ColorNormalizationBoundary(std::vector< double > const &boundaries)
Provides some commonly used string utility functions.
Base class for color normalization.
virtual void is_valid_or_throw_() const
Throw if the boundaries are incorrect.
Color normalization that maps to discrete intervals.
virtual bool is_valid_() const override
Return whether the boundaries are correct.
double mask_value() const
Mask value that identifies invalid values.
std::vector< double > const & boundaries() const
Return the boundaries currently set.
ColorNormalizationBoundary & autoscale(ForwardIterator first, ForwardIterator last, size_t intervals)
Set the boundaries similar to scale(), but use the given range to determine min and max first...
int interval(double value) const
Return the interval index of a value.
ColorNormalizationBoundary & operator=(ColorNormalizationBoundary const &)=default