QR-разложение прошло успешно, но имеет большое количество ошибок

У меня есть две реализации QR-декомпозиции в коде на Фортране. Одна из них — это пользовательская функция разложения QR:

  ! QR decomposition solving for vector B in AX = B
  subroutine qr_eqsystem(A, B, X, M, N, q, r, error)
  
  implicit none
  !CALL qr_eqsystem(A, C(:,i,j), B, GNDINT, NEQ, IERR)
  integer, intent(in) :: m, n
  real, intent(in) :: A(m, n), B(m)
  
  real, intent(out) :: x(n),q(m, n),r(n, n)
  real, intent(out) :: error
  
  !locals
  real :: aux
  integer :: i, j, k
  
  do i = 1,n
    do k = 1,m
      q(k, i) = A(k, i)
    enddo
  enddo
  
  do i = 1,n
    do j = 1,n
      r(j, i) = 0.0
    enddo
  enddo
  
  do i = 1,n
    do j = 1,i-1
      r(j, i) = 0.0
      do k = 1,m
        r(j, i) = r(j, i) + q(k, j)*A(k, i)
      enddo
      
      do k = 1,m
        q(k, i) = q(k, i) - r(j, i)*q(k,j)
      enddo
    enddo
    
    r(i, i) = 0.0
    do k = 1,m
      r(i, i) = r(i, i) + q(k, i)*q(k, i)
    enddo
    r(i, i) = sqrt(r(i, i))
    
    if (r(i,i).ne.0.0)then
      do k = 1,m
        q(k, i) = q(k, i) / r(i, i)
      enddo
    endif
    
  enddo
  
  do i = 1,n
    x(i) = 0.0
    do j = 1,m
      x(i) = x(i) + q(j, i)*B(j)
    enddo
  enddo
  
  if (r(n, n).ne.0.0)then
    x(n) = x(n) / r(n,n)
  endif
  
  do i = n-1,1,-1
    do j = i+1,n
      x(i) = x(i) - r(i, j) * x(j)
    enddo
    
    if (r(i, i).ne.0.0)then
       x(i) = x(i) / r(i, i)
    endif
  enddo
  
  error = 0
  
  ! calculate AX and compare with B as an error, report total raw error
  error = sum(abs(matmul(A,X)-B))
  endsubroutine qr_eqsystem

Второй использует встроенный LAPACK Intel Fortran с dgeqrf, dormqr и dtrtrs:

  q = A
  call dgeqrf(totPoints,ncol,q,totPoints,tau,work,1280,info)
  Call dormqr('L','T',totPoints,1,ncol,q,totPoints,tau,C,
#               totPoints,work,1280,info)
  x = C
  Call dtrtrs('U','N','N',ncol,1,q,totPoints,x,totPoints,info)
  error = sum(abs(matmul(A,x(1:ncol))-C))

Приведенные выше коды позволяют найти x в системе уравнений Ax = B, где размер A равен 125x21, B 125x1 и x 21x1. Во время моделирования матрица A является относительно постоянной, так как она построена на основе пространственных координат, но вектор B развивается со временем, при этом величина элементов в B и разница между самым большим и наименьшим элементами со временем увеличиваются. Оба кода реализованы в коде Fortran для отладки, где пользовательская реализация является основным решателем, и когда ошибка превышает высокое значение, например 10,0, код вызывает подпрограмму Intel для проверки.

Значения error в обоих кодах возвращают очень низкие значения (<1e-5) в начале моделирования, но error увеличиваются до высоких значений по мере продолжения моделирования и, похоже, не имеют насыщенного значения, и я этого не делаю. понять, почему это происходит. Я проверил разложенные матрицы Q и R: Q^T*Q возвращает единичную матрицу, а Q*R возвращает A с разумной точностью. Насколько я понимаю подход к декомпозиции QR, поскольку A успешно разлагается, x всегда должен быть разрешимым, поскольку он включает в себя простое вычисление Q^T*B, а обратная замена Rx = Q^T*B для решения x всегда должна быть возможна, поскольку все значения действительны и мои переменные достаточно велики (8 байт с реальным размером флага: 64), чтобы не вызывать значительных ошибок с плавающей запятой.

Может ли кто-нибудь дать мне больше информации о том, почему QR-разложение Ax = B может дать большую ошибку при оценке A*xp - B, где xp является решенным x из процесса?

Кроме того, я проверил MATLAB x = A\B. Интересно, что решения Intel Fortran dgeqrf и MATLAB дают более высокие ошибки, чем первая пользовательская реализация, причем процесс MATLAB дает наибольшую ошибку, несмотря на то, что разложенные матрицы Q и R из всех трех подходов практически идентичны друг другу.

Код для матрицы A. Это написано в фиксированной форме Фортрана с расширением .for. GridCoords просто генерируется путем сопоставления сетки точек 5x5x5 в диапазоне [-1 1] с диапазоном, соответствующим объекту размером 1e-5, например [-2.5e-5 2.5e-5] для объекта диаметром 50 микрон.

  subroutine defineA(ix,iy,iz,A,ipt)
  
  implicit none
  
  integer, intent(in) :: ix,iy,iz,ipt
  real, intent(out) :: A(totPoints,ncol)
  
  if (iInterp.ge.1) then
    A(ipt,1) = 1
    A(ipt,2) = gridCoords(1,ix)
    A(ipt,3) = gridCoords(2,iy)
    A(ipt,4) = gridCoords(3,iz)
    A(ipt,5) = gridCoords(1,ix)*gridCoords(2,iy)
    A(ipt,6) = gridCoords(1,ix)*gridCoords(3,iz)
    A(ipt,7) = gridCoords(2,iy)*gridCoords(3,iz)
    A(ipt,8) = gridCoords(1,ix)*gridCoords(2,iy)*gridCoords(3,iz)
    if (iInterp.ge.2) then
      A(ipt,9) = gridCoords(1,ix)*gridCoords(1,ix)
      A(ipt,10) = gridCoords(2,iy)*gridCoords(2,iy)
      A(ipt,11) = gridCoords(3,iz)*gridCoords(3,iz)
      A(ipt,12) = gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(2,iy)
      A(ipt,13) = gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(3,iz)
      A(ipt,14) = gridCoords(1,ix)*gridCoords(2,iy)*gridCoords(2,iy)
      A(ipt,15) = gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(3,iz)
      A(ipt,16) = gridCoords(1,ix)*gridCoords(3,iz)*gridCoords(3,iz)
      A(ipt,17) = gridCoords(2,iy)*gridCoords(3,iz)*gridCoords(3,iz)
      A(ipt,18) = gridCoords(1,ix)*gridCoords(1,ix)
 #                        *gridCoords(2,iy)*gridCoords(2,iy)
      A(ipt,19) = gridCoords(1,ix)*gridCoords(1,ix)
 #                        *gridCoords(3,iz)*gridCoords(3,iz)
      A(ipt,20) = gridCoords(2,iy)*gridCoords(2,iy)
 #                        *gridCoords(3,iz)*gridCoords(3,iz)
      A(ipt,21) = gridCoords(1,ix)*gridCoords(1,ix)
 #                        *gridCoords(2,iy)*gridCoords(3,iz)
      A(ipt,22) = gridCoords(1,ix)*gridCoords(2,iy)
 #                        *gridCoords(2,iy)*gridCoords(3,iz)
      A(ipt,23) = gridCoords(1,ix)*gridCoords(2,iy)
 #                        *gridCoords(3,iz)*gridCoords(3,iz)
      A(ipt,24) = gridCoords(1,ix)*gridCoords(1,ix)
 #                        *gridCoords(2,iy)*gridCoords(2,iy)
 #                        *gridCoords(3,iz)
      A(ipt,25) = gridCoords(1,ix)*gridCoords(1,ix)
 #                        *gridCoords(2,iy)*gridCoords(3,iz)
 #                        *gridCoords(3,iz)
      A(ipt,26) = gridCoords(1,ix)*gridCoords(2,iy)
 #                        *gridCoords(2,iy)*gridCoords(3,iz)
 #                        *gridCoords(3,iz)
      A(ipt,27) = gridCoords(1,ix)*gridCoords(1,ix)
 #                        *gridCoords(2,iy)*gridCoords(2,iy)
 #                        *gridCoords(3,iz)*gridCoords(3,iz)
      if (iInterp.ge.3) then
        A(ipt,28)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
        A(ipt,29)=gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
        A(ipt,30)=gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,31)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)
        A(ipt,32)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(3,iz)
        A(ipt,33)=gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
        
        A(ipt,34)=gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)
        A(ipt,35)=gridCoords(1,ix)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,36)=gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,37)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)
 #               *gridCoords(3,iz)
        A(ipt,38)=gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)
        A(ipt,39)=gridCoords(1,ix)
 #               *gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,40)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)
        A(ipt,41)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,42)=gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
        
        A(ipt,43)=gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,44)=gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,45)=gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,46)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)
        A(ipt,47)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,48)=gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,49)=gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)
        A(ipt,50)=gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,51)=gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,52)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,53)=gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,54)=gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,55)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
        A(ipt,56)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,57)=gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,58)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)
        A(ipt,59)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,60)=gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,61)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,62)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        A(ipt,63)=gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
        
        A(ipt,64)=gridCoords(1,ix)*gridCoords(1,ix)*gridCoords(1,ix)
 #               *gridCoords(2,iy)*gridCoords(2,iy)*gridCoords(2,iy)
 #               *gridCoords(3,iz)*gridCoords(3,iz)*gridCoords(3,iz)
      endif
    endif
  endif
  
  endsubroutine defineA

Это очень похоже на предыдущий пост. Я подозреваю, что именно моделирование для создания подходящей матрицы A вне решателя является основной причиной ваших проблем. Матрица A, должно быть, балансирует на грани нестабильности или что-то в этом роде. Заставить решатель матрицы работать в арифметике вещественного*10 или вещественного*16 может быть достаточно, чтобы взять его под контроль.

Martin Brown 08.07.2024 18:34

@MartinBrown Я удалил свой предыдущий комментарий. Симуляция достигла той же точки, что и раньше, когда я установил условие ошибки > 10, и на том же шаге она все равно достигла ошибки > 10. Но на этот раз ошибка процесса dgeqrf имеет величину 3000, в то время как ошибка пользовательской реализации, использующей реальные 16, по-прежнему равна только 16. Теперь я прогоню всю симуляцию, чтобы увидеть ошибку в конце, и посмотрю еще несколько способов устранения ошибки с плавающей запятой. Возможно, я смогу масштабировать матрицу A, чтобы она не использовала такие маленькие значения.

Jesse Feng 08.07.2024 18:50

Я думаю, стоит посмотреть, как вычисляются матрица A и вектор B и величины входящих в них переменных. Я подозреваю, что проблема как-то затруднительна по мере приближения к вашему решению. Вам необходимо улучшить его состояние.

Martin Brown 08.07.2024 22:18

Возможно, стоит опубликовать код, создающий матрицу A и вектор B. Вы производите выборку своей функции с равными шагами по x/y/z или делаете что-то более сложное?

Martin Brown 09.07.2024 17:33

@MartinBrown Матрица A проста, добавил я в пост. Вектор B гораздо сложнее. Он выбирается из распределения, рассчитанного на основе второго моментного разброса, который определяет стандартное отклонение, затем присваивается каждой из точек сетки 5x5x5, а 125 значений располагаются по столбцам в векторе B, соответствующем матрице A.

Jesse Feng 09.07.2024 20:37

Я могу привести примеры вектора B, используемого в моем коде, но коды, которые его создали, более сложны, и я не думаю, что мне следует публиковать их в Интернете в любом случае.

Jesse Feng 09.07.2024 20:56

Я думаю, что, по крайней мере, часть проблемы заключается в том, что ваше масштабирование переменных x,y,z~10^-5 вместе с задействованными высокими степенями делает матрицу A чрезвычайно близкой к единственному числу и подверженной большим ошибкам округления. Другой вопрос, который у меня возникает, заключается в том, как 5 известных точек данных распределены по [-1,1] -1 -0,5 0 0,5 1 или чему-то вроде нулей полинома Чебышева T5 (x). Поскольку это зависит только от координат x, y, z, почему A вообще меняется? (я ожидал, что это также будет зависеть от ваших известных данных). Также нормально ли это работает для iInterp = 1 & 2?

Martin Brown 10.07.2024 10:56
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
7
83
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

У меня есть две реализации QR-разложения... Приведенные выше коды определяют x в системе уравнений Ax = B, где размер A равен 125x21, B равен 125x1, а x равен 21x1...

Обратите внимание, что количество уравнений в вашей системе превышает количество переменных, поэтому точного решения не существует. И решением вашей Ax = B-системы, результирующим решением Rx = Q^T*B-системы, будет такой x, для которого достигается минимальный функционал (B - Ax)*(B - Ax). Другими словами, это решение задачи наименьших квадратов.

Значения ошибки в обоих кодах возвращают очень низкие значения (<1e-5) в начале моделирования, но ошибка увеличивается до высоких величин по мере продолжения моделирования и, похоже, не имеет насыщенного значения, и я этого не делаю. понять, почему это происходит.

В предыдущем вопросе вы написали: «QR-разложение используется для решения коэффициентов, используемых в квадратичной интерполяции, адаптированной из трилинейной...». Я не очень понимаю, откуда у вас функция с 21 коэффициентом: f(x,y,z) = a_0 + a_1x + a_2y + a_3*z+ ....

Но вполне вероятно, что в процессе моделирования целевая функция стремится к виду, который не может быть хорошо представлен функцией f(x,y,z) с 21 коэффициентом. Таким образом, решение вашей системы начинает иметь большой остаток.

Кроме того, я проверил x = A\B MATLAB. Интересно, что и решения Intel Fortran dgeqrf, и решения MATLAB дают более высокие ошибки, чем первая пользовательская реализация, при этом процесс MATLAB дает наибольшую ошибку, несмотря на то, что разложенные матрицы Q и R из всех трех подходов практически идентичны друг другу.

Поскольку вы сравниваете error = sum(abs(matmul(A,X)-B)), но qr_eqsystem(), Intel Fortran dgeqrf и MATLAB, минимизируйте error2 = DOT_PRODUCT(matmul(A,X)-B, matmul(A,X)-B), то это сравнение ничего не говорит о качестве этих трёх решений. Вполне возможно, что более точное решение задачи наименьших квадратов (в норме L2) будет иметь худшую невязку в норме L1 (сумму абсолютных значений отклонений).

Если нет физической причины использовать норму Л1, лучше использовать норму Л2.

Во время моделирования матрица A является относительно постоянной, поскольку она строится из пространственных координат...

Если A — константа, то cond(A) = cond(R) ∼ max(abs(diagonal(R)))/min(abs(diagonal(R))) это тоже константа. Соответственно, если проблема решена хорошо в начале моделирования, то причина, скорее всего, не в точности матрицы, ее декомпозиции или решении.

Резюме: поскольку вы пишете «относительно постоянный», все равно стоит посмотреть на изменение cond(A) во время моделирования.

  1. Если cond(A) > 10¹⁰ в начале или во время моделирования, то может возникнуть проблема с точностью решения и/или, возможно, проблема с координатами функции f(x,y,z);

  2. Если cond(A) всегда < 10⁶, то проблема определенно в том, что форма вашего f(x,y,z) не соответствует модели. Тогда его нужно заменить на что-то другое, может на многомерные B-сплайны, может на что-то более подходящее к физике задачи.

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