agd.Eikonal.HFM_CUDA.Eigh
1# Copyright 2022 Jean-Marie Mirebeau, ENS Paris-Saclay, CNRS, University Paris-Saclay 2# Distributed WITHOUT ANY WARRANTY. Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 3 4import os 5import numpy as np 6import cupy as cp 7 8from . import cupy_module_helper 9from .cupy_module_helper import SetModuleConstant 10 11def eigh(m,quaternion=False,flatsym=None,use_numpy=False): 12 """ 13 Computes the eigenvalues of the symmetric matrix m, of dimension d<=3. 14 The cuda routines are likely more accurate, but they are often slow and memory 15 intensive for large arrays of small matrices. 16 Input : 17 - m : array of shape (n1,...,nk,d,d) symmetric w.r.t the last two entries, or of 18 shape (n1,...,nk, d(d+1)/2) if flatsym==True 19 - flatsym (bool, default: autodetect): format for the symmetric matrices, see above. 20 - quaternion (bool): format specifier for the eigenvectors, see below 21 - use_numpy : use the numpy.linalg.eigh routine (calls cuda) 22 Output : 23 - λ : array of shape (n1,...,nk,d). The eigenvalues of m, sorted increasingly. 24 - v : the eigenvectors of m, in the following format 25 quaternion == false : shape (n1,...,nk,d,d) 26 - true : export the eigenvectors compactly using the quaternion format 27 (d=3), or omitting the second eigenvector (d=2). 28 """ 29 float_t = np.float32 30 int_t = np.int32 31 block_size = 1024 32 33 if flatsym is None: flatsym = not(m.ndim>=2 and m.shape[-1]==m.shape[-2]<=3) 34 shape = m.shape[:(-1 if flatsym else -2)] 35 size = np.prod(shape,dtype=int) 36 d = int(np.floor(np.sqrt(2*m.shape[-1]))) if flatsym else m.shape[-1] 37 38 assert m.shape == ( (*shape,(d*(d+1))//2) if flatsym else (*shape,d,d) ) 39 assert isinstance(quaternion,bool) or quaternion is None 40 assert 1<=d<=3 41 vshape = (*shape,{1:1,2:2,3:4}[d]) if quaternion else (*shape,d,d) 42 43 if d==1: # Trivial 1D case 44 v=np.broadcast_to(cp.ones(1,dtype=float_t),vshape) 45 λ = m.reshape((*shape,d)).astype(float_t) 46 return λ,v 47 if use_numpy: 48 from ... import Sphere 49 assert not flatsym 50 if quaternion is None: return np.linalg.eigvalsh(m) 51 λ,v = np.linalg.eigh(m) 52 if not quaternion: return λ,v 53 v[np.linalg.det(v)<0,:,-1] *= -1 # Turn v into a direct orthogonal basis 54 v = np.moveaxis(v,(-2,-1),(0,1)) 55 v = Sphere.sphere1_from_rotation2(v) if d==2 else Sphere.sphere3_from_rotation3(v) 56 v = np.moveaxis(v,0,-1) 57 return λ,v 58 if not flatsym: 59 m = cp.stack([m[...,i,j] for i in range(d) for j in range(i+1)], axis=-1) 60 m = cp.ascontiguousarray(m,dtype=float_t) 61 62 traits = { 63 'Scalar':float_t, 64 'Int':int_t, 65 'quaternion_macro':bool(quaternion), 66 'ndim_macro':d, 67 } 68 69 cuda_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"cuda") 70 date_modified = cupy_module_helper.getmtime_max(cuda_path) 71 source = cupy_module_helper.traits_header(traits) 72 73 source += [ 74 '#include "Kernel_Eigh.h"', 75 f"// Date cuda code last modified : {date_modified}"] 76 cuoptions = ("-default-device", f"-I {cuda_path}") 77 78 source = "\n".join(source) 79 module = cupy_module_helper.GetModule(source,cuoptions) 80 def setcst(*args): SetModuleConstant(module,*args) 81 setcst('size_tot',size,int_t) 82 83 λ = cp.empty((*shape,d),dtype=float_t) 84 grid_size = int(np.ceil(size/block_size)) 85 if quaternion is None: # Special value to get the eigenvalues alone 86 f = module.get_function('kernel_eigvalsh') 87 f((grid_size,),(block_size,),(m,λ)) 88 return λ 89 90 v = cp.empty(vshape,dtype=float_t) 91 f = module.get_function('kernel_eigh') 92 f((grid_size,),(block_size,),(m,λ,v)) 93 return λ,v 94 95def eigvalsh(m,flatsym=False): 96 """ 97 Computes the eigenvalues of the symmetric matrix m, of dimension d<=3. 98 The cuda routines are likely more accurate, but often slow and memory intensive. 99 Input : 100 - m : array of shape (n1,...,nk,d,d) symmetric w.r.t the last two entries, or of 101 shape (n1,...,nk, d(d+1)/2) if flatsym==True 102 - flatsym (bool): format for the symmetric matrices, see above 103 104 Output : 105 - λ : array of shape (n1,...,nk,d). The eigenvalues of m, sorted increasingly. 106 """ 107 return eigh(m,flatsym=flatsym,quaternion=None)
def
eigh(m, quaternion=False, flatsym=None, use_numpy=False):
12def eigh(m,quaternion=False,flatsym=None,use_numpy=False): 13 """ 14 Computes the eigenvalues of the symmetric matrix m, of dimension d<=3. 15 The cuda routines are likely more accurate, but they are often slow and memory 16 intensive for large arrays of small matrices. 17 Input : 18 - m : array of shape (n1,...,nk,d,d) symmetric w.r.t the last two entries, or of 19 shape (n1,...,nk, d(d+1)/2) if flatsym==True 20 - flatsym (bool, default: autodetect): format for the symmetric matrices, see above. 21 - quaternion (bool): format specifier for the eigenvectors, see below 22 - use_numpy : use the numpy.linalg.eigh routine (calls cuda) 23 Output : 24 - λ : array of shape (n1,...,nk,d). The eigenvalues of m, sorted increasingly. 25 - v : the eigenvectors of m, in the following format 26 quaternion == false : shape (n1,...,nk,d,d) 27 - true : export the eigenvectors compactly using the quaternion format 28 (d=3), or omitting the second eigenvector (d=2). 29 """ 30 float_t = np.float32 31 int_t = np.int32 32 block_size = 1024 33 34 if flatsym is None: flatsym = not(m.ndim>=2 and m.shape[-1]==m.shape[-2]<=3) 35 shape = m.shape[:(-1 if flatsym else -2)] 36 size = np.prod(shape,dtype=int) 37 d = int(np.floor(np.sqrt(2*m.shape[-1]))) if flatsym else m.shape[-1] 38 39 assert m.shape == ( (*shape,(d*(d+1))//2) if flatsym else (*shape,d,d) ) 40 assert isinstance(quaternion,bool) or quaternion is None 41 assert 1<=d<=3 42 vshape = (*shape,{1:1,2:2,3:4}[d]) if quaternion else (*shape,d,d) 43 44 if d==1: # Trivial 1D case 45 v=np.broadcast_to(cp.ones(1,dtype=float_t),vshape) 46 λ = m.reshape((*shape,d)).astype(float_t) 47 return λ,v 48 if use_numpy: 49 from ... import Sphere 50 assert not flatsym 51 if quaternion is None: return np.linalg.eigvalsh(m) 52 λ,v = np.linalg.eigh(m) 53 if not quaternion: return λ,v 54 v[np.linalg.det(v)<0,:,-1] *= -1 # Turn v into a direct orthogonal basis 55 v = np.moveaxis(v,(-2,-1),(0,1)) 56 v = Sphere.sphere1_from_rotation2(v) if d==2 else Sphere.sphere3_from_rotation3(v) 57 v = np.moveaxis(v,0,-1) 58 return λ,v 59 if not flatsym: 60 m = cp.stack([m[...,i,j] for i in range(d) for j in range(i+1)], axis=-1) 61 m = cp.ascontiguousarray(m,dtype=float_t) 62 63 traits = { 64 'Scalar':float_t, 65 'Int':int_t, 66 'quaternion_macro':bool(quaternion), 67 'ndim_macro':d, 68 } 69 70 cuda_path = os.path.join(os.path.dirname(os.path.realpath(__file__)),"cuda") 71 date_modified = cupy_module_helper.getmtime_max(cuda_path) 72 source = cupy_module_helper.traits_header(traits) 73 74 source += [ 75 '#include "Kernel_Eigh.h"', 76 f"// Date cuda code last modified : {date_modified}"] 77 cuoptions = ("-default-device", f"-I {cuda_path}") 78 79 source = "\n".join(source) 80 module = cupy_module_helper.GetModule(source,cuoptions) 81 def setcst(*args): SetModuleConstant(module,*args) 82 setcst('size_tot',size,int_t) 83 84 λ = cp.empty((*shape,d),dtype=float_t) 85 grid_size = int(np.ceil(size/block_size)) 86 if quaternion is None: # Special value to get the eigenvalues alone 87 f = module.get_function('kernel_eigvalsh') 88 f((grid_size,),(block_size,),(m,λ)) 89 return λ 90 91 v = cp.empty(vshape,dtype=float_t) 92 f = module.get_function('kernel_eigh') 93 f((grid_size,),(block_size,),(m,λ,v)) 94 return λ,v
Computes the eigenvalues of the symmetric matrix m, of dimension d<=3. The cuda routines are likely more accurate, but they are often slow and memory intensive for large arrays of small matrices. Input :
- m : array of shape (n1,...,nk,d,d) symmetric w.r.t the last two entries, or of shape (n1,...,nk, d(d+1)/2) if flatsym==True
- flatsym (bool, default: autodetect): format for the symmetric matrices, see above.
- quaternion (bool): format specifier for the eigenvectors, see below
- use_numpy : use the numpy.linalg.eigh routine (calls cuda) Output :
- λ : array of shape (n1,...,nk,d). The eigenvalues of m, sorted increasingly.
- v : the eigenvectors of m, in the following format
quaternion == false : shape (n1,...,nk,d,d)
- true : export the eigenvectors compactly using the quaternion format (d=3), or omitting the second eigenvector (d=2).
def
eigvalsh(m, flatsym=False):
96def eigvalsh(m,flatsym=False): 97 """ 98 Computes the eigenvalues of the symmetric matrix m, of dimension d<=3. 99 The cuda routines are likely more accurate, but often slow and memory intensive. 100 Input : 101 - m : array of shape (n1,...,nk,d,d) symmetric w.r.t the last two entries, or of 102 shape (n1,...,nk, d(d+1)/2) if flatsym==True 103 - flatsym (bool): format for the symmetric matrices, see above 104 105 Output : 106 - λ : array of shape (n1,...,nk,d). The eigenvalues of m, sorted increasingly. 107 """ 108 return eigh(m,flatsym=flatsym,quaternion=None)
Computes the eigenvalues of the symmetric matrix m, of dimension d<=3. The cuda routines are likely more accurate, but often slow and memory intensive. Input :
- m : array of shape (n1,...,nk,d,d) symmetric w.r.t the last two entries, or of shape (n1,...,nk, d(d+1)/2) if flatsym==True
- flatsym (bool): format for the symmetric matrices, see above
Output :
- λ : array of shape (n1,...,nk,d). The eigenvalues of m, sorted increasingly.