我已经为CGR方法编写了一个Matlab代码,它可以正常工作。这是:
MATLAB代码
% Generalized Conjugate Residual Algorithm
% Solves M*x=b (J*dx=-F)
function [x,converged]=gcr_Mfree(F,Cnew,Cold,Fold,alpha);
% define global variables
global h n M;
%Change above
b = -F ;
tol = 5.E-3 ;
maxiter = length(b) ;
x = zeros(maxiter,1) ;
r = b ; % we need this when calculating r_new
normr(1) = norm(r) ;
alpha2 = 1e-6 ; % alpha2 is the epsilon in the report!
for iter=1:maxiter
%Get preliminary search direction
p(:,iter) = r ;
%% Approximate the Jacobian(M) residual product
%alpha2 = 1.E-6 ;
Cnew(1:n) = Cnew(1:n)+alpha2*r ;
% Cnew(1) = Cold(1) ;
F2 = Funct2(Cnew,Cold,Fold,alpha) ;
Mr = 1/alpha2*(F2-F) ;
Mp(:,iter) = Mr ;
%% Orthogonalize search direction
for j=1:iter-1
p(:,iter) = p(:,iter) -Mp(:,j)'*Mp(:,iter)*p(:,j) ;
Mp(:,iter) = Mp(:,iter) -Mp(:,j)'*Mp(:,iter)*Mp(:,j) ;
end
%norm(Mp(:,iter))
%Normalize search direction
p(:,iter) = p(:,iter)/(norm(Mp(:,iter))) ;
Mp(:,iter) = Mp(:,iter)/(norm(Mp(:,iter))) ;
%Update solution and residual
alpha2 = r'*(Mp(:,iter))/( (Mp(:,iter))'*(Mp(:,iter))) ;
%alpha2
x = x+alpha2*p(:,iter) ;
r = r-alpha2*(Mp(:,iter)) ;
%Check convergence
normr=norm(r);
%fprintf('norm(r) = %g iter = %g\n',normr,iter+1);
if(normr < tol)
%fprintf('GCR Converged, iter = %g\n',iter+1);
converged=1;
break;
end
end %for loop
if(normr > tol)
fprintf('SOLUTION DID NOT CONVERGE!!');
converged=0;
end
这是我最后一个版本的Fortran代码:
SUBROUTINE gcr_Mfree(F2 ,Cnew,Cold,C_Fold,IGG,JGG,x ,converged)
!**** FUNCTIONS TO BE SOLVED ****
! Generalized Conjugate Residual Algorithm
! Solves M*x=b (J*dx=-F)
! Use somemodule
! Arguments declarations
IMPLICIT REAL*8 (A-H,O-Z)
INTEGER :: IGG,JGG
real*8, dimension(:), ALLOCATABLE :: x
real*8, intent(out) :: converged !<
REAL*8, DIMENSION(:,:), ALLOCATABLE :: F2,F22,Cnew,Cold,C_Fold,p,Mp
! Variable declarations
real*8 :: tol
real*8, DIMENSION(:), ALLOCATABLE :: r,b,Mr,F2V,F22V,CnewV,ColdV,C_FoldV,alpha2
integer :: i,j,maxiter,normr,iter
MASK = SIZE(F2)
ALLOCATE(F2V(1:MASK))
ALLOCATE(CnewV(1:MASK))
ALLOCATE(ColdV(1:MASK))
ALLOCATE(C_FoldV(1:MASK))
!*********** RESHAPING MATRICES TO VECTORS.........
F2V = RESHAPE(F2 ,(/MASK/))
CnewV = RESHAPE(Cnew,(/MASK/))
ColdV = RESHAPE(Cold,(/MASK/))
C_FoldV = RESHAPE(C_Fold,(/MASK/))
b = -F2V !(why minus?)
tol = 5.E-2
alpha2 = 1e-6 ! alpha2 is the epsilon in the report!
maxiter = size(b)
DEALLOCATE(x)
ALLOCATE(x(1:maxiter))
x = 0.0D0 !GUIdE: ""m2f: x = zeros(maxiter,1)""
r = b ! we need this when calculating r_new
! normr(1) = norm2(r) !!!! Norm
DO iter=1,maxiter
!Get preliminary search direction
DO i=1,iter
p(:,i) = r
ENDDO
! Approximate the Jacobian(M) residual product
CnewV = CnewV + alpha2*r
Cnew = RESHAPE(CnewV,(/IGG,JGG/))
F22 = CNF2(Cnew,Cold,Fold,DT)
F22V= RESHAPE(F22V,(/MASK/))
Mr = (1/alpha2)*(F22V-F2V) !GUIDE: (The apporximated Jacobian matrix)
DO i=1,iter
Mp(:,i) = Mr
ENDDO
!! Orthogonalize search direction
do j=1,iter-1
!m2f: p(:,iter) = p(:,iter) - Mp(:,j)'* Mp(:,iter) * p(:,j)
p(:,iter) = p(:,iter) - Mp(j,:) * Mp(:,iter) * p(:,j)
!m2f: Mp(:,iter) = Mp(:,iter) - Mp(:,j)'* Mp(:,iter) * Mp(:,j)
Mp( :, iter ) = Mp( :, iter ) - sum( Mp( :, j ) * Mp( :, iter ) ) * Mp( :, j )
end do
!norm(Mp(:,iter))
!Normalize search direction
ALLOCATE(p(1:IG,1:JG))
ALLOCATE(Mp(1:IG,1:JG))
p(:,iter) = p(:,iter) / (norm2(Mp(:,iter)))
Mp(:,iter) = Mp(:,iter) / (norm2(Mp(:,iter)))
!Update solution and residual
!call gemv(Mp, r, y [,alpha][,beta] [,trans])
IF(iter.EQ.1) THEN
SUM1 = dot_product(Mp(:,iter),r)
ELSE
DO I=1,MASK
SUM1 = SUM ( Mp(:,MASK) * r )
ENDDO
ENDIF
IF ((iter.EQ.1) THEN)
alpha2 = SUM1 / SUM(Mp(1:iter,:) * Mp(:,1:iter))
x = x + alpha2 * p(:,iter)
r = r - alpha2 * Mp(:,iter) ! where is the *(Cnew - C)?
!Check convergence
normr=norm2(r)
norm
!fprintf('norm(r) = !g iter = !gNewLine',normr,iter+1);
if (normr < tol) then
!fprintf('GCR Converged, iter = !gNewLine',iter+1);
converged=1
exit
end if
END DO !for loop
if (normr > tol) then
write(*,*) 'GCR SOLUTION DID NOT CONVERGE!'
converged=0
end if
RETURN
END subroutine gcr_Mfree
以下是将MATLAB格式转换为FORTRAN的主要问题:
我不知道如何找到收敛条件的向量和矩阵的范数? 2008年标准有一个内在声明(nomr2)。 norm2 statement in 2008 standard
我不知道如何正确更新代码来计算p,mp,alpha2?
alpha2等式:
alpha2 = r'*Mp(:,iter)) / (Mp(iter,:) * Mp(:,iter))
alpha2 definition in MATLAB when iter = 2
alpha2 = (Nx1)'*(Nx2) / (Nx2)' * (Nx2)
(1x2) / (2x2)
(1x2)
我找到了 gemv 函数和这个描述向量到矩阵数组操作的链接。
答案 0 :(得分:1)
这是&#34;零序&#34;的一个例子。转换代码的版本。因为我对Matlab语法不是很熟悉,所以代码可能包含一些错误或者可能没有精确地反映原始代码(我提到this tutorial),但我想它至少可以作为更多的起点。精确转换...
subroutine gcr_Mfree( F, Cnew, Cold, Fold, alpha, x, converged )
!! Generalized Conjugate Residual Algorithm
!! Solves M*x=b (J*dx=-F)
!! (M(:,:) does not seem to be used in the code...
!! Funct2() may be multiplying M(:,:) inside.)
use my_module, only: h, n, M
use iso_fortran_env, only: dp => real64
implicit none
!! Dummy arguments.
real(dp), dimension( n ) :: F, Cnew, Cold, Fold, x
real(dp) :: alpha
integer :: converged
!! Locals.
integer :: iter, maxiter, j
real(dp) :: alpha2, tol, normr
real(dp), allocatable :: b(:), r(:), F2(:), Mr(:), p(:,:), Mp(:,:)
!! This interface block should be deleted when Funct2() is defined in some module.
interface
function Funct2( Cnew, Cold, Fold, alpha ) result( ret )
import dp, n
real(dp) :: Cnew( n ), Cold( n ), Fold( n ), alpha, ret( n )
endfunction
endinterface
allocate( b(n), r(n), F2(n), Mr(n), &
p( n, maxiter ), Mp( n, maxiter ) )
b(:) = -F(:)
tol = 5.e-3_dp
maxiter = size( b )
x(:) = 0.0_dp
r(:) = b(:)
normr = norm2( r ) !! or sqrt(sum( r(:)**2 )) if norm2() not available
alpha2 = 1e-6_dp
do iter = 1, maxiter
!! Get preliminary search direction
p(:,iter) = r(:)
!! Approximate the Jacobian(M) residual product
Cnew(:) = Cnew(:) + alpha2 * r(:)
F2(:) = Funct2( Cnew, Cold, Fold, alpha )
Mr(:) = 1 / alpha2 * ( F2(:) - F(:) )
Mp(:,iter) = Mr(:)
!! Orthogonalize search direction
do j = 1, iter-1
p(:,iter) = p(:,iter) - sum( Mp(:,j) * Mp(:,iter) ) * p(:,j)
Mp(:,iter) = Mp(:,iter) - sum( Mp(:,j) * Mp(:,iter) ) * Mp(:,j)
enddo
!! Normalize search direction
p(:,iter) = p(:,iter) / norm2( Mp(:,iter) )
Mp(:,iter) = Mp(:,iter) / norm2( Mp(:,iter) )
!! Update solution and residual
alpha2 = sum( r(:) * Mp(:,iter) ) / sum( Mp(:,iter)**2 )
x(:) = x(:) + alpha2 * p(:,iter)
r(:) = r(:) - alpha2 * Mp(:,iter)
!! Check convergence
normr = norm2( r )
if ( normr < tol ) then
converged = 1
return
endif
enddo !! iter
if( normr > tol ) then
print *, "SOLUTION DID NOT CONVERGE!!"
converged = 0
endif
endsubroutine
更多细节:
根据上面的Matlab教程,在Matlab中区分列和行向量,单引号(&#39;)代表transpose,*
代表matrix multiplication,{ {3}}似乎与Fortran非常相似。
相反,正如评论中所解释的那样,Fortran中的列向量和行向量之间没有区别。 (关于制作Nx1或1xN 2-dim阵列,请参阅另一个答案。)。此外,*
表示逐元素乘法而非矩阵乘法,因此计算内积时需要sum()
或dot_product()
。
此外,Fortran中的矩阵不会动态地改变它们的大小(例如,通过动态增加列或行),因此需要事先分配给定大小(当然可以动态确定分配大小)。
调用Funct2()
可能需要谨慎。如果它是在某个模块中定义的,那么接口块应不提供(因为它由编译器自动处理)。如果在模块外部定义Funct2()
,我们需要手动编写接口块,如上所示。在这里,我假设Funct2()
定义如下:
function Funct2( Cnew, Cold, Fold, alpha ) result( ret )
use iso_fortran_env, only: dp => real64
use my_module, only: n, M
implicit none
real(dp) :: Cnew(n), Cold(n), Fold(n), alpha
real(dp) :: ret(n)
!! do some multiplication by M(:,:)...
endfunction
答案 1 :(得分:0)
如果要在Fortran中使用带有矩阵表示法的行向量和列向量,则必须将它们分配为(n,1)
和(1,n)
数组。例如
real :: x(10,1), A(10,10), chisq
chisq = matmul(transpose(x),matmul(A,x))
1-D数组既不是行也不是列向量,它只是一个数组。但这通常都是你需要的。以上示例可以使用1-D数组编写:
real :: x(10), A(10,10), chisq
chisq = sum(x*matmul(A,x))
数组不会自动增长,因此最简单的做法是分配所需的最大大小,如果你知道它将会是什么。但是,在您的情况下,您实际上并未在算法中引用iter
的先前值。如果Mp
只是一个工作变量并且本身并不感兴趣,那么您可以在Mp
中只使用一列并更新该列而不是添加新列。