Related
My question is instead of F = svd(A), can one first allocate an appropriate memory for an SVD structure, and then do F .= svd(A) ?
What I had in mind is something like the following:
function main()
F = Vector{SVD}(undef,10)
# how to preallocate F?
test(F)
end
function test(F::Vector{SVD})
for i in 1:10
F .= svd(rand(3,3))
end
end
Your code almost works. But what you probably wanted was this:
using LinearAlgebra
function main()
F = Vector{SVD}(undef, 10)
test(F)
end
function test(F::Vector{SVD})
for i in 1:10
F[i] = svd(rand(3, 3))
end
return F
end
The line that you had in the for loop was this:
F .= svd(rand(3,3))
which does the same operation on every loop, since you were not indexing into F. In particular, this operation was trying to broadcast a single SVD object into all the fields of F on each iteration of the loop. (And that broadcast operation failed because by default structs are treated as iterable objects with a length method, but SVD does not have a length method.)
However, I would recommend against pre-allocating a vector in this situation. First, let's look at the type of F:
julia> typeof(Vector{SVD}(undef, 10))
Array{SVD,1}
The problem with this vector is that it is parameterized by an abstract type. There is a section in the Performance Tips chapter of the manual that advises against this. SVD is an abstract type because the types of its parameters have not been specified. To make it concrete, you need to specify the types of the parameters, like this:
julia> SVD{Float64,Float64,Array{Float64,2}}
SVD{Float64,Float64,Array{Float64,2}}
julia> Vector{SVD{Float64,Float64,Array{Float64,2}}}(undef, 2)
2-element Array{SVD{Float64,Float64,Array{Float64,2}},1}:
#undef
#undef
As you can see, it is difficult to correctly specify the concrete type when you are working with complicated types like SVD. Additionally, if you do so, your code will not be as generic as it could be.
A better approach for a problem like this is to use mapping, broadcasting, or a list comprehension. Then the correct output type will automatically be inferred. Here are some examples:
List comprehension
julia> [svd(rand(3, 3)) for _ in 1:2]
2-element Array{SVD{Float64,Float64,Array{Float64,2}},1}:
SVD{Float64,Float64,Array{Float64,2}}([-0.6357040496635746 -0.2941425771794837 -0.7136949667270628; -0.45459999623274916 -0.6045700314848496 0.654090147040599; -0.6238743500629883 0.7402534845042064 0.2506104028424691], [1.4535849689665463, 0.7212190827260345, 0.05010669163393896], [-0.5975505057447164 -0.588792736048385 -0.5442945039782142; 0.7619724725128861 -0.6283345569895092 -0.15682358121595258; -0.2496624605679292 -0.5084474392397449 0.8241054891903787])
SVD{Float64,Float64,Array{Float64,2}}([-0.5593632049776268 0.654338345992878 -0.5088753618327984; -0.6687620652652163 -0.7189576326033171 -0.18936003428293915; -0.4897653570633183 0.23439550227070827 0.8397551092645418], [1.8461274187259178, 0.21226179692488983, 0.14194607536315287], [-0.29089551972856004 -0.7086270946133293 -0.6428276887173754; -0.9203610429640889 0.023709029028269546 0.390350397126212; 0.2613720474647311 -0.7051847436823973 0.6590896221923739])
Map
julia> map(_ -> svd(rand(3, 3)), 1:2)
2-element Array{SVD{Float64,Float64,Array{Float64,2}},1}:
SVD{Float64,Float64,Array{Float64,2}}([-0.5807809149601634 0.5635242755434755 0.5874809951745127; -0.6884131975465821 0.0451903888051729 -0.7239095925620322; -0.43448912329507794 -0.8248625459025509 0.3616918330643316], [1.488618654040125, 0.4122166626927311, 0.004235624485479941], [-0.6721098925787947 -0.2684664121709399 -0.6900681689759235; -0.7384292974335966 0.31185073633575333 0.5978890289498324; -0.05468514413847799 -0.9114136842196914 0.4078414290231468])
SVD{Float64,Float64,Array{Float64,2}}([-0.3677873424759118 0.8090638526628051 -0.4584191892023337; -0.43071684640222546 -0.5851169278783189 -0.6871107472129654; -0.8241452960126802 -0.055261768200600137 0.5636760310989947], [1.6862363968739773, 0.5899255050748418, 0.24246688716190598], [-0.3751742784957875 -0.7172409091515735 -0.5872050229643736; 0.8600668700980193 -0.505618838823938 0.06807766730822862; -0.3457300098559026 -0.4794945964927631 0.8065703268899])
Broadcasting
julia> g = (rand(3, 3) for _ in 1:2)
Base.Generator{UnitRange{Int64},var"#17#18"}(var"#17#18"(), 1:2)
julia> svd.(g)
2-element Array{SVD{Float64,Float64,Array{Float64,2}},1}:
SVD{Float64,Float64,Array{Float64,2}}([-0.7988295268840152 0.5443221484534134 -0.256095266807727; -0.5436890668169485 -0.8354777569473182 -0.0798693700362902; -0.257436566171119 0.07543418554831638 0.963346302244777], [1.8188722412547844, 0.3934389096422389, 0.2020398396772306], [-0.7147404794808727 -0.37763644211761316 -0.5886737335538281; -0.6944558966482991 0.4830041206449164 0.5333273169925189; -0.08292800854873916 -0.7899985677359054 0.607474450798845])
SVD{Float64,Float64,Array{Float64,2}}([-0.5910620103531503 0.3599866268397522 0.7218416228050514; -0.7367495542691711 0.12340124384185132 -0.664809918173956; -0.3283988340440176 -0.9247603805931685 0.1922821996018057], [1.826019614357666, 0.5333148215847028, 0.11639139812894106], [-0.6415954756495915 -0.6888196183142843 -0.33746522643279503; -0.5845558664639438 0.7239484700883465 -0.3663236978948133; -0.4966383841474222 0.037764349353666515 0.8671356118331964])
Furthermore, mapping, broadcasting, and list comprehensions should be just as efficient as pre-allocating the vector. If you're doing a simple mapping, then it's usually easier and more readable to use mapping, broadcasting, or list comprehensions. Pre-allocating vectors is a tool I reserve for writing custom algorithms from scratch.
A final note. In most cases, type parameters are considered an implementation detail and are not a part of the public API for a type. As such, it's best to use generic programming approaches that do not rely on fixing the types for type parameters. Of course there are some exceptions to this rule of thumb, like Array{T,N} and Dict{K,V}.
There's a differnent way of preallocation -- you can reuse the input array by always overwriting it, with both the rand call and svd's internal needs:
function test!(F::Vector{SVD})
A = Matrix{Float64}(undef, 3, 3)
for i in 1:10
rand!(A)
F[i] = svd!(A)
end
end
Cameron's advice still holds. I'd probably use something like
function test()
A = Matrix{Float64}(undef, 3, 3)
return map(1:10) do i
svd!(rand!(A))
end
end
given that the number of loops seems not be the critical part.
Consider an existing function in Base, which takes in a variable number of arguments of some abstract type T. I have defined a subtype S<:T and would like to write a method which dispatches if any of the arguments is my subtype S.
As an example, consider function Base.cat, with T being an AbstractArray and S being some MyCustomArray <: AbstractArray.
Desired behaviour:
julia> v = [1, 2, 3];
julia> cat(v, v, v, dims=2)
3×3 Array{Int64,2}:
1 1 1
2 2 2
3 3 3
julia> w = MyCustomArray([1,2,3])
julia> cat(v, v, w, dims=2)
"do something fancy"
Attempt:
function Base.cat(w::MyCustomArray, a::AbstractArray...; dims)
pritnln("do something fancy")
end
But this only works if the first argument is MyCustomArray.
What is an elegant way of achieving this?
I would say that it is not possible to do it cleanly without type piracy (but if it is possible I would also like to learn how).
For example consider cat that you asked about. It has one very general signature in Base (actually not requiring A to be AbstractArray as you write):
julia> methods(cat)
# 1 method for generic function "cat":
[1] cat(A...; dims) in Base at abstractarray.jl:1654
You could write a specific method:
Base.cat(A::AbstractArray...; dims) = ...
and check if any of elements of A is your special array, but this would be type piracy.
Now the problem is that you cannot even write Union{S, T} as since S <: T it will be resolved as just T.
This would mean that you would have to use S explicitly in the signature, but then even:
f(::S, ::T) = ...
f(::T, ::S) = ...
is problematic and a compiler will ask you to define f(::S, ::S) as the above definitions lead to dispatch ambiguity. So, even if you wanted to limit the number of varargs to some maximum number you would have to annotate types for all divisions of A into subsets to avoid dispatch ambiguity (which is doable using macros, but grows the number of required methods exponentially).
For general usage, I concur with Bogumił, but let me make an additional comment. If you have control over how cat is called, you can at least write some kind of trait-dispatch code:
struct MyCustomArray{T, N} <: AbstractArray{T, N}
x::Array{T, N}
end
HasCustom() = Val(false)
HasCustom(::MyCustomArray, rest...) = Val(true)
HasCustom(::AbstractArray, rest...) = HasCustom(rest...)
# `IsCustom` or something would be more elegant, but `Val` is quicker for now
Base.cat(::Val{true}, args...; dims) = println("something fancy")
Base.cat(::Val{false}, args...; dims) = cat(args...; dims=dims)
And the compiler is cool enough to optimize that away:
julia> args = (v, v, w);
julia> #code_warntype cat(HasCustom(args...), args...; dims=2);
Variables
#self#::Core.Compiler.Const(cat, false)
#unused#::Core.Compiler.Const(Val{true}(), false)
args::Tuple{Array{Int64,1},Array{Int64,1},MyCustomArray{Int64,1}}
Body::Nothing
1 ─ %1 = Main.println("something fancy")::Core.Compiler.Const(nothing, false)
└── return %1
If you don't have control over calls to cat, the only resort I can think of to make the above technique work is to overdub methods containing such call, to replace matching calls by the custom implementation. In which case you don't even need to overload cat, but can directly replace it by some mycat doing your fancy stuff.
Let for example function g be defined by g(x):=x+1.
I want to programm a function f which can take a arbitrary function h(a_1,...,a_n) (a_1,...,a_n being the arguments) and returns the function g(h). So that
f(h)(a_1=1,...,a_n=n) works and returns the same as g(h(a_1=1,...,a_n=n)).
So we need something like
f <- (h){
- get the arguments of h and put them in a list/vector arg(I found functions that do that)
- return a function ´f(h)´ that has the elements of arg as arguments. (I am not sure how to do that)
}
I'm not sure I understand your question since what you wrote seems ok but is that what you are looking for?
somelistorvector <- list(a = 1, b = 2)
fct <- function(arg){
arg[[1]] + arg[[2]] # arg[["a"]] + arg[["b"]] could also work
}
fct(somelistorvector)
[1] 3
Also are the arguments always going to be a and b or element 1 and 2 ?
In the Julia documentation for SortedSet, there is a reference to "ordering objects", which can be used in the constructor. I'm working on a project where I need to implement a custom sort on a set of structs. I'd like to use a functor for this, since there is additional state I need for my comparisons.
Here is a somewhat simplified version of the problem I want to solve. I have two structs, Point and Edge:
struct Point{T<:Real}
x::T
y::T
end
struct Edge{T<:Real}
first::Point{T}
second::Point{T}
end
I have a Point called 'vantage', and I want to order Edges by their distance from 'vantage'. Conceptually:
function edge_ordering(vantage::Point, e1::Edge, e2::Edge)
d1 = distance(vantage, e1)
d2 = distance(vantage, e2)
return d1 < d2
end
Are "ordering objects" functors (or functor-ish)? Is there some other conventional way of doing this sort of ordering in Julia?
An Ordering object can contain fields, you can store your state there. This is an example of a Remainder Ordering which sort integers by it's remainder:
using DataStructures
struct RemainderOrdering <: Base.Order.Ordering
r::Int
end
import Base.Order.lt
lt(o::RemainderOrdering, a, b) = isless(a % o.r, b % o.r)
SortedSet(RemainderOrdering(3), [1,2,3]) # 3, 1, 2
I'm not sure how it is related to functors, so I may misunderstand your question. This is an alternative implementation that defines an Ordering functor. I made explanations in comments.
using DataStructures
import Base: isless, map
struct Foo # this is your structure
x::Int
end
struct PrimaryOrdered{T, F} # this is the functor, F is the additional state.
x::T
end
map(f::Base.Callable, x::T) where {T <: PrimaryOrdered} = T(f(x.x)) # this makes it a functor?
isless(x::PrimaryOrdered{T, F}, y::PrimaryOrdered{T, F}) where {T, F} =
F(x.x) < F(y.x) # do comparison with your additional state, here I assume it is a closure
const OrderR3 = PrimaryOrdered{Foo, x -> x.x % 3} # a order that order by the remainder by 3
a = OrderR3(Foo(2))
f(x::Foo) = Foo(x.x + 1) # this is a Foo -> Foo
a = map(f, a) # you can map f on a OrderR3 object
a == OrderR3(Foo(33)) # true
a = map(OrderR3 ∘ Foo, [1, 2, 3])
s = SortedSet(a)
map(x->x.x, s) # Foo[3, 1, 2]
As always, an MWE is important for a question to be understood better. You can include a piece of code to show how you want to construct and use your SortedSet, instead of the vague "state" and "functor".
The sorting is based on the method isless for the type. So for instance if you have a type in which you want to sort on the b field. For instance you can do
struct Foo{T}
a::T
b::T
end
Base.:isless(x::T,y::T) where {T<:Foo} = isless(x.b,y.b)
s=[Foo(1,2),Foo(2,-1)]
res=SortedSet(s)
#SortedSet(Foo[Foo(2, -1), Foo(1, 2)],
#Base.Order.ForwardOrdering())
Tuples are also sorted in order, so you can also use
sort(s,by=x->(x.b,x.a)) to sort by b,thena without having to define isless for the type.
Is there a way to check if a function has keywords arguments in Julia? I am looking for something like has_kwargs(fun::Function) that would return true if fun has a method with keyword arguments.
The high level idea is to build a function:
function master_fun(foo::Any, fun::Function, ar::Tuple, kw::Tuple)
if has_kwargs(fun)
fun(ar... ; kw...)
else
fun(ar...)
end
end
Basically, #Michael K. Borregaard's suggestion to use try-catch is correct and officially works.
Looking into the unofficial implementation details, I came up with the followng:
haskw(f,tup) = isdefined(typeof(f).name.mt,:kwsorter) &&
length(methods(typeof(f).name.mt.kwsorter,(Vector{Any},typeof(f),tup...)))>0
This function first looks if there is any keyword processing on any method of the generic function, and if so, looks at the specific tuple of types.
For example:
julia> f(x::Int) = 1
f (generic function with 1 method)
julia> f(x::String ; y="value") = 2
f (generic function with 2 methods)
julia> haskw(f,(Int,))
false
julia> haskw(f,(String,))
true
This should be tested for the specific application, as it probably doesn't work when non-leaf types are involved. As Michael commented, in the question's context the statement would be:
if haskw(fun, typeof.(ar))
...
I don't think you can guarantee that a given function has keyword arguments. Check
f(;x = 3) = println(x)
f(x) = println(2x)
f(3)
#6
f(x = 3)
#3
f(3, x = 3)
#ERROR: MethodError: no method matching f(::Int64; x=3)
#Closest candidates are:
# f(::Any) at REPL[2]:1 got unsupported keyword argument "x"
# f(; x) at REPL[1]:1
So, does the f function have keywords? You can only check for a given method. Note that, in your example above, you'd normally just do
function master_fun(foo, fun::Function, ar::Tuple, kw....)
fun(ar... ; kw...)
end
which should work, and if keywords are passed to a function that does not take them you'd just leave the error reporting to fun. If that is not acceptable you could try to wrap the fun(ar...; kw...) in a try-catch block.