OpenAI-Fortran-agent
April 18, 2025 ยท View on GitHub
Python script that uses OpenAI LLMs to create Fortran programs, iterating with compiler error messages until they compile.
An OpenAI key and credit card are needed. I made similar Fortran agents using Groq Cloud and Google Gemini. Here is a sample run that cost $0.07. The resulting code is saved here
as mix.f90.
c:\python\code\openai\OpenAI-Fortran-agent>python xopenai.py
prompt:
Write a Fortran subroutine in a module that fits a finite mixture of
normals with a specified # of components to univariate data, using the
EM algorithm, and write a main program that tests it for data
simulated from a known mixture distribution. Declare real variables as
real(kind=dp) with
integer, parameter :: dp = kind(1.0d0)
Define a derived type to store the parameters of a finite mixture, and
write a subroutine to display the parameters in a formatted table. Use
the subroutine to display the true and estimated parameters. Declare
procedures as pure or elemental when feasible.
Only output Fortran code. Do not give commentary.
model: o1-mini
Attempt 1 failed with error (generation time: 21.036 seconds, LOC=123):
mix.f90:51:40:
51 | prev_log_likelihood = -1.0d20_dp
| 1
Error: Real number at (1) has a 'd' exponent and an explicit kind
compilation terminated due to -fmax-errors=1.
Attempt 2 failed with error (generation time: 17.532 seconds, LOC=123):
mix.f90:5:47:
5 | real(kind=dp), parameter :: tol = 1.0d-6_dp
| 1
Error: Real number at (1) has a 'd' exponent and an explicit kind
compilation terminated due to -fmax-errors=1.
Attempt 3 failed with error (generation time: 17.101 seconds, LOC=123):
mix.f90:41:39:
41 | real(kind=dp), allocatable :: gamma_mat(n, k)
| 1~~~~~~~~~~~~~~
Error: Allocatable array 'gamma_mat' at (1) must have a deferred shape or assumed rank
compilation terminated due to -fmax-errors=1.
Attempt 4 failed with error (generation time: 17.158 seconds, LOC=123):
mix.f90:112:25:
112 | call random_seed(get=seed)
| 1
Error: Size of 'get' argument of 'random_seed' intrinsic at (1) too small (2/8)
Code compiled successfully after 5 attempts (generation time: 18.645 seconds, LOC=131)!
Final version:
module mixture_module
implicit none
integer, parameter :: dp = kind(1.0d0)
integer, parameter :: max_iter = 1000
real(kind=dp), parameter :: tol = 1.0e-6_dp
type :: mixture_parameters
real(kind=dp), allocatable :: weights(:)
real(kind=dp), allocatable :: means(:)
real(kind=dp), allocatable :: stddevs(:)
end type mixture_parameters
contains
pure function gaussian_pdf(x, mean, stddev) result(pdf)
real(kind=dp), intent(in) :: x, mean, stddev
real(kind=dp) :: pdf
pdf = (1.0_dp / (sqrt(2.0_dp * 3.14159265358979323846_dp) * stddev)) * &
exp(-0.5_dp * ((x - mean)/stddev)**2)
end function gaussian_pdf
subroutine display_parameters(params, title)
implicit none
type(mixture_parameters), intent(in) :: params
character(*), intent(in) :: title
integer :: i
print *, title
print '(A)', 'Comp | Weight | Mean | Std Dev '
do i = 1, size(params%weights)
print '(I4, 2X, F10.6, 2X, F10.6, 2X, F10.6)', &
i, params%weights(i), params%means(i), params%stddevs(i)
end do
end subroutine display_parameters
subroutine EM_fit(data, n, k, params)
implicit none
integer, intent(in) :: n, k
real(kind=dp), intent(in) :: data(n)
type(mixture_parameters), intent(out) :: params
real(kind=dp), allocatable :: weights(:), means(:), stddevs(:)
real(kind=dp), allocatable :: gamma_mat(:,:)
integer :: iter, i, j
real(kind=dp) :: log_likelihood, prev_log_likelihood, diff
real(kind=dp) :: sum_gamma
allocate(weights(k), means(k), stddevs(k), gamma_mat(n, k))
weights = 1.0_dp / k
means = data(1:k)
stddevs = 1.0_dp
iter = 0
prev_log_likelihood = -1.0e20_dp
do while (iter < max_iter)
! E-step
do i = 1, n
sum_gamma = 0.0_dp
do j = 1, k
gamma_mat(i,j) = weights(j) * gaussian_pdf(data(i), means(j), stddevs(j))
sum_gamma = sum_gamma + gamma_mat(i,j)
end do
if (sum_gamma > 0.0_dp) then
gamma_mat(i, :) = gamma_mat(i, :) / sum_gamma
else
gamma_mat(i, :) = 1.0_dp / k
end if
end do
! M-step
do j = 1, k
sum_gamma = sum(gamma_mat(:,j))
weights(j) = sum_gamma / n
means(j) = sum(gamma_mat(:,j) * data) / sum_gamma
stddevs(j) = sqrt(sum(gamma_mat(:,j) * (data - means(j))**2) / sum_gamma)
end do
! Compute log-likelihood
log_likelihood = 0.0_dp
do i = 1, n
sum_gamma = 0.0_dp
do j = 1, k
sum_gamma = sum_gamma + weights(j) * gaussian_pdf(data(i), means(j), stddevs(j))
end do
log_likelihood = log_likelihood + log(sum_gamma)
end do
diff = log_likelihood - prev_log_likelihood
if (abs(diff) < tol) exit
prev_log_likelihood = log_likelihood
iter = iter + 1
end do
params%weights = weights
params%means = means
params%stddevs = stddevs
deallocate(weights, means, stddevs, gamma_mat)
end subroutine EM_fit
end module mixture_module
program test_mixture
use mixture_module
implicit none
integer, parameter :: n = 1000
integer, parameter :: k = 2
integer :: i, component
real(kind=dp), allocatable :: data(:)
type(mixture_parameters) :: true_params, est_params
real(kind=dp) :: u1, u2, z0
integer :: seed_size
integer, allocatable :: seed(:)
! Determine the required size for the seed array
call random_seed(size=seed_size)
allocate(seed(seed_size))
! Initialize the random seed
call random_seed()
! Retrieve the current seed values
call random_seed(get=seed)
! Define true parameters
allocate(true_params%weights(k), true_params%means(k), true_params%stddevs(k))
true_params%weights = (/ 0.3_dp, 0.7_dp /)
true_params%means = (/ -2.0_dp, 3.0_dp /)
true_params%stddevs = (/ 1.0_dp, 0.5_dp /)
! Simulate data using Box-Muller
allocate(data(n))
do i = 1, n
call random_number(u1)
if (u1 < true_params%weights(1)) then
component = 1
else
component = 2
end if
call random_number(u1)
call random_number(u2)
z0 = sqrt(-2.0_dp * log(u1)) * cos(2.0_dp * 3.14159265358979323846_dp * u2)
data(i) = true_params%means(component) + true_params%stddevs(component) * z0
end do
! Fit EM
call EM_fit(data, n, k, est_params)
! Display results
call display_parameters(true_params, 'True Parameters:')
call display_parameters(est_params, 'Estimated Parameters:')
! Deallocate allocated arrays
deallocate(true_params%weights, true_params%means, true_params%stddevs, data, seed)
end program test_mixture
Running executable: .\mix.exe
Output:
True Parameters:
Comp | Weight | Mean | Std Dev
1 0.300000 -2.000000 1.000000
2 0.700000 3.000000 0.500000
Estimated Parameters:
Comp | Weight | Mean | Std Dev
1 0.699392 2.966891 0.479197
2 0.300608 -2.058820 0.966146
Total generation time: 91.473 seconds across 5 attempts
Compilation command: gfortran -O0 -fmax-errors=1 -o mix mix.f90