Path: blob/master/src/sage/modules/vector_double_dense.pyx
8815 views
r"""1Dense vectors using a NumPy backend. This serves as a base class for2dense vectors over Real Double Field and Complex Double Field34EXAMPLES:5sage: v = vector(CDF,[(1,-1), (2,pi), (3,5)])6sage: v7(1.0 - 1.0*I, 2.0 + 3.14159265359*I, 3.0 + 5.0*I)8sage: type(v)9<type 'sage.modules.vector_complex_double_dense.Vector_complex_double_dense'>10sage: parent(v)11Vector space of dimension 3 over Complex Double Field12sage: v[0] = 513sage: v14(5.0, 2.0 + 3.14159265359*I, 3.0 + 5.0*I)15sage: loads(dumps(v)) == v16True17sage: v = vector(RDF, [1,2,3,4]); v18(1.0, 2.0, 3.0, 4.0)19sage: loads(dumps(v)) == v20True2122AUTHORS:23-- Jason Grout, Oct 2008: switch to numpy backend, factored out24Vector_double_dense class25-- Josh Kantor26-- William Stein27"""2829###############################################################################30# Sage: System for Algebra and Geometry Experimentation31# Copyright (C) 2006 William Stein <[email protected]>32# Distributed under the terms of the GNU General Public License (GPL)33# http://www.gnu.org/licenses/34###############################################################################3536cimport free_module_element37import free_module_element3839from sage.structure.element cimport Element, ModuleElement, RingElement, Vector4041from sage.rings.real_double import RDF4243from sage.rings.complex_double import CDF44from sage.rings.complex_double cimport ComplexDoubleElement, new_ComplexDoubleElement4546cimport numpy as cnumpy4748numpy = None49scipy = None5051# This is for the NumPy C API to work52cnumpy.import_array()5354cdef class Vector_double_dense(free_module_element.FreeModuleElement):55"""56Base class for vectors over the Real Double Field and the Complex57Double Field. These are supposed to be fast vector operations58using C doubles. Most operations are implemented using numpy which59will call the underlying BLAS, if needed, on the system.6061This class cannot be instantiated on its own. The numpy vector62creation depends on several variables that are set in the63subclasses.6465EXAMPLES:66sage: v = vector(RDF, [1,2,3,4]); v67(1.0, 2.0, 3.0, 4.0)68sage: v*v6930.070"""71def __cinit__(self, parent, entries, coerce=True, copy=True):72"""73Set up a new vector7475EXAMPLE:76sage: v = vector(RDF, range(3))77sage: v.__new__(v.__class__, v.parent(), [1,1,1]) # random output78(3.00713073107e-261, 3.06320700422e-322, 2.86558074588e-322)79sage: v = vector(CDF, range(3))80sage: v.__new__(v.__class__, v.parent(), [1,1,1]) # random output81(-2.26770549592e-39, 5.1698223615e-252*I, -5.9147262602e-62 + 4.63145528786e-258*I)82"""83self._is_mutable = 184self._degree = parent.degree()85self._parent = parent8687cdef Vector_double_dense _new(self, cnumpy.ndarray vector_numpy):88"""89Return a new vector with same parent as self.90"""91cdef Vector_double_dense v92v = self.__class__.__new__(self.__class__,self._parent,None,None,None)93v._is_mutable = 194v._parent = self._parent95v._degree = self._parent.degree()9697v._vector_numpy = vector_numpy98return v99100def __create_vector__(self):101"""102Create a new uninitialized numpy array to hold the data for the class.103104This function assumes that self._numpy_dtypeint and105self._nrows and self._ncols have already been initialized.106107EXAMPLE:108In this example, we throw away the current array and make a109new uninitialized array representing the data for the class.110sage: a=vector(RDF, range(9))111sage: a.__create_vector__()112"""113cdef cnumpy.npy_intp dims[1]114dims[0] = self._degree115self._vector_numpy = cnumpy.PyArray_SimpleNew(1, dims, self._numpy_dtypeint)116return117118cdef bint is_dense_c(self):119"""120Return True (i.e., 1) if self is dense.121"""122return 1123124cdef bint is_sparse_c(self):125"""126Return True (i.e., 1) if self is sparse.127"""128return 0129130131def __copy__(self, copy=True):132"""133Return a copy of the vector134135EXAMPLE:136sage: a = vector(RDF, range(9))137sage: a == copy(a)138True139"""140if self._degree == 0:141return self142from copy import copy143return self._new(copy(self._vector_numpy))144145def __init__(self, parent, entries, coerce = True, copy = True):146"""147Fill the vector with entries.148149The numpy array must have already been allocated.150151EXAMPLES:152sage: vector(RDF, range(9))153(0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0)154sage: vector(CDF, 5)155(0.0, 0.0, 0.0, 0.0, 0.0)156157TESTS:158sage: vector(CDF, 0)159()160sage: vector(RDF, 0)161()162sage: vector(CDF, 4)163(0.0, 0.0, 0.0, 0.0)164sage: vector(RDF, 4)165(0.0, 0.0, 0.0, 0.0)166sage: vector(CDF, [CDF(1+I)*j for j in range(4)])167(0.0, 1.0 + 1.0*I, 2.0 + 2.0*I, 3.0 + 3.0*I)168sage: vector(RDF, 4, range(4))169(0.0, 1.0, 2.0, 3.0)170171sage: V = RDF^2172sage: V._element_class(V, 5)173Traceback (most recent call last):174...175TypeError: entries must be a list or 0176sage: V._element_class(V, 0)177(0.0, 0.0)178"""179cdef Py_ssize_t i,j180if isinstance(entries,(tuple, list)):181if len(entries)!=self._degree:182raise TypeError("entries has wrong length")183184if coerce:185for i from 0<=i<self._degree:186self.set_unsafe(i,self._python_dtype(entries[i]))187else:188for i from 0<=i<self._degree:189self.set_unsafe(i,entries[i])190191elif isinstance(entries, cnumpy.ndarray):192self._replace_self_with_numpy(entries)193else:194cnumpy.PyArray_FILLWBYTE(self._vector_numpy, 0)195if entries is None:196print "entries is None"197return198else:199try:200z = self._python_dtype(entries)201except TypeError:202raise TypeError("cannot coerce entry to type %s"%self._python_dtype)203if z != 0:204raise TypeError("entries must be a list or 0")205else:206# Set all entries to z=0.207for i from 0<=i<self._degree:208self.set_unsafe(i,z)209210def __len__(self):211"""212Return the length of the vector.213214EXAMPLE:215sage: v = vector(RDF, 5); v216(0.0, 0.0, 0.0, 0.0, 0.0)217sage: len(v)2185219"""220return self._degree221222def __setitem__(self, i, object value):223"""224Set the `i`-th entry of self.225226EXAMPLES:227sage: v = vector(CDF, [1,CDF(3,2), -1]); v228(1.0, 3.0 + 2.0*I, -1.0)229sage: v[1] = 2230sage: v[-1] = I231sage: v[5] = 2232Traceback (most recent call last):233...234IndexError: index out of range235sage: v236(1.0, 2.0, 1.0*I)237sage: v[1:3] = [1, 1]; v238(1.0, 1.0, 1.0)239"""240if not self._is_mutable:241raise ValueError("vector is immutable; please change a copy instead (use copy())")242cdef Py_ssize_t k, d, n243if isinstance(i, slice):244start, stop = i.start, i.stop245d = self.degree()246R = self.base_ring()247n = 0248for k from start <= k < stop:249if k >= d:250return251if k >= 0:252self[k] = R(value[n])253n = n + 1254else:255if i < 0:256i += self._degree257if i < 0 or i >= self._degree:258raise IndexError('index out of range')259self.set_unsafe(i, value)260261def __getitem__(self, i):262"""263Return the `i`-th entry of self.264265EXAMPLES:266sage: v = vector(CDF, [1,CDF(3,2), -1]); v267(1.0, 3.0 + 2.0*I, -1.0)268sage: v[1]2693.0 + 2.0*I270sage: v[-1]271-1.0272sage: v[5]273Traceback (most recent call last):274...275IndexError: index out of range276sage: v[-5]277Traceback (most recent call last):278...279IndexError: index out of range280sage: v[1:3]281(3.0 + 2.0*I, -1.0)282"""283if isinstance(i, slice):284start, stop, step = i.indices(len(self))285return free_module_element.vector(self.base_ring(), self.list()[start:stop])286else:287if i < 0:288i += self._degree289if i < 0 or i >= self._degree:290raise IndexError('index out of range')291return self.get_unsafe(i)292293cdef set_unsafe(self, Py_ssize_t i, object value):294"""295Set the ith entry to value without any bounds checking,296mutability checking, etc.297"""298# We assume that Py_ssize_t is the same as npy_intp299300# We must patch the ndarrayobject.h file so that the SETITEM301# macro does not have a semicolon at the end for this to work.302# Cython wraps the macro in a function that converts the303# returned int to a python object, which leads to compilation304# errors because after preprocessing you get something that305# looks like "););". This is bug306# http://scipy.org/scipy/numpy/ticket/918307308# We call the self._python_dtype function on the value since309# numpy does not know how to deal with complex numbers other310# than the built-in complex number type.311cdef int status312status = cnumpy.PyArray_SETITEM(self._vector_numpy,313cnumpy.PyArray_GETPTR1(self._vector_numpy, i),314self._python_dtype(value))315#TODO: Throw an error if status == -1316317318cdef get_unsafe(self, Py_ssize_t i):319"""320Get the (i,j) entry without any bounds checking, etc.321"""322# We assume that Py_ssize_t is the same as npy_intp323return self._sage_dtype(cnumpy.PyArray_GETITEM(self._vector_numpy,324cnumpy.PyArray_GETPTR1(self._vector_numpy, i)))325326327cpdef ModuleElement _add_(self, ModuleElement right):328"""329Add two vectors together.330331EXAMPLES:332sage: A = vector(RDF, range(3))333sage: A+A334(0.0, 2.0, 4.0)335"""336if self._degree == 0:337from copy import copy338return copy(self)339340cdef Vector_double_dense _right, _left341_right = right342_left = self343344return self._new(_left._vector_numpy + _right._vector_numpy)345346cpdef ModuleElement _sub_(self, ModuleElement right):347"""348Return self - right349350EXAMPLES:351sage: A = vector(RDF, range(3))352sage: (A-A).is_zero()353True354"""355if self._degree == 0:356from copy import copy357return copy(self)358359cdef Vector_double_dense _right, _left360_right = right361_left = self362363return self._new(_left._vector_numpy - _right._vector_numpy)364365cpdef Element _dot_product_(self, Vector right):366"""367Dot product of self and right.368369EXAMPLES:370sage: v = vector(RDF, [1,2,3]); w = vector(RDF, [2, 4, -3])371sage: v*w3721.0373sage: w*v3741.0375"""376if not right.parent() == self.parent():377right = self.parent().ambient_module()(right)378if self._degree == 0:379from copy import copy380return copy(self)381382cdef Vector_double_dense _right, _left383_right = right384_left = self385386global scipy387if scipy is None:388import scipy389return self._sage_dtype(scipy.dot(_left._vector_numpy, _right._vector_numpy))390391cpdef Vector _pairwise_product_(self, Vector right):392"""393Return the component-wise product of self and right.394395EXAMPLES:396sage: v = vector(CDF, [1,2,3]); w = vector(CDF, [2, 4, -3])397sage: v.pairwise_product(w)398(2.0, 8.0, -9.0)399"""400if not right.parent() == self.parent():401right = self.parent().ambient_module()(right)402403if self._degree == 0:404from copy import copy405return copy(self)406407cdef Vector_double_dense _right, _left408_right = right409_left = self410411return self._new(_left._vector_numpy * _right._vector_numpy)412413cpdef ModuleElement _rmul_(self, RingElement left):414"""415Multiply a scalar and vector416417EXAMPLE:418sage: v = vector(CDF, range(3))419sage: 3*v420(0.0, 3.0, 6.0)421"""422if self._degree == 0:423from copy import copy424return copy(self)425426return self._new(self._python_dtype(left)*self._vector_numpy)427428429cpdef ModuleElement _lmul_(self, RingElement right):430"""431Multiply a scalar and vector432433EXAMPLE:434sage: v = vector(CDF, range(3))435sage: v*3436(0.0, 3.0, 6.0)437"""438if self._degree == 0:439from copy import copy440return copy(self)441442return self._new(self._vector_numpy*self._python_dtype(right))443444445def inv_fft(self,algorithm="radix2", inplace=False):446"""447This performs the inverse fast fourier transform on the vector.448449The fourier transform can be done in place using the keyword450inplace=True451452This will be fastest if the vector's length is a power of 2.453454EXAMPLES:455sage: v = vector(CDF,[1,2,3,4])456sage: w = v.fft()457sage: max(v - w.inv_fft()) < 1e-12458True459"""460return self.fft(direction="backward",algorithm=algorithm,inplace=inplace)461462def fft(self, direction = "forward", algorithm = "radix2", inplace=False):463"""464This performs a fast fourier transform on the vector.465466INPUT:467direction -- 'forward' (default) or 'backward'468469The algorithm and inplace arguments are ignored.470471This function is fastest if the vector's length is a power of 2.472473EXAMPLES:474sage: v = vector(CDF,[1+2*I,2,3*I,4])475sage: v.fft()476(7.0 + 5.0*I, 1.0 + 1.0*I, -5.0 + 5.0*I, 1.0 - 3.0*I)477sage: v.fft(direction='backward')478(1.75 + 1.25*I, 0.25 - 0.75*I, -1.25 + 1.25*I, 0.25 + 0.25*I)479sage: v.fft().fft(direction='backward')480(1.0 + 2.0*I, 2.0, 3.0*I, 4.0)481sage: v.fft().parent()482Vector space of dimension 4 over Complex Double Field483sage: v.fft(inplace=True)484sage: v485(7.0 + 5.0*I, 1.0 + 1.0*I, -5.0 + 5.0*I, 1.0 - 3.0*I)486487sage: v = vector(RDF,4,range(4)); v488(0.0, 1.0, 2.0, 3.0)489sage: v.fft()490(6.0, -2.0 + 2.0*I, -2.0, -2.0 - 2.0*I)491sage: v.fft(direction='backward')492(1.5, -0.5 - 0.5*I, -0.5, -0.5 + 0.5*I)493sage: v.fft().fft(direction='backward')494(0.0, 1.0, 2.0, 3.0)495sage: v.fft().parent()496Vector space of dimension 4 over Complex Double Field497sage: v.fft(inplace=True)498Traceback (most recent call last):499...500ValueError: inplace can only be True for CDF vectors501"""502if direction not in ('forward', 'backward'):503raise ValueError("direction must be either 'forward' or 'backward'")504505if self._degree == 0:506return self507global scipy508if scipy is None:509import scipy510import scipy.fftpack511512if inplace:513if self._sage_dtype is not CDF:514raise ValueError("inplace can only be True for CDF vectors")515if direction == 'forward':516self._vector_numpy = scipy.fftpack.fft(self._vector_numpy, overwrite_x = True)517else:518self._vector_numpy = scipy.fftpack.ifft(self._vector_numpy, overwrite_x = True)519else:520V = CDF**self._degree521from vector_complex_double_dense import Vector_complex_double_dense522if direction == 'forward':523return Vector_complex_double_dense(V, scipy.fft(self._vector_numpy))524else:525return Vector_complex_double_dense(V, scipy.ifft(self._vector_numpy))526527528def numpy(self):529"""530Return numpy array corresponding to this vector.531532EXAMPLES:533sage: v = vector(CDF,4,range(4))534sage: v.numpy()535array([ 0.+0.j, 1.+0.j, 2.+0.j, 3.+0.j])536sage: v = vector(CDF,0)537sage: v.numpy()538array([], dtype=complex128)539sage: v = vector(RDF,4,range(4))540sage: v.numpy()541array([ 0., 1., 2., 3.])542sage: v = vector(RDF,0)543sage: v.numpy()544array([], dtype=float64)545"""546from copy import copy547return copy(self._vector_numpy)548549cdef _replace_self_with_numpy(self,cnumpy.ndarray numpy_array):550"""551Replace the underlying numpy array with numpy_array.552"""553if self._degree == 0:554return555if numpy_array.ndim != 1 or len(self._vector_numpy) != numpy_array.shape[0]:556raise ValueError("vector lengths are not the same")557558self._vector_numpy = numpy_array.astype(self._numpy_dtype)559560561def complex_vector(self):562"""563Return the associated complex vector, i.e., this vector but with564coefficients viewed as complex numbers.565566EXAMPLES:567sage: v = vector(RDF,4,range(4)); v568(0.0, 1.0, 2.0, 3.0)569sage: v.complex_vector()570(0.0, 1.0, 2.0, 3.0)571sage: v = vector(RDF,0)572sage: v.complex_vector()573()574"""575return self.change_ring(CDF)576577578def zero_at(self, eps):579r"""580Returns a copy with small entries replaced by zeros.581582This is useful for modifying output from algorithms583which have large relative errors when producing zero584elements, e.g. to create reliable doctests.585586INPUT:587588- ``eps`` - cutoff value589590OUTPUT:591592A modified copy of the vector. Elements smaller than593or equal to ``eps`` are replaced with zeroes. For594complex vectors, the real and imaginary parts are595considered individually.596597598EXAMPLES::599600sage: v = vector(RDF, [1.0, 2.0, 10^-10, 3.0])601sage: v.zero_at(1e-8)602(1.0, 2.0, 0.0, 3.0)603sage: v.zero_at(1e-12)604(1.0, 2.0, 1e-10, 3.0)605606For complex numbers the real and imaginary parts are considered607separately. ::608609sage: w = vector(CDF, [10^-6 + 5*I, 5 + 10^-6*I, 5 + 5*I, 10^-6 + 10^-6*I])610sage: w.zero_at(1.0e-4)611(5.0*I, 5.0, 5.0 + 5.0*I, 0.0)612sage: w.zero_at(1.0e-8)613(1e-06 + 5.0*I, 5.0 + 1e-06*I, 5.0 + 5.0*I, 1e-06 + 1e-06*I)614"""615import sage.rings.complex_double616global numpy617cdef Vector_double_dense v618if numpy is None:619import numpy620eps = float(eps)621out = self._vector_numpy.copy()622if self._sage_dtype is sage.rings.complex_double.CDF:623out.real[numpy.abs(out.real) <= eps] = 0624out.imag[numpy.abs(out.imag) <= eps] = 0625else:626out[numpy.abs(out) <= eps] = 0627v = self._new(out)628return v629630631def norm(self, p=2):632r"""633Returns the norm (or related computations) of the vector.634635INPUT:636637- ``p`` - default: 2 - controls which norm is computed,638allowable values are any real number and positive and639negative infinity. See output discussion for specifics.640641OUTPUT:642643Returned value is a double precision floating point value644in ``RDF`` (or an integer when ``p=0``). The default value645of ``p = 2`` is the "usual" Euclidean norm. For other values:646647- ``p = Infinity`` or ``p = oo``: the maximum of the648absolute values of the entries, where the absolute value649of the complex number `a+bi` is `\sqrt{a^2+b^2}`.650- ``p = -Infinity`` or ``p = -oo``: the minimum of the651absolute values of the entries.652- ``p = 0`` : the number of nonzero entries in the vector.653- ``p`` is any other real number: for a vector `\vec{x}`654this method computes655656.. math::657658\left(\sum_i x_i^p\right)^{1/p}659660For ``p < 0`` this function is not a norm, but the above661computation may be useful for other purposes.662663ALGORITHM:664665Computation is performed by the ``norm()`` function of666the SciPy/NumPy library.667668EXAMPLES:669670First over the reals. ::671672sage: v = vector(RDF, range(9))673sage: v.norm()67414.28285685...675sage: v.norm(p=2)67614.28285685...677sage: v.norm(p=6)6788.744039097...679sage: v.norm(p=Infinity)6808.0681sage: v.norm(p=-oo)6820.0683sage: v.norm(p=0)6848.0685sage: v.norm(p=0.3)6864099.153615...687688And over the complex numbers. ::689690sage: w = vector(CDF, [3-4*I, 0, 5+12*I])691sage: w.norm()69213.9283882...693sage: w.norm(p=2)69413.9283882...695sage: w.norm(p=0)6962.0697sage: w.norm(p=4.2)69813.0555695...699sage: w.norm(p=oo)70013.0701702Negative values of ``p`` are allowed and will703provide the same computation as for positive values.704A zero entry in the vector will raise a warning and return705zero. ::706707sage: v = vector(CDF, range(1,10))708sage: v.norm(p=-3.2)7090.953760808...710sage: w = vector(CDF, [-1,0,1])711sage: w.norm(p=-1.6)712doctest:1992: RuntimeWarning: divide by zero encountered in power7130.0714715Return values are in ``RDF``, or an integer when ``p = 0``. ::716717sage: v = vector(RDF, [1,2,4,8])718sage: v.norm() in RDF719True720sage: v.norm(p=0) in ZZ721True722723Improper values of ``p`` are caught. ::724725sage: w = vector(CDF, [-1,0,1])726sage: w.norm(p='junk')727Traceback (most recent call last):728...729ValueError: vector norm 'p' must be +/- infinity or a real number, not junk730"""731global numpy732if numpy is None:733import numpy734import sage.rings.infinity735import sage.rings.integer736if p == sage.rings.infinity.Infinity:737p = numpy.inf738elif p == -sage.rings.infinity.Infinity:739p = -numpy.inf740else:741try:742p = RDF(p)743except Exception:744raise ValueError("vector norm 'p' must be +/- infinity or a real number, not %s" % p)745n = numpy.linalg.norm(self._vector_numpy, ord=p)746# p = 0 returns integer *count* of non-zero entries747return RDF(n)748749750#############################751# statistics752#############################753def mean(self):754"""755Calculate the arithmetic mean of the vector.756757EXAMPLE:758sage: v = vector(RDF, range(9))759sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])760sage: v.mean()7614.0762sage: w.mean()7634.0 + 5.0*I764"""765global numpy766if numpy is None:767import numpy768return self._sage_dtype(numpy.mean(self._vector_numpy))769770def variance(self, population=True):771"""772Calculate the variance of entries of the vector.773774INPUT:775population -- If False, calculate the sample variance.776777EXAMPLE:778sage: v = vector(RDF, range(9))779sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])780sage: v.variance()7817.5782sage: v.variance(population=False)7836.66666666667784sage: w.variance()78515.0786sage: w.variance(population=False)78713.3333333333788"""789global numpy790if numpy is None:791import numpy792793if population is True:794return self._sage_dtype(numpy.var(self._vector_numpy, ddof=1))795else:796return self._sage_dtype(numpy.var(self._vector_numpy, ddof=0))797798def standard_deviation(self, population=True):799"""800Calculate the standard deviation of entries of the vector.801802INPUT:803population -- If False, calculate the sample standard deviation.804805EXAMPLES:806sage: v = vector(RDF, range(9))807sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])808sage: v.standard_deviation()8092.73861278753810sage: v.standard_deviation(population=False)8112.58198889747812sage: w.standard_deviation()8133.87298334621814sage: w.standard_deviation(population=False)8153.6514837167816"""817global numpy818if numpy is None:819import numpy820821if population is True:822return self._sage_dtype(numpy.std(self._vector_numpy, ddof=1))823else:824return self._sage_dtype(numpy.std(self._vector_numpy, ddof=0))825826827def stats_kurtosis(self):828"""829Compute the kurtosis of a dataset.830831Kurtosis is the fourth central moment divided by the square of832the variance. Since we use Fisher's definition, 3.0 is833subtracted from the result to give 0.0 for a normal834distribution. (Paragraph from the scipy.stats docstring.)835836EXAMPLE:837sage: v = vector(RDF, range(9))838sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])839sage: v.stats_kurtosis()840-1.23841sage: w.stats_kurtosis()842-1.23843"""844global scipy845if scipy is None:846import scipy847import scipy.stats848return self._sage_dtype(scipy.stats.kurtosis(self._vector_numpy))849850def prod(self):851"""852Return the product of the entries of self.853854EXAMPLES:855sage: v = vector(RDF, range(9))856sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])857sage: v.prod()8580.0859sage: w.prod()86057204225.0*I861"""862return self._sage_dtype(self._vector_numpy.prod())863864def sum(self):865"""866Return the sum of the entries of self.867868EXAMPLES:869sage: v = vector(RDF, range(9))870sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])871sage: v.sum()87236.0873sage: w.sum()87436.0 + 45.0*I875"""876return self._sage_dtype(self._vector_numpy.sum())877878879880