TESSEorg / ttg

TTG: Template Task Graph C++ API
18 stars 12 forks source link

Vector inputs to a task #124

Open josephjohnjj opened 3 years ago

josephjohnjj commented 3 years ago

I have a task that takes a std::vector as input

auto make_test(ttg::Edge<Key, std::vector<Octant_Aggregator_Data>>& testEdge) 
{
  auto f = [=](const Key& key, std::vector<Octant_Aggregator_Data>& testData, std::tuple<>& out)
            {
              printf("==========TEST========  \n"); 
            };

  return ttg::wrap<Key>(f, ttg::edges(testEdge), ttg::edges(), "test", {"testEdge"}, {});
} 

The class Octant_Aggregator_Data is as follows

struct Octant_Aggregator_Data
{
  Key octant_key;
  Key parent_key;
  int next_level;

  Octant_Aggregator_Data() = default;
  Octant_Aggregator_Data(Key key1, Key key2, int level) : 
    octant_key(key1), parent_key(key2), next_level(level){}

  bool operator==(const Octant_Aggregator_Data& b) const 
      { return octant_key == b.octant_key && parent_key == b.parent_key && next_level == b.next_level; }

  bool operator!=(const Octant_Aggregator_Data& b) const { return !((*this) == b); }

  Key get_octant_key() const { return octant_key; }
  Key get_parent_key() const { return parent_key; }
  int get_next_level() const { return next_level; }

  void set_octant_key(Key key)  { octant_key = key; }
  void set_parent_key(Key key)  { parent_key = key; }
  void set_next_level(int value) { next_level = value; }

  //template <typename Archive>
  //void serialize(Archive& ar) {
  //  ar& madness::archive::wrap((unsigned char*)this, sizeof(*this));
  //}

};

I followed ttg/examples/randomaccess/randomaccess.cc to serialise the object of this class:

namespace madness {
  namespace archive {
    template <class Archive>
      struct ArchiveStoreImpl<Archive, Octant_Aggregator_Data> {
        static inline void store(const Archive& ar, const Octant_Aggregator_Data& d) {
          ar << d.get_octant_key() << d.get_parent_key() << d.get_next_level();
          ar << wrap(&d, sizeof(Octant_Aggregator_Data));
        }
      };

    template <class Archive>
      struct ArchiveLoadImpl<Archive, Octant_Aggregator_Data> {
        static inline void load(const Archive& ar, Octant_Aggregator_Data& d) {
          Key octant_key;
          Key parent_key;
          int next_level;
          ar >> octant_key >> parent_key >> next_level;
          d = Octant_Aggregator_Data(octant_key, parent_key, next_level);
          ar >> wrap(&d, sizeof(Octant_Aggregator_Data));
        }
      };
  }
}

but I get the error

../examples/miniamr/miniamr.cc:565:88:   required from here
../ttg/ttg/parsec/ttg.h:546:21: error: incomplete type ‘ttg::default_data_descriptor<std::vector<Octant_Aggregator_Data>, void>’ used in nested name specifier
  546 |       if constexpr (!ttg::default_data_descriptor<ttg::meta::remove_cvr_t<T>>::serialize_size_is_const) {
      |                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../ttg/ttg/parsec/ttg.h: In instantiation of ‘uint64_t ttg_parsec::Op<keyT, output_terminalsT, derivedT, input_valueTs>::pack(T&, void*, uint64_t) [with T = const std::vector<Octant_Aggregator_Data>; keyT = Key; output_terminalsT = std::tuple<>; derivedT = ttg_parsec::WrapOpArgs<make_test(ttg::Edge<Key, std::vector<Octant_Aggregator_Data> >&)::<lambda(const Key&, std::vector<Octant_Aggregator_Data>&, std::tuple<>&)>&, Key, std::tuple<>, std::vector<Octant_Aggregator_Data, std::allocator<Octant_Aggregator_Data> > >; input_valueTs = {std::vector<Octant_Aggregator_Data, std::allocator<Octant_Aggregator_Data> >}; uint64_t = long unsigned int]’:

Are there any problems with the load() and store() function here?

devreal commented 3 years ago

@evaleev we don't support vectors of madness-serializable types? I though that was transitive

josephjohnjj commented 3 years ago

So there is no need for load() and store() functions?

evaleev commented 3 years ago

@josephjohnjj I can't reproduce the issue ... could you please show the complete example?

Here's what I can compile successfully:

#define WORLD_INSTANTIATE_STATIC_TEMPLATES
#define TTG_USE_MADNESS 1
#include "ttg.h"
#include <catch2/catch.hpp>

struct Key {
  int i[3];
  bool operator==(const Key& other) const {
    return i[0] == other.i[0] && i[1] == other.i[1] && i[2] == other.i[2];
  }
};

std::ostream& operator<<(std::ostream& os, const Key& key) {
  os << key.i[0] << " " << key.i[1] << " " << key.i[2];
  return os;
}

struct Octant_Aggregator_Data
{
  Key octant_key;
  Key parent_key;
  int next_level;

  Octant_Aggregator_Data() = default;
  Octant_Aggregator_Data(Key key1, Key key2, int level) :
      octant_key(key1), parent_key(key2), next_level(level){}

  bool operator==(const Octant_Aggregator_Data& b) const
  { return octant_key == b.octant_key && parent_key == b.parent_key && next_level == b.next_level; }

  bool operator!=(const Octant_Aggregator_Data& b) const { return !((*this) == b); }

  Key get_octant_key() const { return octant_key; }
  Key get_parent_key() const { return parent_key; }
  int get_next_level() const { return next_level; }

  void set_octant_key(Key key)  { octant_key = key; }
  void set_parent_key(Key key)  { parent_key = key; }
  void set_next_level(int value) { next_level = value; }

  //template <typename Archive>
  //void serialize(Archive& ar) {
  //  ar& madness::archive::wrap((unsigned char*)this, sizeof(*this));
  //}

};

namespace madness {
  namespace archive {
    template <class Archive>
    struct ArchiveStoreImpl<Archive, Octant_Aggregator_Data> {
      static inline void store(const Archive& ar, const Octant_Aggregator_Data& d) {
        ar << d.get_octant_key() << d.get_parent_key() << d.get_next_level();
        ar << wrap(&d, sizeof(Octant_Aggregator_Data));
      }
    };

    template <class Archive>
    struct ArchiveLoadImpl<Archive, Octant_Aggregator_Data> {
      static inline void load(const Archive& ar, Octant_Aggregator_Data& d) {
        Key octant_key;
        Key parent_key;
        int next_level;
        ar >> octant_key >> parent_key >> next_level;
        d = Octant_Aggregator_Data(octant_key, parent_key, next_level);
        ar >> wrap(&d, sizeof(Octant_Aggregator_Data));
      }
    };
  }
}

//static_assert(!ttg::default_data_descriptor<std::vector<Octant_Aggregator_Data>>::serialize_size_is_const);

auto make_test(ttg::Edge<Key, std::vector<Octant_Aggregator_Data>>& testEdge)
{
  auto f = [=](const Key& key, std::vector<Octant_Aggregator_Data>& testData, std::tuple<>& out)
            {
              printf("==========TEST========  \n");
            };

  return ttg::wrap<Key>(f, ttg::edges(testEdge), ttg::edges(), "test", {"testEdge"}, {});
}

TEST_CASE("issue 124", "[issues]") {
  ttg::Edge<Key, std::vector<Octant_Aggregator_Data>> edge;
  auto op = make_test(edge);
  op->invoke(Key{0,0,0}, std::vector<Octant_Aggregator_Data>{});
}
josephjohnjj commented 3 years ago
#include <array>
#include <cmath>
#include <cstdio>
#include "ttg.h"
using namespace ttg;
#include <madness/world/world.h>

int d = 6;

struct Key {
  // (x, y, z, l, ts) where (x, y, z) is the cartesian coordinate of the anchor
  //                    l is the level of the block in the octree
  //                    ts is the timestep

  int x = -1, y = -1, z = -1, l = -1, ts = -1;
  madness::hashT hash_val;

  Key() { morton_3d_rehash(); }
  Key(int x, int y, int z, int l, int ts) : x(x), y(y), z(z), l(l), ts(ts) 
  { 
    morton_3d_rehash(); 
  }

  madness::hashT hash() const { return hash_val; }

  inline int morton_3d_rehash()
  {
    int X = x, Y = y, Z = z;
    X = (X | (X << 16)) & 0x030000FF;
    X = (X | (X <<  8)) & 0x0300F00F;
    X = (X | (X <<  4)) & 0x030C30C3;
    X = (X | (X <<  2)) & 0x09249249;

    Y = (Y | (Y << 16)) & 0x030000FF;
    Y = (Y | (Y <<  8)) & 0x0300F00F;
    Y = (Y | (Y <<  4)) & 0x030C30C3;
    Y = (Y | (Y <<  2)) & 0x09249249;

    Z = (Z | (Z << 16)) & 0x030000FF;
    Z = (Z | (Z <<  8)) & 0x0300F00F;
    Z = (Z | (Z <<  4)) & 0x030C30C3;
    Z = (Z | (Z <<  2)) & 0x09249249;

    int morton = X | (Y << 1) | (Z << 2);
    hash_val = ( morton << (d-1) ) | l;
    return 1;
  }

  // Equality test
  bool operator==(const Key& b) const { return (x == b.x && y == b.y && z == b.z && l == b.l && ts == b.ts); }
  // Inequality test
  bool operator!=(const Key& b) const { return !((*this) == b); }

  //less than operator
  bool operator<(const Key& b) const 
  { 
    if( (hash_val + ts) < (b.hash_val + b.ts) )
      return true;
    else
      return false;

  }

  template <typename Archive>
  void serialize(Archive& ar) {
    ar& madness::archive::wrap((unsigned char*)this, sizeof(*this));
  }
};

struct Octant_Aggregator_Data
{
  Key octant_key;
  Key parent_key;
  int next_level;

  Octant_Aggregator_Data() = default;
  Octant_Aggregator_Data(Key key1, Key key2, int level) :
      octant_key(key1), parent_key(key2), next_level(level){}

  bool operator==(const Octant_Aggregator_Data& b) const
  { return octant_key == b.octant_key && parent_key == b.parent_key && next_level == b.next_level; }

  bool operator!=(const Octant_Aggregator_Data& b) const { return !((*this) == b); }

  Key get_octant_key() const { return octant_key; }
  Key get_parent_key() const { return parent_key; }
  int get_next_level() const { return next_level; }

  void set_octant_key(Key key)  { octant_key = key; }
  void set_parent_key(Key key)  { parent_key = key; }
  void set_next_level(int value) { next_level = value; }

  //template <typename Archive>
  //void serialize(Archive& ar) {
  //  ar& madness::archive::wrap((unsigned char*)this, sizeof(*this));
  //}

};

namespace madness {
  namespace archive {
    template <class Archive>
    struct ArchiveStoreImpl<Archive, Octant_Aggregator_Data> {
      static inline void store(const Archive& ar, const Octant_Aggregator_Data& d) {
        ar << d.get_octant_key() << d.get_parent_key() << d.get_next_level();
        ar << wrap(&d, sizeof(Octant_Aggregator_Data));
      }
    };

    template <class Archive>
    struct ArchiveLoadImpl<Archive, Octant_Aggregator_Data> {
      static inline void load(const Archive& ar, Octant_Aggregator_Data& d) {
        Key octant_key;
        Key parent_key;
        int next_level;
        ar >> octant_key >> parent_key >> next_level;
        d = Octant_Aggregator_Data(octant_key, parent_key, next_level);
        ar >> wrap(&d, sizeof(Octant_Aggregator_Data));
      }
    };
  }
}

auto make_test(ttg::Edge<Key, std::vector<Octant_Aggregator_Data>>& testEdge)
{
  auto f = [=](const Key& key, std::vector<Octant_Aggregator_Data>& testData, std::tuple<>& out)
            {
              printf("==========TEST========  \n");
            };

  return ttg::wrap<Key>(f, ttg::edges(testEdge), ttg::edges(), "test", {"testEdge"}, {});
}

int main(int argc, char** argv) 
{

  ttg::ttg_initialize(argc, argv, -1);
  auto world = ttg::ttg_default_execution_context();

  ttg::Edge<Key, std::vector<Octant_Aggregator_Data>> edge;
  auto op = make_test(edge);

  auto connected = make_graph_executable(op.get());
  assert(connected);
  TTGUNUSED(connected);
  std::cout << "Graph is connected: " << connected << std::endl;

  if (world.rank() == 0) 
  {
    op->invoke(Key{0, 0, 0, 0, 0}, std::vector<Octant_Aggregator_Data>{});
  }

  ttg::ttg_execute(world);
  ttg::ttg_fence(world);
  ttg::ttg_finalize();

  return 0;
}

compile.txt

therault commented 3 years ago

Joseph, can you try with a compiler that is more recent? Do you have clang 11 or 12?

evaleev commented 3 years ago

@josephjohnjj

if I add

std::ostream& operator<<(std::ostream& os, const Key& key) {
  os << "{" << key.x << " " << key.y << " " << key.z << " " << key.l << " " << key.ts << "}";
  return os;
}

then everything compiles on my Mac with clang (apple) and gcc 11. I suspect compiler bug.

josephjohnjj commented 3 years ago

I added the code suggested by @evaleev and compiled using clang, but I get a different set of errors. clang.txt

devreal commented 2 years ago

@josephjohnjj What is the status of this? Is it still failing with current master?