Często piszę bardzo podobny kod dla jedno-, dwu- i trójwymiarowych wersji danej operacji / algorytmu. Utrzymanie wszystkich tych wersji może być nudne. Proste generowanie kodu działa dość dobrze, ale wydaje się, że istnieje lepszy sposób.
Czy istnieje względnie prosty sposób, aby napisać operację raz i generalizować ją do wyższych lub niższych wymiarów?
Jednym konkretnym przykładem jest: załóżmy, że muszę obliczyć gradient pola prędkości w przestrzeni spektralnej. W trzech wymiarach pętle Fortrana wyglądałyby mniej więcej tak:
do k = 1, n
do j = 1, n
do i = 1, n
phi(i,j,k) = ddx(i)*u(i,j,k) + ddx(j)*v(i,j,k) + ddx(k)*w(i,j,k)
end do
end do
end do
gdzie ddx
tablica jest odpowiednio zdefiniowana. (Można to również zrobić z mnożnikami macierzy.) Kod dwuwymiarowego przepływu jest prawie dokładnie taki sam, z wyjątkiem: trzeci wymiar jest usuwany z pętli, indeksów i liczby składników. Czy istnieje lepszy sposób na wyrażenie tego?
Innym przykładem jest: załóżmy, że mam prędkości płynu zdefiniowane punktowo na trójwymiarowej siatce. Aby interpolować prędkość do dowolnej lokalizacji (tj. Nie odpowiadającej punktom siatki), można zastosować jednowymiarowy algorytm Neville'a kolejno we wszystkich trzech wymiarach (tj. Redukcja wymiarów). Czy istnieje prosty sposób na zmniejszenie wymiarów, biorąc pod uwagę jednowymiarową implementację prostego algorytmu?
źródło
Pytanie podkreśla, że większość „prostych” języków programowania (przynajmniej C, Fortran) nie pozwala ci robić tego w czysty sposób. Dodatkowym ograniczeniem jest to, że chcesz notatywnej wygody i dobrej wydajności.
Dlatego zamiast pisać kod specyficzny dla wymiaru, należy rozważyć napisanie kodu generującego kod specyficzny dla wymiaru. Ten generator jest niezależny od wymiarów, nawet jeśli kod obliczeniowy nie jest. Innymi słowy, dodajesz warstwę rozumowania między twoją notacją a kodem wyrażającym obliczenia. Szablony C ++ mają tę samą rzecz: do góry, są wbudowane bezpośrednio w język. Minusem, są nieco nieporęczne w pisaniu. Ogranicza to pytanie do praktycznej realizacji generatora kodu.
OpenCL pozwala generować kod w czasie wykonywania dość czysto. Zapewnia również bardzo czysty podział między „zewnętrznym programem sterującym” a „wewnętrznymi pętlami / jądrem”. Zewnętrzny, generujący program jest znacznie mniej ograniczony pod względem wydajności i dlatego równie dobrze może być napisany w wygodnym języku, takim jak Python. To moja nadzieja na wykorzystanie PyOpenCL - przepraszam za nową bezwstydną wtyczkę.
źródło
Można to osiągnąć w dowolnym języku za pomocą następującego szorstkiego prototypu mentalnego:
Odtąd chodzi o walkę ze składnią określonego języka, aby zachować zgodność z kodem nd.
Po napisaniu n-wymiarowego solwera dynamiki płynów odkryłem, że pomocne jest posiadanie języka, który obsługuje rozpakowywanie listy podobnej do obiektu jako argumentów funkcji. Tj. A = (1,2,3) f (a *) -> f (1,2,3). Dodatkowo zaawansowane iteratory (takie jak ndenumerate in numpy) sprawiają, że kod jest o rząd wielkości czystszy.
źródło
źródło
Jasne odpowiedzi, jeśli chcesz utrzymać szybkość Fortran, to użyć języka, który ma prawidłowe generowanie kodu, takiego jak Julia lub C ++. Wzory C ++ zostały już wspomniane, więc wspomnę tutaj o narzędziach Julii. Funkcje generowane przez Julię umożliwiają wykorzystanie jej metaprogramowania do budowania funkcji na żądanie za pomocą informacji o typie. Zasadniczo możesz tutaj zrobić
a następnie używasz go
N
do programowego budowania kodu, który chcesz wykonać, biorąc pod uwagę jegoN
wymiar. Następnie kartezjańską bibliotekę Julii lub pakiety, takie jak wyrażenia Einsum.jl, można łatwo zbudować dlaN
funkcji wymiarowej.To, co jest miłe w Julii, to fakt, że ta funkcja jest kompilowana statycznie i zoptymalizowana dla każdej nowej macierzy wymiarów, której używasz, więc nie skompiluje więcej, niż potrzebujesz, ale zapewni Ci szybkość C / Fortran. W końcu jest to podobne do korzystania z szablonów C ++, ale jest to język wyższego poziomu z wieloma narzędziami, aby to ułatwić (na tyle łatwe, że byłby to miły problem dla studentów.
Kolejnym dobrym językiem jest Lisp, jak Common Lisp. Jest łatwy w użyciu, ponieważ podobnie jak Julia daje skompilowaną AST z wieloma wbudowanymi narzędziami introspekcji, ale w przeciwieństwie do Julii nie skompiluje jej automatycznie (w większości dystrybucji).
źródło
Jestem na tej samej łodzi (Fortran). Kiedy mam już elementy 1D, 2D, 3D i 4D (wykonuję geometrię rzutową), tworzę te same operatory dla każdego typu, a następnie piszę moją logikę za pomocą równań wysokiego poziomu, które wyjaśniają, co się dzieje. Nie jest tak wolny, jak mogłoby się wydawać, że ma osobne pętle dla każdej operacji i dużo kopii pamięci. Pozwoliłem kompilatorowi / procesorowi wykonać optymalizacje.
Na przykład
Do użycia w równaniach takich jak
gdzie
e
ar
ig
może mieć dowolną wymiarowości sprawia, że matematyczny sens.źródło