void pydynd::array_no_dim_broadcast_assign_from_py(
    const dynd::ndt::type &dst_tp, const char *dst_arrmeta, char *dst_data,
    PyObject *value, const dynd::eval::eval_context *ectx)
{
  // TODO: This is a hack, need a proper way to pass this dst param
  dynd::nd::array tmp_dst(
      dynd::make_array_memory_block(dst_tp.get_arrmeta_size()));
  tmp_dst.get()->tp = dst_tp;
  tmp_dst.get()->flags =
      dynd::nd::read_access_flag | dynd::nd::write_access_flag;
  if (dst_tp.get_arrmeta_size() > 0) {
    dst_tp.extended()->arrmeta_copy_construct(
        tmp_dst.get()->metadata(), dst_arrmeta,
        dynd::intrusive_ptr<dynd::memory_block_data>());
  }
  tmp_dst.get()->data = dst_data;
  dynd::ndt::type src_tp = dynd::ndt::type::make<void>();
  const char *src_arrmeta = NULL;
  char *src_data = reinterpret_cast<char *>(&value);
  const char *kwd_names[1] = {"broadcast"};
  dynd::nd::array kwd_values[1] = {false};
  (*pydynd::nd::copy_from_pyobject.get().get())(
      tmp_dst.get_type(), tmp_dst.get()->metadata(), tmp_dst.data(), 1, &src_tp,
      &src_arrmeta, &src_data, 1, kwd_values,
      std::map<std::string, dynd::ndt::type>());

  tmp_dst.get()->tp = dynd::ndt::type();
}
Example #2
0
std::string pydynd::ndt_type_repr(const dynd::ndt::type& d)
{
  std::stringstream ss;
  if (d.is_builtin() && d.get_type_id() != dynd::complex_float32_type_id &&
      d.get_type_id() != dynd::complex_float64_type_id) {
    ss << "ndt." << d;
  } else {
    switch (d.get_type_id()) {
    case complex_float32_type_id:
      ss << "ndt.complex_float32";
      break;
    case complex_float64_type_id:
      ss << "ndt.complex_float64";
      break;
    case date_type_id:
      ss << "ndt.date";
      break;
    case time_type_id:
      if (d.tcast<time_type>()->get_timezone() == tz_abstract) {
        ss << "ndt.time";
      } else {
        print_generic_type_repr(ss, d);
      }
      break;
    case datetime_type_id:
      if (d.tcast<datetime_type>()->get_timezone() == tz_abstract) {
        ss << "ndt.datetime";
      } else if (d.tcast<datetime_type>()->get_timezone() == tz_utc) {
        ss << "ndt.datetimeutc";
      } else {
        print_generic_type_repr(ss, d);
      }
      break;
    case json_type_id:
      ss << "ndt.json";
      break;
    case bytes_type_id:
      if (d.tcast<bytes_type>()->get_target_alignment() == 1) {
        ss << "ndt.bytes";
      } else {
        print_generic_type_repr(ss, d);
      }
      break;
    case string_type_id:
      if (d.tcast<string_type>()->get_encoding() == string_encoding_utf_8) {
        ss << "ndt.string";
      } else {
        print_generic_type_repr(ss, d);
      }
      break;
    default:
      print_generic_type_repr(ss, d);
      break;
    }
  }
  return ss.str();
}
bool pydynd::broadcast_as_scalar(const dynd::ndt::type &tp, PyObject *obj)
{
  intptr_t obj_ndim = 0;
  // Estimate the number of dimensions in ``obj`` by repeatedly indexing
  // along zero
  pyobject_ownref v(obj);
  Py_INCREF(v);
  for (;;) {
    // Don't treat these types as sequences
    if (PyDict_Check(v)) {
      if (tp.get_dtype().get_kind() == struct_kind) {
        // If the object to assign to a dynd struct ends in a dict, apply
        // the dict as the struct's value
        return (tp.get_ndim() > obj_ndim);
      }
      break;
    }
    else if (PyUnicode_Check(v) || PyBytes_Check(v)) {
      break;
    }
    PyObject *iter = PyObject_GetIter(v);
    if (iter != NULL) {
      ++obj_ndim;
      if (iter == v.get()) {
        // This was already an iterator, don't do any broadcasting,
        // because we have no visibility into it.
        Py_DECREF(iter);
        return false;
      }
      else {
        pyobject_ownref iter_owner(iter);
        PyObject *item = PyIter_Next(iter);
        if (item == NULL) {
          if (PyErr_ExceptionMatches(PyExc_StopIteration)) {
            PyErr_Clear();
            break;
          }
          else {
            throw exception();
          }
        }
        else {
          v.reset(item);
        }
      }
    }
    else {
      PyErr_Clear();
      break;
    }
  }

  return (get_leading_dim_count(tp) > obj_ndim);
}
void pydynd::nd::array_copy_from_numpy(const dynd::ndt::type &dst_tp,
                                       const char *dst_arrmeta, char *dst_data,
                                       PyArrayObject *src_arr,
                                       const dynd::eval::eval_context *ectx)
{
  intptr_t src_ndim = PyArray_NDIM(src_arr);

  strided_of_numpy_arrmeta src_am_holder;
  const char *src_am = reinterpret_cast<const char *>(
      &src_am_holder.sdt[NPY_MAXDIMS - src_ndim]);
  // Fill in metadata for a multi-dim strided array, corresponding
  // to the numpy array, with a void type at the end for the numpy
  // specific data.
  uintptr_t src_alignment = reinterpret_cast<uintptr_t>(PyArray_DATA(src_arr));
  for (intptr_t i = 0; i < src_ndim; ++i) {
    dynd::fixed_dim_type_arrmeta &am =
        src_am_holder.sdt[NPY_MAXDIMS - src_ndim + i];
    am.dim_size = PyArray_DIM(src_arr, (int)i);
    am.stride = am.dim_size != 1 ? PyArray_STRIDE(src_arr, (int)i) : 0;
    src_alignment |= static_cast<uintptr_t>(am.stride);
  }
  dynd::ndt::type src_tp = dynd::ndt::make_type(
      src_ndim, PyArray_SHAPE(src_arr), dynd::ndt::type::make<void>());
  src_am_holder.am.src_dtype = PyArray_DTYPE(src_arr);
  src_am_holder.am.src_alignment = src_alignment;

  // TODO: This is a hack, need a proper way to pass this dst param
  dynd::nd::array tmp_dst(
      dynd::make_array_memory_block(dst_tp.get_arrmeta_size()));
  tmp_dst.get()->tp = dst_tp;
  tmp_dst.get()->flags =
      dynd::nd::read_access_flag | dynd::nd::write_access_flag;
  if (dst_tp.get_arrmeta_size() > 0) {
    dst_tp.extended()->arrmeta_copy_construct(
        tmp_dst.get()->metadata(), dst_arrmeta,
        dynd::intrusive_ptr<dynd::memory_block_data>());
  }
  tmp_dst.get()->data = dst_data;
  char *src_data = reinterpret_cast<char *>(PyArray_DATA(src_arr));
  const char *kwd_names[1] = {"broadcast"};
  dynd::nd::array kwd_values[1] = {true};
  (*pydynd::nd::copy_from_numpy::get().get())(
      tmp_dst.get_type(), tmp_dst.get()->metadata(), tmp_dst.data(), 1, &src_tp,
      &src_am, &src_data, 1, kwd_values,
      std::map<std::string, dynd::ndt::type>());

  tmp_dst.get()->tp = dynd::ndt::type();
}
Example #5
0
inline ::testing::AssertionResult MatchNdtTypes(const char *expr1, const char *expr2, const dynd::ndt::type &pattern,
                                                const dynd::ndt::type &candidate)
{
  if (pattern.match(candidate)) {
    return ::testing::AssertionSuccess();
  }
  else {
    return ::testing::AssertionFailure() << "The type of candidate " << expr2 << " does not match pattern " << expr1
                                         << "\n" << expr1 << " has value " << pattern << ",\n" << expr2 << " has value "
                                         << candidate << ".";
  }
}
void pydynd::array_copy_to_numpy(PyArrayObject *dst_arr,
                                 const dynd::ndt::type &src_tp,
                                 const char *src_arrmeta, const char *src_data)
{
  intptr_t dst_ndim = PyArray_NDIM(dst_arr);
  intptr_t src_ndim = src_tp.get_ndim();
  uintptr_t dst_alignment = reinterpret_cast<uintptr_t>(PyArray_DATA(dst_arr));

  strided_of_numpy_arrmeta dst_am_holder;
  const char *dst_am = reinterpret_cast<const char *>(
      &dst_am_holder.sdt[NPY_MAXDIMS - dst_ndim]);
  // Fill in metadata for a multi-dim strided array, corresponding
  // to the numpy array, with a void type at the end for the numpy
  // specific data.
  for (intptr_t i = 0; i < dst_ndim; ++i) {
    dynd::fixed_dim_type_arrmeta &am =
        dst_am_holder.sdt[NPY_MAXDIMS - dst_ndim + i];
    am.stride = PyArray_STRIDE(dst_arr, (int)i);
    dst_alignment |= static_cast<uintptr_t>(am.stride);
    am.dim_size = PyArray_DIM(dst_arr, (int)i);
  }
  dynd::ndt::type dst_tp = dynd::ndt::make_type(
      dst_ndim, PyArray_SHAPE(dst_arr), dynd::ndt::make_type<void>());
  dst_am_holder.am.dst_dtype = PyArray_DTYPE(dst_arr);
  dst_am_holder.am.dst_alignment = dst_alignment;

  // TODO: This is a hack, need a proper way to pass this dst param
  intptr_t tmp_dst_arrmeta_size =
      dst_ndim * sizeof(dynd::fixed_dim_type_arrmeta) +
      sizeof(copy_to_numpy_arrmeta);
  dynd::nd::array tmp_dst(
      reinterpret_cast<dynd::array_preamble *>(
          dynd::make_array_memory_block(tmp_dst_arrmeta_size).get()),
      true);
  tmp_dst.get()->tp = dst_tp;
  tmp_dst.get()->flags =
      dynd::nd::read_access_flag | dynd::nd::write_access_flag;
  if (dst_tp.get_arrmeta_size() > 0) {
    memcpy(tmp_dst.get()->metadata(), dst_am, tmp_dst_arrmeta_size);
  }
  tmp_dst.get()->data = (char *)PyArray_DATA(dst_arr);
  char *src_data_nonconst = const_cast<char *>(src_data);
  copy_to_numpy::get()->call(tmp_dst.get_type(), tmp_dst.get()->metadata(),
                             tmp_dst.data(), 1, &src_tp, &src_arrmeta,
                             &src_data_nonconst, 1, NULL,
                             std::map<std::string, dynd::ndt::type>());
}
Example #7
0
dynd::ndt::type pydynd::ndt_type_getitem(const dynd::ndt::type& d, PyObject *subscript)
{
    // Convert the pyobject into an array of iranges
    intptr_t size;
    shortvector<irange> indices;
    if (!PyTuple_Check(subscript)) {
        // A single subscript
        size = 1;
        indices.init(1);
        indices[0] = pyobject_as_irange(subscript);
    } else {
        size = PyTuple_GET_SIZE(subscript);
        // Tuple of subscripts
        indices.init(size);
        for (Py_ssize_t i = 0; i < size; ++i) {
            indices[i] = pyobject_as_irange(PyTuple_GET_ITEM(subscript, i));
        }
    }

    // Do an indexing operation
    return d.at_array((int)size, indices.get());
}
/**
 * Gets the number of dimensions at index 0, including tuple
 * and struct as dimensions.
 */
static intptr_t get_leading_dim_count(const dynd::ndt::type &tp)
{
  intptr_t ndim = tp.get_ndim();
  if (ndim) {
    return ndim + get_leading_dim_count(tp.get_dtype());
  }
  else if (tp.get_kind() == expr_kind) {
    return get_leading_dim_count(tp.value_type());
  }
  else if (tp.get_kind() == tuple_kind || tp.get_kind() == struct_kind) {
    if (tp.extended<ndt::tuple_type>()->get_field_count() == 0) {
      return 1;
    }
    else {
      return 1 + get_leading_dim_count(
                     tp.extended<ndt::tuple_type>()->get_field_type(0));
    }
  }
  else {
    return 0;
  }
}
/**
 * This sets up a ckernel to copy from a dynd array
 * to a numpy array. The destination numpy array is
 * represented by dst_tp being ``void`` and the dst_arrmeta
 * being a pointer to the ``PyArray_Descr *`` of the type for the destination.
 */
intptr_t pydynd::copy_to_numpy_ck::instantiate(
    char *DYND_UNUSED(static_data), char *DYND_UNUSED(data), void *ckb,
    intptr_t ckb_offset, const dynd::ndt::type &dst_tp, const char *dst_arrmeta,
    intptr_t nsrc, const dynd::ndt::type *src_tp,
    const char *const *src_arrmeta, dynd::kernel_request_t kernreq,
    intptr_t nkwd, const dynd::nd::array *kwds,
    const std::map<std::string, dynd::ndt::type> &tp_vars)
{
  if (dst_tp.get_type_id() != dynd::void_type_id) {
    stringstream ss;
    ss << "Cannot instantiate dynd::nd::callable with signature (";
    ss << src_tp[0] << ") -> " << dst_tp;
    throw dynd::type_error(ss.str());
  }

  PyObject *dst_obj = *reinterpret_cast<PyObject *const *>(dst_arrmeta);
  uintptr_t dst_alignment = reinterpret_cast<const uintptr_t *>(dst_arrmeta)[1];

  PyArray_Descr *dtype = reinterpret_cast<PyArray_Descr *>(dst_obj);
  if (!PyDataType_FLAGCHK(dtype, NPY_ITEM_HASOBJECT)) {
    // If there is no object type in the numpy type, get the dynd equivalent
    // type and use it to do the copying
    dynd::ndt::type dst_view_tp = _type_from_numpy_dtype(dtype, dst_alignment);
    return dynd::make_assignment_kernel(ckb, ckb_offset, dst_view_tp, NULL,
                                        src_tp[0], src_arrmeta[0], kernreq,
                                        &dynd::eval::default_eval_context);
  }
  else if (PyDataType_ISOBJECT(dtype)) {
    dynd::nd::base_callable *af = const_cast<dynd::nd::base_callable *>(
        nd::copy_to_pyobject::get().get());
    return af->instantiate(af->static_data(), NULL, ckb, ckb_offset,
                           dynd::ndt::make_type<void>(), NULL, nsrc, src_tp,
                           src_arrmeta, kernreq, 0, NULL, tp_vars);
  }
  else if (PyDataType_HASFIELDS(dtype)) {
    if (src_tp[0].get_kind() != dynd::struct_kind &&
        src_tp[0].get_kind() != dynd::tuple_kind) {
      stringstream ss;
      pyobject_ownref dtype_str(PyObject_Str((PyObject *)dtype));
      ss << "Cannot assign from source dynd type " << src_tp[0]
         << " to numpy type " << pydynd::pystring_as_string(dtype_str.get());
      throw invalid_argument(ss.str());
    }

    // Get the fields out of the numpy dtype
    vector<PyArray_Descr *> field_dtypes_orig;
    vector<string> field_names_orig;
    vector<size_t> field_offsets_orig;
    pydynd::extract_fields_from_numpy_struct(
        dtype, field_dtypes_orig, field_names_orig, field_offsets_orig);
    intptr_t field_count = field_dtypes_orig.size();
    if (field_count !=
        src_tp[0].extended<dynd::ndt::tuple_type>()->get_field_count()) {
      stringstream ss;
      pyobject_ownref dtype_str(PyObject_Str((PyObject *)dtype));
      ss << "Cannot assign from source dynd type " << src_tp[0]
         << " to numpy type " << pydynd::pystring_as_string(dtype_str.get());
      throw invalid_argument(ss.str());
    }

    // Permute the numpy fields to match with the dynd fields
    vector<PyArray_Descr *> field_dtypes;
    vector<size_t> field_offsets;
    if (src_tp[0].get_kind() == dynd::struct_kind) {
      field_dtypes.resize(field_count);
      field_offsets.resize(field_count);
      for (intptr_t i = 0; i < field_count; ++i) {
        intptr_t src_i =
            src_tp[0].extended<dynd::ndt::struct_type>()->get_field_index(
                field_names_orig[i]);
        if (src_i >= 0) {
          field_dtypes[src_i] = field_dtypes_orig[i];
          field_offsets[src_i] = field_offsets_orig[i];
        }
        else {
          stringstream ss;
          pyobject_ownref dtype_str(PyObject_Str((PyObject *)dtype));
          ss << "Cannot assign from source dynd type " << src_tp[0]
             << " to numpy type "
             << pydynd::pystring_as_string(dtype_str.get());
          throw invalid_argument(ss.str());
        }
      }
    }
    else {
      // In the tuple case, use position instead of name
      field_dtypes.swap(field_dtypes_orig);
      field_offsets.swap(field_offsets_orig);
    }

    vector<dynd::ndt::type> dst_fields_tp(field_count,
                                          dynd::ndt::make_type<void>());
    vector<copy_to_numpy_arrmeta> dst_arrmeta_values(field_count);
    vector<const char *> dst_fields_arrmeta(field_count);
    for (intptr_t i = 0; i < field_count; ++i) {
      dst_arrmeta_values[i].dst_dtype = field_dtypes[i];
      dst_arrmeta_values[i].dst_alignment = dst_alignment | field_offsets[i];
      dst_fields_arrmeta[i] =
          reinterpret_cast<const char *>(&dst_arrmeta_values[i]);
    }

    const uintptr_t *src_arrmeta_offsets =
        src_tp[0].extended<dynd::ndt::tuple_type>()->get_arrmeta_offsets_raw();
    dynd::shortvector<const char *> src_fields_arrmeta(field_count);
    for (intptr_t i = 0; i != field_count; ++i) {
      src_fields_arrmeta[i] = src_arrmeta[0] + src_arrmeta_offsets[i];
    }

    // Todo: Remove this
    dynd::nd::callable af = dynd::nd::callable::make<copy_to_numpy_ck>(
        dynd::ndt::type("(Any) -> void"), 0);

    return make_tuple_unary_op_ckernel(
        af.get(), af.get_type(), ckb, ckb_offset, field_count,
        &field_offsets[0], &dst_fields_tp[0], &dst_fields_arrmeta[0],
        src_tp[0].extended<dynd::ndt::tuple_type>()->get_data_offsets(
            src_arrmeta[0]),
        src_tp[0].extended<dynd::ndt::tuple_type>()->get_field_types_raw(),
        src_fields_arrmeta.get(), kernreq);
  }
  else {
    stringstream ss;
    ss << "TODO: implement assign from source dynd type " << src_tp[0]
       << " to numpy type " << pyobject_repr((PyObject *)dtype);
    throw invalid_argument(ss.str());
  }
}
void pydynd::nd::copy_from_numpy_callable::instantiate(char *DYND_UNUSED(data), dynd::nd::kernel_builder *ckb,
                                                       const dynd::ndt::type &dst_tp, const char *dst_arrmeta,
                                                       intptr_t DYND_UNUSED(nsrc), const dynd::ndt::type *src_tp,
                                                       const char *const *src_arrmeta, dynd::kernel_request_t kernreq,
                                                       intptr_t nkwd, const dynd::nd::array *kwds,
                                                       const std::map<std::string, dynd::ndt::type> &tp_vars)
{
  if (src_tp[0].get_id() != dynd::void_id) {
    stringstream ss;
    ss << "Cannot instantiate dynd::nd::callable copy_from_numpy with "
          "signature (";
    ss << src_tp[0] << ") -> " << dst_tp;
    throw dynd::type_error(ss.str());
  }

  PyArray_Descr *dtype = *reinterpret_cast<PyArray_Descr *const *>(src_arrmeta[0]);
  uintptr_t src_alignment = reinterpret_cast<const uintptr_t *>(src_arrmeta[0])[1];

  if (!PyDataType_FLAGCHK(dtype, NPY_ITEM_HASOBJECT)) {
    // If there is no object type in the numpy type, get the dynd equivalent
    // type and use it to do the copying
    dynd::ndt::type src_view_tp = _type_from_numpy_dtype(dtype, src_alignment);
    dynd::nd::array error_mode = dynd::assign_error_fractional;
    dynd::nd::assign->instantiate(NULL, ckb, dst_tp, dst_arrmeta, 1, &src_view_tp, NULL, kernreq, 1, &error_mode,
                                  std::map<std::string, dynd::ndt::type>());

    return;
  }
  else if (PyDataType_ISOBJECT(dtype)) {
    dynd::nd::base_callable *af = dynd::nd::assign.get();
    dynd::ndt::type child_src_tp = dynd::ndt::make_type<pyobject_type>();
    af->instantiate(NULL, ckb, dst_tp, dst_arrmeta, 1, &child_src_tp, NULL, kernreq, nkwd, kwds, tp_vars);
    return;
  }
  else if (PyDataType_HASFIELDS(dtype)) {
    if (dst_tp.get_id() != dynd::struct_id && dst_tp.get_id() != dynd::tuple_id) {
      stringstream ss;
      ss << "Cannot assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp;
      throw invalid_argument(ss.str());
    }

    // Get the fields out of the numpy dtype
    vector<PyArray_Descr *> field_dtypes_orig;
    vector<std::string> field_names_orig;
    vector<size_t> field_offsets_orig;
    pydynd::extract_fields_from_numpy_struct(dtype, field_dtypes_orig, field_names_orig, field_offsets_orig);
    intptr_t field_count = field_dtypes_orig.size();
    if (field_count != dst_tp.extended<dynd::ndt::tuple_type>()->get_field_count()) {
      stringstream ss;
      ss << "Cannot assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp;
      throw invalid_argument(ss.str());
    }

    // Permute the numpy fields to match with the dynd fields
    vector<PyArray_Descr *> field_dtypes;
    vector<size_t> field_offsets;
    if (dst_tp.get_id() == dynd::struct_id) {
      field_dtypes.resize(field_count);
      field_offsets.resize(field_count);
      for (intptr_t i = 0; i < field_count; ++i) {
        intptr_t src_i = dst_tp.extended<dynd::ndt::struct_type>()->get_field_index(field_names_orig[i]);
        if (src_i >= 0) {
          field_dtypes[src_i] = field_dtypes_orig[i];
          field_offsets[src_i] = field_offsets_orig[i];
        }
        else {
          stringstream ss;
          ss << "Cannot assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp;
          throw invalid_argument(ss.str());
        }
      }
    }
    else {
      // In the tuple case, use position instead of name
      field_dtypes.swap(field_dtypes_orig);
      field_offsets.swap(field_offsets_orig);
    }

    vector<dynd::ndt::type> src_fields_tp(field_count, dynd::ndt::make_type<void>());
    vector<copy_from_numpy_arrmeta> src_arrmeta_values(field_count);
    vector<const char *> src_fields_arrmeta(field_count);
    for (intptr_t i = 0; i < field_count; ++i) {
      src_arrmeta_values[i].src_dtype = field_dtypes[i];
      src_arrmeta_values[i].src_alignment = src_alignment | field_offsets[i];
      src_fields_arrmeta[i] = reinterpret_cast<const char *>(&src_arrmeta_values[i]);
    }

    const uintptr_t *dst_arrmeta_offsets = dst_tp.extended<dynd::ndt::tuple_type>()->get_arrmeta_offsets_raw();
    dynd::shortvector<const char *> dst_fields_arrmeta(field_count);
    for (intptr_t i = 0; i != field_count; ++i) {
      dst_fields_arrmeta[i] = dst_arrmeta + dst_arrmeta_offsets[i];
    }

    // Todo: Remove this line
    dynd::nd::callable af = dynd::nd::make_callable<copy_from_numpy_callable>();

    make_tuple_unary_op_ckernel(af.get(), af.get_type(), ckb, field_count,
                                dst_tp.extended<dynd::ndt::tuple_type>()->get_data_offsets(dst_arrmeta),
                                dst_tp.extended<dynd::ndt::tuple_type>()->get_field_types_raw(),
                                dst_fields_arrmeta.get(), &field_offsets[0], &src_fields_tp[0], &src_fields_arrmeta[0],
                                kernreq);
    return;
  }
  else {
    stringstream ss;
    ss << "TODO: implement assign from numpy type " << pyobject_repr((PyObject *)dtype) << " to dynd type " << dst_tp;
    throw invalid_argument(ss.str());
  }
}
Example #11
0
void pydynd::fill_arrmeta_from_numpy_dtype(const dynd::ndt::type &dt,
                                           PyArray_Descr *d, char *arrmeta)
{
  switch (dt.get_id()) {
  case dynd::struct_id: {
    // In DyND, the struct offsets are part of the arrmeta instead of the dtype.
    // That's why we have to populate them here.
    PyObject *d_names = d->names;
    const dynd::ndt::struct_type *sdt = dt.extended<dynd::ndt::struct_type>();
    const uintptr_t *arrmeta_offsets = sdt->get_arrmeta_offsets_raw();
    size_t field_count = sdt->get_field_count();
    uintptr_t *offsets = reinterpret_cast<size_t *>(arrmeta);
    for (size_t i = 0; i < field_count; ++i) {
      PyObject *tup = PyDict_GetItem(d->fields, PyTuple_GET_ITEM(d_names, i));
      PyArray_Descr *fld_dtype;
      PyObject *title;
      int offset = 0;
      if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &offset, &title)) {
        throw dynd::type_error("Numpy struct dtype has corrupt data");
      }
      // Set the field offset in the output arrmeta
      offsets[i] = offset;
      // Fill the arrmeta for the field, if necessary
      const dynd::ndt::type &ft = sdt->get_field_type(i);
      if (!ft.is_builtin()) {
        fill_arrmeta_from_numpy_dtype(ft, fld_dtype,
                                      arrmeta + arrmeta_offsets[i]);
      }
    }
    break;
  }
  case dynd::fixed_dim_id: {
    // The Numpy subarray becomes a series of fixed_dim types, so we
    // need to copy the strides into the arrmeta.
    dynd::ndt::type el;
    PyArray_ArrayDescr *adescr = d->subarray;
    if (adescr == NULL) {
      stringstream ss;
      ss << "Internal error building dynd arrmeta: Numpy dtype has "
            "NULL subarray corresponding to strided_dim type";
      throw dynd::type_error(ss.str());
    }
    if (PyTuple_Check(adescr->shape)) {
      int ndim = (int)PyTuple_GET_SIZE(adescr->shape);
      dynd::fixed_dim_type_arrmeta *md =
          reinterpret_cast<dynd::fixed_dim_type_arrmeta *>(arrmeta);
      intptr_t stride = adescr->base->elsize;
      el = dt;
      for (int i = ndim - 1; i >= 0; --i) {
        md[i].dim_size =
            pydynd::pyobject_as_index(PyTuple_GET_ITEM(adescr->shape, i));
        md[i].stride = stride;
        stride *= md[i].dim_size;
        el = el.extended<dynd::ndt::fixed_dim_type>()->get_element_type();
      }
      arrmeta += ndim * sizeof(dynd::fixed_dim_type_arrmeta);
    }
    else {
      dynd::fixed_dim_type_arrmeta *md =
          reinterpret_cast<dynd::fixed_dim_type_arrmeta *>(arrmeta);
      arrmeta += sizeof(dynd::fixed_dim_type_arrmeta);
      md->dim_size = pydynd::pyobject_as_index(adescr->shape);
      md->stride = adescr->base->elsize;
      el = dt.extended<dynd::ndt::fixed_dim_type>()->get_element_type();
    }
    // Fill the arrmeta for the array element, if necessary
    if (!el.is_builtin()) {
      fill_arrmeta_from_numpy_dtype(el, adescr->base, arrmeta);
    }
    break;
  }
  default:
    break;
  }
}