차원에 무관 한 코드를 작성하는 방법


19

주어진 연산 / 알고리즘의 1, 2, 3 차원 버전에 대해 매우 유사한 코드를 작성하는 경우가 종종 있습니다. 이 모든 버전을 유지 관리하는 것은 지루할 수 있습니다. 간단한 코드 생성은 잘 작동하지만 더 나은 방법이 있어야한다고 생각되는 것 같습니다.

작업을 한 번 작성하고 더 높거나 더 작은 차원으로 일반화하는 비교적 간단한 방법이 있습니까?

구체적인 예는 다음과 같습니다. 스펙트럼 공간에서 속도 필드의 기울기를 계산해야한다고 가정합니다. 3 차원에서 Fortran 루프는 다음과 같습니다.

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

어디에 ddx배열을 적절하게 정의된다. 행렬 곱셈으로이 작업을 수행 할 수도 있습니다. 2 차원 흐름의 코드는 거의 동일합니다. 단, 3 차원은 루프, 인덱스 및 구성 요소 수에서 삭제됩니다. 이것을 표현하는 더 좋은 방법이 있습니까?

다른 예는 다음과 같습니다. 유체 속도가 3 차원 격자에 점 단위로 정의되어 있다고 가정합니다. 속도를 임의의 위치로 보간하기 위해 (즉, 그리드 포인트에 해당하지 않음) 3 차원 모두에 대해 1 차원 Neville 알고리즘을 연속적으로 사용할 수 있습니다 (즉, 차원 축소). 간단한 알고리즘의 1 차원 구현에서 차원 축소를 수행하는 쉬운 방법이 있습니까?

답변:


13

deal.II ( http://www.dealii.org/ )가 어떻게 작동하는지 살펴보십시오. 차원 독립성은 라이브러리의 핵심이며 대부분의 데이터 유형에 대한 템플릿 인수로 모델링됩니다. 예를 들어, 4 단계 학습 프로그램의 차원에 구애받지 않는 Laplace 솔버를 참조하십시오.

http://www.dealii.org/developer/doxygen/deal.II/step_4.html

또한보십시오

https://github.com/dealii/dealii/wiki/Frequently-Asked-Questions#why-use-templates-for-the-space-dimension


나는 매우 동의합니다. Deal.II 가하는 것보다 더 나은 접근법을 찾지 못했습니다. 이 문제를 해결하기 위해 템플릿을 매우 흥미로운 방식으로 사용합니다.
Eldila

1
좋은 리소스이지만 C ++ 템플릿을 사용하지 않으면 매우 위협적입니다.
meawoppl

@ Wolffang Bangerth : deal.ii는 템플릿을 사용하여 반복자를 정의합니까?
Matthew Emmett

@ MatthewEmmett : 예.
Wolfgang Bangerth

@meawoppl : 사실은 아닙니다. 나는 정기적으로 거래에 관한 수업을 가르치고 II는 처음에는 학생들에게 ClassWhatever <2>는 2d, ClassWhatever <3>은 3d, ClassWhatever <dim>은 희미하다고 말합니다. 3 주차 어딘가에 템플릿에 대한 강의를 듣게되는데, 학생들은 그 전에 어떻게 작동하는지 이해 하지 못할 있지만 어쨌든 완전히 사용 하게됩니다 .
Wolfgang Bangerth

12

문제는 대부분의 "일반"프로그래밍 언어 (C, Fortran 등)가이를 명확하게 수행 할 수 없다는 점을 강조합니다. 추가적인 제약은 표기법의 편리함을 원한다는 것입니다 우수한 성능 것입니다.

그러므로 쓰는 대신 차원 별 코드 하는 차원 별 코드를 생성 하는 코드를 작성하는 것이 좋습니다. 이 생성기는 계산 코드가 아니더라도 차원에 독립적입니다. 즉, 표기법과 계산을 표현하는 코드 사이에 추론 계층을 추가합니다. C ++ 템플릿도 마찬가지입니다. 거꾸로 언어에 내장되어 있습니다. 단점은 작성하기가 다소 번거 롭습니다. 이것은 실제로 코드 생성기를 실현하는 방법에 대한 질문을 줄입니다.

OpenCL을 사용하면 런타임에 코드를 상당히 깨끗하게 생성 할 수 있습니다. 또한 '외부 제어 프로그램'과 '내부 루프 / 커널'을 매우 깔끔하게 분리 할 수 ​​있습니다. 외부에서 생성되는 프로그램은 성능 제한이 훨씬 적으므로 Python과 같은 편안한 언어로 작성 될 수도 있습니다. 이것이 PyOpenCL 이 어떻게 사용 되는지에 대한 나의 희망입니다 .


안드레아스! Scicomp에 오신 것을 환영합니다! 이 사이트를 방문하게되어 기쁘다. 궁금한 점이 있으면 연락 방법을 알고있다.
Aron Ahmadia

2
C ++ 매직 대신이 문제에 대한 솔루션으로 자동 코드 생성을 위해 +10000.
Jeff

9

이는 다음과 같은 대략적인 정신적 프로토 타입을 사용하여 모든 언어로 수행 할 수 있습니다.

  1. 각 차원의 범위 목록을 만듭니다 (MATLAB의 shape ()와 같은 것)
  2. 각 차원에서 현재 위치 목록을 만듭니다.
  3. 바깥 쪽 루프를 기준으로 크기 변경 루프를 포함하는 각 차원에 루프를 작성하십시오.

거기에서 코드를 부적합하게 유지하기 위해 특정 언어의 구문과 싸우는 문제입니다.

n 차원 유체 역학 솔버 를 작성하면서 객체와 같은 목록의 압축 풀기를 지원하는 언어를 함수의 인수로 갖는 것이 도움이된다는 것을 알았습니다. 즉 a = (1,2,3) f (a *)-> f (1,2,3)입니다. 또한 고급 반복자 (예 : numpy의 ndenumerate )는 코드 를 훨씬 더 깔끔하게 만듭니다.


이 작업을 수행하는 Python 구문은 간결하고 간결 해 보입니다. Fortran으로 이것을 할 수있는 좋은 방법이 있는지 궁금합니다.
Matthew Emmett

1
포트란에서 동적 메모리를 다루는 것은 약간 고통 스럽다. 아마도 언어에 대한 나의 주요 불만.
meawoppl

5

n1×n2×n3nj=1


따라서 차원에 관계없이 maxdim + 1 차원으로 코드를 작성해야합니다. 여기서 maxdim은 사용자가 경험할 수있는 최대 차원입니다. maxdim = 100이라고합시다. 결과 코드는 얼마나 유용합니까?
Jeff

4

Fortran 속도를 유지하려면 명확한 대답은 Julia 또는 C ++와 같은 적절한 코드 생성 기능이있는 언어를 사용하는 것입니다. C ++ 템플릿은 이미 언급되었으므로 여기서 Julia의 도구에 대해 언급하겠습니다. Julia의 생성 된 함수 를 사용하면 메타 프로그래밍을 사용하여 유형 정보를 통해 요청시 함수를 작성할 수 있습니다. 본질적으로 여기서 할 수있는 일은

@generated function f(x)
   N = ndims(x)
   quote
     # build the code for the function
   end
end

그런 다음 차원을 사용 N하여 실행할 코드를 프로그래밍 방식으로 빌드하는 데 사용 N합니다. 그런 다음 Julia의 Cartesian 라이브러리 또는 Einsum.jl 표현식 과 같은 패키지를 N차원 함수에 쉽게 빌드 할 수 있습니다 .

Julia의 장점은이 함수가 사용하는 각각의 새로운 차원 배열에 대해 정적으로 컴파일되고 최적화되어 있으므로 필요한 것보다 더 많이 컴파일되지 않고 C / Fortran 속도를 얻을 수 있다는 것입니다. 결국 이것은 C ++ 템플릿을 사용하는 것과 비슷하지만 더 쉽게 만들 수있는 많은 도구가있는 고급 언어입니다 (학부생에게는 좋은 숙제 문제가 될 정도로 쉽습니다).

이것에 좋은 또 다른 언어는 Common Lisp와 같은 Lisp입니다. Julia와 같이 사용하기 쉽습니다. 내장 검사 도구가 많이 내장 된 컴파일 된 AST를 제공하지만 Julia와 달리 자동으로 컴파일하지 않습니다 (대부분의 배포판).


1

나는 같은 (Fortran) 보트에 있습니다. 1D, 2D, 3D 및 4D (투영 형상 수행) 요소가 있으면 각 유형에 대해 동일한 연산자를 만든 다음 진행 상황을 명확하게하는 높은 수준의 방정식으로 논리를 작성합니다. 각 작업과 많은 메모리 복사본에 대해 별도의 루프가 있다고 생각하는 것만 큼 느리지 않습니다. 컴파일러 / 프로세서가 최적화를 수행하도록했습니다.

예를 들어

interface operator (.x.)
    module procedure cross_product_1x2
    module procedure cross_product_2x1
    module procedure cross_product_2x2
    module procedure cross_product_3x3
end interface 

subroutine cross_product_1x2(a,b,c)
    real(dp), intent(in) :: a(1), b(2)
    real(dp), intent(out) :: c(2)

    c = [ -a(1)*b(2), a(1)*b(1) ]
end subroutine

subroutine cross_product_2x1(a,b,c)
    real(dp), intent(in) :: a(2), b(1)
    real(dp), intent(out) :: c(2)

    c = [ a(2)*b(1), -a(1)*b(1) ]
end subroutine

subroutine cross_product_2x2(a,b,c)
    real(dp), intent(in) :: a(2), b(2)
    real(dp), intent(out) :: c(1)

    c = [ a(1)*b(2)-a(2)*b(1) ]
end subroutine

subroutine cross_product_3x3(a,b,c)
    real(dp), intent(in) :: a(3), b(3)
    real(dp), intent(out) :: c(3)

    c = [a(2)*b(3)-a(3)*b(2), a(3)*b(1)-a(1)*b(3), a(1)*b(2)-a(2)*b(1)]
end subroutine

다음과 같은 방정식에 사용

m = e .x. (r .x. g)  ! m = e×(r×g)

여기서 erg수학적 의미가있는 차원을 가질 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.