Skip to content

Commit

Permalink
chore(bb): constexpr simplifications
Browse files Browse the repository at this point in the history
  • Loading branch information
fcarreiro committed Aug 11, 2024
1 parent 088aae5 commit 50cf392
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 166 deletions.
117 changes: 16 additions & 101 deletions barretenberg/cpp/src/barretenberg/common/constexpr_utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,22 @@
#include <tuple>
#include <utility>

namespace bb {
namespace detail {

/**
* @brief constexpr_utils defines some helper methods that perform some stl-equivalent operations
* but in a constexpr context over quantities known at compile-time
*
* Current methods are:
*
* constexpr_for : loop over a range , where the size_t iterator `i` is a constexpr variable
* constexpr_find : find if an element is in an array
* @brief Create an index sequence from Min to Max (not included) with an increment of Inc
*/
namespace bb {
template <size_t Min, size_t Max, size_t Inc> constexpr auto make_index_range()
{
static_assert(Max >= Min);
static_assert(Inc >= 1);
return []<size_t... Is>(std::index_sequence<Is...>) {
return std::index_sequence<Min + (Is * Inc)...>{};
}(std::make_index_sequence<(Max - Min - 1) / Inc + 1>{});
}

} // namespace detail

/**
* @brief Implements a loop using a compile-time iterator. Requires c++20.
Expand Down Expand Up @@ -64,99 +70,8 @@ namespace bb {
*/
template <size_t Start, size_t End, size_t Inc, class F> constexpr void constexpr_for(F&& f)
{
// Call function `f<Start>()` iff Start < End
if constexpr (Start < End) {
// F must be a template lambda with a single **typed** template parameter that represents the iterator
// (e.g. [&]<size_t i>(){ ... } is good)
// (and [&]<typename i>(){ ... } won't compile!)

/**
* Explaining f.template operator()<Start>()
*
* The following line must explicitly tell the compiler that <Start> is a template parameter by using the
* `template` keyword.
* (if we wrote f<Start>(), the compiler could legitimately interpret `<` as a less than symbol)
*
* The fragment `f.template` tells the compiler that we're calling a *templated* member of `f`.
* The "member" being called is the function operator, `operator()`, which must be explicitly provided
* (for any function X, `X(args)` is an alias for `X.operator()(args)`)
* The compiler has no alias `X.template <tparam>(args)` for `X.template operator()<tparam>(args)` so we must
* write it explicitly here
*
* To summarize what the next line tells the compiler...
* 1. I want to call a member of `f` that expects one or more template parameters
* 2. The member of `f` that I want to call is the function operator
* 3. The template parameter is `Start`
* 4. The function operator itself contains no arguments
*/
f.template operator()<Start>();

// Once we have executed `f`, we recursively call the `constexpr_for` function, increasing the value of `Start`
// by `Inc`
constexpr_for<Start + Inc, End, Inc>(f);
}
}

/**
* @brief returns true/false depending on whether `key` is in `container`
*
* @tparam container i.e. what are we looking in?
* @tparam key i.e. what are we looking for?
* @return true found!
* @return false not found!
*
* @details method is constexpr and can be used in static_asserts
*/
template <const auto& container, auto key> constexpr bool constexpr_find()
{
// using ElementType = typename std::remove_extent<ContainerType>::type;
bool found = false;
constexpr_for<0, container.size(), 1>([&]<size_t k>() {
if constexpr (std::get<k>(container) == key) {
found = true;
}
});
return found;
}

/**
* @brief Create a constexpr array object whose elements contain a default value
*
* @tparam T type contained in the array
* @tparam Is index sequence
* @param value the value each array element is being initialized to
* @return constexpr std::array<T, sizeof...(Is)>
*
* @details This method is used to create constexpr arrays whose encapsulated type:
*
* 1. HAS NO CONSTEXPR DEFAULT CONSTRUCTOR
* 2. HAS A CONSTEXPR COPY CONSTRUCTOR
*
* An example of this is bb::field_t
* (the default constructor does not default assign values to the field_t member variables for efficiency reasons, to
* reduce the time require to construct large arrays of field elements. This means the default constructor for field_t
* cannot be constexpr)
*/
template <typename T, std::size_t... Is>
constexpr std::array<T, sizeof...(Is)> create_array(T value, std::index_sequence<Is...> /*unused*/)
{
// cast Is to void to remove the warning: unused value
std::array<T, sizeof...(Is)> result = { { (static_cast<void>(Is), value)... } };
return result;
constexpr auto indices = detail::make_index_range<Start, End, Inc>();
[&]<size_t... Is>(std::index_sequence<Is...>) { (f.template operator()<Is>(), ...); }(indices);
}

/**
* @brief Create a constexpr array object whose values all are 0
*
* @tparam T
* @tparam N
* @return constexpr std::array<T, N>
*
* @details Use in the same context as create_array, i.e. when encapsulated type has a default constructor that is not
* constexpr
*/
template <typename T, size_t N> constexpr std::array<T, N> create_empty_array()
{
return create_array(T(0), std::make_index_sequence<N>());
}
}; // namespace bb
104 changes: 39 additions & 65 deletions barretenberg/cpp/src/barretenberg/flavor/flavor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,70 +262,41 @@ auto get_unshifted_then_shifted(const auto& all_entities)
return concatenate(all_entities.get_unshifted(), all_entities.get_shifted());
};

template <typename Tuple, size_t... I>
static constexpr auto _compute_max_partial_relation_length_internal([[maybe_unused]] std::index_sequence<I...>)
{
constexpr std::array<size_t, sizeof...(I)> lengths = { std::tuple_element_t<I, Tuple>::RELATION_LENGTH... };
return *std::max_element(lengths.begin(), lengths.end());
}

/**
* @brief Utility function to find max PARTIAL_RELATION_LENGTH tuples of Relations.
* @details The "partial length" of a relation is 1 + the degree of the relation, where any challenges used in the
* relation are as constants, not as variables..
*/
template <typename Tuple, std::size_t Index = 0> static constexpr size_t compute_max_partial_relation_length()
{
return _compute_max_partial_relation_length_internal<Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>());
}

template <typename Tuple, size_t... I>
static constexpr auto _compute_max_total_relation_length_internal([[maybe_unused]] std::index_sequence<I...>)
template <typename Tuple, std::size_t Index = 0> constexpr size_t compute_max_partial_relation_length()
{
constexpr std::array<size_t, sizeof...(I)> lengths = { std::tuple_element_t<I, Tuple>::TOTAL_RELATION_LENGTH... };
return *std::max_element(lengths.begin(), lengths.end());
constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Tuple>>();
return []<std::size_t... Is>(std::index_sequence<Is...>) {
return std::max({ std::tuple_element_t<Is, Tuple>::RELATION_LENGTH... });
}(seq);
}

/**
* @brief Utility function to find max TOTAL_RELATION_LENGTH among tuples of Relations.
* @details The "total length" of a relation is 1 + the degree of the relation, where any challenges used in the
* relation are regarded as variables.
*/
template <typename Tuple> static constexpr size_t compute_max_total_relation_length()
template <typename Tuple> constexpr size_t compute_max_total_relation_length()
{
return _compute_max_total_relation_length_internal<Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>());
}

template <typename Tuple, size_t... I>
static constexpr auto _compute_number_of_subrelations_internal([[maybe_unused]] std::index_sequence<I...>)
{
constexpr std::array<size_t, sizeof...(I)> lengths = {
std::tuple_element_t<I, Tuple>::SUBRELATION_PARTIAL_LENGTHS.size()...
};
return std::accumulate(lengths.begin(), lengths.end(), 0);
constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Tuple>>();
return []<std::size_t... Is>(std::index_sequence<Is...>) {
return std::max({ std::tuple_element_t<Is, Tuple>::TOTAL_RELATION_LENGTH... });
}(seq);
}

/**
* @brief Utility function to find the number of subrelations.
*/
template <typename Tuple> static constexpr size_t compute_number_of_subrelations()
{
return _compute_number_of_subrelations_internal<Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>());
}

template <typename Tuple, size_t NUM_INSTANCES, bool optimised = false, size_t... I>
static constexpr auto _create_protogalaxy_tuple_of_tuples_of_univariates_internal(
[[maybe_unused]] std::index_sequence<I...>)
template <typename Tuple> constexpr size_t compute_number_of_subrelations()
{
if constexpr (optimised) {
return std::make_tuple(
typename std::tuple_element_t<I, Tuple>::template OptimisedProtogalaxyTupleOfUnivariatesOverSubrelations<
NUM_INSTANCES>{}...);
} else {
return std::make_tuple(
typename std::tuple_element_t<I, Tuple>::template ProtogalaxyTupleOfUnivariatesOverSubrelations<
NUM_INSTANCES>{}...);
}
constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Tuple>>();
return []<std::size_t... I>(std::index_sequence<I...>) {
return (0 + ... + std::tuple_element_t<I, Tuple>::SUBRELATION_PARTIAL_LENGTHS.size());
}(seq);
}

/**
Expand All @@ -336,17 +307,20 @@ static constexpr auto _create_protogalaxy_tuple_of_tuples_of_univariates_interna
* @tparam optimised Enable optimised version with skipping some of the computation
*/
template <typename Tuple, size_t NUM_INSTANCES, bool optimised = false>
static constexpr auto create_protogalaxy_tuple_of_tuples_of_univariates()
{
return _create_protogalaxy_tuple_of_tuples_of_univariates_internal<Tuple, NUM_INSTANCES, optimised>(
std::make_index_sequence<std::tuple_size_v<Tuple>>());
}

template <typename Tuple, size_t... I>
static constexpr auto _create_sumcheck_tuple_of_tuples_of_univariates_internal(
[[maybe_unused]] std::index_sequence<I...>)
constexpr auto create_protogalaxy_tuple_of_tuples_of_univariates()
{
return std::make_tuple(typename std::tuple_element_t<I, Tuple>::SumcheckTupleOfUnivariatesOverSubrelations{}...);
constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Tuple>>();
return []<size_t... I>(std::index_sequence<I...>) {
if constexpr (optimised) {
return std::make_tuple(
typename std::tuple_element_t<I, Tuple>::
template OptimisedProtogalaxyTupleOfUnivariatesOverSubrelations<NUM_INSTANCES>{}...);
} else {
return std::make_tuple(
typename std::tuple_element_t<I, Tuple>::template ProtogalaxyTupleOfUnivariatesOverSubrelations<
NUM_INSTANCES>{}...);
}
}(seq);
}

/**
Expand All @@ -355,26 +329,26 @@ static constexpr auto _create_sumcheck_tuple_of_tuples_of_univariates_internal(
* univariates whose size is equal to the number of subrelations of the relation. The length of a univariate in an inner
* tuple is determined by the corresponding subrelation length.
*/
template <typename Tuple> static constexpr auto create_sumcheck_tuple_of_tuples_of_univariates()
{
return _create_sumcheck_tuple_of_tuples_of_univariates_internal<Tuple>(
std::make_index_sequence<std::tuple_size_v<Tuple>>());
}

template <typename Tuple, size_t... I>
static constexpr auto _create_tuple_of_arrays_of_values_internal([[maybe_unused]] std::index_sequence<I...>)
template <typename Tuple> constexpr auto create_sumcheck_tuple_of_tuples_of_univariates()
{
return std::make_tuple(typename std::tuple_element_t<I, Tuple>::SumcheckArrayOfValuesOverSubrelations{}...);
constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Tuple>>();
return []<size_t... I>(std::index_sequence<I...>) {
return std::make_tuple(
typename std::tuple_element_t<I, Tuple>::SumcheckTupleOfUnivariatesOverSubrelations{}...);
}(seq);
}

/**
* @brief Construct tuple of arrays
* @details Container for storing value of each identity in each relation. Each Relation contributes an array of
* length num-identities.
*/
template <typename Tuple> static constexpr auto create_tuple_of_arrays_of_values()
template <typename Tuple> constexpr auto create_tuple_of_arrays_of_values()
{
return _create_tuple_of_arrays_of_values_internal<Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>());
constexpr auto seq = std::make_index_sequence<std::tuple_size_v<Tuple>>();
return []<size_t... I>(std::index_sequence<I...>) {
return std::make_tuple(typename std::tuple_element_t<I, Tuple>::SumcheckArrayOfValuesOverSubrelations{}...);
}(seq);
}

} // namespace bb
Expand Down

0 comments on commit 50cf392

Please sign in to comment.