/** * 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()); } }
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; } }