I recently watched a video which inspired me to write my own neural network system, and I wanted to have the amount of nodes in the network be adjustable.
At first I achieved this at runtime by parsing an array of the numbers of nodes but I was wondering if I could do this at compile time instead. Here's an example of the kind of thing I was hoping to accomplish.
template<int FirstNodes, int SecondNodes, int... OtherNodes>
class Net
{
    tuple<Eigen::Matrix<float, FirstNodes, SecondNodes>, ...> m_weights;
    // More matricies with the values from the OtherNodes
};
As a more detailed example, Net<784, 16, 16, 10> n; n.m_weight should have type
tuple<Eigen::Matrix<float, 784, 16>,
    Eigen::Matrix<float, 16, 16>,
    Eigen::Matrix<float, 16, 10>>
From what I know about C++ and constexpr, this should be possible.
I should add that I was able to do
template<int FirstNodes, int SecondNodes, int... OtherNodes>
class Net
{
public:
    Net()
    {
        auto nodes = {FirstNodes, SecondNodes, OtherNodes...};
        auto i = nodes.begin();
        do 
        {
            // Eigen::Matrix<float, Dynamic, Dynamic>
            Eigen::MatrixXf m(*(i++), *i);
        } while (i+1 != nodes.end());
    }
};
But then I'm just using dynamic matricies again and that isn't what I was hoping for.
Any advice or working examples would be greatly appreciated.
You want some sort of type transformation that given a list of N integers returns a tuple of N - 1 matrices. Here's a C++17 solution:
template <int A, int B, int... Is>
auto make_matrix_tuple()
{   
    if constexpr(sizeof...(Is) == 0)
    {
        return std::tuple<Eigen::Matrix<float, A, B>>{};
    }
    else
    {
        return std::tuple_cat(make_matrix_tuple<A, B>(), 
                            make_matrix_tuple<B, Is...>());
    }
}
live example on wandbox
In C++11, you can implement this type transformation recursively:
template <int... Is>
struct matrix_tuple_helper;
template <int A, int B, int... Rest>
struct matrix_tuple_helper<A, B, Rest...>
{
    using curr_matrix = Eigen::Matrix<float, A, B>;
    using type = 
        decltype(
            std::tuple_cat(
                std::tuple<curr_matrix>{},
                typename matrix_tuple_helper<B, Rest...>::type{}
            )
        );
};
template <int A, int B>
struct matrix_tuple_helper<A, B>
{
    using curr_matrix = Eigen::Matrix<float, A, B>;
    using type = std::tuple<curr_matrix>;
};
template <int... Is>
using matrix_tuple = typename matrix_tuple_helper<Is...>::type;
C++14 approach:
struct matrix_tuple_maker
{
    template <int A, int B, int C, int... Is>
    static auto get()
    {
        return std::tuple_cat(get<A, B>(), get<B, C, Is...>());
    }
    template <int A, int B>
    static auto get()
    {
        return std::tuple<Eigen::Matrix<float, A, B>>{};
    }
};
static_assert(std::is_same_v<
    decltype(matrix_tuple_maker::get<784, 16, 16, 10>()),
    std::tuple<Eigen::Matrix<float, 784, 16>,
               Eigen::Matrix<float, 16, 16>,
               Eigen::Matrix<float, 16, 10>>
    >);
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With