* finishing the user guide

* get master badges back
This commit is contained in:
Hans Dembinski
2019-02-24 22:14:30 +01:00
parent 468510a443
commit ff97700470
26 changed files with 372 additions and 296 deletions

View File

@@ -6,10 +6,8 @@ Coded with ❤. Powered by the [Boost community](https://www.boost.org) and the
Branch | Linux [1] and OSX [2] | Windows [3] | Coverage
------- | --------------------- |------------ | --------
develop | [![Build Status Travis](https://travis-ci.com/HDembinski/histogram.svg?branch=develop)](https://travis-ci.com/HDembinski/histogram?branch=develop) | [![Build status](https://ci.appveyor.com/api/projects/status/400lx25l3jdpk96b/branch/develop?svg=true)](https://ci.appveyor.com/project/HDembinski/histogram/branch/develop) | [![codecov](https://codecov.io/gh/HDembinski/histogram/branch/develop/graph/badge.svg)](https://codecov.io/gh/HDembinski/histogram)
<!--
master | [![Build Status Travis](https://travis-ci.org/HDembinski/histogram.svg?branch=master)](https://travis-ci.org/HDembinski/histogram?branch=master) | [![Build status Appveyor](https://ci.appveyor.com/api/projects/status/6a15ga3upiv9ca51/branch/master?svg=true)](https://ci.appveyor.com/project/hdembinski/histogram/branch/master) | [![Coverage Status](https://coveralls.io/repos/github/HDembinski/histogram/badge.svg?branch=master)](https://coveralls.io/github/HDembinski/histogram?branch=master) -->
develop | [![Build Status Travis](https://travis-ci.com/HDembinski/histogram.svg?branch=develop)](https://travis-ci.com/HDembinski/histogram?branch=develop) | [![Build status](https://ci.appveyor.com/api/projects/status/400lx25l3jdpk96b/branch/develop?svg=true)](https://ci.appveyor.com/project/HDembinski/histogram/branch/develop) | [![codecov](https://codecov.io/gh/HDembinski/histogram/branch/develop/graph/badge.svg)](https://codecov.io/gh/HDembinski/histogram/branch/develop)
master | [![Build Status Travis](https://travis-ci.com/HDembinski/histogram.svg?branch=master)](https://travis-ci.com/HDembinski/histogram?branch=master) | [![Build status Appveyor](https://ci.appveyor.com/api/projects/status/400lx25l3jdpk96b/branch/master?svg=true)](https://ci.appveyor.com/project/HDembinski/histogram/branch/master) | [![codecov](https://codecov.io/gh/HDembinski/histogram/branch/master/graph/badge.svg)](https://codecov.io/gh/HDembinski/histogram/branch/master)
1. gcc-5.5.0, clang-5.0.0
2. Xcode 9.4

View File

@@ -1,6 +1,6 @@
[section:concepts Concepts]
Users can extend the library with new axis, storage, and accumulator types.
Users can extend the library with various new types whose concepts are defined here.
[section:axis AxisType]
@@ -60,4 +60,10 @@ A `StorageType` is required to:
[endsect]
[section:accumulator AccumulatorType]
TODO
[endsect]
[endsect]

View File

@@ -11,7 +11,7 @@ When the axis types for the histogram are known at compile-time, the library gen
[import ../examples/getting_started_listing_01.cpp]
[getting_started_listing_01]
We passed the [classref boost::histogram::axis::regular regular] axis type directly to the [funcref boost::histogram::make_histogram() make_histogram] function. The library then generates a specialized histogram type with just one regular axis from a generic template.
We passed the [classref boost::histogram::axis::regular regular] axis type directly to the [headerref boost/histogram/make_histogram.hpp make_histogram] function. The library then generates a specialized histogram type with just one regular axis from a generic template.
* Pro: Many user errors are already caught at compile-time, not at run-time.
* Con: You get template errors if you make a mistake, which may be hard to read. We try to give you useful error messages, but still.

View File

@@ -2,18 +2,18 @@
Boost.Histogram is designed to make simple things simple, yet complex things possible. Correspondingly, this guides covers the basic usage first, and the advanced usage in later sections. For an alternative quick start guide, have a look at the [link histogram.getting_started Getting started] section.
[section Make a histogram]
[section Making a histogram]
A histogram consists of a [link histogram.concepts.storage storage] and a sequence of [link histogram.concepts.axis axis] objects. The storage represents a grid of cells of counters. The axis objects maps input values to indices, which are used to look up the cell. You don't normally have to worry about the storage, since the library provides a very good default. There are many interesting axis types to choose from, but for now let us stick to the most common axis, the [classref boost::histogram::axis::regular regular] axis. It represents equidistant intervals on the real line.
Use the convenient factory function [funcref boost::histogram::make_histogram make_histogram] to make the histograms. In the following example, a histogram with a single axis is created.
Use the convenient factory function [headerref boost/histogram/make_histogram.hpp make_histogram] to make the histograms. In the following example, a histogram with a single axis is created.
[import ../examples/guide_make_static_histogram.cpp]
[guide_make_static_histogram]
An axis object defines how input values are mapped to bins, which means that it defines the number of bins along that axis and a mapping function from input values to bins. If you provide one axis, the histogram is one-dimensional. If you provide two, it is two-dimensional, and so on.
In the example above, the compiler knows the number of axes and their type at compile-time, the information can be deduced from the arguments to [funcref boost::histogram::make_histogram make_histogram]. This gives the best performance, but sometimes you only know the axis configuration at run-time, because it depends on information that's only available at run-time. So you can also create axes at run-time and pass them to an overload of the [funcref boost::histogram::make_histogram make_histogram] function. Here is an example.
In the example above, the compiler knows the number of axes and their type at compile-time, the information can be deduced from the arguments to [headerref boost/histogram/make_histogram.hpp make_histogram]. This gives the best performance, but sometimes you only know the axis configuration at run-time, because it depends on information that's only available at run-time. So you can also create axes at run-time and pass them to an overload of the [headerref boost/histogram/make_histogram.hpp make_histogram] function. Here is an example.
[import ../examples/guide_make_dynamic_histogram.cpp]
[guide_make_dynamic_histogram]
@@ -22,9 +22,9 @@ In the example above, the compiler knows the number of axes and their type at co
When the axis types are known at compile-time, the histogram internally holds them in a `std::tuple`, which is very efficient and avoids a memory allocation from the heap. If they are only known at run-time, it stores the axes in a `std::vector`. The interface hides this difference, but not perfectly. There are a corner cases where the difference becomes apparent. The [link histogram.overview.rationale.structure.host rationale] has more details on this point.
]
The factory function [funcref boost::histogram::make_histogram make_histogram] uses the default storage type, which provides safe counting, is fast, and memory efficient. If you want to create a histogram with another storage type, use [funcref boost::histogram::make_histogram_with make_histogram_with]. To learn more about other storage types and how to create your own, have a look at the section [link histogram.guide.expert Advanced Usage].
The factory function named [headerref boost/histogram/make_histogram.hpp make_histogram] uses the default storage type, which provides safe counting, is fast, and memory efficient. If you want to create a histogram with another storage type, use [headerref boost/histogram/make_histogram.hpp make_histogram_with]. To learn more about other storage types and how to create your own, have a look at the section [link histogram.guide.expert Advanced Usage].
[section Choose the right axis]
[section Choosing an axis]
The library provides a number of useful axis types. Here is some advice when to use which.
@@ -93,7 +93,7 @@ All builtin axis types have template arguments for customization. All arguments
[
[Transform (only [classref boost::histogram::axis::regular regular] axis)]
[
A class that implements a bijective transform between the data space and the space in which the bins are equi-distant. Users can define their own transforms and use them with the axis.
A class that implements a monotonic transform between the data space and the space in which the bins are equi-distant. Users can define their own transforms and use them with the axis.
]
]
[
@@ -105,7 +105,7 @@ All builtin axis types have template arguments for customization. All arguments
[
[Options]
[
Compile-time options for the axis, see [enumref boost::histogram::axis::option]. This is used to enable/disable under- and overflow bins, to make an axis circular, or to enable dynamic grow of the axis beyond the initial range.
[headerref boost/histogram/axis/option.hpp Compile-time options] for the axis. This is used to enable/disable under- and overflow bins, to make an axis circular, or to enable dynamic grow of the axis beyond the initial range.
]
]
[
@@ -116,9 +116,11 @@ All builtin axis types have template arguments for customization. All arguments
]
]
You can use the `boost::histogram::use_default` tag type for any of these options, except for the Value and Allocator, to use the library default.
[section Transforms]
A transform is the second template argument of the [classref boost::histogram::axis::regular regular] axis. By default, it is the identity transform, which just forwards the value. Transforms allow you to chose the faster stack-allocated regular axis over the generic [classref boost::histogram::axis::variable variable] axis in more cases.
Transforms are a way to customize a [classref boost::histogram::axis::regular regular] axis. The default is the identity transform which forwards the value. Transforms allow you to chose the faster stack-allocated regular axis over the generic [classref boost::histogram::axis::variable variable] axis in some cases.
A common need is a regular binning in the logarithm of the input value. This can be achieved with a [classref boost::histogram::axis::transform::log log transform]. The follow example shows the builtin transforms.
@@ -133,11 +135,11 @@ Users may write their own transforms and use them with the builtin [classref boo
[section Options]
A set of options can be used to configure each axis type, by OR-ing [enumref boost::histogram::axis::option option identifiers].
A set of options can be used to configure each axis type, by [headerref boost/histogram/axis/option.hpp joining options].
[*Under- and overflow bins]
By default, under- and overflow bins are added automatically for each axis (except if adding them would make no sense). If you create an axis with 20 bins, the histogram will actually have 22 bins along that axis. The extra bins are very useful, as explained in the [link histogram.overview.rationale.uoflow rationale]. If the input cannot exceed the axis range, you can disable the extra bins to save memory. Example:
By default, under- and overflow bins are added automatically for each axis (except if adding them makes no sense). If you create an axis with 20 bins, the histogram will actually have 22 bins along that axis. The extra bins are very useful, as explained in the [link histogram.overview.rationale.uoflow rationale]. If the input cannot exceed the axis range, you can disable the extra bins to save memory. Example:
[import ../examples/guide_axis_with_uoflow_off.cpp]
[guide_axis_with_uoflow_off]
@@ -160,12 +162,12 @@ Each builtin axis has an option to make it grow beyond its initial range when a
[import ../examples/guide_axis_growing.cpp]
[guide_axis_growing]
So this feature is very convenient, but before you use it all the time, keep two caveats in mind.
So this feature is very convenient, but keep two caveats in mind.
* Growing axes come with a run-time cost, since the histogram has to reallocate memory
for all cells when any axis size changes. Whether this performance hit is noticeable depends on your application. This is a minor issue.
for all cells when any axis size changes. Whether this performance hit is noticeable depends on your application. This is a minor issue, the next is more severe.
* If you have unexpected outliers in your data which are far away from the normal range,
the axis could grow to a huge size and the corresponding huge memory request could bring your computer to its knees. This can be a major issue, and it is the reason why growing axis are not the default.
the axis could grow to a huge size and the corresponding huge memory request could bring your computer to its knees. This is the reason why growing axes are not the default.
A growing axis can have under- and overflow bins, but these only count the special floating point values +-infinity and NaN.
@@ -177,88 +179,64 @@ A growing axis can have under- and overflow bins, but these only count the speci
[endsect] [/ make a histogram]
[section Fill a histogram]
[section Filling and accessing a histogram]
After you created a histogram, you want to insert tuples of possibly multi-dimensional and values of different types. This is done with the flexible `operator()` call, which you typically use in a loop. After the histogram has been filled, you want to access the counts per bin at some point. To access each bin, you use a multi-dimensional index, which consists of a sequence of bin indices for each axis in order. Example:
A histogram has been created, now you want to insert tuples of possibly multi-dimensional and values of different types. This is done with the flexible [memberref boost::histogram::histogram::operator() histogram::operator()], which you typically use in a loop. It accepts `N` arguments or a `std::tuple` with `N` elements, where `N` is equal to the number of axes of the histogram. It finds the corresponding bin for the input and increments the bin counter by one.
After the histogram has been filled, use [memberref boost::histogram::histogram::at histogram::at] (in analogy to `std::vector::at`) to access the cell values. It accepts integer indices, one for each axis of the histogram. Example:
[import ../examples/guide_fill_histogram.cpp]
[guide_fill_histogram]
`operator()` either takes `N` arguments or a container with `N` elements, where `N` is equal to the number of axes of the histogram. It finds the corresponding bin for the input and increments the bin counter by one.
`operator()(..., weight(w))` or `operator()(weight(w), ...)` does the same, but increments the bin counter by the value `w`, which may be an integer or floating point type. The helper function `weight()` just marks this argument as a weight, so that it can be distinguished from the other inputs. It may be the first or last argument. You can freely mix calls with and without a `weight`. Calls without `weight` act like the weight is `1`. Why weighted increments are sometimes useful is explained [link histogram.overview.rationale.weights in the rationale].
For a histogram `hist`, the calls `hist(weight(w), ...)` and `hist(..., weight(w))` increment the bin counter by the value `w` instead, where `w` may be an integer or floating point number. The helper function [funcref boost::histogram::weight() weight()] marks this argument as a weight, so that it can be distinguished from the other inputs. It can be the first or last argument. You can freely mix calls with and without a weight. Calls without `weight` act like the weight is `1`. Why weighted increments are sometimes useful is explained [link histogram.overview.rationale.weights in the rationale].
[note The default storage loses its no-overflow-guarantee when you pass floating point weights, but maintains it for integer weights.]
The method `histogram::at(...)` (in analogy to `std::vector::at`). It accepts integral indices, one for each axis of the histogram, and returns the cell value.
When the weights come from a stochastic process, it is useful to keep track of the variance of the sum of weights per cell. A specialized histogram can be generated with the [funcref boost::histogram::make_weighted_histogram make_weighted_histogram] factory function which does that.
If you want to iterate over all cells, use the [funcref boost::histogram::indexed indexed] range generator, which is convenient and faster than a naive for loop:
[import ../examples/guide_fill_weighted_histogram.cpp]
[guide_fill_weighted_histogram]
When iterating over all cells, using [memberref boost::histogram::histogram::at histogram::at] can be inconvenient. The [funcref boost::histogram::indexed indexed] range generator is provided for this case, which is very convenient and /faster/ than naive for-loops:
[import ../examples/guide_indexed_access.cpp]
[guide_indexed_access]
[endsect]
[section Fill a profile or any histogram with accumulators]
[section Making a profile]
You can do more than just counting with this library. Histogram cells can be filled with arbitrary accumulators which may accept a sample. When the accumulator computes the mean of the samples, the data structure is called a /profile/. Profiles can be generated with the factory function [boost::histogram::make_profile make_profile]. Example:
Histograms from this library can do more than counting, they can hold arbitrary accumulators which accept samples. A histogram is called a /profile/, if it computes the means of the samples in each cell. Profiles can be generated with the factory function [funcref boost::histogram::make_profile make_profile].
[import ../examples/guide_fill_profile.cpp]
[guide_fill_profile]
Just like `weight()`, `sample()` is a marker function. And like `weight()`, it must be the first or last argument. Accumulators may accept more than a single sample value, and they may also accept a weight. Example:
Just like [funcref boost::histogram::weight weight()], [funcref boost::histogram::sample sample()] is a marker function. It must be the first or last argument.
[import ../examples/guide_fill_accumulator.cpp]
[guide_fill_accumulator]
Weights and samples may be combined, if the histogram accumulators can handle weights. When both [funcref boost::histogram::weight weight()] and [funcref boost::histogram::sample sample()] appear in [memberref boost::histogram::operator() histogram::operator()], they can be in any order with respect to other, but they must be the first and/or last arguments. To make a profile which can compute weighted means use the [funcref boost::histogram::make_weighted_profile make_weighted_profile] factory function.
[endsect]
[section Arithmetic operators]
[section Using operators]
Some arithmetic operations are supported for histograms. Histograms are...
The following operators are supported for pairs of histograms `+, -, *, /, ==, !=`. Histograms can also be multiplied and divided by a scalar. Only a subset of the arithmetic operators is available when the underlying cell value type only supports a subset.
* equal comparable
* addable (adding histograms with non-matching axes is an error)
* multipliable and divisible by a number
The arithmetic operators can only be used when the histograms have the same axis configuration. This checked at compile-time if possible, or at run-time. An exception is thrown if the configurations do not match. Two histograms have the same axis configuration, if all axes compare equal, which includes a comparison of their metadata. Two histograms compare equal, when their axis configurations and all their cell values compare equal.
These operations are commutative, except for division. Dividing a number by a histogram is not implemented.
Two histograms compare equal, if...
* all axes compare equal, including axis labels
* all values and variance estimates compare equal
Adding histograms is useful, if you want to parallelize the filling of a histogram over several threads or processes. Fill independent copies of the histogram in worker threads, and then add them all up in the main thread.
Multiplying by a number is useful to re-weight histograms before adding them, for those who need to work with weights. Multiplying by a factor `x` has a different effect on value and variance of each bin counter. The value is multiplied by `x`, but the variance is multiplied by `x*x`. This follows from the properties of the variance, as explained in [link histogram.overview.rationale.variance the rationale].
[warning Because of special behavior of the variance, adding a histogram to itself is not identical to multiplying the original histogram by two, as far as the variance is concerned.]
[note Scaling a histogram automatically converts the bin counters from an integral number per bin to two real numbers per bin, if that has not happened already, because value and variance are different after the multiplication.]
Here is an example which demonstrates the supported operators.
[note If the metadata type has `operator==` defined, it is used in the axis configuration comparison. Metadata types without `operator==` are considered equal, if they are the same type.]
[import ../examples/guide_histogram_operators.cpp]
[guide_histogram_operators]
[endsect]
[note A histogram with default storage converts its cell values to double when they are to be multiplied with or divided by a real number, or when a real number is added or subtracted. At this point the no-overflow-guarantee is lost.]
[section Reductions]
When you have a high-dimensional histogram, sometimes you want to remove some axes and look at the equivalent lower-dimensional version obtained by summing over the counts along the removed axes. Perhaps you found out that there is no interesting structure along an axis, so it is not worth keeping that axis around, or you want to visualize 1d or 2d projections of a high-dimensional histogram.
For this purpose use the `histogram::reduce_to(...)` method, which returns a new reduced histogram with fewer axes. The method accepts indices (one or more), which indicate the axes that are kept. The static histogram only accepts compile-time numbers, while the dynamic histogram also accepts runtime numbers and iterators over numbers.
Here is an example to illustrates this.
[import ../examples/guide_histogram_reduction.cpp]
[guide_histogram_reduction]
[note When the storage tracks weight variances, such as [classref boost::histogram::weight_storage], adding two copies of a histogram produces a different result than scaling the histogram by a factor of two, as shown in the last example. The is a consequence of the mathematical properties of variances. They add like normal numbers, but scaling by `s` means that variances are scaled by `s^2`.]
[endsect]
[section Streaming]
Simple ostream operators are shipped with the library, which are internally used by the unit tests. These give text representations of axis and histogram configurations, but do not show the histogram content. They may be useful for debugging, but users are encouraged to write their own ostream operators. Therefore, the headers with the builtin implementations are not included by the super header `#include <boost/histogram.hpp>`, so that users can use their own implementations. The following example shows the effect of output streaming.
Simple ostream operators are shipped with the library, which are internally used by the unit tests. These give text representations of axis and histogram configurations, but do not show the histogram content. They may be useful for debugging and more useful text representations are planned for the future. Since user may want to use their own implementations, the headers with the builtin implementations are not included by the super header `#include <boost/histogram.hpp>`. The following example shows the effect of output streaming.
[import ../examples/guide_histogram_streaming.cpp]
[guide_histogram_streaming]
@@ -267,56 +245,144 @@ Simple ostream operators are shipped with the library, which are internally used
[section Serialization]
The library supports serialization via [@boost:/libs/serialization/index.html Boost.Serialization]. The serialization code is not included by the super header `#include <boost/histogram.hpp>`, so that the library can be used without Boost.Serialization.
The library supports serialization via [@boost:/libs/serialization/index.html Boost.Serialization]. The serialization code is not included by the super header `#include <boost/histogram.hpp>`, so that the library can be used without Boost.Serialization or with a different serialization library.
[import ../examples/guide_histogram_serialization.cpp]
[guide_histogram_serialization]
[endsect]
[section Algorithms]
The library was designed to work with algorithms from the C++ standard library. In addition, a support library of algorithms is included with common operations on histograms.
[section Using algorithms from the C++ standard library]
The histogram class has standard random-access iterators which can be used with various algorithms from the standard library. Some things need to be kept in mind:
* The histogram iterators also walk over the under- and overflow bins, if they are present. If this is inconvenient, the corresponding axis options may be disabled. A way to skip the extra bins is to use the iterators from the [funcref boost::histogram::indexed] range generator instead of the raw histogram iterators, but the values then need to be accessed through an extra dereference operation.
* The iteration order for histograms with several axes is an implementation-detail, but for histograms with one axis it is guaranteed to be the unsurprising order: bins are sequentially increasing, the upper edge of the current bin is the lower edge of the next bin.
[import ../examples/guide_stdlib_algorithms.cpp]
[guide_stdlib_algorithms]
[endsect]
[section Summation]
It is easy to iterate over all histogram cells to compute the sum of cell values by hand or to use an algorithm from the standard library to do so, but it is not safe. The result may not be accurate or overflow, if the sum is represented by an integer type. The library provides [funcref boost::histogram::algorithm::sum] as a safer, albeit slower, alternative. It uses the [@https://en.wikipedia.org/wiki/Kahan_summation_algorithm Neumaier algorithm] in double precision for integers and floating point types, and does the naive sum otherwise.
[endsect]
[section Projections]
Sometimes you want to remove some axes from a high-dimensional histogram and obtain the equivalent lower-dimensional version, that one gets by summing up the bin contents of the removed axes. This is called a /projection/. This is useful if you found out that there is no interesting structure along an axis, so it is not worth keeping that axis around, or if you want to visualize 1d or 2d projections of a high-dimensional histogram.
The library provides the [funcref boost::histogram::algorithm::project] function for this purpose, which returns the reduced histogram. It accepts the original histogram and the indices (one or more) of the axes that are kept. If your histogram is static, meaning the axis configuration is known at compile-time, you need to use compile-time numbers as indices, while the histogram with a dynamic axis configuration can also accepts run-time indices and even an iterators over indices.
[import ../examples/guide_histogram_projection.cpp]
[guide_histogram_projection]
[endsect]
[section Reductions]
A projection removes an axis completely. Another way to obtain a smaller histogram which consume less memory is the [funcref boost::histogram::algorithm::reduce] function to /shrink/ and /rebin/ axes.
Shrinking means that the range of an axis is reduced and the number of bins along this axis. Rebinning means that adjacent bins are merged. For each set of adjacent bins, new bin is formed which covers the interval of the merged bins and has their added content. These two operations can be combined and applied to several axes at once (which is more efficient than doing it sequentially).
[import ../examples/guide_histogram_reduction.cpp]
[guide_histogram_reduction]
[endsect]
[endsect] [/ Algorithms]
[section:expert Advanced usage]
The library is customizable and extensible by users. Users can create new axis types and use them with the histogram, or implement a custom storage policy, or use a builtin storage policy with a custom counter type.
[section User-defined axis class]
[section Parallelization options]
In C++, users can implement their own axis class without touching any library code. The custom axis is just passed to the histogram factories `make_static_histogram(...)` and `make_dynamic_histogram(...)`. The custom axis class must meet the requirements of the [link histogram.concepts.axis axis concept].
There are two ways to generate a single histogram using several threads.
The simplest way to make a custom axis is to derive from a builtin class. Here is a contrived example of a custom axis that inherits from the [classref boost::histogram::axis::integer integer axis] and accepts c-strings representing numbers.
1. Each thread has its own copy of the histogram. Each copy is independently filled. The copies are then added in the main thread. Use this when you can afford having `N` copies of the histogram in memory for `N` threads.
2. Each thread is filling the same histogram, concurrently. This requires a thread-safe storage that can handle concurrent writes and a histogram made entirely of non-growing axes (the second requirement may be relaxed in the future).
The library currently does not provide a builtin thread-safe storage yet (one will be added in the future), but a provisional one can be made with `std::atomic` counters, as shown in the next example.
[import ../examples/guide_parallel_filling.cpp]
[guide_parallel_filling]
[endsect]
[section User-defined axes]
It is easy to make custom axis classes that work with the library. The custom axis class must meet the requirements of the [link histogram.concepts.axis axis concept].
Users can create a custom axis by derive from a builtin axis type and customize its behavior. The following examples demonstrates a modification of the [classref boost::histogram::axis::integer integer axis].
[import ../examples/guide_custom_modified_axis.cpp]
[guide_custom_modified_axis]
Alternatively, you can also make an axis completely from scratch. An minimal axis is a functor that maps an input to a bin index. The index has a range `[0, AxisType::size())`.
How to make an axis completely from scratch is shown in the next example.
[import ../examples/guide_custom_minimal_axis.cpp]
[guide_custom_minimal_axis]
Such a minimal axis works, even though it lacks convenience features provided by the builtin axis types. For example, one cannot iterate over this axis. Not even a bin description can be queried, because `operator[]` is not implemented. It is up to the user to implement these optional aspects.
Such a minimal axis lacks many features provided by the builtin axis types, for example, one cannot iterate over this axis, but this functionality can be added as needed.
[endsect]
[section User-defined storage policy]
[section Axis which accepts several arguments]
Histograms can be created which use a custom storage class with the factory function [funcref boost::histogram::make_histogram_with]. This factory function accepts many standard containers as storage: vectors, arrays, and maps. These are automatically wrapped with a [classref boost::histogram::storage_adaptor] to provide the storage interface needed by the library.
It is also possible to make
A `std::vector` may provide higher performance in some cases than the default storage. The counter type can then be chosen by the user. Usually, this would be an integral or floating point type. This storage may be faster than the default storage for low-dimensional histograms (or not, one has to measure).
[import ../examples/guide_custom_2d_axis.cpp]
[guide_custom_2d_axis]
[endsect]
[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.
[warning The no-overflow-guarantee is only valid if the [classref boost::histogram::unlimited_storage default storage] is used. If you change the storage policy, you need to know what you are doing.]
Users who work exclusively with weighted fills should use a `std::vector<double>`, it will be faster than using the default storage. Users may also store complex accumulators in the vector. [classref boost::histogram::accumulators::weighted_sum] tracks a variance estimate together with the sum of weights. [classref boost::histogram::accumulators::mean] computes the mean of samples are sorted into the cell.
A `std::vector` may provide higher performance than the default storage with a carefully chosen counter type. Usually, this would be an integral or floating point type. A `std::vector`-based storage may be faster than the default storage for low-dimensional histograms (or not, you need to measure).
An interesting alternative to a `std::vector` is to use a `std::array`. The latter provides a storage with a fixed maximum capacity (the size of the array). `std::array` allocates the memory on the stack. Using this in combination with a static histogram allows one to create histograms completely on the stack, which is very fast.
Users who work exclusively with weighted histograms should chose a `std::vector<double>` over the default storage, it will be faster. If they also want to track the variance of the sum of weights, using the factor function [funcref boost::histogram::make_weighted_histogram make_weighted_histogram] is a convenient, which provides a histogram with a vector-based storage of [classref boost::histogram::accumulators::weighted_sum weighted_sum] accumulators.
Finally, a `std::map` and `std::unordered_map` is adapted into a sparse storage, where empty cells do not consume any memory, but the memory consumption per cell is much larger than for a vector or array, and the cells are usually not located in a contiguous memory section.
An interesting alternative to a `std::vector` is to use a `std::array`. The latter provides a storage with a fixed maximum capacity (the size of the array). `std::array` allocates the memory on the stack. In combination with a static axis configuration this allows one to create histograms completely on the stack without any dynamic memory allocation. Small stack-based histograms can be created and destroyed very fast.
Here is an example of a histogram constructed with an alternative storage policy.
Finally, a `std::map` or `std::unordered_map` is adapted into a sparse storage, where empty cells do not consume any memory. This sounds very attractive, but the memory consumption per cell in a map is much larger than for a vector or array. Furthermore, the cells are usually scattered in memory, which increases cache misses and degrades performance. Whether a sparse storage performs better than a dense storage depends strongly on the usage scenario. It is easy switch from dense to sparse storage and back, so one can try both options.
The following example shows how histograms are constructed which use an alternative storage classes.
[import ../examples/guide_custom_storage.cpp]
[guide_custom_storage]
[endsect]
[section User-defined accumulators]
A storage can hold arbitrary accumulators which may accept an arbitrary number of arguments. The arguments are passed to the accumulator via the [funcref boost::histogram::sample sample] call, for example, `sample(1, 2, 3)` for an accumulator which accepts three arguments. Accumulators are often placed in a vector-based storage, so the library provides an alias, the `boost::histogram::dense_storage`, which is templated on the accumulator type.
The library provides several accumulators:
* [classref boost::histogram::accumulators::sum sum] accepts no samples, but accepts a weight. It is an alternative to a plain arithmetic type as a counter. It provides an advantage when histograms are filled with weights that differ dramatically in magnitude. The sum of weights is computed incrementally with the Neumaier algorithm, which is more accurate than a normal sum of arithmetic types.
* [classref boost::histogram::accumulators::weighted_sum weighted_sum] accepts no samples, but accepts a weight. It computes the sum of weights and the sum of weights squared, the variance estimate of the sum of weights. This type is used by the [funcref boost::histogram::make_weighted_histogram make_weighted_histogram].
* [classref boost::histogram::accumulators::mean mean] accepts a sample and computes the mean of the samples. [funcref boost::histogram::make_profile make_profile] uses this accumulator.
* [classref boost::histogram::accumulators::weighted_mean weighted_mean] accepts a sample and a weight. It computes the weighted mean of the samples. [funcref boost::histogram::make_weighted_profile make_weighted_profile] uses this accumulator.
Users can easily write their own accumulators and plug them into the histogram, if they adhere to the [link histogram.concepts.transform accumulator concept].
[import ../examples/guide_custom_accumulators.cpp]
[guide_custom_accumulators]
[endsect]
[endsect]
[endsect]

View File

@@ -19,11 +19,12 @@ test-suite minimal :
[ run guide_custom_minimal_axis.cpp ]
[ run guide_custom_modified_axis.cpp ]
[ run guide_custom_storage.cpp ]
[ run guide_fill_accumulator.cpp ]
[ run guide_fill_histogram.cpp ]
[ run guide_fill_weighted_histogram.cpp ]
[ run guide_fill_profile.cpp ]
[ run guide_histogram_operators.cpp ]
[ run guide_histogram_reduction.cpp ]
[ run guide_histogram_projection.cpp ]
[ run guide_histogram_streaming.cpp ]
[ run guide_indexed_access.cpp ]
[ run guide_make_dynamic_histogram.cpp ]

View File

@@ -14,9 +14,10 @@ int main() {
// make a regular axis with 10 bins over interval from 1.5 to 2.5
auto r = axis::regular<>{10, 1.5, 2.5};
// `<>` is needed because the axis is templated, you can drop this on a C++17 compiler
// `<>` is needed in C++14 because the axis is templated,
// in C++17 you can do: auto r = axis::regular{10, 1.5, 2.5};
assert(r.size() == 10);
// you can also make a regular axis by defining the step size with the `step` marker
// alternatively, you can define the step size with the `step` marker
auto r_step = axis::regular<>{axis::step(0.1), 1.5, 2.5};
assert(r_step == r);

View File

@@ -13,9 +13,8 @@ int main() {
using namespace boost::histogram;
// make a circular regular axis ... [0, 180), [180, 360), [0, 180) ....
auto r = axis::regular<double, use_default, use_default,
axis::join<axis::option::overflow, axis::option::circular>>{
2, 0., 360.};
using opts = axis::join<axis::option::overflow, axis::option::circular>;
auto r = axis::regular<double, use_default, use_default, opts>{2, 0., 360.};
assert(r.index(-180) == 1);
assert(r.index(0) == 0);
assert(r.index(180) == 1);

View File

@@ -33,9 +33,6 @@ int main() {
// value below range: axis grows new bins until value is in range
h1(-0.3);
// state: [-0.5, 0.0): 1, [0, 0.5): 1, [0.5, 1.0): 1
std::cout << h1.axis().size() << std::endl;
std::cout << h1.axis().bin(0).lower() << std::endl;
std::cout << h1.axis().bin(2).upper() << std::endl;
assert(h1.axis().size() == 3);
assert(h1.axis().bin(0).lower() == -0.5);
assert(h1.axis().bin(2).upper() == 1.0);

View File

@@ -8,13 +8,12 @@
#include <boost/histogram.hpp>
namespace bh = boost::histogram;
int main() {
using namespace boost::histogram;
// create a 2d-histogram with an "age" and an "income" axis
auto h =
bh::make_histogram(bh::axis::regular<>(20, 0.0, 100.0, "age in years"),
bh::axis::regular<>(20, 0.0, 100.0, "yearly income in $1000"));
auto h = make_histogram(axis::regular<>(20, 0.0, 100.0, "age in years"),
axis::regular<>(20, 0.0, 100.0, "yearly income in 1000 EUR"));
// do something with h
}

View File

@@ -40,8 +40,8 @@ int main() {
assert(r_sqrt.index(-1) == 3);
// make a regular axis with a power transform x^1/3 over [1, 8), [8, 27), [27, 64)
using pow = axis::transform::pow;
axis::regular<double, pow> r_pow(pow{1. / 3.}, 3, 1., 64.);
using pow_trans = axis::transform::pow;
axis::regular<double, pow_trans> r_pow(pow_trans{1. / 3.}, 3, 1., 64.);
// pow transform:
// - generalization of the sqrt transform, power index is first argument of ctor
// - starting the axis at value == 0 is ok, 0^p == 0 for p != 0

View File

@@ -9,21 +9,21 @@
#include <boost/histogram.hpp>
#include <cassert>
namespace bh = boost::histogram;
int main() {
using namespace boost::histogram;
// axis which returns 1 if the input falls inside the unit circle and zero otherwise
struct circle_axis {
bh::axis::index_type index(std::tuple<double, double> point) const {
axis::index_type index(std::tuple<double, double> point) const {
const auto x = std::get<0>(point);
const auto y = std::get<1>(point);
return x * x + y * y <= 1.0;
}
bh::axis::index_type size() const { return 2; }
axis::index_type size() const { return 2; }
};
auto h1 = bh::make_histogram(circle_axis());
auto h1 = make_histogram(circle_axis());
// call looks like for a histogram with N 1d axes if histogram has only one Nd axis
h1(0, 0); // in
@@ -37,8 +37,7 @@ int main() {
assert(h1.at(0) == 2); // out
assert(h1.at(1) == 5); // in
auto h2 =
bh::make_histogram(circle_axis(), bh::axis::category<std::string>({"red", "blue"}));
auto h2 = make_histogram(circle_axis(), axis::category<std::string>({"red", "blue"}));
// pass arguments for first axis as std::tuple
h2(std::make_tuple(0, 0), "red");

View File

@@ -39,9 +39,9 @@ int main() {
"1 count 2 mean 3.5 variance 0.5\n"
"2 count 0 mean 0.0 variance 0.0\n");
// Let's make a custom accumulator. It must have a call operator that accepts
// the argument of the `sample` function. Any return value of the call
// operator is ignored.
// Let's make a custom accumulator, which tracks the maximum of the samples. It must
// have a call operator that accepts the argument of the `sample` function. The return
// value of the call operator is ignored.
struct max {
void operator()(double x) {
if (x > value) value = x;
@@ -65,23 +65,6 @@ int main() {
assert(os2.str() == "0 value 2.5\n"
"1 value 4.0\n"
"2 value 0.0\n");
// // Let's go meta and use the histogram itself as an accumulator, just for
// fun. auto h_nested = make_histogram(axis); auto h2 =
// make_histogram_with(dense_storage<decltype(h_nested)>(h_nested), axis);
// h2(0.0, sample(2)); // sample 2 goes to first bin
// h2(0.1, sample(2.5)); // sample 2.5 goes to first bin
// h2(0.4, sample(3)); // sample 3 goes to second bin
// h2(0.5, sample(4)); // sample 4 goes to second bin
//
// std::ostringstream os2;
// for (auto x : indexed(h2)) {
// os2 << boost::format("%i value %.1f\n") % x.index() % x->value;
// }
// std::cout << os2.str() << std::flush;
// assert(os2.str() == "0 value 2.5\n"
// "1 value 4.0\n"
// "2 value 0.0\n");
}
//]

View File

@@ -9,24 +9,24 @@
#include <boost/histogram.hpp>
#include <cassert>
namespace bh = boost::histogram;
int main() {
using namespace boost::histogram;
// stateless axis which returns 1 if the input is even and 0 otherwise
struct even_odd_axis {
bh::axis::index_type index(int x) const { return x % 2; }
bh::axis::index_type size() const { return 2; }
axis::index_type index(int x) const { return x % 2; }
axis::index_type size() const { return 2; }
};
// threshold axis which returns 1 if the input is above threshold
struct threshold_axis {
threshold_axis(double x) : thr(x) {}
bh::axis::index_type index(double x) const { return x >= thr; }
bh::axis::index_type size() const { return 2; }
axis::index_type index(double x) const { return x >= thr; }
axis::index_type size() const { return 2; }
double thr;
};
auto h = bh::make_histogram(even_odd_axis(), threshold_axis(3.0));
auto h = make_histogram(even_odd_axis(), threshold_axis(3.0));
h(0, 2.0);
h(1, 4.0);

View File

@@ -11,11 +11,11 @@
#include <iostream>
#include <sstream>
namespace bh = boost::histogram;
int main() {
using namespace boost::histogram;
// custom axis, which adapts builtin integer axis
struct custom_axis : public bh::axis::integer<> {
struct custom_axis : public axis::integer<> {
using value_type = const char*; // type that is fed to the axis
using integer::integer; // inherit ctors of base
@@ -23,19 +23,17 @@ int main() {
// the customization point
// - accept const char* and convert to int
// - then call index method of base class
bh::axis::index_type index(value_type s) const {
return integer::index(std::atoi(s));
}
axis::index_type index(value_type s) const { return integer::index(std::atoi(s)); }
};
auto h = bh::make_histogram(custom_axis(3, 6));
auto h = make_histogram(custom_axis(3, 6));
h("-10");
h("3");
h("4");
h("9");
std::ostringstream os;
for (auto&& b : bh::indexed(h)) {
for (auto&& b : indexed(h)) {
os << "bin " << b.index() << " [" << b.bin() << "] " << *b << "\n";
}

View File

@@ -1,53 +0,0 @@
// Copyright 2019 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_fill_accumulator
#include <boost/format.hpp>
#include <boost/histogram.hpp>
#include <cassert>
#include <iostream>
#include <sstream>
#include <utility>
int main() {
using namespace boost::histogram;
// make a profile, it computes the mean of the samples in each histogram cell
auto h = make_profile(axis::regular<>(3, 0.0, 1.0));
// mean is computed from the values marked with the sample() helper function
h(0.10, sample(2.5)); // 2.5 goes to bin 0
h(0.15, sample(3.5)); // 3.5 goes to bin 0
h(0.25, sample(1.2)); // 1.2 goes to bin 1
h(0.31, sample(3.4)); // 3.4 goes to bin 1
// fills from tuples are also supported
auto xs = std::make_tuple(0.5, sample(3.1));
h(xs);
// builtin accumulators have methods to access their state
std::ostringstream os;
for (auto x : indexed(h)) {
// use `.` to access methods of accessor, like `index()`
// use `->` to access methods of accumulator
const auto i = x.index();
const auto n = x->count(); // how many samples are in this bin
const auto vl = x->value(); // mean value
const auto vr = x->variance(); // estimated variance of the mean value
os << boost::format("bin %i count %i value %.1f variance %.1f\n") % i % n % vl % vr;
}
std::cout << os.str() << std::flush;
// assert(os.str() == "bin 0 count 2 value 3.0 variance 0.2\n"
// "bin 1 count 1 value 3.0 variance 0.2\n"
// "bin 2 count 1 value 3.0 variance 0.2\n"
// "bin 3 count 0 value 3.0 variance 0.2\n"
// "bin 4 count 0 value 3.0 variance 0.2\n");
}
//]

View File

@@ -19,9 +19,9 @@ int main() {
auto h = make_histogram(axis::integer<>(0, 3), axis::regular<>(2, 0.0, 1.0));
// fill histogram, number of arguments must be equal to number of axes,
// types must be convertible to axis value type (here double)
h(0, 0.2); // increases bin counter by one
h(2, 0.5, weight(2)); // increase bin counter by 2 instead of 1
// types must be convertible to axis value type (here integer and double)
h(0, 0.2); // increase a cell value by one
h(2, 0.5); // increase another cell value by one
// fills from a tuple are also supported; passing a tuple of wrong size
// causes an error at compile-time or an assertion at runtime in debug mode
@@ -48,7 +48,7 @@ int main() {
assert(h.at(1, 0) == 1);
assert(h.at(1, 1) == 0);
assert(h.at(2, 0) == 1);
assert(h.at(2, 1) == 2);
assert(h.at(2, 1) == 1);
assert(h.at(-1, -1) == 0); // underflow for axis 0 and 1
assert(h.at(-1, 0) == 0); // underflow for axis 0, normal bin for axis 1
assert(h.at(-1, 2) == 0); // underflow for axis 0, overflow for axis 1
@@ -58,7 +58,7 @@ int main() {
// - iteration using begin() and end() includes under- and overflow bins
// - iteration order is an implementation detail and may change in future versions
const double sum = std::accumulate(h.begin(), h.end(), 0.0);
assert(sum == 7);
assert(sum == 6);
}
//]

View File

@@ -0,0 +1,30 @@
// Copyright 2019 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_fill_weighted_histogram
#include <boost/histogram.hpp>
int main() {
using namespace boost::histogram;
// Create a histogram with weight counters that keep track of a variance estimate.
auto h = make_weighted_histogram(axis::regular<>(3, 0.0, 1.0));
h(0.0, weight(1)); // weight 1 goes to first bin
h(0.1, weight(2)); // weight 2 goes to first bin
h(0.4, weight(3)); // weight 3 goes to second bin
h(0.5, weight(4)); // weight 4 goes to second bin
// Weight counters have methods to access the value (sum of weights) and the variance
// (sum of weights squared, why this gives the variance is explained in the rationale)
assert(h[0].value() == 1 + 2);
assert(h[0].variance() == 1 * 1 + 2 * 2);
assert(h[1].value() == 3 + 4);
assert(h[1].variance() == 3 * 3 + 4 * 4);
}
//]

View File

@@ -41,23 +41,23 @@ int main() {
assert(g5.at(0) == 1 && g5.at(1) == 1);
assert(g4 != g5 && g4 == 4 * g5);
// note special effect of multiplication on weighted_sum
auto h = make_histogram_with(std::vector<accumulators::weighted_sum<double>>(),
axis::regular<>(2, -1.0, 1.0));
// note the special effect of multiplication on weight_storage
auto h = make_histogram_with(weight_storage(), axis::regular<>(2, -1.0, 1.0));
h(-0.5);
// counts are: 1 0
assert(h.at(0).value() == 1 && h.at(1).value() == 0);
auto h_sum = h + h; // value 2 0 variance 2 0
auto g_mul = 2 * h; // value 2 0 variance 4 0
auto h_sum = h + h;
auto g_mul = 2 * h;
// equality operator checks variances, so following statement is false
assert(h_sum != g_mul);
// variance is different when histograms are scaled
// values are the same as expected...
assert(h_sum.at(0).value() == g_mul.at(0).value());
// ... but variances differ
assert(h_sum.at(0).variance() == 2 && g_mul.at(0).variance() == 4);
// equality operator checks variances, so histograms are not equal
assert(h_sum != g_mul);
}
//]

View File

@@ -0,0 +1,58 @@
// Copyright 2015-2018 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_histogram_projection
#include <boost/histogram.hpp>
#include <cassert>
#include <iostream>
#include <sstream>
int main() {
using namespace boost::histogram;
using namespace literals; // enables _c suffix
// make a 2d histogram
auto h = make_histogram(axis::regular<>(3, -1.0, 1.0), axis::integer<>(0, 2));
h(-0.9, 0);
h(0.9, 1);
h(0.1, 0);
auto hr0 = algorithm::project(h, 0_c); // keep only first axis
auto hr1 = algorithm::project(h, 1_c); // keep only second axis
// reduce does not remove counts; returned histograms are summed over
// the removed axes, so h, hr0, and hr1 have same number of total counts;
// we compute the sum of counts with the sum algorithm
assert(algorithm::sum(h) == 3 && algorithm::sum(hr0) == 3 && algorithm::sum(hr1) == 3);
std::ostringstream os1;
for (auto x : indexed(h))
os1 << "(" << x.index(0) << ", " << x.index(1) << "): " << *x << "\n";
std::cout << os1.str() << std::flush;
assert(os1.str() == "(0, 0): 1\n"
"(1, 0): 1\n"
"(2, 0): 0\n"
"(0, 1): 0\n"
"(1, 1): 0\n"
"(2, 1): 1\n");
std::ostringstream os2;
for (auto x : indexed(hr0)) os2 << "(" << x.index(0) << ", -): " << *x << "\n";
std::cout << os2.str() << std::flush;
assert(os2.str() == "(0, -): 1\n"
"(1, -): 1\n"
"(2, -): 1\n");
std::ostringstream os3;
for (auto x : indexed(hr1)) os3 << "(- ," << x.index(0) << "): " << *x << "\n";
std::cout << os3.str() << std::flush;
assert(os3.str() == "(- ,0): 2\n"
"(- ,1): 1\n");
}
//]

View File

@@ -8,61 +8,31 @@
#include <boost/histogram.hpp>
#include <cassert>
#include <iostream>
#include <sstream>
namespace bh = boost::histogram;
// example of a generic function for histograms, this one sums all entries
template <typename... Ts>
auto sum(const bh::histogram<Ts...>& h) {
auto result = typename bh::histogram<Ts...>::value_type();
for (auto x : h) result += x;
return result;
}
// note: boost/histogram/algorithm/sum.hpp is a better implementation
int main() {
using namespace bh::literals; // enables _c suffix
using namespace boost::histogram;
// make a 2d histogram
auto h =
bh::make_histogram(bh::axis::regular<>(3, -1.0, 1.0), bh::axis::integer<>(0, 2));
auto h = make_histogram(axis::regular<>(4, 0.0, 4.0), axis::regular<>(4, -1.0, 1.0));
h(-0.9, 0);
h(0.9, 1);
h(0.1, 0);
h(0, -0.9);
h(1, 0.9);
h(2, 0.1);
h(3, 0.1);
auto hr0 = bh::algorithm::project(h, 0_c); // keep only first axis
auto hr1 = bh::algorithm::project(h, 1_c); // keep only second axis
// shrink the first axis to remove the highest bin
// rebin the second axis by merging pairs of adjacent bins
auto h2 = algorithm::reduce(h, algorithm::shrink(0, 0.0, 3.0), algorithm::rebin(1, 2));
// reduce does not remove counts; returned histograms are summed over
// the removed axes, so h, hr0, and hr1 have same number of total counts
assert(sum(h) == 3 && sum(hr0) == 3 && sum(hr1) == 3);
// reduce does not remove counts if the histogram has underflow/overflow bins
assert(algorithm::sum(h) == 4 && algorithm::sum(h2) == 4);
std::ostringstream os1;
for (auto x : bh::indexed(h))
os1 << "(" << x.index(0) << ", " << x.index(1) << "): " << *x << "\n";
std::cout << os1.str() << std::flush;
assert(os1.str() == "(0, 0): 1\n"
"(1, 0): 1\n"
"(2, 0): 0\n"
"(0, 1): 0\n"
"(1, 1): 0\n"
"(2, 1): 1\n");
std::ostringstream os2;
for (auto x : bh::indexed(hr0)) os2 << "(" << x.index(0) << ", -): " << *x << "\n";
std::cout << os2.str() << std::flush;
assert(os2.str() == "(0, -): 1\n"
"(1, -): 1\n"
"(2, -): 1\n");
std::ostringstream os3;
for (auto x : bh::indexed(hr1)) os3 << "(- ," << x.index(0) << "): " << *x << "\n";
std::cout << os3.str() << std::flush;
assert(os3.str() == "(- ,0): 2\n"
"(- ,1): 1\n");
assert(h2.axis(0).size() == 3);
assert(h2.axis(0).bin(0).lower() == 0.0);
assert(h2.axis(0).bin(2).upper() == 3.0);
assert(h2.axis(1).size() == 2);
assert(h2.axis(1).bin(0).lower() == -1.0);
assert(h2.axis(1).bin(1).upper() == 1.0);
}
//]

View File

@@ -28,13 +28,13 @@ int main() {
v1.emplace_back(bins, start, stop);
}
// create dynamic histogram from iterator range
// create histogram from iterator range
// (copying or moving the vector also works, move is shown below)
auto h1 = bh::make_histogram(v1.begin(), v1.end());
assert(h1.rank() == v1.size());
// create second vector of axis::variant, a polymorphic axis type that can hold concrete
// axis types, the types and the number of axis can now vary at run-time
// with a vector of axis::variant (polymorphic axis type that can hold any one of the
// template arguments at a time) the types and number of axis can vary at run-time
auto v2 = std::vector<bh::axis::variant<bh::axis::regular<>, bh::axis::integer<>>>();
v2.emplace_back(bh::axis::regular<>(100, -1.0, 1.0));
v2.emplace_back(bh::axis::integer<>(1, 7));

View File

@@ -12,11 +12,12 @@
namespace bh = boost::histogram;
int main() {
/*
create a static 1d-histogram in default configuration
which covers the real line from -1 to 1 in 100 bins
*/
// create a 1d-histogram in default configuration which
// covers the real line from -1 to 1 in 100 bins
auto h = bh::make_histogram(bh::axis::regular<>(100, -1.0, 1.0));
// rank is the number of axes
assert(h.rank() == 1);
}

View File

@@ -4,7 +4,7 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
//[ guide_custom_storage
//[ guide_parallel_filling
#include <atomic>
#include <boost/histogram.hpp>

View File

@@ -4,7 +4,7 @@
// (See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)
//[ guide_stl_algorithms
//[ guide_stdlib_algorithms
#include <boost/histogram.hpp>
#include <cassert>
@@ -13,52 +13,53 @@
#include <cmath> // sqrt
#include <numeric> // partial_sum, inner_product
namespace bh = boost::histogram;
int main() {
// make histogram and set all cells to 0.25, simulating a perfectly uniform PDF,
// this includes underflow and overflow cells
auto a = bh::make_histogram(bh::axis::regular<>(4, 1.0, 3.0));
std::fill(a.begin(), a.end(), 0.25); // set all counters to 0.25, including *flow cells
using namespace boost::histogram;
// make histogram that represents a probability density function (PDF)
auto h1 = make_histogram(axis::regular<>(4, 1.0, 3.0));
// use std::fill to set all counters to 0.25, including *flow cells
std::fill(h1.begin(), h1.end(), 0.25);
// reset *flow cells to zero
a.at(-1) = a.at(4) = 0;
h1.at(-1) = h1.at(4) = 0;
// compute CDF, overriding cell values
std::partial_sum(a.begin(), a.end(), a.begin());
// compute the cumulative density function (CDF), overriding cell values
std::partial_sum(h1.begin(), h1.end(), h1.begin());
assert(a.at(-1) == 0.0);
assert(a.at(0) == 0.25);
assert(a.at(1) == 0.50);
assert(a.at(2) == 0.75);
assert(a.at(3) == 1.00);
assert(a.at(4) == 1.00);
assert(h1.at(-1) == 0.0);
assert(h1.at(0) == 0.25);
assert(h1.at(1) == 0.50);
assert(h1.at(2) == 0.75);
assert(h1.at(3) == 1.00);
assert(h1.at(4) == 1.00);
// use any_of to check if any cell values are smaller than 0.1,
// and use indexed() to skip underflow and overflow cells
auto a_ind = indexed(a);
auto h1_ind = indexed(h1);
const auto any_small =
std::any_of(a_ind.begin(), a_ind.end(), [](const auto& x) { return *x < 0.1; });
std::any_of(h1_ind.begin(), h1_ind.end(), [](const auto& x) { return *x < 0.1; });
assert(any_small == false); // underflow and overflow are zero, but skipped
// find maximum element
const auto max_it = std::max_element(a.begin(), a.end());
assert(max_it == a.end() - 2);
const auto max_it = std::max_element(h1.begin(), h1.end());
assert(max_it == h1.end() - 2);
// find minimum element
const auto min_it = std::min_element(a.begin(), a.end());
assert(min_it == a.begin());
const auto min_it = std::min_element(h1.begin(), h1.end());
assert(min_it == h1.begin());
// make second PDF
auto b = bh::make_histogram(bh::axis::regular<>(4, 1.0, 4.0));
b.at(0) = 0.1;
b.at(1) = 0.3;
b.at(2) = 0.2;
b.at(3) = 0.4;
auto h2 = make_histogram(axis::regular<>(4, 1.0, 4.0));
h2.at(0) = 0.1;
h2.at(1) = 0.3;
h2.at(2) = 0.2;
h2.at(3) = 0.4;
// computing cosine similiarity: cos(theta) = A dot B / sqrt((A dot A) * (B dot B))
const auto aa = std::inner_product(a.begin(), a.end(), a.begin(), 0.0);
const auto bb = std::inner_product(b.begin(), b.end(), b.begin(), 0.0);
const auto ab = std::inner_product(a.begin(), a.end(), b.begin(), 0.0);
const auto aa = std::inner_product(h1.begin(), h1.end(), h1.begin(), 0.0);
const auto bb = std::inner_product(h2.begin(), h2.end(), h2.begin(), 0.0);
const auto ab = std::inner_product(h1.begin(), h1.end(), h2.begin(), 0.0);
const auto cos_sim = ab / std::sqrt(aa * bb);
assert(std::abs(cos_sim - 0.78) < 1e-2);

View File

@@ -11,7 +11,7 @@
#include <type_traits>
/**
\file Options for builtin axis types.
\file option.hpp Options for builtin axis types.
Options circular and growth are mutually exclusive.
Options circular and underflow are mutually exclusive.
@@ -64,11 +64,11 @@ struct join_impl<T, axis::option::growth>
} // namespace detail
namespace axis {
/// Combines options and corrects for mutually exclusive options.
/// Metafunction that combines options and corrects for mutually exclusive flags.
template <class... Ts>
using join = mp11::mp_fold<mp11::mp_list<Ts...>, option_set<0>, detail::join_impl>;
/// Test whether the bits in b are also set in a.
/// Metafunction that tests whether the bits in b are also set in a.
template <class T, class U>
using test = std::integral_constant<bool, (T::value & U::value ? 1 : 0)>;
} // namespace axis

View File

@@ -23,39 +23,61 @@
namespace boost {
namespace histogram {
/// profile factory from compile-time axis configuration
/**
Make profle from compile-time axis configuration.
@param axis First axis instance.
@param axes Other axis instances.
*/
template <typename Axis, typename... Axes, typename = detail::requires_axis<Axis>>
auto make_profile(Axis&& axis, Axes&&... axes) {
return make_histogram_with(profile_storage(), std::forward<Axis>(axis),
std::forward<Axes>(axes)...);
}
/// profile factory from compile-time axis configuration with weighted mean
/**
Make profle from compile-time axis configuration which accepts weights.
@param axis First axis instance.
@param axes Other axis instances.
*/
template <typename Axis, typename... Axes, typename = detail::requires_axis<Axis>>
auto make_weighted_profile(Axis&& axis, Axes&&... axes) {
return make_histogram_with(weighted_profile_storage(), std::forward<Axis>(axis),
std::forward<Axes>(axes)...);
}
/// profile factory from vector-like
/**
Make profile from iterable range.
@param iterable Iterable range of axis objects.
*/
template <typename Iterable, typename = detail::requires_sequence_of_any_axis<Iterable>>
auto make_profile(Iterable&& c) {
return make_histogram_with(profile_storage(), std::forward<Iterable>(c));
}
/// profile factory from vector-like with weighted mean
/**
Make profile from iterable range which accepts weights.
@param iterable Iterable range of axis objects.
*/
template <typename Iterable, typename = detail::requires_sequence_of_any_axis<Iterable>>
auto make_weighted_profile(Iterable&& c) {
return make_histogram_with(weighted_profile_storage(), std::forward<Iterable>(c));
}
/// profile factory from iterator range
/**
Make profile from iterator interval.
@param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects.
*/
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
auto make_profile(Iterator begin, Iterator end) {
return make_histogram_with(profile_storage(), begin, end);
}
/// profile factory from iterator range with weighted mean
/**
Make profile from iterator interval which accepts weights.
@param begin Iterator to range of axis objects.
@param end Iterator to range of axis objects.
*/
template <typename Iterator, typename = detail::requires_iterator<Iterator>>
auto make_weighted_profile(Iterator begin, Iterator end) {
return make_histogram_with(weighted_profile_storage(), begin, end);