123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- """
- Template for each `dtype` helper function for sparse ops
- WARNING: DO NOT edit .pxi FILE directly, .pxi is generated from .pxi.in
- """
- # ----------------------------------------------------------------------
- # Sparse op
- # ----------------------------------------------------------------------
- ctypedef fused sparse_t:
- float64_t
- int64_t
- cdef float64_t __div__(sparse_t a, sparse_t b):
- if b == 0:
- if a > 0:
- return INF
- elif a < 0:
- return -INF
- else:
- return NaN
- else:
- return float(a) / b
- cdef float64_t __truediv__(sparse_t a, sparse_t b):
- return __div__(a, b)
- cdef sparse_t __mod__(sparse_t a, sparse_t b):
- if b == 0:
- if sparse_t is float64_t:
- return NaN
- else:
- return 0
- else:
- return a % b
- cdef sparse_t __floordiv__(sparse_t a, sparse_t b):
- if b == 0:
- if sparse_t is float64_t:
- # Match non-sparse Series behavior implemented in mask_zero_div_zero
- if a > 0:
- return INF
- elif a < 0:
- return -INF
- return NaN
- else:
- return 0
- else:
- return a // b
- # ----------------------------------------------------------------------
- # sparse array op
- # ----------------------------------------------------------------------
- {{py:
- # dtype, arith_comp_group, logical_group
- dtypes = [('float64', True, False),
- ('int64', True, True),
- ('uint8', False, True)]
- # do not generate arithmetic / comparison template for uint8,
- # it should be done in fused types
- def get_op(tup):
- assert isinstance(tup, tuple)
- assert len(tup) == 4
- opname, lval, rval, dtype = tup
- ops_dict = {'add': '{0} + {1}',
- 'sub': '{0} - {1}',
- 'mul': '{0} * {1}',
- 'div': '__div__({0}, {1})',
- 'mod': '__mod__({0}, {1})',
- 'truediv': '__truediv__({0}, {1})',
- 'floordiv': '__floordiv__({0}, {1})',
- 'pow': '{0} ** {1}',
- 'eq': '{0} == {1}',
- 'ne': '{0} != {1}',
- 'lt': '{0} < {1}',
- 'gt': '{0} > {1}',
- 'le': '{0} <= {1}',
- 'ge': '{0} >= {1}',
- 'and': '{0} & {1}', # logical op
- 'or': '{0} | {1}',
- 'xor': '{0} ^ {1}'}
- return ops_dict[opname].format(lval, rval)
- def get_dispatch(dtypes):
- ops_list = ['add', 'sub', 'mul', 'div', 'mod', 'truediv',
- 'floordiv', 'pow',
- 'eq', 'ne', 'lt', 'gt', 'le', 'ge',
- 'and', 'or', 'xor']
- for opname in ops_list:
- for dtype, arith_comp_group, logical_group in dtypes:
- if opname in ('div', 'truediv'):
- rdtype = 'float64'
- elif opname in ('eq', 'ne', 'lt', 'gt', 'le', 'ge'):
- # comparison op
- rdtype = 'uint8'
- elif opname in ('and', 'or', 'xor'):
- # logical op
- rdtype = 'uint8'
- else:
- rdtype = dtype
- if opname in ('and', 'or', 'xor'):
- if logical_group:
- yield opname, dtype, rdtype
- else:
- if arith_comp_group:
- yield opname, dtype, rdtype
- }}
- {{for opname, dtype, rdtype in get_dispatch(dtypes)}}
- {{if opname == "pow"}}
- @cython.cpow(True) # Cython 3 matches Python pow, which isn't what we want here
- {{endif}}
- @cython.wraparound(False)
- @cython.boundscheck(False)
- cdef tuple block_op_{{opname}}_{{dtype}}({{dtype}}_t[:] x_,
- BlockIndex xindex,
- {{dtype}}_t xfill,
- {{dtype}}_t[:] y_,
- BlockIndex yindex,
- {{dtype}}_t yfill):
- """
- Binary operator on BlockIndex objects with fill values
- """
- cdef:
- BlockIndex out_index
- Py_ssize_t xi = 0, yi = 0, out_i = 0 # fp buf indices
- int32_t xbp = 0, ybp = 0 # block positions
- int32_t xloc, yloc
- Py_ssize_t xblock = 0, yblock = 0 # block numbers
- {{dtype}}_t[:] x, y
- ndarray[{{rdtype}}_t, ndim=1] out
- # to suppress Cython warning
- x = x_
- y = y_
- out_index = xindex.make_union(yindex)
- out = np.empty(out_index.npoints, dtype=np.{{rdtype}})
- # Wow, what a hack job. Need to do something about this
- # walk the two SparseVectors, adding matched locations...
- for out_i in range(out_index.npoints):
- if yblock == yindex.nblocks:
- # use y fill value
- out[out_i] = {{(opname, 'x[xi]', 'yfill', dtype) | get_op}}
- xi += 1
- # advance x location
- xbp += 1
- if xbp == xindex.lenbuf[xblock]:
- xblock += 1
- xbp = 0
- continue
- if xblock == xindex.nblocks:
- # use x fill value
- out[out_i] = {{(opname, 'xfill', 'y[yi]', dtype) | get_op}}
- yi += 1
- # advance y location
- ybp += 1
- if ybp == yindex.lenbuf[yblock]:
- yblock += 1
- ybp = 0
- continue
- yloc = yindex.locbuf[yblock] + ybp
- xloc = xindex.locbuf[xblock] + xbp
- # each index in the out_index had to come from either x, y, or both
- if xloc == yloc:
- out[out_i] = {{(opname, 'x[xi]', 'y[yi]', dtype) | get_op}}
- xi += 1
- yi += 1
- # advance both locations
- xbp += 1
- if xbp == xindex.lenbuf[xblock]:
- xblock += 1
- xbp = 0
- ybp += 1
- if ybp == yindex.lenbuf[yblock]:
- yblock += 1
- ybp = 0
- elif xloc < yloc:
- # use y fill value
- out[out_i] = {{(opname, 'x[xi]', 'yfill', dtype) | get_op}}
- xi += 1
- # advance x location
- xbp += 1
- if xbp == xindex.lenbuf[xblock]:
- xblock += 1
- xbp = 0
- else:
- # use x fill value
- out[out_i] = {{(opname, 'xfill', 'y[yi]', dtype) | get_op}}
- yi += 1
- # advance y location
- ybp += 1
- if ybp == yindex.lenbuf[yblock]:
- yblock += 1
- ybp = 0
- return out, out_index, {{(opname, 'xfill', 'yfill', dtype) | get_op}}
- {{if opname == "pow"}}
- @cython.cpow(True) # Cython 3 matches Python pow, which isn't what we want here
- {{endif}}
- @cython.wraparound(False)
- @cython.boundscheck(False)
- cdef tuple int_op_{{opname}}_{{dtype}}({{dtype}}_t[:] x_,
- IntIndex xindex,
- {{dtype}}_t xfill,
- {{dtype}}_t[:] y_,
- IntIndex yindex,
- {{dtype}}_t yfill):
- cdef:
- IntIndex out_index
- Py_ssize_t xi = 0, yi = 0, out_i = 0 # fp buf indices
- int32_t xloc, yloc
- int32_t[:] xindices, yindices, out_indices
- {{dtype}}_t[:] x, y
- ndarray[{{rdtype}}_t, ndim=1] out
- # suppress Cython compiler warnings due to inlining
- x = x_
- y = y_
- # need to do this first to know size of result array
- out_index = xindex.make_union(yindex)
- out = np.empty(out_index.npoints, dtype=np.{{rdtype}})
- xindices = xindex.indices
- yindices = yindex.indices
- out_indices = out_index.indices
- # walk the two SparseVectors, adding matched locations...
- for out_i in range(out_index.npoints):
- if xi == xindex.npoints:
- # use x fill value
- out[out_i] = {{(opname, 'xfill', 'y[yi]', dtype) | get_op}}
- yi += 1
- continue
- if yi == yindex.npoints:
- # use y fill value
- out[out_i] = {{(opname, 'x[xi]', 'yfill', dtype) | get_op}}
- xi += 1
- continue
- xloc = xindices[xi]
- yloc = yindices[yi]
- # each index in the out_index had to come from either x, y, or both
- if xloc == yloc:
- out[out_i] = {{(opname, 'x[xi]', 'y[yi]', dtype) | get_op}}
- xi += 1
- yi += 1
- elif xloc < yloc:
- # use y fill value
- out[out_i] = {{(opname, 'x[xi]', 'yfill', dtype) | get_op}}
- xi += 1
- else:
- # use x fill value
- out[out_i] = {{(opname, 'xfill', 'y[yi]', dtype) | get_op}}
- yi += 1
- return out, out_index, {{(opname, 'xfill', 'yfill', dtype) | get_op}}
- cpdef sparse_{{opname}}_{{dtype}}({{dtype}}_t[:] x,
- SparseIndex xindex, {{dtype}}_t xfill,
- {{dtype}}_t[:] y,
- SparseIndex yindex, {{dtype}}_t yfill):
- if isinstance(xindex, BlockIndex):
- return block_op_{{opname}}_{{dtype}}(x, xindex.to_block_index(), xfill,
- y, yindex.to_block_index(), yfill)
- elif isinstance(xindex, IntIndex):
- return int_op_{{opname}}_{{dtype}}(x, xindex.to_int_index(), xfill,
- y, yindex.to_int_index(), yfill)
- else:
- raise NotImplementedError
- {{endfor}}
|