   bool operator == (const Apply1Hash& other) const
   {
     return op == other.op && type == other.type && Adim == other.Adim;
+  }
+};
+
+struct Apply2Hash {
+  std::string op;
+  std::string type;
+  int Adim, Bdim;
+
+  Apply2Hash(std::string op, std::string type, int Adim, int Bdim) : op(op), type(type), Adim(Adim), Bdim(Bdim) {}
+
+  bool operator == (const Apply2Hash& other) const
+  {
+    return op == other.op && type == other.type && Adim == other.Adim && Bdim == other.Bdim;
+  }
+};
+
+struct Apply3Hash {
+  std::string op;
+  std::string type;
+  int Adim, Bdim, Cdim;
+
+  Apply3Hash(std::string op, std::string type, int Adim, int Bdim, int Cdim) :
+    op(op), type(type), Adim(Adim), Bdim(Bdim), Cdim(Cdim) {}
+
+  bool operator == (const Apply3Hash& other) const
+  {
+    return op == other.op && type == other.type && Adim == other.Adim && Bdim == other.Bdim && Cdim == other.Cdim;
   }
 };
 


  PTXPtr ptx;
  Apply1Hash hash(op, type, A);
  auto found_hash = cache.find(hash);
  if(found_hash == cache.end())
  {
    ptx = PTXPtr(new PTX());
    compilePTX(src, headers, includeNames, *ptx);
    cache.emplace(hash, ptx);
  }
  else
    ptx = found_hash->second;

  const char *headers[] = {apply_header};
  const char *includeNames[] = {"header.h"};

  PTXPtr ptx;
  Apply2Hash hash(op, type, A, B);
  auto found_hash = apply2cache.find(hash);
  if(found_hash == apply2cache.end())
  {
    ptx = PTXPtr(new PTX());
    compilePTX(src, headers, includeNames, *ptx);
    apply2cache.emplace(hash, ptx);
  }
  else
    ptx = found_hash->second;

  void *args[] = {(void*)&aInfo, (void*)&bInfo, (void*)&totalElements};
  launch(ptx->data(), "kernel", args, grid, block, (CUstream)stream);
}


