Exemplo n.º 1
0
static void as_numpy_analysis(pyobject_ownref *out_numpy_dtype,
                              bool *out_requires_copy, intptr_t ndim,
                              const ndt::type &dt, const char *arrmeta)
{
  if (dt.is_builtin()) {
    // DyND builtin types
    out_numpy_dtype->reset((PyObject *)PyArray_DescrFromType(
        dynd_to_numpy_type_id[dt.get_type_id()]));
    return;
  } else if (dt.get_type_id() == view_type_id &&
             dt.operand_type().get_type_id() == fixed_bytes_type_id) {
    // View operation for alignment
    as_numpy_analysis(out_numpy_dtype, out_requires_copy, ndim, dt.value_type(),
                      NULL);
    return;
  }

  switch (dt.get_type_id()) {
  case fixed_string_type_id: {
    const ndt::fixed_string_type *fsd = dt.extended<ndt::fixed_string_type>();
    PyArray_Descr *result;
    switch (fsd->get_encoding()) {
    case string_encoding_ascii:
      result = PyArray_DescrNewFromType(NPY_STRING);
      result->elsize = (int)fsd->get_data_size();
      out_numpy_dtype->reset((PyObject *)result);
      return;
    case string_encoding_utf_32:
      result = PyArray_DescrNewFromType(NPY_UNICODE);
      result->elsize = (int)fsd->get_data_size();
      out_numpy_dtype->reset((PyObject *)result);
      return;
    default:
      out_numpy_dtype->clear();
      *out_requires_copy = true;
      return;
    }
    break;
  }
  case string_type_id: {
    // Convert to numpy object type, requires copy
    out_numpy_dtype->clear();
    *out_requires_copy = true;
    return;
  }
  case date_type_id: {
#if NPY_API_VERSION >= 6 // At least NumPy 1.6
    out_numpy_dtype->clear();
    *out_requires_copy = true;
    return;
#else
    throw runtime_error("NumPy >= 1.6 is required for dynd date type interop");
#endif
  }
  case datetime_type_id: {
#if NPY_API_VERSION >= 6 // At least NumPy 1.6
    out_numpy_dtype->clear();
    *out_requires_copy = true;
    return;
#else
    throw runtime_error("NumPy >= 1.6 is required for dynd date type interop");
#endif
  }
  case property_type_id: {
    const ndt::property_type *pd = dt.extended<ndt::property_type>();
    // Special-case of 'int64 as date' property type, which is binary
    // compatible with NumPy's "M8[D]"
    if (pd->is_reversed_property() &&
        pd->get_value_type().get_type_id() == date_type_id &&
        pd->get_operand_type().get_type_id() == int64_type_id) {
      PyArray_Descr *datedt = NULL;
#if PY_VERSION_HEX >= 0x03000000
      pyobject_ownref M8str(PyUnicode_FromString("M8[D]"));
#else
      pyobject_ownref M8str(PyString_FromString("M8[D]"));
#endif
      if (!PyArray_DescrConverter(M8str.get(), &datedt)) {
        throw dynd::type_error("Failed to create NumPy datetime64[D] dtype");
      }
      out_numpy_dtype->reset((PyObject *)datedt);
      return;
    }
    break;
  }
  case byteswap_type_id: {
    const ndt::base_expr_type *bed = dt.extended<ndt::base_expr_type>();
    // Analyze the unswapped version
    as_numpy_analysis(out_numpy_dtype, out_requires_copy, ndim,
                      bed->get_value_type(), arrmeta);
    pyobject_ownref swapdt(out_numpy_dtype->release());
    // Byteswap the numpy dtype
    out_numpy_dtype->reset((PyObject *)PyArray_DescrNewByteorder(
        (PyArray_Descr *)swapdt.get(), NPY_SWAP));
    return;
  }
  case fixed_dim_type_id: {
    const ndt::base_dim_type *bdt = dt.extended<ndt::base_dim_type>();
    if (ndim > 0) {
      // If this is one of the array dimensions, it simply
      // becomes one of the numpy _array dimensions
      as_numpy_analysis(out_numpy_dtype, out_requires_copy, ndim - 1,
                        bdt->get_element_type(),
                        arrmeta + sizeof(fixed_dim_type_arrmeta));
      return;
    } else {
      // If this isn't one of the array dimensions, it maps into
      // a numpy dtype with a shape
      out_numpy_dtype->clear();
      *out_requires_copy = true;
      return;
    }
    break;
  }
  /*
    case cfixed_dim_type_id: {
      const cfixed_dim_type *fad = dt.extended<cfixed_dim_type>();
      if (ndim > 0) {
        // If this is one of the array dimensions, it simply
        // becomes one of the numpy _array dimensions
        as_numpy_analysis(out_numpy_dtype, out_requires_copy, ndim - 1,
                          fad->get_element_type(),
                          arrmeta + sizeof(cfixed_dim_type_arrmeta));
        return;
      } else {
        // If this isn't one of the array dimensions, it maps into
        // a numpy dtype with a shape
        // Build up the shape of the array for NumPy
        pyobject_ownref shape(PyList_New(0));
        ndt::type element_tp = dt;
        while (ndim > 0) {
          size_t dim_size = 0;
          if (dt.get_type_id() == cfixed_dim_type_id) {
            const cfixed_dim_type *cfd = element_tp.extended<cfixed_dim_type>();
            element_tp = cfd->get_element_type();
            if (cfd->get_data_size() != element_tp.get_data_size() * dim_size) {
              // If it's not C-order, a copy is required
              out_numpy_dtype->clear();
              *out_requires_copy = true;
              return;
            }
          } else {
            stringstream ss;
            ss << "dynd as_numpy could not convert dynd type ";
            ss << dt;
            ss << " to a numpy dtype";
            throw dynd::type_error(ss.str());
          }
          --ndim;
          if (PyList_Append(shape.get(), PyLong_FromSize_t(dim_size)) < 0) {
            throw runtime_error("propagating python error");
          }
        }
        // Get the numpy dtype of the element
        pyobject_ownref child_numpy_dtype;
        as_numpy_analysis(&child_numpy_dtype, out_requires_copy, 0, element_tp,
                          arrmeta);
        if (*out_requires_copy) {
          // If the child required a copy, stop right away
          out_numpy_dtype->clear();
          return;
        }
        // Create the result numpy dtype
        pyobject_ownref tuple_obj(PyTuple_New(2));
        PyTuple_SET_ITEM(tuple_obj.get(), 0, child_numpy_dtype.release());
        PyTuple_SET_ITEM(tuple_obj.get(), 1, shape.release());

        PyArray_Descr *result = NULL;
        if (!PyArray_DescrConverter(tuple_obj, &result)) {
          throw dynd::type_error(
              "failed to convert dynd type into numpy subarray dtype");
        }
        // Put the final numpy dtype reference in the output
        out_numpy_dtype->reset((PyObject *)result);
        return;
      }
      break;
    }
  */
  case struct_type_id: {
    if (dt.get_type_id() == struct_type_id && arrmeta == NULL) {
      // If it's a struct type with no arrmeta, a copy is required
      out_numpy_dtype->clear();
      *out_requires_copy = true;
      return;
    }
    const ndt::base_struct_type *bs = dt.extended<ndt::base_struct_type>();
    const uintptr_t *offsets = bs->get_data_offsets(arrmeta);
    size_t field_count = bs->get_field_count();

    pyobject_ownref names_obj(PyList_New(field_count));
    for (size_t i = 0; i < field_count; ++i) {
      const dynd::string &fn = bs->get_field_name_raw(i);
#if PY_VERSION_HEX >= 0x03000000
      pyobject_ownref name_str(
          PyUnicode_FromStringAndSize(fn.begin(), fn.end() - fn.begin()));
#else
      pyobject_ownref name_str(
          PyString_FromStringAndSize(fn.begin(), fn.end() - fn.begin()));
#endif
      PyList_SET_ITEM(names_obj.get(), i, name_str.release());
    }

    pyobject_ownref formats_obj(PyList_New(field_count));
    for (size_t i = 0; i < field_count; ++i) {
      // Get the numpy dtype of the element
      pyobject_ownref field_numpy_dtype;
      as_numpy_analysis(&field_numpy_dtype, out_requires_copy, 0,
                        bs->get_field_type(i), arrmeta);
      if (*out_requires_copy) {
        // If the field required a copy, stop right away
        out_numpy_dtype->clear();
        return;
      }
      PyList_SET_ITEM(formats_obj.get(), i, field_numpy_dtype.release());
    }

    pyobject_ownref offsets_obj(PyList_New(field_count));
    for (size_t i = 0; i < field_count; ++i) {
      PyList_SET_ITEM((PyObject *)offsets_obj, i,
                      PyLong_FromSize_t(offsets[i]));
    }

    pyobject_ownref dict_obj(PyDict_New());
    PyDict_SetItemString(dict_obj, "names", names_obj);
    PyDict_SetItemString(dict_obj, "formats", formats_obj);
    PyDict_SetItemString(dict_obj, "offsets", offsets_obj);
    if (dt.get_data_size() > 0) {
      pyobject_ownref itemsize_obj(PyLong_FromSize_t(dt.get_data_size()));
      PyDict_SetItemString(dict_obj, "itemsize", itemsize_obj);
    }

    PyArray_Descr *result = NULL;
    if (!PyArray_DescrConverter(dict_obj, &result)) {
      stringstream ss;
      ss << "failed to convert dynd type " << dt
         << " into numpy dtype via dict";
      throw dynd::type_error(ss.str());
    }
    out_numpy_dtype->reset((PyObject *)result);
    return;
  }
  default: {
    break;
  }
  }

  if (dt.get_kind() == expr_kind) {
    // If none of the prior checks caught this expression,
    // a copy is required.
    out_numpy_dtype->clear();
    *out_requires_copy = true;
    return;
  }

  // Anything which fell through is an error
  stringstream ss;
  ss << "dynd as_numpy could not convert dynd type ";
  ss << dt;
  ss << " to a numpy dtype";
  throw dynd::type_error(ss.str());
}
Exemplo n.º 2
0
void dynd::make_ckernel_deferred_from_assignment(
                const ndt::type& dst_tp, const ndt::type& src_tp, const ndt::type& src_expr_tp,
                deferred_ckernel_funcproto_t funcproto,
                assign_error_mode errmode, ckernel_deferred& out_ckd,
                const dynd::eval::eval_context *ectx)
{
    if (src_tp.operand_type() != src_expr_tp.operand_type()) {
        stringstream ss;
        ss << "make_ckernel_deferred_from_assignment: src_tp " << src_tp;
        ss << " and src_expr_tp " << src_expr_tp;
        ss << " must have matching operand types";
        throw type_error(ss.str());
    }
    memset(&out_ckd, 0, sizeof(ckernel_deferred));
    if (funcproto == unary_operation_funcproto) {
        // Since a unary operation was requested, it's a straightforward unary assignment ckernel
        unary_assignment_ckernel_deferred_data *data = new unary_assignment_ckernel_deferred_data;
        out_ckd.data_ptr = data;
        out_ckd.free_func = &delete_unary_assignment_ckernel_deferred_data;
        data->data_types[0] = dst_tp;
        data->data_types[1] = src_tp;
        data->data_types[2] = src_expr_tp;
        data->errmode = errmode;
        data->ectx = *ectx;
        out_ckd.instantiate_func = &instantiate_unary_assignment_ckernel;
        out_ckd.ckernel_funcproto = unary_operation_funcproto;
        out_ckd.data_types_size = 2;
        out_ckd.data_dynd_types = data->data_types;
    } else if (funcproto == expr_operation_funcproto) {
        if (src_tp.get_type_id() == expr_type_id && (&src_tp == &src_expr_tp)) {
            const expr_type *etp = static_cast<const expr_type *>(src_tp.extended());
            const base_struct_type *operands_type = static_cast<const base_struct_type *>(etp->get_operand_type().extended());
            const ndt::type *operand_types = operands_type->get_field_types();
            // Expose the expr type's expression
            intptr_t nargs = operands_type->get_field_count();
            size_t sizeof_data_mem = sizeof(expr_ckernel_deferred_data) + sizeof(void *) * nargs;
            void *data_mem = malloc(sizeof_data_mem);
            memset(data_mem, 0, sizeof_data_mem);
            expr_ckernel_deferred_data *data = reinterpret_cast<expr_ckernel_deferred_data *>(data_mem);
            out_ckd.data_ptr = data;
            out_ckd.free_func = &delete_expr_ckernel_deferred_data;
            data->data_types_size = nargs + 1;
            ndt::type *data_types_arr = &data->data_types[0];
            data_types_arr[0] = dst_tp;
            for (intptr_t i = 0; i < nargs; ++i) {
                // Dereference the pointer type in each field
                const pointer_type *field_ptr_type = static_cast<const pointer_type *>(operand_types[i].extended());
                data_types_arr[i+1] = field_ptr_type->get_target_type();
            }
            data->expr_type = static_cast<const expr_type *>(ndt::type(etp, true).release());
            data->errmode = errmode;
            data->ectx = *ectx;
            out_ckd.instantiate_func = &instantiate_expr_ckernel;
            out_ckd.ckernel_funcproto = expr_operation_funcproto;
            out_ckd.data_types_size = nargs + 1;
            out_ckd.data_dynd_types = data->data_types;
        } else {
            // Adapt the assignment to an expr kernel
            unary_assignment_ckernel_deferred_data *data = new unary_assignment_ckernel_deferred_data;
            out_ckd.data_ptr = data;
            out_ckd.free_func = &delete_unary_assignment_ckernel_deferred_data;
            data->data_types[0] = dst_tp;
            data->data_types[1] = src_tp;
            data->data_types[2] = src_expr_tp;
            data->errmode = errmode;
            data->ectx = *ectx;
            out_ckd.instantiate_func = &instantiate_adapted_expr_assignment_ckernel;
            out_ckd.ckernel_funcproto = expr_operation_funcproto;
            out_ckd.data_types_size = 2;
            out_ckd.data_dynd_types = data->data_types;
        }
    } else {
        stringstream ss;
        ss << "unrecognized ckernel function prototype enum value " << funcproto;
        throw runtime_error(ss.str());
    }
}