mat_utils.txx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  1. /**
  2. * \file mat_utils.txx
  3. * \author Dhairya Malhotra, dhairya.malhotra@gmail.com
  4. * \date 2-11-2011
  5. * \brief This file contains BLAS and LAPACK wrapper functions.
  6. */
  7. #include <omp.h>
  8. #include <cmath>
  9. #include <cassert>
  10. #include <cstdlib>
  11. #include <algorithm>
  12. #include <iostream>
  13. #include <vector>
  14. #include <matrix.hpp>
  15. #include <device_wrapper.hpp>
  16. #if defined(PVFMM_HAVE_CUDA)
  17. #include <cuda_runtime_api.h>
  18. #include <cublas_v2.h>
  19. #endif
  20. namespace pvfmm{
  21. namespace mat{
  22. template <class T>
  23. inline void gemm(char TransA, char TransB, int M, int N, int K, T alpha, T *A, int lda, T *B, int ldb, T beta, T *C, int ldc){
  24. if((TransA=='N' || TransA=='n') && (TransB=='N' || TransB=='n')){
  25. for(size_t n=0;n<N;n++){ // Columns of C
  26. for(size_t m=0;m<M;m++){ // Rows of C
  27. T AxB=0;
  28. for(size_t k=0;k<K;k++){
  29. AxB+=A[m+lda*k]*B[k+ldb*n];
  30. }
  31. C[m+ldc*n]=alpha*AxB+(beta==0?0:beta*C[m+ldc*n]);
  32. }
  33. }
  34. }else if(TransA=='N' || TransA=='n'){
  35. for(size_t n=0;n<N;n++){ // Columns of C
  36. for(size_t m=0;m<M;m++){ // Rows of C
  37. T AxB=0;
  38. for(size_t k=0;k<K;k++){
  39. AxB+=A[m+lda*k]*B[n+ldb*k];
  40. }
  41. C[m+ldc*n]=alpha*AxB+(beta==0?0:beta*C[m+ldc*n]);
  42. }
  43. }
  44. }else if(TransB=='N' || TransB=='n'){
  45. for(size_t n=0;n<N;n++){ // Columns of C
  46. for(size_t m=0;m<M;m++){ // Rows of C
  47. T AxB=0;
  48. for(size_t k=0;k<K;k++){
  49. AxB+=A[k+lda*m]*B[k+ldb*n];
  50. }
  51. C[m+ldc*n]=alpha*AxB+(beta==0?0:beta*C[m+ldc*n]);
  52. }
  53. }
  54. }else{
  55. for(size_t n=0;n<N;n++){ // Columns of C
  56. for(size_t m=0;m<M;m++){ // Rows of C
  57. T AxB=0;
  58. for(size_t k=0;k<K;k++){
  59. AxB+=A[k+lda*m]*B[n+ldb*k];
  60. }
  61. C[m+ldc*n]=alpha*AxB+(beta==0?0:beta*C[m+ldc*n]);
  62. }
  63. }
  64. }
  65. }
  66. // template<>
  67. // void gemm<float>(char TransA, char TransB, int M, int N, int K, float alpha, float *A, int lda, float *B, int ldb, float beta, float *C, int ldc);
  68. //
  69. // template<>
  70. // void gemm<double>(char TransA, char TransB, int M, int N, int K, double alpha, double *A, int lda, double *B, int ldb, double beta, double *C, int ldc);
  71. #if defined(PVFMM_HAVE_CUDA)
  72. template<>
  73. inline void cublasgemm<float>(char TransA, char TransB, int M, int N, int K, float alpha, float *A, int lda, float *B, int ldb, float beta, float *C, int ldc) {
  74. cublasOperation_t cublasTransA, cublasTransB;
  75. cublasHandle_t *handle = CUDA_Lock::acquire_handle();
  76. if (TransA == 'T' || TransA == 't') cublasTransA = CUBLAS_OP_T;
  77. else if (TransA == 'N' || TransA == 'n') cublasTransA = CUBLAS_OP_N;
  78. if (TransB == 'T' || TransB == 't') cublasTransB = CUBLAS_OP_T;
  79. else if (TransB == 'N' || TransB == 'n') cublasTransB = CUBLAS_OP_N;
  80. cublasStatus_t status = cublasSgemm(*handle, cublasTransA, cublasTransB, M, N, K, &alpha, A, lda, B, ldb, &beta, C, ldc);
  81. }
  82. template<>
  83. inline void cublasgemm<double>(char TransA, char TransB, int M, int N, int K, double alpha, double *A, int lda, double *B, int ldb, double beta, double *C, int ldc){
  84. cublasOperation_t cublasTransA, cublasTransB;
  85. cublasHandle_t *handle = CUDA_Lock::acquire_handle();
  86. if (TransA == 'T' || TransA == 't') cublasTransA = CUBLAS_OP_T;
  87. else if (TransA == 'N' || TransA == 'n') cublasTransA = CUBLAS_OP_N;
  88. if (TransB == 'T' || TransB == 't') cublasTransB = CUBLAS_OP_T;
  89. else if (TransB == 'N' || TransB == 'n') cublasTransB = CUBLAS_OP_N;
  90. cublasStatus_t status = cublasDgemm(*handle, cublasTransA, cublasTransB, M, N, K, &alpha, A, lda, B, ldb, &beta, C, ldc);
  91. }
  92. #endif
  93. #define U(i,j) U_[(i)*dim[0]+(j)]
  94. #define S(i,j) S_[(i)*dim[1]+(j)]
  95. #define V(i,j) V_[(i)*dim[1]+(j)]
  96. //#define SVD_DEBUG
  97. template <class T>
  98. static void GivensL(T* S_, const size_t dim[2], size_t m, T a, T b){
  99. T r=pvfmm::sqrt<T>(a*a+b*b);
  100. if (r == 0) return;
  101. T c=a/r;
  102. T s=-b/r;
  103. #pragma omp parallel for
  104. for(size_t i=0;i<dim[1];i++){
  105. T S0=S(m+0,i);
  106. T S1=S(m+1,i);
  107. S(m ,i)+=S0*(c-1);
  108. S(m ,i)+=S1*(-s );
  109. S(m+1,i)+=S0*( s );
  110. S(m+1,i)+=S1*(c-1);
  111. }
  112. }
  113. template <class T>
  114. static void GivensR(T* S_, const size_t dim[2], size_t m, T a, T b){
  115. T r=pvfmm::sqrt<T>(a*a+b*b);
  116. if (r == 0) return;
  117. T c=a/r;
  118. T s=-b/r;
  119. #pragma omp parallel for
  120. for(size_t i=0;i<dim[0];i++){
  121. T S0=S(i,m+0);
  122. T S1=S(i,m+1);
  123. S(i,m )+=S0*(c-1);
  124. S(i,m )+=S1*(-s );
  125. S(i,m+1)+=S0*( s );
  126. S(i,m+1)+=S1*(c-1);
  127. }
  128. }
  129. template <class T>
  130. static void SVD(const size_t dim[2], T* U_, T* S_, T* V_, T eps=-1){
  131. assert(dim[0]>=dim[1]);
  132. #ifdef SVD_DEBUG
  133. Matrix<T> M0(dim[0],dim[1],S_);
  134. #endif
  135. { // Bi-diagonalization
  136. size_t n=std::min(dim[0],dim[1]);
  137. std::vector<T> house_vec(std::max(dim[0],dim[1]));
  138. for(size_t i=0;i<n;i++){
  139. // Column Householder
  140. {
  141. T x1=S(i,i);
  142. if(x1<0) x1=-x1;
  143. T x_inv_norm=0;
  144. for(size_t j=i;j<dim[0];j++){
  145. x_inv_norm+=S(j,i)*S(j,i);
  146. }
  147. if(x_inv_norm>0) x_inv_norm=1/pvfmm::sqrt<T>(x_inv_norm);
  148. T alpha=pvfmm::sqrt<T>(1+x1*x_inv_norm);
  149. T beta=x_inv_norm/alpha;
  150. if (x_inv_norm == 0) alpha = 0; // nothing to do
  151. house_vec[i]=-alpha;
  152. for(size_t j=i+1;j<dim[0];j++){
  153. house_vec[j]=-beta*S(j,i);
  154. }
  155. if(S(i,i)<0) for(size_t j=i+1;j<dim[0];j++){
  156. house_vec[j]=-house_vec[j];
  157. }
  158. }
  159. #pragma omp parallel for
  160. for(size_t k=i;k<dim[1];k++){
  161. T dot_prod=0;
  162. for(size_t j=i;j<dim[0];j++){
  163. dot_prod+=S(j,k)*house_vec[j];
  164. }
  165. for(size_t j=i;j<dim[0];j++){
  166. S(j,k)-=dot_prod*house_vec[j];
  167. }
  168. }
  169. #pragma omp parallel for
  170. for(size_t k=0;k<dim[0];k++){
  171. T dot_prod=0;
  172. for(size_t j=i;j<dim[0];j++){
  173. dot_prod+=U(k,j)*house_vec[j];
  174. }
  175. for(size_t j=i;j<dim[0];j++){
  176. U(k,j)-=dot_prod*house_vec[j];
  177. }
  178. }
  179. // Row Householder
  180. if(i>=n-1) continue;
  181. {
  182. T x1=S(i,i+1);
  183. if(x1<0) x1=-x1;
  184. T x_inv_norm=0;
  185. for(size_t j=i+1;j<dim[1];j++){
  186. x_inv_norm+=S(i,j)*S(i,j);
  187. }
  188. if(x_inv_norm>0) x_inv_norm=1/pvfmm::sqrt<T>(x_inv_norm);
  189. T alpha=pvfmm::sqrt<T>(1+x1*x_inv_norm);
  190. T beta=x_inv_norm/alpha;
  191. if (x_inv_norm == 0) alpha = 0; // nothing to do
  192. house_vec[i+1]=-alpha;
  193. for(size_t j=i+2;j<dim[1];j++){
  194. house_vec[j]=-beta*S(i,j);
  195. }
  196. if(S(i,i+1)<0) for(size_t j=i+2;j<dim[1];j++){
  197. house_vec[j]=-house_vec[j];
  198. }
  199. }
  200. #pragma omp parallel for
  201. for(size_t k=i;k<dim[0];k++){
  202. T dot_prod=0;
  203. for(size_t j=i+1;j<dim[1];j++){
  204. dot_prod+=S(k,j)*house_vec[j];
  205. }
  206. for(size_t j=i+1;j<dim[1];j++){
  207. S(k,j)-=dot_prod*house_vec[j];
  208. }
  209. }
  210. #pragma omp parallel for
  211. for(size_t k=0;k<dim[1];k++){
  212. T dot_prod=0;
  213. for(size_t j=i+1;j<dim[1];j++){
  214. dot_prod+=V(j,k)*house_vec[j];
  215. }
  216. for(size_t j=i+1;j<dim[1];j++){
  217. V(j,k)-=dot_prod*house_vec[j];
  218. }
  219. }
  220. }
  221. }
  222. size_t k0=0;
  223. size_t iter=0;
  224. if(eps<0){
  225. eps=1.0;
  226. while(eps+(T)1.0>1.0) eps*=0.5;
  227. eps*=64.0;
  228. }
  229. while(k0<dim[1]-1){ // Diagonalization
  230. iter++;
  231. T S_max=0.0;
  232. for (size_t i = 0; i < dim[1]; i++) S_max = (S_max > pvfmm::fabs<T>(S(i, i)) ? S_max : pvfmm::fabs<T>(S(i, i)));
  233. for (size_t i = 0; i < dim[1] - 1; i++) S_max = (S_max > pvfmm::fabs<T>(S(i, i + 1)) ? S_max : pvfmm::fabs<T>(S(i, i + 1)));
  234. //while(k0<dim[1]-1 && pvfmm::fabs<T>(S(k0,k0+1))<=eps*(pvfmm::fabs<T>(S(k0,k0))+pvfmm::fabs<T>(S(k0+1,k0+1)))) k0++;
  235. while(k0<dim[1]-1 && pvfmm::fabs<T>(S(k0,k0+1))<=eps*S_max) k0++;
  236. if(k0==dim[1]-1) continue;
  237. size_t n=k0+2;
  238. //while(n<dim[1] && pvfmm::fabs<T>(S(n-1,n))>eps*(pvfmm::fabs<T>(S(n-1,n-1))+pvfmm::fabs<T>(S(n,n)))) n++;
  239. while(n<dim[1] && pvfmm::fabs<T>(S(n-1,n))>eps*S_max) n++;
  240. T alpha=0;
  241. T beta=0;
  242. if (n - k0 == 2 && pvfmm::fabs<T>(S(k0, k0)) < eps * S_max && pvfmm::fabs<T>(S(k0 + 1, k0 + 1)) < eps * S_max) { // Compute mu
  243. alpha=0;
  244. beta=1;
  245. } else {
  246. T C[2][2];
  247. C[0][0]=S(n-2,n-2)*S(n-2,n-2);
  248. if(n-k0>2) C[0][0]+=S(n-3,n-2)*S(n-3,n-2);
  249. C[0][1]=S(n-2,n-2)*S(n-2,n-1);
  250. C[1][0]=S(n-2,n-2)*S(n-2,n-1);
  251. C[1][1]=S(n-1,n-1)*S(n-1,n-1)+S(n-2,n-1)*S(n-2,n-1);
  252. T b=-(C[0][0]+C[1][1])/2;
  253. T c= C[0][0]*C[1][1] - C[0][1]*C[1][0];
  254. T d=0;
  255. if(b*b-c>0) d=pvfmm::sqrt<T>(b*b-c);
  256. else{
  257. T b=(C[0][0]-C[1][1])/2;
  258. T c=-C[0][1]*C[1][0];
  259. if(b*b-c>0) d=pvfmm::sqrt<T>(b*b-c);
  260. }
  261. T lambda1=-b+d;
  262. T lambda2=-b-d;
  263. T d1=lambda1-C[1][1]; d1=(d1<0?-d1:d1);
  264. T d2=lambda2-C[1][1]; d2=(d2<0?-d2:d2);
  265. T mu=(d1<d2?lambda1:lambda2);
  266. alpha=S(k0,k0)*S(k0,k0)-mu;
  267. beta=S(k0,k0)*S(k0,k0+1);
  268. }
  269. for(size_t k=k0;k<n-1;k++){
  270. size_t dimU[2]={dim[0],dim[0]};
  271. size_t dimV[2]={dim[1],dim[1]};
  272. GivensR(S_,dim ,k,alpha,beta);
  273. GivensL(V_,dimV,k,alpha,beta);
  274. alpha=S(k,k);
  275. beta=S(k+1,k);
  276. GivensL(S_,dim ,k,alpha,beta);
  277. GivensR(U_,dimU,k,alpha,beta);
  278. alpha=S(k,k+1);
  279. beta=S(k,k+2);
  280. }
  281. { // Make S bi-diagonal again
  282. for(size_t i0=k0;i0<n-1;i0++){
  283. for(size_t i1=0;i1<dim[1];i1++){
  284. if(i0>i1 || i0+1<i1) S(i0,i1)=0;
  285. }
  286. }
  287. for(size_t i0=0;i0<dim[0];i0++){
  288. for(size_t i1=k0;i1<n-1;i1++){
  289. if(i0>i1 || i0+1<i1) S(i0,i1)=0;
  290. }
  291. }
  292. for(size_t i=0;i<dim[1]-1;i++){
  293. if(pvfmm::fabs<T>(S(i,i+1))<=eps*S_max){
  294. S(i,i+1)=0;
  295. }
  296. }
  297. }
  298. //std::cout<<iter<<' '<<k0<<' '<<n<<'\n';
  299. }
  300. { // Check Error
  301. #ifdef SVD_DEBUG
  302. Matrix<T> U0(dim[0],dim[0],U_);
  303. Matrix<T> S0(dim[0],dim[1],S_);
  304. Matrix<T> V0(dim[1],dim[1],V_);
  305. Matrix<T> E=M0-U0*S0*V0;
  306. T max_err=0;
  307. T max_nondiag0=0;
  308. T max_nondiag1=0;
  309. for(size_t i=0;i<E.Dim(0);i++)
  310. for(size_t j=0;j<E.Dim(1);j++){
  311. if(max_err<pvfmm::fabs<T>(E[i][j])) max_err=pvfmm::fabs<T>(E[i][j]);
  312. if((i>j+0 || i+0<j) && max_nondiag0<pvfmm::fabs<T>(S0[i][j])) max_nondiag0=pvfmm::fabs<T>(S0[i][j]);
  313. if((i>j+1 || i+1<j) && max_nondiag1<pvfmm::fabs<T>(S0[i][j])) max_nondiag1=pvfmm::fabs<T>(S0[i][j]);
  314. }
  315. std::cout<<max_err<<'\n';
  316. std::cout<<max_nondiag0<<'\n';
  317. std::cout<<max_nondiag1<<'\n';
  318. #endif
  319. }
  320. }
  321. #undef U
  322. #undef S
  323. #undef V
  324. #undef SVD_DEBUG
  325. template<class T>
  326. inline void svd(char *JOBU, char *JOBVT, int *M, int *N, T *A, int *LDA,
  327. T *S, T *U, int *LDU, T *VT, int *LDVT, T *WORK, int *LWORK,
  328. int *INFO){
  329. const size_t dim[2]={(size_t)std::max(*N,*M), (size_t)std::min(*N,*M)};
  330. T* U_=mem::aligned_new<T>(dim[0]*dim[0]); memset(U_, 0, dim[0]*dim[0]*sizeof(T));
  331. T* V_=mem::aligned_new<T>(dim[1]*dim[1]); memset(V_, 0, dim[1]*dim[1]*sizeof(T));
  332. T* S_=mem::aligned_new<T>(dim[0]*dim[1]);
  333. const size_t lda=*LDA;
  334. const size_t ldu=*LDU;
  335. const size_t ldv=*LDVT;
  336. if(dim[1]==*M){
  337. for(size_t i=0;i<dim[0];i++)
  338. for(size_t j=0;j<dim[1];j++){
  339. S_[i*dim[1]+j]=A[i*lda+j];
  340. }
  341. }else{
  342. for(size_t i=0;i<dim[0];i++)
  343. for(size_t j=0;j<dim[1];j++){
  344. S_[i*dim[1]+j]=A[j*lda+i];
  345. }
  346. }
  347. for(size_t i=0;i<dim[0];i++){
  348. U_[i*dim[0]+i]=1;
  349. }
  350. for(size_t i=0;i<dim[1];i++){
  351. V_[i*dim[1]+i]=1;
  352. }
  353. SVD<T>(dim, U_, S_, V_, (T)-1);
  354. for(size_t i=0;i<dim[1];i++){ // Set S
  355. S[i]=S_[i*dim[1]+i];
  356. }
  357. if(dim[1]==*M){ // Set U
  358. for(size_t i=0;i<dim[1];i++)
  359. for(size_t j=0;j<*M;j++){
  360. U[j+ldu*i]=V_[j+i*dim[1]]*(S[i]<0.0?-1.0:1.0);
  361. }
  362. }else{
  363. for(size_t i=0;i<dim[1];i++)
  364. for(size_t j=0;j<*M;j++){
  365. U[j+ldu*i]=U_[i+j*dim[0]]*(S[i]<0.0?-1.0:1.0);
  366. }
  367. }
  368. if(dim[0]==*N){ // Set V
  369. for(size_t i=0;i<*N;i++)
  370. for(size_t j=0;j<dim[1];j++){
  371. VT[j+ldv*i]=U_[j+i*dim[0]];
  372. }
  373. }else{
  374. for(size_t i=0;i<*N;i++)
  375. for(size_t j=0;j<dim[1];j++){
  376. VT[j+ldv*i]=V_[i+j*dim[1]];
  377. }
  378. }
  379. for(size_t i=0;i<dim[1];i++){
  380. S[i]=S[i]*(S[i]<0.0?-1.0:1.0);
  381. }
  382. mem::aligned_delete<T>(U_);
  383. mem::aligned_delete<T>(S_);
  384. mem::aligned_delete<T>(V_);
  385. if(0){ // Verify
  386. const size_t dim[2]={(size_t)std::max(*N,*M), (size_t)std::min(*N,*M)};
  387. const size_t lda=*LDA;
  388. const size_t ldu=*LDU;
  389. const size_t ldv=*LDVT;
  390. Matrix<T> A1(*M,*N);
  391. Matrix<T> S1(dim[1],dim[1]);
  392. Matrix<T> U1(*M,dim[1]);
  393. Matrix<T> V1(dim[1],*N);
  394. for(size_t i=0;i<*N;i++)
  395. for(size_t j=0;j<*M;j++){
  396. A1[j][i]=A[j+i*lda];
  397. }
  398. S1.SetZero();
  399. for(size_t i=0;i<dim[1];i++){ // Set S
  400. S1[i][i]=S[i];
  401. }
  402. for(size_t i=0;i<dim[1];i++)
  403. for(size_t j=0;j<*M;j++){
  404. U1[j][i]=U[j+ldu*i];
  405. }
  406. for(size_t i=0;i<*N;i++)
  407. for(size_t j=0;j<dim[1];j++){
  408. V1[j][i]=VT[j+ldv*i];
  409. }
  410. std::cout<<U1*S1*V1-A1<<'\n';
  411. }
  412. }
  413. // template<>
  414. // void svd<float>(char *JOBU, char *JOBVT, int *M, int *N, float *A, int *LDA,
  415. // float *S, float *U, int *LDU, float *VT, int *LDVT, float *WORK, int *LWORK,
  416. // int *INFO);
  417. //
  418. // template<>
  419. // void svd<double>(char *JOBU, char *JOBVT, int *M, int *N, double *A, int *LDA,
  420. // double *S, double *U, int *LDU, double *VT, int *LDVT, double *WORK, int *LWORK,
  421. // int *INFO);
  422. /**
  423. * \brief Computes the pseudo inverse of matrix M(n1xn2) (in row major form)
  424. * and returns the output M_(n2xn1). Original contents of M are destroyed.
  425. */
  426. template <class T>
  427. void pinv(T* M, int n1, int n2, T eps, T* M_){
  428. if(n1*n2==0) return;
  429. int m = n2;
  430. int n = n1;
  431. int k = (m<n?m:n);
  432. T* tU =mem::aligned_new<T>(m*k);
  433. T* tS =mem::aligned_new<T>(k);
  434. T* tVT=mem::aligned_new<T>(k*n);
  435. //SVD
  436. int INFO=0;
  437. char JOBU = 'S';
  438. char JOBVT = 'S';
  439. //int wssize = max(3*min(m,n)+max(m,n), 5*min(m,n));
  440. int wssize = 3*(m<n?m:n)+(m>n?m:n);
  441. int wssize1 = 5*(m<n?m:n);
  442. wssize = (wssize>wssize1?wssize:wssize1);
  443. T* wsbuf = mem::aligned_new<T>(wssize);
  444. svd(&JOBU, &JOBVT, &m, &n, &M[0], &m, &tS[0], &tU[0], &m, &tVT[0], &k,
  445. wsbuf, &wssize, &INFO);
  446. if(INFO!=0)
  447. std::cout<<INFO<<'\n';
  448. assert(INFO==0);
  449. mem::aligned_delete<T>(wsbuf);
  450. T eps_=tS[0]*eps;
  451. for(int i=0;i<k;i++)
  452. if(tS[i]<eps_)
  453. tS[i]=0;
  454. else
  455. tS[i]=1.0/tS[i];
  456. for(int i=0;i<m;i++){
  457. for(int j=0;j<k;j++){
  458. tU[i+j*m]*=tS[j];
  459. }
  460. }
  461. gemm<T>('T','T',n,m,k,1.0,&tVT[0],k,&tU[0],m,0.0,M_,n);
  462. mem::aligned_delete<T>(tU);
  463. mem::aligned_delete<T>(tS);
  464. mem::aligned_delete<T>(tVT);
  465. }
  466. }//end namespace
  467. }//end namespace