Làm thế nào để tôi viết mã bất khả tri theo chiều?


19

Tôi thường thấy mình viết mã rất giống nhau cho các phiên bản một, hai và ba chiều của một hoạt động / thuật toán nhất định. Duy trì tất cả các phiên bản này có thể trở nên tẻ nhạt. Tạo mã đơn giản hoạt động khá tốt, nhưng có vẻ như phải có một cách tốt hơn.

Có một cách tương đối đơn giản để viết một hoạt động một lần và nó có khái quát đến kích thước cao hơn hoặc thấp hơn không?

Một ví dụ cụ thể là: giả sử tôi cần tính toán độ dốc của trường vận tốc trong không gian quang phổ. Trong ba chiều, các vòng lặp Fortran sẽ trông giống như:

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

trong đó ddxmảng được xác định một cách thích hợp. (Người ta cũng có thể làm điều này với bội số ma trận.) Mã cho luồng hai chiều gần như giống hệt nhau, ngoại trừ: chiều thứ ba được loại bỏ từ các vòng lặp, chỉ mục và số lượng thành phần. Có cách nào tốt hơn để thể hiện điều này?

Một ví dụ khác là: giả sử tôi có vận tốc chất lỏng xác định điểm khôn ngoan trên lưới ba chiều. Để nội suy vận tốc đến một vị trí tùy ý (nghĩa là không tương ứng với các điểm lưới), người ta có thể sử dụng thuật toán Neville một chiều liên tiếp trên cả ba chiều (tức là giảm kích thước). Có cách nào dễ dàng để thực hiện giảm kích thước khi thực hiện một thuật toán đơn giản không?

Câu trả lời:


13

Bạn nhìn vào cách deal.II ( http://www.dealii.org/ ) thực hiện nó - ở đó, tính độc lập về kích thước nằm ở trung tâm của thư viện và được mô hình hóa như một đối số mẫu cho hầu hết các loại dữ liệu. Xem, ví dụ, bộ giải Laplace không xác định thứ nguyên trong chương trình hướng dẫn bước 4:

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

Xem thêm

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


Tôi hoàn toàn đồng ý. Tôi chưa tìm thấy cách tiếp cận nào tốt hơn những gì Deal.II đang làm. Họ sử dụng các mẫu theo một cách rất thú vị để khắc phục vấn đề này.
Eldila

1
Một tài nguyên tốt, nhưng khá đáng sợ nếu bạn không sử dụng các mẫu C ++.
meawoppl

@Wolfgang Bangerth: deal.ii có định nghĩa các trình vòng lặp bằng cách sử dụng các mẫu không?
Matthew Emmett

@MatthewEmmett: Vâng.
Wolfgang Bangerth

@meawoppl: Thật ra, không. Tôi thường xuyên dạy các lớp về deal.II, và ban đầu chỉ cần nói với sinh viên rằng mọi thứ nói ClassWhthing <2> đều ở 2d, ClassWhthing <3> ở dạng 3d và ClassWhthing <dim> nằm trong dim-d. Tôi mang đến bài học về các mẫu ở đâu đó trong tuần 3 và trong khi có khả năng học sinh không hiểu nó hoạt động như thế nào trước đó, dù sao họ cũng có đầy đủ chức năng sử dụng nó.
Wolfgang Bangerth

12

Câu hỏi nhấn mạnh rằng hầu hết các ngôn ngữ lập trình "đơn giản" (ít nhất là C, Fortran) không cho phép bạn làm điều này một cách sạch sẽ. Một hạn chế được thêm vào là bạn muốn thuận tiện công chứng hiệu suất tốt.

Do đó, thay vì viết mã theo thứ nguyên cụ thể, hãy xem xét viết mã tạo mã cụ thể theo thứ nguyên. Trình tạo này không phụ thuộc vào kích thước, ngay cả khi mã tính toán không. Nói cách khác, bạn thêm một lớp lý luận giữa ký hiệu của bạn và mã biểu thị tính toán. Các mẫu C ++ tương đương với điều tương tự: Upside, chúng được tích hợp ngay vào ngôn ngữ. Nhược điểm, chúng hơi rườm rà để viết. Điều này làm giảm câu hỏi làm thế nào để thực tế nhận ra trình tạo mã.

OpenCL cho phép bạn tạo mã trong thời gian chạy khá sạch sẽ. Nó cũng tạo ra sự phân chia rất rõ ràng giữa 'chương trình kiểm soát bên ngoài' và 'vòng lặp / hạt nhân bên trong'. Chương trình tạo bên ngoài ít bị hạn chế về hiệu năng và do đó cũng có thể được viết bằng ngôn ngữ thoải mái, như Python. Đó là hy vọng của tôi về cách PyOpenCL sẽ được sử dụng - xin lỗi vì phích cắm không biết xấu hổ được đổi mới.


Andreas! Chào mừng bạn đến với scicomp! Rất vui khi có bạn trên trang web, tôi nghĩ bạn biết cách liên lạc với tôi nếu bạn có bất kỳ câu hỏi nào.
Aron Ahmadia

2
+10000 để tạo mã tự động như một giải pháp cho vấn đề này thay vì phép thuật C ++.
Jeff

9

Điều này có thể được thực hiện bằng bất kỳ ngôn ngữ nào với nguyên mẫu tinh thần thô sau đây:

  1. Tạo một danh sách các phạm vi của mỗi thứ nguyên (giống như hình dạng () trong MATLAB tôi nghĩ)
  2. Tạo một danh sách các vị trí hiện tại của bạn trong mỗi chiều.
  3. Viết một vòng lặp trên mỗi kích thước, chứa một vòng lặp thay đổi kích thước whos dựa trên vòng lặp bên ngoài.

Từ đó, một câu hỏi về việc chiến đấu với cú pháp của ngôn ngữ nhất định của bạn để giữ cho mã của bạn tuân thủ nd.

Sau khi viết một bộ giải động lực học chất lỏng n chiều , tôi thấy rằng thật hữu ích khi có một ngôn ngữ hỗ trợ giải nén một danh sách như đối tượng làm đối số của hàm. Tức là a = (1,2,3) f (a *) -> f (1,2,3). Ngoài ra các trình vòng lặp tiên tiến (chẳng hạn như ndenum Cả trong numpy) làm cho mã trở nên sạch hơn.


Cú pháp Python để làm điều này trông đẹp và cô đọng. Tôi tự hỏi liệu có một cách hay để làm điều này với Fortran ...
Matthew Emmett

1
Đó là một chút đau đớn để đối phó với bộ nhớ động trong Fortran. Có lẽ là khiếu nại lớn của tôi với ngôn ngữ.
meawoppl

5

n1×n2×n3nj= =1


Vì vậy, để độc lập với kích thước, mã của bạn cần được viết cho các kích thước maxdim + 1, trong đó maxdim là kích thước tối đa có thể mà người dùng có thể gặp phải. Hãy nói maxdim = 100. Mã kết quả hữu ích như thế nào?
Jeff

4

Câu trả lời rõ ràng nếu bạn muốn giữ tốc độ của Fortran là sử dụng ngôn ngữ có thế hệ mã phù hợp như Julia hoặc C ++. Các mẫu C ++ đã được đề cập, vì vậy tôi sẽ đề cập đến các công cụ của Julia tại đây. Các hàm được tạo của Julia cho phép bạn sử dụng siêu lập trình của nó để xây dựng các hàm theo yêu cầu thông qua thông tin loại. Vì vậy, về cơ bản những gì bạn có thể làm ở đây là làm

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

và sau đó bạn sử dụng Nđể xây dựng mã theo chương trình mà bạn muốn thực thi theo Nchiều đó. Sau đó, thư viện Cartesian của Julia hoặc các gói như biểu thức Einsum.jl có thể dễ dàng được xây dựng cho Nhàm thứ nguyên.

Điều tuyệt vời ở Julia ở đây là chức năng này được biên dịch tĩnh và tối ưu hóa cho từng mảng chiều mới mà bạn sử dụng, do đó, nó sẽ không biên dịch nhiều hơn bạn cần nhưng nó sẽ giúp bạn đạt được tốc độ C / Fortran. Cuối cùng, điều này tương tự với việc sử dụng các mẫu C ++, nhưng nó là ngôn ngữ cấp cao hơn với nhiều công cụ để làm cho nó dễ dàng hơn (đủ dễ để đây có thể là một vấn đề bài tập về nhà tốt cho một sinh viên chưa tốt nghiệp).

Một ngôn ngữ khác tốt cho việc này là tiếng Lisp như Common Lisp. Nó rất dễ sử dụng vì giống như Julia, nó cung cấp cho bạn AST được biên dịch với rất nhiều công cụ hướng nội, nhưng không giống như Julia, nó sẽ không tự động biên dịch nó (trong hầu hết các bản phân phối).


1

Tôi ở trong cùng một chiếc thuyền (Fortran). Khi tôi có các phần tử 1D, 2D, 3D và 4D (tôi thực hiện hình học chiếu), tôi tạo các toán tử giống nhau cho từng loại và sau đó viết logic của mình với các phương trình mức cao để làm rõ những gì đang diễn ra. Nó không chậm như bạn nghĩ để có các vòng lặp riêng biệt của từng thao tác và nhiều bản sao bộ nhớ. Tôi để trình biên dịch / bộ xử lý thực hiện tối ưu hóa.

Ví dụ

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

Được sử dụng trong các phương trình như

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

nơi ergcó thể có bất cứ chiều có ý nghĩa toán học.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.