3

This question is an extension of this question for 2D integration.

The formulation of the problem is based on this page

Basically, the composite Simpson's rule for 2D integration is

$ \iint_R f(x,y) dx dy\approx\sum_i\sum_jw_{ij}f(x_i,y_j) $

over the rectangle $R={(x,y):a \le x \le b, c \le y \le d}$.

The domain is subdivided in an even number of intervals with spacing

$h_x=\frac{b-a}{2m},\hphantom{-} x_i=a+ih_x, \hphantom{-} i=0,1,\cdots, 2m \\ h_y=\frac{c-d}{2n}, \hphantom{-} y_j=c+jh_y,\hphantom{-} j=0,1,\cdots,2n$

And the integration is performed using Simpsons rule in each dimension.

For a 1D integration, the composite Simpsons rule can be written as:

$ \int_a^bf(x)dx \approx \frac{h}{3} \left[ f(a)+2\sum_{i=1}^{m-1}f(x_{2i})+4\sum_{i=1}^{m}f(x_{2i-1})+f(b) \right] $

To implement this, I can create the vector of weights

$ \left[ \matrix{ 1 & 4 & 2 & 4 & 2 & 4 & 2 & \cdots & 2 & 4 & 1 } \right] $

And calculate (assuming the element-wise multiplication is .*:

I = sum(w.*f)*h/3

For a 2D, the pattern can be extended to a grid in the rectangle:

$ \left[ \matrix{ 1 & 4 & 2 & 4 & 2 & 4 & 2 & \cdots & 2 & 4 & 1 \\ 4 & 16 & 8 & 16 & 8 & 16 & 8 & \cdots & 8 & 16 & 4 \\ 2 & 8 & 4 & 8 & 4 & 8 & 4 & \cdots & 4 & 8 & 2 \\ \vdots \\ 2 & 8 & 4 & 8 & 4 & 8 & 4 & \cdots & 4 & 8 & 2 \\ 4 & 16 & 8 & 16 & 8 & 16 & 8 & \cdots & 8 & 16 & 4 \\ 1 & 4 & 2 & 4 & 2 & 4 & 2 & \cdots & 2 & 4 & 1 } \right] $

Therefore, the 2D integrations, given the weights above is:

I = sum(w(:).*f(:))*hx*hy/9

If, however, I have an odd number of intervals, I have to change my functions accordingly. Following this answer, to keep the same order of Simpsons rule (with $m$ odd):

$ \int_a^bf(x)dx, \hphantom{-} h_x=\frac{b-a}{m}, \hphantom{-} x_i=a+ih_x, \hphantom{-} i=0,1,\cdots,m $

The idea is to use Simpson's rule for the first $m-3$ points and cover the remaining gridpoints with the Simpsons 3/8 rule. With the weight vectors defined above, we have

w_simpson=ones(1,m-3);
w_simpson(2:2:end-1) = 4;
w_simpson(3:2:end-2) = 2;

w_38 = [1 3 3 1];
I = sum(w_simpson.*f(1:m-3))*h/3 + sum(w_38.*f(m-3:m))*3*h/8

Now, the question: to expand this 3/8 rule for a 2D integration. Are the matrices defined below correct?

$ \left[ \matrix{ 1 & 4 & 2 & 4 & 2 & \cdots & 2 & 4 & 1 \\ 4 & 16 & 8 & 16 & 8 & \cdots & 8 & 16 & 4 \\ 2 & 8 & 4 & 8 & 4 & \cdots & 4 & 8 & 2 \\ \vdots \\ 2 & 8 & 4 & 8 & 4 & \cdots & 4 & 8 & 2 \\ 4 & 16 & 8 & 16 & 8 & \cdots & 8 & 16 & 4 \\ 1 & 4 & 2 & 4 & 2 & \cdots & 2 & 4 & 1 } \right] \left[ \matrix{3&3&1\\ 12&12&4\\ 6&6&2\\ \vdots\\ 6&6&2\\ 12&12&4\\ 3&3&1 } \right]\\ \left[ \matrix{3 & 12 & 6 & 12 & 6 & \cdots & 6 & 12 & 3\\ 3 & 12 & 6 & 12 & 6 & \cdots & 6 & 12 & 3\\ 1 & 4 & 2 & 4 & 2 & \cdots & 2 & 4 & 1\\} \right] \left[ \matrix{9&9&3\\9&9&3\\3&3&1} \right] $

If they are correct, then the 2D integral is:

x = a:hx:b;
y = c:hy:d;
[X,Y] = meshgrid(x,y);
Z = fun(X,Y);

I = sum(sum(w1.*Z(1:n-3,1:m-3)))*hx*hy/9 + sum(sum(w2.*Z(1:n-3,m-3:m)))*hx*hy/8 + ...
    sum(sum(w3.*Z(n-3:n,1:m-3)))*hx*hy/8 + sum(sum(w4.*Z(n-3:n,m-3:m)))*hx*hy*9/64

Is it correct? If so, is this the best way to implement the integration? How do I define the weight matrices? In Matlab, we must use the sum function twice, because I set a range on matrix. Is there a better way to code it without using for loops?

Edit:

The code came out as this:

function I = simpson2D(x,y,z)
% Composite Simpson's rule for 2D integration

    from meshgrid
    Nx = size(x,2); % number of columns
    Ny = size(x,1); % number of rows

    hx = x(1,2)-x(1,1);
    hy = y(2,1)-y(1,1);

    if (mod(Nx,2)) % Nx is odd (even number of intervals)
        NxIsEven = false;
        nx = Nx;
        wx = ones(1,Nx);
        wx(2:2:nx-1) = 4;
        wx(3:2:nx-2) = 2;
    else % Nx is even (odd number of intervals)
        NxIsEven = true;
        nx = Nx-3;
        wx = ones(1,Nx);
        wx(2:2:nx-1) = 4;
        wx(3:2:nx-2) = 2;
        wx(Nx-2:Nx) = [3 3 1];
    end

    if (mod(Ny,2)) % Ny is odd (even number of intervals)
        NyIsEven = false;
        ny = Ny;
        wy = ones(Ny,1);
        wy(2:2:ny-1) = 4;
        wy(3:2:ny-2) = 2;
    else % Ny is even (odd number of intervals)
        NyIsEven = true;
        ny = Ny-3;
        wy = ones(Ny,1);
        wy(2:2:ny-1) = 4;
        wy(3:2:ny-2) = 2;
        wy(Ny-2:Ny) = [3 3 1];
    end

    w = wy*wx;

    if (NxIsEven && NyIsEven)
        I = sum(sum(w(1:ny,1:nx).*z(1:ny,1:nx)))/9 + ...
           (sum(sum(w(1:ny,nx:Nx).*z(1:ny,nx:Nx)))+sum(sum(w(ny:Ny,1:nx).*z(ny:Ny,1:nx))))/8 + ...
            sum(sum(w(ny:Ny,nx:Nx).*z(ny:Ny,nx:Nx)))*9/64;
    elseif (~NxIsEven && NyIsEven)
        I = sum(sum(w(1:ny,1:nx).*z(1:ny,1:nx)))/9 + ...
            sum(sum(w(ny:Ny,1:nx).*z(ny:Ny,1:nx)))/8;
    elseif (NxIsEven && ~NyIsEven)
        I = sum(sum(w(1:ny,1:nx).*z(1:ny,1:nx)))/9 + ...
            sum(sum(w(1:ny,nx:Nx).*z(1:ny,nx:Nx)))/8;
    else % (~NxIsEven && ~NyIsEven)
        I = sum(sum(w(1:ny,1:nx).*z(1:ny,1:nx)))/9;
    end
    I = I*hx*hy;
end

Any hints on how to improve it (to treat the parity of Nx/Ny without so many if-elseif-else and code repeating on the definition of the weight matrices wx and wy?

Thales
  • 663
  • What is the motivation for this bricolage? The grid layout is a design decision, you can always choose to have an even number of sub-intervals in each direction. – Lutz Lehmann Jun 12 '19 at 19:36
  • This is part of a bigger project and the number of sub-intervals should be given by the final user when running the codes. – Thales Jun 12 '19 at 19:50
  • 1
    Then why provide the choice (still on the search for the motivation)? Either get $m$ for the $2m$ sub-intervals as input or round up or down to the next even integer. – Lutz Lehmann Jun 12 '19 at 19:58

1 Answers1

0

It can be written with some complicated expressions, but I don't know whether you think it would be an improvement:

wx = hx/3*[1 4 reshape([2;4]*ones(1,floor((Nx-3)/2)), ...
    1,[]) 1 zeros(1,mod(Nx+1,2))]+...
    mod(Nx+1,2)*hx/24*[zeros(1,2*floor((Nx-3)/2)) ...
    1 -5 19 9*ones(1,mod(Nx+1,2))];
wy = hy/3*[1 4 reshape([2;4]*ones(1,floor((Ny-3)/2)), ...
    1,[]) 1 zeros(1,mod(Ny+1,2))]+...
    mod(Ny+1,2)*hy/24*[zeros(1,2*floor((Ny-3)/2)) ...
    1 -5 19 9*ones(1,mod(Ny+1,2))];
I = wy*z*wx'

The idea is to make the Simpson $1/3$ pattern with an extra zero appended if there is an even number of data points. Then we add an equal-length array which is all zeros for an odd number of data points but corrects the last three Simpson $1/3$ rule and the appended zero to the Simpson $3/8$ rule for the last four data points for an even number of data points. Investing a lot of computational effort on getting wx and wy should not be a problem because they are $O(N)$ processes while the $2$-d integral is $O(N^2)$.

I think your matrices are incorrect because you left out the first point of the Simpson $3/8$ rule parts, but your code right under your matrices looks OK at first glance, although I think Matlab should reject your final block of code. Does it?

EDIT: I found a way to make my answer perhaps a little more palatable. I created two helper functions, the first of which creates the Simpson'2 $1/3$ weights using Matlab's answer to an ac-implied-do and the second uses a Matlab replacement for the ternary operator to choose between an extension of the weights by Simpson's $3/8$ rule. Then the functions are invoked with the proper arguments to produce wx and wy.

g = @(N,h) h/3*[1 4 reshape([2;4]*ones(1,floor((N-3)/2)),1,[]) 1];
f = @(N,w) cell2mat(getfield({[w(1:end-3) [w(end-2:end) 0]+ ...
    w(1)/8*[1 -5 19 9]],w},{1+mod(N,2)}));
wx = f(Nx,g(Nx,hx));
wy = f(Ny,g(Ny,hy));
I = wy*z*wx';

The code, especially for helper function f is still pretty dense but did meet the requirement of eliminating for loops and replaced if-elseif-else syntax with arithmetic expressions like floor((N-3)/2) and mod(N,2). Notice that the $2$-d Simpson's rule can be expressed as a contraction of the array of functional values so there is no need to form a $2$-d array of weights.

user5713492
  • 16,333
  • The matrix in the equation is incorret, indeed. The first point of the $3/8$ Rule is not there. The code, however, is correct and runs just fine. Tested it and it worked with even and odd numbers for the x and/or y. – Thales Jun 13 '19 at 16:08
  • What I was worried about was syntax like $$y = [1,2,3]^{\prime};$$ $$y(2:3) = [3,4];$$ but that works even though y(2:3) is $2\times1$ and $[3,4]$ is $1\times2$. Syntax such as $$y = [1,2,3]^{\prime};$$ $$y(2:3) = y(2:3)+[3,4];$$ Fails: assignment is OK, but addition, for example, is not. I hope you like my latest revision. The code is pretty brief and is more in the Matlab idiom. – user5713492 Jun 13 '19 at 23:13