mirror of
https://github.com/boostorg/histogram.git
synced 2026-01-19 04:12:12 +00:00
Expanded docs to discuss special cases (#412)
This clarifies how to use histogram::fill efficiently with multi-dimensional histograms, and discusses a workaround for the design choice that Axis::index cannot be overloaded. And since I haven't build the documentation locally for a long time, and a new computer where I needed to set things up again from scratch, I also added a README.md on how to do that (which is not trivial for boostbook).
This commit is contained in:
@@ -41,7 +41,7 @@ doxygen reference
|
|||||||
|
|
||||||
actions doxygen-postprocessing
|
actions doxygen-postprocessing
|
||||||
{
|
{
|
||||||
python $(THIS_PATH)/doxygen_postprocessing.py "$(>)"
|
$(THIS_PATH)/doxygen_postprocessing.py "$(>)"
|
||||||
}
|
}
|
||||||
|
|
||||||
notfile reference-pp : @doxygen-postprocessing : reference.xml ;
|
notfile reference-pp : @doxygen-postprocessing : reference.xml ;
|
||||||
|
|||||||
9
doc/README.md
Normal file
9
doc/README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Building the documentation
|
||||||
|
|
||||||
|
For building the documentation, certain dependencies have to be installed and a user-config.jam has to be generated in the home directory.
|
||||||
|
|
||||||
|
See [Quickbook documentation](https://www.boost.org/doc/libs/master/doc/html/quickbook/install.html) for details.
|
||||||
|
|
||||||
|
In addition, b2 needs to be configured with Python support.
|
||||||
|
|
||||||
|
See [b2 documentation](https://www.boost.org/doc/libs/1_76_0/tools/build/doc/html/index.html#bbv2.reference.tools.libraries.python)
|
||||||
@@ -9,6 +9,8 @@
|
|||||||
|
|
||||||
An [*Axis] maps input values to indices. It holds state specific to that axis, like the number of bins and any metadata. Must be [@https://en.cppreference.com/w/cpp/named_req/CopyConstructible CopyConstructible], [@https://en.cppreference.com/w/cpp/named_req/CopyAssignable CopyAssignable], and *nothrow* [@https://en.cppreference.com/w/cpp/named_req/MoveAssignable MoveAssignable].
|
An [*Axis] maps input values to indices. It holds state specific to that axis, like the number of bins and any metadata. Must be [@https://en.cppreference.com/w/cpp/named_req/CopyConstructible CopyConstructible], [@https://en.cppreference.com/w/cpp/named_req/CopyAssignable CopyAssignable], and *nothrow* [@https://en.cppreference.com/w/cpp/named_req/MoveAssignable MoveAssignable].
|
||||||
|
|
||||||
|
[note `Axis::index` cannot be templated or overloaded. This is by design, but also a limitation of the internal code used to detect generic user-defined axes. A workaround for making an axis that accepts multiple value types is described in the [link histogram.guide.expert.axis_multiple_value_types [*Guide]].]
|
||||||
|
|
||||||
[heading Associated Types]
|
[heading Associated Types]
|
||||||
|
|
||||||
* [link histogram.concepts.DiscreteAxis [*DiscreteAxis]]
|
* [link histogram.concepts.DiscreteAxis [*DiscreteAxis]]
|
||||||
|
|||||||
2
doc/doxygen_postprocessing.py
Normal file → Executable file
2
doc/doxygen_postprocessing.py
Normal file → Executable file
@@ -1,3 +1,5 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
# Copyright Hans Dembinski 2018 - 2019.
|
# Copyright Hans Dembinski 2018 - 2019.
|
||||||
# Distributed under the Boost Software License, Version 1.0.
|
# Distributed under the Boost Software License, Version 1.0.
|
||||||
# (See accompanying file LICENSE_1_0.txt or copy at
|
# (See accompanying file LICENSE_1_0.txt or copy at
|
||||||
|
|||||||
@@ -398,6 +398,21 @@ The library supports non-orthogonal grids by allowing axis types to accept a `st
|
|||||||
|
|
||||||
[endsect]
|
[endsect]
|
||||||
|
|
||||||
|
[section:axis_multiple_value_types Axis which accepts multiple value types]
|
||||||
|
|
||||||
|
Can you make an axis that accepts more than one type of value? Sort of, with a workaround.
|
||||||
|
|
||||||
|
What is the issue here? A basic assumption during the design of this library was that `Axis::index` only needs to handle a single value type, and thus the implementation does not allow `Axis::index` to be overloaded or templated. For example, data for one particular axis of the histogram may have type `double` or type `std::string`, but not a mix of the two. Value types which are implicitly convertible also work, for example, `double` and `int`.
|
||||||
|
|
||||||
|
But what if you really need to accept more than one value type, and those types are not implicitly convertible? Then you can make it work with a special converter type, like in the following example.
|
||||||
|
|
||||||
|
[import ../examples/guide_custom_axis_multiple_value_types.cpp]
|
||||||
|
[guide_custom_axis_multiple_value_types]
|
||||||
|
|
||||||
|
In other words, the solution here was to make a type towards which the other value types are implicitly convertible. This is as efficient as making overloads, although formally a temporary object is created here, because the optimizer will inline the calls and never create that temporary object.
|
||||||
|
|
||||||
|
[endsect]
|
||||||
|
|
||||||
[section User-defined storage class]
|
[section User-defined storage class]
|
||||||
|
|
||||||
Histograms which use a different storage class can easily created with the factory function [headerref boost/histogram/make_histogram.hpp make_histogram_with]. For convenience, this factory function accepts many standard containers as storage backends: vectors, arrays, and maps. These are automatically wrapped with a [classref boost::histogram::storage_adaptor] to provide the storage interface needed by the library. Users may also place custom accumulators in the vector, as described in the next section.
|
Histograms which use a different storage class can easily created with the factory function [headerref boost/histogram/make_histogram.hpp make_histogram_with]. For convenience, this factory function accepts many standard containers as storage backends: vectors, arrays, and maps. These are automatically wrapped with a [classref boost::histogram::storage_adaptor] to provide the storage interface needed by the library. Users may also place custom accumulators in the vector, as described in the next section.
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ alias cxx14 :
|
|||||||
[ run guide_custom_accumulators_ouroboros.cpp ]
|
[ run guide_custom_accumulators_ouroboros.cpp ]
|
||||||
[ run guide_custom_minimal_axis.cpp ]
|
[ run guide_custom_minimal_axis.cpp ]
|
||||||
[ run guide_custom_modified_axis.cpp ]
|
[ run guide_custom_modified_axis.cpp ]
|
||||||
|
[ run guide_custom_axis_multiple_value_types.cpp ]
|
||||||
[ run guide_custom_storage.cpp ]
|
[ run guide_custom_storage.cpp ]
|
||||||
[ run guide_fill_histogram.cpp ]
|
[ run guide_fill_histogram.cpp ]
|
||||||
[ run guide_fill_profile.cpp ]
|
[ run guide_fill_profile.cpp ]
|
||||||
|
|||||||
60
examples/guide_custom_axis_multiple_value_types.cpp
Normal file
60
examples/guide_custom_axis_multiple_value_types.cpp
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2025 Hans Dembinski
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0.
|
||||||
|
// (See accompanying file LICENSE_1_0.txt
|
||||||
|
// or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
|
//[ guide_custom_axis_multiple_value_types
|
||||||
|
|
||||||
|
#include <boost/histogram.hpp>
|
||||||
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace boost::histogram;
|
||||||
|
|
||||||
|
struct fraction {
|
||||||
|
int numerator;
|
||||||
|
int denominator;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We can use a converter type to accept multiple value types.
|
||||||
|
struct my_axis : axis::regular<double> {
|
||||||
|
using base_type = axis::regular<double>;
|
||||||
|
|
||||||
|
using base_type::regular; // inherit constructors
|
||||||
|
|
||||||
|
struct converter_type {
|
||||||
|
double val_;
|
||||||
|
// put overloads to handle multiple data types here or use a template
|
||||||
|
converter_type(const fraction& x)
|
||||||
|
: val_{static_cast<double>(x.numerator) / x.denominator} {}
|
||||||
|
converter_type(double x) : val_{x} {}
|
||||||
|
};
|
||||||
|
|
||||||
|
axis::index_type index(converter_type x) const {
|
||||||
|
return base_type::index(x.val_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto h = make_histogram(my_axis(4, 0.0, 1.0));
|
||||||
|
|
||||||
|
h(fraction{1, 3}); // 0.3333
|
||||||
|
h(0.8);
|
||||||
|
|
||||||
|
std::vector<fraction> a = {
|
||||||
|
{1, 5}, // 0.2
|
||||||
|
{3, 5}, // 0.6
|
||||||
|
};
|
||||||
|
h.fill(a);
|
||||||
|
|
||||||
|
std::vector<double> b = {0.2, 0.4};
|
||||||
|
h.fill(b);
|
||||||
|
|
||||||
|
assert(h.at(0) == 2); // 0.0 ... 0.25
|
||||||
|
assert(h.at(1) == 2); // 0.25 ... 0.5
|
||||||
|
assert(h.at(2) == 1); // 0.5 ... 0.75
|
||||||
|
assert(h.at(3) == 1); // 0.75 ... 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
//]
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
//[ guide_fill_histogram
|
//[ guide_fill_histogram
|
||||||
|
|
||||||
#include <boost/histogram.hpp>
|
#include <boost/histogram.hpp>
|
||||||
|
#include <boost/core/span.hpp>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@@ -29,7 +30,16 @@ int main() {
|
|||||||
h(xy);
|
h(xy);
|
||||||
|
|
||||||
// chunk-wise filling is also supported and more efficient, make some data...
|
// chunk-wise filling is also supported and more efficient, make some data...
|
||||||
std::vector<double> xy2[2] = {{0, 2, 5}, {0.8, 0.4, 0.7}};
|
std::vector<double> x = {0, 2, 5};
|
||||||
|
std::vector<double> y = {0.8, 0.4, 0.7};
|
||||||
|
|
||||||
|
// fill accepts an iterable over iterables, to avoid copying data we use
|
||||||
|
// std::vector<boost::span<double>> and not std::vector<std::vector<double>>
|
||||||
|
std::vector<boost::span<double>> xy2 = {boost::make_span(x), boost::make_span(y)};
|
||||||
|
|
||||||
|
// alternatively, if the number of axes is known at compile-time,
|
||||||
|
// it is better to use an array of spans, which avoids the heap allocation
|
||||||
|
// std::array<boost::span<double>, 2> xy2 = {boost::make_span(x), boost::make_span(y)};
|
||||||
|
|
||||||
// ... and call fill method
|
// ... and call fill method
|
||||||
h.fill(xy2);
|
h.fill(xy2);
|
||||||
|
|||||||
Reference in New Issue
Block a user