Created: 2021-09-09 Thu 12:52
presentations/lesHouches21
“If a program or package (the words are used interchangeably) is to have a long life and to be of wide application in its field, it is essential for it to be easily moved from one machine to another.
It used to be common to dismiss such movement with the statement, ‘There is no such thing as a machine-independent program.’
Nonetheless, a great many packages do now move from one machine to another”[lyonUsingAnsFortran1980]
–> Through the magic of automated coding and standards
“The standard is the contract between the compiler writer and the application developer.”[clermanModernFortranStyle2012]
character(10) BLAH*8 character*8 :: BLAH_ONE(10) character(8) :: BLAH_ONE(10)
#!/usr/bin/env python print("Hello World") print "Hello World"
Language | Files | Lines | Code | Comments | Blanks |
---|---|---|---|---|---|
C | 3 | 1023 | 694 | 191 | 138 |
C Header | 57 | 14237 | 11416 | 1041 | 1780 |
CMake | 11 | 430 | 361 | 16 | 53 |
C++ | 54 | 30745 | 26911 | 1560 | 2274 |
C++ Header | 1 | 8938 | 8297 | 348 | 293 |
FORTRAN | 20 | 1738 | 1344 | 174 | 220 |
Python | 2 | 224 | 191 | 4 | 29 |
Total | 148 | 57335 | 49214 | 3334 | 4787 |
program main print*, tiny(0.d0) end program
2.2250738585072014e-308
“I don’t care because I use libraries. ”
“I don’t care because I write my own algorithms.”
real(dp) function dsin(x) result(r) real(dp), parameter :: pi = 3.1415926535897932384626433832795_dp real(dp), intent(in) :: x ! Assume folded to [-2𝜋,2𝜋] real(dp) :: y, xnew, twoPi, invTwoPi if (abs(x) < pi/2) then r = kernel_dsin(x) else ! fold to pi/2 from https://realtimecollisiondetection.net/blog/?p=9 y = modulo(xnew, 2*pi) y = min(y, pi - y) y = max(y, -pi - y) y = min(y, pi - y) r = kernel_dsin(y) end if end function
nix-shell -p sollya rlwrap rlwrap -A sollya prec=120; f=sin(x); d=[-pi/2;pi/2]; # Use min/max poly p=remez(f,16,d, default,1e-16,1e-30); p; # Returns coefficients # Zero out even terms
! Accurate on [-pi/2,pi/2] to about 1e-16 elemental real(dp) function kernel_dsin2(x) result(res) real(dp), intent(in) :: x real(dp), parameter :: S1 = 0.9999999999999990771_dp real(dp), parameter :: S2 = -0.16666666666664811048_dp real(dp), parameter :: S3 = 8.333333333226519387e-3_dp real(dp), parameter :: S4 = -1.9841269813888534497e-4_dp real(dp), parameter :: S5 = 2.7557315514280769795e-6_dp real(dp), parameter :: S6 = -2.5051823583393710429e-8_dp real(dp), parameter :: S7 = 1.6046585911173017112e-10_dp real(dp), parameter :: S8 = -7.3572396558796051923e-13_dp real(dp) :: z z = x*x res = x * (S1+z*(S2+z*(S3+z*(S4+z*(S5+z* (S6+z*(S7+z*S8))))))) end function
elemental real(dp) function dmodulo(x, y) result(r) real(dp), intent(in) :: x, y r = x-floor(x/y)*y end function elemental integer function dfloor(x) result(r) real(dp), intent(in) :: x if (x >= 0) then r = x else r = x-1 end if end function elemental real(dp) function dabs(x) result(r) real(dp), intent(in) :: x if (x >= 0) then r = x else r = -x end if end function
With an evenly spaced grid of 0.01
:
modulo
ieee754_rem_pio2
Do not naively assume “handcrafted” algorithms are IEEE 754 accurate
ISO_C_BINDING
f2py.py
–> Fortran to Python Interface Generator (FPIG)f2py2e
–> Fortran to Python Interface Generator, 2nd edition.numpy.f2py
–> f2py2e moved to NumPy project. This is current stable code of f2py.
.pyf
or inline commentsWriting efficient wrappers without being a language lawyer
main: push rbp mov rbp, rsp mov DWORD PTR [rbp-4], 3 mov eax, 0 pop rbp ret __static_initialization_ and_destruction_0(int, int): push rbp mov rbp, rsp sub rsp, 16 mov DWORD PTR [rbp-4], edi mov DWORD PTR [rbp-8], esi cmp DWORD PTR [rbp-4], 1 jne .L5 cmp DWORD PTR [rbp-8], 65535 jne .L5 mov edi, OFFSET FLAT:_ZStL8 __ioinit
call std::ios_base::Init::Init() [complete object constructor] mov edx, OFFSET FLAT:__dso_handle mov esi, OFFSET FLAT:_ZStL8__ioinit mov edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev call __cxa_atexit .L5: nop leave ret _GLOBAL__sub_I_main: push rbp mov rbp, rsp mov esi, 65535 mov edi, 1 call __static_initialization_ and_destruction_0(int, int) pop rbp ret
int main () { int D.48918; { int a; a = 3; D.48918 = 0; return D.48918; } D.48918 = 0; return D.48918; } void _GLOBAL__sub_I_main.cpp () { __static_initialization_ and_destruction_0 (1, 65535); }
void __static_initialization_ and_destruction_0 (int __initialize_p, int __priority) { if (__initialize_p == 1) goto <D.48920>; else goto <D.48921>; <D.48920>: if (__priority == 65535) goto <D.48922>; else goto <D.48923>; <D.48922>: std::ios_base::Init::Init (&__ioinit); __cxxabiv1::__cxa_atexit (__dt_comp , &__ioinit, &__dso_handle); goto <D.48924>; <D.48923>: <D.48924>: goto <D.48925>; <D.48921>: <D.48925>: }
gcc
representation…#include<iostream> int main() { int a=3; return 0; }
g++ main.cpp -o file
file
binary which can be run as:./file
gcc
is just an ugly compiler…program main integer :: x = 3 + 6 print *, x end program
lfortran
has a nicer intermediate structureconda create -n lf
conda activate lf
conda install lfortran \
-c conda-forge
lfortran --show-asr consint.f90
f2py
and lfortran
ISO_C_BINDINGS
IEEE
standardsf2py