// =============================================================================
int Epetra_NumPyVector::SumIntoMyValues(int blockOffset, PyObject * values,
					PyObject * indices)
{
  PyArrayObject * myValues  = NULL;
  PyArrayObject * myIndices = NULL;
  int lenValues;
  int lenIndices;
  int result;
  myValues  = (PyArrayObject *)
    PyArray_ContiguousFromObject(values,PyArray_DOUBLE,0,0);
  if (!myValues) goto fail;
  myIndices = (PyArrayObject *)
    PyArray_ContiguousFromObject(indices,PyArray_INT,0,0);
  if (!myIndices) goto fail;
  lenValues  = (int) PyArray_MultiplyList(myValues->dimensions,  myValues->nd );
  lenIndices = (int) PyArray_MultiplyList(myIndices->dimensions, myIndices->nd);
  if (lenValues != lenIndices)
  {
    PyErr_Format(PyExc_ValueError,
		 "Sequence lengths are %d and %d, but must be of same length",
		 lenValues, lenIndices);
    goto fail;
  }
  result = Epetra_Vector::SumIntoMyValues(lenValues, blockOffset,
					  (double *) myValues->data,
					  (int    *) myIndices->data);
  Py_DECREF(myValues );
  Py_DECREF(myIndices);
  return result;
 fail:
  Py_XDECREF(myValues );
  Py_XDECREF(myIndices);
  return -1;
}
Esempio n. 2
0
// =============================================================================
int * Epetra_NumPyIntVector::getArray(const Epetra_BlockMap & blockMap,
				      PyObject * pyObject)
{
  // Only build the tmp_array if it does not already exist
  if (!tmp_array)
  {
    // Default dimensions
    npy_intp defaultDims[ ] = { blockMap.NumMyPoints() };

    // PyObject argument is a bool
    if (PyBool_Check(pyObject))
    {
      tmp_array = (PyArrayObject *)
	PyArray_SimpleNew(1,defaultDims,NPY_INT);
    }
    // PyObject argument is not a bool ... try to build a contiguous
    // PyArrayObject from it
    else
    {
      tmp_array = (PyArrayObject *)
	PyArray_ContiguousFromObject(pyObject,NPY_INT,0,0);
    }
    // If any PyArray factory functions fail, clean up and throw a
    // PythonException
    if (!tmp_array)
    {
      cleanup();
      throw PythonException();
    }
    int  nd = PyArray_NDIM(tmp_array);
    npy_intp arraySize = PyArray_MultiplyList(PyArray_DIMS(tmp_array),nd);
    if (arraySize != defaultDims[0])
    {
      PyArrayObject * myArray = (PyArrayObject *)
	PyArray_SimpleNew(1,defaultDims,NPY_INT);
      if (!myArray)
      {
	cleanup();
	throw PythonException();
      }
      int * myData  = (int *) PyArray_DATA(myArray);
      int * tmpData = (int *) PyArray_DATA(tmp_array);
      for (int i=0; i<defaultDims[0]; i++)
      {
	myData[i] = tmpData[i];
      }
      Py_XDECREF(tmp_array);
      tmp_array = myArray;
    }
  }
  return (int*)(PyArray_DATA(tmp_array));
}
Esempio n. 3
0
static int
fortran_setattr(PyFortranObject *fp, char *name, PyObject *v) {
    int i,j,flag;
    PyArrayObject *arr = NULL;
    for (i=0,j=1;i<fp->len && (j=strcmp(name,fp->defs[i].name));i++);
    if (j==0) {
        if (fp->defs[i].rank==-1) {
            PyErr_SetString(PyExc_AttributeError,"over-writing fortran routine");
            return -1;
        }
        if (fp->defs[i].func!=NULL) { /* is allocatable array */
            npy_intp dims[F2PY_MAX_DIMS];
            int k;
            save_def = &fp->defs[i];
            if (v!=Py_None) {     /* set new value (reallocate if needed --
                                     see f2py generated code for more
                                     details ) */
                for(k=0;k<fp->defs[i].rank;k++) dims[k]=-1;
                if ((arr = array_from_pyobj(fp->defs[i].type,dims,fp->defs[i].rank,F2PY_INTENT_IN,v))==NULL)
                    return -1;
                (*(fp->defs[i].func))(&fp->defs[i].rank,arr->dimensions,set_data,&flag);
            } else {             /* deallocate */
                for(k=0;k<fp->defs[i].rank;k++) dims[k]=0;
                (*(fp->defs[i].func))(&fp->defs[i].rank,dims,set_data,&flag);
                for(k=0;k<fp->defs[i].rank;k++) dims[k]=-1;
            }
            memcpy(fp->defs[i].dims.d,dims,fp->defs[i].rank*sizeof(npy_intp));
        } else {                     /* not allocatable array */
            if ((arr = array_from_pyobj(fp->defs[i].type,fp->defs[i].dims.d,fp->defs[i].rank,F2PY_INTENT_IN,v))==NULL)
                return -1;
        }
        if (fp->defs[i].data!=NULL) { /* copy Python object to Fortran array */
            npy_intp s = PyArray_MultiplyList(fp->defs[i].dims.d,arr->nd);
            if (s==-1)
                s = PyArray_MultiplyList(arr->dimensions,arr->nd);
            if (s<0 ||
                (memcpy(fp->defs[i].data,arr->data,s*PyArray_ITEMSIZE(arr)))==NULL) {
                if ((PyObject*)arr!=v) {
                    Py_DECREF(arr);
                }
                return -1;
            }
            if ((PyObject*)arr!=v) {
                Py_DECREF(arr);
            }
        } else return (fp->defs[i].func==NULL?-1:0);
        return 0; /* succesful */
    }
    if (fp->dict == NULL) {
        fp->dict = PyDict_New();
        if (fp->dict == NULL)
            return -1;
    }
    if (v == NULL) {
        int rv = PyDict_DelItemString(fp->dict, name);
        if (rv < 0)
            PyErr_SetString(PyExc_AttributeError,"delete non-existing fortran attribute");
        return rv;
    }
    else
        return PyDict_SetItemString(fp->dict, name, v);
}
// =============================================================================
int Epetra_NumPySerialDenseVector::getVectorSize(PyObject * pyObject)
{
  if (!tmp_array) getArray(pyObject);
  return (int) PyArray_MultiplyList(tmp_array->dimensions, tmp_array->nd);
}
Esempio n. 5
0
/* unravel_index implementation - see add_newdocs.py */
NPY_NO_EXPORT PyObject *
arr_unravel_index(PyObject *self, PyObject *args, PyObject *kwds)
{
    PyObject *indices0 = NULL, *ret_tuple = NULL;
    PyArrayObject *ret_arr = NULL;
    PyArrayObject *indices = NULL;
    PyArray_Descr *dtype = NULL;
    PyArray_Dims dimensions={0,0};
    NPY_ORDER order = NPY_CORDER;
    npy_intp unravel_size;

    NpyIter *iter = NULL;
    int i, ret_ndim;
    npy_intp ret_dims[NPY_MAXDIMS], ret_strides[NPY_MAXDIMS];

    char *kwlist[] = {"indices", "dims", "order", NULL};

    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&|O&:unravel_index",
                    kwlist,
                    &indices0,
                    PyArray_IntpConverter, &dimensions,
                    PyArray_OrderConverter, &order)) {
        goto fail;
    }

    if (dimensions.len == 0) {
        PyErr_SetString(PyExc_ValueError,
                "dims must have at least one value");
        goto fail;
    }

    unravel_size = PyArray_MultiplyList(dimensions.ptr, dimensions.len);

    if (!PyArray_Check(indices0)) {
        indices = (PyArrayObject*)PyArray_FromAny(indices0,
                                                    NULL, 0, 0, 0, NULL);
        if (indices == NULL) {
            goto fail;
        }
    }
    else {
        indices = (PyArrayObject *)indices0;
        Py_INCREF(indices);
    }

    dtype = PyArray_DescrFromType(NPY_INTP);
    if (dtype == NULL) {
        goto fail;
    }

    iter = NpyIter_New(indices, NPY_ITER_READONLY|
                                NPY_ITER_ALIGNED|
                                NPY_ITER_BUFFERED|
                                NPY_ITER_ZEROSIZE_OK|
                                NPY_ITER_DONT_NEGATE_STRIDES|
                                NPY_ITER_MULTI_INDEX,
                                NPY_KEEPORDER, NPY_SAME_KIND_CASTING,
                                dtype);
    if (iter == NULL) {
        goto fail;
    }

    /*
     * Create the return array with a layout compatible with the indices
     * and with a dimension added to the end for the multi-index
     */
    ret_ndim = PyArray_NDIM(indices) + 1;
    if (NpyIter_GetShape(iter, ret_dims) != NPY_SUCCEED) {
        goto fail;
    }
    ret_dims[ret_ndim-1] = dimensions.len;
    if (NpyIter_CreateCompatibleStrides(iter,
                dimensions.len*sizeof(npy_intp), ret_strides) != NPY_SUCCEED) {
        goto fail;
    }
    ret_strides[ret_ndim-1] = sizeof(npy_intp);

    /* Remove the multi-index and inner loop */
    if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED) {
        goto fail;
    }
    if (NpyIter_EnableExternalLoop(iter) != NPY_SUCCEED) {
        goto fail;
    }

    ret_arr = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype,
                            ret_ndim, ret_dims, ret_strides, NULL, 0, NULL);
    dtype = NULL;
    if (ret_arr == NULL) {
        goto fail;
    }

    if (order == NPY_CORDER) {
        if (NpyIter_GetIterSize(iter) != 0) {
            NpyIter_IterNextFunc *iternext;
            char **dataptr;
            npy_intp *strides;
            npy_intp *countptr, count;
            npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr);

            iternext = NpyIter_GetIterNext(iter, NULL);
            if (iternext == NULL) {
                goto fail;
            }
            dataptr = NpyIter_GetDataPtrArray(iter);
            strides = NpyIter_GetInnerStrideArray(iter);
            countptr = NpyIter_GetInnerLoopSizePtr(iter);

            do {
                count = *countptr;
                if (unravel_index_loop_corder(dimensions.len, dimensions.ptr,
                            unravel_size, count, *dataptr, *strides,
                            coordsptr) != NPY_SUCCEED) {
                    goto fail;
                }
                coordsptr += count*dimensions.len;
            } while(iternext(iter));
        }
    }
    else if (order == NPY_FORTRANORDER) {
        if (NpyIter_GetIterSize(iter) != 0) {
            NpyIter_IterNextFunc *iternext;
            char **dataptr;
            npy_intp *strides;
            npy_intp *countptr, count;
            npy_intp *coordsptr = (npy_intp *)PyArray_DATA(ret_arr);

            iternext = NpyIter_GetIterNext(iter, NULL);
            if (iternext == NULL) {
                goto fail;
            }
            dataptr = NpyIter_GetDataPtrArray(iter);
            strides = NpyIter_GetInnerStrideArray(iter);
            countptr = NpyIter_GetInnerLoopSizePtr(iter);

            do {
                count = *countptr;
                if (unravel_index_loop_forder(dimensions.len, dimensions.ptr,
                            unravel_size, count, *dataptr, *strides,
                            coordsptr) != NPY_SUCCEED) {
                    goto fail;
                }
                coordsptr += count*dimensions.len;
            } while(iternext(iter));
        }
    }
    else {
        PyErr_SetString(PyExc_ValueError,
                        "only 'C' or 'F' order is permitted");
        goto fail;
    }

    /* Now make a tuple of views, one per index */
    ret_tuple = PyTuple_New(dimensions.len);
    if (ret_tuple == NULL) {
        goto fail;
    }
    for (i = 0; i < dimensions.len; ++i) {
        PyArrayObject *view;

        view = (PyArrayObject *)PyArray_New(&PyArray_Type, ret_ndim-1,
                                ret_dims, NPY_INTP,
                                ret_strides,
                                PyArray_BYTES(ret_arr) + i*sizeof(npy_intp),
                                0, NPY_ARRAY_WRITEABLE, NULL);
        if (view == NULL) {
            goto fail;
        }
        Py_INCREF(ret_arr);
        if (PyArray_SetBaseObject(view, (PyObject *)ret_arr) < 0) {
            Py_DECREF(view);
            goto fail;
        }
        PyTuple_SET_ITEM(ret_tuple, i, PyArray_Return(view));
    }

    Py_DECREF(ret_arr);
    Py_XDECREF(indices);
    PyDimMem_FREE(dimensions.ptr);
    NpyIter_Deallocate(iter);

    return ret_tuple;

fail:
    Py_XDECREF(ret_tuple);
    Py_XDECREF(ret_arr);
    Py_XDECREF(dtype);
    Py_XDECREF(indices);
    PyDimMem_FREE(dimensions.ptr);
    NpyIter_Deallocate(iter);
    return NULL;
}
// =============================================================================
double * Epetra_NumPyMultiVector::getArray(const Epetra_BlockMap & blockMap,
					   PyObject * pyObject)
{
  // PyObject argument is an int
  if (PyInt_Check(pyObject))
  {
    int numVectors = (int) PyInt_AsLong(pyObject);
    npy_intp dimensions[ ] = { numVectors, blockMap.NumMyPoints() };
    tmp_array = (PyArrayObject *)
      PyArray_SimpleNew(2,dimensions,NPY_DOUBLE);
    if (!tmp_array)
    {
      cleanup();
      throw PythonException();
    }

  // PyObject argument is not an int ... try to build a contiguous PyArrayObject from it
  }
  else
  {
    if (!tmp_array)
      tmp_array = (PyArrayObject *)
	PyArray_ContiguousFromObject(pyObject,NPY_DOUBLE,0,0);

    // If this fails, clean up and throw a PythonException
    if (!tmp_array)
    {
      cleanup();
      throw PythonException();
    }
    // If the contiguous PyArrayObject built successfully, make sure it has the correct
    // number of dimensions
    else
    {
      int nd = PyArray_NDIM(tmp_array);
      npy_intp dimensions[ ] = { 1, blockMap.NumMyPoints() };  // Default dimensions
      bool reallocate = false;
      npy_intp arraySize = PyArray_MultiplyList(PyArray_DIMS(tmp_array),nd);

      if (nd < 2)
      {
	reallocate = true;
      }
      else
      {
	arraySize /= PyArray_DIMS(tmp_array)[0];
	if (arraySize != dimensions[1])
	{
	  dimensions[0] = PyArray_DIMS(tmp_array)[0];
	  reallocate = true;
	}
      }

      // Reallocate the tmp_array if necessary
      if (reallocate)
      {
	PyArrayObject * myArray = (PyArrayObject *)
	  PyArray_SimpleNew(2,dimensions,NPY_DOUBLE);
	if (!myArray)
	{
	  cleanup();
	  throw PythonException();
	}
	double        * myData  = (double *) PyArray_DATA(myArray);
	double        * tmpData = (double *) PyArray_DATA(tmp_array);
	for (int i=0; i<dimensions[0]; i++)
	{
	  for (int j=0; j<arraySize && j<dimensions[1]; j++)
	  {
	    myData[i*dimensions[1]+j] = tmpData[i*arraySize+j];
	  }
	}
	Py_XDECREF(tmp_array);
	tmp_array = myArray;
      }
    }
  }
  return (double *) (PyArray_DATA(tmp_array));
}
// =============================================================================
double * Epetra_NumPyVector::getArray(const Epetra_BlockMap & blockMap,
				      PyObject * pyObject)
{
  // Only build the tmp_array if it does not already exist
  if (!tmp_array)
  {
    // Default dimensions
    npy_intp defaultDims[ ] = { blockMap.NumMyPoints() };

    // PyObject argument is a bool
    if (PyBool_Check(pyObject))
    {
      tmp_array = (PyArrayObject *)
	PyArray_SimpleNew(1,defaultDims,PyArray_DOUBLE);
      if (tmp_array == NULL)
      {
	cleanup();
	throw PythonException();
      }
      else
      {
	if (pyObject == Py_True)     // bool zeroOut is True
	{
	  double * data = (double*) tmp_array->data;
	  for (int i=0; i<defaultDims[0]; ++i) data[i] = 0.0;
	}
      }

    // PyObject argument is not an bool ... try to build a contiguous
    // PyArrayObject from it
    }
    else
    {
      tmp_array = (PyArrayObject *)
	PyArray_ContiguousFromObject(pyObject,PyArray_DOUBLE,0,0);

      // If this fails, clean up and throw a PythonException
      if (!tmp_array)
      {
	cleanup();
	throw PythonException();
      }
      // If the contiguous PyArrayObject built successfully, make sure
      // it has the correct number of dimensions
      else
      {
	int nd = tmp_array->nd;
	npy_intp arraySize = PyArray_MultiplyList(tmp_array->dimensions,nd);
	if (arraySize != defaultDims[0])
	{
	  PyArrayObject * myArray = (PyArrayObject *)
	    PyArray_SimpleNew(1,defaultDims,PyArray_DOUBLE);
	  double * myData  = (double *) myArray->data;
	  double * tmpData = (double *) tmp_array->data;
	  for (int i=0; i<defaultDims[0]; i++)
	  {
	    myData[i] = tmpData[i];
	  }
	  Py_XDECREF(tmp_array);
	  tmp_array = myArray;
	}
      }
    }
  }
  return (double*)(tmp_array->data);
}
// =============================================================================
int Epetra_NumPyIntSerialDenseVector::getVectorSize(PyObject * pyObject)
{
  if (!tmp_array) getArray(pyObject);
  return (int) PyArray_MultiplyList(PyArray_DIMS(tmp_array),
                                    PyArray_NDIM(tmp_array));
}
Esempio n. 9
0
/*NUMPY_API
 * ArgMax
 */
NPY_NO_EXPORT PyObject *
PyArray_ArgMax(PyArrayObject *op, int axis, PyArrayObject *out)
{
    PyArrayObject *ap = NULL, *rp = NULL;
    PyArray_ArgFunc* arg_func;
    char *ip;
    intp *rptr;
    intp i, n, m;
    int elsize;
    int copyret = 0;
    NPY_BEGIN_THREADS_DEF;

    if ((ap=(PyAO *)_check_axis(op, &axis, 0)) == NULL) {
        return NULL;
    }
    /*
     * We need to permute the array so that axis is placed at the end.
     * And all other dimensions are shifted left.
     */
    if (axis != ap->nd-1) {
        PyArray_Dims newaxes;
        intp dims[MAX_DIMS];
        int i;

        newaxes.ptr = dims;
        newaxes.len = ap->nd;
        for (i = 0; i < axis; i++) dims[i] = i;
        for (i = axis; i < ap->nd - 1; i++) dims[i] = i + 1;
        dims[ap->nd - 1] = axis;
        op = (PyAO *)PyArray_Transpose(ap, &newaxes);
        Py_DECREF(ap);
        if (op == NULL) {
            return NULL;
        }
    }
    else {
        op = ap;
    }

    /* Will get native-byte order contiguous copy. */
    ap = (PyArrayObject *)
        PyArray_ContiguousFromAny((PyObject *)op,
                                  op->descr->type_num, 1, 0);
    Py_DECREF(op);
    if (ap == NULL) {
        return NULL;
    }
    arg_func = ap->descr->f->argmax;
    if (arg_func == NULL) {
        PyErr_SetString(PyExc_TypeError, "data type not ordered");
        goto fail;
    }
    elsize = ap->descr->elsize;
    m = ap->dimensions[ap->nd-1];
    if (m == 0) {
        PyErr_SetString(PyExc_ValueError,
                        "attempt to get argmax/argmin "\
                        "of an empty sequence");
        goto fail;
    }

    if (!out) {
        rp = (PyArrayObject *)PyArray_New(ap->ob_type, ap->nd-1,
                                          ap->dimensions, PyArray_INTP,
                                          NULL, NULL, 0, 0,
                                          (PyObject *)ap);
        if (rp == NULL) {
            goto fail;
        }
    }
    else {
        if (PyArray_SIZE(out) !=
                PyArray_MultiplyList(ap->dimensions, ap->nd - 1)) {
            PyErr_SetString(PyExc_TypeError,
                            "invalid shape for output array.");
        }
        rp = (PyArrayObject *)\
            PyArray_FromArray(out,
                              PyArray_DescrFromType(PyArray_INTP),
                              NPY_CARRAY | NPY_UPDATEIFCOPY);
        if (rp == NULL) {
            goto fail;
        }
        if (rp != out) {
            copyret = 1;
        }
    }

    NPY_BEGIN_THREADS_DESCR(ap->descr);
    n = PyArray_SIZE(ap)/m;
    rptr = (intp *)rp->data;
    for (ip = ap->data, i = 0; i < n; i++, ip += elsize*m) {
        arg_func(ip, m, rptr, ap);
        rptr += 1;
    }
    NPY_END_THREADS_DESCR(ap->descr);

    Py_DECREF(ap);
    if (copyret) {
        PyArrayObject *obj;
        obj = (PyArrayObject *)rp->base;
        Py_INCREF(obj);
        Py_DECREF(rp);
        rp = obj;
    }
    return (PyObject *)rp;

 fail:
    Py_DECREF(ap);
    Py_XDECREF(rp);
    return NULL;
}
Esempio n. 10
0
/*NUMPY_API
 * ArgMax
 */
NPY_NO_EXPORT PyObject *
PyArray_ArgMax(PyArrayObject *op, int axis, PyArrayObject *out)
{
    PyArrayObject *ap = NULL, *rp = NULL;
    PyArray_ArgFunc* arg_func;
    char *ip;
    npy_intp *rptr;
    npy_intp i, n, m;
    int elsize;
    NPY_BEGIN_THREADS_DEF;

    if ((ap = (PyArrayObject *)PyArray_CheckAxis(op, &axis, 0)) == NULL) {
        return NULL;
    }
    /*
     * We need to permute the array so that axis is placed at the end.
     * And all other dimensions are shifted left.
     */
    if (axis != PyArray_NDIM(ap)-1) {
        PyArray_Dims newaxes;
        npy_intp dims[MAX_DIMS];
        int j;

        newaxes.ptr = dims;
        newaxes.len = PyArray_NDIM(ap);
        for (j = 0; j < axis; j++) {
            dims[j] = j;
        }
        for (j = axis; j < PyArray_NDIM(ap) - 1; j++) {
            dims[j] = j + 1;
        }
        dims[PyArray_NDIM(ap) - 1] = axis;
        op = (PyArrayObject *)PyArray_Transpose(ap, &newaxes);
        Py_DECREF(ap);
        if (op == NULL) {
            return NULL;
        }
    }
    else {
        op = ap;
    }

    /* Will get native-byte order contiguous copy. */
    ap = (PyArrayObject *)PyArray_ContiguousFromAny((PyObject *)op,
                                  PyArray_DESCR(op)->type_num, 1, 0);
    Py_DECREF(op);
    if (ap == NULL) {
        return NULL;
    }
    arg_func = PyArray_DESCR(ap)->f->argmax;
    if (arg_func == NULL) {
        PyErr_SetString(PyExc_TypeError,
                "data type not ordered");
        goto fail;
    }
    elsize = PyArray_DESCR(ap)->elsize;
    m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1];
    if (m == 0) {
        PyErr_SetString(PyExc_ValueError,
                "attempt to get argmax of an empty sequence");
        goto fail;
    }

    if (!out) {
        rp = (PyArrayObject *)PyArray_New(Py_TYPE(ap), PyArray_NDIM(ap)-1,
                                          PyArray_DIMS(ap), PyArray_INTP,
                                          NULL, NULL, 0, 0,
                                          (PyObject *)ap);
        if (rp == NULL) {
            goto fail;
        }
    }
    else {
        if (PyArray_SIZE(out) !=
                PyArray_MultiplyList(PyArray_DIMS(ap), PyArray_NDIM(ap) - 1)) {
            PyErr_SetString(PyExc_TypeError,
                    "invalid shape for output array.");
        }
        rp = (PyArrayObject *)PyArray_FromArray(out,
                              PyArray_DescrFromType(PyArray_INTP),
                              NPY_ARRAY_CARRAY | NPY_ARRAY_UPDATEIFCOPY);
        if (rp == NULL) {
            goto fail;
        }
    }

    NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(ap));
    n = PyArray_SIZE(ap)/m;
    rptr = (npy_intp *)PyArray_DATA(rp);
    for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) {
        arg_func(ip, m, rptr, ap);
        rptr += 1;
    }
    NPY_END_THREADS_DESCR(PyArray_DESCR(ap));

    Py_DECREF(ap);
    /* Trigger the UPDATEIFCOPY if necessary */
    if (out != NULL && out != rp) {
        Py_DECREF(rp);
        rp = out;
        Py_INCREF(rp);
    }
    return (PyObject *)rp;

 fail:
    Py_DECREF(ap);
    Py_XDECREF(rp);
    return NULL;
}