在 Fortran 的牛顿方法中传递附加参数
Passing additional arguments in Newton’s method in Fortran
我在实现在 Fortran 程序中调用牛顿法的方法时遇到问题。
所以我想用牛顿法求解the link
后的方程
但是,我的程序与上面的例子略有不同。在我的例子中,方程式需要一些在运行时产生的额外信息。
subroutine solve(f, fp, x0, x, iters, debug)
这意味着f不仅是根据x计算的,还有一些其他变量(但x是未知数)。
我有一个解决方案,只适用于一个简单的案例:
我使用了一个模块来包含牛顿求解器。我还定义了一个派生数据类型来保存模块内的所有参数。现在效果很好。
我的问题是:我需要多次调用牛顿法,每次的参数都不一样。我应该如何设计模块的结构?或者我应该使用不同的解决方案?
我在下面提供了一个简单的例子:
module solver
type argu
integer :: m
end type argu
type(argu):: aArgu_test !should I put here?
contains
subroutine solve(f, fp, x0, x, iters, debug)
...
!m is used inside here
end subroutine solve
subroutine set_parameter(m_in)
aArgu%m = m_in
end subroutine set_parameter()
end module solver
调用模块是:
!only one set of argument, but used many times
module A
use module solver
do i = 1, 4, 1
set_parameter(i)
!call Newtow method
...
enddo
end module A
!can I use an array for argu type if possible?
module B
use module solver
type(argu), dimension(:), allocable :: aArgu ! or should I put here or inside solver module?
end module B
我的理解是,如果我将 argu 对象放在求解器模块中,那么所有求解器调用都将使用相同的参数(我仍然可以使用上述方法将它们全部保存在模块 A 中)。在那种情况下,我必须在每个 for 循环期间更新参数?
因为程序运行使用MPI/OpenMP,我想确保线程之间没有覆盖。
谢谢。
经过与同事的讨论,我的问题2有了解决方案
如果求解器模块只有一个参数对象,那么所有调用都将访问相同的参数,因为它们共享相同的内存space。
为避免这种情况,我们希望将参数对象作为参数传递给求解器。
因此,我们将不使用默认的求解器子程序,而是重写牛顿法,使其可以接受额外的参数。
(我之前使用了最简单的牛顿子程序,因为我想保持原状。)
这样,我们将定义一个参数对象数组,并在运行时传递它们。
感谢您的评论。
在现代 Fortran 中有一个通用模式可以解决您面临的问题 (partial function application)。与其他语言不同,Fortran 没有函数闭包,因此为函数创建词法作用域有点 "convoluted" 并且有点受限。
您真的应该考虑重新访问评论中分享的所有 link@VladmirF,其中大部分都直接适用于您的案例。我会给你一个解决方案的例子。
这是一个不使用包装类型的解决方案。我将使用 Fortran 2008 标准中包含的一项功能:将内部过程作为参数传递。它与最新的 gfortran、Intel 和许多其他公司兼容。
如果您无法访问具有此功能的编译器,或者您更喜欢具有派生类型的解决方案,您可以参考 答案。
module without_custom_type
use, intrinsic :: iso_fortran_env, only: r8 => real64
use :: solver
contains
subroutine solve_quad(a, b, c, x0, x, iters, debug)
integer, intent(in) :: a, b, c
real(r8), intent(in) :: x0
real(r8), intent(out) :: x
integer, intent(out) :: iters
logical, intent(in) :: debug
call solve(f, fp, x0, x, iters, debug)
contains
real(r8) function f(x)
real(r8),intent(in) :: x
f = a * x * x + b * x + c
end
real(r8) function fp(x)
real(r8),intent(in) :: x
fp = 2 * a * x + b
end
end
end
此代码的基本原理是:由于 f
和 fp
位于 solve_quad
过程内部,它们可以访问参数 a
、b
和 c
通过主机关联,而不触及这些函数的签名。产生的效果就像改变函数的元数一样。
使用 gfortran 8.0 和您分享的 link 中的 solver
实现对其进行测试,我得到了这个:
program test
use, intrinsic :: iso_fortran_env, only: r8 => real64
use :: without_custom_type
implicit none
real(r8) :: x, x0
integer :: iters
integer :: a = 1, b = -5, c = 4
x0 = 0
call solve_quad(a, b, c, x0, x, iters, .false.)
print *, x, iters
! output: 1.0000000000000000, 5
x0 = 7
call solve_quad(a, b, c, x0, x, iters, .false.)
print *, x, iters
! output: 4.0000000000000000, 6
end
我在实现在 Fortran 程序中调用牛顿法的方法时遇到问题。 所以我想用牛顿法求解the link
后的方程但是,我的程序与上面的例子略有不同。在我的例子中,方程式需要一些在运行时产生的额外信息。
subroutine solve(f, fp, x0, x, iters, debug)
这意味着f不仅是根据x计算的,还有一些其他变量(但x是未知数)。
我有一个解决方案,只适用于一个简单的案例: 我使用了一个模块来包含牛顿求解器。我还定义了一个派生数据类型来保存模块内的所有参数。现在效果很好。
我的问题是:我需要多次调用牛顿法,每次的参数都不一样。我应该如何设计模块的结构?或者我应该使用不同的解决方案?
我在下面提供了一个简单的例子:
module solver
type argu
integer :: m
end type argu
type(argu):: aArgu_test !should I put here?
contains
subroutine solve(f, fp, x0, x, iters, debug)
...
!m is used inside here
end subroutine solve
subroutine set_parameter(m_in)
aArgu%m = m_in
end subroutine set_parameter()
end module solver
调用模块是:
!only one set of argument, but used many times
module A
use module solver
do i = 1, 4, 1
set_parameter(i)
!call Newtow method
...
enddo
end module A
!can I use an array for argu type if possible?
module B
use module solver
type(argu), dimension(:), allocable :: aArgu ! or should I put here or inside solver module?
end module B
我的理解是,如果我将 argu 对象放在求解器模块中,那么所有求解器调用都将使用相同的参数(我仍然可以使用上述方法将它们全部保存在模块 A 中)。在那种情况下,我必须在每个 for 循环期间更新参数?
因为程序运行使用MPI/OpenMP,我想确保线程之间没有覆盖。 谢谢。
经过与同事的讨论,我的问题2有了解决方案
如果求解器模块只有一个参数对象,那么所有调用都将访问相同的参数,因为它们共享相同的内存space。
为避免这种情况,我们希望将参数对象作为参数传递给求解器。 因此,我们将不使用默认的求解器子程序,而是重写牛顿法,使其可以接受额外的参数。
(我之前使用了最简单的牛顿子程序,因为我想保持原状。)
这样,我们将定义一个参数对象数组,并在运行时传递它们。
感谢您的评论。
在现代 Fortran 中有一个通用模式可以解决您面临的问题 (partial function application)。与其他语言不同,Fortran 没有函数闭包,因此为函数创建词法作用域有点 "convoluted" 并且有点受限。
您真的应该考虑重新访问评论中分享的所有 link@VladmirF,其中大部分都直接适用于您的案例。我会给你一个解决方案的例子。
这是一个不使用包装类型的解决方案。我将使用 Fortran 2008 标准中包含的一项功能:将内部过程作为参数传递。它与最新的 gfortran、Intel 和许多其他公司兼容。
如果您无法访问具有此功能的编译器,或者您更喜欢具有派生类型的解决方案,您可以参考
module without_custom_type
use, intrinsic :: iso_fortran_env, only: r8 => real64
use :: solver
contains
subroutine solve_quad(a, b, c, x0, x, iters, debug)
integer, intent(in) :: a, b, c
real(r8), intent(in) :: x0
real(r8), intent(out) :: x
integer, intent(out) :: iters
logical, intent(in) :: debug
call solve(f, fp, x0, x, iters, debug)
contains
real(r8) function f(x)
real(r8),intent(in) :: x
f = a * x * x + b * x + c
end
real(r8) function fp(x)
real(r8),intent(in) :: x
fp = 2 * a * x + b
end
end
end
此代码的基本原理是:由于 f
和 fp
位于 solve_quad
过程内部,它们可以访问参数 a
、b
和 c
通过主机关联,而不触及这些函数的签名。产生的效果就像改变函数的元数一样。
使用 gfortran 8.0 和您分享的 link 中的 solver
实现对其进行测试,我得到了这个:
program test
use, intrinsic :: iso_fortran_env, only: r8 => real64
use :: without_custom_type
implicit none
real(r8) :: x, x0
integer :: iters
integer :: a = 1, b = -5, c = 4
x0 = 0
call solve_quad(a, b, c, x0, x, iters, .false.)
print *, x, iters
! output: 1.0000000000000000, 5
x0 = 7
call solve_quad(a, b, c, x0, x, iters, .false.)
print *, x, iters
! output: 4.0000000000000000, 6
end