├── CHANGES.txt ├── MANIFEST.in ├── choldate ├── test │ ├── __init__.py │ └── test.py ├── __init__.py ├── _choldate.pxd └── _choldate.pyx ├── .gitignore ├── setup.py ├── bin └── example.py ├── README.md └── LICENSE.txt /CHANGES.txt: -------------------------------------------------------------------------------- 1 | v0.1.0, 2013-02-16 -- Initial release. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include *.txt 2 | recursive-include docs *.txt 3 | recursive-include cython * -------------------------------------------------------------------------------- /choldate/test/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Feb 15, 2013 3 | 4 | @author: jasonrudy 5 | ''' 6 | from test import * -------------------------------------------------------------------------------- /choldate/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'jasonrudy' 2 | #__all__ = ['choldate','choldate.test'] 3 | from ._choldate import cholupdate, choldowndate 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | choldate/build 2 | choldate/choldate.c 3 | choldate/*.so 4 | build 5 | *.egg-info/ 6 | 7 | .DS_Store 8 | 9 | .project 10 | 11 | .pydevproject 12 | 13 | choldate/__init__.pyc 14 | 15 | installTest.sh 16 | -------------------------------------------------------------------------------- /choldate/_choldate.pxd: -------------------------------------------------------------------------------- 1 | 2 | cimport numpy as np 3 | ctypedef np.float64_t FLOAT_t 4 | 5 | cpdef cholupdate(np.ndarray[FLOAT_t, ndim=2] R, np.ndarray[FLOAT_t, ndim=1] x) 6 | 7 | cpdef choldowndate(np.ndarray[FLOAT_t, ndim=2] R, np.ndarray[FLOAT_t, ndim=1] x) -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from distutils.core import setup 2 | from distutils.extension import Extension 3 | from Cython.Distutils import build_ext 4 | import numpy 5 | from Cython.Build import cythonize 6 | 7 | setup( 8 | name='choldate', 9 | version='0.1.0', 10 | packages=['choldate','choldate.test'], 11 | cmdclass = {'build_ext': build_ext}, 12 | ext_modules = cythonize([Extension("choldate._choldate", ["choldate/_choldate.pyx"],include_dirs = [numpy.get_include()]) 13 | ]), requires=['numpy','cython'] 14 | 15 | ) 16 | -------------------------------------------------------------------------------- /bin/example.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Feb 15, 2013 3 | 4 | @author: jasonrudy 5 | ''' 6 | from choldate import cholupdate, choldowndate 7 | import numpy 8 | 9 | #Create a random positive definite matrix, V 10 | numpy.random.seed(1) 11 | X = numpy.random.normal(size=(100,10)) 12 | V = numpy.dot(X.transpose(),X) 13 | 14 | #Calculate the upper Cholesky factor, R 15 | R = numpy.linalg.cholesky(V).transpose() 16 | 17 | #Create a random update vector, u 18 | u = numpy.random.normal(size=R.shape[0]) 19 | 20 | #Calculate the updated positive definite matrix, V1, and its Cholesky factor, R1 21 | V1 = V + numpy.outer(u,u) 22 | R1 = numpy.linalg.cholesky(V1).transpose() 23 | 24 | #The following is equivalent to the above 25 | R1_ = R.copy() 26 | cholupdate(R1_,u.copy()) 27 | assert(numpy.all((R1 - R1_)**2 < 1e-16)) 28 | 29 | #And downdating is the inverse of updating 30 | R_ = R1.copy() 31 | choldowndate(R_,u.copy()) 32 | assert(numpy.all((R - R_)**2 < 1e-16)) -------------------------------------------------------------------------------- /choldate/test/test.py: -------------------------------------------------------------------------------- 1 | __author__ = 'jasonrudy' 2 | 3 | import unittest 4 | import numpy 5 | from choldate import cholupdate, choldowndate 6 | 7 | 8 | 9 | class TestCholdate(unittest.TestCase): 10 | 11 | def setUp(self): 12 | numpy.random.seed(1) 13 | self.X = numpy.random.normal(size=(100,10)) 14 | 15 | def test_update(self): 16 | V = numpy.dot(self.X.transpose(),self.X) 17 | R = numpy.linalg.cholesky(V).transpose() 18 | u = numpy.random.normal(size=R.shape[0]) 19 | V1 = V + numpy.outer(u,u) 20 | R1 = numpy.linalg.cholesky(V1).transpose() 21 | R_ = R.copy() 22 | u_ = u.copy() 23 | cholupdate(R_,u_) 24 | self.assertAlmostEqual(numpy.max((numpy.abs(R_) - numpy.abs(R1))**2),0) 25 | 26 | def test_downdate(self): 27 | V = numpy.dot(self.X.transpose(),self.X) 28 | R = numpy.linalg.cholesky(V).transpose() 29 | u = numpy.random.normal(size=R.shape[0]) 30 | V1 = V + numpy.outer(u,u) 31 | R1 = numpy.linalg.cholesky(V1).transpose() 32 | R_ = R1.copy() 33 | u_ = u.copy() 34 | choldowndate(R_,u_) 35 | self.assertAlmostEqual(numpy.max((numpy.abs(R_) - numpy.abs(R))**2),0) 36 | 37 | 38 | if __name__ == '__main__': 39 | unittest.main() 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | choldate 2 | ======== 3 | 4 | Somewhat fast updating and downdating of Cholesky factors in Python 5 | 6 | ##installation 7 | 8 | Install from GitHub: `pip install git+git://github.com/jcrudy/choldate.git` 9 | 10 | Or, clone the GitHub repository and install from source, e.g., 11 | 12 | 1. `git clone git://github.com/jcrudy/choldate.git` 13 | 2. `cd choldate; python setup.py install` 14 | 15 | I am new to packaging Python modules. If it doesn't work on your system, get in touch and I'll try to help you. 16 | 17 | ##usage 18 | ```python 19 | from choldate import cholupdate, choldowndate 20 | import numpy 21 | 22 | #Create a random positive definite matrix, V 23 | numpy.random.seed(1) 24 | X = numpy.random.normal(size=(100,10)) 25 | V = numpy.dot(X.transpose(),X) 26 | 27 | #Calculate the upper Cholesky factor, R 28 | R = numpy.linalg.cholesky(V).transpose() 29 | 30 | #Create a random update vector, u 31 | u = numpy.random.normal(size=R.shape[0]) 32 | 33 | #Calculate the updated positive definite matrix, V1, and its Cholesky factor, R1 34 | V1 = V + numpy.outer(u,u) 35 | R1 = numpy.linalg.cholesky(V1).transpose() 36 | 37 | #The following is equivalent to the above 38 | R1_ = R.copy() 39 | cholupdate(R1_,u.copy()) 40 | assert(numpy.all((R1 - R1_)**2 < 1e-16)) 41 | 42 | #And downdating is the inverse of updating 43 | R_ = R1.copy() 44 | choldowndate(R_,u.copy()) 45 | assert(numpy.all((R - R_)**2 < 1e-16)) 46 | ``` 47 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013, Jason Rudy 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | Neither the name of Clinicast, Inc., nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /choldate/_choldate.pyx: -------------------------------------------------------------------------------- 1 | #cython: cdivision=True 2 | #cython: boundscheck=True 3 | #cython: wraparound=True 4 | 5 | from libc.math cimport sqrt 6 | from libc.math cimport abs as cabs 7 | import numpy as np 8 | 9 | cdef inline FLOAT_t hypot(FLOAT_t x,FLOAT_t y): 10 | cdef FLOAT_t t 11 | x = cabs(x) 12 | y = cabs(y) 13 | t = x if x < y else y 14 | x = x if x > y else y 15 | t = t/x 16 | return x*sqrt(1+t*t) 17 | 18 | cdef inline FLOAT_t rypot(FLOAT_t x,FLOAT_t y): 19 | cdef FLOAT_t t 20 | x = cabs(x) 21 | y = cabs(y) 22 | t = x if x < y else y 23 | x = x if x > y else y 24 | t = t/x 25 | return x*sqrt(1-t*t) 26 | 27 | cpdef cholupdate(np.ndarray[FLOAT_t, ndim=2] R, np.ndarray[FLOAT_t, ndim=1] x): 28 | ''' 29 | Update the upper triangular Cholesky factor R with the rank 1 addition 30 | implied by x such that: 31 | R_'R_ = R'R + outer(x,x) 32 | where R_ is the upper triangular Cholesky factor R after updating. Note 33 | that both x and R are modified in place. 34 | ''' 35 | cdef unsigned int p 36 | cdef unsigned int k 37 | cdef unsigned int i 38 | cdef FLOAT_t r 39 | cdef FLOAT_t c 40 | cdef FLOAT_t s 41 | cdef FLOAT_t a 42 | cdef FLOAT_t b 43 | 44 | p = len(x) 45 | for k in range(p): 46 | r = hypot(R[k,k], x[k]) 47 | c = r / R[k,k] 48 | s = x[k] / R[k,k] 49 | R[k,k] = r 50 | #TODO: Use BLAS instead of inner for loop 51 | for i in range((k+1),p): 52 | R[k,i] = (R[k,i] + s*x[i]) / c 53 | x[i] = c * x[i] - s * R[k,i] 54 | 55 | cpdef choldowndate(np.ndarray[FLOAT_t, ndim=2] R, np.ndarray[FLOAT_t, ndim=1] x): 56 | ''' 57 | Update the upper triangular Cholesky factor R with the rank 1 subtraction 58 | implied by x such that: 59 | R_'R_ = R'R - outer(x,x) 60 | where R_ is the upper triangular Cholesky factor R after updating. Note 61 | that both x and R are modified in place. 62 | ''' 63 | cdef unsigned int p 64 | cdef unsigned int k 65 | cdef unsigned int i 66 | cdef FLOAT_t r 67 | cdef FLOAT_t c 68 | cdef FLOAT_t s 69 | 70 | p = len(x) 71 | for k in range(p): 72 | r = rypot(R[k,k], x[k]) 73 | c = r / R[k,k] 74 | s = x[k] / R[k,k] 75 | R[k,k] = r 76 | #TODO: Use BLAS instead of inner for loop 77 | for i in range((k+1),p): 78 | R[k,i] = (R[k,i] - s*x[i]) / c 79 | x[i] = c * x[i] - s * R[k,i] 80 | --------------------------------------------------------------------------------