adapt_type::adapt_type(const ndt::type &operand_type, const ndt::type &value_type, const nd::string &op) : base_expr_type( adapt_type_id, expr_kind, operand_type.get_data_size(), operand_type.get_data_alignment(), inherited_flags(value_type.get_flags(), operand_type.get_flags()), 0), m_value_type(value_type), m_operand_type(operand_type), m_op(op) { if (!value_type.is_builtin() && value_type.extended()->adapt_type(operand_type.value_type(), op, m_forward, m_reverse)) { } else if (!operand_type.value_type().is_builtin() && operand_type.value_type().extended()->reverse_adapt_type( value_type, op, m_forward, m_reverse)) { } else { stringstream ss; ss << "Cannot create type "; print_type(ss); throw type_error(ss.str()); } // If the operand is an expression, make a buffering arrfunc if (m_operand_type.get_kind() == expr_kind && !m_forward.is_null() && m_operand_type != m_forward.get_type()->get_arg_type(0)) { m_forward = make_chain_arrfunc( make_arrfunc_from_assignment(m_forward.get_type()->get_arg_type(0), m_operand_type, assign_error_default), m_forward, m_forward.get_type()->get_arg_type(0)); } }
ndt::type view_type::with_replaced_storage_type(const ndt::type& replacement_type) const { if (m_operand_type.get_kind() == expression_kind) { return ndt::type(new view_type(m_value_type, static_cast<const base_expression_type *>(m_operand_type.extended())->with_replaced_storage_type(replacement_type)), false); } else { if (m_operand_type != replacement_type.value_type()) { std::stringstream ss; ss << "Cannot chain types, because the view's storage type, " << m_operand_type; ss << ", does not match the replacement's value type, " << replacement_type.value_type(); throw std::runtime_error(ss.str()); } return ndt::type(new view_type(m_value_type, replacement_type), false); } }
view_type::view_type(const ndt::type& value_type, const ndt::type& operand_type) : base_expression_type(view_type_id, expression_kind, operand_type.get_data_size(), operand_type.get_data_alignment(), inherited_flags(value_type.get_flags(), operand_type.get_flags()), operand_type.get_metadata_size()), m_value_type(value_type), m_operand_type(operand_type) { if (value_type.get_data_size() != operand_type.value_type().get_data_size()) { std::stringstream ss; ss << "view_type: Cannot view " << operand_type.value_type() << " as " << value_type << " because they have different sizes"; throw std::runtime_error(ss.str()); } if (!value_type.is_pod()) { throw std::runtime_error("view_type: Only POD types are supported"); } }
byteswap_type::byteswap_type(const ndt::type& value_type, const ndt::type& operand_type) : base_expr_type(byteswap_type_id, expr_kind, operand_type.get_data_size(), operand_type.get_data_alignment(), type_flag_scalar, 0), m_value_type(value_type), m_operand_type(operand_type) { // Only a bytes type be the operand to the byteswap if (operand_type.value_type().get_type_id() != fixedbytes_type_id) { std::stringstream ss; ss << "byteswap_type: The operand to the type must have a value type of bytes, not " << operand_type.value_type(); throw dynd::type_error(ss.str()); } // Automatically realign if needed if (operand_type.value_type().get_data_alignment() < value_type.get_data_alignment()) { m_operand_type = ndt::make_view(operand_type, ndt::make_fixedbytes(operand_type.get_data_size(), value_type.get_data_alignment())); } }
/** * Makes a conversion type to convert from the operand_type to the value_type. * If the value_type has expression_kind, it chains operand_type.value_type() * into value_type.storage_type(). */ inline ndt::type make_convert(const ndt::type& value_type, const ndt::type& operand_type, assign_error_mode errmode = assign_error_default) { if (operand_type.value_type() != value_type) { if (value_type.get_kind() != expression_kind) { // Create a conversion type when the value kind is different return ndt::type(new convert_type(value_type, operand_type, errmode), false); } else if (value_type.storage_type() == operand_type.value_type()) { // No conversion required at the connection return static_cast<const base_expression_type *>( value_type.extended())->with_replaced_storage_type(operand_type); } else { // A conversion required at the connection return static_cast<const base_expression_type *>( value_type.extended())->with_replaced_storage_type( ndt::type(new convert_type( value_type.storage_type(), operand_type, errmode), false)); } } else { return operand_type; } }
ndt::type convert_type::with_replaced_storage_type(const ndt::type& replacement_type) const { if (m_operand_type.get_kind() == expr_kind) { return ndt::type( new convert_type( m_value_type, m_operand_type.tcast<base_expr_type>() ->with_replaced_storage_type(replacement_type)), false); } else { if (m_operand_type != replacement_type.value_type()) { std::stringstream ss; ss << "Cannot chain expression types, because the conversion's " "storage type, " << m_operand_type << ", does not match the replacement's value type, " << replacement_type.value_type(); throw std::runtime_error(ss.str()); } return ndt::type(new convert_type(m_value_type, replacement_type), false); } }
void nd::detail::check_arg(const ndt::callable_type *af_tp, intptr_t i, const ndt::type &actual_tp, const char *actual_arrmeta, std::map<nd::string, ndt::type> &tp_vars) { ndt::type expected_tp = af_tp->get_pos_type(i); if (!expected_tp.match(NULL, actual_tp.value_type(), actual_arrmeta, tp_vars)) { std::stringstream ss; ss << "positional argument " << i << " to callable does not match, "; ss << "expected " << expected_tp << ", received " << actual_tp; throw std::invalid_argument(ss.str()); } }
size_t dynd::make_expression_comparison_kernel( ckernel_builder *out, size_t offset_out, const ndt::type& src0_dt, const char *src0_metadata, const ndt::type& src1_dt, const char *src1_metadata, comparison_type_t comptype, const eval::eval_context *ectx) { size_t current_offset = offset_out + sizeof(buffered_kernel_extra); out->ensure_capacity(current_offset); buffered_kernel_extra *e = out->get_at<buffered_kernel_extra>(offset_out); e->base.set_function<binary_single_predicate_t>(&buffered_kernel_extra::kernel); e->base.destructor = &buffered_kernel_extra::destruct; // Initialize the information for buffering the operands if (src0_dt.get_kind() == expression_kind) { e->init_buffer(0, src0_dt.value_type()); e->buf[0].kernel_offset = current_offset - offset_out; current_offset = make_assignment_kernel(out, current_offset, src0_dt.value_type(), e->buf[0].metadata, src0_dt, src0_metadata, kernel_request_single, assign_error_none, ectx); // Have to re-retrieve 'e', because creating another kernel may invalidate it e = out->get_at<buffered_kernel_extra>(offset_out); } if (src1_dt.get_kind() == expression_kind) { e->init_buffer(1, src1_dt.value_type()); e->buf[1].kernel_offset = current_offset - offset_out; current_offset = make_assignment_kernel(out, current_offset, src1_dt.value_type(), e->buf[1].metadata, src1_dt, src1_metadata, kernel_request_single, assign_error_none, ectx); // Have to re-retrieve 'e', because creating another kernel may invalidate it e = out->get_at<buffered_kernel_extra>(offset_out); } // Allocate the data for the buffers if (e->buf[0].kernel_offset != 0) { current_offset = inc_to_alignment(current_offset, src0_dt.get_data_alignment()); e->buf[0].data_offset = current_offset - offset_out; current_offset += e->buf[0].data_size; } if (e->buf[1].kernel_offset != 0) { current_offset = inc_to_alignment(current_offset, src1_dt.get_data_alignment()); e->buf[1].data_offset = current_offset - offset_out; current_offset += e->buf[1].data_size; } out->ensure_capacity(current_offset); // Have to re-retrieve 'e', because allocating the buffer data may invalidate it e = out->get_at<buffered_kernel_extra>(offset_out); e->cmp_kernel_offset = current_offset - offset_out; return make_comparison_kernel(out, current_offset, src0_dt.value_type(), (e->buf[0].kernel_offset != 0) ? e->buf[0].metadata : src0_metadata, src1_dt.value_type(), (e->buf[1].kernel_offset != 0) ? e->buf[1].metadata : src1_metadata, comptype, ectx); }
size_t categorical_type::make_assignment_kernel( ckernel_builder *ckb, intptr_t ckb_offset, const ndt::type &dst_tp, const char *dst_arrmeta, const ndt::type &src_tp, const char *src_arrmeta, kernel_request_t kernreq, const eval::eval_context *ectx) const { if (this == dst_tp.extended()) { if (this == src_tp.extended()) { // When assigning identical types, just use a POD copy return make_pod_typed_data_assignment_kernel( ckb, ckb_offset, get_data_size(), get_data_alignment(), kernreq); } // try to assign from another categorical type if it can be mapped else if (src_tp.get_type_id() == categorical_type_id) { // out_kernel.specializations = // assign_from_commensurate_category_specializations; // TODO auxdata throw std::runtime_error( "assignment between different categorical types isn't supported yet"); } // assign from the same category value type else if (src_tp == m_category_tp) { ckb_offset = make_kernreq_to_single_kernel_adapter(ckb, ckb_offset, 1, kernreq); category_to_categorical_kernel_extra *e = ckb->alloc_ck_leaf<category_to_categorical_kernel_extra>(ckb_offset); switch (m_storage_type.get_type_id()) { case uint8_type_id: e->base.set_function<expr_single_t>( &category_to_categorical_kernel_extra::single_uint8); break; case uint16_type_id: e->base.set_function<expr_single_t>( &category_to_categorical_kernel_extra::single_uint16); break; case uint32_type_id: e->base.set_function<expr_single_t>( &category_to_categorical_kernel_extra::single_uint32); break; default: throw runtime_error( "internal error in categorical_type::make_assignment_kernel"); } e->base.destructor = &category_to_categorical_kernel_extra::destruct; // The kernel type owns a reference to this type e->dst_cat_tp = static_cast<const categorical_type *>(ndt::type(dst_tp).release()); e->src_arrmeta = src_arrmeta; return ckb_offset; } else if (src_tp.value_type() != m_category_tp && src_tp.value_type().get_type_id() != categorical_type_id) { // Make a convert type to the category type, and have it do the chaining ndt::type src_cvt_tp = ndt::make_convert(m_category_tp, src_tp); return src_cvt_tp.extended()->make_assignment_kernel( ckb, ckb_offset, dst_tp, dst_arrmeta, src_cvt_tp, src_arrmeta, kernreq, ectx); } else { // Let the src_tp handle it return src_tp.extended()->make_assignment_kernel( ckb, ckb_offset, dst_tp, dst_arrmeta, src_tp, src_arrmeta, kernreq, ectx); } } else { if (dst_tp.value_type().get_type_id() != categorical_type_id) { ckb_offset = make_kernreq_to_single_kernel_adapter(ckb, ckb_offset, 1, kernreq); categorical_to_other_kernel_extra *e = ckb->alloc_ck<categorical_to_other_kernel_extra>(ckb_offset); switch (m_storage_type.get_type_id()) { case uint8_type_id: e->base.set_function<expr_single_t>( &categorical_to_other_kernel_extra::single_uint8); break; case uint16_type_id: e->base.set_function<expr_single_t>( &categorical_to_other_kernel_extra::single_uint16); break; case uint32_type_id: e->base.set_function<expr_single_t>( &categorical_to_other_kernel_extra::single_uint32); break; default: throw runtime_error( "internal error in categorical_type::make_assignment_kernel"); } e->base.destructor = &categorical_to_other_kernel_extra::destruct; // The kernel type owns a reference to this type e->src_cat_tp = static_cast<const categorical_type *>(ndt::type(src_tp).release()); return ::make_assignment_kernel( ckb, ckb_offset, dst_tp, dst_arrmeta, get_category_type(), get_category_arrmeta(), kernel_request_single, ectx); } else { stringstream ss; ss << "Cannot assign from " << src_tp << " to " << dst_tp; throw runtime_error(ss.str()); } } }
size_t dynd::make_expression_comparison_kernel(void *ckb, intptr_t ckb_offset, const ndt::type &src0_dt, const char *src0_arrmeta, const ndt::type &src1_dt, const char *src1_arrmeta, comparison_type_t comptype, const eval::eval_context *ectx) { intptr_t root_ckb_offset = ckb_offset; buffered_kernel_extra *e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->alloc_ck<buffered_kernel_extra>(ckb_offset); e->base.function = reinterpret_cast<void *>(&buffered_kernel_extra::kernel); e->base.destructor = &buffered_kernel_extra::destruct; // Initialize the information for buffering the operands if (src0_dt.get_kind() == expr_kind) { e->init_buffer(0, src0_dt.value_type()); e->buf[0].kernel_offset = ckb_offset - root_ckb_offset; ckb_offset = make_assignment_kernel( ckb, ckb_offset, src0_dt.value_type(), e->buf[0].arrmeta, src0_dt, src0_arrmeta, kernel_request_single, ectx); // Have to re-retrieve 'e', because creating another kernel may invalidate // it e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->get_at<buffered_kernel_extra>(root_ckb_offset); } if (src1_dt.get_kind() == expr_kind) { e->init_buffer(1, src1_dt.value_type()); e->buf[1].kernel_offset = ckb_offset - root_ckb_offset; ckb_offset = make_assignment_kernel( ckb, ckb_offset, src1_dt.value_type(), e->buf[1].arrmeta, src1_dt, src1_arrmeta, kernel_request_single, ectx); // Have to re-retrieve 'e', because creating another kernel may invalidate // it e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->get_at<buffered_kernel_extra>(root_ckb_offset); } // Allocate the data for the buffers if (e->buf[0].kernel_offset != 0) { ckb_offset = inc_to_alignment(ckb_offset, src0_dt.get_data_alignment()); e->buf[0].data_offset = ckb_offset - root_ckb_offset; ckb_offset += e->buf[0].data_size; } if (e->buf[1].kernel_offset != 0) { ckb_offset = inc_to_alignment(ckb_offset, src1_dt.get_data_alignment()); e->buf[1].data_offset = ckb_offset - root_ckb_offset; ckb_offset += e->buf[1].data_size; } reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->reserve(ckb_offset + sizeof(ckernel_prefix)); // Have to re-retrieve 'e', because allocating the buffer data may invalidate // it e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->get_at<buffered_kernel_extra>(root_ckb_offset); e->cmp_kernel_offset = ckb_offset - root_ckb_offset; return make_comparison_kernel( ckb, ckb_offset, src0_dt.value_type(), (e->buf[0].kernel_offset != 0) ? e->buf[0].arrmeta : src0_arrmeta, src1_dt.value_type(), (e->buf[1].kernel_offset != 0) ? e->buf[1].arrmeta : src1_arrmeta, comptype, ectx); }
static void make_numpy_dtype_for_copy(pyobject_ownref *out_numpy_dtype, intptr_t ndim, const ndt::type &dt, const char *arrmeta) { // DyND builtin types if (dt.is_builtin()) { out_numpy_dtype->reset((PyObject *)PyArray_DescrFromType(dynd_to_numpy_id(dt.get_id()))); return; } switch (dt.get_id()) { case fixed_string_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: // If it's not one of the encodings NumPy supports, // use Unicode result = PyArray_DescrNewFromType(NPY_UNICODE); result->elsize = (int)fsd->get_data_size() * 4 / string_encoding_char_size_table[fsd->get_encoding()]; out_numpy_dtype->reset((PyObject *)result); return; } break; } case string_id: { // Convert variable-length strings into NumPy object arrays PyArray_Descr *dtype = PyArray_DescrNewFromType(NPY_OBJECT); // Add metadata to the string type being created so that // it can round-trip. This metadata is compatible with h5py. out_numpy_dtype->reset((PyObject *)dtype); if (dtype->metadata == NULL) { dtype->metadata = PyDict_New(); } PyDict_SetItemString(dtype->metadata, "vlen", (PyObject *)&PyUnicode_Type); return; } case fixed_dim_id: { if (ndim > 0) { const ndt::base_dim_type *bdt = dt.extended<ndt::base_dim_type>(); make_numpy_dtype_for_copy(out_numpy_dtype, 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 // Build up the shape of the array for NumPy pyobject_ownref shape(PyList_New(0)); ndt::type element_tp = dt; while (ndim > 0) { const fixed_dim_type_arrmeta *am = reinterpret_cast<const fixed_dim_type_arrmeta *>(arrmeta); intptr_t dim_size = am->dim_size; element_tp = dt.extended<ndt::base_dim_type>()->get_element_type(); arrmeta += sizeof(fixed_dim_type_arrmeta); --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; make_numpy_dtype_for_copy(&child_numpy_dtype, 0, element_tp, arrmeta); // 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_id: { const ndt::struct_type *bs = dt.extended<ndt::struct_type>(); 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(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)); pyobject_ownref offsets_obj(PyList_New(field_count)); size_t standard_offset = 0, standard_alignment = 1; for (size_t i = 0; i < field_count; ++i) { // Get the numpy dtype of the element pyobject_ownref field_numpy_dtype; make_numpy_dtype_for_copy(&field_numpy_dtype, 0, bs->get_field_type(i), arrmeta); size_t field_alignment = ((PyArray_Descr *)field_numpy_dtype.get())->alignment; size_t field_size = ((PyArray_Descr *)field_numpy_dtype.get())->elsize; standard_offset = inc_to_alignment(standard_offset, field_alignment); standard_alignment = max(standard_alignment, field_alignment); PyList_SET_ITEM(formats_obj.get(), i, field_numpy_dtype.release()); PyList_SET_ITEM((PyObject *)offsets_obj, i, PyLong_FromSize_t(standard_offset)); standard_offset += field_size; } // Get the full element size standard_offset = inc_to_alignment(standard_offset, standard_alignment); pyobject_ownref itemsize_obj(PyLong_FromSize_t(standard_offset)); 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); PyDict_SetItemString(dict_obj, "itemsize", itemsize_obj); PyArray_Descr *result = NULL; if (!PyArray_DescrAlignConverter(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_base_id() == expr_kind_id) { // Convert the value type for the copy make_numpy_dtype_for_copy(out_numpy_dtype, ndim, dt.value_type(), NULL); 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()); }
const ndt::type &get_value_type() const { return m_value_tp.value_type(); }
ndt::type dynd::promote_types_arithmetic(const ndt::type &tp0, const ndt::type &tp1) { // Use the value types const ndt::type &tp0_val = tp0.value_type(); const ndt::type &tp1_val = tp1.value_type(); // cout << "Doing type promotion with value types " << tp0_val << " and " << // tp1_val << endl; if (tp0_val.is_builtin() && tp1_val.is_builtin()) { const size_t int_size = sizeof(int); if (tp0_val.get_id() == void_id) { return tp1_val; } switch (tp0_val.get_base_id()) { case bool_kind_id: if (tp1_val.get_id() == void_id) { return tp0_val; } switch (tp1_val.get_base_id()) { case bool_kind_id: return ndt::make_type<int>(); case int_kind_id: case uint_kind_id: return (tp1_val.get_data_size() >= int_size) ? tp1_val : ndt::make_type<int>(); case float_kind_id: // The bool type doesn't affect float type sizes, except // require at least float32 return tp1_val.unchecked_get_builtin_id() != float16_id ? tp1_val : ndt::make_type<float>(); default: return tp1_val; } case int_kind_id: if (tp1_val.get_id() == void_id) { return tp0_val; } switch (tp1_val.get_base_id()) { case bool_kind_id: return (tp0_val.get_data_size() >= int_size) ? tp0_val : ndt::make_type<int>(); case int_kind_id: if (tp0_val.get_data_size() < int_size && tp1_val.get_data_size() < int_size) { return ndt::make_type<int>(); } else { return (tp0_val.get_data_size() >= tp1_val.get_data_size()) ? tp0_val : tp1_val; } case uint_kind_id: if (tp0_val.get_data_size() < int_size && tp1_val.get_data_size() < int_size) { return ndt::make_type<int>(); } else { // When the element_sizes are equal, the uint kind wins return (tp0_val.get_data_size() > tp1_val.get_data_size()) ? tp0_val : tp1_val; } case float_kind_id: // Integer type sizes don't affect float type sizes, except // require at least float32 return tp1_val.unchecked_get_builtin_id() != float16_id ? tp1_val : ndt::make_type<float>(); case complex_kind_id: // Integer type sizes don't affect complex type sizes return tp1_val; default: break; } break; case uint_kind_id: if (tp1_val.get_id() == void_id) { return tp0_val; } switch (tp1_val.get_base_id()) { case bool_kind_id: return (tp0_val.get_data_size() >= int_size) ? tp0_val : ndt::make_type<int>(); case int_kind_id: if (tp0_val.get_data_size() < int_size && tp1_val.get_data_size() < int_size) { return ndt::make_type<int>(); } else { // When the element_sizes are equal, the uint kind wins return (tp0_val.get_data_size() >= tp1_val.get_data_size()) ? tp0_val : tp1_val; } case uint_kind_id: if (tp0_val.get_data_size() < int_size && tp1_val.get_data_size() < int_size) { return ndt::make_type<int>(); } else { return (tp0_val.get_data_size() >= tp1_val.get_data_size()) ? tp0_val : tp1_val; } case float_kind_id: // Integer type sizes don't affect float type sizes, except // require at least float32 return tp1_val.unchecked_get_builtin_id() != float16_id ? tp1_val : ndt::make_type<float>(); case complex_kind_id: // Integer type sizes don't affect complex type sizes return tp1_val; default: break; } break; case float_kind_id: if (tp1_val.get_id() == void_id) { return tp0_val; } switch (tp1_val.get_base_id()) { // Integer type sizes don't affect float type sizes case bool_kind_id: case int_kind_id: case uint_kind_id: return tp0_val; case float_kind_id: return ndt::type(reinterpret_cast<ndt::base_type *>(max( max(tp0_val.unchecked_get_builtin_id(), tp1_val.unchecked_get_builtin_id()), float32_id)), false); case complex_kind_id: if (tp0_val.get_id() == float64_id && tp1_val.get_id() == complex_float32_id) { return ndt::make_type<complex<double>>(); } else { return tp1_val; } default: break; } break; case complex_kind_id: if (tp1_val.get_id() == void_id) { return tp0_val; } switch (tp1_val.get_base_id()) { // Integer and float type sizes don't affect complex type sizes case bool_kind_id: case int_kind_id: case uint_kind_id: case float_kind_id: if (tp0_val.unchecked_get_builtin_id() == complex_float32_id && tp1_val.unchecked_get_builtin_id() == float64_id) { return ndt::make_type<complex<double>>(); } else { return tp0_val; } case complex_kind_id: return (tp0_val.get_data_size() >= tp1_val.get_data_size()) ? tp0_val : tp1_val; default: break; } break; default: break; } stringstream ss; ss << "internal error in built-in dynd type promotion of " << tp0_val << " and " << tp1_val; throw dynd::type_error(ss.str()); } // HACK for getting simple string type promotions. // TODO: Do this properly in a pluggable manner. if ((tp0_val.get_id() == string_id || tp0_val.get_id() == fixed_string_id) && (tp1_val.get_id() == string_id || tp1_val.get_id() == fixed_string_id)) { // Always promote to the default utf-8 string (for now, maybe return // encoding, etc later?) return ndt::make_type<ndt::string_type>(); } // the value underneath the option type promotes if (tp0_val.get_id() == option_id) { if (tp1_val.get_id() == option_id) { return ndt::make_type<ndt::option_type>( promote_types_arithmetic(tp0_val.extended<ndt::option_type>()->get_value_type(), tp1_val.extended<ndt::option_type>()->get_value_type())); } else { return ndt::make_type<ndt::option_type>( promote_types_arithmetic(tp0_val.extended<ndt::option_type>()->get_value_type(), tp1_val)); } } else if (tp1_val.get_id() == option_id) { return ndt::make_type<ndt::option_type>( promote_types_arithmetic(tp0_val, tp1_val.extended<ndt::option_type>()->get_value_type())); } // type, string -> type if (tp0_val.get_id() == type_id && tp1_val.get_base_id() == string_kind_id) { return tp0_val; } // string, type -> type if (tp0_val.get_base_id() == string_kind_id && tp1_val.get_id() == type_id) { return tp1_val; } // In general, if one type is void, just return the other type if (tp0_val.get_id() == void_id) { return tp1_val; } else if (tp1_val.get_id() == void_id) { return tp0_val; } // Promote some dimension types if ((tp0_val.get_id() == var_dim_id && tp1_val.get_base_id() == dim_kind_id) || (tp1_val.get_id() == var_dim_id && tp0_val.get_base_id() == dim_kind_id)) { return ndt::make_type<ndt::var_dim_type>( promote_types_arithmetic(tp0_val.extended<ndt::base_dim_type>()->get_element_type(), tp1_val.extended<ndt::base_dim_type>()->get_element_type())); } stringstream ss; ss << "type promotion of " << tp0 << " and " << tp1 << " is not yet supported"; throw dynd::type_error(ss.str()); }
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()); }
size_t dynd::make_expression_assignment_kernel( void *ckb, intptr_t ckb_offset, const ndt::type &dst_tp, const char *dst_arrmeta, const ndt::type &src_tp, const char *src_arrmeta, kernel_request_t kernreq, const eval::eval_context *ectx) { intptr_t root_ckb_offset = ckb_offset; if (dst_tp.get_kind() == expr_kind) { const ndt::base_expr_type *dst_bed = dst_tp.extended<ndt::base_expr_type>(); if (src_tp == dst_bed->get_value_type()) { // In this case, it's just a chain of value -> operand on the dst side const ndt::type &opdt = dst_bed->get_operand_type(); if (opdt.get_kind() != expr_kind) { // Leaf case, just a single value -> operand kernel return dst_bed->make_value_to_operand_assignment_kernel( ckb, ckb_offset, dst_arrmeta, src_arrmeta, kernreq, ectx); } else { // Chain case, buffer one segment of the chain const ndt::type &buffer_tp = static_cast<const ndt::base_expr_type *>( opdt.extended())->get_value_type(); buffered_kernel_extra *e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->alloc_ck<buffered_kernel_extra>(ckb_offset); e->init(buffer_tp, kernreq); // Construct the first kernel (src -> buffer) e->first_kernel_offset = ckb_offset - root_ckb_offset; ckb_offset = dst_bed->make_value_to_operand_assignment_kernel( ckb, ckb_offset, e->buffer_arrmeta, src_arrmeta, kernreq, ectx); // Allocate the buffer data ckb_offset = inc_to_alignment(ckb_offset, buffer_tp.get_data_alignment()); intptr_t buffer_data_offset = ckb_offset; inc_ckb_offset(ckb_offset, e->buffer_data_size); reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->reserve(ckb_offset + sizeof(ckernel_prefix)); // This may have invalidated the 'e' pointer, so get it again! e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->get_at<buffered_kernel_extra>(root_ckb_offset); e->buffer_data_offset = buffer_data_offset - root_ckb_offset; // Construct the second kernel (buffer -> dst) e->second_kernel_offset = ckb_offset - root_ckb_offset; return ::make_assignment_kernel(ckb, ckb_offset, opdt, dst_arrmeta, buffer_tp, e->buffer_arrmeta, kernreq, ectx); } } else { ndt::type buffer_tp; if (src_tp.get_kind() != expr_kind) { // In this case, need a data converting assignment to // dst_tp.value_type(), // then the dst_tp expression chain buffer_tp = dst_bed->get_value_type(); } else { // Both src and dst are expression types, use the src expression chain, // and // the src value type to dst type as the two segments to buffer together buffer_tp = src_tp.value_type(); } buffered_kernel_extra *e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->alloc_ck<buffered_kernel_extra>(ckb_offset); e->init(buffer_tp, kernreq); // Construct the first kernel (src -> buffer) e->first_kernel_offset = ckb_offset - root_ckb_offset; ckb_offset = ::make_assignment_kernel(ckb, ckb_offset, buffer_tp, e->buffer_arrmeta, src_tp, src_arrmeta, kernreq, ectx); ckb_offset = inc_to_alignment(ckb_offset, buffer_tp.get_data_alignment()); // Allocate the buffer data intptr_t buffer_data_offset = ckb_offset; inc_ckb_offset(ckb_offset, e->buffer_data_size); reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->reserve(ckb_offset + sizeof(ckernel_prefix)); // This may have invalidated the 'e' pointer, so get it again! e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->get_at<buffered_kernel_extra>(root_ckb_offset); e->buffer_data_offset = buffer_data_offset - root_ckb_offset; // Construct the second kernel (buffer -> dst) e->second_kernel_offset = ckb_offset - root_ckb_offset; return ::make_assignment_kernel(ckb, ckb_offset, dst_tp, dst_arrmeta, buffer_tp, e->buffer_arrmeta, kernreq, ectx); } } else { const ndt::base_expr_type *src_bed = src_tp.extended<ndt::base_expr_type>(); if (dst_tp == src_bed->get_value_type()) { // In this case, it's just a chain of operand -> value on the src side const ndt::type &opdt = src_bed->get_operand_type(); if (opdt.get_kind() != expr_kind) { // Leaf case, just a single value -> operand kernel return src_bed->make_operand_to_value_assignment_kernel( ckb, ckb_offset, dst_arrmeta, src_arrmeta, kernreq, ectx); } else { // Chain case, buffer one segment of the chain const ndt::type &buffer_tp = static_cast<const ndt::base_expr_type *>( opdt.extended())->get_value_type(); buffered_kernel_extra *e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->alloc_ck<buffered_kernel_extra>(ckb_offset); e->init(buffer_tp, kernreq); size_t buffer_data_size = e->buffer_data_size; // Construct the first kernel (src -> buffer) e->first_kernel_offset = ckb_offset - root_ckb_offset; ckb_offset = ::make_assignment_kernel(ckb, ckb_offset, buffer_tp, e->buffer_arrmeta, opdt, src_arrmeta, kernreq, ectx); // Allocate the buffer data ckb_offset = inc_to_alignment(ckb_offset, buffer_tp.get_data_alignment()); size_t buffer_data_offset = ckb_offset; inc_ckb_offset(ckb_offset, buffer_data_size); reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->reserve(ckb_offset + sizeof(ckernel_prefix)); // This may have invalidated the 'e' pointer, so get it again! e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->get_at<buffered_kernel_extra>(root_ckb_offset); e->buffer_data_offset = buffer_data_offset - root_ckb_offset; // Construct the second kernel (buffer -> dst) e->second_kernel_offset = ckb_offset - root_ckb_offset; return src_bed->make_operand_to_value_assignment_kernel( ckb, ckb_offset, dst_arrmeta, e->buffer_arrmeta, kernreq, ectx); } } else { // Put together the src expression chain and the src value type // to dst value type conversion const ndt::type &buffer_tp = src_tp.value_type(); buffered_kernel_extra *e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->alloc_ck<buffered_kernel_extra>(ckb_offset); e->init(buffer_tp, kernreq); size_t buffer_data_size = e->buffer_data_size; // Construct the first kernel (src -> buffer) e->first_kernel_offset = ckb_offset - root_ckb_offset; ckb_offset = ::make_assignment_kernel(ckb, ckb_offset, buffer_tp, e->buffer_arrmeta, src_tp, src_arrmeta, kernreq, ectx); // Allocate the buffer data ckb_offset = inc_to_alignment(ckb_offset, buffer_tp.get_data_alignment()); size_t buffer_data_offset = ckb_offset; inc_ckb_offset(ckb_offset, buffer_data_size); reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->reserve(ckb_offset + sizeof(ckernel_prefix)); // This may have invalidated the 'e' pointer, so get it again! e = reinterpret_cast<ckernel_builder<kernel_request_host> *>(ckb) ->get_at<buffered_kernel_extra>(root_ckb_offset); e->buffer_data_offset = buffer_data_offset - root_ckb_offset; // Construct the second kernel (buffer -> dst) e->second_kernel_offset = ckb_offset - root_ckb_offset; return ::make_assignment_kernel(ckb, ckb_offset, dst_tp, dst_arrmeta, buffer_tp, e->buffer_arrmeta, kernreq, ectx); } } }