Неточные собственные векторы из программы LAPACK ZHEEVR

Я пытался заменить восемь процедур Scipy в моем коде Cython на LAPACK уровня C. По сути, я хочу вычислить собственные векторы, соответствующие наибольшим и наименьшим собственным значениям эрмитовой матрицы размером около 50x50. Я сделал небольшой пример 3x3, чтобы убедиться, что получаю тот же результат, что и для eigh, который, кажется, внутренне использует подпрограмму ZHEEVR. Scipy предоставляет версии подпрограмм LAPACK уровня C для импорта в Cython, который я использую. Я думаю, что синтаксис и т. д. я понял правильно, поскольку один из собственных векторов и все собственные значения абсолютно одинаковы как для eigh, так и для ZHEEVR. Однако другие собственные векторы ZHEEVR неточны. На самом деле я не получаю правильного разложения.

import numpy as np
cimport numpy as cnp

from scipy.linalg import eigh
from scipy.linalg.cython_lapack cimport zheevr
from libc.stdlib cimport malloc, free

dim = 3

A = (np.arange(dim**2) + 2j).reshape(dim, dim) + 2*np.eye(dim)
A = A @ np.transpose(np.conjugate(A))

print(A)
print('')

values_eigh, vectors_eigh = eigh(A)

print(values_eigh)
print('')
print(vectors_eigh)
print('')

cdef:
    char jobz = 'V'
    char rrange = 'A'
    char uplo = 'U'
    int n = dim
    double complex * A_pointer = <double complex *>cnp.PyArray_DATA(
        A.astype(np.complex128))
    int lda = dim
    double vl = 0
    double vu = 0
    int il = 0
    int iu = 0
    double abstol = 1e-12
    int m = n
    double * values_zheever = <double *> malloc(
        n * sizeof(double))
    double complex * vectors_zheever = <double complex *>malloc(
        n * n * sizeof(double complex))
    int ldz = n
    int * isuppz = <int *> malloc(2 * n * sizeof(int))
    double complex * work = <double complex *>malloc(
        1 * sizeof(double complex))
    int lwork = -1
    double * rwork = <double *>malloc(1 * sizeof(double))
    int lrwork = -1
    int * iwork = <int *>malloc(1 * sizeof(int))
    int liwork = -1
    int info

zheevr(&jobz, &rrange, &uplo, &n, A_pointer, &lda, &vl, &vu, &il, &iu, &abstol, &m, values_zheever, vectors_zheever, &ldz, isuppz, work, &lwork, rwork, &lrwork, iwork, &liwork, &info)

lwork = <int> work[0]
lrwork = <int> rwork[0]
liwork = <int> iwork[0]
free(work)
free(rwork)
free(iwork)
work = <double complex *>malloc(lwork * sizeof(double complex))
rwork = <double *>malloc(lrwork * sizeof(double))
iwork = <int *>malloc(liwork * sizeof(int))

zheevr(&jobz, &rrange, &uplo, &n, A_pointer, &lda, &vl, &vu, &il, &iu, &abstol, &m, values_zheever, vectors_zheever, &ldz, isuppz, work, &lwork, rwork, &lrwork, iwork, &liwork, &info)

for i in range(dim):
    print(values_zheever[i])

vectors_zheevr_np = np.zeros((n* n), dtype=np.complex128)
for i in range(n*n):
        vectors_zheevr_np[i] = vectors_zheever[i]
vectors_zheevr_np = vectors_zheevr_np.reshape(n, n).T

print('')
print(vectors_zheevr_np)
print('')
print(vectors_zheevr_np.T.conj() @ A @ vectors_zheevr_np)
print('')
print(info)

Это дает результаты

[[ 21. +0.j  34.+18.j  51.+36.j]
 [ 34.-18.j  82. +0.j 122.+18.j]
 [ 51.-36.j 122.-18.j 197. +0.j]]

[  0.82663284   4.         295.17336716]

[[ 0.8755536 -0.00000000e+00j -0.40824829-0.00000000e+00j
  -0.25833936-0.00000000e+00j]
 [ 0.24467905+6.98442317e-02j  0.81649658+1.22124533e-15j
  -0.46103588+2.36713326e-01j]
 [-0.38619551+1.39688463e-01j -0.40824829-6.10622664e-16j
  -0.6637324 +4.73426651e-01j]]

0.8266328441181744
4.000000000000024
295.1733671558813

[[-0.8233493 +2.97808740e-01j -0.40824829-2.22044605e-16j
   0.21031944-1.50016524e-01j]
 [-0.20633357+1.48904370e-01j  0.81649658-1.56819002e-15j
   0.51279727-7.50082620e-02j]
 [ 0.41068216-0.00000000e+00j -0.40824829-0.00000000e+00j
   0.8152751 +0.00000000e+00j]]

[[ 2.72444561e+01+3.55271368e-15j -1.35003120e-13-1.42108547e-14j
   1.95681896e+01-8.18241075e+01j]
 [-1.30784272e-13+2.16125482e-14j  4.00000000e+00-4.07921987e-16j
  -2.66453526e-14+4.85653027e-13j]
 [ 1.95681896e+01+8.18241075e+01j -2.84217094e-14-4.84945417e-13j
   2.68755544e+02+0.00000000e+00j]]

0

Инфо=0 говорит о том, что алгоритм выполнился правильно, и собственные значения верны, но Vectors_zheevr_np.T.conj()@A@vectors_zheevr_np является, как видите, не диагональной матрицей, как должно быть для корректного разложения. - верхний правый и нижний левый элементы ненулевые.

Кто-нибудь сталкивался с этой проблемой раньше, знает, как ее исправить, или есть какие-нибудь идеи? Большое спасибо!

Ожидает ли эта функция упорядоченного ввода на Фортране или C? Просто прочитав документы, я не могу сказать. Я предполагаю, что библиотека LAPACK будет ожидать заказа на Фортране. NumPy по умолчанию использует порядок C для большинства вещей.

Nick ODell 21.07.2024 18:16

Это оно. Большое спасибо!!

mgns 23.07.2024 15:50

Пожалуйста, не отмечайте этот вопрос C, так как он вообще не имеет кода C и не связан с C, а округляет числа с плавающей запятой.

Luis Colorado 23.07.2024 20:58

Я включил C, поскольку Cython создает код C, но ладно, поехали.

mgns 25.07.2024 12:00
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
4
102
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как отметил Ник Оделл, LAPACK требует непрерывных входных массивов в формате FORTRAN. Итак, замена

cdef double complex * A_pointer = <double complex *>cnp.PyArray_DATA(
        A.astype(np.complex128))

с

cdef double complex * A_pointer = <double complex *>cnp.PyArray_DATA(
            np.asfortranarray(A.astype(np.complex128)))

решает проблему. Кроме того, обратите внимание, что собственные векторы эрмитовых матриц в случаях различных собственных значений уникальны только с точностью до мультипликативной (комплексной) константы.

Кстати, np.asfortranarray() — немного более прямой способ сделать это. Его преимущество заключается в том, что он меняет порядок памяти, но не меняет способ печати/индексации массива с помощью операций NumPy.

Nick ODell 23.07.2024 19:52

Приятно это знать, спасибо за помощь!

mgns 25.07.2024 11:56

Другие вопросы по теме