/**
 * 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_base_id() == expr_kind_id) {
    return get_leading_dim_count(tp.value_type());
  }
  else if (tp.get_id() == tuple_id || tp.get_id() == struct_id) {
    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;
  }
}
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 #3
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;
  }
}