Gemini-Fortran-agent
April 18, 2025 ยท View on GitHub
Python script that uses Gemini LLMs to create Fortran programs, iterating with compiler error messages until they compile. You need to get a free Gemini API key and store it in gemini_key.txt. I made similar Fortran coding agents using Groq Cloud and OpenAI. Sample output:
c:\python\code\gemini>python xgemini.py
prompt:
Do a Fortran simulation to find the optimal trimmed mean estimator of
the location of the Cauchy distribution, trying trimming proportions
of 0%, 10%, 20%, 30%, 40%, and 45%. Declare real variables as
real(kind=dp) with dp a module constant, and put procedures in a
module. Have the simulation use 100 samples of 1000 observations each.
Only output Fortran code. Do not give commentary.
model: gemini-2.5-pro-exp-03-25
Attempt 1 failed with error (generation time: 38.606 seconds, LOC=151):
cauchy.f90:128:30:
128 | real(dp) :: current_alpha, current_tmean
| 1~~~~~~~~~~~~
Error: Unused variable 'current_tmean' declared at (1) [-Werror=unused-variable]
f951.exe: some warnings being treated as errors
Code compiled successfully after 2 attempts (generation time: 18.096 seconds, LOC=158)!
Final version:
module simulation_utilities
implicit none
private
public :: dp, generate_cauchy, trimmed_mean
integer, parameter :: dp = selected_real_kind(p=15, r=300)
real(dp), parameter :: pi = acos(-1.0_dp)
contains
subroutine generate_cauchy(n, x)
integer, intent(in) :: n
real(dp), intent(out) :: x(n)
real(dp) :: u(n)
integer :: i
! Generate uniform random numbers (0, 1)
call random_number(u)
! Apply inverse CDF method for standard Cauchy
do i = 1, n
! Avoid u exactly 0 or 1, though probability is negligible
if (u(i) == 0.0_dp) u(i) = epsilon(u(i))
if (u(i) == 1.0_dp) u(i) = 1.0_dp - epsilon(u(i))
x(i) = tan(pi * (u(i) - 0.5_dp))
end do
end subroutine generate_cauchy
! Simple selection sort (in-place)
subroutine selection_sort(arr)
real(dp), intent(inout) :: arr(:)
integer :: n, i, j, min_idx
real(dp) :: temp
n = size(arr)
if (n <= 1) return
do i = 1, n - 1
min_idx = i
do j = i + 1, n
if (arr(j) < arr(min_idx)) then
min_idx = j
end if
end do
! Swap arr(i) and arr(min_idx)
if (min_idx /= i) then
temp = arr(i)
arr(i) = arr(min_idx)
arr(min_idx) = temp
end if
end do
end subroutine selection_sort
function trimmed_mean(x, alpha) result(t_mean)
real(dp), intent(in) :: x(:)
real(dp), intent(in) :: alpha
real(dp) :: t_mean
integer :: n, k, i
real(dp), allocatable :: sorted_x(:)
real(dp) :: sum_trimmed
n = size(x)
if (n == 0) then
t_mean = 0.0_dp ! Or NaN, or handle error
return
end if
! Calculate number of elements to trim from each end
k = floor(real(n, dp) * alpha)
! Ensure k is within valid bounds
k = max(0, k)
if (2 * k >= n) then
! Handle case where trimming leaves no elements or negative number
! For alpha < 0.5 this should not happen if n > 0
if (n > 0) then
! If alpha >= 0.5, could return median or handle as error
print *, "Warning: Trimming proportion alpha=", alpha, " is too high (>= 0.5 or leaves <= 0 elements). Returning median."
allocate(sorted_x(n))
sorted_x = x
call selection_sort(sorted_x)
if (mod(n, 2) == 1) then
t_mean = sorted_x(n/2 + 1) ! Odd number of elements
else
t_mean = (sorted_x(n/2) + sorted_x(n/2 + 1)) / 2.0_dp ! Even number of elements
end if
deallocate(sorted_x)
else
t_mean = 0.0_dp ! Should not happen if n > 0 check passed
end if
return
end if
! Allocate and copy data to sort
allocate(sorted_x(n))
sorted_x = x
! Sort the data
call selection_sort(sorted_x)
! Sum the middle elements
sum_trimmed = 0.0_dp
do i = k + 1, n - k
sum_trimmed = sum_trimmed + sorted_x(i)
end do
! Calculate the mean of the trimmed sample
if ( (n - 2 * k) > 0 ) then
t_mean = sum_trimmed / real(n - 2 * k, dp)
else
! This case should be caught by the check 'if (2 * k >= n)' above,
! but adding a safeguard here.
print *, "Error: Zero elements remaining after trimming. Check alpha and n."
t_mean = 0.0_dp ! Or NaN
end if
deallocate(sorted_x)
end function trimmed_mean
end module simulation_utilities
program cauchy_simulation
use simulation_utilities
implicit none
integer, parameter :: nsamp = 100 ! Number of samples (replications)
integer, parameter :: nobs = 1000 ! Number of observations per sample
real(dp), parameter :: alphas(*) = [0.0_dp, 0.10_dp, 0.20_dp, 0.30_dp, 0.40_dp, 0.45_dp]
integer, parameter :: num_alphas = size(alphas)
real(dp), allocatable :: sample(:) ! Holds one sample of Cauchy deviates
real(dp) :: estimates(nsamp) ! Holds trimmed means for one alpha across samples
real(dp) :: mse(num_alphas) ! Holds Mean Squared Error for each alpha
real(dp) :: current_alpha ! Variable 'current_tmean' removed as it was unused
real(dp) :: min_mse, optimal_alpha
integer :: i, j, optimal_idx
! Allocate memory for one sample
allocate(sample(nobs))
! Initialize random seed
call random_seed()
print *, "Starting Cauchy location estimation simulation..."
print *, "Number of samples (replications): ", nsamp
print *, "Observations per sample: ", nobs
print *, "--------------------------------------------------"
! Loop over trimming proportions
do i = 1, num_alphas
current_alpha = alphas(i)
print '(A, F4.2, A)', "Processing alpha = ", current_alpha * 100.0_dp, "% ..."
! Loop over samples (replications) for the current alpha
do j = 1, nsamp
! Generate a new sample from the standard Cauchy distribution
call generate_cauchy(nobs, sample)
! Calculate the trimmed mean for this sample
estimates(j) = trimmed_mean(sample, current_alpha)
end do
! Calculate the Mean Squared Error (MSE) for the current alpha
! Since the true location of the standard Cauchy is 0, MSE = mean(estimate^2)
mse(i) = sum(estimates**2) / real(nsamp, dp)
print '(A, F6.2, A, E12.5)', " Alpha = ", current_alpha*100.0_dp, "% | MSE = ", mse(i)
end do ! End loop over alphas
print *, "--------------------------------------------------"
print *, "Simulation complete. Finding optimal alpha..."
! Find the alpha with the minimum MSE
min_mse = mse(1)
optimal_alpha = alphas(1)
optimal_idx = 1
do i = 2, num_alphas
if (mse(i) < min_mse) then
min_mse = mse(i)
optimal_alpha = alphas(i)
optimal_idx = i
end if
end do
print '(A, F6.2, A)', "Optimal trimming proportion: ", optimal_alpha * 100.0_dp, "%"
print '(A, E12.5)', "Minimum Mean Squared Error: ", min_mse
print *, "--------------------------------------------------"
! Deallocate memory
deallocate(sample)
end program cauchy_simulation
Running executable: .\cauchy.exe
Output:
Starting Cauchy location estimation simulation...
Number of samples (replications): 100
Observations per sample: 1000
--------------------------------------------------
Processing alpha = 0.00% ...
Alpha = 0.00% | MSE = 0.46403E+04
Processing alpha = ****% ...
Alpha = 10.00% | MSE = 0.43341E-02
Processing alpha = ****% ...
Alpha = 20.00% | MSE = 0.36191E-02
Processing alpha = ****% ...
Alpha = 30.00% | MSE = 0.21073E-02
Processing alpha = ****% ...
Alpha = 40.00% | MSE = 0.24019E-02
Processing alpha = ****% ...
Alpha = 45.00% | MSE = 0.30734E-02
--------------------------------------------------
Simulation complete. Finding optimal alpha...
Optimal trimming proportion: 30.00%
Minimum Mean Squared Error: 0.21073E-02
--------------------------------------------------
Total generation time: 56.702 seconds across 2 attempts
Compilation command: gfortran -O0 -fmax-errors=1 -Wall -Werror=unused-parameter -Werror=unused-variable -Werror=unused-function -Wno-maybe-uninitialized -Wno-surprising -fbounds-check -static -g -o cauchy cauchy.f90