Passing Arrays as a parameter to a VBA function - arrays

I have a user defined Excell worksheet function (Linear) that interpolates from an array of X and an array of Y values at a defined X1 value, which works fine. I have tried to use this within another function (NPL in the example code below) be setting it a a Private Static function within a VBA module and then calling the function using arrays of data created within the function.
When I use this in the spreadsheet I get a #VALUE error.
Any ideas what I am doing wrong?
Example code:
Function NPL(Length, Beam)
A = Array(1, 2, 3, 4)
B = Array(2, 4, 6, 8)
C = Linear(A, B, 1.5)
NPL = C
End Function
Private Static Function Linear(X, Y, X1)
N = 0
I = 1
Do
N = I
I = I + 1
Loop Until X(I) < X(I - 1) Or N = X.Count
A = 0
I = 0
Do
I = I + 1
Loop Until X(I) > X1 Or I > N - 1
If X1 < X(N) And X1 > X(1) Then
Linear = Y(I - 1) + (X1 - X(I - 1)) * (Y(I) - Y(I - 1)) / (X(I) - X(I - 1))
ElseIf X1 > X(N) Or X1 = X(N) Then
Linear = Y(N)
Else
Linear = Y(1)
End If
End Function

Replace your
Do
N = I
I = I + 1
Loop Until X(I) < X(I - 1) Or N = X.Count
with
Do
N = I
I = I + 1
Loop Until X(I) < X(I - 1) Or N = UBound(X) - LBound(X) + 1
This should work for any 1D array.

Related

Summing columns of an array in model constraint in Julia

Consider the following optimisation model:
I = 1:5; J = 1:5; K = 1:5;
m = Model(HiGHS.Optimizer)
#variable(m, A[i in I, j in J, k in K] >= 0)
#variable(m, y[i in I, k in K] >= 0)
#constraint(m, sum(collect(A[:,:,1]),dims = 2) .== y[:,1])
In the model, I have a 5x5x5 array variable A, and a 5x5 array variable y. I want to have an equation constraint, where the vector obtained by adding the column vectors of the matrix A[:,:,1] together is (entry-wise) equal to the column vector p[:,1].
However, the above code gives an error:
MethodError: no method matching _broadcasted_type(::Base.Broadcast.ArrayStyle{JuMP.Containers.DenseAxisArray}, ::Base.HasShape{2}, ::Type{AffExpr})
How can I fix that?
Apply collect to the other side of the equation:
julia> #constraint(m, vec(sum(collect(A[:,:,1]),dims = 2)) .== collect(y[:,1]))
5-element Vector{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.EqualTo{Float64}}, ScalarShape}}:
A[1,1,1] + A[1,2,1] + A[1,3,1] + A[1,4,1] + A[1,5,1] - y[1,1] == 0.0
A[2,1,1] + A[2,2,1] + A[2,3,1] + A[2,4,1] + A[2,5,1] - y[2,1] == 0.0
....
EDIT
note that your JuMP experience will be much more enjoyable if you do:
I = 5; J = 5; K = 5;
#variable(m, A[1:I, 1:J, 1:K] >= 0)
#variable(m, y[1:I, 1:K] >= 0)
Now you do not have dense arrays and can do what you initially wanted:
julia> #constraint(m, sum(A[:,:,1],dims = 2) .== y[:,1])
5×1 Matrix{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.EqualTo{Float64}}, ScalarShape}}:
A[1,1,1] + A[1,2,1] + A[1,3,1] + A[1,4,1] + A[1,5,1] - y[1,1] == 0.0
A[2,1,1] + A[2,2,1] + A[2,3,1] + A[2,4,1] + A[2,5,1] - y[2,1] == 0.0
...

MATLAB array indexing and slicing

Im writing currently rewriting a Matlab script in C. When i get to the last few lines of the Matlab script a for loop is executed and it iterates through an array. Since i am trying to rewrite the program in C the slicing notation in the Matlab script is confusing me. I have attached the line of code that is troubling me below.
How would i write this line of code in a nested for loop indexing with i and j only, since you cant slice in c obviously. just for reference u = 1, Ubc is 2D array of size (NX+2, NY+2). Where NX = NY = 40.
Below is the line of code in Matlab i need to translate to for loop indexing.
Nx = 40;
Ny = 40;
u = 1;
Ubc = rand(Nx + 2, Ny + 2);
% First the i interfaces
F = 0.5* u *( Ubc(2:Nx+2,2:Ny+1) + Ubc(1:Nx+1,2:Ny+1))
- 0.5*abs(u)*( Ubc(2:Nx+2,2:Ny+1) - Ubc(1:Nx+1,2:Ny+1));
You can calculate the same in a loop as
Nx = 40;
Ny = 40;
u = 1;
Ubc = rand(Nx + 2, Ny + 2);
F = zeros(Nx + 1, Ny);
for z1 = 1 : Nx + 1
for z2 = 1 : Ny
F(z1, z2) = 0.5* u *( Ubc(z1 + 1, z2 + 1) + Ubc(z1, z2 + 1))
- 0.5*abs(u)*( Ubc(z1 + 1, z2 + 1) - Ubc(z1, z2 + 1));
end
end
You shouldn't use i and j as loop index in Matlab. Both are the imaginary unit.

Two dimensional convolution in matlab . Result differs from conv2 of matlab toolbox.Any tip?

[r,c] = size(x);
[m,n] = size(y);
h = rot90(y, 2);
center = floor((size(h)+1)/2);
Rep = zeros(r + m*2-2, c + n*2-2);
return
for x1 = m : m+r-1
for y1 = n : n+r-1
Rep(x1,y1) = x(x1-m+1, y1-n+1);
end
end
B = zeros(r+m-1,n+c-1);
for x1 = 1 : r+m-1
for y1 = 1 : n+c-1
for i = 1 : m
for j = 1 : n
B(x1, y1) = B(x1, y1) + (Rep(x1+i-1, y1+j-1) * h(i, j));
end
end
end
end
Con=conv2(Rep,h);
I have this code for 2d convolution.But Con and B are different(last column of correct convolution(=Con) does not exist in B,and other elements also differ.I cant understand what i do wrong.

Obtaining approximate derivatives with variable x spacing in MATLAB? [duplicate]

This question already has answers here:
Numerical derivative of a vector
(3 answers)
Closed 7 years ago.
I have two arrays: x and y. In practice, y is dependent on x, but both arrays are measured values. I would like to obtain the derivative of y with respect to x. If x were uniformly spaced (i.e. x=[1 2 3 4 5]), I could do something with diff like this:
h = 0.001;
x = -pi:h:pi;
f = sin(X);
y = diff(f)/h;
However, x is not uniformly spaced (i.e. x=[1 1.9 2.8 4.1]). How can I obtain the partial derivative of this data set?
A good way to do it is gradient,
dydx = gradient(y, x);
I like it because it returns a vector which is the same length as x and y. The downside though, is it's first order accurate. This can sometimes be a problem, a fix could be to write your own,
x = unique([linspace(0, 2*pi, 50), logspace(0, log10(2*pi), 50)]);
y = cos(x) ;
subplot(2,1,1) ;
plot(x, Gradient(y, x), x, gradient(y,x), x, -sin(x));
legend('2^{nd} order', '1^{st} order', 'exact') ;
subplot(2,1,2) ;
plot(x, Gradient(y, x) + sin(x), x, gradient(y,x) + sin(x));
legend('2^{nd} order - exact', '1^{st} order - exact')
With Gradient being
function dydx = Gradient(y,x)
y = y(:);
p = x(3:end) - x(2:end-1);
p = p(:);
m = x(2:end-1) - x(1:end-2);
m = m(:);
p1 = x(2) - x(1);
p2 = x(3) - x(1);
m1 = x(end) - x(end-1);
m2 = x(end) - x(end-2);
dydx = reshape([ ((-p1^2 + p2^2)*y(1) - p2^2*y(2) + p1^2*y(3))/(p1*(p1 - p2)*p2);
((-m.^2 + p.^2).*y(2:end-1) - p.^2.*y(1:end-2) + m.^2.*y(3:end))./(m.*p.*(m + p));
((m1^2 - m2^2)*y(end) + m2^2*y(end-1) - m1^2*y(end-2))/(m1^2*m2 - m1*m2^2) ...
], size(x));
end
Edit:
Improved it for multidimensional array and constant spacing support
function dydx = Gradient(y,x)
if length(y) < 3
dydx = gradient(y,x);
return
end
[~, n] = max(size(y));
N = ndims(y);
i = repmat({':'},1,N-1);
y = permute(y, [n, 1:n-1, n+1:N]);
if isscalar(x)
%"x" is actually a spacing value
p = x;
m = x;
p1 = x;
p2 = x;
m1 = x;
m2 = x;
else
if isvector(x)
x = repmat(x(:), size(y(1, i{:})));
else
x = permute(x, [n, 1:n-1, n+1:N]);
end
if all(size(x) ~= size(y))
error('Sizes of arrays must be the same.')
end
p = x(3:end, i{:}) - x(2:end-1, i{:});
m = x(2:end-1, i{:}) - x(1:end-2, i{:});
p1 = x(2, i{:}) - x(1, i{:});
p2 = x(3, i{:}) - x(1, i{:});
m1 = x(end, i{:}) - x(end-1, i{:});
m2 = x(end, i{:}) - x(end-2, i{:});
end
dydx = ipermute([ ((-p1.^2 + p2.^2).*y(1,i{:}) - p2.^2.*y(2,i{:}) + p1.^2.*y(3,i{:}))./(p1.*(p1 - p2).*p2);
((-m.^2 + p.^2).*y(2:end-1,i{:}) - p.^2.*y(1:end-2,i{:}) + m.^2.*y(3:end,i{:}))./(m.*p.*(m + p));
((m1.^2 - m2.^2).*y(end,i{:}) + m2.^2.*y(end-1,i{:}) - m1.^2.*y(end-2,i{:}))./(m1.^2.*m2 - m1.*m2.^2) ...
], [n, 1:n-1, n+1:N]);
end

VBScript loop advice

sqlRows = rst.GetRows()
i = 0
For Each element In sqlRows
If i > 0 And i < sizeOfState + 1 Then
SmartTags("visu_state_on")(i - 1) = element
ElseIf i > sizeOfState And i < 2 * sizeOfState + 1 Then
SmartTags("visu_state_off")(i - sizeOfState - 1) = element
ElseIf i > (2 * sizeOfState ) And i < 2 * sizeOfState + sizeOfMeasurement + 1 Then
SmartTags("visu_limits_right")(i - (2 * sizeOfState - 1)) = element
ElseIf i > 2 * sizeOfState + sizeOfMeasurement And i < 2 * (sizeOfStanja + sizeOfMeasurement ) + 1 Then
SmartTags("visu_limits_left")(i - (2 * sizeOfState + sizeOfMeasurement )) = element
End If
i = i + 1
Next
With code above I'm looping through array sqlRows and with variable i I'm filling other four arrays with data from sqlRows.
This solution works but I'm wondering is there more elegant way to achieve the same.
sqlRows is array with dimensions 343x1,
visu_state_on is array with dimensions 8x1,
visu_state_off is array with dimensions 8x1,
visu_limits_right is array with dimensions 160x1,
visu_limits_left is array with dimensions 160x1,
and variables sizeOfState and sizeOfMeasurement are just there that I can calculate indexes for these four arrays.
For one thing you can remove the first clause from each of your conditions, because they will always be true
i starts with the value 0 and is always incremented, so the value will never be less than zero.
If a value is not less than n + 1 (condition in previous ElseIf) then it's guaranteed to be greater than n.
Also, VBScript does have a <= comparison operator, so it's better to compare i <= n rather than i < n + 1.
I would also recommend calculating values that won't change inside the loop just once outside the loop. Re-calculating them with each loop cycle is a waste of resources.
Adding an Else branch to handle values greater than 2 * (sizeOfStanja + sizeOfMeasurement) might be a good idea too.
sqlRows = rst.GetRows()
i = 0
ref1 = 2 * sizeOfState
ref2 = ref1 + sizeOfMeasurement
ref3 = 2 * (sizeOfStanja + sizeOfMeasurement)
For Each element In sqlRows
If i <= sizeOfState Then
SmartTags("visu_state_on")(i - 1) = element
ElseIf i <= ref1 Then
SmartTags("visu_state_off")(i - sizeOfState - 1) = element
ElseIf i <= ref2 Then
SmartTags("visu_limits_right")(i - ref1 + 1) = element
ElseIf i <= ref3 Then
SmartTags("visu_limits_left")(i - ref2) = element
Else
'handle i > ref3 here
End If
i = i + 1
Next

Resources