Path: blob/master/sage/modules/vector_double_dense.pyx
4036 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)7120.0713714Return values are in ``RDF``, or an integer when ``p = 0``. ::715716sage: v = vector(RDF, [1,2,4,8])717sage: v.norm() in RDF718True719sage: v.norm(p=0) in ZZ720True721722Improper values of ``p`` are caught. ::723724sage: w = vector(CDF, [-1,0,1])725sage: w.norm(p='junk')726Traceback (most recent call last):727...728ValueError: vector norm 'p' must be +/- infinity or a real number, not junk729"""730global numpy731if numpy is None:732import numpy733import sage.rings.infinity734import sage.rings.integer735if p == sage.rings.infinity.Infinity:736p = numpy.inf737elif p == -sage.rings.infinity.Infinity:738p = -numpy.inf739else:740try:741p = RDF(p)742except:743raise ValueError("vector norm 'p' must be +/- infinity or a real number, not %s" % p)744n = numpy.linalg.norm(self._vector_numpy, ord=p)745# p = 0 returns integer *count* of non-zero entries746return RDF(n)747748749#############################750# statistics751#############################752def mean(self):753"""754Calculate the arithmetic mean of the vector.755756EXAMPLE:757sage: v = vector(RDF, range(9))758sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])759sage: v.mean()7604.0761sage: w.mean()7624.0 + 5.0*I763"""764global numpy765if numpy is None:766import numpy767return self._sage_dtype(numpy.mean(self._vector_numpy))768769def variance(self, population=True):770"""771Calculate the variance of entries of the vector.772773INPUT:774population -- If False, calculate the sample variance.775776EXAMPLE:777sage: v = vector(RDF, range(9))778sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])779sage: v.variance()7807.5781sage: v.variance(population=False)7826.66666666667783sage: w.variance()78415.0785sage: w.variance(population=False)78613.3333333333787"""788global numpy789if numpy is None:790import numpy791792if population is True:793return self._sage_dtype(numpy.var(self._vector_numpy, ddof=1))794else:795return self._sage_dtype(numpy.var(self._vector_numpy, ddof=0))796797def standard_deviation(self, population=True):798"""799Calculate the standard deviation of entries of the vector.800801INPUT:802population -- If False, calculate the sample standard deviation.803804EXAMPLES:805sage: v = vector(RDF, range(9))806sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])807sage: v.standard_deviation()8082.73861278753809sage: v.standard_deviation(population=False)8102.58198889747811sage: w.standard_deviation()8123.87298334621813sage: w.standard_deviation(population=False)8143.6514837167815"""816global numpy817if numpy is None:818import numpy819820if population is True:821return self._sage_dtype(numpy.std(self._vector_numpy, ddof=1))822else:823return self._sage_dtype(numpy.std(self._vector_numpy, ddof=0))824825826def stats_kurtosis(self):827"""828Compute the kurtosis of a dataset.829830Kurtosis is the fourth central moment divided by the square of831the variance. Since we use Fisher's definition, 3.0 is832subtracted from the result to give 0.0 for a normal833distribution. (Paragraph from the scipy.stats docstring.)834835EXAMPLE:836sage: v = vector(RDF, range(9))837sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])838sage: v.stats_kurtosis()839-1.23840sage: w.stats_kurtosis()841-1.23842"""843global scipy844if scipy is None:845import scipy846import scipy.stats847return self._sage_dtype(scipy.stats.kurtosis(self._vector_numpy))848849def prod(self):850"""851Return the product of the entries of self.852853EXAMPLES:854sage: v = vector(RDF, range(9))855sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])856sage: v.prod()8570.0858sage: w.prod()85957204225.0*I860"""861return self._sage_dtype(self._vector_numpy.prod())862863def sum(self):864"""865Return the sum of the entries of self.866867EXAMPLES:868sage: v = vector(RDF, range(9))869sage: w = vector(CDF, [k+(9-k)*I for k in range(9)])870sage: v.sum()87136.0872sage: w.sum()87336.0 + 45.0*I874"""875return self._sage_dtype(self._vector_numpy.sum())876877878879