C++ Logo

STD-PROPOSALS

Advanced search

Subject: [std-proposals] Arbitrary dimensional bittensor extending bitset.
From: Pablo Ruiz (parugawork_at_[hidden])
Date: 2020-12-14 14:22:50


Hi everyone,

These past days I have been playing with some bit manipulation by using the
c++20 std and extending from it.
What I have found is the nice templated class std::bitset<N> and the
utility functions in the numerics library (added in c++20), if there is
anything more in the standard let me know.
What I would want to propose is for an extension, you could say a
generalization, of this std::bitset class to arbitrary dimensions. Namely
what I have called a bittensor. The idea would be to use the already
existing std::bitset for storing the data and defining over it an easy to
use interface for dealing with n-dimensional binary tensors, instead of
relying in manual bit fiddling (that one should also be able to use) one
could transform this tensors in a higher-level way, with the same
performance and size (this would be the ideal).

Here I put a very simple sketch of what I think it should look like:

___________________________________________________________________________

template<std::size_t... N_i>
struct bittensor
{
    std::bitset< (... * N_i) > _data;
    constexpr static std::size_t _dims[] = {N_i ... };
    constexpr bittensor () noexcept {};
    constexpr bittensor (const std::bitset< (... * N_i) >& _val) noexcept :
_data(_val) {};
    constexpr bittensor (const bittensor< N_i ... >& bt) noexcept :
_data(bt._data) {};
    template<typename T>
    constexpr bittensor (const T& _val) noexcept
    {_data = std::bitset< (... * N_i) > (_val);}
    //TODO -> specify types of constructor as in bitset or just
    // remain with this general templated constructor that
    // seems to work as expected.

    //Now come the operators defined on the class bittensor.
    //Mainly this is a wrapper that just sends the job
    //to the 1d version bitset that we are using as _data.
    constexpr inline bittensor& operator&= (const bittensor& rhs) noexcept
{ _data &= rhs._data; return *this;};
    constexpr inline bittensor& operator|= (const bittensor& rhs) noexcept
{ _data |= rhs._data; return *this;};
    constexpr inline bittensor& operator^= (const bittensor& rhs) noexcept
{ _data ^= rhs._data; return *this;};
    constexpr inline bittensor& operator<<= (std::size_t pos) noexcept {
_data <<= pos; return *this;};
    constexpr inline bittensor& operator>>= (std::size_t pos) noexcept {
_data >>= pos; return *this;};
    constexpr inline bittensor operator~() const noexcept { return
bittensor< N_i ... > (~_data);};
    constexpr inline bittensor operator<< (std::size_t pos) const noexcept
{ return bittensor< N_i ... > (_data << pos);};
    constexpr inline bittensor operator>> (std::size_t pos) const noexcept
{ return bittensor< N_i ... > (_data >> pos);};
    constexpr inline bool operator== (const bittensor& rhs) const noexcept
{ return _data == rhs._data;};
    constexpr inline bool operator!= (const bittensor& rhs) const noexcept
{ return _data != rhs._data;};

    //Bit access, mainly the ones given by the bitset class
    constexpr inline bool operator[] (std::size_t pos) const noexcept {
return _data[pos];};
    constexpr inline std::size_t count () const noexcept { return
_data.count();};
    constexpr inline std::size_t size () const noexcept { return
_data.size();};
    constexpr inline bool test (std::size_t pos) const { return
_data.test(pos);};
    constexpr inline bool any () const noexcept { return _data.any();};
    constexpr inline bool none () const noexcept { return _data.none();};
    constexpr inline bool all () const noexcept { return _data.all();};

    //Bit operations, mainly the ones given by the bitset class
    constexpr inline bittensor& set () noexcept { _data.set(); return
*this;};
    constexpr inline bittensor& set (std::size_t pos, bool val = true) {
_data.set(pos, val); return *this;};
    constexpr inline bittensor& reset () noexcept { _data.reset(); return
*this;};
    constexpr inline bittensor& reset (std::size_t pos) { _data.reset(pos);
return *this;};
    constexpr inline bittensor& flip () noexcept { _data.flip(); return
*this;};
    constexpr inline bittensor& flip (std::size_t pos) { _data.flip(pos);
return *this;};

    //Type conversions, mainly the ones given by the bitset class
    constexpr inline unsigned long to_ulong () const { return
_data.to_ulong;};
    constexpr inline unsigned long long to_ullong () const { return
_data.to_ullong;};
};
//non-member functions
template<std::size_t... N_i>
bittensor< N_i ... > operator& (const bittensor< N_i ... >& lhs, const
bittensor< N_i ... >& rhs) noexcept { return bittensor< N_i ... >
(lhs._data & rhs._data);}
template<std::size_t... N_i >
bittensor< N_i ... > operator| (const bittensor< N_i ... >& lhs, const
bittensor< N_i ... >& rhs) noexcept { return bittensor< N_i ... >
(lhs._data | rhs._data);}
template<std::size_t... N_i >
bittensor< N_i ... > operator^ (const bittensor< N_i ... >& lhs, const
bittensor< N_i ... >& rhs) noexcept { return bittensor< N_i ... >
(lhs._data ^ rhs._data);}
//And now come the iostream inserters and extractors for printing _data
(for the moment) to a certain stream
template<class charT, class traits, std::size_t... N_i>
std::basic_istream<charT, traits>& operator>> (std::basic_istream<charT,
traits>& is, bittensor< N_i ... >& rhs) { is >> rhs._data; return is;}
template<class charT, class traits, std::size_t... N_i>
std::basic_ostream<charT, traits>& operator<< (std::basic_ostream<charT,
traits>& os, const bittensor< N_i ... >& rhs) { os << rhs._data; return os;}

//Add hashing support (based on the one given by bitset)
namespace std
{
template<std::size_t... N_i>
struct hash<bittensor< N_i ... >>
{
    hash () noexcept {};
    size_t operator() (const bittensor< N_i ... >& bt) const noexcept
    { return hash< std::bitset< (... * N_i) > > ()(bt._data);}
};
template<>
struct hash<bittensor<0>>
{
    hash () noexcept {};
    size_t operator() (const bittensor<0>& bt) const noexcept {return
static_cast<size_t> (bt[0]);}
};
}

___________________________________________________________________________

This is far from something really useful but I thought it may help to
introduce more explicitly the idea I have in mind. I hope there are not
much mistakes in the code (which compiles well in g++-10 as a .h file) and
to receive your thoughts about this extension of the std::bitset. I think
that it c++ wants to incorporate more math libraries into its standard (as
it seems to be doing) incorporating something like this is a must (look at
the numpy arrays for the python case for instance). And something that we
may further extend by templating on the 1d data structure (here
std::bitset) to an arbitrary vector-space-like structure, but this seems to
be a really harder project.

Regards,

Pablo Ruiz.



STD-PROPOSALS list run by std-proposals-owner@lists.isocpp.org

Standard Proposals Archives on Google Groups