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);
}
Exemplo n.º 2
0
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>());
}
/**
 * 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;
  }
}