As a Matlab person, I can assure you that it is a blessed relief that Julia distinguishes scalars, vectors and matrices.
The way Matlab works in this regard is a terrible headache, with zero advantages.
As a Matlab person, I can assure you that it is a blessed relief that Julia distinguishes scalars, vectors and matrices.
The way Matlab works in this regard is a terrible headache, with zero advantages.
The easiest way to understand it is to simply count the dimensions. (n, 1)
are 1, 2 dimensions. (n,)
is 1 dimension. The fact that the length of the last dimension is 1 is incidental. It could even be (n, 0)
and itâs still 2D.
Actually, the Fortran example works in Julia as well:
julia> x = collect([1 1 1;]')
3Ă1 Array{Int64,2}:
1
1
1
julia> mysum(x) = sum(x[i] for i in 1:length(x));
julia> mysum(x)
3
You can access a Julia multi-dimensional array by a single (column-major) index, but thatâs not the same as their types being the same. I suspect gfortranâs MATMUL will also choke on two vector inputs: âMATRIX_A and MATRIX_B shall not both be rank one arraysâ.
Well, that is why I think that the error thrown by those functions is an imposition of the type verification only. If a method accepted the input unidimensional matrix and feed the same calculations with the correct pointers, the result must be the same.
Actually it works:
program main
double precision :: x(3), y(3,1)
do i = 1, 3
x(i) = 1.
y(i,1) = 1.
end do
write(*,*) matmul(x,y)
end program main
$ gfortran matmul.f90 -o matmul
$ ./matmul
3.0000000000000000
Edit: it does choke if x
is declared as x(3,1)
.
I sort of agree with you, because for this case the memory layout is what you are thinking. But as you found, Fortranâs MATMUL can also choke from its type verification. (BTW your example doesnât show two vectors x(3)
and y(3)
which the documentation says should fail.)
The admonition against thinking in terms of pointers and memory layout deserves some explanation. The type system allows for more abstraction than a Fortran real array, and allows things like sparse arrays, or LU decompositions, or adjoint arrays to be used with the same common syntax like *
, and things will usually just work. The abstraction is incredibly powerful, and enables automatic differentiation to propagate through packages that were never written with it in mind. Enabled by the type system. Similarly, curve_fit
should just work if fed abstractions of 2d arrays with entirely different memory layout. (But alas, not 1d arraysâŚ)
I think my expectation on this also comes from the fact that a naive algorithm would work in these cases:
julia> function matmul(a,b)
na = size(a,1); ma = size(a,2)
nb = size(b,1); mb = size(b,2)
if ( ma != nb ); error(" ma != nb "); end
c = zeros(na,mb)
for i in 1:na
for j in 1:mb
for k in 1:ma
c[i,j] = c[i,j] + a[i,k]*b[k,j]
end
end
end
c
end
julia> a = ones(3); b = ones(3,1) ;
julia> matmul(transpose(a),b)
1Ă1 Array{Float64,2}:
3.0
julia> a = ones(3,1); b = ones(3) ;
julia> matmul(transpose(a),b)
1Ă1 Array{Float64,2}:
3.0
julia> a = ones(3,1); b = ones(3,1);
julia> matmul(transpose(a),b)
1Ă1 Array{Float64,2}:
3.0
Edit: Yes, fortran complains at compiling time if both are vectors:
Error: âmatrix_bâ argument of âmatmulâ intrinsic at (1) must be of rank 2
âŚa naive algorithm would work in these cases
Yes, but the issue is not mul!
per se. As @mcabbott explains, curve_fit
doesnât anticipate matrix input, with a potentially very easy fix.
In fact, mul!
does handle vectors or matrices, it just doesnât like mismatched types (i.e. Matlab ambiguity). Also, the documentation calls mul!
a low-level function, which signals that higher level functions should take responsibility for massaging its various arguments before calling it. mul!
is specific to get its high performance, whereas your example or even a plain *
would work more generally at the cost of new allocation. So I would look to curve_fit
(or perhaps lmfit
) to handle edge cases if it wants to, or have clearer documentation.
But in Julia, it isnât. Also, it is not something that needs to be explicitly documented, as the manual gives you no reason to assume that it is.