I have a text file (S.txt) which contains a certain parameter with its amount in each interval. Something like this:
-
-
x=a
-
-
x=b
-
-
x=c
-
-
.
.
.
I want to write Fortran code to open the text file (S.txt) and read it in order to find each 'x' and read its amount into a parameter. something like this:
a
b
c
.
.
.
I have come up with this code but it does not work:
PROGRAM deter
IMPLICIT NONE
real,Dimension(2) :: value
open(unit=40,file='D:\S.txt',action='read')
READ(40,fmt='(2X,f3.3)') value
close(40)
END PROGRAM deter
when I run this program I get NO ERROR, but it doesn't work either.
any suggestion?
program extract_value
implicit none
integer :: ios
character(len=200), allocatable :: command(:)
character(len=200), allocatable :: word(:)
real, allocatable :: x(:), deter(:)
character(len=200) :: line
integer :: n, i, j, r
character (len=5), parameter :: sstr='x='
open(unit=50, file='D:\S.txt', iostat=ios)
if ( ios /= 0 ) stop "Error opening file S.txt"
n = 0
do
read(50, '(A)', iostat=ios) line
if (ios /= 0) exit
n = n + 1
end do
allocate(command(n))
allocate(word(n))
rewind(50)
j=0
do i = 1, n
read(50,'(A)') command(i)
read (command(i),'(a2)') word(i)
if (word(i)==sstr) then
j=j+1
end if
end do
allocate(x(n))
allocate(deter(j))
x=0
do i = 1, n
if (word(i)==sstr) then
read(command(i), fmt='(2X,f5.2)') x(i)
end if
end do
deter=0
deter=pack(x, x /= 0)
close(50)
open(unit=100, file='D:\R.txt', action="write",status="replace")
WRITE(100,fmt='(2X,f5.2)')(deter(r), r=1,j)
close(100)
end program extract_value
I've written a minimal implementation for the fast xoroshiro128plus pseudo-random number generator in Fortran to replace the intrinsic random_number. This implementation is quite fast (4X faster than random_number) and the quality is good enough for my purposes, I don't use it in cryptography applications.
My question is how can I optimize this subroutine to get the last drop of performance from my compiler, even 10% improvement is appreciated. This subroutine is to be used in tight loops inside long simulations. I'm interested more in generating a single random number at a time and not big vectors or nD arrays at once.
Here is a test program to give you some context about how my subroutine is used:
program test_xoroshiro128plus
implicit none
integer, parameter :: n = 10000
real*8 :: A(n,n)
integer :: i, j, t0, t1, count_rate, count_max
call system_clock(t0, count_rate, count_max)
do j = 1,n
do i = 1,n
call drand128(A(i,j))
end do
end do
! call drand128(A) ! works also with 2D
call system_clock(t1)
print *, "Time :", real(t1-t0)/count_rate
print *, "Mean :", sum(A)/size(A), char(10), A(1:2,1:3)
contains
impure elemental subroutine drand128(r)
real*8, intent(out) :: r
integer*8 :: s0 = 113, s1 = 19937
s1 = xor(s0,s1)
s0 = xor(xor(ior(ishft(s0,55), ishft(s0,-9)),s1), ishft(s1,14))
s1 = ior(ishft(s1,36), ishft(s1,-28))
r = ishft(s0+s1, -1) / 9223372036854775808.d0
end
end program
Only now I realized you are asking about this particular PRNG. I am using it in Fortran myself https://bitbucket.org/LadaF/elmm/src/eb5b54b9a8eb6af158a38038f72d07865fe23ee3/src/rng_par_zig.f90?at=master&fileviewer=file-view-default
My code in the link is slower than yours, because it calls several subroutines and aims to be more universal. Bet let's try to condense the code I use into a single subroutine.
So let's just compare the performance of your code and the optimized version by #SeverinPappadeux and my optimized code with Gfortran 4.8.5
> gfortran -cpp -O3 -mtune=native xoroshiro.f90
Time drand128 sub: 1.80900002
Time drand128 fun: 1.80900002
Time rng_uni: 1.32900000
the code is here, remember to let the CPU spin-up, the first iteration of the k loop is just garbage!!!
program test_xoroshiro128plus
use iso_fortran_env
implicit none
integer, parameter :: n = 30000
real*8 :: A(n,n)
real*4 :: B(n,n)
integer :: i, j, k, t0, t1, count_rate, count_max
integer(int64) :: s1 = int(Z'1DADBEEFBAADD0D0', int64), s2 = int(Z'5BADD0D0DEADBEEF', int64)
!let the CPU spin-up
do k = 1, 3
call system_clock(t0, count_rate, count_max)
do j = 1,n
do i = 1,n
call drand128(A(i,j))
end do
end do
! call drand128(A) ! works also with 2D
call system_clock(t1)
print *, "Time drand128 sub:", real(t1-t0)/count_rate
call system_clock(t0, count_rate, count_max)
do j = 1,n
do i = 1,n
A(i,j) = drand128_fun()
end do
end do
! call drand128(A) ! works also with 2D
call system_clock(t1)
print *, "Time drand128 fun:", real(t1-t0)/count_rate
call system_clock(t0, count_rate, count_max)
do j = 1,n
do i = 1,n
call rng_uni(A(i,j))
end do
end do
call system_clock(t1)
print *, "Time rng_uni:", real(t1-t0)/count_rate
end do
print *, "Mean :", sum(A)/size(A), char(10), A(1:2,1:3)
contains
impure elemental subroutine drand128(r)
real*8, intent(out) :: r
integer*8 :: s0 = 113, s1 = 19937
s1 = xor(s0,s1)
s0 = xor(xor(ior(ishft(s0,55), ishft(s0,-9)),s1), ishft(s1,14))
s1 = ior(ishft(s1,36), ishft(s1,-28))
r = ishft(s0+s1, -1) / 9223372036854775808.d0
end
impure elemental real*8 function drand128_fun()
real*8, parameter :: c = 1.0d0/9223372036854775808.d0
integer*8 :: s0 = 113, s1 = 19937
s1 = xor(s0,s1)
s0 = xor(xor(ior(ishft(s0,55), ishft(s0,-9)),s1), ishft(s1,14))
s1 = ior(ishft(s1,36), ishft(s1,-28))
drand128_fun = ishft(s0+s1, -1) * c
end
impure elemental subroutine rng_uni(fn_val)
real(real64), intent(inout) :: fn_val
integer(int64) :: ival
ival = s1 + s2
s2 = ieor(s2, s1)
s1 = ieor( ieor(rotl(s1, 24), s2), shiftl(s2, 16))
s2 = rotl(s2, 37)
ival = ior(int(Z'3FF0000000000000',int64), shiftr(ival, 12))
fn_val = transfer(ival, 1.0_real64) - 1;
end subroutine
function rotl(x, k)
integer(int64) :: rotl
integer(int64) :: x
integer :: k
rotl = ior( shiftl(x, k), shiftr(x, 64-k))
end function
end program
The main difference should come from the faster and better way to convert from integers to reals http://experilous.com/1/blog/post/perfect-fast-random-floating-point-numbers#half-open-range
If you are bored, you could try to inline rotl() manually, but I trust the compiler here.
Ok, here is my attempt. First, I made it to function - in x64 or similar ABI function returning float value do in in register - much faster than parameter transfer. Second,
replaced final division by multiplication, though Intel compiler might do it for you.
Timing, Intel i7 6820, WSL, Ubuntu 18.04:
before - 0.850000024
after - 0.601000011
GNU Fortran 7.3.0, command line
gfortran -std=gnu -O3 -ffast-math -mavx2 /mnt/c/Users/kkk/Documents/CPP/a.for
Code
program test_xoroshiro128plus
implicit none
integer, parameter :: n = 10000
real*8 :: A(n,n)
integer :: i, j, t0, t1, count_rate, count_max
call system_clock(t0, count_rate, count_max)
do j = 1,n
do i = 1,n
A(i,j) = drand128()
end do
end do
A = drand128() ! works also with 2D
call system_clock(t1)
print *, "Time :", real(t1-t0)/count_rate
print *, "Mean :", sum(A)/size(A), char(10), A(1:2,1:3)
contains
impure elemental real*8 function drand128()
real*8, parameter :: c = 1.0d0/9223372036854775808.d0
integer*8 :: s0 = 113, s1 = 19937
s1 = xor(s0,s1)
s0 = xor(xor(ior(ishft(s0,55), ishft(s0,-9)),s1), ishft(s1,14))
s1 = ior(ishft(s1,36), ishft(s1,-28))
drand128 = ishft(s0+s1, -1) * c
end
end program
I'm trying to write a simple function in VBA that will test a real value and output a string result if it's a perfect cube. Here's my code:
Function PerfectCubeTest(x as Double)
If (x) ^ (1 / 3) = Int(x) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
As you can see, I'm using a simple if statement to test if the cube root of a value is equal to its integer portion (i.e. no remainder). I tried testing the function with some perfect cubes (1, 8, 27, 64, 125), but it only works for the number 1. Any other value spits out the "Flawed" case. Any idea what's wrong here?
You are testing whether the cube is equal to the double supplied.
So for 8 you would be testing whether 2 = 8.
EDIT: Also found a floating point issue. To resolve we will round the decimals a little to try and overcome the issue.
Change to the following:
Function PerfectCubeTest(x As Double)
If Round((x) ^ (1 / 3), 10) = Round((x) ^ (1 / 3), 0) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
Or (Thanks to Ron)
Function PerfectCubeTest(x As Double)
If CDec(x ^ (1 / 3)) = Int(CDec(x ^ (1 / 3))) Then
PerfectCubeTest = "Perfect"
Else
PerfectCubeTest = "Flawed"
End If
End Function
#ScottCraner correctly explains why you were getting incorrect results, but there are a couple other things to point out here. First, I'm assuming that you are taking a Double as input because the range of acceptable numbers is higher. However, by your implied definition of a perfect cube only numbers with an integer cube root (i.e. it would exclude 3.375) need to be evaluated. I'd just test for this up front to allow an early exit.
The next issue you run into is that 1 / 3 can't be represented exactly by a Double. Since you're raising to the inverse power to get your cube root you're also compounding the floating point error. There's a really easy way to avoid this - take the cube root, cube it, and see if it matches the input. You get around the rest of the floating point errors by going back to your definition of a perfect cube as an integer value - just round the cube root to both the next higher and next lower integer before you re-cube it:
Public Function IsPerfectCube(test As Double) As Boolean
'By your definition, no non-integer can be a perfect cube.
Dim rounded As Double
rounded = Fix(test)
If rounded <> test Then Exit Function
Dim cubeRoot As Double
cubeRoot = rounded ^ (1 / 3)
'Round both ways, then test the cube for equity.
If Fix(cubeRoot) ^ 3 = rounded Then
IsPerfectCube = True
ElseIf (Fix(cubeRoot) + 1) ^ 3 = rounded Then
IsPerfectCube = True
End If
End Function
This returned the correct result up to 1E+27 (1 billion cubed) when I tested it. I stopped going higher at that point because the test was taking so long to run and by that point you're probably outside of the range that you would reasonably need it to be accurate.
For fun, here is an implementation of a number-theory based method described here . It defines a Boolean-valued (rather than string-valued) function called PerfectCube() that tests if an integer input (represented as a Long) is a perfect cube. It first runs a quick test which throws away many numbers. If the quick test fails to classify it, it invokes a factoring-based method. Factor the number and check if the multiplicity of each prime factor is a multiple of 3. I could probably optimize this stage by not bothering to find the complete factorization when a bad factor is found, but I had a VBA factoring algorithm already lying around:
Function DigitalRoot(n As Long) As Long
'assumes that n >= 0
Dim sum As Long, digits As String, i As Long
If n < 10 Then
DigitalRoot = n
Exit Function
Else
digits = Trim(Str(n))
For i = 1 To Len(digits)
sum = sum + Mid(digits, i, 1)
Next i
DigitalRoot = DigitalRoot(sum)
End If
End Function
Sub HelperFactor(ByVal n As Long, ByVal p As Long, factors As Collection)
'Takes a passed collection and adds to it an array of the form
'(q,k) where q >= p is the smallest prime divisor of n
'p is assumed to be odd
'The function is called in such a way that
'the first divisor found is automatically prime
Dim q As Long, k As Long
q = p
Do While q <= Sqr(n)
If n Mod q = 0 Then
k = 1
Do While n Mod q ^ k = 0
k = k + 1
Loop
k = k - 1 'went 1 step too far
factors.Add Array(q, k)
n = n / q ^ k
If n > 1 Then HelperFactor n, q + 2, factors
Exit Sub
End If
q = q + 2
Loop
'if we get here then n is prime - add it as a factor
factors.Add Array(n, 1)
End Sub
Function factor(ByVal n As Long) As Collection
Dim factors As New Collection
Dim k As Long
Do While n Mod 2 ^ k = 0
k = k + 1
Loop
k = k - 1
If k > 0 Then
n = n / 2 ^ k
factors.Add Array(2, k)
End If
If n > 1 Then HelperFactor n, 3, factors
Set factor = factors
End Function
Function PerfectCubeByFactors(n As Long) As Boolean
Dim factors As Collection
Dim f As Variant
Set factors = factor(n)
For Each f In factors
If f(1) Mod 3 > 0 Then
PerfectCubeByFactors = False
Exit Function
End If
Next f
'if we get here:
PerfectCubeByFactors = True
End Function
Function PerfectCube(n As Long) As Boolean
Dim d As Long
d = DigitalRoot(n)
If d = 0 Or d = 1 Or d = 8 Or d = 9 Then
PerfectCube = PerfectCubeByFactors(n)
Else
PerfectCube = False
End If
End Function
Fixed the integer division error thanks to #Comintern. Seems to be correct up to 208064 ^ 3 - 2
Function isPerfectCube(n As Double) As Boolean
n = Abs(n)
isPerfectCube = n = Int(n ^ (1 / 3) - (n > 27)) ^ 3
End Function
It would be nice if someone could explain what causes function above return #value error.
Public Function papild(x)
Dim Sum As Double, A As Double, pi As Double,
Sum = 0.5 - (x - pi / 4)
A = -(x - pi / 4)
pi = Application.WorksheetFunction.pi()
Dim k As Integer, i As Integer
k = 2
i = 0
Do While Abs(A) > 0.0001
A = -A * 4 * A * A / (k + i) * (k + i + 1)
Sum = Sum + A
k = k + 1
i = i + 1
Loop
paplid = Sum
End Function
Function takes x value from MS Excel cell and it's equal = -1.5708 (=-PI()/2 #Formula Bar)
In lines 3 and 4 you work with variable pi before setting it in line 5...
Could there be some brackets missing in your formula. It basically says:
A = -4A^3 * (k+i+1)/(k+1)
This obviously drifts to +/- infinite so your loop cannot end.
Also there is a comma too much in the second line and a spelling error in the last line (paplid instead of papild).
Have you tried debugging the code?
When I run the code I get an overflow error # the 6th iteration of the while loop starting with x = -1.5708. Number gets to large to fit inside variable
.Other than that there are some minor things:
missing As Double
Public Function papild(x) As Double
and unnecessary comma at the end
Dim Sum As Double, A As Double, pi As Double,
Sorry for the terrible title, but I have no clue on how to generalize (or simplify) my loop case here.
I have a program that iterates to a sequence of integer, for example dimension=1 to 5.
In each iteration, there will be a main loop, and inside the main loop, there will be a nested loop. The number of the nested loop will be [dimension].
For example, in dimension=1, there is a For loop. In dimension=2, there is a For loop inside a For loop. And so on.
Is there any possible way to simplify the algorithm? currently I'm manually write totally different code for each value of [dimension]. Imagine if dimension=1 to 100? I'll be dead.
Here's my piece of program (written in VB.NET)
for dimension=2
Dim result(2) As Integer
For i = 0 To 1
For j = 0 To 1
result(0)=i
result(1)=j
Next
Next
For dimension=3
Dim result(3) As Integer
For i = 0 To 1
For j = 0 To 1
For k = 0 To 1
result(0)=i
result(1)=j
result(2)=k
Next
Next
Next
For dimension=4
Dim result(4) As Integer
For i = 0 To 1
For j = 0 To 1
For k = 0 To 1
For l = 0 To 1
result(0)=i
result(1)=j
result(2)=k
result(3)=l
Next
Next
Next
Next
And so on..
Any suggestion?
Thanks!
There are plenty of solutions:
Recursion
Idk, if vb.net supports methods, but if it does, this would probably be the simplest:
void nestedLoop(int lower , int upper , int remaining_loops , int[] values)
if(remaining_loops == 0)
//process values list
else
for int i in [lower , upper)
values[remaining_loops] = i
nestedLoop(lower , upper , remaining_loops - 1)
Integer Transformation
In theory, a number can be represented by any radix:
d_i * radix ^ i + d_i-1 * radix ^ (i - 1) ... + d_0 * radix ^ 0
Consider each digit the value of one of the nested loops:
for int i in [0 , max)
for int j in [0 , max)
for int k in [0 , max)
...
Could be represented by a 3-digit number with radix max, where d_0 = i, d_1 = j, etc.. Basically how each digit is mapped to one of the values can be arbitrary and will only affect the order of the output.
void nestedLoops(int upper , int dimension)
for int i in [0 , pow(upper , dimension))
int[] values
int digit_sub = 1
int tmp = i
for int j in [0 , dimension)
values[j] = tmp % dimension
tmp /= dimension
//all values of the loops are now in values
//process them here
There would be a few other options aswell, but these are the most common.
Please do note that when you do
Dim result(2) As Integer
You are actually declaring an array of 3 elements see this question for why. It's a subtle difference in VB.NET
That being said, I'll assume that you meant to declare an array of only 2 elements. If this is the case then you could build and call a recursive function like this
LoopOver(result)
Sub LoopOver(ByRef array() As Integer, ByVal Optional level As Integer = 0)
If array.Length = level Then
Return
Else
array(level) = 1
LoopOver(array, level + 1)
End If
End Sub
This recursive function will call itself (i.e., it will loop) for as many times as the array's size.