OPALX (Object Oriented Parallel Accelerator Library for Exascal) master (dc2a29eed580)
OPALX
Loading...
Searching...
No Matches
GSLEigen.h
Go to the documentation of this file.
1//
2// GSL Eigenvalue compatibility to replace gsl_eigen
3//
4// Copyright (c) 2023, Paul Scherrer Institute, Villigen PSI, Switzerland
5// All rights reserved
6//
7// This file is part of OPAL.
8//
9// OPAL is free software: you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation, either version 3 of the License, or
12// (at your option) any later version.
13//
14// You should have received a copy of the GNU General License
15// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
16//
17
18#ifndef OPAL_GSL_EIGEN_HH
19#define OPAL_GSL_EIGEN_HH
20
21#include <cmath>
22#include <complex>
23#include <vector>
25#include "Utilities/GSLMatrix.h"
26
31 size_t n;
32 std::vector<double> work;
33
35};
36
39 size_t n;
40 std::vector<double> work;
41
43};
44
51 w->n = n;
52 w->work.resize(n * n);
53 return w;
54}
55
62 int /* balance */, int /* compute_shur */, gsl_eigen_nonsymm_workspace* /* w */) {
63 // Parameters not used in simple implementation
64}
65
70
77 w->n = n;
78 w->work.resize(n * n);
79 return w;
80}
81
86
96 size_t n = A->size1;
97 if (A->size2 != n || eval->size != n) {
98 return -1;
99 }
100
101 // Convert to complex matrix for computation
102 std::vector<std::complex<double>> matrix(n * n);
103 for (size_t i = 0; i < n; ++i) {
104 for (size_t j = 0; j < n; ++j) {
105 matrix[i * n + j] = std::complex<double>(*gsl_matrix_ptr(A, i, j), 0.0);
106 }
107 }
108
109 // Simple QR algorithm (limited iterations for stability)
110 const int max_iter = 100;
111 for (int iter = 0; iter < max_iter; ++iter) {
112 // QR decomposition
113 std::vector<std::complex<double>> Q(n * n, 0.0);
114 std::vector<std::complex<double>> R(n * n, 0.0);
115
116 // Initialize Q as identity
117 for (size_t i = 0; i < n; ++i) {
118 Q[i * n + i] = 1.0;
119 }
120
121 // Compute QR using Gram-Schmidt
122 for (size_t j = 0; j < n; ++j) {
123 std::complex<double> norm = 0.0;
124 for (size_t i = 0; i < n; ++i) {
125 norm += matrix[i * n + j] * std::conj(matrix[i * n + j]);
126 }
127 norm = std::sqrt(norm);
128
129 if (std::abs(norm) < 1e-15) {
130 R[j * n + j] = 0.0;
131 for (size_t i = 0; i < n; ++i) {
132 Q[i * n + j] = (i == j) ? 1.0 : 0.0;
133 }
134 } else {
135 R[j * n + j] = norm;
136 for (size_t i = 0; i < n; ++i) {
137 Q[i * n + j] = matrix[i * n + j] / norm;
138 }
139
140 for (size_t k = j + 1; k < n; ++k) {
141 std::complex<double> dot = 0.0;
142 for (size_t i = 0; i < n; ++i) {
143 dot += std::conj(Q[i * n + j]) * matrix[i * n + k];
144 }
145 R[j * n + k] = dot;
146 for (size_t i = 0; i < n; ++i) {
147 matrix[i * n + k] -= Q[i * n + j] * dot;
148 }
149 }
150 }
151 }
152
153 // Update: A = R * Q
154 for (size_t i = 0; i < n; ++i) {
155 for (size_t j = 0; j < n; ++j) {
156 std::complex<double> sum = 0.0;
157 for (size_t k = 0; k < n; ++k) {
158 sum += R[i * n + k] * Q[k * n + j];
159 }
160 matrix[i * n + j] = sum;
161 }
162 }
163
164 // Check convergence (diagonal elements are eigenvalues)
165 bool converged = true;
166 for (size_t i = 0; i < n; ++i) {
167 for (size_t j = 0; j < n; ++j) {
168 if (i != j && std::abs(matrix[i * n + j]) > 1e-10) {
169 converged = false;
170 break;
171 }
172 }
173 if (!converged) break;
174 }
175 if (converged) break;
176 }
177
178 // Extract eigenvalues from diagonal
179 for (size_t i = 0; i < n; ++i) {
180 *gsl_vector_complex_ptr(eval, i) = gsl_complex(matrix[i * n + i]);
181 }
182
183 return 0;
184}
185
197 // First compute eigenvalues
198 int err = gsl_eigen_nonsymm(A, eval, reinterpret_cast<gsl_eigen_nonsymm_workspace*>(w));
199 if (err != 0) return err;
200
201 // For eigenvectors, use a simplified approach
202 // In practice, this would solve (A - lambda*I)*v = 0 for each eigenvalue
203 // This is a simplified implementation
204 size_t n = A->size1;
205
206 for (size_t i = 0; i < n; ++i) {
207 gsl_complex lambda = *gsl_vector_complex_ptr(eval, i);
208
209 // Create A - lambda*I
210 gsl_matrix_complex* A_minus_lambda = gsl_matrix_complex_alloc(n, n);
211 for (size_t row = 0; row < n; ++row) {
212 for (size_t col = 0; col < n; ++col) {
213 if (row == col) {
214 gsl_complex diag =
215 gsl_complex_sub(gsl_complex(*gsl_matrix_ptr(A, row, col), 0.0), lambda);
216 *gsl_matrix_complex_ptr(A_minus_lambda, row, col) = diag;
217 } else {
218 *gsl_matrix_complex_ptr(A_minus_lambda, row, col) =
219 gsl_complex(*gsl_matrix_ptr(A, row, col), 0.0);
220 }
221 }
222 }
223
224 // Solve for eigenvector (simplified: use last column as starting point)
225 // Set eigenvector to unit vector in last dimension
226 for (size_t j = 0; j < n; ++j) {
227 *gsl_matrix_complex_ptr(evec, j, i) =
228 (j == n - 1) ? gsl_complex(1.0, 0.0) : gsl_complex(0.0, 0.0);
229 }
230
231 gsl_matrix_complex_free(A_minus_lambda);
232 }
233
234 return 0;
235}
236
237#endif // OPAL_GSL_EIGEN_HH
gsl_complex gsl_complex_sub(gsl_complex a, gsl_complex b)
Difference .
Definition GSLComplex.h:88
void gsl_eigen_nonsymmv_free(gsl_eigen_nonsymmv_workspace *w)
Free a workspace allocated by gsl_eigen_nonsymmv_alloc.
Definition GSLEigen.h:85
int gsl_eigen_nonsymm(gsl_matrix *A, gsl_vector_complex *eval, gsl_eigen_nonsymm_workspace *)
Compute eigenvalues of a real nonsymmetric matrix.
Definition GSLEigen.h:94
int gsl_eigen_nonsymmv(gsl_matrix *A, gsl_vector_complex *eval, gsl_matrix_complex *evec, gsl_eigen_nonsymmv_workspace *w)
Compute eigenvalues and a simplified set of eigenvectors.
Definition GSLEigen.h:194
void gsl_eigen_nonsymm_params(int, int, gsl_eigen_nonsymm_workspace *)
Accept GSL-style parameters (no-op in this implementation).
Definition GSLEigen.h:61
gsl_eigen_nonsymm_workspace * gsl_eigen_nonsymm_alloc(size_t n)
Allocate a nonsymmetric eigenvalue workspace for matrices.
Definition GSLEigen.h:49
void gsl_eigen_nonsymm_free(gsl_eigen_nonsymm_workspace *w)
Free a workspace allocated by gsl_eigen_nonsymm_alloc.
Definition GSLEigen.h:69
gsl_eigen_nonsymmv_workspace * gsl_eigen_nonsymmv_alloc(size_t n)
Allocate a nonsymmetric eigenvalue/vector workspace for matrices.
Definition GSLEigen.h:75
gsl_complex * gsl_vector_complex_ptr(gsl_vector_complex *v, size_t i)
Return pointer to element in a complex vector.
Definition GSLMatrix.h:252
void gsl_matrix_complex_free(gsl_matrix_complex *m)
Free a matrix allocated by gsl_matrix_complex_alloc.
Definition GSLMatrix.h:136
double * gsl_matrix_ptr(gsl_matrix *m, size_t i, size_t j)
Return pointer to element in a real matrix.
Definition GSLMatrix.h:199
gsl_complex * gsl_matrix_complex_ptr(gsl_matrix_complex *m, size_t i, size_t j)
Return pointer to element in a complex matrix.
Definition GSLMatrix.h:217
gsl_matrix_complex * gsl_matrix_complex_alloc(size_t n1, size_t n2)
Allocate a zero-initialized complex matrix of size .
Definition GSLMatrix.h:120
double dot(const Vector3D &lhs, const Vector3D &rhs)
Vector dot product.
Definition Vector3D.cpp:95
Complex number stored as .
Definition GSLComplex.h:27
Workspace for nonsymmetric eigenvalue computation.
Definition GSLEigen.h:30
std::vector< double > work
Definition GSLEigen.h:32
Workspace for nonsymmetric eigenvalues/eigenvectors.
Definition GSLEigen.h:38
std::vector< double > work
Definition GSLEigen.h:40
Dense complex matrix in row-major storage.
Definition GSLMatrix.h:45
Dense real matrix in row-major storage.
Definition GSLMatrix.h:29
size_t size1
Definition GSLMatrix.h:30
size_t size2
Definition GSLMatrix.h:31
Dense complex vector with stride.
Definition GSLMatrix.h:76