yoda is hosted by Hepforge, IPPP Durham
YODA - Yet more Objects for Data Analysis  1.7.2
Axis2D.h
Go to the documentation of this file.
1 #ifndef YODA_Axis2D_h
2 #define YODA_Axis2D_h
3 
4 #include "YODA/AnalysisObject.h"
5 #include "YODA/Exceptions.h"
6 #include "YODA/Bin.h"
7 #include "YODA/Utils/MathUtils.h"
10 #include <limits>
11 #include <string>
12 
13 namespace YODA {
14 
15 
20  template <typename BIN2D, typename DBN>
21  class Axis2D {
22  public:
23 
25 
26 
28  typedef BIN2D Bin;
29 
31  typedef typename std::vector<Bin> Bins;
32 
33  // Distinguishing between single edges and edge pairs (and pairs of pairs) is useful
34  typedef std::vector<double> Edges;
35  typedef std::pair<double, double> EdgePair1D;
36  typedef std::pair<EdgePair1D, EdgePair1D> EdgePair2D;
37  typedef std::vector<EdgePair2D> EdgePair2Ds;
38 
39  // Outflow distribution lists: see outflow(int, int)
40  typedef std::vector<DBN> Outflow;
41  typedef std::vector<Outflow> Outflows;
42 
44 
46 
47 
48  // Empty constructor
50  : _locked(false)
51  {
52  reset();
53  }
54 
56  Axis2D(const Edges& xedges, const Edges& yedges)
57  : _locked(false)
58  {
59  addBins(xedges, yedges);
60  reset();
61  }
62 
65  Axis2D(size_t nbinsX, const std::pair<double,double>& rangeX,
66  size_t nbinsY, const std::pair<double,double>& rangeY)
67  : _locked(false)
68  {
69  addBins(linspace(nbinsX, rangeX.first, rangeX.second),
70  linspace(nbinsY, rangeY.first, rangeY.second));
71  reset();
72  }
73 
75  Axis2D(const Bins& bins)
76  : _locked(false)
77  {
78  addBins(bins);
79  reset();
80  }
81 
83  Axis2D(const Bins& bins,
84  const DBN& totalDbn,
85  const Outflows& outflows)
86  : _dbn(totalDbn), _outflows(outflows),
87  _locked(false) // Does this make sense?
88  {
89  if (_outflows.size() != 8) {
90  throw Exception("Axis2D outflow containers must have exactly 8 elements");
91  }
92  addBins(bins);
93  }
94 
95 
96  void reset() {
97  _dbn.reset();
98  _outflows.assign(8, Outflow());
99  for (Bin& bin : _bins) bin.reset();
100  _locked = false;
101  }
102 
103 
105  size_t numBins() const {
106  return _bins.size();
107  }
108 
112  size_t numBinsX() const {
113  return _nx;
114  }
115 
119  size_t numBinsY() const {
120  return _ny;
121  }
122 
124  //
126 
127 
134  Outflow& outflow(int ix, int iy) {
135  return _outflows[_outflowIndex(ix, iy)];
136  }
137 
144  const Outflow& outflow(int ix, int iy) const {
145  return _outflows[_outflowIndex(ix, iy)];
146  }
147 
149  void scaleX(double xscale) {
150  scaleXY(xscale, 1.0);
151  }
152 
154  void scaleY(double yscale) {
155  scaleXY(1.0, yscale);
156  }
157 
160  void scaleXY(double sx, double sy) {
161  _dbn.scaleXY(sx, sy);
163  // for (Outflow& outflow : _outflows)
164  // for (DBN& dbn : outflow)
165  // dbn.scaleXY(sx, sy);
166  for (size_t io = 0; io < _outflows.size(); ++io) {
167  Outflow& outflow = _outflows[io];
168  for (size_t id = 0; id < outflow.size(); ++id) {
169  DBN& dbn = outflow[id];
170  dbn.scaleXY(sx, sy);
171  }
172  }
174  // for (Bin& bin : _bins)
175  // bin.scaleXY(sx, sy);
176  for (size_t ib = 0; ib < _bins.size(); ++ib) _bins[ib].scaleXY(sx, sy);
177  _updateAxis(_bins);
178  }
179 
180 
183  void scaleW(double scalefactor) {
184  _dbn.scaleW(scalefactor);
186  // for (Outflow& outflow : _outflows)
187  // for (DBN& dbn : outflow)
188  // dbn.scaleW(scalefactor);
189  for (size_t io = 0; io < _outflows.size(); ++io) {
190  Outflow& outflow = _outflows[io];
191  for (size_t id = 0; id < outflow.size(); ++id) {
192  DBN& dbn = outflow[id];
193  dbn.scaleW(scalefactor);
194  }
195  }
197  // for (Bin& bin : _bins)
198  // bin.scaleW(scalefactor);
199  for (size_t ib = 0; ib < _bins.size(); ++ib) _bins[ib].scaleW(scalefactor);
200  _updateAxis(_bins);
201  }
202 
203 
207  void eraseBin(size_t i) {
208  if (i >= numBins())
209  throw RangeError("Bin index is out of range");
210 
211  // Temporarily unlock the axis during the update
212  _bins.erase(_bins.begin() + i);
213  _updateAxis(_bins);
214  }
215 
216 
218  void eraseBins(const size_t from, const size_t to) {
219  if (from >= numBins())
220  throw RangeError("Initial bin index is out of range");
221  if (from >= numBins())
222  throw RangeError("Final bin index is out of range");
223 
224  Bin& fromBin = bin(from);
225  Bin& toBin = bin(to);
226 
227  eraseBins(std::make_pair(fromBin.xMin(), toBin.xMax()),
228  std::make_pair(fromBin.yMin(), toBin.yMax()));
229  }
230 
237  void eraseBins(const std::pair<double, double>& xrange,
238  const std::pair<double, double>& yrange)
239  {
240  size_t xiLow = _binSearcherX.index(xrange.first) - 1;
241  size_t xiHigh = _binSearcherX.index(xrange.second) - 1;
242 
243  size_t yiLow = _binSearcherY.index(yrange.first) - 1;
244  size_t yiHigh = _binSearcherY.index(yrange.second) - 1;
245 
247  std::vector<bool> deleteMask(numBins(), false);
248 
249  for (size_t yi = yiLow; yi < yiHigh; yi++) {
250  for (size_t xi = xiLow; xi < xiHigh; xi++) {
251  ssize_t i = _indexes[_index(_nx, xi, yi)];
252  if (i == -1 || deleteMask[i]) continue;
253  if (bin(i).fitsInside(xrange, yrange)) deleteMask[i] = true;
254  }
255  }
256 
257  // Now we just update
258  eraseBins(deleteMask);
259  }
260 
261 
264  void eraseBins(const std::vector<bool>& deleteMask) {
265  Bins newBins;
266  for (size_t i = 0; i < numBins(); i++)
267  if (!deleteMask[i]) newBins.push_back(bins(i));
268  _update(newBins);
269  }
270 
271 
274  void mergeBins(size_t from, size_t to) {
275  // Correctness checking
276  if (from >= numBins())
277  throw RangeError("Initial merge index is out of range");
278  if (to >= numBins())
279  throw RangeError("Final merge index is out of range");
280  if (from > to)
281  throw RangeError("Final bin must be greater than or equal to initial bin");
282  if (_gapInRange(from, to))
283  throw RangeError("Bin ranges containing gaps cannot be merged");
284  if (from == to)
285  return; // nothing to be done
286 
287  Bin& b = bin(from);
288  for (size_t i = from + 1; i <= to; ++i)
289  b.merge(_bins[i]);
290  eraseBins(from+1, to);
291  }
292 
293 
295  void rebin(unsigned int n) {
296  rebinXY(n, n);
297  }
298 
300  void rebinXY(unsigned int nx, unsigned int ny) {
301  rebinX(nx);
302  rebinY(ny);
303  }
304 
306  void rebinX(unsigned int nx) {
308  }
309 
311  void rebinY(unsigned int ny) {
313  }
314 
315 
318  void _setLock(bool locked) { _locked = locked; }
319 
320 
322  double xMin() const { return _xRange.first; }
323 
325  double xMax() const { return _xRange.second; }
326 
327 
329  double yMin() const { return _yRange.first; }
330 
332  double yMax() const { return _yRange.second; }
333 
334 
336  void addBin(EdgePair1D xrange, EdgePair1D yrange) {
337  _checkUnlocked();
338  Bins newBins = _bins;
339  newBins.push_back(Bin(xrange, yrange));
340  _updateAxis(newBins);
341  }
342 
344  void addBin(const Bin& bin) {
345  _checkUnlocked();
346  Bins newBins = _bins;
347  newBins.push_back(bin);
348  _updateAxis(newBins);
349  }
350 
352  void addBins(const Bins& bins) {
353  if (bins.size() == 0) return;
354  _checkUnlocked();
355  Bins newBins = _bins;
357  // for (const Bin& b : bins)
358  // newBins.push_back(b);
359  for (size_t ib = 0; ib < bins.size(); ++ib) newBins.push_back(bins[ib]);
360  _updateAxis(newBins);
361  }
362 
364  void addBins(const std::vector<double>& xedges, const std::vector<double>& yedges) {
365  if (xedges.size() == 0) return;
366  if (yedges.size() == 0) return;
367  _checkUnlocked();
368 
369  Bins newBins = _bins;
370  for (size_t xi = 0; xi < xedges.size()-1; xi++) {
371  for (size_t yi = 0; yi < yedges.size()-1; yi++) {
372  const std::pair<double,double> xx = std::make_pair(xedges[xi], xedges[xi+1]);
373  const std::pair<double,double> yy = std::make_pair(yedges[yi], yedges[yi+1]);
374  // std::cout << "New bin with edges: [(" << xx.first << "," << xx.second << "), " << yy.first << "," << yy.second << ")]" << std::endl;
375  newBins.push_back(Bin(xx, yy));
376  }
377  }
378 
379  _updateAxis(newBins);
380  }
381 
382 
384  Bin& bin(size_t i) {
385  return _bins[i];
386  }
387 
389  const Bin& bin(size_t i) const {
390  return _bins[i];
391  }
392 
394  int binIndexAt(double x, double y) const {
395  size_t xi = _binSearcherX.index(x) - 1;
396  size_t yi = _binSearcherY.index(y) - 1;
397  if (xi > _nx) return -1;
398  if (yi > _ny) return -1;
399 
400  return _indexes[_index(_nx, xi, yi)];
401  }
402 
404  Bin& binAt(double x, double y) {
405  const int ret = binIndexAt(x, y);
406  if (ret == -1) throw RangeError("No bin found!!");
407  return bin(ret);
408  }
409 
411  const Bin& binAt(double x, double y) const {
412  const int ret = binIndexAt(x, y);
413  if (ret == -1) throw RangeError("No bin found!!");
414  return bin(ret);
415  }
416 
417 
419  DBN& totalDbn() {
420  return _dbn;
421  }
423  const DBN& totalDbn() const {
424  return _dbn;
425  }
427  void setTotalDbn(const DBN& dbn) {
428  _dbn = dbn;
429  }
430 
431 
433  Bins& bins() {
434  return _bins;
435  }
436 
438  const Bins& bins() const {
439  return _bins;
440  }
441 
442 
445  // (DM: Doesn't this break the semantics of equality? As it's used only
446  // rarely, isn't there a real case for having a "binningsCompatible" or
447  // similar method?)
448  bool operator == (const Axis2D& other) const {
449  if (numBins() != other.numBins()) return false;
450  for (size_t i = 0; i < numBins(); i++)
451  if (!(fuzzyEquals(bin(i).xMin(), other.bin(i).xMin()) &&
452  fuzzyEquals(bin(i).xMax(), other.bin(i).xMax()) &&
453  fuzzyEquals(bin(i).yMin(), other.bin(i).yMin()) &&
454  fuzzyEquals(bin(i).yMax(), other.bin(i).yMax())))
455  return false;
456  return true;
457  }
458 
460  bool operator != (const Axis2D& other) const {
461  return ! operator == (other);
462  }
463 
464 
467  if (*this != toAdd) {
468  throw LogicError("YODA::Axis2D: Cannot add axes with different binnings.");
469  }
470  for (size_t i = 0; i < bins().size(); ++i) {
471  bin(i) += toAdd.bin(i);
472  }
473  _dbn += toAdd._dbn;
474  return *this;
475  }
476 
479  if (*this != toSubtract) {
480  throw LogicError("YODA::Axis2D: Cannot add axes with different binnings.");
481  }
482  for (size_t i = 0; i < bins().size(); ++i) {
483  bin(i) -= toSubtract.bin(i);
484  }
485  _dbn -= toSubtract._dbn;
486  return *this;
487  }
488 
489 
490  private:
491 
492  void _checkUnlocked(void) {
493  // Ensure that axis is not locked
494  if (_locked)
495  throw LockError("Attempting to update a locked 2D axis");
496  }
497 
498 
501  bool _gapInRange(size_t from, size_t to) {
502  Bin& toBin = bin(to);
503  Bin& fromBin = bin(from);
504  return true;
505  }
506 
507 
508  void _updateAxis(Bins& bins) {
509  // Deal with the case that there are no bins supplied (who called that?!)
510  if (bins.size() == 0) {
511  _binSearcherX = Utils::BinSearcher();
512  _binSearcherY = Utils::BinSearcher();
513  _nx = 0;
514  _ny = 0;
515  _xRange = std::make_pair(0, 0);
516  _yRange = std::make_pair(0, 0);
517  }
518 
519  // Sort the bins
520  std::sort(bins.begin(), bins.end());
521 
522  // Create the edges
523  std::vector<double> xedges, yedges, xwidths, ywidths;
524  for (const Bin& bin : bins) {
525  xedges.push_back(bin.xMin());
526  xedges.push_back(bin.xMax());
527  xwidths.push_back(bin.xWidth());
528  yedges.push_back(bin.yMin());
529  yedges.push_back(bin.yMax());
530  ywidths.push_back(bin.yWidth());
531  }
532 
533  // Sort the edges and widths
534  std::sort(xedges.begin(), xedges.end());
535  std::sort(yedges.begin(), yedges.end());
536  std::sort(xwidths.begin(), xwidths.end());
537  std::sort(ywidths.begin(), ywidths.end());
538 
539  // Obtain the median widths as a typical scale for uniqueness comparisons
540  const double medianxwidth = xwidths[ (xwidths.size()-1)/2 ];
541  const double medianywidth = ywidths[ (ywidths.size()-1)/2 ];
542 
543  // Uniqueify the bin edges in the x- and y-cut vectors, with some numerical fuzziness
544  xedges.resize(std::unique(xedges.begin(), xedges.end(), CmpFloats(1e-3, medianxwidth)) - xedges.begin());
545  yedges.resize(std::unique(yedges.begin(), yedges.end(), CmpFloats(1e-3, medianywidth)) - yedges.begin());
546 
547  const size_t nx = xedges.size();
548  const size_t ny = yedges.size();
549  const size_t N = nx * ny;
550  //std::cout << "Unique Axis2D edge list sizes: nx = " << nx << ", ny = " << ny << std::endl;
551  assert(bins.size() <= (nx-1)*(ny-1) && "Input bins vector size must agree with computed number of unique bins");
552 
553  // Create a sea of indices, starting with an all-gaps configuration
554  std::vector<ssize_t> indexes(N, -1);
555 
556  // Iterate through bins and find out which
557  Utils::BinSearcher xSearcher(xedges);
558  Utils::BinSearcher ySearcher(yedges);
559  for (size_t i = 0; i < bins.size(); ++i) {
560  Bin& bin = bins[i];
561 
562  // std::cout << "Bin #" << i << " edges: "
563  // << "[(" << bin.xMin() << "," << bin.xMax() << "), "
564  // << "(" << bin.yMin() << "," << bin.yMax() << ")] " << std::endl;
565 
566  const size_t xiMin= xSearcher.index(bin.xMin()) - 1;
567  const size_t xiMax= xSearcher.index(bin.xMax()) - 1;
568  const size_t yiMin = ySearcher.index(bin.yMin()) - 1;
569  const size_t yiMax = ySearcher.index(bin.yMax()) - 1;
570 
571  // std::cout << "Sub-bin range indices: x = " << xiMin << ".." << xiMax << ", y = " << yiMin << ".." << yiMax << std::endl;
572 
573  // Loop over sub-bins in the edge list and assign indices / detect overlaps
574  for (size_t xi = xiMin; xi < xiMax; xi++) {
575  for (size_t yi = yiMin; yi < yiMax; yi++) {
576  const size_t ii = _index(nx, xi, yi);
577  if (indexes[ii] != -1) {
578  std::stringstream ss;
579  ss << "Bin edges overlap! Bin #" << i << " with edges "
580  << "[(" << bin.xMin() << "," << bin.xMax() << "), "
581  << "(" << bin.yMin() << "," << bin.yMax() << ")] "
582  << "overlaps bin #" << indexes[ii] << " in sub-bin #" << ii;
583  throw RangeError(ss.str());
584  }
585  indexes[ii] = i;
586  }
587  }
588  }
589 
590  // Job's a good'n - let's change our class.
591  _nx = nx;
592  _ny = ny;
593 
594  _xRange = std::make_pair(xedges.front(), xedges.back());
595  _yRange = std::make_pair(yedges.front(), yedges.back());
596 
597  _indexes = indexes;
598  _bins = bins;
599 
600  _binSearcherX = xSearcher;
601  _binSearcherY = ySearcher;
602  }
603 
604 
606  static size_t _index(size_t nx, size_t x, size_t y) {
607  return y * nx + x;
608  }
609 
615  static size_t _outflowIndex(int ix, int iy) {
616  if (ix == 0 || iy == 0)
617  throw UserError("The in-range (0,0) index pair is not a valid outflow specifier");
618  ix += 1;
619  iy += 1;
620  if (ix > 2 || iy > 2)
621  throw UserError("Outflow index out of range: valid indices are -1, 0, 1");
622  size_t rtn = 3*ix + iy; // uncorrected for (0,0) index offset
623  if (rtn > 4) rtn -= 1; // offset correction (note that raw rtn == 4 is not possible)
624  return rtn;
625  }
626 
627 
629 
630 
632  Bins _bins;
633 
635  DBN _dbn;
636 
637  // Outflows
638  Outflows _outflows;
639 
640  // Binsearcher, for searching bins
641  Utils::BinSearcher _binSearcherX;
642  Utils::BinSearcher _binSearcherY;
643 
644  EdgePair1D _xRange;
645  EdgePair1D _yRange;
646 
647  // Mapping from bin-searcher indices to bin indices (allowing gaps)
648  std::vector<ssize_t> _indexes;
649 
650  // Necessary for bounds checking and indexing
651  size_t _nx;
652  size_t _ny;
653 
655  bool _locked;
656 
658 
659  };
660 
661 }
662 
663 #endif
std::pair< double, double > EdgePair1D
Definition: Axis2D.h:35
void scaleXY(double sx, double sy)
Definition: Axis2D.h:160
Axis2D< BIN2D, DBN > & operator+=(const Axis2D< BIN2D, DBN > &toAdd)
Addition operator.
Definition: Axis2D.h:466
Bins & bins()
Return the bins vector (non-const)
Definition: Axis2D.h:433
void rebinXY(unsigned int nx, unsigned int ny)
Rebin with separate rebinning factors nx, ny in x and y.
Definition: Axis2D.h:300
void eraseBins(const size_t from, const size_t to)
Erase a rectangle of bins.
Definition: Axis2D.h:218
const Bin & bin(size_t i) const
Access bin by index (const)
Definition: Axis2D.h:389
void scaleY(double yscale)
Scale each bin as if the entire y-axis had been scaled by this factor.
Definition: Axis2D.h:154
Generic unspecialised YODA runtime error.
Definition: Exceptions.h:20
std::vector< EdgePair2D > EdgePair2Ds
Definition: Axis2D.h:37
Bin & bin(size_t i)
Access bin by index.
Definition: Axis2D.h:384
Error for e.g. use of invalid bin ranges.
Definition: Exceptions.h:34
void rebinY(unsigned int ny)
Rebin in y by factor ny.
Definition: Axis2D.h:311
bool fuzzyEquals(double a, double b, double tolerance=1E-5)
Compare two floating point numbers for equality with a degree of fuzziness.
Definition: MathUtils.h:72
std::vector< Bin > Bins
A vector containing 2D bins. Not used for searching.
Definition: Axis2D.h:31
std::vector< double > linspace(size_t nbins, double start, double end, bool include_end=true)
Make a list of nbins + 1 values equally spaced between start and end inclusive.
Definition: MathUtils.h:252
void eraseBins(const std::vector< bool > &deleteMask)
Definition: Axis2D.h:264
void rebin(unsigned int n)
Rebin with the same rebinning factor n in x and y.
Definition: Axis2D.h:295
void setTotalDbn(const DBN &dbn)
Set the total distribution: CAREFUL!
Definition: Axis2D.h:427
void addBin(const Bin &bin)
Add a pre-made bin.
Definition: Axis2D.h:344
void scaleW(double scalefactor)
Definition: Axis2D.h:183
void mergeBins(size_t from, size_t to)
Definition: Axis2D.h:274
2D bin container
Definition: Axis2D.h:21
void addBin(EdgePair1D xrange, EdgePair1D yrange)
Add a bin, providing its x- and y- edge ranges.
Definition: Axis2D.h:336
const Bin & binAt(double x, double y) const
Get the bin containing point (x, y) (const).
Definition: Axis2D.h:411
Axis2D< BIN2D, DBN > & operator-=(const Axis2D< BIN2D, DBN > &toSubtract)
Subtraction operator.
Definition: Axis2D.h:478
double xMin() const
Return the lowest-valued bin edge along the x-axis.
Definition: Axis2D.h:322
Error for problems introduced outside YODA, to put it nicely.
Definition: Exceptions.h:100
BIN2D Bin
Typedefs.
Definition: Axis2D.h:28
size_t numBins() const
Get the number of bins.
Definition: Axis2D.h:105
Axis2D(size_t nbinsX, const std::pair< double, double > &rangeX, size_t nbinsY, const std::pair< double, double > &rangeY)
Definition: Axis2D.h:65
void eraseBin(size_t i)
Definition: Axis2D.h:207
void eraseBins(const std::pair< double, double > &xrange, const std::pair< double, double > &yrange)
Definition: Axis2D.h:237
DBN & totalDbn()
Return the total distribution (non-const)
Definition: Axis2D.h:419
const Outflow & outflow(int ix, int iy) const
Get the outflow by x-index and y-index (const version)
Definition: Axis2D.h:144
double yMin() const
Return the lowest-valued bin edge along the y-axis.
Definition: Axis2D.h:329
Error for modification of a data object where filling has already begun.
Definition: Exceptions.h:41
Error for places where it should not have been possible to get to!
Definition: Exceptions.h:55
size_t numBinsY() const
Definition: Axis2D.h:119
void reset()
Definition: Axis2D.h:96
void addBins(const Bins &bins)
Add a vector of pre-made bins.
Definition: Axis2D.h:352
void addBins(const std::vector< double > &xedges, const std::vector< double > &yedges)
Add a contiguous set of bins to an axis, via their list of edges.
Definition: Axis2D.h:364
const DBN & totalDbn() const
Return the total distribution (const)
Definition: Axis2D.h:423
Functor to compare two floating point numbers and return whether they are fuzzily equivalent...
Definition: Predicates.h:20
Bin & binAt(double x, double y)
Get the bin containing point (x, y).
Definition: Axis2D.h:404
void rebinX(unsigned int nx)
Rebin in x by factor nx.
Definition: Axis2D.h:306
bool operator!=(const Axis2D &other) const
Non-equality operator.
Definition: Axis2D.h:460
Axis2D(const Bins &bins)
Constructor accepting a list of bins.
Definition: Axis2D.h:75
std::vector< Outflow > Outflows
Definition: Axis2D.h:41
double xMax() const
Return the highest-valued bin edge along the x-axis.
Definition: Axis2D.h:325
double yMax() const
Return the highest-valued bin edge along the y-axis.
Definition: Axis2D.h:332
std::vector< DBN > Outflow
Definition: Axis2D.h:40
const Bins & bins() const
Return the bins vector (const)
Definition: Axis2D.h:438
std::pair< EdgePair1D, EdgePair1D > EdgePair2D
Definition: Axis2D.h:36
Axis2D(const Edges &xedges, const Edges &yedges)
A constructor with specified x and y axis bin cuts.
Definition: Axis2D.h:56
size_t numBinsX() const
Definition: Axis2D.h:112
bool operator==(const Axis2D &other) const
Definition: Axis2D.h:448
Outflow & outflow(int ix, int iy)
Get the outflow by x-index and y-index (non-const version)
Definition: Axis2D.h:134
void scaleX(double xscale)
Scale each bin as if the entire x-axis had been scaled by this factor.
Definition: Axis2D.h:149
int binIndexAt(double x, double y) const
Get the bin index of the bin containing point (x, y).
Definition: Axis2D.h:394
std::vector< double > Edges
Definition: Axis2D.h:34
Axis2D(const Bins &bins, const DBN &totalDbn, const Outflows &outflows)
State-setting constructor for persistency.
Definition: Axis2D.h:83