From 870ce0eb04027a1600eb61919af3bad229da607e Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Mon, 23 Oct 2017 06:42:03 -0500 Subject: [PATCH 01/34] Emit explanatory error message for iteration on CartesianIndex Fixes #23982 --- base/multidimensional.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 2811eca7a3b72..6247389de4d97 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -147,6 +147,11 @@ module IteratorsMD return ni end + # Iteration over the elements of CartesianIndex cannot be supported until its length can be inferred, + # see #23719 + Base.start(::CartesianIndex) = + error("iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`") + # Iteration """ CartesianRange(sz::Dims) -> R From 51930da60572e406febf493be742416c99851401 Mon Sep 17 00:00:00 2001 From: Curtis Vogt Date: Tue, 24 Oct 2017 10:43:46 -0500 Subject: [PATCH 02/34] Fix issue with mod(::BigInt, ::Unsigned) --- base/int.jl | 2 +- test/bigint.jl | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/base/int.jl b/base/int.jl index 6d063368f7140..ff7f24246bb75 100644 --- a/base/int.jl +++ b/base/int.jl @@ -207,7 +207,7 @@ function mod(x::T, y::T) where T<:Integer y == -1 && return T(0) # avoid potential overflow in fld return x - fld(x, y) * y end -mod(x::Signed, y::Unsigned) = rem(y + unsigned(rem(x, y)), y) +mod(x::BitSigned, y::Unsigned) = rem(y + unsigned(rem(x, y)), y) mod(x::Unsigned, y::Signed) = rem(y + signed(rem(x, y)), y) mod(x::T, y::T) where {T<:Unsigned} = rem(x, y) diff --git a/test/bigint.jl b/test/bigint.jl index 1b40d67a27409..575235876649b 100644 --- a/test/bigint.jl +++ b/test/bigint.jl @@ -393,3 +393,6 @@ end @test typeof(cos(a)) == BigFloat @test typeof(sin(a)) == BigFloat end + +# Issue #24298 +@test mod(BigInt(6), UInt(5)) == mod(6, 5) From c59b8b184e10805a23ebc1e0702e0199147afbb1 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 18 Oct 2017 17:23:54 -0400 Subject: [PATCH 03/34] bugfix in collect(A) for zero-dimensional array --- base/array.jl | 2 +- test/abstractarray.jl | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/base/array.jl b/base/array.jl index 3b7096fdd254b..c4abdf899b377 100644 --- a/base/array.jl +++ b/base/array.jl @@ -620,7 +620,7 @@ function _collect(cont, itr, ::HasEltype, isz::SizeUnknown) return a end -_collect_indices(::Tuple{}, A) = copy!(Vector{eltype(A)}(), A) +_collect_indices(::Tuple{}, A) = copy!(Array{eltype(A)}(), A) _collect_indices(indsA::Tuple{Vararg{OneTo}}, A) = copy!(Array{eltype(A)}(length.(indsA)), A) function _collect_indices(indsA, A) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index a1d698d2ac933..6e852a90c5b22 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -872,3 +872,8 @@ end copy!(Array{T,n}(size(a)), a) @test isa(similar(Dict(:a=>1, :b=>2.0), Pair{Union{},Union{}}), Dict{Union{}, Union{}}) end + +@testset "zero-dimensional copy" begin + Z = Array{Int}(); Z[] = 17 + @test Z == collect(Z) == copy(Z) +end From 6f4987e12a860eadefc3e2b2ccdc29f718e5d796 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 17 Oct 2017 17:24:31 -0400 Subject: [PATCH 04/34] add Iterators.reverse and Iterators.Reverse type for reverse-order iteration --- NEWS.md | 3 ++ base/array.jl | 3 +- base/iterators.jl | 74 ++++++++++++++++++++++++++++++++++-- base/multidimensional.jl | 28 ++++++++++++++ base/strings/basic.jl | 8 ++++ base/strings/types.jl | 1 + doc/src/manual/interfaces.md | 19 +++++++++ doc/src/stdlib/iterators.md | 2 + test/iterators.jl | 18 +++++++++ 9 files changed, 152 insertions(+), 4 deletions(-) diff --git a/NEWS.md b/NEWS.md index b261a65620b2d..b35b254ad2e65 100644 --- a/NEWS.md +++ b/NEWS.md @@ -274,6 +274,9 @@ Library improvements For example, `x^-1` is now essentially a synonym for `inv(x)`, and works in a type-stable way even if `typeof(x) != typeof(inv(x))` ([#24240]). + * New `Iterators.reverse(itr)` for reverse-order iteration ([#24187]). Iterator + types `T` can implement `start` etc. for `Iterators.Reverse{T}` to support this. + * The functions `nextind` and `prevind` now accept `nchar` argument that indicates the number of characters to move ([#23805]). diff --git a/base/array.jl b/base/array.jl index c4abdf899b377..8a002399ec862 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1450,7 +1450,8 @@ end """ reverse(v [, start=1 [, stop=length(v) ]] ) -Return a copy of `v` reversed from start to stop. +Return a copy of `v` reversed from start to stop. See also [`Iterators.reverse`](@ref) +for reverse-order iteration without making a copy. # Examples ```jldoctest diff --git a/base/iterators.jl b/base/iterators.jl index 6d006070c7717..1b537e3f6beae 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -5,10 +5,10 @@ Methods for working with Iterators. """ module Iterators -import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims, pairs +import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, indices, ndims, pairs, last, first using Base: tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape, - IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds + IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds, Generator, AbstractRange export enumerate, zip, rest, countfrom, take, drop, cycle, repeated, product, flatten, partition @@ -30,6 +30,52 @@ and_iteratorsize(a, b) = SizeUnknown() and_iteratoreltype(iel::T, ::T) where {T} = iel and_iteratoreltype(a, b) = EltypeUnknown() +## Reverse-order iteration for arrays and other collections. Collections +## should implement start/next/done etcetera if possible/practical. +""" + Iterators.reverse(itr) + +Given an iterator `itr`, then `reverse(itr)` is an iterator over the +same collection but in the reverse order. + +This iterator is "lazy" in that it does not make a copy of the collection in +order to reverse it; see [`Base.reverse`](@ref) for an eager implementation. + +Not all iterator types `T` support reverse-order iteration. If `T` +doesn't, then iterating over `Iterators.reverse(itr::T)` will throw a [`MethodError`](@ref) +because of the missing [`start`](@ref), [`next`](@ref), and [`done`](@ref) +methods for `Iterators.Reverse{T}`. (To implement these methods, the original iterator +`itr::T` can be obtained from `r = Iterators.reverse(itr)` by `r.itr`.) +""" +reverse(itr) = Reverse(itr) + +struct Reverse{T} + itr::T +end +eltype(r::Reverse) = eltype(r.itr) +length(r::Reverse) = length(r.itr) +size(r::Reverse) = size(r.itr) +iteratorsize(r::Reverse) = iteratorsize(r.itr) +iteratoreltype(r::Reverse) = iteratoreltype(r.itr) +last(r::Reverse) = first(r.itr) # the first shall be last +first(r::Reverse) = last(r.itr) # and the last shall be first + +# reverse-order array iterators: assumes more-specialized Reverse for eachindex +@inline start(A::Reverse{<:AbstractArray}) = (itr = reverse(eachindex(A.itr)); (itr, start(itr))) +@propagate_inbounds next(A::Reverse{<:AbstractArray}, i) = ((idx, s) = next(i[1], i[2]); (A.itr[idx], (i[1], s))) +@propagate_inbounds done(A::Reverse{<:AbstractArray}, i) = done(i[1], i[2]) + +reverse(R::AbstractRange) = Base.reverse(R) # copying ranges is cheap +reverse(G::Generator) = Generator(G.f, reverse(G.iter)) +reverse(r::Reverse) = r.itr +reverse(x::Union{Number,Char}) = x +reverse(p::Pair) = Base.reverse(p) # copying pairs is cheap + +start(r::Reverse{<:Tuple}) = length(r.itr) +done(r::Reverse{<:Tuple}, i::Int) = i < 1 +next(r::Reverse{<:Tuple}, i::Int) = (r.itr[i], i-1) + + # enumerate struct Enumerate{I} @@ -75,6 +121,16 @@ eltype(::Type{Enumerate{I}}) where {I} = Tuple{Int, eltype(I)} iteratorsize(::Type{Enumerate{I}}) where {I} = iteratorsize(I) iteratoreltype(::Type{Enumerate{I}}) where {I} = iteratoreltype(I) +@inline function start(r::Reverse{<:Enumerate}) + ri = reverse(r.itr.itr) + return (length(ri), ri, start(ri)) +end +@inline function next(r::Reverse{<:Enumerate}, state) + n = next(state[2],state[3]) + (state[1],n[1]), (state[1]-1,state[2],n[2]) +end +@inline done(r::Reverse{<:Enumerate}, state) = state[1] < 1 + struct IndexValue{I,A<:AbstractArray} data::A itr::I @@ -147,6 +203,8 @@ eltype(::Type{IndexValue{I,A}}) where {I,A} = Pair{eltype(I), eltype(A)} iteratorsize(::Type{IndexValue{I}}) where {I} = iteratorsize(I) iteratoreltype(::Type{IndexValue{I}}) where {I} = iteratoreltype(I) +reverse(v::IndexValue) = IndexValue(v.data, reverse(v.itr)) + # zip abstract type AbstractZipIterator end @@ -246,6 +304,10 @@ end iteratorsize(::Type{Zip{I1,I2}}) where {I1,I2} = zip_iteratorsize(iteratorsize(I1),iteratorsize(I2)) iteratoreltype(::Type{Zip{I1,I2}}) where {I1,I2} = and_iteratoreltype(iteratoreltype(I1),iteratoreltype(I2)) +reverse(z::Zip1) = Zip1(reverse(z.a)) +reverse(z::Zip2) = Zip2(reverse(z.a), reverse(z.b)) +reverse(z::Zip) = Zip(reverse(z.a), reverse(z.z)) + # filter struct Filter{F,I} @@ -313,6 +375,8 @@ eltype(::Type{Filter{F,I}}) where {F,I} = eltype(I) iteratoreltype(::Type{Filter{F,I}}) where {F,I} = iteratoreltype(I) iteratorsize(::Type{<:Filter}) = SizeUnknown() +reverse(f::Filter) = Filter(f.flt, reverse(f.itr)) + # Rest -- iterate starting at the given state struct Rest{I,S} @@ -346,7 +410,6 @@ rest_iteratorsize(a) = SizeUnknown() rest_iteratorsize(::IsInfinite) = IsInfinite() iteratorsize(::Type{Rest{I,S}}) where {I,S} = rest_iteratorsize(iteratorsize(I)) - # Count -- infinite counting struct Count{S<:Number} @@ -539,6 +602,7 @@ end done(it::Cycle, state) = state[2] +reverse(it::Cycle) = Cycle(reverse(it.xs)) # Repeated - repeat an object infinitely many times @@ -576,6 +640,7 @@ done(it::Repeated, state) = false iteratorsize(::Type{<:Repeated}) = IsInfinite() iteratoreltype(::Type{<:Repeated}) = HasEltype() +reverse(it::Union{Repeated,Take{<:Repeated}}) = it # Product -- cartesian product of iterators struct ProductIterator{T<:Tuple} @@ -706,6 +771,8 @@ function _prod_next(iterators, states, nvalues) end end +reverse(p::ProductIterator) = ProductIterator(map(reverse, p.iterators)) + # flatten an iterator of iterators struct Flatten{I} @@ -781,6 +848,7 @@ end return done(f.it, s) && done(inner, s2) end +reverse(f::Flatten) = Flatten(reverse(itr) for itr in reverse(f.it)) """ partition(collection, n) diff --git a/base/multidimensional.jl b/base/multidimensional.jl index cc5f4349d573c..7559c91010f84 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -9,6 +9,7 @@ module IteratorsMD import Base: +, -, * import Base: simd_outer_range, simd_inner_length, simd_index using Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail + using Base.Iterators: Reverse export CartesianIndex, CartesianRange @@ -314,6 +315,33 @@ module IteratorsMD i, j = split(R.indices, V) CartesianRange(i), CartesianRange(j) end + + # reversed CartesianRange iteration + @inline function start(r::Reverse{<:CartesianRange}) + iterfirst, iterlast = last(r.itr), first(r.itr) + if any(map(<, iterfirst.I, iterlast.I)) + return iterlast-1 + end + iterfirst + end + @inline function next(r::Reverse{<:CartesianRange}, state) + state, CartesianIndex(dec(state.I, last(r.itr).I, first(r.itr).I)) + end + # decrement & carry + @inline dec(::Tuple{}, ::Tuple{}, ::Tuple{}) = () + @inline dec(state::Tuple{Int}, start::Tuple{Int}, stop::Tuple{Int}) = (state[1]-1,) + @inline function dec(state, start, stop) + if state[1] > stop[1] + return (state[1]-1,tail(state)...) + end + newtail = dec(tail(state), tail(start), tail(stop)) + (start[1], newtail...) + end + @inline done(r::Reverse{<:CartesianRange}, state) = state.I[end] < first(r.itr.indices[end]) + # 0-d cartesian ranges are special-cased to iterate once and only once + start(iter::Reverse{<:CartesianRange{0}}) = false + next(iter::Reverse{<:CartesianRange{0}}, state) = CartesianIndex(), true + done(iter::Reverse{<:CartesianRange{0}}, state) = state end # IteratorsMD diff --git a/base/strings/basic.jl b/base/strings/basic.jl index c04e56eb9a6ac..6dd8ffddc3566 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -638,3 +638,11 @@ function last(str::AbstractString, nchar::Integer) end str[prevind(str, e, nchar-1):e] end + +# reverse-order iteration for strings and indices thereof +start(r::Iterators.Reverse{<:AbstractString}) = endof(r.itr) +done(r::Iterators.Reverse{<:AbstractString}, i) = i < start(r.itr) +next(r::Iterators.Reverse{<:AbstractString}, i) = (r.itr[i], prevind(r.itr, i)) +start(r::Iterators.Reverse{<:EachStringIndex}) = endof(r.itr.s) +done(r::Iterators.Reverse{<:EachStringIndex}, i) = i < start(r.itr.s) +next(r::Iterators.Reverse{<:EachStringIndex}, i) = (i, prevind(r.itr.s, i)) diff --git a/base/strings/types.jl b/base/strings/types.jl index 925c95d6d45b1..d4fd4af39f053 100644 --- a/base/strings/types.jl +++ b/base/strings/types.jl @@ -133,6 +133,7 @@ main utility is for reversed-order string processing, especially for reversed regular-expression searches. See also [`reverseind`](@ref) to convert indices in `s` to indices in `reverse(s)` and vice-versa, and [`graphemes`](@ref) to operate on user-visible "characters" (graphemes) rather than codepoints. +See also [`Iterators.reverse`](@ref) for reverse-order iteration without making a copy. # Examples ```jldoctest diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index 4bee25b025040..7e19c255df667 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -136,6 +136,25 @@ define an informal interface that enable many fancier behaviors. In some cases, to additionally specialize those extra behaviors when they know a more efficient algorithm can be used in their specific case. +It is also often useful to allow iteration over a collection in *reverse order* +by iterating over [`Iterators.reverse(iterator)`](@ref). To actually support +reverse-order iteration, however, an iterator +type `T` needs to implement `start`, `next`, and `done` methods for `Iterators.Reverse{T}`. +(Given `r::Iterators.Reverse{T}`, the underling iterator of type `T` is `r.itr`.) +In our `Squares` example, we would implement `Iterators.Reverse{Squares}` methods: + +```jldoctest squaretype +julia> Base.start(rS::Iterators.Reverse{Squares}) = rS.itr.count + +julia> Base.next(::Iterators.Reverse{Squares}, state) = (state*state, state-1) + +julia> Base.done(::Iterators.Reverse{Squares}, state) = state < 1 + +julia> collect(Iterators.reverse(Squares(10)))' # transposed to save space +1×10 RowVector{Int64,Array{Int64,1}}: + 100 81 64 49 36 25 16 9 4 1 +``` + ## Indexing | Methods to implement | Brief description | diff --git a/doc/src/stdlib/iterators.md b/doc/src/stdlib/iterators.md index a2b1aae2b4bea..7fa782ef2669f 100644 --- a/doc/src/stdlib/iterators.md +++ b/doc/src/stdlib/iterators.md @@ -12,4 +12,6 @@ Base.Iterators.repeated Base.Iterators.product Base.Iterators.flatten Base.Iterators.partition +Base.Iterators.filter +Base.Iterators.reverse ``` diff --git a/test/iterators.jl b/test/iterators.jl index b53b4e8ca1400..9e09d85c8b823 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -429,3 +429,21 @@ end @test length(arr) == 0 @test eltype(arr) == Int end + +@testset "reverse iterators" begin + squash(A) = reshape(A, length(A)) + Z = Array{Int}(); Z[] = 17 # zero-dimensional test case + for itr in (2:10, "∀ϵ>0", 1:0, "", (2,3,5,7,11), [2,3,5,7,11], rand(5,6), Z, 3, true, 'x', 4=>5, + eachindex("∀ϵ>0"), view(Z), view(rand(5,6),2:4,2:6), (x^2 for x in 1:10), + Iterators.Filter(isodd, 1:10), flatten((1:10, 50:60)), enumerate("foo"), + pairs(50:60), zip(1:10,21:30,51:60), product(1:3, 10:12), repeated(3.14159, 5)) + @test squash(collect(Iterators.reverse(itr))) == reverse(squash(collect(itr))) + end + @test collect(take(Iterators.reverse(cycle(1:3)), 7)) == collect(take(cycle(3:-1:1), 7)) + let r = repeated(3.14159) + @test Iterators.reverse(r) === r + end + let t = (2,3,5,7,11) + @test Iterators.reverse(Iterators.reverse(t)) === t + end +end From 35079ebcf5f9930426cf9ea5d1c3e83fe959aa56 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Mon, 23 Oct 2017 22:41:16 -0400 Subject: [PATCH 05/34] Do not trigger write barrier when the child is a permanently rooted object Or when the parent and the child are the same object --- src/llvm-late-gc-lowering.cpp | 161 ++++++++++++++++++++++++++++------ 1 file changed, 135 insertions(+), 26 deletions(-) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 2dda1840a26ad..df95eb8521136 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -305,10 +305,13 @@ struct State { // The result of the local analysis std::map BBStates; - // Refinement map. If all of the values are rooted (-1 means an externally rooted value), + // Refinement map. If all of the values are rooted + // (-1 means an externally rooted value and -2 means a globally/permanently rooted value), // the key is already rooted (but not the other way around). + // A value that can be refined to -2 never need any rooting or write barrier. + // A value that can be refined to -1 don't need local root but still need write barrier. // At the end of `LocalScan` this map has a few properties - // 1. Values are either -1 or dominates the key + // 1. Values are either < 0 or dominates the key // 2. Therefore this is a DAG std::map> Refinements; @@ -430,7 +433,7 @@ struct LateLowerGCFrame: public FunctionPass { bool doFinalization(Module &) override; bool runOnFunction(Function &F) override; Instruction *get_pgcstack(Instruction *ptlsStates); - bool CleanupIR(Function &F); + bool CleanupIR(Function &F, State *S=nullptr); void NoteUseChain(State &S, BBState &BBS, User *TheUser); SmallVector GetPHIRefinements(PHINode *phi, State &S); void FixUpRefinements(ArrayRef PHINumbers, State &S); @@ -571,9 +574,12 @@ int LateLowerGCFrame::NumberBase(State &S, Value *V, Value *CurrentV) if (it != S.AllPtrNumbering.end()) return it->second; int Number; - if (isa(CurrentV) || isa(CurrentV) || - ((isa(CurrentV) || isa(CurrentV)) && - getValueAddrSpace(CurrentV) != AddressSpace::Tracked)) { + if (isa(CurrentV)) { + // Perm rooted + Number = -2; + } else if (isa(CurrentV) || + ((isa(CurrentV) || isa(CurrentV)) && + getValueAddrSpace(CurrentV) != AddressSpace::Tracked)) { // We know this is rooted in the parent Number = -1; } else if (isa(CurrentV) && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { @@ -666,7 +672,7 @@ static bool HasBitSet(const BitVector &BV, unsigned Bit) { } static void NoteDef(State &S, BBState &BBS, int Num, const std::vector &SafepointsSoFar) { - assert(Num != -1); + assert(Num >= 0); MaybeResize(BBS, Num); assert(BBS.Defs[Num] == 0 && "SSA Violation or misnumbering?"); BBS.Defs[Num] = 1; @@ -739,7 +745,7 @@ void LateLowerGCFrame::NoteUse(State &S, BBState &BBS, Value *V, BitVector &Uses } else { int Num = Number(S, V); - if (Num == -1) + if (Num < 0) return; MaybeResize(BBS, Num); Uses[Num] = 1; @@ -821,7 +827,8 @@ static bool isLoadFromImmut(LoadInst *LI) return false; while (TBAA->getNumOperands() > 1) { TBAA = cast(TBAA->getOperand(1).get()); - if (cast(TBAA->getOperand(0))->getString() == "jtbaa_immut") { + auto str = cast(TBAA->getOperand(0))->getString(); + if (str == "jtbaa_immut" || str == "jtbaa_const") { return true; } } @@ -869,6 +876,27 @@ SmallVector LateLowerGCFrame::GetPHIRefinements(PHINode *Phi, State &S) return RefinedPtr; } +JL_USED_FUNC static void DumpRefinements(State *S) +{ + for (auto &kv: S->Refinements) { + int Num = kv.first; + if (Num < 0) + continue; + jl_safe_printf("Refinements for %d -- ", Num); + auto V = S->ReversePtrNumbering[Num]; + llvm_dump(V); + for (auto refine: kv.second) { + if (refine < 0) { + jl_safe_printf(" %d\n", refine); + continue; + } + jl_safe_printf(" %d: ", refine); + auto R = S->ReversePtrNumbering[refine]; + llvm_dump(R); + } + } +} + void LateLowerGCFrame::FixUpRefinements(ArrayRef PHINumbers, State &S) { // Now we have all the possible refinement information, we can remove ones for the invalid @@ -883,12 +911,14 @@ void LateLowerGCFrame::FixUpRefinements(ArrayRef PHINumbers, State &S) // We do this by first assuming all values to be externally rooted and then removing // values that are or can be derived from non-externally rooted values recursively. BitVector extern_rooted(S.MaxPtrNumber + 1, true); + BitVector perm_rooted(S.MaxPtrNumber + 1, true); // * First clear all values that are not derived from anything. // This only needs to be done once. for (int i = 0; i <= S.MaxPtrNumber; i++) { auto it = S.Refinements.find(i); if (it == S.Refinements.end() || it->second.empty()) { extern_rooted[i] = false; + perm_rooted[i] = false; } } // * Then remove values reachable from those values recursively @@ -901,20 +931,42 @@ void LateLowerGCFrame::FixUpRefinements(ArrayRef PHINumbers, State &S) if (!HasBitSet(extern_rooted, Num)) continue; for (auto refine: kv.second) { - if (refine == -1) + if (refine == -2) { + continue; + } + else if (refine == -1) { + if (HasBitSet(perm_rooted, Num)) { + changed = true; + perm_rooted[Num] = false; + } continue; - if (!HasBitSet(extern_rooted, refine)) { + } + else if (!HasBitSet(extern_rooted, refine)) { changed = true; extern_rooted[Num] = false; + perm_rooted[Num] = false; break; } + else if (!HasBitSet(perm_rooted, refine)) { + if (HasBitSet(perm_rooted, Num)) { + changed = true; + perm_rooted[Num] = false; + } + } } } } while (changed); - // * Now the `extern_rooted` map is accurate, normalize all externally rooted values. + // * Now the `extern_rooted` and `perm_rooted` map is accurate, + // normalize all externally rooted values. for (auto &kv: S.Refinements) { int Num = kv.first; - if (HasBitSet(extern_rooted, Num)) { + if (HasBitSet(perm_rooted, Num)) { + // For permanently rooted values, set their refinements simply to `{-2}` + kv.second.resize(1); + kv.second[0] = -2; + continue; + } + else if (HasBitSet(extern_rooted, Num)) { // For externally rooted values, set their refinements simply to `{-1}` kv.second.resize(1); kv.second[0] = -1; @@ -923,6 +975,7 @@ void LateLowerGCFrame::FixUpRefinements(ArrayRef PHINumbers, State &S) for (auto &refine: kv.second) { // For other values, // remove all externally rooted values from their refinements (replace with -1) + // No need to handle -2 specially since it won't make a difference. if (HasBitSet(extern_rooted, refine)) { refine = -1; } @@ -938,7 +991,7 @@ void LateLowerGCFrame::FixUpRefinements(ArrayRef PHINumbers, State &S) BitVector visited(S.MaxPtrNumber + 1, false); for (auto Num: PHINumbers) { // Not sure if `Num` can be `-1` - if (Num == -1 || HasBitSet(extern_rooted, Num)) + if (Num < 0 || HasBitSet(extern_rooted, Num)) continue; visited[Num] = true; auto Phi = cast(S.ReversePtrNumbering[Num]); @@ -946,7 +999,7 @@ void LateLowerGCFrame::FixUpRefinements(ArrayRef PHINumbers, State &S) unsigned j = 0; // new length for (unsigned i = 0; i < RefinedPtr.size(); i++) { auto refine = RefinedPtr[i]; - if (refine == -1 || visited[refine]) + if (refine < 0 || visited[refine]) continue; visited[refine] = true; if (i != j) @@ -1011,7 +1064,7 @@ State LateLowerGCFrame::LocalScan(Function &F) { } auto callee = CI->getCalledFunction(); if (callee && callee == typeof_func) { - MaybeNoteDef(S, BBS, CI, BBS.Safepoints, SmallVector{-1}); + MaybeNoteDef(S, BBS, CI, BBS.Safepoints, SmallVector{-2}); } else { MaybeNoteDef(S, BBS, CI, BBS.Safepoints); @@ -1080,7 +1133,7 @@ State LateLowerGCFrame::LocalScan(Function &F) { else if (isLoadFromConstGV(LI)) { // If this is a const load from a global, // we know that the object is a constant as well and doesn't need rooting. - RefinedPtr.push_back(-1); + RefinedPtr.push_back(-2); } MaybeNoteDef(S, BBS, LI, BBS.Safepoints, std::move(RefinedPtr)); NoteOperandUses(S, BBS, I, BBS.UpExposedUsesUnrooted); @@ -1092,7 +1145,7 @@ State LateLowerGCFrame::LocalScan(Function &F) { if (S.AllPtrNumbering.find(SI) != S.AllPtrNumbering.end()) continue; auto Num = LiftSelect(S, SI); - if (Num == -1) + if (Num < 0) continue; auto SelectBase = cast(S.ReversePtrNumbering[Num]); SmallVector RefinedPtr{Number(S, SelectBase->getTrueValue()), @@ -1133,7 +1186,7 @@ State LateLowerGCFrame::LocalScan(Function &F) { auto origin = ASCI->getPointerOperand()->stripPointerCasts(); if (auto LI = dyn_cast(origin)) { if (isLoadFromConstGV(LI)) { - RefinedPtr.push_back(-1); + RefinedPtr.push_back(-2); } } MaybeNoteDef(S, BBS, ASCI, BBS.Safepoints, std::move(RefinedPtr)); @@ -1242,11 +1295,11 @@ void LateLowerGCFrame::RefineLiveSet(BitVector &LS, State &S) changed = false; for (auto &kv: S.Refinements) { int Num = kv.first; - if (Num == -1 || HasBitSet(FullLS, Num) || kv.second.empty()) + if (Num < 0 || HasBitSet(FullLS, Num) || kv.second.empty()) continue; bool live = true; for (auto &refine: kv.second) { - if (refine == -1 || HasBitSet(FullLS, refine)) + if (refine < 0 || HasBitSet(FullLS, refine)) continue; live = false; break; @@ -1268,7 +1321,7 @@ void LateLowerGCFrame::RefineLiveSet(BitVector &LS, State &S) continue; bool rooted = true; for (auto RefPtr: RefinedPtr) { - if (RefPtr == -1 || HasBitSet(FullLS, RefPtr)) + if (RefPtr < 0 || HasBitSet(FullLS, RefPtr)) continue; rooted = false; break; @@ -1526,7 +1579,53 @@ Value *LateLowerGCFrame::EmitLoadTag(IRBuilder<> &builder, Value *V) return load; } -bool LateLowerGCFrame::CleanupIR(Function &F) { +// Enable this optimization only on LLVM 4.0+ since this cause LLVM to optimize +// constant store loop to produce a `memset_pattern16` with a global variable +// that's initialized by `addrspacecast`. Such a global variable is not supported by the backend. +// This is not a problem on 4.0+ since that transformation (in loop-idiom) is disabled +// for NI pointers. +#if JL_LLVM_VERSION >= 40000 +static SmallVector *FindRefinements(Value *V, State *S) +{ + if (!S) + return nullptr; + auto it = S->AllPtrNumbering.find(V); + if (it == S->AllPtrNumbering.end()) + return nullptr; + auto rit = S->Refinements.find(it->second); + return rit != S->Refinements.end() && !rit->second.empty() ? &rit->second : nullptr; +} + +static bool IsPermRooted(Value *V, State *S) +{ + if (isa(V)) + return true; + if (auto *RefinePtr = FindRefinements(V, S)) + return RefinePtr->size() == 1 && (*RefinePtr)[0] == -2; + return false; +} +#else +static bool IsPermRooted(Value *V, State *S) +{ + return false; +} +#endif + +static inline void UpdatePtrNumbering(Value *From, Value *To, State *S) +{ + if (!S) + return; + auto it = S->AllPtrNumbering.find(From); + if (it == S->AllPtrNumbering.end()) + return; + auto Num = it->second; + S->AllPtrNumbering.erase(it); + if (To) { + S->AllPtrNumbering[To] = Num; + } +} + +bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { bool ChangesMade = false; // We create one alloca for all the jlcall frames that haven't been processed // yet. LLVM would merge them anyway later, so might as well save it a bit @@ -1561,6 +1660,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { auto *ASCI = new AddrSpaceCastInst(obj, T_pjlvalue, "", CI); ASCI->takeName(CI); CI->replaceAllUsesWith(ASCI); + UpdatePtrNumbering(CI, ASCI, S); } else if (alloc_obj_func && callee == alloc_obj_func) { assert(CI->getNumArgOperands() == 3); auto sz = (size_t)cast(CI->getArgOperand(1))->getZExtValue(); @@ -1588,6 +1688,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { EmitTagPtr(builder, T_prjlvalue, newI)); store->setMetadata(LLVMContext::MD_tbaa, tbaa_tag); CI->replaceAllUsesWith(newI); + UpdatePtrNumbering(CI, newI, S); } else if (typeof_func && callee == typeof_func) { assert(CI->getNumArgOperands() == 1); IRBuilder<> builder(CI); @@ -1598,6 +1699,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { T_prjlvalue); typ->takeName(CI); CI->replaceAllUsesWith(typ); + UpdatePtrNumbering(CI, typ, S); } else if (write_barrier_func && callee == write_barrier_func) { // The replacement for this requires creating new BasicBlocks // which messes up the loop. Queue all of them to be replaced later. @@ -1649,6 +1751,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { #endif NewCall->setDebugLoc(CI->getDebugLoc()); CI->replaceAllUsesWith(NewCall); + UpdatePtrNumbering(CI, NewCall, S); } else if (CI->getNumArgOperands() == CI->getNumOperands()) { /* No operand bundle to lower */ ++it; @@ -1657,19 +1760,25 @@ bool LateLowerGCFrame::CleanupIR(Function &F) { CallInst *NewCall = CallInst::Create(CI, None, CI); NewCall->takeName(CI); CI->replaceAllUsesWith(NewCall); + UpdatePtrNumbering(CI, NewCall, S); } if (!CI->use_empty()) { CI->replaceAllUsesWith(UndefValue::get(CI->getType())); + UpdatePtrNumbering(CI, nullptr, S); } it = CI->eraseFromParent(); ChangesMade = true; } } for (auto CI: write_barriers) { - IRBuilder<> builder(CI); - builder.SetCurrentDebugLocation(CI->getDebugLoc()); auto parent = CI->getArgOperand(0); auto child = CI->getArgOperand(1); + if (parent == child || IsPermRooted(child, S)) { + CI->eraseFromParent(); + continue; + } + IRBuilder<> builder(CI); + builder.SetCurrentDebugLocation(CI->getDebugLoc()); auto parBits = builder.CreateAnd(EmitLoadTag(builder, parent), 3); auto parOldMarked = builder.CreateICmpEQ(parBits, ConstantInt::get(T_size, 3)); auto mayTrigTerm = SplitBlockAndInsertIfThen(parOldMarked, CI, false); @@ -1991,7 +2100,7 @@ bool LateLowerGCFrame::runOnFunction(Function &F) { std::vector Colors = ColorRoots(S); std::map> CallFrames; // = OptimizeCallFrames(S, Ordering); PlaceRootsAndUpdateCalls(Colors, S, CallFrames); - CleanupIR(F); + CleanupIR(F, &S); return true; } From 665aeab129576b3d46cfaecf2ffaa7a8985f272a Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Mon, 30 Oct 2017 15:33:07 -0700 Subject: [PATCH 06/34] Deprecate fill!(A::[Diagonal|AbstractTriangular], x) = fillslots!(A, x) methods. --- NEWS.md | 3 +++ base/deprecated.jl | 4 ++++ base/linalg/bidiag.jl | 4 ---- base/linalg/diagonal.jl | 5 +++++ test/linalg/bidiag.jl | 14 +++++++++++--- test/linalg/triangular.jl | 3 --- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/NEWS.md b/NEWS.md index cdf940755907f..a188d85bf7e7e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -367,6 +367,9 @@ Deprecated or removed Instead, reshape the array or add trailing indices so the dimensionality and number of indices match ([#14770], [#23628]). + * `fill!(A::Diagonal, x)` and `fill!(A::AbstractTriangular, x)` have been deprecated + in favor of `Base.LinAlg.fillslots!(A, x)` ([#24413]). + * Using Bool values directly as indices is now deprecated and will be an error in the future. Convert them to `Int` before indexing if you intend to access index `1` for `true` and `0` for `false`. diff --git a/base/deprecated.jl b/base/deprecated.jl index 9927285ba7d26..4d613246b7cf6 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1886,6 +1886,10 @@ end # After deprecation is removed, enable the @testset "indexing by Bool values" in test/arrayops.jl # Also un-comment the new definition in base/indices.jl +# deprecate odd fill! methods +@deprecate fill!(D::Diagonal, x) fillslots!(D, x) +@deprecate fill!(A::Base.LinAlg.AbstractTriangular, x) fillslots!(A, x) + function diagm(v::BitVector) depwarn(string("diagm(v::BitVector) is deprecated, use diagm(0 => v) or ", "BitMatrix(Diagonal(v)) instead"), :diagm) diff --git a/base/linalg/bidiag.jl b/base/linalg/bidiag.jl index 4a1f05ab222ef..68d1aa076ae09 100644 --- a/base/linalg/bidiag.jl +++ b/base/linalg/bidiag.jl @@ -607,10 +607,6 @@ function fillslots!(A::SpecialArrays, x) return A end -# for historical reasons: -fill!(a::AbstractTriangular, x) = fillslots!(a, x) -fill!(D::Diagonal, x) = fillslots!(D, x) - _small_enough(A::Bidiagonal) = size(A, 1) <= 1 _small_enough(A::Tridiagonal) = size(A, 1) <= 2 _small_enough(A::SymTridiagonal) = size(A, 1) <= 2 diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index 93533a4b02f64..1582371f17a91 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -61,6 +61,11 @@ convert(::Type{Array}, D::Diagonal) = convert(Matrix, D) similar(D::Diagonal, ::Type{T}) where {T} = Diagonal(similar(D.diag, T)) similar(D::Diagonal, ::Type{T}, dims::Union{Dims{1},Dims{2}}) where {T} = spzeros(T, dims...) +Base.zeros(D::Diagonal) = Diagonal(fill!(similar(D.diag), 0)) +Base.zeros(D::Diagonal, ::Type{T}) where {T} = Diagonal(fill!(similar(D, T), 0)) +Base.zeros(D::Diagonal, ::Type{T}, dims::Dims) where {T} = fill!(similar(D, T, dims), 0) +Base.zeros(D::Diagonal, ::Type{T}, dims::Integer...) where {T} = fill!(similar(D, T, dims), 0) + copy!(D1::Diagonal, D2::Diagonal) = (copy!(D1.diag, D2.diag); D1) size(D::Diagonal) = (length(D.diag),length(D.diag)) diff --git a/test/linalg/bidiag.jl b/test/linalg/bidiag.jl index bfd745c286500..052642d314e5a 100644 --- a/test/linalg/bidiag.jl +++ b/test/linalg/bidiag.jl @@ -323,10 +323,10 @@ import Base.LinAlg: fillslots!, UnitLowerTriangular Bidiagonal(randn(3), randn(2), rand([:U,:L])), SymTridiagonal(randn(3), randn(2)), sparse(randn(3,4)), - Diagonal(randn(5)), + # Diagonal(randn(5)), # Diagonal fill! deprecated, see below sparse(rand(3)), - LowerTriangular(randn(3,3)), - UpperTriangular(randn(3,3)) + # LowerTriangular(randn(3,3)), # AbstractTriangular fill! deprecated, see below + # UpperTriangular(randn(3,3)) # AbstractTriangular fill! deprecated, see below ] for A in exotic_arrays fill!(A, 0) @@ -334,6 +334,14 @@ import Base.LinAlg: fillslots!, UnitLowerTriangular @test a == 0 end end + # Diagonal and AbstractTriangular fill! were defined as fillslots!, + # not matching the general behavior of fill!, and so have been deprecated. + # In a future dev cycle, these fill! methods should probably be reintroduced + # with behavior matching that of fill! for other structured matrix types. + # In the interm, equivalently test fillslots! below + @test iszero(fillslots!(Diagonal(fill(1, 3)), 0)) + @test iszero(fillslots!(LowerTriangular(fill(1, 3, 3)), 0)) + @test iszero(fillslots!(UpperTriangular(fill(1, 3, 3)), 0)) end let # fill!(small, x) val = randn() diff --git a/test/linalg/triangular.jl b/test/linalg/triangular.jl index 862fa75f184e3..7c1d7c547e528 100644 --- a/test/linalg/triangular.jl +++ b/test/linalg/triangular.jl @@ -34,9 +34,6 @@ for elty1 in (Float32, Float64, BigFloat, Complex64, Complex128, Complex{BigFloa # full! @test full!(copy(A1)) == A1 - # fill! - @test full!(fill!(copy(A1), 1)) == t1(ones(size(A1)...)) - # similar @test isa(similar(A1), t1) @test eltype(similar(A1)) == elty1 From 328680e1f6a421f28f6e398bec94f745e4b53f9c Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Mon, 30 Oct 2017 22:38:34 -0400 Subject: [PATCH 07/34] Improve allocation optimization with gc_preserve intrinsics Delete them directly so that they don't mess up LLVM optimizations. --- src/llvm-alloc-opt.cpp | 13 +++++++- test/llvmpasses/alloc-opt2.jl | 57 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 test/llvmpasses/alloc-opt2.jl diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 4c754a5bbc90c..cb26f53451a2b 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -606,13 +606,24 @@ void AllocOpt::replaceUsesWith(Instruction *orig_inst, Instruction *new_inst, call->eraseFromParent(); return; } + // Also remove the preserve intrinsics so that it can be better optimized. + if (gc_preserve_begin && gc_preserve_begin == call->getCalledFunction()) { + while (!call->use_empty()) { + auto end = cast(*call->user_begin()); + // gc_preserve_end returns void. + assert(end->use_empty()); + end->eraseFromParent(); + } + call->eraseFromParent(); + return; + } if (auto intrinsic = dyn_cast(call)) { if (Intrinsic::ID ID = intrinsic->getIntrinsicID()) { replaceIntrinsicUseWith(intrinsic, ID, orig_i, new_i); return; } } - // remove from operand bundle or arguments for gc_perserve_begin + // remove from operand bundle Type *orig_t = orig_i->getType(); user->replaceUsesOfWith(orig_i, ConstantPointerNull::get(cast(orig_t))); } diff --git a/test/llvmpasses/alloc-opt2.jl b/test/llvmpasses/alloc-opt2.jl new file mode 100644 index 0000000000000..18f9e780f4cf9 --- /dev/null +++ b/test/llvmpasses/alloc-opt2.jl @@ -0,0 +1,57 @@ +# RUN: julia --startup-file=no %s | opt -load libjulia.so -AllocOpt -S - | FileCheck %s + +isz = sizeof(UInt) == 8 ? "i64" : "i32" + +println(""" +%jl_value_t = type opaque +@tag = external addrspace(10) global %jl_value_t +""") + +# Test that the gc_preserve intrinsics are deleted directly. + +# CHECK-LABEL: @preserve_branches +# CHECK: alloca i64 +# CHECK: call %jl_value_t*** @julia.ptls_states() +# CHECK: L1: +# CHECK-NEXT: call void @llvm.lifetime.start{{.*}}(i64 8, +# CHECK-NEXT: @external_function() +# CHECK-NEXT: br i1 %b2, label %L2, label %L3 + +# CHECK: L2: +# CHECK-NOT: call void @llvm.lifetime.end{{.*}}(i64 8, +# CHECK: @external_function() +# CHECK-NEXT: br label %L3 + +# CHECK: L3: +# CHECK-NEXT: call void @llvm.lifetime.end{{.*}}(i64 8, +println(""" +define void @preserve_branches(i8* %fptr, i1 %b, i1 %b2) { + %ptls = call %jl_value_t*** @julia.ptls_states() + %ptls_i8 = bitcast %jl_value_t*** %ptls to i8* + br i1 %b, label %L1, label %L3 + +L1: + %v = call noalias %jl_value_t addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, $isz 8, %jl_value_t addrspace(10)* @tag) + %tok = call token (...) @llvm.julia.gc_preserve_begin(%jl_value_t addrspace(10)* %v) + call void @external_function() + br i1 %b2, label %L2, label %L3 + +L2: + call void @external_function() + br label %L3 + +L3: + ret void +} +""") +# CHECK-LABEL: } + +println(""" +declare void @external_function() +declare %jl_value_t*** @julia.ptls_states() +declare noalias %jl_value_t addrspace(10)* @julia.gc_alloc_obj(i8*, $isz, %jl_value_t addrspace(10)*) +declare i64 @julia.pointer_from_objref(%jl_value_t addrspace(11)*) +declare void @llvm.memcpy.p11i8.p0i8.i64(i8 addrspace(11)* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) +declare token @llvm.julia.gc_preserve_begin(...) +declare void @llvm.julia.gc_preserve_end(token) +""") From 1638a315b9cae648f72f5a27fb6ad9fafb7762c0 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Tue, 31 Oct 2017 10:05:31 -0400 Subject: [PATCH 08/34] Mark `memcmp` as `readonly`, `nounwind` and `argmemonly` This should allow LLVM to optimize it better --- src/codegen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 63cb4ad10ed85..cb42345fc7deb 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6240,6 +6240,9 @@ static void init_julia_llvm_env(Module *m) memcmp_derived_func = Function::Create(FunctionType::get(T_int32, args_memcmp, false), Function::ExternalLinkage, "memcmp", m); + memcmp_derived_func->addFnAttr(Attribute::ReadOnly); + memcmp_derived_func->addFnAttr(Attribute::NoUnwind); + memcmp_derived_func->addFnAttr(Attribute::ArgMemOnly); add_named_global(memcmp_derived_func, &memcmp); std::vector te_args(0); From 7a54d3df317529307e0ac10be0e69027e944ea4c Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Mon, 30 Oct 2017 16:29:18 -0700 Subject: [PATCH 09/34] Add Diagonal[{T}](S::UniformScaling, n) constructors. --- base/linalg/uniformscaling.jl | 4 ++++ test/linalg/uniformscaling.jl | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/base/linalg/uniformscaling.jl b/base/linalg/uniformscaling.jl index cd0afb23567d7..4060fcc3e533b 100644 --- a/base/linalg/uniformscaling.jl +++ b/base/linalg/uniformscaling.jl @@ -381,3 +381,7 @@ Matrix(s::UniformScaling, dims::Dims{2}) = Matrix{eltype(s)}(s, dims) # convenience variations that accept a single integer to specify dims Matrix{T}(s::UniformScaling, m::Integer) where {T} = Matrix{T}(s, m, m) Matrix(s::UniformScaling, m::Integer) = Matrix(s, m, m) + +## Diagonal construction from UniformScaling +Diagonal{T}(s::UniformScaling, m::Integer) where {T} = Diagonal{T}(fill(T(s.λ), m)) +Diagonal(s::UniformScaling, m::Integer) = Diagonal{eltype(s)}(s, m) diff --git a/test/linalg/uniformscaling.jl b/test/linalg/uniformscaling.jl index 4541b274e5614..c506f7c7ef5e7 100644 --- a/test/linalg/uniformscaling.jl +++ b/test/linalg/uniformscaling.jl @@ -201,6 +201,13 @@ end @test Matrix{Float64}(2I, 3, 3)::Matrix{Float64} == 2*eye(3) end +@testset "Diagonal construction from UniformScaling" begin + @test Diagonal(2I, 3)::Diagonal{Int} == 2*eye(3) + @test Diagonal(2.0I, 3)::Diagonal{Float64} == 2*eye(3) + @test Diagonal{Real}(2I, 3)::Diagonal{Real} == 2*eye(3) + @test Diagonal{Float64}(2I, 3)::Diagonal{Float64} == 2*eye(3) +end + @testset "equality comparison of matrices with UniformScaling" begin # AbstractMatrix methods diagI = Diagonal(fill(1, 3)) From 3fa96bdadfa5cf7f5b4d0d914af50baf7486228f Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Mon, 30 Oct 2017 16:33:28 -0700 Subject: [PATCH 10/34] Deprecate eye(::Type{Diagonal{T}}, m::Integer) method. --- NEWS.md | 3 +++ base/deprecated.jl | 2 ++ base/linalg/diagonal.jl | 2 -- test/linalg/diagonal.jl | 1 - 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/NEWS.md b/NEWS.md index d2e14b873ed5c..cd90627d8f354 100644 --- a/NEWS.md +++ b/NEWS.md @@ -305,6 +305,9 @@ Library improvements * The `crc32c` function for CRC-32c checksums is now exported ([#22274]). + * `eye(::Type{Diagonal{T}}, m::Integer)` has been deprecated in favor of + `Diagonal{T}(I, m)` ([#24413]). + * The output of `versioninfo` is now controlled with keyword arguments ([#21974]). * The function `LibGit2.set_remote_url` now always sets both the fetch and push URLs for a diff --git a/base/deprecated.jl b/base/deprecated.jl index 1582e8bb8df37..c6a6561a3304c 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1786,6 +1786,8 @@ end @deprecate get_creds!(cache::CachedCredentials, credid, default) get!(cache, credid, default) end +@deprecate eye(::Type{Diagonal{T}}, n::Int) where {T} Diagonal{T}(I, n) + export tic, toq, toc function tic() depwarn("tic() is deprecated, use @time, @elapsed, or calls to time_ns() instead.", :tic) diff --git a/base/linalg/diagonal.jl b/base/linalg/diagonal.jl index 93533a4b02f64..a655516456878 100644 --- a/base/linalg/diagonal.jl +++ b/base/linalg/diagonal.jl @@ -341,8 +341,6 @@ function logdet(D::Diagonal{<:Complex}) # make sure branch cut is correct z = sum(log, D.diag) complex(real(z), rem2pi(imag(z), RoundNearest)) end -# identity matrices via eye(Diagonal{type},n) -eye(::Type{Diagonal{T}}, n::Int) where {T} = Diagonal(ones(T,n)) # Matrix functions for f in (:exp, :log, :sqrt, diff --git a/test/linalg/diagonal.jl b/test/linalg/diagonal.jl index d57807c9eaf6c..5319efd7a8742 100644 --- a/test/linalg/diagonal.jl +++ b/test/linalg/diagonal.jl @@ -29,7 +29,6 @@ srand(1) end @testset "Basic properties" begin - @test eye(Diagonal{elty},n) == Diagonal(ones(elty,n)) @test_throws ArgumentError size(D,0) @test typeof(convert(Diagonal{Complex64},D)) <: Diagonal{Complex64} @test typeof(convert(AbstractMatrix{Complex64},D)) <: Diagonal{Complex64} From d9fcc64676fb5d74d914d06b31d8f530629538d5 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Tue, 31 Oct 2017 21:44:42 -0400 Subject: [PATCH 11/34] Fix tag gep for bits union field in mutable type Close #24363 --- src/cgutils.cpp | 7 ++++++- test/core.jl | 11 +++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/cgutils.cpp b/src/cgutils.cpp index ce173e4edc08d..8c310790da851 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -1528,7 +1528,12 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st } else if (jl_is_uniontype(jfty)) { int fsz = jl_field_size(jt, idx); - Value *ptindex = emit_struct_gep(ctx, lt, staddr, byte_offset + fsz - 1); + Value *ptindex; + if (isa(lt)) + ptindex = emit_struct_gep(ctx, lt, staddr, byte_offset + fsz - 1); + else + ptindex = ctx.builder.CreateConstInBoundsGEP1_32( + T_int8, emit_bitcast(ctx, staddr, T_pint8), byte_offset + fsz - 1); Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), ctx.builder.CreateLoad(T_int8, ptindex)); bool isimmutable = strct.isimmutable; if (jt->mutabl) { diff --git a/test/core.jl b/test/core.jl index 23be56778df78..06fe3ee87421f 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5868,3 +5868,14 @@ end macro m22098 end handle_on_m22098 = getfield(@__MODULE__, Symbol("@m22098")) @test isempty(methods(handle_on_m22098)) + +# issue 24363 +mutable struct A24363 + x::Union{Int,Void} +end + +int24363 = A24363(65535) +void24363 = A24363(nothing) +f24363(a) = a.x +@test f24363(int24363) === 65535 +@test f24363(void24363) === nothing From 5b95805fc73abe672a85ef04249cf34378ec9f74 Mon Sep 17 00:00:00 2001 From: lucatrv Date: Wed, 1 Nov 2017 15:45:07 +0100 Subject: [PATCH 12/34] Remove unused function "imagePath" (#24394) --- contrib/julia-config.jl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contrib/julia-config.jl b/contrib/julia-config.jl index 288d29ff32f83..03d3420a6cf3e 100755 --- a/contrib/julia-config.jl +++ b/contrib/julia-config.jl @@ -15,11 +15,6 @@ function shell_escape(str) return "'$str'" end -function imagePath() - opts = Base.JLOptions() - return unsafe_string(opts.image_file) -end - function libDir() return if ccall(:jl_is_debugbuild, Cint, ()) != 0 dirname(abspath(Libdl.dlpath("libjulia-debug"))) From 9b3fbf53c2272e4e63e67b7c45a63aed2cfef073 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Wed, 25 Oct 2017 22:47:47 -0400 Subject: [PATCH 13/34] Improve debug info correctness and printing in disassembler * Mark functions with correct line numbers This is consistent with what Clang does. * Mark functions with correct argument types for non-zero debug level * Do not print extra debug info before disassembling starts --- src/codegen.cpp | 8 ++++---- src/disasm.cpp | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index cb42345fc7deb..ec9374af5510b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5074,11 +5074,11 @@ static std::unique_ptr emit_function( dbgFuncName, // Name f->getName(), // LinkageName topfile, // File - 0, // LineNo + toplineno, // LineNo subrty, // Ty false, // isLocalToUnit true, // isDefinition - 0, // ScopeLine + toplineno, // ScopeLine DIFlagZero, // Flags true, // isOptimized nullptr); // Template Parameters @@ -5512,11 +5512,11 @@ static std::unique_ptr emit_function( std::string(inl_name) + ";", inl_name, new_file, - 0, + inlined_func_lineno, jl_di_func_null_sig, false, true, - 0, + inlined_func_lineno, DIFlagZero, true, nullptr); diff --git a/src/disasm.cpp b/src/disasm.cpp index aa9140d578ff8..0eb8ea11d5bdc 100644 --- a/src/disasm.cpp +++ b/src/disasm.cpp @@ -712,7 +712,25 @@ static void jl_dump_asm_internal( DILineInfoTable di_lineinfo; if (di_ctx) - di_lineinfo = di_ctx->getLineInfoForAddressRange(Fptr+slide, Fsize); + di_lineinfo = di_ctx->getLineInfoForAddressRange(Fptr+slide, Fsize); + if (!di_lineinfo.empty()) { + auto cur_addr = di_lineinfo[0].first; + auto nlineinfo = di_lineinfo.size(); + // filter out line infos that doesn't contain any instructions + unsigned j = 0; + for (unsigned i = 1; i < nlineinfo; i++) { + auto &info = di_lineinfo[i]; + if (info.first != cur_addr) + j++; + cur_addr = info.first; + if (i != j) { + di_lineinfo[j] = std::move(info); + } + } + if (j + 1 < nlineinfo) { + di_lineinfo.resize(j + 1); + } + } // Take two passes: In the first pass we record all branch labels, // in the second we actually perform the output @@ -739,15 +757,15 @@ static void jl_dump_asm_internal( DILineInfoTable::iterator di_lineEnd = di_lineinfo.end(); DILineInfoPrinter dbgctx{';', true}; if (pass != 0) { - if (di_ctx) { + if (di_ctx && di_lineIter != di_lineEnd) { // Set up the line info - if (di_lineIter != di_lineEnd) { + nextLineAddr = di_lineIter->first; + if (nextLineAddr != Fptr + slide) { std::string buf; dbgctx.emit_lineinfo(buf, di_lineIter->second); - Streamer->EmitRawText(buf); - if (di_lineIter->second.Line <= 0) - ++di_lineIter; - nextLineAddr = di_lineIter->first; + if (!buf.empty()) { + Streamer->EmitRawText(buf); + } } } } @@ -770,7 +788,8 @@ static void jl_dump_asm_internal( else { dbgctx.emit_lineinfo(buf, di_lineIter->second); } - Streamer->EmitRawText(buf); + if (!buf.empty()) + Streamer->EmitRawText(buf); nextLineAddr = (++di_lineIter)->first; } } @@ -844,7 +863,9 @@ static void jl_dump_asm_internal( if (pass != 0 && di_ctx) { std::string buf; dbgctx.emit_finish(buf); - Streamer->EmitRawText(buf); + if (!buf.empty()) { + Streamer->EmitRawText(buf); + } } } } From f92817a481a551fd8e58174ce03c3ad576f314bf Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 1 Nov 2017 11:59:25 -0400 Subject: [PATCH 14/34] deprecate unused `initialized` argument to `sort`. (#24424) extracted from #22388 --- base/deprecated.jl | 2 ++ base/sort.jl | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/base/deprecated.jl b/base/deprecated.jl index 1582e8bb8df37..6ec4ca697357d 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1692,6 +1692,8 @@ end @deprecate selectperm partialsortperm @deprecate selectperm! partialsortperm! +# `initialized` keyword arg to `sort` is deprecated in sort.jl + @deprecate promote_noncircular promote false import .Iterators.enumerate diff --git a/base/sort.jl b/base/sort.jl index 9f0f48a3e21e3..246f81dd43535 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -834,7 +834,7 @@ end ## sorting multi-dimensional arrays ## """ - sort(A, dim::Integer; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward, initialized::Bool=false) + sort(A, dim::Integer; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) Sort a multidimensional array `A` along the given dimension. See [`sort!`](@ref) for a description of possible @@ -864,7 +864,10 @@ function sort(A::AbstractArray, dim::Integer; by=identity, rev::Bool=false, order::Ordering=Forward, - initialized::Bool=false) + initialized::Union{Bool,Void}=nothing) + if initialized !== nothing + Base.depwarn("`initialized` keyword argument is deprecated", :sort) + end order = ord(lt,by,rev,order) n = length(indices(A, dim)) if dim != 1 From c8ca0850861dd8b72ccbba27ec5de687cfbd6ff7 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Wed, 25 Oct 2017 22:57:52 -0400 Subject: [PATCH 15/34] Enable FPO again on non-windows or freebsd This was enabled when we were porting to 3.7 for better unwinding. The unwinding should have been improved (thanks to registering JIT functions to libunwind and a new libunwind version) on non-windows a lot that we don't need this anymore. The performance impact of this is likely small but it makes `code_native` for simple functions quite a bit simpler and has the biggest yet effect on the code size in system image (-4%). --- src/codegen.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index ec9374af5510b..3a62acbf67926 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -13,6 +13,9 @@ // also, use ELF because RuntimeDyld COFF X86_64 doesn't seem to work (fails to generate function pointers)? #define FORCE_ELF #endif +#if defined(_OS_WINDOWS_) || defined(_OS_FREEBSD_) +# define JL_DISABLE_FPO +#endif #if defined(_CPU_X86_) #define JL_NEED_FLOATTEMP_VAR 1 #endif @@ -4164,7 +4167,9 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t funcName.str(), M); jl_init_function(cw); cw->setAttributes(sig.attributes); +#ifdef JL_DISABLE_FPO cw->addFnAttr("no-frame-pointer-elim", "true"); +#endif Function *cw_proto = function_proto(cw); jl_codectx_t ctx(jl_LLVMContext); @@ -4343,7 +4348,9 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t GlobalVariable::InternalLinkage, funcName.str(), M); jl_init_function(gf_thunk); gf_thunk->setAttributes(returninfo.decl->getAttributes()); +#ifdef JL_DISABLE_FPO gf_thunk->addFnAttr("no-frame-pointer-elim", "true"); +#endif // build a specsig -> jl_apply_generic converter thunk // this builds a method that calls jl_apply_generic (as a closure over a singleton function pointer), // but which has the signature of a specsig @@ -4570,7 +4577,9 @@ static Function *gen_jlcall_wrapper(jl_method_instance_t *lam, const jl_returnin add_return_attr(w, Attribute::NonNull); w->addFnAttr("thunk"); jl_init_function(w); +#ifdef JL_DISABLE_FPO w->addFnAttr("no-frame-pointer-elim", "true"); +#endif Function::arg_iterator AI = w->arg_begin(); Value *fArg = &*AI++; Value *argArray = &*AI++; @@ -5011,7 +5020,9 @@ static std::unique_ptr emit_function( declarations->specFunctionObject = NULL; } +#ifdef JL_DISABLE_FPO f->addFnAttr("no-frame-pointer-elim", "true"); +#endif if (jlrettype == (jl_value_t*)jl_bottom_type) f->setDoesNotReturn(); #if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) From 55736a0174343ec721798719ad7520a9b8904372 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 1 Nov 2017 15:18:03 -0400 Subject: [PATCH 16/34] add an enum type for jlcall_api (#24426) --- src/anticodegen.c | 2 +- src/codegen.cpp | 36 ++++++++++++++++++------------------ src/debuginfo.cpp | 2 +- src/dump.c | 2 +- src/gf.c | 16 ++++++++-------- src/interpreter.c | 2 +- src/julia.h | 22 ++++++++++++++++------ src/julia_internal.h | 18 +++++++++--------- src/precompile.c | 8 ++++---- src/staticdata.c | 4 ++-- src/threading.c | 2 +- 11 files changed, 62 insertions(+), 52 deletions(-) diff --git a/src/anticodegen.c b/src/anticodegen.c index 8c87d306408f8..c5af15fb43603 100644 --- a/src/anticodegen.c +++ b/src/anticodegen.c @@ -48,7 +48,7 @@ jl_value_t *jl_interpret_call(jl_method_instance_t *lam, jl_value_t **args, uint void jl_generate_fptr(jl_method_instance_t *li) { li->fptr = (jl_fptr_t)&jl_interpret_call; - li->jlcall_api = 4; + li->jlcall_api = JL_API_INTERPRETED; } JL_DLLEXPORT uint32_t jl_get_LLVM_VERSION(void) diff --git a/src/codegen.cpp b/src/codegen.cpp index ec9374af5510b..def4f38d2779e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -252,12 +252,12 @@ int32_t jl_jlcall_api(const char *fname) return 0; StringRef Name(fname); if (Name.startswith("japi3_")) // jlcall abi 3 from JIT - return 3; + return JL_API_WITH_PARAMETERS; assert(Name.startswith("japi1_") || // jlcall abi 1 from JIT Name.startswith("jsys1_") || // jlcall abi 1 from sysimg Name.startswith("jlcall_") || // jlcall abi 1 from JIT wrapping a specsig method Name.startswith("jlsysw_")); // jlcall abi 1 from sysimg wrapping a specsig method - return 1; + return JL_API_GENERIC; } @@ -1079,7 +1079,7 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t decls = li->functionObjectsDecls; bool already_compiled = params->cached && decls.functionObject != NULL; if (!src) { - if ((already_compiled || li->jlcall_api == 2) && + if ((already_compiled || li->jlcall_api == JL_API_CONST) && (li->min_world <= world && li->max_world >= world)) { return decls; } @@ -1098,7 +1098,7 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t // we waited at the lock). if (!jl_is_method(li->def.method)) { src = (jl_code_info_t*)li->inferred; - if (decls.functionObject != NULL || !src || !jl_is_code_info(src) || li->jlcall_api == 2) { + if (decls.functionObject != NULL || !src || !jl_is_code_info(src) || li->jlcall_api == JL_API_CONST) { goto locked_out; } } @@ -1106,7 +1106,7 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t // If the caller didn't provide the source, // try to infer it for ourself, but first, re-check if it's already compiled. assert(li->min_world <= world && li->max_world >= world); - if ((params->cached && decls.functionObject != NULL) || li->jlcall_api == 2) + if ((params->cached && decls.functionObject != NULL) || li->jlcall_api == JL_API_CONST) goto locked_out; // see if it is inferred @@ -1118,7 +1118,7 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t src = jl_type_infer(pli, world, 0); li = *pli; } - if (!src || li->jlcall_api == 2) + if (!src || li->jlcall_api == JL_API_CONST) goto locked_out; } else { @@ -1128,7 +1128,7 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t } else if (params->cached && decls.functionObject != NULL) { // similar to above, but never returns a NULL - // decl (unless compile fails), even if jlcall_api == 2 + // decl (unless compile fails), even if jlcall_api == JL_API_CONST goto locked_out; } else { @@ -1203,7 +1203,7 @@ jl_llvm_functions_t jl_compile_linfo(jl_method_instance_t **pli, jl_code_info_t // and there is something to delete (test this before calling jl_ast_flag_inlineable) li->inferred != jl_nothing && // don't delete inlineable code, unless it is constant - (li->jlcall_api == 2 || !jl_ast_flag_inlineable((jl_array_t*)li->inferred)) && + (li->jlcall_api == JL_API_CONST || !jl_ast_flag_inlineable((jl_array_t*)li->inferred)) && // don't delete code when generating a precompile file !imaging_mode && // don't delete code when it's not actually directly being used @@ -1333,7 +1333,7 @@ jl_generic_fptr_t jl_generate_fptr(jl_method_instance_t *li, const char *F, size return fptr; } fptr.fptr = li->unspecialized_ducttape; - fptr.jlcall_api = 1; + fptr.jlcall_api = JL_API_GENERIC; if (!li->inferred && fptr.fptr) { return fptr; } @@ -1390,7 +1390,7 @@ jl_generic_fptr_t jl_generate_fptr(jl_method_instance_t *li, const char *F, size // don't change fptr as that leads to race conditions // with the (not) simultaneous update to jlcall_api } - else if (li->inferred || fptr.jlcall_api != 1) { + else if (li->inferred || fptr.jlcall_api != JL_API_GENERIC) { li->jlcall_api = fptr.jlcall_api; li->fptr = fptr.fptr; } @@ -1404,7 +1404,7 @@ jl_generic_fptr_t jl_generate_fptr(jl_method_instance_t *li, const char *F, size // with the (not) simultaneous update to jlcall_api } else if (unspec == li) { - if (fptr.jlcall_api == 1) + if (fptr.jlcall_api == JL_API_GENERIC) li->unspecialized_ducttape = fptr.fptr; } else if (unspec->functionObjectsDecls.functionObject == F) { @@ -1562,7 +1562,7 @@ void *jl_get_llvmf_decl(jl_method_instance_t *linfo, size_t world, bool getwrapp // compile this normally jl_llvm_functions_t decls = jl_compile_for_dispatch(&linfo, world); - if (decls.functionObject == NULL && linfo->jlcall_api == 2 && jl_is_method(linfo->def.method)) { + if (decls.functionObject == NULL && linfo->jlcall_api == JL_API_CONST && jl_is_method(linfo->def.method)) { // normally we don't generate native code for these functions, so need an exception here // This leaks a bit of memory to cache native code that we'll never actually need JL_LOCK(&codegen_lock); @@ -3021,13 +3021,13 @@ static jl_cgval_t emit_invoke(jl_codectx_t &ctx, jl_expr_t *ex) jl_method_instance_t *li = (jl_method_instance_t*)lival.constant; assert(jl_is_method_instance(li)); jl_llvm_functions_t decls = jl_compile_linfo(&li, NULL, ctx.world, ctx.params); - if (li->jlcall_api == 2) { + if (li->jlcall_api == JL_API_CONST) { assert(li->inferred_const); return mark_julia_const(li->inferred_const); } if (decls.functionObject) { int jlcall_api = jl_jlcall_api(decls.functionObject); - if (jlcall_api == 1) { + if (jlcall_api == JL_API_GENERIC) { jl_cgval_t result = emit_call_function_object(li, decls, argv, nargs, rt, ctx); if (result.typ == jl_bottom_type) CreateTrap(ctx.builder); @@ -4135,7 +4135,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t if (!lam->inferred) // TODO: this isn't ideal to be unconditionally calling type inference from here src = jl_type_infer(&lam, world, 0); jl_compile_linfo(&lam, src, world, &jl_default_cgparams); - if (lam->jlcall_api != 2) { + if (lam->jlcall_api != JL_API_CONST) { if (lam->functionObjectsDecls.functionObject == NULL || jl_jlcall_api(lam->functionObjectsDecls.functionObject) != 1) { lam = NULL; // TODO: use emit_invoke framework to dispatch these @@ -4287,7 +4287,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t // Create the call bool jlfunc_sret; jl_cgval_t retval; - if (lam && lam->jlcall_api == 2) { + if (lam && lam->jlcall_api == JL_API_CONST) { nargs = 0; // arguments not needed -- TODO: not really true, should emit an age_ok test and jlcall jlfunc_sret = false; retval = mark_julia_const(lam->inferred_const); @@ -5946,7 +5946,7 @@ extern "C" void jl_fptr_to_llvm(jl_fptr_t fptr, jl_method_instance_t *lam, int s funcName << "jlsys_"; // the specsig implementation else if (lam->functionObjectsDecls.specFunctionObject) funcName << "jlsysw_"; // it's a specsig wrapper - else if (lam->jlcall_api == 1) + else if (lam->jlcall_api == JL_API_GENERIC) funcName << "jsys1_"; // it's a jlcall without a specsig const char* unadorned_name = jl_symbol_name(lam->def.method->name); funcName << unadorned_name << "_" << globalUnique++; @@ -5959,7 +5959,7 @@ extern "C" void jl_fptr_to_llvm(jl_fptr_t fptr, jl_method_instance_t *lam, int s else { assert(lam->fptr == NULL); lam->fptr = fptr; - if (lam->jlcall_api == 1) { + if (lam->jlcall_api == JL_API_GENERIC) { if (lam->functionObjectsDecls.functionObject == NULL) { lam->functionObjectsDecls.functionObject = strdup(f->getName().str().c_str()); } diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 82f3cbf9e28ff..970b50e595494 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -384,7 +384,7 @@ class JuliaJITEventListener: public JITEventListener const char *F = linfo->functionObjectsDecls.functionObject; if (!linfo->fptr && F && sName.equals(F)) { int jlcall_api = jl_jlcall_api(F); - if (linfo->inferred || jlcall_api != 1) { + if (linfo->inferred || jlcall_api != JL_API_GENERIC) { linfo->jlcall_api = jlcall_api; linfo->fptr = (jl_fptr_t)(uintptr_t)Addr; } diff --git a/src/dump.c b/src/dump.c index 4b24da41e7131..d64b39ddda255 100644 --- a/src/dump.c +++ b/src/dump.c @@ -720,7 +720,7 @@ static void jl_serialize_value_(jl_serializer_state *s, jl_value_t *v, int as_li backedges = NULL; } jl_serialize_value(s, (jl_value_t*)backedges); - write_uint8(s->s, li->jlcall_api == 2 ? 2 : 0); + write_uint8(s->s, li->jlcall_api == JL_API_CONST ? JL_API_CONST : 0); } else if (jl_typeis(v, jl_module_type)) { jl_serialize_module(s, (jl_module_t*)v); diff --git a/src/gf.c b/src/gf.c index 73cc18b47615f..9b8d53364e3cc 100644 --- a/src/gf.c +++ b/src/gf.c @@ -208,7 +208,7 @@ void jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_t fptr) } jl_method_instance_t *li = jl_new_method_instance_uninit(); li->fptr = fptr; - li->jlcall_api = 1; + li->jlcall_api = JL_API_GENERIC; li->specTypes = (jl_value_t*)jl_anytuple_type; li->min_world = 1; li->max_world = ~(size_t)0; @@ -412,7 +412,7 @@ JL_DLLEXPORT jl_method_instance_t* jl_set_method_inferred( jl_gc_wb(li, inferred); if (const_flags & 1) { assert(const_flags & 2); - li->jlcall_api = 2; + li->jlcall_api = JL_API_CONST; } if (const_flags & 2) { li->inferred_const = inferred_const; @@ -1653,14 +1653,14 @@ JL_DLLEXPORT jl_value_t *jl_matching_methods(jl_tupletype_t *types, int lim, int jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t **pli, size_t world) { jl_method_instance_t *li = *pli; - if (li->jlcall_api == 2) + if (li->jlcall_api == JL_API_CONST) return li->functionObjectsDecls; if (jl_options.compile_enabled == JL_OPTIONS_COMPILE_OFF || jl_options.compile_enabled == JL_OPTIONS_COMPILE_MIN) { // copy fptr from the template method definition jl_method_t *def = li->def.method; if (jl_is_method(def) && def->unspecialized) { - if (def->unspecialized->jlcall_api == 2) { + if (def->unspecialized->jlcall_api == JL_API_CONST) { li->functionObjectsDecls.functionObject = NULL; li->functionObjectsDecls.specFunctionObject = NULL; li->inferred = def->unspecialized->inferred; @@ -1668,7 +1668,7 @@ jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t **pli, size_t w li->inferred_const = def->unspecialized->inferred_const; if (li->inferred_const) jl_gc_wb(li, li->inferred_const); - li->jlcall_api = 2; + li->jlcall_api = JL_API_CONST; return li->functionObjectsDecls; } if (def->unspecialized->fptr) { @@ -1686,7 +1686,7 @@ jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t **pli, size_t w } } jl_llvm_functions_t decls = li->functionObjectsDecls; - if (decls.functionObject != NULL || li->jlcall_api == 2) + if (decls.functionObject != NULL || li->jlcall_api == JL_API_CONST) return decls; jl_code_info_t *src = NULL; @@ -1699,7 +1699,7 @@ jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t **pli, size_t w } // check again, because jl_type_infer may have changed li or compiled it decls = li->functionObjectsDecls; - if (decls.functionObject != NULL || li->jlcall_api == 2) + if (decls.functionObject != NULL || li->jlcall_api == JL_API_CONST) return decls; return jl_compile_linfo(&li, src, world, &jl_default_cgparams); } @@ -1764,7 +1764,7 @@ JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) jl_code_info_t *src = NULL; if (!jl_is_rettype_inferred(li)) src = jl_type_infer(&li, world, 0); - if (li->jlcall_api != 2) { + if (li->jlcall_api != JL_API_CONST) { if (jl_options.outputo || jl_options.outputbc || jl_options.outputunoptbc) { // If we are saving LLVM or native code, generate the LLVM IR so that it'll // be included in the saved LLVM module. diff --git a/src/interpreter.c b/src/interpreter.c index 8c2e1d0e521b0..0875044c5cfd5 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -636,7 +636,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, int start, jl_value_t *jl_interpret_call(jl_method_instance_t *lam, jl_value_t **args, uint32_t nargs) { - if (lam->jlcall_api == 2) + if (lam->jlcall_api == JL_API_CONST) return lam->inferred_const; jl_code_info_t *src = (jl_code_info_t*)lam->inferred; if (!src || (jl_value_t*)src == jl_nothing) { diff --git a/src/julia.h b/src/julia.h index 32a7d2a62f4b8..17583038ee7b1 100644 --- a/src/julia.h +++ b/src/julia.h @@ -191,11 +191,21 @@ union jl_typemap_t { struct _jl_value_t *unknown; // nothing }; +// calling conventions for externally-callable julia entry points. +// this is used to set jlcall_api fields +typedef enum { + JL_API_NOT_SET = 0, + JL_API_GENERIC = 1, // (function, args ptr, arg count) + JL_API_CONST = 2, // result is a constant value, no function pointer + JL_API_WITH_PARAMETERS = 3, // (svec of static parameter values, function, args ptr, arg count) + JL_API_INTERPRETED = 4, // jl_interpret_call(method_instance, func and args ptr, arg count + 1, svec of sparam_vals) +} jl_callingconv_t; + // "jlcall" calling convention signatures. // This defines the default ABI used by compiled julia functions. -typedef jl_value_t *(*jl_fptr_t)(jl_value_t*, jl_value_t**, uint32_t); -typedef jl_value_t *(*jl_fptr_sparam_t)(jl_svec_t*, jl_value_t*, jl_value_t**, uint32_t); -typedef jl_value_t *(*jl_fptr_linfo_t)(struct _jl_method_instance_t*, jl_value_t**, uint32_t, jl_svec_t*); +typedef jl_value_t *(*jl_fptr_t)(jl_value_t*, jl_value_t**, uint32_t); // JL_API_GENERIC +typedef jl_value_t *(*jl_fptr_sparam_t)(jl_svec_t*, jl_value_t*, jl_value_t**, uint32_t); // JL_API_WITH_PARAMETERS +typedef jl_value_t *(*jl_fptr_linfo_t)(struct _jl_method_instance_t*, jl_value_t**, uint32_t, jl_svec_t*); // JL_API_INTERPRETED JL_EXTENSION typedef struct { union { @@ -280,15 +290,15 @@ typedef struct _jl_method_instance_t { jl_value_t *rettype; // return type for fptr jl_svec_t *sparam_vals; // static parameter values, indexed by def.method->sparam_syms jl_array_t *backedges; - jl_value_t *inferred; // inferred jl_code_info_t, or value of the function if jlcall_api == 2, or null + jl_value_t *inferred; // inferred jl_code_info_t, or value of the function if jlcall_api == JL_API_CONST, or null jl_value_t *inferred_const; // inferred constant return value, or null size_t min_world; size_t max_world; uint8_t inInference; // flags to tell if inference is running on this function - uint8_t jlcall_api; // the c-abi for fptr; 0 = jl_fptr_t, 1 = jl_fptr_sparam_t, 2 = constval + uint8_t jlcall_api; uint8_t compile_traced; // if set will notify callback if this linfo is compiled jl_fptr_t fptr; // jlcall entry point with api specified by jlcall_api - jl_fptr_t unspecialized_ducttape; // if template can't be compiled due to intrinsics, an un-inferred fptr may get stored here, jlcall_api = 1 + jl_fptr_t unspecialized_ducttape; // if template can't be compiled due to intrinsics, an un-inferred fptr may get stored here, jlcall_api = JL_API_GENERIC // names of declarations in the JIT, // suitable for referencing in LLVM IR diff --git a/src/julia_internal.h b/src/julia_internal.h index afb14cd16f190..1b7ec23858ff1 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -322,7 +322,7 @@ jl_code_info_t *jl_new_code_info_from_ast(jl_expr_t *ast); STATIC_INLINE jl_value_t *jl_compile_method_internal(jl_generic_fptr_t *fptr, jl_method_instance_t *meth) { - if (meth->jlcall_api == 2) + if (meth->jlcall_api == JL_API_CONST) return jl_assume(meth->inferred_const); fptr->fptr = meth->fptr; fptr->jlcall_api = meth->jlcall_api; @@ -332,12 +332,12 @@ STATIC_INLINE jl_value_t *jl_compile_method_internal(jl_generic_fptr_t *fptr, const char *F = meth->functionObjectsDecls.functionObject; if (!F) // ask codegen to try to turn it into llvm code F = jl_compile_for_dispatch(&meth, world).functionObject; - if (meth->jlcall_api == 2) + if (meth->jlcall_api == JL_API_CONST) return jl_assume(meth->inferred_const); // if it hasn't been inferred, try using the unspecialized meth cache instead if (!meth->inferred) { fptr->fptr = meth->unspecialized_ducttape; - fptr->jlcall_api = 1; + fptr->jlcall_api = JL_API_GENERIC; if (!fptr->fptr) { if (jl_is_method(meth->def.method) && meth->def.method->unspecialized) { fptr->fptr = meth->def.method->unspecialized->fptr; @@ -351,7 +351,7 @@ STATIC_INLINE jl_value_t *jl_compile_method_internal(jl_generic_fptr_t *fptr, if (!fptr->fptr || fptr->jlcall_api == 0) { // ask codegen to make the fptr *fptr = jl_generate_fptr(meth, F, world); - if (fptr->jlcall_api == 2) + if (fptr->jlcall_api == JL_API_CONST) return jl_assume(meth->inferred_const); } } @@ -362,13 +362,13 @@ STATIC_INLINE jl_value_t *jl_call_fptr_internal(const jl_generic_fptr_t *fptr, jl_method_instance_t *meth, jl_value_t **args, uint32_t nargs) { - if (fptr->jlcall_api == 1) + if (fptr->jlcall_api == JL_API_GENERIC) return fptr->fptr1(args[0], &args[1], nargs-1); - else if (fptr->jlcall_api == 2) + else if (fptr->jlcall_api == JL_API_CONST) return meth->inferred; - else if (fptr->jlcall_api == 3) + else if (fptr->jlcall_api == JL_API_WITH_PARAMETERS) return fptr->fptr3(meth->sparam_vals, args[0], &args[1], nargs-1); - else if (fptr->jlcall_api == 4) + else if (fptr->jlcall_api == JL_API_INTERPRETED) return fptr->fptr4(meth, &args[0], nargs, meth->sparam_vals); else abort(); @@ -381,7 +381,7 @@ STATIC_INLINE jl_value_t *jl_call_method_internal(jl_method_instance_t *meth, jl jl_value_t *v = jl_compile_method_internal(&fptr, meth); if (v) return v; - (void)jl_assume(fptr.jlcall_api != 2); + (void)jl_assume(fptr.jlcall_api != JL_API_CONST); return jl_call_fptr_internal(&fptr, meth, args, nargs); } diff --git a/src/precompile.c b/src/precompile.c index 1a200df271f2b..0a806595c1d2c 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -243,7 +243,7 @@ static void _compile_all_deq(jl_array_t *found) jl_gc_wb(m, linfo); } - if (linfo->jlcall_api == 2) + if (linfo->jlcall_api == JL_API_CONST) continue; src = m->source; // TODO: the `unspecialized` field is not yet world-aware, so we can't store @@ -251,7 +251,7 @@ static void _compile_all_deq(jl_array_t *found) //src = jl_type_infer(&linfo, jl_world_counter, 1); //m->unspecialized = linfo; //jl_gc_wb(m, linfo); - //if (linfo->jlcall_api == 2) + //if (linfo->jlcall_api == JL_API_CONST) // continue; // first try to create leaf signatures from the signature declaration and compile those @@ -272,7 +272,7 @@ static int compile_all_enq__(jl_typemap_entry_t *ml, void *env) if (m->source && (!m->unspecialized || (m->unspecialized->functionObjectsDecls.functionObject == NULL && - m->unspecialized->jlcall_api != 2 && + m->unspecialized->jlcall_api != JL_API_CONST && m->unspecialized->fptr == NULL))) { // found a lambda that still needs to be compiled jl_array_ptr_1d_push(found, (jl_value_t*)ml); @@ -309,7 +309,7 @@ static int precompile_enq_specialization_(jl_typemap_entry_t *l, void *closure) { if (jl_is_method_instance(l->func.value) && l->func.linfo->functionObjectsDecls.functionObject == NULL && - l->func.linfo->jlcall_api != 2) + l->func.linfo->jlcall_api != JL_API_CONST) jl_array_ptr_1d_push((jl_array_t*)closure, (jl_value_t*)l->sig); return 1; } diff --git a/src/staticdata.c b/src/staticdata.c index a5e7b75f73f66..797ab89ac6895 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -648,7 +648,7 @@ static void jl_write_values(jl_serializer_state *s) newm->unspecialized_ducttape = NULL; if (jl_is_method(m->def.method)) { uintptr_t fptr_id = jl_fptr_id((void*)(uintptr_t)m->fptr); - if (m->jlcall_api == 2) { + if (m->jlcall_api == JL_API_CONST) { } else if (fptr_id >= 2) { //write_int8(s->s, -li->jlcall_api); @@ -950,7 +950,7 @@ static void jl_update_all_fptrs(jl_serializer_state *s) } else { uintptr_t base = (uintptr_t)fvars.base; - assert(jl_is_method(li->def.method) && li->jlcall_api && li->jlcall_api != 2); + assert(jl_is_method(li->def.method) && li->jlcall_api && li->jlcall_api != JL_API_CONST); linfos[i] = li; int32_t offset = fvars.offsets[i]; for (; clone_idx < fvars.nclones; clone_idx++) { diff --git a/src/threading.c b/src/threading.c index 8dbe9066b9224..6ba8acf0524d7 100644 --- a/src/threading.c +++ b/src/threading.c @@ -307,7 +307,7 @@ static jl_value_t *ti_run_fun(const jl_generic_fptr_t *fptr, jl_method_instance_ { jl_ptls_t ptls = jl_get_ptls_states(); JL_TRY { - (void)jl_assume(fptr->jlcall_api != 2); + (void)jl_assume(fptr->jlcall_api != JL_API_CONST); jl_call_fptr_internal(fptr, mfunc, args, nargs); } JL_CATCH { From 4b0626626cbfd5afcf01da3ebda0c3c684141763 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 1 Nov 2017 17:30:51 -0400 Subject: [PATCH 17/34] make default value of `rev` keyword argument type stable (#24422) this lets us remove the method overwrite during sysimg build --- base/ordering.jl | 14 +++++++++----- base/sort.jl | 17 ++++++++--------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/base/ordering.jl b/base/ordering.jl index 52d065b4d36ad..7d3f50f1baac4 100644 --- a/base/ordering.jl +++ b/base/ordering.jl @@ -64,12 +64,16 @@ ordtype(o::Perm, vs::AbstractArray) = ordtype(o.order, o.data) ordtype(o::By, vs::AbstractArray) = try typeof(o.by(vs[1])) catch; Any end ordtype(o::Ordering, vs::AbstractArray) = eltype(vs) +_ord(lt::typeof(isless), by::typeof(identity), order::Ordering) = order +_ord(lt::typeof(isless), by, order::Ordering) = By(by) +_ord(lt, by::typeof(identity), order::Ordering) = Lt(lt) +_ord(lt, by, order::Ordering) = Lt((x,y)->lt(by(x),by(y))) + +ord(lt, by, rev::Void, order::Ordering=Forward) = _ord(lt, by, order) + function ord(lt, by, rev::Bool, order::Ordering=Forward) - o = (lt===isless) & (by===identity) ? order : - (lt===isless) & (by!==identity) ? By(by) : - (lt!==isless) & (by===identity) ? Lt(lt) : - Lt((x,y)->lt(by(x),by(y))) - rev ? ReverseOrdering(o) : o + o = _ord(lt, by, order) + return rev ? ReverseOrdering(o) : o end end diff --git a/base/sort.jl b/base/sort.jl index 246f81dd43535..842e9b1ee476f 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -79,7 +79,7 @@ true ``` """ issorted(itr; - lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = + lt=isless, by=identity, rev::Union{Bool,Void}=nothing, order::Ordering=Forward) = issorted(itr, ord(lt,by,rev,order)) function partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) @@ -140,7 +140,7 @@ julia> a ``` """ partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}; - lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = + lt=isless, by=identity, rev::Union{Bool,Void}=nothing, order::Ordering=Forward) = partialsort!(v, k, ord(lt,by,rev,order)) """ @@ -272,9 +272,8 @@ for s in [:searchsortedfirst, :searchsortedlast, :searchsorted] @eval begin $s(v::AbstractVector, x, o::Ordering) = (inds = indices(v, 1); $s(v,x,first(inds),last(inds),o)) $s(v::AbstractVector, x; - lt=isless, by=identity, rev::Bool=false, order::Ordering=Forward) = + lt=isless, by=identity, rev::Union{Bool,Void}=nothing, order::Ordering=Forward) = $s(v,x,ord(lt,by,rev,order)) - $s(v::AbstractVector, x) = $s(v, x, Forward) end end @@ -604,7 +603,7 @@ function sort!(v::AbstractVector; alg::Algorithm=defalg(v), lt=isless, by=identity, - rev::Bool=false, + rev::Union{Bool,Void}=nothing, order::Ordering=Forward) ordr = ord(lt,by,rev,order) if ordr === Forward && isa(v,Vector) && eltype(v)<:Integer @@ -695,7 +694,7 @@ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, k::Union{Int, OrdinalRange}; lt::Function=isless, by::Function=identity, - rev::Bool=false, + rev::Union{Bool,Void}=nothing, order::Ordering=Forward, initialized::Bool=false) if !initialized @@ -746,7 +745,7 @@ function sortperm(v::AbstractVector; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, - rev::Bool=false, + rev::Union{Bool,Void}=nothing, order::Ordering=Forward) ordr = ord(lt,by,rev,order) if ordr === Forward && isa(v,Vector) && eltype(v)<:Integer @@ -795,7 +794,7 @@ function sortperm!(x::AbstractVector{<:Integer}, v::AbstractVector; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, - rev::Bool=false, + rev::Union{Bool,Void}=nothing, order::Ordering=Forward, initialized::Bool=false) if indices(x,1) != indices(v,1) @@ -862,7 +861,7 @@ function sort(A::AbstractArray, dim::Integer; alg::Algorithm=DEFAULT_UNSTABLE, lt=isless, by=identity, - rev::Bool=false, + rev::Union{Bool,Void}=nothing, order::Ordering=Forward, initialized::Union{Bool,Void}=nothing) if initialized !== nothing From 37d47ba006b7f185e752bbdcd483a156151b1a15 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Wed, 1 Nov 2017 22:37:24 -0400 Subject: [PATCH 18/34] implement named tuples (#22194) Based on #16580, also much work done by quinnj. `(a=1, ...)` syntax is implemented, and `(; ...)` syntax is implemented but not yet enabled. --- NEWS.md | 3 + base/boot.jl | 2 +- base/inference.jl | 17 ++- base/namedtuple.jl | 215 ++++++++++++++++++++++++++++++++++++ base/reflection.jl | 20 +++- base/sysimg.jl | 5 +- doc/src/manual/functions.md | 50 ++++++++- doc/src/manual/types.md | 25 +++++ src/ast.scm | 1 + src/builtins.c | 1 + src/datatype.c | 3 +- src/dump.c | 9 +- src/gf.c | 2 + src/interpreter.c | 2 +- src/jltypes.c | 77 ++++++++++--- src/julia-syntax.scm | 95 ++++++++++++++-- src/julia.h | 14 ++- src/rtutils.c | 6 +- src/staticdata.c | 2 + test/ambiguous.jl | 1 + test/choosetests.jl | 2 +- test/inference.jl | 16 +++ test/namedtuple.jl | 191 ++++++++++++++++++++++++++++++++ 23 files changed, 715 insertions(+), 44 deletions(-) create mode 100644 base/namedtuple.jl create mode 100644 test/namedtuple.jl diff --git a/NEWS.md b/NEWS.md index 4d32e6497cdc9..3213bc23c6089 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,6 +11,9 @@ New language features a function argument name, the argument is unpacked into local variables `x` and `y` as in the assignment `(x, y) = arg` ([#6614]). + * Named tuples, with the syntax `(a=1, b=2)`. These behave very similarly to tuples, + except components can also be accessed by name using dot syntax `t.a` ([#22194]). + * Custom infix operators can now be defined by appending Unicode combining marks, primes, and sub/superscripts to other operators. For example, `+̂ₐ″` is parsed as an infix operator with the same diff --git a/base/boot.jl b/base/boot.jl index 7d1163f24d32d..ae4d02e553d09 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -120,7 +120,7 @@ export # key types Any, DataType, Vararg, ANY, NTuple, Tuple, Type, UnionAll, TypeName, TypeVar, Union, Void, - SimpleVector, AbstractArray, DenseArray, + SimpleVector, AbstractArray, DenseArray, NamedTuple, # special objects Function, CodeInfo, Method, MethodTable, TypeMapEntry, TypeMapLevel, Module, Symbol, Task, Array, WeakRef, VecElement, diff --git a/base/inference.jl b/base/inference.jl index 53585a3288371..865cd4a63d5bc 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -493,6 +493,8 @@ end const _Type_name = Type.body.name isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _Type_name +const _NamedTuple_name = NamedTuple.body.body.name + # true if Type is inlineable as constant (is a singleton) function isconstType(@nospecialize t) isType(t) || return false @@ -734,6 +736,10 @@ function isdefined_tfunc(args...) end if 1 <= idx <= a1.ninitialized return Const(true) + elseif a1.name === _NamedTuple_name + if isleaftype(a1) + return Const(false) + end elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) return Const(false) elseif !isvatuple(a1) && isbits(fieldtype(a1, idx)) @@ -771,7 +777,9 @@ add_tfunc(nfields, 1, 1, # TODO: remove with deprecation in builtins.c for nfields(::Type) isleaftype(x.parameters[1]) && return Const(old_nfields(x.parameters[1])) elseif isa(x,DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) && x !== DataType - return Const(length(x.types)) + if !(x.name === _NamedTuple_name && !isleaftype(x)) + return Const(length(x.types)) + end end return Int end, 0) @@ -1333,6 +1341,10 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) end return Any end + if s.name === _NamedTuple_name && !isleaftype(s) + # TODO: better approximate inference + return Any + end if isempty(s.types) return Bottom end @@ -1416,6 +1428,9 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) if !isa(u,DataType) || u.abstract return Type end + if u.name === _NamedTuple_name && !isleaftype(u) + return Type + end ftypes = u.types if isempty(ftypes) return Bottom diff --git a/base/namedtuple.jl b/base/namedtuple.jl new file mode 100644 index 0000000000000..4f224bf47afcf --- /dev/null +++ b/base/namedtuple.jl @@ -0,0 +1,215 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + NamedTuple{names,T}(args::Tuple) + +Construct a named tuple with the given `names` (a tuple of Symbols) and field types `T` +(a `Tuple` type) from a tuple of values. +""" +function NamedTuple{names,T}(args::Tuple) where {names, T <: Tuple} + if length(args) == length(names) + if @generated + N = length(names) + types = T.parameters + Expr(:new, :(NamedTuple{names,T}), Any[ :(convert($(types[i]), args[$i])) for i in 1:N ]...) + else + N = length(names) + NT = NamedTuple{names,T} + types = T.parameters + fields = Any[ convert(types[i], args[i]) for i = 1:N ] + ccall(:jl_new_structv, Any, (Any, Ptr{Void}, UInt32), NT, fields, N)::NT + end + else + throw(ArgumentError("Wrong number of arguments to named tuple constructor.")) + end +end + +""" + NamedTuple{names}(args::Tuple) + +Construct a named tuple with the given `names` (a tuple of Symbols) from a tuple of +values. +""" +function NamedTuple{names}(args::Tuple) where {names} + NamedTuple{names,typeof(args)}(args) +end + +""" + NamedTuple{names}(nt::NamedTuple) + +Construct a named tuple by selecting fields in `names` (a tuple of Symbols) from +another named tuple. +""" +function NamedTuple{names}(nt::NamedTuple) where {names} + if @generated + types = Tuple{(fieldtype(nt, n) for n in names)...} + Expr(:new, :(NamedTuple{names, $types}), Any[ :(getfield(nt, $(QuoteNode(n)))) for n in names ]...) + else + types = Tuple{(fieldtype(typeof(nt), n) for n in names)...} + NamedTuple{names, types}(Tuple(getfield(nt, n) for n in names)) + end +end + +NamedTuple() = NamedTuple{(),Tuple{}}(()) + +length(t::NamedTuple) = nfields(t) +start(t::NamedTuple) = 1 +done(t::NamedTuple, iter) = iter > nfields(t) +next(t::NamedTuple, iter) = (getfield(t, iter), iter + 1) +endof(t::NamedTuple) = nfields(t) +getindex(t::NamedTuple, i::Int) = getfield(t, i) +getindex(t::NamedTuple, i::Symbol) = getfield(t, i) +indexed_next(t::NamedTuple, i::Int, state) = (getfield(t, i), i+1) + +convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names,T}) where {names,T} = nt +convert(::Type{NamedTuple{names}}, nt::NamedTuple{names}) where {names} = nt + +function convert(::Type{NamedTuple{names,T}}, nt::NamedTuple{names}) where {names,T} + NamedTuple{names,T}(T(nt)) +end + +function show(io::IO, t::NamedTuple) + n = nfields(t) + for i = 1:n + # if field types aren't concrete, show full type + if typeof(getfield(t, i)) !== fieldtype(typeof(t), i) + show(io, typeof(t)) + print(io, "(") + show(io, Tuple(t)) + print(io, ")") + return + end + end + if n == 0 + print(io, "NamedTuple()") + else + print(io, "(") + for i = 1:n + print(io, fieldname(typeof(t),i), " = "); show(io, getfield(t, i)) + if n == 1 + print(io, ",") + elseif i < n + print(io, ", ") + end + end + print(io, ")") + end +end + +eltype(::Type{NamedTuple{names,T}}) where {names,T} = eltype(T) + +==(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = Tuple(a) == Tuple(b) +==(a::NamedTuple, b::NamedTuple) = false + +isequal(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isequal(Tuple(a), Tuple(b)) +isequal(a::NamedTuple, b::NamedTuple) = false + +_nt_names(::NamedTuple{names}) where {names} = names +_nt_names(::Type{T}) where {names,T<:NamedTuple{names}} = names + +hash(x::NamedTuple, h::UInt) = xor(object_id(_nt_names(x)), hash(Tuple(x), h)) + +isless(a::NamedTuple{n}, b::NamedTuple{n}) where {n} = isless(Tuple(a), Tuple(b)) +# TODO: case where one argument's names are a prefix of the other's + +same_names(::NamedTuple{names}...) where {names} = true +same_names(::NamedTuple...) = false + +function map(f, nt::NamedTuple{names}, nts::NamedTuple...) where names + if !same_names(nt, nts...) + throw(ArgumentError("Named tuple names do not match.")) + end + # this method makes sure we don't define a map(f) method + NT = NamedTuple{names} + if @generated + N = length(names) + M = length(nts) + args = Expr[:(f($(Expr[:(getfield(nt, $j)), (:(getfield(nts[$i], $j)) for i = 1:M)...]...))) for j = 1:N] + :( NT(($(args...),)) ) + else + NT(map(f, map(Tuple, (nt, nts...))...)) + end +end + +# a version of `in` for the older world these generated functions run in +@pure function sym_in(x::Symbol, itr::Tuple{Vararg{Symbol}}) + for y in itr + y === x && return true + end + return false +end + +@pure function merge_names(an::Tuple{Vararg{Symbol}}, bn::Tuple{Vararg{Symbol}}) + names = Symbol[an...] + for n in bn + if !sym_in(n, an) + push!(names, n) + end + end + (names...,) +end + +@pure function merge_types(names::Tuple{Vararg{Symbol}}, a::Type{<:NamedTuple}, b::Type{<:NamedTuple}) + bn = _nt_names(b) + Tuple{Any[ fieldtype(sym_in(n, bn) ? b : a, n) for n in names ]...} +end + +""" + merge(a::NamedTuple, b::NamedTuple) + +Construct a new named tuple by merging two existing ones. +The order of fields in `a` is preserved, but values are taken from matching +fields in `b`. Fields present only in `b` are appended at the end. + +```jldoctest +julia> merge((a=1, b=2, c=3), (b=4, d=5)) +(a = 1, b = 4, c = 3, d = 5) +``` +""" +function merge(a::NamedTuple{an}, b::NamedTuple{bn}) where {an, bn} + if @generated + names = merge_names(an, bn) + types = merge_types(names, a, b) + vals = Any[ :(getfield($(sym_in(n, bn) ? :b : :a), $(QuoteNode(n)))) for n in names ] + :( NamedTuple{$names,$types}(($(vals...),)) ) + else + names = merge_names(an, bn) + types = merge_types(names, typeof(a), typeof(b)) + NamedTuple{names,types}(map(n->getfield(sym_in(n, bn) ? b : a, n), names)) + end +end + +merge(a::NamedTuple{()}, b::NamedTuple) = b + +""" + merge(a::NamedTuple, iterable) + +Interpret an iterable of key-value pairs as a named tuple, and perform a merge. + +```jldoctest +julia> merge((a=1, b=2, c=3), [:b=>4, :d=>5]) +(a = 1, b = 4, c = 3, d = 5) +``` +""" +function merge(a::NamedTuple, itr) + names = Symbol[] + vals = Any[] + inds = ObjectIdDict() + for (k,v) in itr + oldind = get(inds, k, 0) + if oldind > 0 + vals[oldind] = v + else + push!(names, k) + push!(vals, v) + inds[k] = length(names) + end + end + merge(a, NamedTuple{(names...,)}((vals...,))) +end + +keys(nt::NamedTuple{names}) where {names} = names +values(nt::NamedTuple) = Tuple(nt) +haskey(nt::NamedTuple, key::Union{Integer, Symbol}) = isdefined(nt, key) +get(nt::NamedTuple, key::Union{Integer, Symbol}, default) = haskey(nt, key) ? getfield(nt, key) : default +get(f::Callable, nt::NamedTuple, key::Union{Integer, Symbol}) = haskey(nt, key) ? getfield(nt, key) : f() diff --git a/base/reflection.jl b/base/reflection.jl index 27a24eb540fd0..b7cf6c4ffbf55 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -127,12 +127,14 @@ julia> fieldname(SparseMatrixCSC, 5) ``` """ function fieldname(t::DataType, i::Integer) - n_fields = length(t.name.names) + names = isdefined(t, :names) ? t.names : t.name.names + n_fields = length(names) field_label = n_fields == 1 ? "field" : "fields" i > n_fields && throw(ArgumentError("Cannot access field $i since type $t only has $n_fields $field_label.")) i < 1 && throw(ArgumentError("Field numbers must be positive integers. $i is invalid.")) - return t.name.names[i]::Symbol + return names[i]::Symbol end + fieldname(t::UnionAll, i::Integer) = fieldname(unwrap_unionall(t), i) fieldname(t::Type{<:Tuple}, i::Integer) = i < 1 || i > fieldcount(t) ? throw(BoundsError(t, i)) : Int(i) @@ -481,7 +483,19 @@ function fieldcount(@nospecialize t) if !(t isa DataType) throw(TypeError(:fieldcount, "", Type, t)) end - if t.abstract || (t.name === Tuple.name && isvatuple(t)) + if t.name === NamedTuple.body.body.name + names, types = t.parameters + if names isa Tuple + return length(names) + end + if types isa DataType && types <: Tuple + return fieldcount(types) + end + abstr = true + else + abstr = t.abstract || (t.name === Tuple.name && isvatuple(t)) + end + if abstr error("type does not have a definite number of fields") end return length(t.types) diff --git a/base/sysimg.jl b/base/sysimg.jl index a0ec603619702..0a3963d4872a2 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -142,6 +142,10 @@ Vector(m::Integer) = Array{Any,1}(Int(m)) Matrix{T}(m::Integer, n::Integer) where {T} = Matrix{T}(Int(m), Int(n)) Matrix(m::Integer, n::Integer) = Matrix{Any}(Int(m), Int(n)) +include("associative.jl") + +include("namedtuple.jl") + # numeric operations include("hashing.jl") include("rounding.jl") @@ -175,7 +179,6 @@ include("reduce.jl") include("reshapedarray.jl") include("bitarray.jl") include("bitset.jl") -include("associative.jl") if !isdefined(Core, :Inference) include("docs/core.jl") diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 3b1478c4dd659..2779d27c8f9a0 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -221,6 +221,48 @@ A zero-argument anonymous function is written as `()->3`. The idea of a function may seem strange, but is useful for "delaying" a computation. In this usage, a block of code is wrapped in a zero-argument function, which is later invoked by calling it as `f`. +## Tuples + +Julia has a built-in data structure called a *tuple* that is closely related to function +arguments and return values. +A tuple is a fixed-length container that can hold any values, but cannot be modified +(it is *immutable*). +Tuples are constructed with commas and parentheses, and can be accessed via indexing: + +```jldoctest +julia> (1, 1+1) +(1, 2) + +julia> (1,) +(1,) + +julia> x = (0.0, "hello", 6*7) +(0.0, "hello", 42) + +julia> x[2] +"hello" +``` + +Notice that a length-1 tuple must be written with a comma, `(1,)`, since `(1)` would just +be a parenthesized value. +`()` represents the empty (length-0) tuple. + +## Named Tuples + +The components of tuples can optionally be named, in which case a *named tuple* is +constructed: + +```jldoctest +julia> x = (a=1, b=1+1) +(a = 1, b = 2) + +julia> x.a +1 +``` + +Named tuples are very similar to tuples, except that fields can additionally be accessed by name +using dot syntax (`x.a`). + ## Multiple Return Values In Julia, one returns a tuple of values to simulate returning multiple values. However, tuples @@ -320,7 +362,7 @@ In all these cases, `x` is bound to a tuple of the trailing values passed to `ba It is possible to constrain the number of values passed as a variable argument; this will be discussed later in [Parametrically-constrained Varargs methods](@ref). -On the flip side, it is often handy to "splice" the values contained in an iterable collection +On the flip side, it is often handy to "splat" the values contained in an iterable collection into a function call as individual arguments. To do this, one also uses `...` but in the function call instead: @@ -349,7 +391,7 @@ julia> bar(x...) (1, 2, (3, 4)) ``` -Furthermore, the iterable object spliced into a function call need not be a tuple: +Furthermore, the iterable object splatted into a function call need not be a tuple: ```jldoctest barfunc julia> x = [3,4] @@ -371,7 +413,7 @@ julia> bar(x...) (1, 2, (3, 4)) ``` -Also, the function that arguments are spliced into need not be a varargs function (although it +Also, the function that arguments are splatted into need not be a varargs function (although it often is): ```jldoctest @@ -397,7 +439,7 @@ Closest candidates are: baz(::Any, ::Any) at none:1 ``` -As you can see, if the wrong number of elements are in the spliced container, then the function +As you can see, if the wrong number of elements are in the splatted container, then the function call will fail, just as it would if too many arguments were given explicitly. ## Optional Arguments diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index ef2955262bd1a..9eacec5057752 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -905,6 +905,31 @@ used to represent the arguments accepted by varargs methods (see [Varargs Functi The type `Vararg{T,N}` corresponds to exactly `N` elements of type `T`. `NTuple{N,T}` is a convenient alias for `Tuple{Vararg{T,N}}`, i.e. a tuple type containing exactly `N` elements of type `T`. +### Named Tuple Types + +Named tuples are instances of the `NamedTuple` type, which has two parameters: a tuple of +symbols giving the field names, and a tuple type giving the field types. + +```jldoctest +julia> typeof((a=1,b="hello")) +NamedTuple{(:a, :b),Tuple{Int64,String}} +``` + +A `NamedTuple` type can be used as a constructor, accepting a single tuple argument. +The constructed `NamedTuple` type can be either a concrete type, with both parameters specified, +or a type that specifies only field names: + +```jldoctest +julia> NamedTuple{(:a, :b),Tuple{Float32, String}}((1,"")) +(a = 1.0f0, b = "") + +julia> NamedTuple{(:a, :b)}((1,"")) +(a = 1, b = "") +``` + +If field types are specified, the arguments are converted. Otherwise the types of the arguments +are used directly. + #### [Singleton Types](@id man-singleton-types) There is a special kind of abstract parametric type that must be mentioned here: singleton types. diff --git a/src/ast.scm b/src/ast.scm index c8799364a93e2..5f724c34d3998 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -153,6 +153,7 @@ ;; predicates and accessors (define (quoted? e) (memq (car e) '(quote top core globalref outerref line break inert meta))) +(define (quotify e) `',e) (define (lam:args x) (cadr x)) (define (lam:vars x) (llist-vars (lam:args x))) diff --git a/src/builtins.c b/src/builtins.c index fa61ab54eadd6..38ae82d232cc1 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1295,6 +1295,7 @@ void jl_init_primitives(void) add_builtin("QuoteNode", (jl_value_t*)jl_quotenode_type); add_builtin("NewvarNode", (jl_value_t*)jl_newvarnode_type); add_builtin("GlobalRef", (jl_value_t*)jl_globalref_type); + add_builtin("NamedTuple", (jl_value_t*)jl_namedtuple_type); add_builtin("Bool", (jl_value_t*)jl_bool_type); add_builtin("UInt8", (jl_value_t*)jl_uint8_type); diff --git a/src/datatype.c b/src/datatype.c index f413596592675..9ebc3a6d0ac15 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -81,6 +81,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) t->hasfreetypevars = 0; t->isleaftype = 1; t->layout = NULL; + t->names = NULL; return t; } @@ -288,7 +289,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) return; } } - if (st->types == NULL) + if (st->types == NULL || (jl_is_namedtuple_type(st) && !jl_is_leaf_type((jl_value_t*)st))) return; uint32_t nfields = jl_svec_len(st->types); if (nfields == 0) { diff --git a/src/dump.c b/src/dump.c index d64b39ddda255..0181d0e83c791 100644 --- a/src/dump.c +++ b/src/dump.c @@ -367,6 +367,7 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) if (has_instance) jl_serialize_value(s, dt->instance); jl_serialize_value(s, dt->name); + jl_serialize_value(s, dt->names); jl_serialize_value(s, dt->parameters); jl_serialize_value(s, dt->super); jl_serialize_value(s, dt->types); @@ -1245,6 +1246,8 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v } dt->name = (jl_typename_t*)jl_deserialize_value(s, (jl_value_t**)&dt->name); jl_gc_wb(dt, dt->name); + dt->names = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&dt->names); + jl_gc_wb(dt, dt->names); dt->parameters = (jl_svec_t*)jl_deserialize_value(s, (jl_value_t**)&dt->parameters); jl_gc_wb(dt, dt->parameters); dt->super = (jl_datatype_t*)jl_deserialize_value(s, (jl_value_t**)&dt->super); @@ -2804,7 +2807,6 @@ void jl_init_serializer(void) jl_box_int32(30), jl_box_int32(31), jl_box_int32(32), #ifndef _P64 jl_box_int32(33), jl_box_int32(34), jl_box_int32(35), - jl_box_int32(36), jl_box_int32(37), #endif jl_box_int64(0), jl_box_int64(1), jl_box_int64(2), jl_box_int64(3), jl_box_int64(4), jl_box_int64(5), @@ -2819,7 +2821,6 @@ void jl_init_serializer(void) jl_box_int64(30), jl_box_int64(31), jl_box_int64(32), #ifdef _P64 jl_box_int64(33), jl_box_int64(34), jl_box_int64(35), - jl_box_int64(36), jl_box_int64(37), #endif jl_labelnode_type, jl_linenumbernode_type, jl_gotonode_type, jl_quotenode_type, jl_type_type, jl_bottom_type, jl_ref_type, @@ -2845,7 +2846,8 @@ void jl_init_serializer(void) jl_intrinsic_type->name, jl_task_type->name, jl_labelnode_type->name, jl_linenumbernode_type->name, jl_builtin_type->name, jl_gotonode_type->name, jl_quotenode_type->name, jl_globalref_type->name, jl_typeofbottom_type->name, - jl_string_type->name, jl_abstractstring_type->name, + jl_string_type->name, jl_abstractstring_type->name, jl_namedtuple_type, + jl_namedtuple_typename, ptls->root_task, @@ -2883,6 +2885,7 @@ void jl_init_serializer(void) arraylist_push(&builtin_typenames, ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_densearray_type))->name); arraylist_push(&builtin_typenames, jl_tuple_typename); arraylist_push(&builtin_typenames, jl_vararg_typename); + arraylist_push(&builtin_typenames, jl_namedtuple_typename); } #ifdef __cplusplus diff --git a/src/gf.c b/src/gf.c index 9b8d53364e3cc..61b1f6ea7fd35 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2398,6 +2398,8 @@ int jl_has_concrete_subtype(jl_value_t *typ) typ = jl_unwrap_vararg(typ); if (!jl_is_datatype(typ)) return 1; + if (((jl_datatype_t*)typ)->name == jl_namedtuple_typename) + return jl_has_concrete_subtype(jl_tparam1(typ)); jl_svec_t *fields = ((jl_datatype_t*)typ)->types; size_t i, l = jl_svec_len(fields); if (l != ((jl_datatype_t*)typ)->ninitialized) diff --git a/src/interpreter.c b/src/interpreter.c index 0875044c5cfd5..e7dfd42c3af6c 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -172,7 +172,7 @@ void jl_set_datatype_super(jl_datatype_t *tt, jl_value_t *super) if (!jl_is_datatype(super) || !jl_is_abstracttype(super) || tt->name == ((jl_datatype_t*)super)->name || jl_subtype(super,(jl_value_t*)jl_vararg_type) || - jl_is_tuple_type(super) || + jl_is_tuple_type(super) || jl_is_namedtuple_type(super) || jl_subtype(super,(jl_value_t*)jl_type_type) || super == (jl_value_t*)jl_builtin_type) { jl_errorf("invalid subtyping in definition of %s", diff --git a/src/jltypes.c b/src/jltypes.c index 56209a26aedef..09d44e8f9dc8a 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -112,6 +112,8 @@ jl_unionall_t *jl_pointer_type; jl_typename_t *jl_pointer_typename; jl_datatype_t *jl_void_type; jl_datatype_t *jl_voidpointer_type; +jl_typename_t *jl_namedtuple_typename; +jl_unionall_t *jl_namedtuple_type; jl_value_t *jl_an_empty_vec_any=NULL; jl_value_t *jl_stackovf_exception; #ifdef SEGV_EXCEPTION @@ -1133,6 +1135,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_typestack_t top; jl_typename_t *tn = dt->name; int istuple = (tn == jl_tuple_typename); + int isnamedtuple = (tn == jl_namedtuple_typename); // check type cache if (cacheable) { JL_LOCK(&typecache_lock); // Might GC @@ -1255,7 +1258,41 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value ndt->super = NULL; ndt->parameters = p; jl_gc_wb(ndt, ndt->parameters); - ndt->types = istuple ? p : NULL; // to be filled in below + ndt->types = NULL; // to be filled in below + if (istuple) { + ndt->types = p; + } + else if (isnamedtuple) { + jl_value_t *names_tup = jl_svecref(p, 0); + jl_value_t *values_tt = jl_svecref(p, 1); + if (!jl_has_free_typevars(names_tup) && !jl_has_free_typevars(values_tt)) { + if (!jl_is_tuple(names_tup)) + jl_type_error_rt("NamedTuple", "names", (jl_value_t*)jl_anytuple_type, names_tup); + size_t nf = jl_nfields(names_tup); + jl_svec_t *names = jl_alloc_svec_uninit(nf); + for (size_t i = 0; i < nf; i++) { + jl_value_t *ni = jl_fieldref(names_tup, i); + if (!jl_is_symbol(ni)) + jl_type_error_rt("NamedTuple", "name", (jl_value_t*)jl_symbol_type, ni); + for (size_t j = 0; j < i; j++) { + if (ni == jl_svecref(names, j)) + jl_errorf("duplicate field name in NamedTuple: \"%s\" is not unique", jl_symbol_name((jl_sym_t*)ni)); + } + jl_svecset(names, i, ni); + } + if (!jl_is_datatype(values_tt)) + jl_error("NamedTuple field type must be a tuple type"); + if (jl_is_va_tuple((jl_datatype_t*)values_tt) || jl_nparams(values_tt) != nf) + jl_error("NamedTuple names and field types must have matching lengths"); + ndt->names = names; + jl_gc_wb(ndt, ndt->names); + ndt->types = ((jl_datatype_t*)values_tt)->parameters; + jl_gc_wb(ndt, ndt->types); + } + else { + ndt->types = jl_emptysvec; + } + } ndt->mutabl = dt->mutabl; ndt->abstract = dt->abstract; ndt->instance = NULL; @@ -1269,7 +1306,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value if (cacheable && !ndt->abstract) ndt->uid = jl_assign_type_uid(); - if (istuple) { + if (istuple || isnamedtuple) { ndt->super = jl_any_type; } else if (dt->super) { @@ -1280,13 +1317,13 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value if (ftypes == NULL || dt->super == NULL) { // in the process of creating this type definition: // need to instantiate the super and types fields later - assert(inside_typedef && !istuple); + assert(inside_typedef && !istuple && !isnamedtuple); arraylist_push(&partial_inst, ndt); } else { - assert(ftypes != jl_emptysvec || jl_field_names(ndt) == jl_emptysvec); + assert(ftypes != jl_emptysvec || jl_field_names(ndt) == jl_emptysvec || isnamedtuple); assert(ftypes == jl_emptysvec || !ndt->abstract); - if (!istuple) { + if (!istuple && !isnamedtuple) { // recursively instantiate the types of the fields ndt->types = inst_all(ftypes, env, stack, 1); jl_gc_wb(ndt, ndt->types); @@ -1302,6 +1339,8 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value if (istuple) ndt->ninitialized = ntp - isvatuple; + else if (isnamedtuple) + ndt->ninitialized = jl_svec_len(ndt->types); else ndt->ninitialized = dt->ninitialized; @@ -1687,11 +1726,12 @@ void jl_init_types(void) jl_datatype_type->name->wrapper = (jl_value_t*)jl_datatype_type; jl_datatype_type->super = (jl_datatype_t*)jl_type_type; jl_datatype_type->parameters = jl_emptysvec; - jl_datatype_type->name->names = jl_perm_symsvec(16, + jl_datatype_type->name->names = jl_perm_symsvec(17, "name", "super", "parameters", "types", + "names", "instance", "layout", "size", @@ -1704,11 +1744,11 @@ void jl_init_types(void) "depth", "hasfreetypevars", "isleaftype"); - jl_datatype_type->types = jl_svec(16, + jl_datatype_type->types = jl_svec(17, jl_typename_type, jl_datatype_type, jl_simplevector_type, - jl_simplevector_type, + jl_simplevector_type, jl_simplevector_type, jl_any_type, // instance jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, @@ -2147,20 +2187,29 @@ void jl_init_types(void) jl_string_type->instance = NULL; jl_compute_field_offsets(jl_string_type); + jl_tvar_t *ntval_var = jl_new_typevar(jl_symbol("T"), (jl_value_t*)jl_bottom_type, + (jl_value_t*)jl_anytuple_type); + tv = jl_svec2(tvar("names"), ntval_var); + jl_datatype_t *ntt = jl_new_datatype(jl_symbol("NamedTuple"), core, jl_any_type, tv, + jl_emptysvec, jl_emptysvec, 0, 0, 0); + jl_namedtuple_type = (jl_unionall_t*)ntt->name->wrapper; + ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_namedtuple_type))->layout = NULL; + jl_namedtuple_typename = ntt->name; + // complete builtin type metadata jl_value_t *pointer_void = jl_apply_type1((jl_value_t*)jl_pointer_type, (jl_value_t*)jl_void_type); jl_voidpointer_type = (jl_datatype_t*)pointer_void; - jl_svecset(jl_datatype_type->types, 5, jl_voidpointer_type); - jl_svecset(jl_datatype_type->types, 6, jl_int32_type); + jl_svecset(jl_datatype_type->types, 6, jl_voidpointer_type); jl_svecset(jl_datatype_type->types, 7, jl_int32_type); jl_svecset(jl_datatype_type->types, 8, jl_int32_type); - jl_svecset(jl_datatype_type->types, 9, jl_bool_type); + jl_svecset(jl_datatype_type->types, 9, jl_int32_type); jl_svecset(jl_datatype_type->types, 10, jl_bool_type); - jl_svecset(jl_datatype_type->types, 11, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 11, jl_bool_type); jl_svecset(jl_datatype_type->types, 12, jl_voidpointer_type); - jl_svecset(jl_datatype_type->types, 13, jl_int32_type); - jl_svecset(jl_datatype_type->types, 14, jl_bool_type); + jl_svecset(jl_datatype_type->types, 13, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 14, jl_int32_type); jl_svecset(jl_datatype_type->types, 15, jl_bool_type); + jl_svecset(jl_datatype_type->types, 16, jl_bool_type); jl_svecset(jl_typename_type->types, 1, jl_module_type); jl_svecset(jl_typename_type->types, 6, jl_long_type); jl_svecset(jl_typename_type->types, 3, jl_type_type); diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 6286affbb2c87..9741051a77836 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -924,7 +924,7 @@ ,@(map (lambda (v) `(local ,v)) params) ,@(map (lambda (n v) (make-assignment n (bounds-to-TypeVar v))) params bounds) (struct_type ,name (call (core svec) ,@params) - (call (core svec) ,@(map (lambda (x) `',x) field-names)) + (call (core svec) ,@(map quotify field-names)) ,super (call (core svec) ,@field-types) ,mut ,min-initialized))) ;; "inner" constructors (scope-block @@ -1920,6 +1920,76 @@ (extract (cdr params) (cons p newparams) whereparams))))) (extract (cddr e) '() '())) +(define (named-tuple-expr names values) + `(call (curly (core NamedTuple) (tuple ,@names)) + (tuple ,@values))) + +(define (lower-named-tuple lst) + (let* ((names (apply append + (map (lambda (x) + (cond #;((symbol? x) (list x)) + ((and (or (assignment? x) (kwarg? x)) (symbol? (cadr x))) + (list (cadr x))) + #;((and (length= x 3) (eq? (car x) '|.|)) + (list (cadr (caddr x)))) + (else '()))) + lst))) + (dups (has-dups names))) + (if dups + (error (string "field name \"" (car dups) "\" repeated in named tuple")))) + (define (to-nt n v) + (if (null? n) + #f + (named-tuple-expr (reverse! (map quotify n)) (reverse v)))) + (define (merge old new) + (if old + (if new + `(call (top merge) ,old ,new) + old) + new)) + (let loop ((L lst) + (current-names '()) + (current-vals '()) + (expr #f)) + (if (null? L) + (merge expr (to-nt current-names current-vals)) + (let ((el (car L))) + (cond ((or (assignment? el) (kwarg? el)) + (if (not (symbol? (cadr el))) + (error (string "invalid named tuple field name \"" (deparse (cadr el)) "\""))) + (loop (cdr L) + (cons (cadr el) current-names) + (cons (caddr el) current-vals) + expr)) +#| + ((symbol? el) ;; x => x = x + (loop (cdr L) + (cons el current-names) + (cons el current-vals) + expr)) + ((and (length= el 3) (eq? (car el) '|.|)) ;; a.x => x = a.x + (loop (cdr L) + (cons (cadr (caddr el)) current-names) + (cons el current-vals) + expr)) + ((and (length= el 4) (eq? (car el) 'call) (eq? (cadr el) '=>)) + (loop (cdr L) + '() + '() + (merge (merge expr (to-nt current-names current-vals)) + (named-tuple-expr (list (caddr el)) (list (cadddr el)))))) +|# + ((vararg? el) + (loop (cdr L) + '() + '() + (let ((current (merge expr (to-nt current-names current-vals)))) + (if current + (merge current (cadr el)) + `(call (top merge) (call (top NamedTuple)) ,(cadr el)))))) + (else + (error (string "invalid named tuple element \"" (deparse el) "\"")))))))) + (define (expand-forms e) (if (or (atom? e) (memq (car e) '(quote inert top core globalref outerref line module toplevel ssavalue null meta))) e @@ -2246,11 +2316,16 @@ 'tuple (lambda (e) - (if (and (length> e 1) (pair? (cadr e)) (eq? (caadr e) 'parameters)) - (error "unexpected semicolon in tuple")) - (if (any assignment? (cdr e)) - (error "assignment not allowed inside tuple")) - (expand-forms `(call (core tuple) ,@(cdr e)))) + (cond ((and (length> e 1) (pair? (cadr e)) (eq? (caadr e) 'parameters)) + (error "unexpected semicolon in tuple") + ;; this enables `(; ...)` named tuple syntax + #;(if (length= e 2) + (expand-forms (lower-named-tuple (cdr (cadr e)))) + (error "unexpected semicolon in tuple"))) + ((any assignment? (cdr e)) + (expand-forms (lower-named-tuple (cdr e)))) + (else + (expand-forms `(call (core tuple) ,@(cdr e)))))) '=> (lambda (e) @@ -2861,7 +2936,7 @@ f(x) = yt(x) (body (global ,name) (const ,name) ,@(map (lambda (p n) `(= ,p (call (core TypeVar) ',n (core Any)))) P names) (struct_type ,name (call (core svec) ,@P) - (call (core svec) ,@(map (lambda (v) `',v) fields)) + (call (core svec) ,@(map quotify fields)) ,super (call (core svec) ,@types) false ,(length fields)) (return (null)))))))) @@ -2871,7 +2946,7 @@ f(x) = yt(x) (() () 0 ()) (body (global ,name) (const ,name) (struct_type ,name (call (core svec)) - (call (core svec) ,@(map (lambda (v) `',v) fields)) + (call (core svec) ,@(map quotify fields)) ,super (call (core svec) ,@(map (lambda (v) '(core Box)) fields)) false ,(length fields)) @@ -2888,7 +2963,7 @@ f(x) = yt(x) ; (const ,name) ; ,@(map (lambda (p n) `(= ,p (call (core TypeVar) ',n (core Any)))) P names) ; (struct_type ,name (call (core svec) ,@P) -; (call (core svec) ,@(map (lambda (v) `',v) fields)) +; (call (core svec) ,@(map quotify fields)) ; ,super ; (call (core svec) ,@types) false ,(length fields))))) @@ -2897,7 +2972,7 @@ f(x) = yt(x) ; `((global ,name) ; (const ,name) ; (struct_type ,name (call (core svec)) -; (call (core svec) ,@(map (lambda (v) `',v) fields)) +; (call (core svec) ,@(map quotify fields)) ; ,super ; (call (core svec) ,@(map (lambda (v) 'Any) fields)) ; false ,(length fields)))) diff --git a/src/julia.h b/src/julia.h index 17583038ee7b1..3b633bbd2dc2c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -384,6 +384,7 @@ typedef struct _jl_datatype_t { struct _jl_datatype_t *super; jl_svec_t *parameters; jl_svec_t *types; + jl_svec_t *names; jl_value_t *instance; // for singletons const jl_datatype_layout_t *layout; int32_t size; // TODO: move to _jl_datatype_layout_t @@ -566,6 +567,8 @@ extern JL_DLLEXPORT jl_datatype_t *jl_voidpointer_type; extern JL_DLLEXPORT jl_unionall_t *jl_pointer_type; extern JL_DLLEXPORT jl_unionall_t *jl_ref_type; extern JL_DLLEXPORT jl_typename_t *jl_pointer_typename; +extern JL_DLLEXPORT jl_typename_t *jl_namedtuple_typename; +extern JL_DLLEXPORT jl_unionall_t *jl_namedtuple_type; extern JL_DLLEXPORT jl_value_t *jl_array_uint8_type; extern JL_DLLEXPORT jl_value_t *jl_array_any_type; @@ -788,7 +791,10 @@ STATIC_INLINE void jl_array_uint8_set(void *a, size_t i, uint8_t x) // struct type info STATIC_INLINE jl_svec_t *jl_field_names(jl_datatype_t *st) { - return st->name->names; + jl_svec_t *names = st->names; + if (!names) + names = st->name->names; + return names; } STATIC_INLINE jl_sym_t *jl_field_name(jl_datatype_t *st, size_t i) { @@ -974,6 +980,12 @@ STATIC_INLINE int jl_is_tuple_type(void *t) ((jl_datatype_t*)(t))->name == jl_tuple_typename); } +STATIC_INLINE int jl_is_namedtuple_type(void *t) +{ + return (jl_is_datatype(t) && + ((jl_datatype_t*)(t))->name == jl_namedtuple_typename); +} + STATIC_INLINE int jl_is_vecelement_type(jl_value_t* t) { return (jl_is_datatype(t) && diff --git a/src/rtutils.c b/src/rtutils.c index e81cc2d2a6035..e3f5957b8cabf 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -862,8 +862,8 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, ")"); } else if (jl_is_datatype(vt)) { - int istuple = jl_is_tuple_type(vt); - if (!istuple) + int istuple = jl_is_tuple_type(vt), isnamedtuple = jl_is_namedtuple_type(vt); + if (!istuple && !isnamedtuple) n += jl_static_show_x(out, (jl_value_t*)vt, depth); n += jl_printf(out, "("); size_t nb = jl_datatype_size(vt); @@ -896,7 +896,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } n += jl_static_show_x_(out, (jl_value_t*)fld_ptr, ft, depth); } - if (istuple && tlen == 1) + if ((istuple || isnamedtuple) && tlen == 1) n += jl_printf(out, ","); else if (i != tlen - 1) n += jl_printf(out, ", "); diff --git a/src/staticdata.c b/src/staticdata.c index 797ab89ac6895..85d4f1e60a4c1 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -1583,6 +1583,7 @@ static void jl_init_serializer2(int for_serialize) jl_gotonode_type->name, jl_quotenode_type->name, jl_globalref_type->name, jl_typeofbottom_type->name, jl_string_type->name, jl_abstractstring_type->name, + jl_namedtuple_type, jl_namedtuple_typename, jl_int32_type, jl_int64_type, jl_bool_type, jl_uint8_type, @@ -1649,6 +1650,7 @@ static void jl_init_serializer2(int for_serialize) arraylist_push(&builtin_typenames, ((jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)jl_densearray_type))->name); arraylist_push(&builtin_typenames, jl_tuple_typename); arraylist_push(&builtin_typenames, jl_vararg_typename); + arraylist_push(&builtin_typenames, jl_namedtuple_typename); } static void jl_cleanup_serializer2(void) diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 6165813bc395e..6f7f8fc13f7fd 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -270,6 +270,7 @@ end pop!(need_to_handle_undef_sparam, which(Base._totuple, (Type{Tuple{Vararg{E}}} where E, Any, Any))) pop!(need_to_handle_undef_sparam, which(Base.eltype, Tuple{Type{Tuple{Vararg{E}}} where E})) pop!(need_to_handle_undef_sparam, which(Base.eltype, Tuple{Type{Tuple{Any}}})) + pop!(need_to_handle_undef_sparam, first(methods(Base.same_names))) @test_broken need_to_handle_undef_sparam == Set() pop!(need_to_handle_undef_sparam, which(Base.cat, Tuple{Any, AbstractArray})) pop!(need_to_handle_undef_sparam, which(Base.byteenv, (Union{AbstractArray{Pair{T}, 1}, Tuple{Vararg{Pair{T}}}} where T<:AbstractString,))) diff --git a/test/choosetests.jl b/test/choosetests.jl index 9a749d5bc4e6a..77f1407b9ec11 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -37,7 +37,7 @@ function choosetests(choices = []) "bitarray", "copy", "math", "fastmath", "functional", "iterators", "operators", "path", "ccall", "parse", "loading", "bigint", "bigfloat", "sorting", "statistics", "spawn", "backtrace", - "file", "read", "version", "resolve", + "file", "read", "version", "resolve", "namedtuple", "mpfr", "broadcast", "complex", "socket", "floatapprox", "stdlib", "reflection", "regex", "float16", "combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi", diff --git a/test/inference.jl b/test/inference.jl index 0530d3012d002..f9dca24b0eb8f 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -1001,6 +1001,22 @@ copy_dims_pair(out, dim::Colon, tail...) = copy_dims_pair(out => dim, tail...) @test Base.return_types(copy_dims_pair, (Tuple{}, Vararg{Union{Int,Colon}})) == Any[Tuple{}, Tuple{}, Tuple{}] @test all(m -> 5 < count_specializations(m) < 25, methods(copy_dims_pair)) +@test isdefined_tfunc(typeof(NamedTuple()), Const(0)) === Const(false) +@test isdefined_tfunc(typeof(NamedTuple()), Const(1)) === Const(false) +@test isdefined_tfunc(typeof((a=1,b=2)), Const(:a)) === Const(true) +@test isdefined_tfunc(typeof((a=1,b=2)), Const(:b)) === Const(true) +@test isdefined_tfunc(typeof((a=1,b=2)), Const(:c)) === Const(false) +@test isdefined_tfunc(typeof((a=1,b=2)), Const(0)) === Const(false) +@test isdefined_tfunc(typeof((a=1,b=2)), Const(1)) === Const(true) +@test isdefined_tfunc(typeof((a=1,b=2)), Const(2)) === Const(true) +@test isdefined_tfunc(typeof((a=1,b=2)), Const(3)) === Const(false) +@test isdefined_tfunc(NamedTuple, Const(1)) == Bool +@test isdefined_tfunc(NamedTuple, Symbol) == Bool +@test Const(false) ⊑ isdefined_tfunc(NamedTuple{(:x,:y)}, Const(:z)) +@test Const(true) ⊑ isdefined_tfunc(NamedTuple{(:x,:y)}, Const(1)) +@test Const(false) ⊑ isdefined_tfunc(NamedTuple{(:x,:y)}, Const(3)) +@test Const(true) ⊑ isdefined_tfunc(NamedTuple{(:x,:y)}, Const(:y)) + # splatting an ::Any should still allow inference to use types of parameters preceding it f22364(::Int, ::Any...) = 0 f22364(::String, ::Any...) = 0.0 diff --git a/test/namedtuple.jl b/test/namedtuple.jl new file mode 100644 index 0000000000000..42423da7a96b4 --- /dev/null +++ b/test/namedtuple.jl @@ -0,0 +1,191 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +@test_throws TypeError NamedTuple{1,Tuple{}} +@test_throws TypeError NamedTuple{(),1} +@test_throws TypeError NamedTuple{(:a,1),Tuple{Int}} +@test_throws ErrorException NamedTuple{(:a,:b),Tuple{Int}} +@test_throws ErrorException NamedTuple{(:a,:b),Tuple{Int,Vararg{Int}}} +@test_throws ErrorException NamedTuple{(:a,),Union{Tuple{Int},Tuple{String}}} +@test_throws ErrorException NamedTuple{(:a,:a),Tuple{Int,Int}} +@test_throws ErrorException NamedTuple{(:a,:a)}((1,2)) +@test_throws ErrorException NamedTuple{(:a, :b, :a), NTuple{3, Int}}((1, 2, 3)) + +@test fieldcount(NamedTuple{(:a,:b,:c)}) == 3 +@test fieldcount(NamedTuple{<:Any,Tuple{Int,Int}}) == 2 +@test_throws ErrorException fieldcount(NamedTuple) +@test_throws ErrorException fieldcount(NamedTuple{<:Any,<:Tuple{Int,Vararg{Int}}}) + +@test (a=1,).a == 1 +@test (a=2,)[1] == 2 +@test (a=3,)[:a] == 3 +@test (x=4, y=5, z=6).y == 5 +@test (x=4, y=5, z=6).z == 6 +@test_throws ErrorException (x=4, y=5, z=6).a +@test_throws BoundsError (a=2,)[0] +@test_throws BoundsError (a=2,)[2] + +@test length(NamedTuple()) == 0 +@test length((a=1,)) == 1 +@test length((a=1, b=0)) == 2 + +@test (a=1,b=2) === (a=1,b=2) +@test (a=1,b=2) !== (b=1,a=2) + +@test (a=1,b=2) == (a=1,b=2) +@test (a=1,b=2) != (b=1,a=2) +@test NamedTuple() === NamedTuple() +@test NamedTuple() != (a=1,) + +@test string((a=1,)) == "(a = 1,)" +@test string((name="", day=:today)) == "(name = \"\", day = :today)" +@test string(NamedTuple()) == "NamedTuple()" + +@test hash((a = 1, b = "hello")) == hash(NamedTuple{(:a,:b),Tuple{Int,String}}((1, "hello"))) +@test hash((a = 1, b = "hello")) != hash(NamedTuple{(:a,:c),Tuple{Int,String}}((1, "hello"))) +@test hash((a = 1, b = "hello")) != hash(NamedTuple{(:a,:b),Tuple{Int,String}}((1, "helo"))) + +@test NamedTuple{(:a,:b),Tuple{Int8,Int16}}((1,2)) === (a=Int8(1), b=Int16(2)) +@test convert(NamedTuple{(:a,:b),Tuple{Int8,Int16}}, (a=3,b=4)) === (a=Int8(3), b=Int16(4)) +let NT = NamedTuple{(:a,:b),Tuple{Int8,Int16}}, nt = (x=3,y=4) + @test_throws MethodError convert(NT, nt) +end + +@test NamedTuple{(:a,:c)}((b=1,z=2,c=3,aa=4,a=5)) === (a=5, c=3) +@test NamedTuple{(:a,)}(NamedTuple{(:b, :a), Tuple{Int, Union{Int,Void}}}((1, 2))) === + NamedTuple{(:a,), Tuple{Union{Int,Void}}}((2,)) + +@test eltype((a=[1,2], b=[3,4])) === Vector{Int} + +@test Tuple((a=[1,2], b=[3,4])) == ([1,2], [3,4]) +@test Tuple(NamedTuple()) === () +@test Tuple((x=4, y=5, z=6)) == (4,5,6) +@test collect((x=4, y=5, z=6)) == [4,5,6] +@test Tuple((a=1, b=2, c=3)) == (1, 2, 3) + +@test isless((a=1,b=2), (a=1,b=3)) +@test_broken isless((a=1,), (a=1,b=2)) +@test !isless((a=1,b=2), (a=1,b=2)) +@test !isless((a=2,b=1), (a=1,b=2)) +@test_throws MethodError isless((a=1,), (x=2,)) + +@test map(-, (x=1, y=2)) == (x=-1, y=-2) +@test map(+, (x=1, y=2), (x=10, y=20)) == (x=11, y=22) +@test_throws ArgumentError map(+, (x=1, y=2), (y=10, x=20)) +@test map(string, (x=1, y=2)) == (x="1", y="2") +@test map(round, (x=1//3, y=Int), (x=3, y=2//3)) == (x=0.333, y=1) + +@test merge((a=1, b=2), (a=10,)) == (a=10, b=2) +@test merge((a=1, b=2), (a=10, z=20)) == (a=10, b=2, z=20) +@test merge((a=1, b=2), (z=20,)) == (a=1, b=2, z=20) +@test merge(NamedTuple(), (a=2, b=1)) == (a=2, b=1) +@test merge((a=2, b=1), NamedTuple()) == (a=2, b=1) +@test merge(NamedTuple(), NamedTuple()) == NamedTuple() +# `merge` should preserve element types +let nt = merge(NamedTuple{(:a,:b),Tuple{Int32,Union{Int32,Void}}}((1,Int32(2))), + NamedTuple{(:a,:c),Tuple{Union{Int8,Void},Float64}}((nothing,1.0))) + @test typeof(nt) == NamedTuple{(:a,:b,:c),Tuple{Union{Int8,Void},Union{Int32,Void},Float64}} + @test repr(nt) == "NamedTuple{(:a, :b, :c),Tuple{Union{Void, Int8},Union{Void, Int32},Float64}}((nothing, 2, 1.0))" +end + +@test merge(NamedTuple(), [:a=>1, :b=>2, :c=>3, :a=>4, :c=>5]) == (a=4, b=2, c=5) +@test merge((c=0, z=1), [:a=>1, :b=>2, :c=>3, :a=>4, :c=>5]) == (c=5, z=1, a=4, b=2) + +@test keys((a=1, b=2, c=3)) == (:a, :b, :c) +@test keys(NamedTuple()) == () +@test keys((a=1,)) == (:a,) +@test values((a=1, b=2, c=3)) == (1, 2, 3) +@test values(NamedTuple()) == () +@test values((a=1,)) == (1,) +@test haskey((a=1, b=2, c=3), :a) +@test !haskey(NamedTuple(), :a) +@test !haskey((a=1,), :b) +@test get((a=1, b=2, c=3), :a, 0) == 1 +@test get(NamedTuple(), :a, 0) == 0 +@test get((a=1,), :b, 0) == 0 +@test get(()->0, (a=1, b=2, c=3), :a) == 1 +@test get(()->0, NamedTuple(), :a) == 0 +@test get(()->0, (a=1,), :b) == 0 + +# syntax errors + +@test Meta.lower(Main, parse("(a=1, 0)")) == Expr(:error, "invalid named tuple element \"0\"") +@test Meta.lower(Main, parse("(a=1, f(x))")) == Expr(:error, "invalid named tuple element \"f(x)\"") +@test Meta.lower(Main, parse("(a=1,a=2)")) == Expr(:error, "field name \"a\" repeated in named tuple") +@test Meta.lower(Main, parse("(a=1,b=0,a=2)")) == Expr(:error, "field name \"a\" repeated in named tuple") +@test Meta.lower(Main, parse("(c=1,a=1,b=0,a=2)")) == Expr(:error, "field name \"a\" repeated in named tuple") + +@test parse("(;)") == quote end +@test Meta.lower(Main, parse("(1,;2)")) == Expr(:error, "unexpected semicolon in tuple") + +# splatting + +let d = [:a=>1, :b=>2, :c=>3] # use an array to preserve order + @test (d..., a=10) == (a=10, b=2, c=3) + @test (a=0, b=0, z=1, d..., x=4, y=5) == (a=1, b=2, z=1, c=3, x=4, y=5) + @test (a=0, (b=2,a=1)..., c=3) == (a=1, b=2, c=3) +end + +# inference tests + +namedtuple_get_a(x) = x.a +@test Base.return_types(namedtuple_get_a, (NamedTuple,)) == Any[Any] +@test Base.return_types(namedtuple_get_a, (typeof((b=1,a="")),)) == Any[String] + +namedtuple_fieldtype_a(x) = fieldtype(typeof(x), :a) +@test Base.return_types(namedtuple_fieldtype_a, (NamedTuple,)) == Any[Type] +@test Base.return_types(namedtuple_fieldtype_a, (typeof((b=1,a="")),)) == Any[Type{String}] +namedtuple_fieldtype__(x, y) = fieldtype(typeof(x), y) +@test Base.return_types(namedtuple_fieldtype__, (typeof((b=1,a="")),Symbol))[1] >: Union{Type{Int}, Type{String}} + +namedtuple_nfields(x) = nfields(x) === 0 ? 1 : "" +@test Union{Int,String} <: Base.return_types(namedtuple_nfields, (NamedTuple,))[1] + +function nt_from_abstractly_typed_array() + a = NamedTuple[(a=3,b=5)] + (getfield(a[1],1), getfield(a[1],2)) +end +@test nt_from_abstractly_typed_array() === (3,5) + +let T = NamedTuple{(:a, :b), Tuple{Int64, Union{Float64, Void}}}, nt = T((1, nothing)) + @test nt == (a=1, b=nothing) + @test typeof(nt) == T + @test convert(T, (a=1, b=nothing)) == nt + @test typeof(convert(T, (a=1, b=nothing))) === T +end + +function abstr_nt_22194() + a = NamedTuple[(a=1,), (b=2,)] + return (a[1].a, a[2].b) +end +@test abstr_nt_22194() == (1, 2) +@test Base.return_types(abstr_nt_22194, ()) == Any[Tuple{Any,Any}] +function abstr_nt_22194_2() + a = NamedTuple[(a=1,), (b=2,)] + return a[1].b +end +@test_throws ErrorException abstr_nt_22194_2() +@test Base.return_types(abstr_nt_22194_2, ()) == Any[Any] + +mutable struct HasAbstractNamedTuples + x::NamedTuple{(:a,:b)} +end + +function abstr_nt_22194_3() + s = HasAbstractNamedTuples((a="",b=8)) + @test s.x.a === "" + @test s.x.b === 8 + s.x = (a=1,b=:b) + @test s.x.a === 1 + @test s.x.b === :b + @test isdefined(s.x, :a) + @test isdefined(s.x, :b) + @test !isdefined(s.x, :c) + @test nfields(s) == 1 + @test isdefined(s, :x) + @test fieldtype(typeof(s), 1) == fieldtype(typeof(s), :x) == NamedTuple{(:a,:b)} + @test fieldtype(typeof(s.x), :a) === Int + @test fieldtype(typeof(s.x), :b) === Symbol + return s.x.b +end +abstr_nt_22194_3() +@test Base.return_types(abstr_nt_22194_3, ()) == Any[Any] From f16a1191886d3a72820526eb3db27c3bfb9d336e Mon Sep 17 00:00:00 2001 From: Gem Newman Date: Thu, 2 Nov 2017 07:39:29 -0500 Subject: [PATCH 19/34] Add rounding support for `Periods` (#24182) * Add rounding for Periods * Improve period rounding function type signatures * Use promote instead of Nanosecond to round periods Period rounding functions no longer unnecessarily convert all values to nanoseconds prior to rounding. * Minor refactoring for period rounding --- base/dates/rounding.jl | 121 +++++++++++++++++++++++++++++++++++++--- doc/src/stdlib/dates.md | 8 +++ test/dates/rounding.jl | 76 ++++++++++++++++++++++++- 3 files changed, 196 insertions(+), 9 deletions(-) diff --git a/base/dates/rounding.jl b/base/dates/rounding.jl index 6a4a40d38ae13..d8efe47f1a94c 100644 --- a/base/dates/rounding.jl +++ b/base/dates/rounding.jl @@ -7,6 +7,9 @@ const DATETIMEEPOCH = value(DateTime(0)) # According to ISO 8601, the first day of the first week of year 0000 is 0000-01-03 const WEEKEPOCH = value(Date(0, 1, 3)) +const ConvertiblePeriod = Union{TimePeriod, Week, Day} +const TimeTypeOrPeriod = Union{TimeType, ConvertiblePeriod} + """ epochdays2date(days) -> Date @@ -76,6 +79,35 @@ function Base.floor(dt::DateTime, p::TimePeriod) return epochms2datetime(milliseconds - mod(milliseconds, value(Millisecond(p)))) end +""" + floor(x::Period, precision::T) where T <: Union{TimePeriod, Week, Day} -> T + +Rounds `x` down to the nearest multiple of `precision`. If `x` and `precision` are different +subtypes of `Period`, the return value will have the same type as `precision`. + +For convenience, `precision` may be a type instead of a value: `floor(x, Dates.Hour)` is a +shortcut for `floor(x, Dates.Hour(1))`. + +```jldoctest +julia> floor(Dates.Day(16), Dates.Week) +2 weeks + +julia> floor(Dates.Minute(44), Dates.Minute(15)) +30 minutes + +julia> floor(Dates.Hour(36), Dates.Day) +1 day +``` + +Rounding to a `precision` of `Month`s or `Year`s is not supported, as these `Period`s are of +inconsistent length. +""" +function Base.floor(x::ConvertiblePeriod, precision::T) where T <: ConvertiblePeriod + value(precision) < 1 && throw(DomainError(precision)) + _x, _precision = promote(x, precision) + return T(_x - mod(_x, _precision)) +end + """ floor(dt::TimeType, p::Period) -> TimeType @@ -121,6 +153,34 @@ function Base.ceil(dt::TimeType, p::Period) return (dt == f) ? f : f + p end +""" + ceil(x::Period, precision::T) where T <: Union{TimePeriod, Week, Day} -> T + +Rounds `x` up to the nearest multiple of `precision`. If `x` and `precision` are different +subtypes of `Period`, the return value will have the same type as `precision`. + +For convenience, `precision` may be a type instead of a value: `ceil(x, Dates.Hour)` is a +shortcut for `ceil(x, Dates.Hour(1))`. + +```jldoctest +julia> ceil(Dates.Day(16), Dates.Week) +3 weeks + +julia> ceil(Dates.Minute(44), Dates.Minute(15)) +45 minutes + +julia> ceil(Dates.Hour(36), Dates.Day) +3 days +``` + +Rounding to a `precision` of `Month`s or `Year`s is not supported, as these `Period`s are of +inconsistent length. +""" +function Base.ceil(x::ConvertiblePeriod, precision::ConvertiblePeriod) + f = floor(x, precision) + return (x == f) ? f : f + precision +end + """ floorceil(dt::TimeType, p::Period) -> (TimeType, TimeType) @@ -132,6 +192,17 @@ function floorceil(dt::TimeType, p::Period) return f, (dt == f) ? f : f + p end +""" + floorceil(x::Period, precision::T) where T <: Union{TimePeriod, Week, Day} -> (T, T) + +Simultaneously return the `floor` and `ceil` of `Period` at resolution `p`. More efficient +than calling both `floor` and `ceil` individually. +""" +function floorceil(x::ConvertiblePeriod, precision::ConvertiblePeriod) + f = floor(x, precision) + return f, (x == f) ? f : f + precision +end + """ round(dt::TimeType, p::Period, [r::RoundingMode]) -> TimeType @@ -160,21 +231,55 @@ function Base.round(dt::TimeType, p::Period, r::RoundingMode{:NearestTiesUp}) return (dt - f) < (c - dt) ? f : c end -Base.round(dt::TimeType, p::Period, r::RoundingMode{:Down}) = Base.floor(dt, p) -Base.round(dt::TimeType, p::Period, r::RoundingMode{:Up}) = Base.ceil(dt, p) +""" + round(x::Period, precision::T, [r::RoundingMode]) where T <: Union{TimePeriod, Week, Day} -> T + +Rounds `x` to the nearest multiple of `precision`. If `x` and `precision` are different +subtypes of `Period`, the return value will have the same type as `precision`. By default +(`RoundNearestTiesUp`), ties (e.g., rounding 90 minutes to the nearest hour) will be rounded +up. + +For convenience, `precision` may be a type instead of a value: `round(x, Dates.Hour)` is a +shortcut for `round(x, Dates.Hour(1))`. + +```jldoctest +julia> round(Dates.Day(16), Dates.Week) +2 weeks + +julia> round(Dates.Minute(44), Dates.Minute(15)) +45 minutes + +julia> round(Dates.Hour(36), Dates.Day) +3 days +``` + +Valid rounding modes for `round(::Period, ::T, ::RoundingMode)` are `RoundNearestTiesUp` +(default), `RoundDown` (`floor`), and `RoundUp` (`ceil`). + +Rounding to a `precision` of `Month`s or `Year`s is not supported, as these `Period`s are of +inconsistent length. +""" +function Base.round(x::ConvertiblePeriod, precision::ConvertiblePeriod, r::RoundingMode{:NearestTiesUp}) + f, c = floorceil(x, precision) + _x, _f, _c = promote(x, f, c) + return (_x - _f) < (_c - _x) ? f : c +end + +Base.round(x::TimeTypeOrPeriod, p::Period, r::RoundingMode{:Down}) = Base.floor(x, p) +Base.round(x::TimeTypeOrPeriod, p::Period, r::RoundingMode{:Up}) = Base.ceil(x, p) # No implementation of other `RoundingMode`s: rounding to nearest "even" is skipped because # "even" is not defined for Period; rounding toward/away from zero is skipped because ISO # 8601's year 0000 is not really "zero". -Base.round(::TimeType, p::Period, ::RoundingMode) = throw(DomainError(p)) +Base.round(::TimeTypeOrPeriod, p::Period, ::RoundingMode) = throw(DomainError(p)) # Default to RoundNearestTiesUp. -Base.round(dt::TimeType, p::Period) = Base.round(dt, p, RoundNearestTiesUp) +Base.round(x::TimeTypeOrPeriod, p::Period) = Base.round(x, p, RoundNearestTiesUp) # Make rounding functions callable using Period types in addition to values. -Base.floor(dt::TimeType, p::Type{<:Period}) = Base.floor(dt, p(1)) -Base.ceil(dt::TimeType, p::Type{<:Period}) = Base.ceil(dt, p(1)) +Base.floor(x::TimeTypeOrPeriod, ::Type{P}) where P <: Period = Base.floor(x, oneunit(P)) +Base.ceil(x::TimeTypeOrPeriod, ::Type{P}) where P <: Period = Base.ceil(x, oneunit(P)) -function Base.round(dt::TimeType, p::Type{<:Period}, r::RoundingMode=RoundNearestTiesUp) - return Base.round(dt, p(1), r) +function Base.round(x::TimeTypeOrPeriod, ::Type{P}, r::RoundingMode=RoundNearestTiesUp) where P <: Period + return Base.round(x, oneunit(P), r) end diff --git a/doc/src/stdlib/dates.md b/doc/src/stdlib/dates.md index a7ebd9f3d64dd..b7ff1f950bda0 100644 --- a/doc/src/stdlib/dates.md +++ b/doc/src/stdlib/dates.md @@ -132,6 +132,14 @@ Base.ceil(::Base.Dates.TimeType, ::Base.Dates.Period) Base.round(::Base.Dates.TimeType, ::Base.Dates.Period, ::RoundingMode{:NearestTiesUp}) ``` +Most `Period` values can also be rounded to a specified resolution: + +```@docs +Base.floor(::Base.Dates.ConvertiblePeriod, ::T) where T <: Base.Dates.ConvertiblePeriod +Base.ceil(::Base.Dates.ConvertiblePeriod, ::Base.Dates.ConvertiblePeriod) +Base.round(::Base.Dates.ConvertiblePeriod, ::Base.Dates.ConvertiblePeriod, ::RoundingMode{:NearestTiesUp}) +``` + The following functions are not exported: ```@docs diff --git a/test/dates/rounding.jl b/test/dates/rounding.jl index b68c14ba80eb4..b12d1a757a992 100644 --- a/test/dates/rounding.jl +++ b/test/dates/rounding.jl @@ -133,7 +133,7 @@ end @test_throws DomainError round(dt, Dates.Day, RoundToZero) @test round(dt, Dates.Day) == round(dt, Dates.Day, RoundNearestTiesUp) end -@testset "Rounding to invalid resolutions" begin +@testset "Rounding datetimes to invalid resolutions" begin dt = Dates.DateTime(2016, 2, 28, 12, 15) for p in [Dates.Year, Dates.Month, Dates.Week, Dates.Day, Dates.Hour] local p @@ -144,3 +144,77 @@ end end end end +@testset "Rounding for periods" begin + x = Dates.Second(172799) + @test floor(x, Dates.Week) == Dates.Week(0) + @test floor(x, Dates.Day) == Dates.Day(1) + @test floor(x, Dates.Hour) == Dates.Hour(47) + @test floor(x, Dates.Minute) == Dates.Minute(2879) + @test floor(x, Dates.Second) == Dates.Second(172799) + @test floor(x, Dates.Millisecond) == Dates.Millisecond(172799000) + @test ceil(x, Dates.Week) == Dates.Week(1) + @test ceil(x, Dates.Day) == Dates.Day(2) + @test ceil(x, Dates.Hour) == Dates.Hour(48) + @test ceil(x, Dates.Minute) == Dates.Minute(2880) + @test ceil(x, Dates.Second) == Dates.Second(172799) + @test ceil(x, Dates.Millisecond) == Dates.Millisecond(172799000) + @test round(x, Dates.Week) == Dates.Week(0) + @test round(x, Dates.Day) == Dates.Day(2) + @test round(x, Dates.Hour) == Dates.Hour(48) + @test round(x, Dates.Minute) == Dates.Minute(2880) + @test round(x, Dates.Second) == Dates.Second(172799) + @test round(x, Dates.Millisecond) == Dates.Millisecond(172799000) + + x = Dates.Nanosecond(2000999999) + @test floor(x, Dates.Second) == Dates.Second(2) + @test floor(x, Dates.Millisecond) == Dates.Millisecond(2000) + @test floor(x, Dates.Microsecond) == Dates.Microsecond(2000999) + @test floor(x, Dates.Nanosecond) == x + @test ceil(x, Dates.Second) == Dates.Second(3) + @test ceil(x, Dates.Millisecond) == Dates.Millisecond(2001) + @test ceil(x, Dates.Microsecond) == Dates.Microsecond(2001000) + @test ceil(x, Dates.Nanosecond) == x + @test round(x, Dates.Second) == Dates.Second(2) + @test round(x, Dates.Millisecond) == Dates.Millisecond(2001) + @test round(x, Dates.Microsecond) == Dates.Microsecond(2001000) + @test round(x, Dates.Nanosecond) == x +end +@testset "Rounding for periods that should not need rounding" begin + for x in [Dates.Week(3), Dates.Day(14), Dates.Second(604800)] + local x + for p in [Dates.Week, Dates.Day, Dates.Hour, Dates.Second, Dates.Millisecond, Dates.Microsecond, Dates.Nanosecond] + local p + @test floor(x, p) == p(x) + @test ceil(x, p) == p(x) + end + end +end +@testset "Various available RoundingModes for periods" begin + x = Dates.Hour(36) + @test round(x, Dates.Day, RoundNearestTiesUp) == Dates.Day(2) + @test round(x, Dates.Day, RoundUp) == Dates.Day(2) + @test round(x, Dates.Day, RoundDown) == Dates.Day(1) + @test_throws DomainError round(x, Dates.Day, RoundNearest) + @test_throws DomainError round(x, Dates.Day, RoundNearestTiesAway) + @test_throws DomainError round(x, Dates.Day, RoundToZero) + @test round(x, Dates.Day) == round(x, Dates.Day, RoundNearestTiesUp) +end +@testset "Rounding periods to invalid resolutions" begin + x = Dates.Hour(86399) + for p in [Dates.Week, Dates.Day, Dates.Hour, Dates.Second, Dates.Millisecond, Dates.Microsecond, Dates.Nanosecond] + local p + for v in [-1, 0] + @test_throws DomainError floor(x, p(v)) + @test_throws DomainError ceil(x, p(v)) + @test_throws DomainError round(x, p(v)) + end + end + for p in [Dates.Year, Dates.Month] + local p + for v in [-1, 0, 1] + @test_throws MethodError floor(x, p(v)) + @test_throws MethodError ceil(x, p(v)) + @test_throws DomainError round(x, p(v)) + end + end +end From b1ea7d716d0d13dcc54629eea64b5831bb36bd73 Mon Sep 17 00:00:00 2001 From: Yichao Yu Date: Thu, 2 Nov 2017 10:43:37 -0400 Subject: [PATCH 20/34] Remove errno.jl It is unused since 16d38127293b7ae2635ef7cc9e8e7aebcd1b978f --- base/errno.jl | 135 -------------------------------------------------- 1 file changed, 135 deletions(-) delete mode 100644 base/errno.jl diff --git a/base/errno.jl b/base/errno.jl deleted file mode 100644 index 19da1239109a6..0000000000000 --- a/base/errno.jl +++ /dev/null @@ -1,135 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -include("errno_h.jl") -export - E2BIG, - EACCES, - EADDRINUSE, - EADDRNOTAVAIL, - EADV, - EAFNOSUPPORT, - EAGAIN, - EALREADY, - EBADE, - EBADF, - EBADFD, - EBADMSG, - EBADR, - EBADRQC, - EBADSLT, - EBFONT, - EBUSY, - ECANCELED, - ECHILD, - ECHRNG, - ECOMM, - ECONNABORTED, - ECONNREFUSED, - ECONNRESET, - EDEADLK, - EDESTADDRREQ, - EDOM, - EDOTDOT, - EDQUOT, - EEXIST, - EFAULT, - EFBIG, - EHOSTDOWN, - EHOSTUNREACH, - EHWPOISON, - EIDRM, - EILSEQ, - EINPROGRESS, - EINTR, - EINVAL, - EIO, - EISCONN, - EISDIR, - EISNAM, - EKEYEXPIRED, - EKEYREJECTED, - EKEYREVOKED, - EL2HLT, - EL2NSYNC, - EL3HLT, - EL3RST, - ELIBACC, - ELIBBAD, - ELIBEXEC, - ELIBMAX, - ELIBSCN, - ELNRNG, - ELOOP, - EMEDIUMTYPE, - EMFILE, - EMLINK, - EMSGSIZE, - EMULTIHOP, - ENAMETOOLONG, - ENAVAIL, - ENETDOWN, - ENETRESET, - ENETUNREACH, - ENFILE, - ENOANO, - ENOBUFS, - ENOCSI, - ENODATA, - ENODEV, - ENOENT, - ENOEXEC, - ENOKEY, - ENOLCK, - ENOLINK, - ENOMEDIUM, - ENOMEM, - ENOMSG, - ENONET, - ENOPKG, - ENOPROTOOPT, - ENOSPC, - ENOSR, - ENOSTR, - ENOSYS, - ENOTBLK, - ENOTCONN, - ENOTDIR, - ENOTEMPTY, - ENOTNAM, - ENOTRECOVERABLE, - ENOTSOCK, - ENOTTY, - ENOTUNIQ, - ENXIO, - EOPNOTSUPP, - EOVERFLOW, - EOWNERDEAD, - EPERM, - EPFNOSUPPORT, - EPIPE, - EPROTO, - EPROTONOSUPPORT, - EPROTOTYPE, - ERANGE, - EREMCHG, - EREMOTE, - EREMOTEIO, - ERESTART, - ERFKILL, - EROFS, - ESHUTDOWN, - ESOCKTNOSUPPORT, - ESPIPE, - ESRCH, - ESRMNT, - ESTALE, - ESTRPIPE, - ETIME, - ETIMEDOUT, - ETOOMANYREFS, - ETXTBSY, - EUCLEAN, - EUNATCH, - EUSERS, - EXDEV, - EXFULL From 098176c959a80ccbada168b63decd481045c4c60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Thu, 2 Nov 2017 17:53:00 +0100 Subject: [PATCH 21/34] Clarify meaning of p in hexadecimal floating-point literals (#24376) --- doc/src/manual/integers-and-floating-point-numbers.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 75dfabf19a7f4..0934e19bd5e62 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -218,7 +218,8 @@ second argument is zero. ## Floating-Point Numbers -Literal floating-point numbers are represented in the standard formats: +Literal floating-point numbers are represented in the standard formats, using +[E-notation](https://en.wikipedia.org/wiki/Scientific_notation#E-notation) when necessary: ```jldoctest julia> 1.0 @@ -267,7 +268,8 @@ julia> typeof(ans) Float32 ``` -Hexadecimal floating-point literals are also valid, but only as [`Float64`](@ref) values: +Hexadecimal floating-point literals are also valid, but only as [`Float64`](@ref) values, +with `p` preceding the base-2 exponent: ```jldoctest julia> 0x1p0 From c67f7b5960b8193a41df7d1742e7dd007809cb37 Mon Sep 17 00:00:00 2001 From: Jane Herriman Date: Thu, 2 Nov 2017 13:50:25 -0700 Subject: [PATCH 22/34] Add doctests to complex.jl (#24429) --- base/complex.jl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/base/complex.jl b/base/complex.jl index 277ab796de933..7e9cfe55afd74 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -137,6 +137,18 @@ isone(z::Complex) = isone(real(z)) & iszero(imag(z)) complex(r, [i]) Convert real numbers or arrays to complex. `i` defaults to zero. + +# Examples +```jldoctest +julia> complex(7) +7 + 0im + +julia> complex([1, 2, 3]) +3-element Array{Complex{Int64},1}: + 1 + 0im + 2 + 0im + 3 + 0im +``` """ complex(z::Complex) = z complex(x::Real) = Complex(x) @@ -935,6 +947,12 @@ Returns the nearest integral value of the same type as the complex-valued `z` to breaking ties using the specified [`RoundingMode`](@ref)s. The first [`RoundingMode`](@ref) is used for rounding the real components while the second is used for rounding the imaginary components. + +# Example +```jldoctest +julia> round(3.14 + 4.5im) +3.0 + 4.0im +``` """ function round(z::Complex{<:AbstractFloat}, ::RoundingMode{MR}, ::RoundingMode{MI}) where {MR,MI} Complex(round(real(z), RoundingMode{MR}()), From 8782c36541cf91ce622ff2e5a413ff7b691705f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bogumi=C5=82=20Kami=C5=84ski?= Date: Thu, 2 Nov 2017 22:00:23 +0100 Subject: [PATCH 23/34] correct serialization of SubString{<:AbstractString} (#24275) --- base/serialize.jl | 4 ++-- test/serialize.jl | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/base/serialize.jl b/base/serialize.jl index ffdc66526caf7..53f01f2a51a73 100644 --- a/base/serialize.jl +++ b/base/serialize.jl @@ -302,9 +302,9 @@ function serialize(s::AbstractSerializer, ss::String) write(s.io, ss) end -function serialize(s::AbstractSerializer, ss::SubString{T}) where T<:AbstractString +function serialize(s::AbstractSerializer, ss::SubString{String}) # avoid saving a copy of the parent string, keeping the type of ss - serialize_any(s, convert(SubString{T}, convert(T,ss))) + serialize_any(s, SubString(String(ss))) end # Don't serialize the pointers diff --git a/test/serialize.jl b/test/serialize.jl index a9d0fc9ca14a2..bb87f96eb4f50 100644 --- a/test/serialize.jl +++ b/test/serialize.jl @@ -31,6 +31,24 @@ create_serialization_stream() do s @test data[end] == UInt8(Serializer.sertag(Symbol)) end +# SubString + +create_serialization_stream() do s + ss = Any[SubString("12345", 2, 4), + SubString(GenericString("12345"), 2, 4), + SubString(s"12345", 2, 4), + SubString(GenericString(s"12345"), 2, 4),] + for x in ss + serialize(s, x) + end + seek(s, 0) + for x in ss + y = deserialize(s) + @test x == y + @test typeof(x) == typeof(y) + end +end + # Boolean & Empty & Nothing create_serialization_stream() do s serialize(s, true) From a547d7331555b26c933d35d07c81f2ec1a7bb635 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Thu, 2 Nov 2017 17:03:40 -0400 Subject: [PATCH 24/34] fix #24305, stack overflow when intersecting sequence of big unions (#24314) --- src/subtype.c | 17 +++++++++++------ test/subtype.jl | 25 +++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/subtype.c b/src/subtype.c index 7b3f0061fb165..7c686967b46bf 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2092,11 +2092,15 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) e->Runions.depth = 0; e->Runions.more = 0; memset(e->Runions.stack, 0, sizeof(e->Runions.stack)); - int lastset = 0, niter = 0; + jl_value_t **is; + JL_GC_PUSHARGS(is, 2); + int lastset = 0, niter = 0, total_iter = 0; jl_value_t *ii = intersect(x, y, e, 0); while (e->Runions.more) { - if (e->emptiness_only && ii != jl_bottom_type) + if (e->emptiness_only && ii != jl_bottom_type) { + JL_GC_POP(); return ii; + } e->Runions.depth = 0; int set = e->Runions.more - 1; e->Runions.more = 0; @@ -2105,8 +2109,6 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) statestack_set(&e->Runions, i, 0); lastset = set; - jl_value_t **is; - JL_GC_PUSHARGS(is, 2); is[0] = ii; is[1] = intersect(x, y, e, 0); if (is[0] == jl_bottom_type) @@ -2118,10 +2120,13 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) ii = jl_type_union(is, 2); niter++; } - JL_GC_POP(); - if (niter > 3) + total_iter++; + if (niter > 3 || total_iter > 400000) { + JL_GC_POP(); return y; + } } + JL_GC_POP(); return ii; } diff --git a/test/subtype.jl b/test/subtype.jl index 10f7e4667ad5e..51fe6302ea2f0 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -926,6 +926,12 @@ function test_intersection() A = Tuple{Ref, Vararg{Any}} B = Tuple{Vararg{Union{Z,Ref,Void}}} where Z<:Union{Ref,Void} @test B <: _type_intersect(A, B) + # TODO: this would be a better version of that test: + #let T = _type_intersect(A, B) + # @test T <: A + # @test T <: B + # @test Tuple{Ref, Vararg{Union{Ref,Void}}} <: T + #end @testintersect(Tuple{Int,Any,Vararg{A}} where A>:Integer, Tuple{Any,Int,Vararg{A}} where A>:Integer, Tuple{Int,Int,Vararg{A}} where A>:Integer) @@ -1229,3 +1235,22 @@ struct A23764{T, N, S} <: AbstractArray{Union{T, S}, N}; end struct A23764_2{T, N, S} <: AbstractArray{Union{Ref{T}, S}, N}; end @test Tuple{A23764_2{T, 1, Void} where T} <: Tuple{AbstractArray{T,N}} where {T,N} @test Tuple{A23764_2{T, 1, Void} where T} <: Tuple{AbstractArray{T,N} where {T,N}} + +# issue #24305 +f24305(x) = [g24305(x) g24305(x) g24305(x) g24305(x); g24305(x) g24305(x) 0 0]; +@test_throws UndefVarError f24305(1) + +f1_24305(x,y,z) = x*y-z^2-1 +f2_24305(x,y,z) = x*y*z+y^2-x^2-2 +f3_24305(x,y,z) = exp(x)+z-exp(y)-3 +Fun_24305(x) = [ f1_24305(x[1],x[2],x[3]); f2_24305(x[1],x[2],x[3]); f3_24305(x[1],x[2],x[3]) ] +Jac_24305(x) = [ x[2] x[1] -2*x[3] ; x[2]*x[3]-2x[1] x[1]*x[3]+2x[2] x[1]*x[2] ; exp(x[1]) -exp(x[2]) 1 ] + +x_24305 = ones(3) + +for it = 1:5 + h = - \(Jac_24305(x_24305), Fun_24305(x_24305)) + global x_24305 = x_24305 + h +end + +@test round.(x_24305, 2) == [1.78, 1.42, 1.24] From c7344401545938eebdf7620c77567027d1c9edd7 Mon Sep 17 00:00:00 2001 From: Jan Weidner Date: Thu, 2 Nov 2017 22:29:02 +0100 Subject: [PATCH 25/34] document maxdepth keyword for dump (#24288) (#24378) --- base/show.jl | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/base/show.jl b/base/show.jl index 1c476f814d987..aeb5d3bc73c01 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1501,18 +1501,60 @@ function dumpsubtypes(io::IO, x::DataType, m::Module, n::Int, indent) end +const DUMP_DEFAULT_MAXDEPTH = 8 # For abstract types, use _dumptype only if it's a form that will be called # interactively. -dump(io::IO, x::DataType; maxdepth=8) = ((x.abstract ? dumptype : dump)(io, x, maxdepth, ""); println(io)) +dump(io::IO, x::DataType; maxdepth=DUMP_DEFAULT_MAXDEPTH) = ((x.abstract ? dumptype : dump)(io, x, maxdepth, ""); println(io)) -dump(io::IO, arg; maxdepth=8) = (dump(io, arg, maxdepth, ""); println(io)) +dump(io::IO, arg; maxdepth=DUMP_DEFAULT_MAXDEPTH) = (dump(io, arg, maxdepth, ""); println(io)) """ - dump(x) + dump(x; maxdepth=$DUMP_DEFAULT_MAXDEPTH) Show every part of the representation of a value. +```jldoctest +julia> struct MyStruct + x + y + end + +julia> x = MyStruct(1, (2,3)); + +julia> dump(x) +MyStruct + x: Int64 1 + y: Tuple{Int64,Int64} + 1: Int64 2 + 2: Int64 3 +``` +Nested data structures are truncated at `maxdepth`. +```jldoctest +julia> struct DeeplyNested + xs::Vector{DeeplyNested} + end; + +julia> x = DeeplyNested([]); + +julia> push!(x.xs, x); + +julia> dump(x) +DeeplyNested + xs: Array{DeeplyNested}((1,)) + 1: DeeplyNested + xs: Array{DeeplyNested}((1,)) + 1: DeeplyNested + xs: Array{DeeplyNested}((1,)) + 1: DeeplyNested + xs: Array{DeeplyNested}((1,)) + 1: DeeplyNested + +julia> dump(x, maxdepth=2) +DeeplyNested + xs: Array{DeeplyNested}((1,)) + 1: DeeplyNested +``` """ -dump(arg; maxdepth=8) = dump(IOContext(STDOUT::IO, :limit => true), arg; maxdepth=maxdepth) +dump(arg; maxdepth=DUMP_DEFAULT_MAXDEPTH) = dump(IOContext(STDOUT::IO, :limit => true), arg; maxdepth=maxdepth) """ From c7d639186461becce64de1a1905d51108d9c2852 Mon Sep 17 00:00:00 2001 From: Matt Bauman Date: Thu, 2 Nov 2017 16:30:29 -0500 Subject: [PATCH 26/34] Test IntSet overflow behavior with large unsigned ints (#24366) * add tests (cherry picked from commit e6390dc67659e7ba09f7a5c77aeaaaa3045313a0) --- test/bitset.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/bitset.jl b/test/bitset.jl index 212c452e16833..a7b4e3e061f29 100644 --- a/test/bitset.jl +++ b/test/bitset.jl @@ -298,6 +298,13 @@ end @test pop!(s, 99, 0) === 99 end +@testset "unsigned overflow" begin + @test BitSet(UInt8(2^8-1)) == BitSet(2^8-1) + @test [x for x in BitSet(UInt8(2^8-1))] == [UInt8(2^8-1)] + @test BitSet(UInt16(2^16-1)) == BitSet(2^16-1) + @test [x for x in BitSet(UInt16(2^16-1))] == [UInt16(2^16-1)] +end + @testset "order" begin a = rand(1:1000, 100) s = BitSet(a) From 06d0016777460a6681d3b62659037259cecc0b3a Mon Sep 17 00:00:00 2001 From: Tim Holy Date: Sat, 21 Oct 2017 11:38:19 -0500 Subject: [PATCH 27/34] Require stepsize in all Dates AbstractRange constructors --- NEWS.md | 2 + base/dates/ranges.jl | 8 --- base/deprecated.jl | 11 ++++ test/dates/adjusters.jl | 38 ++++++------ test/dates/io.jl | 2 +- test/dates/ranges.jl | 129 ++++++++++++++++++++-------------------- test/ranges.jl | 8 +-- 7 files changed, 103 insertions(+), 95 deletions(-) diff --git a/NEWS.md b/NEWS.md index 788374aa38244..1126cb2d11796 100644 --- a/NEWS.md +++ b/NEWS.md @@ -608,6 +608,8 @@ Deprecated or removed * `num2hex` and `hex2num` have been deprecated in favor of `reinterpret` combined with `parse`/`hex` ([#22088]). + * `a:b` is deprecated for constructing a `StepRange` when `a` and `b` have physical units + (Dates and Times). Use `a:s:b`, where `s = Dates.Day(1)` or `s = Dates.Second(1)`. Command-line option changes --------------------------- diff --git a/base/dates/ranges.jl b/base/dates/ranges.jl index 2a2795102757b..ba9cfc88d3b41 100644 --- a/base/dates/ranges.jl +++ b/base/dates/ranges.jl @@ -2,14 +2,6 @@ # Date/DateTime Ranges -# Override default step; otherwise it would be Millisecond(1) -Base.colon(start::T, stop::T) where {T<:DateTime} = StepRange(start, Day(1), stop) -Base.colon(start::T, stop::T) where {T<:Date} = StepRange(start, Day(1), stop) -Base.colon(start::T, stop::T) where {T<:Time} = StepRange(start, Second(1), stop) - -Base.range(start::DateTime, len::Integer) = range(start, Day(1), len) -Base.range(start::Date, len::Integer) = range(start, Day(1), len) - (::Type{StepRange{<:Dates.DatePeriod,<:Real}})(start, step, stop) = throw(ArgumentError("must specify step as a Period when constructing Dates ranges")) diff --git a/base/deprecated.jl b/base/deprecated.jl index 2049001ebb4a6..ee81e772f9360 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -2073,6 +2073,17 @@ end # issue #24167 @deprecate EnvHash EnvDict +# #24258 +# Physical units define an equivalence class: there is no such thing as a step of "1" (is +# it one day or one second or one nanosecond?). So require the user to specify the step +# (in physical units). +@deprecate colon(start::T, stop::T) where {T<:DateTime} start:Dates.Day(1):stop +@deprecate colon(start::T, stop::T) where {T<:Date} start:Dates.Day(1):stop +@deprecate colon(start::T, stop::T) where {T<:Dates.Time} start:Dates.Second(1):stop + +@deprecate range(start::DateTime, len::Integer) range(start, Dates.Day(1), len) +@deprecate range(start::Date, len::Integer) range(start, Dates.Day(1), len) + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/test/dates/adjusters.jl b/test/dates/adjusters.jl index 5329e788997af..fbbcd314f57c9 100644 --- a/test/dates/adjusters.jl +++ b/test/dates/adjusters.jl @@ -317,32 +317,32 @@ dt = Dates.Date(2014, 5, 21) # filter (was recur) startdate = Dates.Date(2014, 1, 1) stopdate = Dates.Date(2014, 2, 1) -@test length(filter(x->true, startdate:stopdate)) == 32 +@test length(filter(x->true, startdate:Dates.Day(1):stopdate)) == 32 @test length(filter(x->true, stopdate:Dates.Day(-1):startdate)) == 32 Januarymondays2014 = [Dates.Date(2014, 1, 6), Dates.Date(2014, 1, 13), Dates.Date(2014, 1, 20), Dates.Date(2014, 1, 27)] -@test filter(Dates.ismonday, startdate:stopdate) == Januarymondays2014 +@test filter(Dates.ismonday, startdate:Dates.Day(1):stopdate) == Januarymondays2014 -@test_throws MethodError filter((x, y)->x + y, Dates.Date(2013):Dates.Date(2014)) +@test_throws MethodError filter((x, y)->x + y, Dates.Date(2013):Dates.Day(1):Dates.Date(2014)) @test_throws MethodError Dates.DateFunction((x, y)->x + y, Date(0)) @test_throws ArgumentError Dates.DateFunction((dt)->2, Date(0)) -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 2))) == 32 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 1))) == 1 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 2))) == 2 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 3))) == 3 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 4))) == 4 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 5))) == 5 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 6))) == 6 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 7))) == 7 -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2013, 1, 8))) == 8 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 2))) == 32 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 1))) == 1 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 2))) == 2 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 3))) == 3 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 4))) == 4 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 5))) == 5 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 6))) == 6 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 7))) == 7 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2013, 1, 8))) == 8 @test length(filter(x->true, Dates.Date(2013):Dates.Month(1):Dates.Date(2013, 1, 1))) == 1 @test length(filter(x->true, Dates.Date(2013):Dates.Day(-1):Dates.Date(2012, 1, 1))) == 367 # Empty range -@test length(filter(x->true, Dates.Date(2013):Dates.Date(2012, 1, 1))) == 0 +@test length(filter(x->true, Dates.Date(2013):Dates.Day(1):Dates.Date(2012, 1, 1))) == 0 # All leap days in 20th century -@test length(filter(Dates.Date(1900):Dates.Date(2000)) do x +@test length(filter(Dates.Date(1900):Dates.Day(1):Dates.Date(2000)) do x Dates.month(x) == Dates.Feb && Dates.day(x) == 29 end) == 24 @@ -362,7 +362,7 @@ end == Dates.Date(2014, 11, 27) end == Dates.Date(2013, 11, 28) # Pittsburgh street cleaning -dr = Dates.Date(2014):Dates.Date(2015) +dr = Dates.Date(2014):Dates.Day(1):Dates.Date(2015) @test length(filter(dr) do x Dates.dayofweek(x) == Dates.Tue && Dates.April < Dates.month(x) < Dates.Nov && @@ -437,7 +437,7 @@ const OBSERVEDHOLIDAYS = x->begin end end -observed = filter(OBSERVEDHOLIDAYS, Dates.Date(1999):Dates.Date(2000)) +observed = filter(OBSERVEDHOLIDAYS, Dates.Date(1999):Dates.Day(1):Dates.Date(2000)) @test length(observed) == 11 @test observed[10] == Dates.Date(1999, 12, 24) @test observed[11] == Dates.Date(1999, 12, 31) @@ -445,14 +445,14 @@ observed = filter(OBSERVEDHOLIDAYS, Dates.Date(1999):Dates.Date(2000)) # Get all business/working days of 2014 # Since we have already defined observed holidays, # we just look at weekend days and negate the result -@test length(filter(Dates.Date(2014):Dates.Date(2015)) do x +@test length(filter(Dates.Date(2014):Dates.Day(1):Dates.Date(2015)) do x !(OBSERVEDHOLIDAYS(x) || Dates.dayofweek(x) > 5) end) == 251 # First day of the next month for each day of 2014 @test length([Dates.firstdayofmonth(i + Dates.Month(1)) - for i in Dates.Date(2014):Dates.Date(2014, 12, 31)]) == 365 + for i in Dates.Date(2014):Dates.Day(1):Dates.Date(2014, 12, 31)]) == 365 # From those goofy email forwards claiming a "special, lucky month" # that has 5 Fridays, 5 Saturdays, and 5 Sundays and that it only @@ -469,7 +469,7 @@ end) == 15 # On average, there's one of those months every year r = Dates.Time(x->Dates.second(x) == 5, 1) @test r == Dates.Time(1, 0, 5) -r = filter(x->Dates.second(x) == 5, Dates.Time(0):Dates.Time(10)) +r = filter(x->Dates.second(x) == 5, Dates.Time(0):Dates.Second(1):Dates.Time(10)) @test length(r) == 600 @test first(r) == Dates.Time(0, 0, 5) @test last(r) == Dates.Time(9, 59, 5) diff --git a/test/dates/io.jl b/test/dates/io.jl index 808625042e092..e9059e1b590ff 100644 --- a/test/dates/io.jl +++ b/test/dates/io.jl @@ -327,7 +327,7 @@ end @testset "formerly vectorized Date/DateTime/format methods" begin dr = ["2000-01-01", "2000-01-02", "2000-01-03", "2000-01-04", "2000-01-05", "2000-01-06", "2000-01-07", "2000-01-08", "2000-01-09", "2000-01-10"] - dr2 = [Dates.Date(2000) : Dates.Date(2000, 1, 10);] + dr2 = [Dates.Date(2000) : Dates.Day(1) : Dates.Date(2000, 1, 10);] @test Dates.Date.(dr) == dr2 @test Dates.Date.(dr, dateformat"yyyy-mm-dd") == dr2 @test Dates.DateTime.(dr) == Dates.DateTime.(dr2) diff --git a/test/dates/ranges.jl b/test/dates/ranges.jl index 3ba38ec64dd07..576f5efd7e7de 100644 --- a/test/dates/ranges.jl +++ b/test/dates/ranges.jl @@ -231,20 +231,26 @@ let end end +# Dates are physical units, and ranges should require an explicit step. +# See #19896 and https://discourse.julialang.org/t/type-restriction-on-unitrange/6557/12 +if VERSION >= v"1.0.0" + @test_throws MethodError Dates.DateTime(2013, 1, 1):Dates.DateTime(2013, 2, 1) +end + # All the range representations we want to test # Date ranges -dr = Dates.DateTime(2013, 1, 1):Dates.DateTime(2013, 2, 1) -dr1 = Dates.DateTime(2013, 1, 1):Dates.DateTime(2013, 1, 1) -dr2 = Dates.DateTime(2013, 1, 1):Dates.DateTime(2012, 2, 1) # empty range +dr = Dates.DateTime(2013, 1, 1):Dates.Day(1):Dates.DateTime(2013, 2, 1) +dr1 = Dates.DateTime(2013, 1, 1):Dates.Day(1):Dates.DateTime(2013, 1, 1) +dr2 = Dates.DateTime(2013, 1, 1):Dates.Day(1):Dates.DateTime(2012, 2, 1) # empty range dr3 = Dates.DateTime(2013, 1, 1):Dates.Day(-1):Dates.DateTime(2012) # negative step # Big ranges -dr4 = Dates.DateTime(0):Dates.DateTime(20000, 1, 1) -dr5 = Dates.DateTime(0):Dates.DateTime(200000, 1, 1) -dr6 = Dates.DateTime(0):Dates.DateTime(2000000, 1, 1) -dr7 = Dates.DateTime(0):Dates.DateTime(20000000, 1, 1) -dr8 = Dates.DateTime(0):Dates.DateTime(200000000, 1, 1) -dr9 = typemin(Dates.DateTime):typemax(Dates.DateTime) -# Non-default steps +dr4 = Dates.DateTime(0):Dates.Day(1):Dates.DateTime(20000, 1, 1) +dr5 = Dates.DateTime(0):Dates.Day(1):Dates.DateTime(200000, 1, 1) +dr6 = Dates.DateTime(0):Dates.Day(1):Dates.DateTime(2000000, 1, 1) +dr7 = Dates.DateTime(0):Dates.Day(1):Dates.DateTime(20000000, 1, 1) +dr8 = Dates.DateTime(0):Dates.Day(1):Dates.DateTime(200000000, 1, 1) +dr9 = typemin(Dates.DateTime):Dates.Day(1):typemax(Dates.DateTime) +# Other steps dr10 = typemax(Dates.DateTime):Dates.Day(-1):typemin(Dates.DateTime) dr11 = typemin(Dates.DateTime):Dates.Week(1):typemax(Dates.DateTime) @@ -280,8 +286,8 @@ end @test_throws MethodError dr .+ 1 a = Dates.DateTime(2013, 1, 1) b = Dates.DateTime(2013, 2, 1) -@test map!(x->x + Dates.Day(1), Array{Dates.DateTime}(32), dr) == [(a + Dates.Day(1)):(b + Dates.Day(1));] -@test map(x->x + Dates.Day(1), dr) == [(a + Dates.Day(1)):(b + Dates.Day(1));] +@test map!(x->x + Dates.Day(1), Array{Dates.DateTime}(32), dr) == [(a + Dates.Day(1)):Dates.Day(1):(b + Dates.Day(1));] +@test map(x->x + Dates.Day(1), dr) == [(a + Dates.Day(1)):Dates.Day(1):(b + Dates.Day(1));] @test map(x->a in x, drs[1:4]) == [true, true, false, true] @test a in dr @@ -295,7 +301,7 @@ b = Dates.DateTime(2013, 2, 1) @test all(x->step(x) < zero(step(x)) ? issorted(reverse(x)) : issorted(x), drs) @test length(b:Dates.Day(-1):a) == 32 -@test length(b:a) == 0 +@test length(b:Dates.Day(1):a) == 0 @test length(b:Dates.Day(1):a) == 0 @test length(a:Dates.Day(2):b) == 16 @test last(a:Dates.Day(2):b) == Dates.DateTime(2013, 1, 31) @@ -303,31 +309,31 @@ b = Dates.DateTime(2013, 2, 1) @test last(a:Dates.Day(7):b) == Dates.DateTime(2013, 1, 29) @test length(a:Dates.Day(32):b) == 1 @test last(a:Dates.Day(32):b) == Dates.DateTime(2013, 1, 1) -@test (a:b)[1] == Dates.DateTime(2013, 1, 1) -@test (a:b)[2] == Dates.DateTime(2013, 1, 2) -@test (a:b)[7] == Dates.DateTime(2013, 1, 7) -@test (a:b)[end] == b -@test first(a:Dates.DateTime(20000, 1, 1)) == a -@test first(a:Dates.DateTime(200000, 1, 1)) == a -@test first(a:Dates.DateTime(2000000, 1, 1)) == a -@test first(a:Dates.DateTime(20000000, 1, 1)) == a -@test first(a:Dates.DateTime(200000000, 1, 1)) == a -@test first(a:typemax(Dates.DateTime)) == a -@test first(typemin(Dates.DateTime):typemax(Dates.DateTime)) == typemin(Dates.DateTime) +@test (a:Dates.Day(1):b)[1] == Dates.DateTime(2013, 1, 1) +@test (a:Dates.Day(1):b)[2] == Dates.DateTime(2013, 1, 2) +@test (a:Dates.Day(1):b)[7] == Dates.DateTime(2013, 1, 7) +@test (a:Dates.Day(1):b)[end] == b +@test first(a:Dates.Day(1):Dates.DateTime(20000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.DateTime(200000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.DateTime(2000000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.DateTime(20000000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.DateTime(200000000, 1, 1)) == a +@test first(a:Dates.Day(1):typemax(Dates.DateTime)) == a +@test first(typemin(Dates.DateTime):Dates.Day(1):typemax(Dates.DateTime)) == typemin(Dates.DateTime) # Date ranges -dr = Dates.Date(2013, 1, 1):Dates.Date(2013, 2, 1) -dr1 = Dates.Date(2013, 1, 1):Dates.Date(2013, 1, 1) -dr2 = Dates.Date(2013, 1, 1):Dates.Date(2012, 2, 1) # empty range +dr = Dates.Date(2013, 1, 1):Dates.Day(1):Dates.Date(2013, 2, 1) +dr1 = Dates.Date(2013, 1, 1):Dates.Day(1):Dates.Date(2013, 1, 1) +dr2 = Dates.Date(2013, 1, 1):Dates.Day(1):Dates.Date(2012, 2, 1) # empty range dr3 = Dates.Date(2013, 1, 1):Dates.Day(-1):Dates.Date(2012, 1, 1) # negative step # Big ranges -dr4 = Dates.Date(0):Dates.Date(20000, 1, 1) -dr5 = Dates.Date(0):Dates.Date(200000, 1, 1) -dr6 = Dates.Date(0):Dates.Date(2000000, 1, 1) -dr7 = Dates.Date(0):Dates.Date(20000000, 1, 1) -dr8 = Dates.Date(0):Dates.Date(200000000, 1, 1) -dr9 = typemin(Dates.Date):typemax(Dates.Date) -# Non-default steps +dr4 = Dates.Date(0):Dates.Day(1):Dates.Date(20000, 1, 1) +dr5 = Dates.Date(0):Dates.Day(1):Dates.Date(200000, 1, 1) +dr6 = Dates.Date(0):Dates.Day(1):Dates.Date(2000000, 1, 1) +dr7 = Dates.Date(0):Dates.Day(1):Dates.Date(20000000, 1, 1) +dr8 = Dates.Date(0):Dates.Day(1):Dates.Date(200000000, 1, 1) +dr9 = typemin(Dates.Date):Dates.Day(1):typemax(Dates.Date) +# Other steps dr10 = typemax(Dates.Date):Dates.Day(-1):typemin(Dates.Date) dr11 = typemin(Dates.Date):Dates.Week(1):typemax(Dates.Date) dr12 = typemin(Dates.Date):Dates.Month(1):typemax(Dates.Date) @@ -358,8 +364,8 @@ end @test_throws MethodError dr .+ 1 a = Dates.Date(2013, 1, 1) b = Dates.Date(2013, 2, 1) -@test map!(x->x + Dates.Day(1), Array{Dates.Date}(32), dr) == [(a + Dates.Day(1)):(b + Dates.Day(1));] -@test map(x->x + Dates.Day(1), dr) == [(a + Dates.Day(1)):(b + Dates.Day(1));] +@test map!(x->x + Dates.Day(1), Array{Dates.Date}(32), dr) == [(a + Dates.Day(1)):Dates.Day(1):(b + Dates.Day(1));] +@test map(x->x + Dates.Day(1), dr) == [(a + Dates.Day(1)):Dates.Day(1):(b + Dates.Day(1));] @test map(x->a in x, drs[1:4]) == [true, true, false, true] @test a in dr @@ -373,7 +379,6 @@ b = Dates.Date(2013, 2, 1) @test all(x->step(x) < zero(step(x)) ? issorted(reverse(x)) : issorted(x), drs) @test length(b:Dates.Day(-1):a) == 32 -@test length(b:a) == 0 @test length(b:Dates.Day(1):a) == 0 @test length(a:Dates.Day(2):b) == 16 @test last(a:Dates.Day(2):b) == Dates.Date(2013, 1, 31) @@ -381,19 +386,18 @@ b = Dates.Date(2013, 2, 1) @test last(a:Dates.Day(7):b) == Dates.Date(2013, 1, 29) @test length(a:Dates.Day(32):b) == 1 @test last(a:Dates.Day(32):b) == Dates.Date(2013, 1, 1) -@test (a:b)[1] == Dates.Date(2013, 1, 1) -@test (a:b)[2] == Dates.Date(2013, 1, 2) -@test (a:b)[7] == Dates.Date(2013, 1, 7) -@test (a:b)[end] == b -@test first(a:Dates.Date(20000, 1, 1)) == a -@test first(a:Dates.Date(200000, 1, 1)) == a -@test first(a:Dates.Date(2000000, 1, 1)) == a -@test first(a:Dates.Date(20000000, 1, 1)) == a -@test first(a:Dates.Date(200000000, 1, 1)) == a -@test first(a:typemax(Dates.Date)) == a -@test first(typemin(Dates.Date):typemax(Dates.Date)) == typemin(Dates.Date) - -# Non-default step sizes +@test (a:Dates.Day(1):b)[1] == Dates.Date(2013, 1, 1) +@test (a:Dates.Day(1):b)[2] == Dates.Date(2013, 1, 2) +@test (a:Dates.Day(1):b)[7] == Dates.Date(2013, 1, 7) +@test (a:Dates.Day(1):b)[end] == b +@test first(a:Dates.Day(1):Dates.Date(20000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.Date(200000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.Date(2000000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.Date(20000000, 1, 1)) == a +@test first(a:Dates.Day(1):Dates.Date(200000000, 1, 1)) == a +@test first(a:Dates.Day(1):typemax(Dates.Date)) == a +@test first(typemin(Dates.Date):Dates.Day(1):typemax(Dates.Date)) == typemin(Dates.Date) + @test length(typemin(Dates.Date):Dates.Week(1):typemax(Dates.Date)) == 26351950414948059 # Big Month/Year ranges @test length(typemin(Dates.Date):Dates.Month(1):typemax(Dates.Date)) == 6060531933867600 @@ -414,17 +418,16 @@ c = Dates.Date(2013, 6, 1) @test [a:Dates.Month(2):Dates.Date(2013, 1, 2);] == [a] @test [c:Dates.Month(-1):a;] == reverse([a:Dates.Month(1):c;]) -@test length(range(Date(2000), 366)) == 366 +@test length(range(Date(2000), Dates.Day(1), 366)) == 366 let n = 100000 local a = Dates.Date(2000) for i = 1:n - @test length(range(a, i)) == i + @test length(range(a, Dates.Day(1), i)) == i end return a + Dates.Day(n) end -# Custom definition to override default step of DateTime ranges -@test typeof(step(Dates.DateTime(2000):Dates.DateTime(2001))) == Dates.Day +@test typeof(step(Dates.DateTime(2000):Dates.Day(1):Dates.DateTime(2001))) == Dates.Day a = Dates.Date(2013, 1, 1) b = Dates.Date(2013, 2, 1) @@ -492,10 +495,10 @@ end @test_throws OverflowError length(typemin(Dates.Year):Dates.Year(1):typemax(Dates.Year)) @test_throws MethodError Dates.Date(0):Dates.DateTime(2000) @test_throws MethodError Dates.Date(0):Dates.Year(10) -@test length(range(Dates.Date(2000), 366)) == 366 -@test last(range(Dates.Date(2000), 366)) == Dates.Date(2000, 12, 31) -@test last(range(Dates.Date(2001), 365)) == Dates.Date(2001, 12, 31) -@test last(range(Dates.Date(2000), 367)) == last(range(Dates.Date(2000), Dates.Month(12), 2)) == last(range(Dates.Date(2000), Dates.Year(1), 2)) +@test length(range(Dates.Date(2000), Dates.Day(1), 366)) == 366 +@test last(range(Dates.Date(2000), Dates.Day(1), 366)) == Dates.Date(2000, 12, 31) +@test last(range(Dates.Date(2001), Dates.Day(1), 365)) == Dates.Date(2001, 12, 31) +@test last(range(Dates.Date(2000), Dates.Day(1), 367)) == last(range(Dates.Date(2000), Dates.Month(12), 2)) == last(range(Dates.Date(2000), Dates.Year(1), 2)) @test last(range(Dates.DateTime(2000), Dates.Day(366), 2)) == last(range(Dates.DateTime(2000), Dates.Hour(8784), 2)) # Issue 5 @@ -509,14 +512,14 @@ let d = Dates.Day(1) end # Time ranges -dr = Dates.Time(23, 1, 1):Dates.Time(23, 2, 1) -dr1 = Dates.Time(23, 1, 1):Dates.Time(23, 1, 1) -dr2 = Dates.Time(23, 1, 1):Dates.Time(22, 2, 1) # empty range +dr = Dates.Time(23, 1, 1):Dates.Second(1):Dates.Time(23, 2, 1) +dr1 = Dates.Time(23, 1, 1):Dates.Second(1):Dates.Time(23, 1, 1) +dr2 = Dates.Time(23, 1, 1):Dates.Second(1):Dates.Time(22, 2, 1) # empty range dr3 = Dates.Time(23, 1, 1):Dates.Minute(-1):Dates.Time(22, 1, 1) # negative step # Big ranges -dr8 = typemin(Dates.Time):typemax(Dates.Time) +dr8 = typemin(Dates.Time):Dates.Second(1):typemax(Dates.Time) dr9 = typemin(Dates.Time):Dates.Nanosecond(1):typemax(Dates.Time) -# Non - default steps +# Other steps dr10 = typemax(Dates.Time):Dates.Microsecond(-1):typemin(Dates.Time) dr11 = typemin(Dates.Time):Dates.Millisecond(1):typemax(Dates.Time) dr12 = typemin(Dates.Time):Dates.Minute(1):typemax(Dates.Time) diff --git a/test/ranges.jl b/test/ranges.jl index dde41fcb5a8f8..c665fe920676a 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -369,10 +369,10 @@ end @test !(π in 1.0:3.0) @test !("a" in 1:3) @test !("a" in 1.0:3.0) - @test !(1 in Date(2017, 01, 01):Date(2017, 01, 05)) - @test !(Complex(1, 0) in Date(2017, 01, 01):Date(2017, 01, 05)) - @test !(π in Date(2017, 01, 01):Date(2017, 01, 05)) - @test !("a" in Date(2017, 01, 01):Date(2017, 01, 05)) + @test !(1 in Date(2017, 01, 01):Dates.Day(1):Date(2017, 01, 05)) + @test !(Complex(1, 0) in Date(2017, 01, 01):Dates.Day(1):Date(2017, 01, 05)) + @test !(π in Date(2017, 01, 01):Dates.Day(1):Date(2017, 01, 05)) + @test !("a" in Date(2017, 01, 01):Dates.Day(1):Date(2017, 01, 05)) end end @testset "indexing range with empty range (#4309)" begin From 7a4ca9de1e8efc57538f7bad603532ff79176567 Mon Sep 17 00:00:00 2001 From: Andreas Noack Date: Fri, 3 Nov 2017 09:33:25 +0100 Subject: [PATCH 28/34] Wrap cholmod tests in testsets to avoid global variables (#24446) * Wrap cholmod tests in testsets to avoid global variables * Wrap the body of a testset in lapack tests in a guardsrand to avoid intermittent errors --- test/linalg/lapack.jl | 30 +- test/sparse/cholmod.jl | 866 +++++++++++++++++++++-------------------- 2 files changed, 463 insertions(+), 433 deletions(-) diff --git a/test/linalg/lapack.jl b/test/linalg/lapack.jl index 2f642fa6c58cb..63cb120a2a710 100644 --- a/test/linalg/lapack.jl +++ b/test/linalg/lapack.jl @@ -447,20 +447,22 @@ end @testset "hesv" begin @testset for elty in (Complex64, Complex128) - A = rand(elty,10,10) - A = A + A' #hermitian! - b = rand(elty,10) - c = A \ b - b,A = LAPACK.hesv!('U',A,b) - @test b ≈ c - @test_throws DimensionMismatch LAPACK.hesv!('U',A,rand(elty,11)) - A = rand(elty,10,10) - A = A + A' #hermitian! - b = rand(elty,10) - c = A \ b - b,A = LAPACK.hesv_rook!('U',A,b) - @test b ≈ c - @test_throws DimensionMismatch LAPACK.hesv_rook!('U',A,rand(elty,11)) + guardsrand(935) do + A = rand(elty,10,10) + A = A + A' #hermitian! + b = rand(elty,10) + c = A \ b + b,A = LAPACK.hesv!('U',A,b) + @test b ≈ c + @test_throws DimensionMismatch LAPACK.hesv!('U',A,rand(elty,11)) + A = rand(elty,10,10) + A = A + A' #hermitian! + b = rand(elty,10) + c = A \ b + b,A = LAPACK.hesv_rook!('U',A,b) + @test b ≈ c + @test_throws DimensionMismatch LAPACK.hesv_rook!('U',A,rand(elty,11)) + end end end diff --git a/test/sparse/cholmod.jl b/test/sparse/cholmod.jl index 48744801989be..256732c71c308 100644 --- a/test/sparse/cholmod.jl +++ b/test/sparse/cholmod.jl @@ -1,11 +1,13 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -srand(123) - using Base.SparseArrays.CHOLMOD using DelimitedFiles +using Test + +# CHOLMOD tests +srand(123) -# based on deps/SuiteSparse-4.0.2/CHOLMOD/Demo/ +@testset "based on deps/SuiteSparse-4.0.2/CHOLMOD/Demo/" begin # chm_rdsp(joinpath(JULIA_HOME, "../../deps/SuiteSparse-4.0.2/CHOLMOD/Demo/Matrix/bcsstk01.tri")) # because the file may not exist in binary distributions and when a system suitesparse library @@ -36,120 +38,128 @@ using DelimitedFiles ## residual 1.3e-19 (|Ax-b|/(|A||x|+|b|)) after iterative refinement ## rcond 9.5e-06 -A = CHOLMOD.Sparse(48, 48, - CHOLMOD.SuiteSparse_long[0,1,2,3,6,9,12,15,18,20,25,30,34,36,39,43,47,52,58, - 62,67,71,77,84,90,93,95,98,103,106,110,115,119,123,130,136,142,146,150,155, - 161,167,174,182,189,197,207,215,224], # zero-based column pointers - CHOLMOD.SuiteSparse_long[0,1,2,1,2,3,0,2,4,0,1,5,0,4,6,1,3,7,2,8,1,3,7,8,9, - 0,4,6,8,10,5,6,7,11,6,12,7,11,13,8,10,13,14,9,13,14,15,8,10,12,14,16,7,11, - 12,13,16,17,0,12,16,18,1,5,13,15,19,2,4,14,20,3,13,15,19,20,21,2,4,12,16,18, - 20,22,1,5,17,18,19,23,0,5,24,1,25,2,3,26,2,3,25,26,27,4,24,28,0,5,24,29,6, - 11,24,28,30,7,25,27,31,8,9,26,32,8,9,25,27,31,32,33,10,24,28,30,32,34,6,11, - 29,30,31,35,12,17,30,36,13,31,35,37,14,15,32,34,38,14,15,33,37,38,39,16,32, - 34,36,38,40,12,17,31,35,36,37,41,12,16,17,18,23,36,40,42,13,14,15,19,37,39, - 43,13,14,15,20,21,38,43,44,13,14,15,20,21,37,39,43,44,45,12,16,17,22,36,40, - 42,46,12,16,17,18,23,41,42,46,47], - [2.83226851852e6,1.63544753086e6,1.72436728395e6,-2.0e6,-2.08333333333e6, - 1.00333333333e9,1.0e6,-2.77777777778e6,1.0675e9,2.08333333333e6, - 5.55555555555e6,1.53533333333e9,-3333.33333333,-1.0e6,2.83226851852e6, - -6666.66666667,2.0e6,1.63544753086e6,-1.68e6,1.72436728395e6,-2.0e6,4.0e8, - 2.0e6,-2.08333333333e6,1.00333333333e9,1.0e6,2.0e8,-1.0e6,-2.77777777778e6, - 1.0675e9,-2.0e6,2.08333333333e6,5.55555555555e6,1.53533333333e9,-2.8e6, - 2.8360994695e6,-30864.1975309,-5.55555555555e6,1.76741074446e6, - -15432.0987654,2.77777777778e6,517922.131816,3.89003806848e6, - -3.33333333333e6,4.29857058902e6,-2.6349902747e6,1.97572063531e9, - -2.77777777778e6,3.33333333333e8,-2.14928529451e6,2.77777777778e6, - 1.52734651547e9,5.55555555555e6,6.66666666667e8,2.35916180402e6, - -5.55555555555e6,-1.09779731332e8,1.56411143711e9,-2.8e6,-3333.33333333, - 1.0e6,2.83226851852e6,-30864.1975309,-5.55555555555e6,-6666.66666667, - -2.0e6,1.63544753086e6,-15432.0987654,2.77777777778e6,-1.68e6, - 1.72436728395e6,-3.33333333333e6,2.0e6,4.0e8,-2.0e6,-2.08333333333e6, - 1.00333333333e9,-2.77777777778e6,3.33333333333e8,-1.0e6,2.0e8,1.0e6, - 2.77777777778e6,1.0675e9,5.55555555555e6,6.66666666667e8,-2.0e6, - 2.08333333333e6,-5.55555555555e6,1.53533333333e9,-28935.1851852, - -2.08333333333e6,60879.6296296,-1.59791666667e6,3.37291666667e6, - -28935.1851852,2.08333333333e6,2.41171296296e6,-2.08333333333e6, - 1.0e8,-2.5e6,-416666.666667,1.5e9,-833333.333333,1.25e6,5.01833333333e8, - 2.08333333333e6,1.0e8,416666.666667,5.025e8,-28935.1851852, - -2.08333333333e6,-4166.66666667,-1.25e6,3.98587962963e6,-1.59791666667e6, - -8333.33333333,2.5e6,3.41149691358e6,-28935.1851852,2.08333333333e6, - -2.355e6,2.43100308642e6,-2.08333333333e6,1.0e8,-2.5e6,5.0e8,2.5e6, - -416666.666667,1.50416666667e9,-833333.333333,1.25e6,2.5e8,-1.25e6, - -3.47222222222e6,1.33516666667e9,2.08333333333e6,1.0e8,-2.5e6, - 416666.666667,6.94444444444e6,2.16916666667e9,-28935.1851852, - -2.08333333333e6,-3.925e6,3.98587962963e6,-1.59791666667e6, - -38580.2469136,-6.94444444444e6,3.41149691358e6,-28935.1851852, - 2.08333333333e6,-19290.1234568,3.47222222222e6,2.43100308642e6, - -2.08333333333e6,1.0e8,-4.16666666667e6,2.5e6,-416666.666667, - 1.50416666667e9,-833333.333333,-3.47222222222e6,4.16666666667e8, - -1.25e6,3.47222222222e6,1.33516666667e9,2.08333333333e6,1.0e8, - 6.94444444445e6,8.33333333333e8,416666.666667,-6.94444444445e6, - 2.16916666667e9,-3830.95098171,1.14928529451e6,-275828.470683, - -28935.1851852,-2.08333333333e6,-4166.66666667,1.25e6,64710.5806113, - -131963.213599,-517922.131816,-2.29857058902e6,-1.59791666667e6, - -8333.33333333,-2.5e6,3.50487988027e6,-517922.131816,-2.16567078453e6, - 551656.941366,-28935.1851852,2.08333333333e6,-2.355e6,517922.131816, - 4.57738374749e6,2.29857058902e6,-551656.941367,4.8619365099e8, - -2.08333333333e6,1.0e8,2.5e6,5.0e8,-4.79857058902e6,134990.2747, - 2.47238730198e9,-1.14928529451e6,2.29724661236e8,-5.57173510779e7, - -833333.333333,-1.25e6,2.5e8,2.39928529451e6,9.61679848804e8,275828.470683, - -5.57173510779e7,1.09411960038e7,2.08333333333e6,1.0e8,-2.5e6, - 140838.195984,-1.09779731332e8,5.31278103775e8], 1) -@test CHOLMOD.norm_sparse(A, 0) ≈ 3.570948074697437e9 -@test CHOLMOD.norm_sparse(A, 1) ≈ 3.570948074697437e9 -@test_throws ArgumentError CHOLMOD.norm_sparse(A, 2) -@test CHOLMOD.isvalid(A) - -B = A * ones(size(A,2)) -chma = ldltfact(A) # LDL' form -@test CHOLMOD.isvalid(chma) -@test unsafe_load(pointer(chma)).is_ll == 0 # check that it is in fact an LDLt -x = chma\B -@test x ≈ ones(size(x)) -@test nnz(ldltfact(A, perm=1:size(A,1))) > nnz(chma) -@test size(chma) == size(A) -chmal = CHOLMOD.FactorComponent(chma, :L) -@test size(chmal) == size(A) -@test size(chmal, 1) == size(A, 1) - -chma = cholfact(A) # LL' form -@test CHOLMOD.isvalid(chma) -@test unsafe_load(pointer(chma)).is_ll == 1 # check that it is in fact an LLt -x = chma\B -@test x ≈ ones(size(x)) -@test nnz(chma) == 489 -@test nnz(cholfact(A, perm=1:size(A,1))) > nnz(chma) -@test size(chma) == size(A) -chmal = CHOLMOD.FactorComponent(chma, :L) -@test size(chmal) == size(A) -@test size(chmal, 1) == size(A, 1) - -#lp_afiro example -afiro = CHOLMOD.Sparse(27, 51, - CHOLMOD.SuiteSparse_long[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, - 23,25,27,29,33,37,41,45,47,49,51,53,55,57,59,63,65,67,69,71,75,79,83,87,89, - 91,93,95,97,99,101,102], - CHOLMOD.SuiteSparse_long[2,3,6,7,8,9,12,13,16,17,18,19,20,21,22,23,24,25,26, - 0,1,2,23,0,3,0,21,1,25,4,5,6,24,4,5,7,24,4,5,8,24,4,5,9,24,6,20,7,20,8,20,9, - 20,3,4,4,22,5,26,10,11,12,21,10,13,10,23,10,20,11,25,14,15,16,22,14,15,17, - 22,14,15,18,22,14,15,19,22,16,20,17,20,18,20,19,20,13,15,15,24,14,26,15], - [1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0, - 1.0,-1.0,-1.06,1.0,0.301,1.0,-1.0,1.0,-1.0,1.0,1.0,-1.0,-1.06,1.0,0.301, - -1.0,-1.06,1.0,0.313,-1.0,-0.96,1.0,0.313,-1.0,-0.86,1.0,0.326,-1.0,2.364, - -1.0,2.386,-1.0,2.408,-1.0,2.429,1.4,1.0,1.0,-1.0,1.0,1.0,-1.0,-0.43,1.0, - 0.109,1.0,-1.0,1.0,-1.0,1.0,-1.0,1.0,1.0,-0.43,1.0,1.0,0.109,-0.43,1.0,1.0, - 0.108,-0.39,1.0,1.0,0.108,-0.37,1.0,1.0,0.107,-1.0,2.191,-1.0,2.219,-1.0, - 2.249,-1.0,2.279,1.4,-1.0,1.0,-1.0,1.0,1.0,1.0], 0) -afiro2 = CHOLMOD.aat(afiro, CHOLMOD.SuiteSparse_long[0:50;], CHOLMOD.SuiteSparse_long(1)) -CHOLMOD.change_stype!(afiro2, -1) -chmaf = cholfact(afiro2) -y = afiro'*ones(size(afiro,1)) -sol = chmaf\(afiro*y) # least squares solution -@test CHOLMOD.isvalid(sol) -pred = afiro'*sol -@test norm(afiro * (convert(Matrix, y) - convert(Matrix, pred))) < 1e-8 - -let # Issue 9160 + A = CHOLMOD.Sparse(48, 48, + CHOLMOD.SuiteSparse_long[0,1,2,3,6,9,12,15,18,20,25,30,34,36,39,43,47,52,58, + 62,67,71,77,84,90,93,95,98,103,106,110,115,119,123,130,136,142,146,150,155, + 161,167,174,182,189,197,207,215,224], # zero-based column pointers + CHOLMOD.SuiteSparse_long[0,1,2,1,2,3,0,2,4,0,1,5,0,4,6,1,3,7,2,8,1,3,7,8,9, + 0,4,6,8,10,5,6,7,11,6,12,7,11,13,8,10,13,14,9,13,14,15,8,10,12,14,16,7,11, + 12,13,16,17,0,12,16,18,1,5,13,15,19,2,4,14,20,3,13,15,19,20,21,2,4,12,16,18, + 20,22,1,5,17,18,19,23,0,5,24,1,25,2,3,26,2,3,25,26,27,4,24,28,0,5,24,29,6, + 11,24,28,30,7,25,27,31,8,9,26,32,8,9,25,27,31,32,33,10,24,28,30,32,34,6,11, + 29,30,31,35,12,17,30,36,13,31,35,37,14,15,32,34,38,14,15,33,37,38,39,16,32, + 34,36,38,40,12,17,31,35,36,37,41,12,16,17,18,23,36,40,42,13,14,15,19,37,39, + 43,13,14,15,20,21,38,43,44,13,14,15,20,21,37,39,43,44,45,12,16,17,22,36,40, + 42,46,12,16,17,18,23,41,42,46,47], + [2.83226851852e6,1.63544753086e6,1.72436728395e6,-2.0e6,-2.08333333333e6, + 1.00333333333e9,1.0e6,-2.77777777778e6,1.0675e9,2.08333333333e6, + 5.55555555555e6,1.53533333333e9,-3333.33333333,-1.0e6,2.83226851852e6, + -6666.66666667,2.0e6,1.63544753086e6,-1.68e6,1.72436728395e6,-2.0e6,4.0e8, + 2.0e6,-2.08333333333e6,1.00333333333e9,1.0e6,2.0e8,-1.0e6,-2.77777777778e6, + 1.0675e9,-2.0e6,2.08333333333e6,5.55555555555e6,1.53533333333e9,-2.8e6, + 2.8360994695e6,-30864.1975309,-5.55555555555e6,1.76741074446e6, + -15432.0987654,2.77777777778e6,517922.131816,3.89003806848e6, + -3.33333333333e6,4.29857058902e6,-2.6349902747e6,1.97572063531e9, + -2.77777777778e6,3.33333333333e8,-2.14928529451e6,2.77777777778e6, + 1.52734651547e9,5.55555555555e6,6.66666666667e8,2.35916180402e6, + -5.55555555555e6,-1.09779731332e8,1.56411143711e9,-2.8e6,-3333.33333333, + 1.0e6,2.83226851852e6,-30864.1975309,-5.55555555555e6,-6666.66666667, + -2.0e6,1.63544753086e6,-15432.0987654,2.77777777778e6,-1.68e6, + 1.72436728395e6,-3.33333333333e6,2.0e6,4.0e8,-2.0e6,-2.08333333333e6, + 1.00333333333e9,-2.77777777778e6,3.33333333333e8,-1.0e6,2.0e8,1.0e6, + 2.77777777778e6,1.0675e9,5.55555555555e6,6.66666666667e8,-2.0e6, + 2.08333333333e6,-5.55555555555e6,1.53533333333e9,-28935.1851852, + -2.08333333333e6,60879.6296296,-1.59791666667e6,3.37291666667e6, + -28935.1851852,2.08333333333e6,2.41171296296e6,-2.08333333333e6, + 1.0e8,-2.5e6,-416666.666667,1.5e9,-833333.333333,1.25e6,5.01833333333e8, + 2.08333333333e6,1.0e8,416666.666667,5.025e8,-28935.1851852, + -2.08333333333e6,-4166.66666667,-1.25e6,3.98587962963e6,-1.59791666667e6, + -8333.33333333,2.5e6,3.41149691358e6,-28935.1851852,2.08333333333e6, + -2.355e6,2.43100308642e6,-2.08333333333e6,1.0e8,-2.5e6,5.0e8,2.5e6, + -416666.666667,1.50416666667e9,-833333.333333,1.25e6,2.5e8,-1.25e6, + -3.47222222222e6,1.33516666667e9,2.08333333333e6,1.0e8,-2.5e6, + 416666.666667,6.94444444444e6,2.16916666667e9,-28935.1851852, + -2.08333333333e6,-3.925e6,3.98587962963e6,-1.59791666667e6, + -38580.2469136,-6.94444444444e6,3.41149691358e6,-28935.1851852, + 2.08333333333e6,-19290.1234568,3.47222222222e6,2.43100308642e6, + -2.08333333333e6,1.0e8,-4.16666666667e6,2.5e6,-416666.666667, + 1.50416666667e9,-833333.333333,-3.47222222222e6,4.16666666667e8, + -1.25e6,3.47222222222e6,1.33516666667e9,2.08333333333e6,1.0e8, + 6.94444444445e6,8.33333333333e8,416666.666667,-6.94444444445e6, + 2.16916666667e9,-3830.95098171,1.14928529451e6,-275828.470683, + -28935.1851852,-2.08333333333e6,-4166.66666667,1.25e6,64710.5806113, + -131963.213599,-517922.131816,-2.29857058902e6,-1.59791666667e6, + -8333.33333333,-2.5e6,3.50487988027e6,-517922.131816,-2.16567078453e6, + 551656.941366,-28935.1851852,2.08333333333e6,-2.355e6,517922.131816, + 4.57738374749e6,2.29857058902e6,-551656.941367,4.8619365099e8, + -2.08333333333e6,1.0e8,2.5e6,5.0e8,-4.79857058902e6,134990.2747, + 2.47238730198e9,-1.14928529451e6,2.29724661236e8,-5.57173510779e7, + -833333.333333,-1.25e6,2.5e8,2.39928529451e6,9.61679848804e8,275828.470683, + -5.57173510779e7,1.09411960038e7,2.08333333333e6,1.0e8,-2.5e6, + 140838.195984,-1.09779731332e8,5.31278103775e8], 1) + @test CHOLMOD.norm_sparse(A, 0) ≈ 3.570948074697437e9 + @test CHOLMOD.norm_sparse(A, 1) ≈ 3.570948074697437e9 + @test_throws ArgumentError CHOLMOD.norm_sparse(A, 2) + @test CHOLMOD.isvalid(A) + + B = A * ones(size(A,2)) + chma = ldltfact(A) # LDL' form + @test CHOLMOD.isvalid(chma) + @test unsafe_load(pointer(chma)).is_ll == 0 # check that it is in fact an LDLt + x = chma\B + @test x ≈ ones(size(x)) + @test nnz(ldltfact(A, perm=1:size(A,1))) > nnz(chma) + @test size(chma) == size(A) + chmal = CHOLMOD.FactorComponent(chma, :L) + @test size(chmal) == size(A) + @test size(chmal, 1) == size(A, 1) + + chma = cholfact(A) # LL' form + @test CHOLMOD.isvalid(chma) + @test unsafe_load(pointer(chma)).is_ll == 1 # check that it is in fact an LLt + x = chma\B + @test x ≈ ones(size(x)) + @test nnz(chma) == 489 + @test nnz(cholfact(A, perm=1:size(A,1))) > nnz(chma) + @test size(chma) == size(A) + chmal = CHOLMOD.FactorComponent(chma, :L) + @test size(chmal) == size(A) + @test size(chmal, 1) == size(A, 1) + + @testset "eltype" begin + @test eltype(Dense(ones(3))) == Float64 + @test eltype(A) == Float64 + @test eltype(chma) == Float64 + end +end + +@testset "lp_afiro example" begin + afiro = CHOLMOD.Sparse(27, 51, + CHOLMOD.SuiteSparse_long[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, + 23,25,27,29,33,37,41,45,47,49,51,53,55,57,59,63,65,67,69,71,75,79,83,87,89, + 91,93,95,97,99,101,102], + CHOLMOD.SuiteSparse_long[2,3,6,7,8,9,12,13,16,17,18,19,20,21,22,23,24,25,26, + 0,1,2,23,0,3,0,21,1,25,4,5,6,24,4,5,7,24,4,5,8,24,4,5,9,24,6,20,7,20,8,20,9, + 20,3,4,4,22,5,26,10,11,12,21,10,13,10,23,10,20,11,25,14,15,16,22,14,15,17, + 22,14,15,18,22,14,15,19,22,16,20,17,20,18,20,19,20,13,15,15,24,14,26,15], + [1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0, + 1.0,-1.0,-1.06,1.0,0.301,1.0,-1.0,1.0,-1.0,1.0,1.0,-1.0,-1.06,1.0,0.301, + -1.0,-1.06,1.0,0.313,-1.0,-0.96,1.0,0.313,-1.0,-0.86,1.0,0.326,-1.0,2.364, + -1.0,2.386,-1.0,2.408,-1.0,2.429,1.4,1.0,1.0,-1.0,1.0,1.0,-1.0,-0.43,1.0, + 0.109,1.0,-1.0,1.0,-1.0,1.0,-1.0,1.0,1.0,-0.43,1.0,1.0,0.109,-0.43,1.0,1.0, + 0.108,-0.39,1.0,1.0,0.108,-0.37,1.0,1.0,0.107,-1.0,2.191,-1.0,2.219,-1.0, + 2.249,-1.0,2.279,1.4,-1.0,1.0,-1.0,1.0,1.0,1.0], 0) + afiro2 = CHOLMOD.aat(afiro, CHOLMOD.SuiteSparse_long[0:50;], CHOLMOD.SuiteSparse_long(1)) + CHOLMOD.change_stype!(afiro2, -1) + chmaf = cholfact(afiro2) + y = afiro'*ones(size(afiro,1)) + sol = chmaf\(afiro*y) # least squares solution + @test CHOLMOD.isvalid(sol) + pred = afiro'*sol + @test norm(afiro * (convert(Matrix, y) - convert(Matrix, pred))) < 1e-8 +end + +@testset "Issue 9160" begin local A, B A = sprand(10, 10, 0.1) A = convert(SparseMatrixCSC{Float64,CHOLMOD.SuiteSparse_long}, A) @@ -177,88 +187,91 @@ let # Issue 9160 @test sparse(cmA*cmA') ≈ A*A' end -# Issue #9915 -@test speye(2)\speye(2) == eye(2) - -# test eltype -@test eltype(Dense(ones(3))) == Float64 -@test eltype(A) == Float64 -@test eltype(chma) == Float64 +@testset "Issue #9915" begin + @test speye(2)\speye(2) == eye(2) +end -# test Sparse constructor Symmetric and Hermitian input (and issymmetric and ishermitian) -ACSC = sprandn(10, 10, 0.3) + I -@test issymmetric(Sparse(Symmetric(ACSC, :L))) -@test issymmetric(Sparse(Symmetric(ACSC, :U))) -@test ishermitian(Sparse(Hermitian(complex(ACSC), :L))) -@test ishermitian(Sparse(Hermitian(complex(ACSC), :U))) +@testset "test Sparse constructor Symmetric and Hermitian input (and issymmetric and ishermitian)" begin + ACSC = sprandn(10, 10, 0.3) + I + @test issymmetric(Sparse(Symmetric(ACSC, :L))) + @test issymmetric(Sparse(Symmetric(ACSC, :U))) + @test ishermitian(Sparse(Hermitian(complex(ACSC), :L))) + @test ishermitian(Sparse(Hermitian(complex(ACSC), :U))) +end -# test Sparse constructor for c_SparseVoid (and read_sparse) -mktempdir() do temp_dir - testfile = joinpath(temp_dir, "tmp.mtx") +@testset "test Sparse constructor for c_SparseVoid (and read_sparse)" begin + mktempdir() do temp_dir + testfile = joinpath(temp_dir, "tmp.mtx") - writedlm(testfile, ["%%MatrixMarket matrix coordinate real symmetric","3 3 4","1 1 1","2 2 1","3 2 0.5","3 3 1"]) - @test sparse(CHOLMOD.Sparse(testfile)) == [1 0 0;0 1 0.5;0 0.5 1] - rm(testfile) + writedlm(testfile, ["%%MatrixMarket matrix coordinate real symmetric","3 3 4","1 1 1","2 2 1","3 2 0.5","3 3 1"]) + @test sparse(CHOLMOD.Sparse(testfile)) == [1 0 0;0 1 0.5;0 0.5 1] + rm(testfile) - writedlm(testfile, ["%%MatrixMarket matrix coordinate complex Hermitian", + writedlm(testfile, ["%%MatrixMarket matrix coordinate complex Hermitian", "3 3 4","1 1 1.0 0.0","2 2 1.0 0.0","3 2 0.5 0.5","3 3 1.0 0.0"]) - @test sparse(CHOLMOD.Sparse(testfile)) == [1 0 0;0 1 0.5-0.5im;0 0.5+0.5im 1] - rm(testfile) + @test sparse(CHOLMOD.Sparse(testfile)) == [1 0 0;0 1 0.5-0.5im;0 0.5+0.5im 1] + rm(testfile) - writedlm(testfile, ["%%MatrixMarket matrix coordinate real symmetric","%3 3 4","1 1 1","2 2 1","3 2 0.5","3 3 1"]) - @test_throws ArgumentError sparse(CHOLMOD.Sparse(testfile)) - rm(testfile) + writedlm(testfile, ["%%MatrixMarket matrix coordinate real symmetric","%3 3 4","1 1 1","2 2 1","3 2 0.5","3 3 1"]) + @test_throws ArgumentError sparse(CHOLMOD.Sparse(testfile)) + rm(testfile) + end end -# test that Sparse(Ptr) constructor throws the right places -@test_throws ArgumentError CHOLMOD.Sparse(convert(Ptr{CHOLMOD.C_Sparse{Float64}}, C_NULL)) -@test_throws ArgumentError CHOLMOD.Sparse(convert(Ptr{CHOLMOD.C_SparseVoid}, C_NULL)) +@testset "test that Sparse(Ptr) constructor throws the right places" begin + @test_throws ArgumentError CHOLMOD.Sparse(convert(Ptr{CHOLMOD.C_Sparse{Float64}}, C_NULL)) + @test_throws ArgumentError CHOLMOD.Sparse(convert(Ptr{CHOLMOD.C_SparseVoid}, C_NULL)) +end ## The struct pointer must be constructed by the library constructor and then modified afterwards to checks that the method throws -### illegal dtype (for now but should be supported at some point) -p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, - (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) -puint = convert(Ptr{UInt32}, p) -unsafe_store!(puint, CHOLMOD.SINGLE, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) -@test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) - -### illegal dtype -p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, - (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) -puint = convert(Ptr{UInt32}, p) -unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) -@test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) - -### illegal xtype -p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, - (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) -puint = convert(Ptr{UInt32}, p) -unsafe_store!(puint, 3, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 3) -@test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) - -### illegal itype -p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, - (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) -puint = convert(Ptr{UInt32}, p) -unsafe_store!(puint, CHOLMOD.INTLONG, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) -@test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) - -### illegal itype -p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, - (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) -puint = convert(Ptr{UInt32}, p) -unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) -@test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) +@testset "illegal dtype (for now but should be supported at some point)" begin + p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, + (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) + puint = convert(Ptr{UInt32}, p) + unsafe_store!(puint, CHOLMOD.SINGLE, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) + @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) +end + +@testset "illegal dtype" begin + p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, + (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) + puint = convert(Ptr{UInt32}, p) + unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 4) + @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) +end + +@testset "illegal xtype" begin + p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, + (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) + puint = convert(Ptr{UInt32}, p) + unsafe_store!(puint, 3, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 3) + @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) +end + +@testset "illegal itype I" begin + p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, + (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) + puint = convert(Ptr{UInt32}, p) + unsafe_store!(puint, CHOLMOD.INTLONG, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) + @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) +end + +@testset "illegal itype II" begin + p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_SparseVoid}, + (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) + puint = convert(Ptr{UInt32}, p) + unsafe_store!(puint, 5, 3*div(sizeof(Csize_t), 4) + 5*div(sizeof(Ptr{Void}), 4) + 2) + @test_throws CHOLMOD.CHOLMODException CHOLMOD.Sparse(p) +end # Test Dense wrappers (only Float64 supported a present) -## High level interface -for elty in (Float64, Complex{Float64}) +@testset "High level interface" for elty in (Float64, Complex{Float64}) local A, b if elty == Float64 A = randn(5, 5) @@ -286,24 +299,26 @@ for elty in (Float64, Complex{Float64}) @test convert(Matrix, AA) == eye(2, 3) end -## Low level interface -@test isa(CHOLMOD.zeros(3, 3, Float64), CHOLMOD.Dense{Float64}) -@test isa(CHOLMOD.zeros(3, 3), CHOLMOD.Dense{Float64}) -@test isa(CHOLMOD.zeros(3, 3, Float64), CHOLMOD.Dense{Float64}) -@test isa(CHOLMOD.ones(3, 3), CHOLMOD.Dense{Float64}) -@test isa(CHOLMOD.eye(3, 4, Float64), CHOLMOD.Dense{Float64}) -@test isa(CHOLMOD.eye(3, 4), CHOLMOD.Dense{Float64}) -@test isa(CHOLMOD.eye(3), CHOLMOD.Dense{Float64}) -@test isa(CHOLMOD.copy_dense(CHOLMOD.eye(3)), CHOLMOD.Dense{Float64}) +@testset "Low level interface" begin + @test isa(CHOLMOD.zeros(3, 3, Float64), CHOLMOD.Dense{Float64}) + @test isa(CHOLMOD.zeros(3, 3), CHOLMOD.Dense{Float64}) + @test isa(CHOLMOD.zeros(3, 3, Float64), CHOLMOD.Dense{Float64}) + @test isa(CHOLMOD.ones(3, 3), CHOLMOD.Dense{Float64}) + @test isa(CHOLMOD.eye(3, 4, Float64), CHOLMOD.Dense{Float64}) + @test isa(CHOLMOD.eye(3, 4), CHOLMOD.Dense{Float64}) + @test isa(CHOLMOD.eye(3), CHOLMOD.Dense{Float64}) + @test isa(CHOLMOD.copy_dense(CHOLMOD.eye(3)), CHOLMOD.Dense{Float64}) +end # Test Sparse and Factor -## test free_sparse! -p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_Sparse{Float64}}, - (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), - 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) -@test CHOLMOD.free_sparse!(p) +@testset "test free_sparse!" begin + p = ccall((:cholmod_l_allocate_sparse, :libcholmod), Ptr{CHOLMOD.C_Sparse{Float64}}, + (Csize_t, Csize_t, Csize_t, Cint, Cint, Cint, Cint, Ptr{Void}), + 1, 1, 1, true, true, 0, CHOLMOD.REAL, CHOLMOD.common_struct) + @test CHOLMOD.free_sparse!(p) +end -for elty in (Float64, Complex{Float64}) +@testset "Core functionality" for elty in (Float64, Complex{Float64}) A1 = sparse([1:5; 1], [1:5; 2], elty == Float64 ? randn(6) : complex.(randn(6), randn(6))) A2 = sparse([1:5; 1], [1:5; 2], elty == Float64 ? randn(6) : complex.(randn(6), randn(6))) A1pd = A1'A1 @@ -321,7 +336,9 @@ for elty in (Float64, Complex{Float64}) @test_throws BoundsError A1Sparse[6, 1] @test_throws BoundsError A1Sparse[1, 6] @test sparse(A1Sparse) == A1 - for i=1:size(A1, 1) A1[i, i] = real(A1[i, i]) end #Construct Hermitian matrix properly + for i = 1:size(A1, 1) + A1[i, i] = real(A1[i, i]) + end #Construct Hermitian matrix properly @test CHOLMOD.sparse(CHOLMOD.Sparse(Hermitian(A1, :L))) == Hermitian(A1, :L) @test CHOLMOD.sparse(CHOLMOD.Sparse(Hermitian(A1, :U))) == Hermitian(A1, :U) @test_throws ArgumentError convert(SparseMatrixCSC{elty,Int}, A1pdSparse) @@ -377,7 +394,7 @@ for elty in (Float64, Complex{Float64}) @test logdet(F) ≈ logdet(Array(A1pd)) @test det(F) == exp(logdet(F)) let # to test supernodal, we must use a larger matrix - Ftmp = sprandn(100,100,0.1) + Ftmp = sprandn(100, 100, 0.1) Ftmp = Ftmp'Ftmp + I @test logdet(cholfact(Ftmp)) ≈ logdet(Array(Ftmp)) end @@ -459,7 +476,6 @@ for elty in (Float64, Complex{Float64}) @test_throws MethodError CHOLMOD.vertcat(A1Sparse, A2Sparse, true) == [A1; A2] end - if elty <: Real @test CHOLMOD.ssmult(A1Sparse, A2Sparse, 0, true, true) ≈ A1*A2 @test CHOLMOD.aat(A1Sparse, [0:size(A1,2)-1;], 1) ≈ A1*A1' @@ -470,205 +486,217 @@ for elty in (Float64, Complex{Float64}) @test CHOLMOD.Sparse(CHOLMOD.Dense(A1Sparse)) == A1Sparse end -Af = float([4 12 -16; 12 37 -43; -16 -43 98]) -As = sparse(Af) -Lf = float([2 0 0; 6 1 0; -8 5 3]) -LDf = float([4 0 0; 3 1 0; -4 5 9]) # D is stored along the diagonal -L_f = float([1 0 0; 3 1 0; -4 5 1]) # L by itself in LDLt of Af -D_f = float([4 0 0; 0 1 0; 0 0 9]) - -# cholfact, no permutation -Fs = cholfact(As, perm=[1:3;]) -@test Fs[:p] == [1:3;] -@test sparse(Fs[:L]) ≈ Lf -@test sparse(Fs) ≈ As -b = rand(3) -@test Fs\b ≈ Af\b -@test Fs[:UP]\(Fs[:PtL]\b) ≈ Af\b -@test Fs[:L]\b ≈ Lf\b -@test Fs[:U]\b ≈ Lf'\b -@test Fs[:L]'\b ≈ Lf'\b -@test Fs[:U]'\b ≈ Lf\b -@test Fs[:PtL]\b ≈ Lf\b -@test Fs[:UP]\b ≈ Lf'\b -@test Fs[:PtL]'\b ≈ Lf'\b -@test Fs[:UP]'\b ≈ Lf\b -@test_throws CHOLMOD.CHOLMODException Fs[:D] -@test_throws CHOLMOD.CHOLMODException Fs[:LD] -@test_throws CHOLMOD.CHOLMODException Fs[:DU] -@test_throws CHOLMOD.CHOLMODException Fs[:PLD] -@test_throws CHOLMOD.CHOLMODException Fs[:DUPt] - -# cholfact, with permutation -p = [2,3,1] -p_inv = [3,1,2] -Fs = cholfact(As, perm=p) -@test Fs[:p] == p -Afp = Af[p,p] -Lfp = cholfact(Afp)[:L] -@test sparse(Fs[:L]) ≈ Lfp -@test sparse(Fs) ≈ As -b = rand(3) -@test Fs\b ≈ Af\b -@test Fs[:UP]\(Fs[:PtL]\b) ≈ Af\b -@test Fs[:L]\b ≈ Lfp\b -@test Fs[:U]'\b ≈ Lfp\b -@test Fs[:U]\b ≈ Lfp'\b -@test Fs[:L]'\b ≈ Lfp'\b -@test Fs[:PtL]\b ≈ Lfp\b[p] -@test Fs[:UP]\b ≈ (Lfp'\b)[p_inv] -@test Fs[:PtL]'\b ≈ (Lfp'\b)[p_inv] -@test Fs[:UP]'\b ≈ Lfp\b[p] -@test_throws CHOLMOD.CHOLMODException Fs[:PL] -@test_throws CHOLMOD.CHOLMODException Fs[:UPt] -@test_throws CHOLMOD.CHOLMODException Fs[:D] -@test_throws CHOLMOD.CHOLMODException Fs[:LD] -@test_throws CHOLMOD.CHOLMODException Fs[:DU] -@test_throws CHOLMOD.CHOLMODException Fs[:PLD] -@test_throws CHOLMOD.CHOLMODException Fs[:DUPt] - -# ldltfact, no permutation -Fs = ldltfact(As, perm=[1:3;]) -@test Fs[:p] == [1:3;] -@test sparse(Fs[:LD]) ≈ LDf -@test sparse(Fs) ≈ As -b = rand(3) -@test Fs\b ≈ Af\b -@test Fs[:UP]\(Fs[:PtLD]\b) ≈ Af\b -@test Fs[:DUP]\(Fs[:PtL]\b) ≈ Af\b -@test Fs[:L]\b ≈ L_f\b -@test Fs[:U]\b ≈ L_f'\b -@test Fs[:L]'\b ≈ L_f'\b -@test Fs[:U]'\b ≈ L_f\b -@test Fs[:PtL]\b ≈ L_f\b -@test Fs[:UP]\b ≈ L_f'\b -@test Fs[:PtL]'\b ≈ L_f'\b -@test Fs[:UP]'\b ≈ L_f\b -@test Fs[:D]\b ≈ D_f\b -@test Fs[:D]'\b ≈ D_f\b -@test Fs[:LD]\b ≈ D_f\(L_f\b) -@test Fs[:DU]'\b ≈ D_f\(L_f\b) -@test Fs[:LD]'\b ≈ L_f'\(D_f\b) -@test Fs[:DU]\b ≈ L_f'\(D_f\b) -@test Fs[:PtLD]\b ≈ D_f\(L_f\b) -@test Fs[:DUP]'\b ≈ D_f\(L_f\b) -@test Fs[:PtLD]'\b ≈ L_f'\(D_f\b) -@test Fs[:DUP]\b ≈ L_f'\(D_f\b) - -# ldltfact, with permutation -Fs = ldltfact(As, perm=p) -@test Fs[:p] == p -@test sparse(Fs) ≈ As -b = rand(3) -Asp = As[p,p] -LDp = sparse(ldltfact(Asp, perm=[1,2,3])[:LD]) -# LDp = sparse(Fs[:LD]) -Lp, dp = Base.SparseArrays.CHOLMOD.getLd!(copy(LDp)) -Dp = sparse(Diagonal(dp)) -@test Fs\b ≈ Af\b -@test Fs[:UP]\(Fs[:PtLD]\b) ≈ Af\b -@test Fs[:DUP]\(Fs[:PtL]\b) ≈ Af\b -@test Fs[:L]\b ≈ Lp\b -@test Fs[:U]\b ≈ Lp'\b -@test Fs[:L]'\b ≈ Lp'\b -@test Fs[:U]'\b ≈ Lp\b -@test Fs[:PtL]\b ≈ Lp\b[p] -@test Fs[:UP]\b ≈ (Lp'\b)[p_inv] -@test Fs[:PtL]'\b ≈ (Lp'\b)[p_inv] -@test Fs[:UP]'\b ≈ Lp\b[p] -@test Fs[:LD]\b ≈ Dp\(Lp\b) -@test Fs[:DU]'\b ≈ Dp\(Lp\b) -@test Fs[:LD]'\b ≈ Lp'\(Dp\b) -@test Fs[:DU]\b ≈ Lp'\(Dp\b) -@test Fs[:PtLD]\b ≈ Dp\(Lp\b[p]) -@test Fs[:DUP]'\b ≈ Dp\(Lp\b[p]) -@test Fs[:PtLD]'\b ≈ (Lp'\(Dp\b))[p_inv] -@test Fs[:DUP]\b ≈ (Lp'\(Dp\b))[p_inv] -@test_throws CHOLMOD.CHOLMODException Fs[:DUPt] -@test_throws CHOLMOD.CHOLMODException Fs[:PLD] - -# Issue 11745 - row and column pointers were not sorted in sparse(Factor) -let A = Float64[10 1 1 1; 1 10 0 0; 1 0 10 0; 1 0 0 10] +@testset "extract factors" begin + Af = float([4 12 -16; 12 37 -43; -16 -43 98]) + As = sparse(Af) + Lf = float([2 0 0; 6 1 0; -8 5 3]) + LDf = float([4 0 0; 3 1 0; -4 5 9]) # D is stored along the diagonal + L_f = float([1 0 0; 3 1 0; -4 5 1]) # L by itself in LDLt of Af + D_f = float([4 0 0; 0 1 0; 0 0 9]) + p = [2,3,1] + p_inv = [3,1,2] + + @testset "cholfact, no permutation" begin + Fs = cholfact(As, perm=[1:3;]) + @test Fs[:p] == [1:3;] + @test sparse(Fs[:L]) ≈ Lf + @test sparse(Fs) ≈ As + b = rand(3) + @test Fs\b ≈ Af\b + @test Fs[:UP]\(Fs[:PtL]\b) ≈ Af\b + @test Fs[:L]\b ≈ Lf\b + @test Fs[:U]\b ≈ Lf'\b + @test Fs[:L]'\b ≈ Lf'\b + @test Fs[:U]'\b ≈ Lf\b + @test Fs[:PtL]\b ≈ Lf\b + @test Fs[:UP]\b ≈ Lf'\b + @test Fs[:PtL]'\b ≈ Lf'\b + @test Fs[:UP]'\b ≈ Lf\b + @test_throws CHOLMOD.CHOLMODException Fs[:D] + @test_throws CHOLMOD.CHOLMODException Fs[:LD] + @test_throws CHOLMOD.CHOLMODException Fs[:DU] + @test_throws CHOLMOD.CHOLMODException Fs[:PLD] + @test_throws CHOLMOD.CHOLMODException Fs[:DUPt] + end + + @testset "cholfact, with permutation" begin + Fs = cholfact(As, perm=p) + @test Fs[:p] == p + Afp = Af[p,p] + Lfp = cholfact(Afp)[:L] + @test sparse(Fs[:L]) ≈ Lfp + @test sparse(Fs) ≈ As + b = rand(3) + @test Fs\b ≈ Af\b + @test Fs[:UP]\(Fs[:PtL]\b) ≈ Af\b + @test Fs[:L]\b ≈ Lfp\b + @test Fs[:U]'\b ≈ Lfp\b + @test Fs[:U]\b ≈ Lfp'\b + @test Fs[:L]'\b ≈ Lfp'\b + @test Fs[:PtL]\b ≈ Lfp\b[p] + @test Fs[:UP]\b ≈ (Lfp'\b)[p_inv] + @test Fs[:PtL]'\b ≈ (Lfp'\b)[p_inv] + @test Fs[:UP]'\b ≈ Lfp\b[p] + @test_throws CHOLMOD.CHOLMODException Fs[:PL] + @test_throws CHOLMOD.CHOLMODException Fs[:UPt] + @test_throws CHOLMOD.CHOLMODException Fs[:D] + @test_throws CHOLMOD.CHOLMODException Fs[:LD] + @test_throws CHOLMOD.CHOLMODException Fs[:DU] + @test_throws CHOLMOD.CHOLMODException Fs[:PLD] + @test_throws CHOLMOD.CHOLMODException Fs[:DUPt] + end + + @testset "ldltfact, no permutation" begin + Fs = ldltfact(As, perm=[1:3;]) + @test Fs[:p] == [1:3;] + @test sparse(Fs[:LD]) ≈ LDf + @test sparse(Fs) ≈ As + b = rand(3) + @test Fs\b ≈ Af\b + @test Fs[:UP]\(Fs[:PtLD]\b) ≈ Af\b + @test Fs[:DUP]\(Fs[:PtL]\b) ≈ Af\b + @test Fs[:L]\b ≈ L_f\b + @test Fs[:U]\b ≈ L_f'\b + @test Fs[:L]'\b ≈ L_f'\b + @test Fs[:U]'\b ≈ L_f\b + @test Fs[:PtL]\b ≈ L_f\b + @test Fs[:UP]\b ≈ L_f'\b + @test Fs[:PtL]'\b ≈ L_f'\b + @test Fs[:UP]'\b ≈ L_f\b + @test Fs[:D]\b ≈ D_f\b + @test Fs[:D]'\b ≈ D_f\b + @test Fs[:LD]\b ≈ D_f\(L_f\b) + @test Fs[:DU]'\b ≈ D_f\(L_f\b) + @test Fs[:LD]'\b ≈ L_f'\(D_f\b) + @test Fs[:DU]\b ≈ L_f'\(D_f\b) + @test Fs[:PtLD]\b ≈ D_f\(L_f\b) + @test Fs[:DUP]'\b ≈ D_f\(L_f\b) + @test Fs[:PtLD]'\b ≈ L_f'\(D_f\b) + @test Fs[:DUP]\b ≈ L_f'\(D_f\b) + end + + @testset "ldltfact, with permutation" begin + Fs = ldltfact(As, perm=p) + @test Fs[:p] == p + @test sparse(Fs) ≈ As + b = rand(3) + Asp = As[p,p] + LDp = sparse(ldltfact(Asp, perm=[1,2,3])[:LD]) + # LDp = sparse(Fs[:LD]) + Lp, dp = Base.SparseArrays.CHOLMOD.getLd!(copy(LDp)) + Dp = sparse(Diagonal(dp)) + @test Fs\b ≈ Af\b + @test Fs[:UP]\(Fs[:PtLD]\b) ≈ Af\b + @test Fs[:DUP]\(Fs[:PtL]\b) ≈ Af\b + @test Fs[:L]\b ≈ Lp\b + @test Fs[:U]\b ≈ Lp'\b + @test Fs[:L]'\b ≈ Lp'\b + @test Fs[:U]'\b ≈ Lp\b + @test Fs[:PtL]\b ≈ Lp\b[p] + @test Fs[:UP]\b ≈ (Lp'\b)[p_inv] + @test Fs[:PtL]'\b ≈ (Lp'\b)[p_inv] + @test Fs[:UP]'\b ≈ Lp\b[p] + @test Fs[:LD]\b ≈ Dp\(Lp\b) + @test Fs[:DU]'\b ≈ Dp\(Lp\b) + @test Fs[:LD]'\b ≈ Lp'\(Dp\b) + @test Fs[:DU]\b ≈ Lp'\(Dp\b) + @test Fs[:PtLD]\b ≈ Dp\(Lp\b[p]) + @test Fs[:DUP]'\b ≈ Dp\(Lp\b[p]) + @test Fs[:PtLD]'\b ≈ (Lp'\(Dp\b))[p_inv] + @test Fs[:DUP]\b ≈ (Lp'\(Dp\b))[p_inv] + @test_throws CHOLMOD.CHOLMODException Fs[:DUPt] + @test_throws CHOLMOD.CHOLMODException Fs[:PLD] + end + + @testset "Element promotion and type inference" begin + @inferred cholfact(As)\ones(Int, size(As, 1)) + @inferred ldltfact(As)\ones(Int, size(As, 1)) + end +end + +@testset "Issue 11745 - row and column pointers were not sorted in sparse(Factor)" begin + A = Float64[10 1 1 1; 1 10 0 0; 1 0 10 0; 1 0 0 10] @test sparse(cholfact(sparse(A))) ≈ A end gc() -# Issue 11747 - Wrong show method defined for FactorComponent -let v = cholfact(sparse(Float64[ 10 1 1 1; 1 10 0 0; 1 0 10 0; 1 0 0 10]))[:L] +@testset "Issue 11747 - Wrong show method defined for FactorComponent" begin + v = cholfact(sparse(Float64[ 10 1 1 1; 1 10 0 0; 1 0 10 0; 1 0 0 10]))[:L] for s in (sprint(show, MIME("text/plain"), v), sprint(show, v)) @test contains(s, "method: simplicial") @test !contains(s, "#undef") end end -# Element promotion and type inference -@inferred cholfact(As)\ones(Int, size(As, 1)) -@inferred ldltfact(As)\ones(Int, size(As, 1)) - -# Issue 14076 -@test cholfact(sparse([1,2,3,4], [1,2,3,4], Float32[1,4,16,64]))\[1,4,16,64] == ones(4) - -# Issue 14134 -A = SparseArrays.CHOLMOD.Sparse(sprandn(10,5,0.1) + I |> t -> t't) -b = IOBuffer() -serialize(b, A) -seekstart(b) -Anew = deserialize(b) -@test_throws ArgumentError show(Anew) -@test_throws ArgumentError size(Anew) -@test_throws ArgumentError Anew[1] -@test_throws ArgumentError Anew[2,1] -F = cholfact(A) -serialize(b, F) -seekstart(b) -Fnew = deserialize(b) -@test_throws ArgumentError Fnew\ones(5) -@test_throws ArgumentError show(Fnew) -@test_throws ArgumentError size(Fnew) -@test_throws ArgumentError diag(Fnew) -@test_throws ArgumentError logdet(Fnew) - -# Issue with promotion during conversion to CHOLMOD.Dense -@test SparseArrays.CHOLMOD.Dense(ones(Float32, 5)) == ones(5, 1) -@test SparseArrays.CHOLMOD.Dense(ones(Int, 5)) == ones(5, 1) -@test SparseArrays.CHOLMOD.Dense(ones(Complex{Float32}, 5, 2)) == ones(5, 2) - -# Further issue with promotion #14894 -@test cholfact(speye(Float16, 5))\ones(5) == ones(5) -@test cholfact(Symmetric(speye(Float16, 5)))\ones(5) == ones(5) -@test cholfact(Hermitian(speye(Complex{Float16}, 5)))\ones(5) == ones(Complex{Float64}, 5) -@test_throws MethodError cholfact(speye(BigFloat, 5)) -@test_throws MethodError cholfact(Symmetric(speye(BigFloat, 5))) -@test_throws MethodError cholfact(Hermitian(speye(Complex{BigFloat}, 5))) - -# test \ for Factor and StridedVecOrMat -let x = rand(5), +@testset "Issue 14076" begin + @test cholfact(sparse([1,2,3,4], [1,2,3,4], Float32[1,4,16,64]))\[1,4,16,64] == ones(4) +end + +@testset "Issue 14134" begin + A = SparseArrays.CHOLMOD.Sparse(sprandn(10,5,0.1) + I |> t -> t't) + b = IOBuffer() + serialize(b, A) + seekstart(b) + Anew = deserialize(b) + @test_throws ArgumentError show(Anew) + @test_throws ArgumentError size(Anew) + @test_throws ArgumentError Anew[1] + @test_throws ArgumentError Anew[2,1] + F = cholfact(A) + serialize(b, F) + seekstart(b) + Fnew = deserialize(b) + @test_throws ArgumentError Fnew\ones(5) + @test_throws ArgumentError show(Fnew) + @test_throws ArgumentError size(Fnew) + @test_throws ArgumentError diag(Fnew) + @test_throws ArgumentError logdet(Fnew) +end + +@testset "Issue with promotion during conversion to CHOLMOD.Dense" begin + @test SparseArrays.CHOLMOD.Dense(ones(Float32, 5)) == ones(5, 1) + @test SparseArrays.CHOLMOD.Dense(ones(Int, 5)) == ones(5, 1) + @test SparseArrays.CHOLMOD.Dense(ones(Complex{Float32}, 5, 2)) == ones(5, 2) +end + +@testset "Further issue with promotion #14894" begin + @test cholfact(speye(Float16, 5))\ones(5) == ones(5) + @test cholfact(Symmetric(speye(Float16, 5)))\ones(5) == ones(5) + @test cholfact(Hermitian(speye(Complex{Float16}, 5)))\ones(5) == ones(Complex{Float64}, 5) + @test_throws MethodError cholfact(speye(BigFloat, 5)) + @test_throws MethodError cholfact(Symmetric(speye(BigFloat, 5))) + @test_throws MethodError cholfact(Hermitian(speye(Complex{BigFloat}, 5))) +end + +@testset "test \\ for Factor and StridedVecOrMat" begin + x = rand(5) A = cholfact(sparse(Diagonal(x.\1))) @test A\view(ones(10),1:2:10) ≈ x @test A\view(eye(5,5),:,:) ≈ Matrix(Diagonal(x)) end -# Real factorization and complex rhs -let A = sprandn(5, 5, 0.4) |> t -> t't + I, +@testset "Real factorization and complex rhs" begin + A = sprandn(5, 5, 0.4) |> t -> t't + I B = complex.(randn(5, 2), randn(5, 2)) @test cholfact(A)\B ≈ A\B end -# Make sure that ldltfact performs an LDLt (Issue #19032) -let m = 400, n = 500, - A = sprandn(m, n, .2), - M = [speye(n) A'; A -speye(m)], - b = M * ones(m + n), - F = ldltfact(M), +@testset "Make sure that ldltfact performs an LDLt (Issue #19032)" begin + m, n = 400, 500 + A = sprandn(m, n, .2) + M = [speye(n) A'; A -speye(m)] + b = M * ones(m + n) + F = ldltfact(M) s = unsafe_load(pointer(F)) @test s.is_super == 0 @test F\b ≈ ones(m + n) end -# Test that \ and '\ and .'\ work for Symmetric and Hermitian. This is just -# a dispatch exercise so it doesn't matter that the complex matrix has -# zero imaginary parts -let Apre = sprandn(10, 10, 0.2) - I +@testset "\\ '\\ and .'\\" begin + # Test that \ and '\ and .'\ work for Symmetric and Hermitian. This is just + # a dispatch exercise so it doesn't matter that the complex matrix has + # zero imaginary parts + Apre = sprandn(10, 10, 0.2) - I for A in (Symmetric(Apre), Hermitian(Apre), Symmetric(Apre + 10I), Hermitian(Apre + 10I), Hermitian(complex(Apre)), Hermitian(complex(Apre) + 10I)) @@ -680,9 +708,9 @@ let Apre = sprandn(10, 10, 0.2) - I end end -# Check that Symmetric{SparseMatrixCSC} can be constructed from CHOLMOD.Sparse -let A = sprandn(10, 10, 0.1), - B = SparseArrays.CHOLMOD.Sparse(A), +@testset "Check that Symmetric{SparseMatrixCSC} can be constructed from CHOLMOD.Sparse" begin + A = sprandn(10, 10, 0.1) + B = SparseArrays.CHOLMOD.Sparse(A) C = B'B # Change internal representation to symmetric (upper/lower) o = fieldoffset(CHOLMOD.C_Sparse{eltype(C)}, find(fieldnames(CHOLMOD.C_Sparse{eltype(C)}) .== :stype)[1]) @@ -710,42 +738,43 @@ end end end -#Test sparse low rank update for cholesky decomposion -A = SparseMatrixCSC{Float64,CHOLMOD.SuiteSparse_long}(10, 5, [1,3,6,8,10,13], [6,7,1,2,9,3,5,1,7,6,7,9], - [-0.138843, 2.99571, -0.556814, 0.669704, -1.39252, 1.33814, - 1.02371, -0.502384, 1.10686, 0.262229, -1.6935, 0.525239]) -AtA = A'*A; -C0 = [1., 2., 0, 0, 0] -#Test both cholfact and LDLt with and without automatic permutations -for F in (cholfact(AtA), cholfact(AtA, perm=1:5), ldltfact(AtA), ldltfact(AtA, perm=1:5)) - local F - B0 = F\ones(5) - #Test both sparse/dense and vectors/matrices - for Ctest in (C0, sparse(C0), [C0 2*C0], sparse([C0 2*C0])) - local B, C, F1 - C = copy(Ctest) - F1 = copy(F) - B = (AtA+C*C')\ones(5) - - #Test update - F11 = CHOLMOD.lowrankupdate(F1, C) - @test Array(sparse(F11)) ≈ AtA+C*C' - @test F11\ones(5) ≈ B - #Make sure we get back the same factor again - F10 = CHOLMOD.lowrankdowndate(F11, C) - @test Array(sparse(F10)) ≈ AtA - @test F10\ones(5) ≈ B0 - - #Test in-place update - CHOLMOD.lowrankupdate!(F1, C) - @test Array(sparse(F1)) ≈ AtA+C*C' - @test F1\ones(5) ≈ B - #Test in-place downdate - CHOLMOD.lowrankdowndate!(F1, C) - @test Array(sparse(F1)) ≈ AtA - @test F1\ones(5) ≈ B0 - - @test C == Ctest #Make sure C didn't change +@testset "Test sparse low rank update for cholesky decomposion" begin + A = SparseMatrixCSC{Float64,CHOLMOD.SuiteSparse_long}(10, 5, [1,3,6,8,10,13], [6,7,1,2,9,3,5,1,7,6,7,9], + [-0.138843, 2.99571, -0.556814, 0.669704, -1.39252, 1.33814, + 1.02371, -0.502384, 1.10686, 0.262229, -1.6935, 0.525239]) + AtA = A'*A + C0 = [1., 2., 0, 0, 0] + # Test both cholfact and LDLt with and without automatic permutations + for F in (cholfact(AtA), cholfact(AtA, perm=1:5), ldltfact(AtA), ldltfact(AtA, perm=1:5)) + local F + B0 = F\ones(5) + #Test both sparse/dense and vectors/matrices + for Ctest in (C0, sparse(C0), [C0 2*C0], sparse([C0 2*C0])) + local B, C, F1 + C = copy(Ctest) + F1 = copy(F) + B = (AtA+C*C')\ones(5) + + #Test update + F11 = CHOLMOD.lowrankupdate(F1, C) + @test Array(sparse(F11)) ≈ AtA+C*C' + @test F11\ones(5) ≈ B + #Make sure we get back the same factor again + F10 = CHOLMOD.lowrankdowndate(F11, C) + @test Array(sparse(F10)) ≈ AtA + @test F10\ones(5) ≈ B0 + + #Test in-place update + CHOLMOD.lowrankupdate!(F1, C) + @test Array(sparse(F1)) ≈ AtA+C*C' + @test F1\ones(5) ≈ B + #Test in-place downdate + CHOLMOD.lowrankdowndate!(F1, C) + @test Array(sparse(F1)) ≈ AtA + @test F1\ones(5) ≈ B0 + + @test C == Ctest #Make sure C didn't change + end end end @@ -760,4 +789,3 @@ end A[3, 3] = 1 @test A[:, 3:-1:1]\ones(3) == [1, 1, 1] end - From ae51693ad4ce45837e3fea96ccfe26051a2ab2ac Mon Sep 17 00:00:00 2001 From: Ronan Arraes Jardim Chagas Date: Fri, 3 Nov 2017 12:39:22 -0200 Subject: [PATCH 29/34] Clarify precedence of numeric literal coefs over parenthesis (#21800) --- doc/src/manual/integers-and-floating-point-numbers.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 0934e19bd5e62..43387758f5cd9 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -613,6 +613,11 @@ Numeric literals also work as coefficients to parenthesized expressions: julia> 2(x-1)^2 - 3(x-1) + 1 3 ``` +!!! note + The precedence of numeric literal coefficients used for implicit + multiplication is higher than other binary operators such as multiplication + (`*`), and division (`/`, `\`, and `//`). This means, for example, that + `1 / 2im` equals `-0.5im` and `6 // 2(2 + 1)` equals `1 // 1`. Additionally, parenthesized expressions can be used as coefficients to variables, implying multiplication of the expression by the variable: From d929f0b8265a11a209e9d6cffa8af12b387f3a56 Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 3 Nov 2017 10:51:17 -0400 Subject: [PATCH 30/34] refactor cartesian.jl to use dispatch in macros (#24450) add a couple helpful type declarations --- base/abstractarray.jl | 2 +- base/array.jl | 2 +- base/cartesian.jl | 60 ++++++++++++------------------------------- base/precompile.jl | 4 --- 4 files changed, 18 insertions(+), 50 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 15778821a84ab..b605cdcb7f183 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -529,7 +529,7 @@ similar(a::AbstractArray, ::Type{T}, dims::Dims{N}) where {T,N} = Array{T,N}( to_shape(::Tuple{}) = () to_shape(dims::Dims) = dims -to_shape(dims::DimsOrInds) = map(to_shape, dims) +to_shape(dims::DimsOrInds) = map(to_shape, dims)::DimsOrInds # each dimension to_shape(i::Int) = i to_shape(i::Integer) = Int(i) diff --git a/base/array.jl b/base/array.jl index 8a002399ec862..4a49a373aaacb 100644 --- a/base/array.jl +++ b/base/array.jl @@ -645,7 +645,7 @@ else end _array_for(::Type{T}, itr, ::HasLength) where {T} = Array{T,1}(Int(length(itr)::Integer)) -_array_for(::Type{T}, itr, ::HasShape) where {T} = similar(Array{T}, indices(itr)) +_array_for(::Type{T}, itr, ::HasShape) where {T} = similar(Array{T}, indices(itr))::Array{T} function collect(itr::Generator) isz = iteratorsize(itr.iter) diff --git a/base/cartesian.jl b/base/cartesian.jl index b944617726345..77af57775ea85 100644 --- a/base/cartesian.jl +++ b/base/cartesian.jl @@ -81,12 +81,8 @@ julia> @macroexpand Base.Cartesian.@nref 3 A i :(A[i_1, i_2, i_3]) ``` """ -macro nref(N, A, sym) - _nref(N, A, sym) -end - -function _nref(N::Int, A::Symbol, ex) - vars = [ inlineanonymous(ex,i) for i = 1:N ] +macro nref(N::Int, A::Symbol, ex) + vars = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:ref, A, vars...)) end @@ -105,14 +101,10 @@ while `@ncall 2 func a b i->c[i]` yields func(a, b, c[1], c[2]) """ -macro ncall(N, f, sym...) - _ncall(N, f, sym...) -end - -function _ncall(N::Int, f, args...) +macro ncall(N::Int, f, args...) pre = args[1:end-1] ex = args[end] - vars = [ inlineanonymous(ex,i) for i = 1:N ] + vars = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:call, f, pre..., vars...)) end @@ -132,12 +124,8 @@ quote end ``` """ -macro nexprs(N, ex) - _nexprs(N, ex) -end - -function _nexprs(N::Int, ex::Expr) - exs = [ inlineanonymous(ex,i) for i = 1:N ] +macro nexprs(N::Int, ex::Expr) + exs = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:block, exs...)) end @@ -159,17 +147,13 @@ while `@nextract 3 x d->y[2d-1]` yields x_3 = y[5] """ -macro nextract(N, esym, isym) - _nextract(N, esym, isym) -end - -function _nextract(N::Int, esym::Symbol, isym::Symbol) - aexprs = [Expr(:escape, Expr(:(=), inlineanonymous(esym, i), :(($isym)[$i]))) for i = 1:N] +macro nextract(N::Int, esym::Symbol, isym::Symbol) + aexprs = Any[ Expr(:escape, Expr(:(=), inlineanonymous(esym, i), :(($isym)[$i]))) for i = 1:N ] Expr(:block, aexprs...) end -function _nextract(N::Int, esym::Symbol, ex::Expr) - aexprs = [Expr(:escape, Expr(:(=), inlineanonymous(esym, i), inlineanonymous(ex,i))) for i = 1:N] +macro nextract(N::Int, esym::Symbol, ex::Expr) + aexprs = Any[ Expr(:escape, Expr(:(=), inlineanonymous(esym, i), inlineanonymous(ex,i))) for i = 1:N ] Expr(:block, aexprs...) end @@ -182,15 +166,11 @@ evaluate to `true`. `@nall 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 && i_2 > 1 && i_3 > 1)`. This can be convenient for bounds-checking. """ -macro nall(N, criterion) - _nall(N, criterion) -end - -function _nall(N::Int, criterion::Expr) +macro nall(N::Int, criterion::Expr) if criterion.head != :-> throw(ArgumentError("second argument must be an anonymous function expression yielding the criterion")) end - conds = [Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N] + conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] Expr(:&&, conds...) end @@ -202,15 +182,11 @@ evaluate to `true`. `@nany 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 || i_2 > 1 || i_3 > 1)`. """ -macro nany(N, criterion) - _nany(N, criterion) -end - -function _nany(N::Int, criterion::Expr) +macro nany(N::Int, criterion::Expr) if criterion.head != :-> error("Second argument must be an anonymous function expression yielding the criterion") end - conds = [Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N] + conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] Expr(:||, conds...) end @@ -220,12 +196,8 @@ end Generates an `N`-tuple. `@ntuple 2 i` would generate `(i_1, i_2)`, and `@ntuple 2 k->k+1` would generate `(2,3)`. """ -macro ntuple(N, ex) - _ntuple(N, ex) -end - -function _ntuple(N::Int, ex) - vars = [ inlineanonymous(ex,i) for i = 1:N ] +macro ntuple(N::Int, ex) + vars = Any[ inlineanonymous(ex,i) for i = 1:N ] Expr(:escape, Expr(:tuple, vars...)) end diff --git a/base/precompile.jl b/base/precompile.jl index 9f51297b22a48..2ccd832138828 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -818,7 +818,6 @@ precompile(Tuple{typeof(Base.length), Tuple{DataType, DataType}}) precompile(Tuple{Type{BoundsError}, Array{Int64, 2}, Tuple{Base.UnitRange{Int64}, Int64}}) precompile(Tuple{typeof(Base.throw_boundserror), Array{Int64, 2}, Tuple{Base.UnitRange{Int64}, Int64}}) precompile(Tuple{getfield(Base.Cartesian, Symbol("#@nexprs")), Int64, Expr}) -precompile(Tuple{typeof(Base.Cartesian._nexprs), Int64, Expr}) precompile(Tuple{typeof(Core.Inference.builtin_tfunction), typeof(===), Array{Any, 1}, Core.Inference.InferenceState, Core.Inference.InferenceParams}) precompile(Tuple{typeof(Core.Inference.typeinf_frame), Core.MethodInstance, Bool, Bool, Core.Inference.InferenceParams}) precompile(Tuple{typeof(Core.Inference.typeinf), Core.Inference.InferenceState}) @@ -837,14 +836,11 @@ precompile(Tuple{typeof(Base.Cartesian.lreplace!), String, Base.Cartesian.LRepla precompile(Tuple{typeof(Base.Cartesian.exprresolve), Expr}) precompile(Tuple{Type{BoundsError}, Array{Expr, 1}, Base.UnitRange{Int64}}) precompile(Tuple{getfield(Base.Cartesian, Symbol("#@ncall")), Int64, Symbol, Symbol}) -precompile(Tuple{typeof(Base.Cartesian._ncall), Int64, Symbol, Symbol}) precompile(Tuple{typeof(Base.getindex), Tuple{Symbol}, Base.UnitRange{Int64}}) precompile(Tuple{getfield(Base.Cartesian, Symbol("#@ncall")), Int64, Symbol, Symbol, Expr}) -precompile(Tuple{typeof(Base.Cartesian._ncall), Int64, Symbol, Symbol, Expr}) precompile(Tuple{typeof(Base.endof), Tuple{Symbol, Expr}}) precompile(Tuple{typeof(Base.getindex), Tuple{Symbol, Expr}, Base.UnitRange{Int64}}) precompile(Tuple{getfield(Base.Cartesian, Symbol("#@nloops")), Int64, Symbol, Expr, Expr}) -precompile(Tuple{typeof(Base.Cartesian._nloops), Int64, Symbol, Expr, Expr}) precompile(Tuple{typeof(Base.endof), Tuple{Expr}}) precompile(Tuple{typeof(Base.endof), Tuple{Symbol, Symbol, Symbol}}) precompile(Tuple{typeof(Base.getindex), Tuple{Symbol, Symbol, Symbol}, Base.UnitRange{Int64}}) From 1bb71e9e1bf58d640ba5c96f49eb312eacd74488 Mon Sep 17 00:00:00 2001 From: Marco van Hulten Date: Fri, 3 Nov 2017 20:41:20 +0100 Subject: [PATCH 31/34] update README wording on system package managers (#24456) - [clarification] a package manager does the installing, there are usually not specialised Julia installers, so the package manager installs Julia; - [grammar] neither/nor go well together. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2cc6628ab2c19..9db95063617ea 100644 --- a/README.md +++ b/README.md @@ -402,8 +402,7 @@ On Windows, double-click `usr/bin/julia.exe`. If everything works correctly, you will see a Julia banner and an interactive prompt into which you can enter expressions for evaluation. You can read about [getting started](https://julialang.org/manual/getting-started) in the manual. -**Note**: While some system package managers have Julia installers available, -these are not maintained nor endorsed by the Julia project. They may be outdated +**Note**: Although some system package managers provide Julia, such installations are neither maintained nor endorsed by the Julia project. They may be outdated and/or unmaintained. We recommend you use the official Julia binaries instead. ## Editor and Terminal Setup From 8cd97c5980c0a6fb0860996399f49b2679aedddd Mon Sep 17 00:00:00 2001 From: Jeff Bezanson Date: Fri, 3 Nov 2017 15:47:03 -0400 Subject: [PATCH 32/34] remove redundant `type_alignment` function (#24434) --- base/reflection.jl | 2 -- src/datatype.c | 7 ------- test/reflection.jl | 6 +++--- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index b7cf6c4ffbf55..63165a62226db 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -462,8 +462,6 @@ function fieldindex(T::DataType, name::Symbol, err::Bool=true) return Int(ccall(:jl_field_index, Cint, (Any, Any, Cint), T, name, err)+1) end -type_alignment(x::DataType) = (@_pure_meta; ccall(:jl_get_alignment, Csize_t, (Any,), x)) - """ fieldcount(t::Type) diff --git a/src/datatype.c b/src/datatype.c index 9ebc3a6d0ac15..3f1f27603651a 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -855,13 +855,6 @@ JL_DLLEXPORT size_t jl_get_field_offset(jl_datatype_t *ty, int field) return jl_field_offset(ty, field - 1); } -JL_DLLEXPORT size_t jl_get_alignment(jl_datatype_t *ty) -{ - if (ty->layout == NULL) - jl_error("non-leaf type doesn't have an alignment"); - return jl_datatype_align(ty); -} - #ifdef __cplusplus } #endif diff --git a/test/reflection.jl b/test/reflection.jl index 72b8d6487d3fc..87eb59d8344bf 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -342,13 +342,13 @@ tlayout = TLayout(5,7,11) @test_throws BoundsError fieldname(NTuple{3, Int}, 0) @test_throws BoundsError fieldname(NTuple{3, Int}, 4) -import Base: isstructtype, type_alignment, return_types +import Base: isstructtype, datatype_alignment, return_types @test !isstructtype(Union{}) @test !isstructtype(Union{Int,Float64}) @test !isstructtype(Int) @test isstructtype(TLayout) -@test type_alignment(UInt16) == 2 -@test type_alignment(TLayout) == 4 +@test datatype_alignment(UInt16) == 2 +@test datatype_alignment(TLayout) == 4 let rts = return_types(TLayout) @test length(rts) >= 3 # general constructor, specific constructor, and call-to-convert adapter(s) @test all(rts .== TLayout) From 33763a01c3af2d3fe543f2f3ca253cd3a1421e82 Mon Sep 17 00:00:00 2001 From: Katharine Hyatt Date: Fri, 3 Nov 2017 14:33:44 -0700 Subject: [PATCH 33/34] Add test, fix bug for chol and test for tridiag (#24411) --- base/linalg/cholesky.jl | 2 +- test/linalg/cholesky.jl | 6 ++++-- test/linalg/tridiag.jl | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/base/linalg/cholesky.jl b/base/linalg/cholesky.jl index 5efb8bd0741e7..aa7e82a5c4d37 100644 --- a/base/linalg/cholesky.jl +++ b/base/linalg/cholesky.jl @@ -406,7 +406,7 @@ function show(io::IO, C::Cholesky{<:Any,<:AbstractMatrix}) println(io, "$(typeof(C)) with factor:") show(io, C[:UL]) else - print("Failed factorization of type $(typeof(C))") + print(io, "Failed factorization of type $(typeof(C))") end end diff --git a/test/linalg/cholesky.jl b/test/linalg/cholesky.jl index eb8d811b1aba2..41b7093f3426a 100644 --- a/test/linalg/cholesky.jl +++ b/test/linalg/cholesky.jl @@ -13,7 +13,7 @@ function unary_ops_tests(a, ca, tol; n=size(a, 1)) @test isposdef(ca) @test_throws KeyError ca[:Z] @test size(ca) == size(a) - @test Matrix(copy(ca)) ≈ a + @test Array(copy(ca)) ≈ a end function factor_recreation_tests(a_U, a_L) @@ -21,7 +21,7 @@ function factor_recreation_tests(a_U, a_L) c_L = cholfact(a_L) cl = chol(a_L) ls = c_L[:L] - @test Matrix(c_U) ≈ Matrix(c_L) ≈ a_U + @test Array(c_U) ≈ Array(c_L) ≈ a_U @test ls*ls' ≈ a_U @test triu(c_U.factors) ≈ c_U[:U] @test tril(c_L.factors) ≈ c_L[:L] @@ -180,6 +180,8 @@ end C = cholfact(A) @test !isposdef(C) @test !LinAlg.issuccess(C) + Cstr = sprint(show, C) + @test Cstr == "Failed factorization of type $(typeof(C))" @test_throws PosDefException C\B @test_throws PosDefException det(C) @test_throws PosDefException logdet(C) diff --git a/test/linalg/tridiag.jl b/test/linalg/tridiag.jl index 85276d686512c..3c1384565284c 100644 --- a/test/linalg/tridiag.jl +++ b/test/linalg/tridiag.jl @@ -288,7 +288,7 @@ guardsrand(123) do invFsv = Fs\vv x = Ts\vv @test x ≈ invFsv - @test Array(AbstractArray(Tldlt)) ≈ Fs + @test Array(Tldlt) ≈ Fs end @testset "similar" begin From 35c74af1b9360746a1b16d4bd44d1c628787b4b9 Mon Sep 17 00:00:00 2001 From: Sacha Verweij Date: Thu, 26 Oct 2017 16:42:01 -0700 Subject: [PATCH 34/34] Deprecate speye. --- NEWS.md | 3 + base/deprecated.jl | 49 +++++++++ base/essentials.jl | 2 +- base/exports.jl | 1 - base/linalg/arnoldi.jl | 2 +- base/sparse/cholmod.jl | 2 +- base/sparse/sparse.jl | 2 +- base/sparse/sparsematrix.jl | 111 ++++++-------------- doc/src/manual/arrays.md | 16 +-- doc/src/stdlib/arrays.md | 2 - test/arrayops.jl | 2 +- test/hashing.jl | 2 +- test/linalg/arnoldi.jl | 2 +- test/linalg/special.jl | 2 +- test/linalg/uniformscaling.jl | 2 +- test/perf/kernel/getdivgrad.jl | 6 +- test/perf/sparse/fem.jl | 2 +- test/show.jl | 2 +- test/sparse/cholmod.jl | 27 ++--- test/sparse/higherorderfns.jl | 2 +- test/sparse/sparse.jl | 181 +++++++++++++++++---------------- test/sparse/sparsevector.jl | 6 +- test/sparse/umfpack.jl | 12 +-- 23 files changed, 219 insertions(+), 219 deletions(-) diff --git a/NEWS.md b/NEWS.md index 1126cb2d11796..8c7fbc1a523b8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -509,6 +509,9 @@ Deprecated or removed particularly, consider instead `triu!(copy(parent(A)))`. On `LowerTriangular` matrices `A` particularly, consider instead `tril!(copy(parent(A)))` ([#24250]). + * `speye` has been deprecated in favor of `I`, `sparse`, and `SparseMatrixCSC` + constructor methods ([#24356]). + * Calling `union` with no arguments is deprecated; construct an empty set with an appropriate element type using `Set{T}()` instead ([#23144]). diff --git a/base/deprecated.jl b/base/deprecated.jl index ee81e772f9360..8fc2b2c1e554c 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -2070,6 +2070,55 @@ end # deprecate bits to bitstring (#24263, #24281) @deprecate bits bitstring +# deprecate speye +export speye +function speye(n::Integer) + depwarn(string("`speye(n::Integer)` has been deprecated in favor of `I`, `sparse`, and ", + "`SparseMatrixCSC` constructor methods. For a direct replacement, consider ", + "`sparse(1.0I, n, n)`, `SparseMatrixCSC(1.0I, n, n)`, or `SparseMatrixCSC{Float64}(I, n, n)`. ", + "If `Float64` element type is not necessary, consider the shorter `sparse(I, n, n)` ", + "or `SparseMatrixCSC(I, n, n)` (with default `eltype(I)` of `Bool`)."), :speye) + return sparse(1.0I, n, n) +end +function speye(m::Integer, n::Integer) + depwarn(string("`speye(m::Integer, n::Integer)` has been deprecated in favor of `I`, ", + "`sparse`, and `SparseMatrixCSC` constructor methods. For a direct ", + "replacement, consider `sparse(1.0I, m, n)`, `SparseMatrixCSC(1.0I, m, n)`, ", + "or `SparseMatrixCSC{Float64}(I, m, n)`. If `Float64` element type is not ", + " necessary, consider the shorter `sparse(I, m, n)` or `SparseMatrixCSC(I, m, n)` ", + "(with default `eltype(I)` of `Bool`)."), :speye) + return sparse(1.0I, m, n) +end +function speye(::Type{T}, n::Integer) where T + depwarn(string("`speye(T, n::Integer)` has been deprecated in favor of `I`, `sparse`, and ", + "`SparseMatrixCSC` constructor methods. For a direct replacement, consider ", + "`sparse(T(1)I, n, n)` if `T` is concrete or `SparseMatrixCSC{T}(I, n, n)` ", + "if `T` is either concrete or abstract. If element type `T` is not necessary, ", + "consider the shorter `sparse(I, n, n)` or `SparseMatrixCSC(I, n, n)` ", + "(with default `eltype(I)` of `Bool`)."), :speye) + return SparseMatrixCSC{T}(I, n) +end +function speye(::Type{T}, m::Integer, n::Integer) where T + depwarn(string("`speye(T, m::Integer, n::Integer)` has been deprecated in favor of `I`, ", + "`sparse`, and `SparseMatrixCSC` constructor methods. For a direct ", + "replacement, consider `sparse(T(1)I, m, n)` if `T` is concrete or ", + "`SparseMatrixCSC{T}(I, m, n)` if `T` is either concrete or abstract. ", + "If element type `T` is not necessary, consider the shorter ", + "`sparse(I, m, n)` or `SparseMatrixCSC(I, m, n)` (with default `eltype(I)` ", + "of `Bool`)."), :speye) + return SparseMatrixCSC{T}(I, m, n) +end +function speye(S::SparseMatrixCSC{T}) where T + depwarn(string("`speye(S::SparseMatrixCSC{T})` has been deprecated in favor of `I`, ", + "`sparse`, and `SparseMatrixCSC` constructor methods. For a direct ", + "replacement, consider `sparse(T(1)I, size(S)...)` if `T` is concrete or ", + "`SparseMatrixCSC{eltype(S)}(I, size(S))` if `T` is either concrete or abstract. ", + "If preserving element type `T` is not necessary, consider the shorter ", + "`sparse(I, size(S)...)` or `SparseMatrixCSC(I, size(S))` (with default ", + "`eltype(I)` of `Bool`)."), :speye) + return SparseMatrixCSC{T}(I, m, n) +end + # issue #24167 @deprecate EnvHash EnvDict diff --git a/base/essentials.jl b/base/essentials.jl index cc4e01f647709..cdfc50f9a272f 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -119,7 +119,7 @@ true Similarly, if `T` is a composite type and `x` a related instance, the result of `convert(T, x)` may alias part or all of `x`. ```jldoctest -julia> x = speye(5); +julia> x = sparse(1.0I, 5, 5); julia> typeof(x) SparseMatrixCSC{Float64,Int64} diff --git a/base/exports.jl b/base/exports.jl index 0e30396010261..df357ab5e28b3 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1256,7 +1256,6 @@ export sparse, sparsevec, spdiagm, - speye, spones, sprand, sprandn, diff --git a/base/linalg/arnoldi.jl b/base/linalg/arnoldi.jl index 70e696c83eb66..fe05312d94f27 100644 --- a/base/linalg/arnoldi.jl +++ b/base/linalg/arnoldi.jl @@ -145,7 +145,7 @@ final residual vector `resid`. # Examples ```jldoctest -julia> A = speye(4, 4); B = Diagonal(1:4); +julia> A = sparse(1.0I, 4, 4); B = Diagonal(1:4); julia> λ, ϕ = eigs(A, B, nev = 2); diff --git a/base/sparse/cholmod.jl b/base/sparse/cholmod.jl index 4939f1af57890..49114148e96c3 100644 --- a/base/sparse/cholmod.jl +++ b/base/sparse/cholmod.jl @@ -16,7 +16,7 @@ export Factor, Sparse -import ..SparseArrays: AbstractSparseMatrix, SparseMatrixCSC, increment, indtype, sparse, speye, +import ..SparseArrays: AbstractSparseMatrix, SparseMatrixCSC, increment, indtype, sparse, spzeros, nnz ######### diff --git a/base/sparse/sparse.jl b/base/sparse/sparse.jl index 4bffa183a8fbb..af3b16bc873dc 100644 --- a/base/sparse/sparse.jl +++ b/base/sparse/sparse.jl @@ -31,7 +31,7 @@ import Base: @get!, acos, acosd, acot, acotd, acsch, asech, asin, asind, asinh, export AbstractSparseArray, AbstractSparseMatrix, AbstractSparseVector, SparseMatrixCSC, SparseVector, blkdiag, droptol!, dropzeros!, dropzeros, - issparse, nonzeros, nzrange, rowvals, sparse, sparsevec, spdiagm, speye, spones, + issparse, nonzeros, nzrange, rowvals, sparse, sparsevec, spdiagm, spones, sprand, sprandn, spzeros, nnz, permute include("abstractsparse.jl") diff --git a/base/sparse/sparsematrix.jl b/base/sparse/sparsematrix.jl index 32c242a7a1984..0d80afc3a8f3d 100644 --- a/base/sparse/sparsematrix.jl +++ b/base/sparse/sparsematrix.jl @@ -59,11 +59,11 @@ Returns the number of stored (filled) elements in a sparse array. # Examples ```jldoctest -julia> A = speye(3) -3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 +julia> A = sparse(2I, 3, 3) +3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries: + [1, 1] = 2 + [2, 2] = 2 + [3, 3] = 2 julia> nnz(A) 3 @@ -83,17 +83,17 @@ modifications to the returned vector will mutate `A` as well. See # Examples ```jldoctest -julia> A = speye(3) -3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 +julia> A = sparse(2I, 3, 3) +3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries: + [1, 1] = 2 + [2, 2] = 2 + [3, 3] = 2 julia> nonzeros(A) -3-element Array{Float64,1}: - 1.0 - 1.0 - 1.0 +3-element Array{Int64,1}: + 2 + 2 + 2 ``` """ nonzeros(S::SparseMatrixCSC) = S.nzval @@ -108,11 +108,11 @@ nonzero values. See also [`nonzeros`](@ref) and [`nzrange`](@ref). # Examples ```jldoctest -julia> A = speye(3) -3×3 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 +julia> A = sparse(2I, 3, 3) +3×3 SparseMatrixCSC{Int64,Int64} with 3 stored entries: + [1, 1] = 2 + [2, 2] = 2 + [3, 3] = 2 julia> rowvals(A) 3-element Array{Int64,1}: @@ -1453,8 +1453,6 @@ julia> spones(A) [3, 3] = 1.0 [2, 4] = 1.0 ``` - -Note the difference from [`speye`](@ref). """ spones(S::SparseMatrixCSC{T}) where {T} = SparseMatrixCSC(S.m, S.n, copy(S.colptr), copy(S.rowval), ones(T, S.colptr[end]-1)) @@ -1487,55 +1485,11 @@ function spzeros(::Type{Tv}, ::Type{Ti}, sz::Tuple{Integer,Integer}) where {Tv, spzeros(Tv, Ti, sz[1], sz[2]) end -speye(n::Integer) = speye(Float64, n) -speye(::Type{T}, n::Integer) where {T} = speye(T, n, n) -speye(m::Integer, n::Integer) = speye(Float64, m, n) - -""" - speye(S) - -Create a sparse identity matrix with the same size as `S`. - -# Examples -```jldoctest -julia> A = sparse([1,2,3,4],[2,4,3,1],[5.,4.,3.,2.]) -4×4 SparseMatrixCSC{Float64,Int64} with 4 stored entries: - [4, 1] = 2.0 - [1, 2] = 5.0 - [3, 3] = 3.0 - [2, 4] = 4.0 - -julia> speye(A) -4×4 SparseMatrixCSC{Float64,Int64} with 4 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 - [4, 4] = 1.0 -``` - -Note the difference from [`spones`](@ref). -""" -speye(S::SparseMatrixCSC{T}) where {T} = speye(T, size(S, 1), size(S, 2)) -eye(S::SparseMatrixCSC) = speye(S) - -""" - speye([type,]m[,n]) - -Create a sparse identity matrix of size `m x m`. When `n` is supplied, -create a sparse identity matrix of size `m x n`. The type defaults to [`Float64`](@ref) -if not specified. - -`sparse(I, m, n)` is equivalent to `speye(Int, m, n)`, and -`sparse(α*I, m, n)` can be used to efficiently create a sparse -multiple `α` of the identity matrix. -""" -speye(::Type{T}, m::Integer, n::Integer) where {T} = SparseMatrixCSC{T}(UniformScaling(one(T)), Dims((m, n))) -sparse(s::UniformScaling, m::Integer, n::Integer=m) = SparseMatrixCSC(s, Dims((m, n))) +eye(S::SparseMatrixCSC{T}) where {T} = SparseMatrixCSC{T}(I, size(S)) function one(S::SparseMatrixCSC{T}) where T - m,n = size(S) - if m != n; throw(DimensionMismatch("multiplicative identity only defined for square matrices")); end - speye(T, m) + S.m == S.n || throw(DimensionMismatch("multiplicative identity only defined for square matrices")) + return SparseMatrixCSC{T}(I, S.m, S.n) end ## SparseMatrixCSC construction from UniformScaling @@ -1573,6 +1527,7 @@ function Base.isone(A::SparseMatrixCSC) return true end +sparse(s::UniformScaling, m::Integer, n::Integer=m) = SparseMatrixCSC(s, m, n) # TODO: More appropriate location? conj!(A::SparseMatrixCSC) = (@inbounds broadcast!(conj, A.nzval, A.nzval); A) @@ -3141,13 +3096,13 @@ Concatenate matrices block-diagonally. Currently only implemented for sparse mat # Examples ```jldoctest -julia> blkdiag(speye(3), 2*speye(2)) -5×5 SparseMatrixCSC{Float64,Int64} with 5 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 - [4, 4] = 2.0 - [5, 5] = 2.0 +julia> blkdiag(sparse(2I, 3, 3), sparse(4I, 2, 2)) +5×5 SparseMatrixCSC{Int64,Int64} with 5 stored entries: + [1, 1] = 2 + [2, 2] = 2 + [3, 3] = 2 + [4, 4] = 4 + [5, 5] = 4 ``` """ function blkdiag(X::SparseMatrixCSC...) @@ -3599,6 +3554,6 @@ end ## Uniform matrix arithmetic -(+)(A::SparseMatrixCSC, J::UniformScaling) = A + J.λ * speye(A) -(-)(A::SparseMatrixCSC, J::UniformScaling) = A - J.λ * speye(A) -(-)(J::UniformScaling, A::SparseMatrixCSC) = J.λ * speye(A) - A +(+)(A::SparseMatrixCSC, J::UniformScaling) = A + sparse(J, size(A)...) +(-)(A::SparseMatrixCSC, J::UniformScaling) = A - sparse(J, size(A)...) +(-)(J::UniformScaling, A::SparseMatrixCSC) = sparse(J, size(A)...) - A diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index 0bc4a53a53f8f..916b8695c5099 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -781,19 +781,13 @@ stored zeros. (See [Sparse Matrix Storage](@ref man-csc).). ### Sparse Vector and Matrix Constructors -The simplest way to create sparse arrays is to use functions equivalent to the [`zeros`](@ref) -and [`eye`](@ref) functions that Julia provides for working with dense arrays. To produce -sparse arrays instead, you can use the same names with an `sp` prefix: +The simplest way to create a sparse array is to use a function equivalent to the [`zeros`](@ref) +function that Julia provides for working with dense arrays. To produce a +sparse array instead, you can use the same name with an `sp` prefix: ```jldoctest julia> spzeros(3) 3-element SparseVector{Float64,Int64} with 0 stored entries - -julia> speye(3,5) -3×5 SparseMatrixCSC{Float64,Int64} with 3 stored entries: - [1, 1] = 1.0 - [2, 2] = 1.0 - [3, 3] = 1.0 ``` The [`sparse`](@ref) function is often a handy way to construct sparse arrays. For @@ -867,7 +861,7 @@ You can go in the other direction using the [`Array`](@ref) constructor. The [`i function can be used to query if a matrix is sparse. ```jldoctest -julia> issparse(speye(5)) +julia> issparse(spzeros(5)) true ``` @@ -895,7 +889,7 @@ section of the standard library reference. |:-------------------------- |:---------------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`spzeros(m,n)`](@ref) | [`zeros(m,n)`](@ref) | Creates a *m*-by-*n* matrix of zeros. ([`spzeros(m,n)`](@ref) is empty.) | | [`spones(S)`](@ref) | [`ones(m,n)`](@ref) | Creates a matrix filled with ones. Unlike the dense version, [`spones`](@ref) has the same sparsity pattern as *S*. | -| [`speye(n)`](@ref) | [`eye(n)`](@ref) | Creates a *n*-by-*n* identity matrix. | +| [`sparse(I, n, n)`](@ref) | [`eye(n)`](@ref) | Creates a *n*-by-*n* identity matrix. | | [`Array(S)`](@ref) | [`sparse(A)`](@ref) | Interconverts between dense and sparse formats. | | [`sprand(m,n,d)`](@ref) | [`rand(m,n)`](@ref) | Creates a *m*-by-*n* random matrix (of density *d*) with iid non-zero elements distributed uniformly on the half-open interval ``[0, 1)``. | | [`sprandn(m,n,d)`](@ref) | [`randn(m,n)`](@ref) | Creates a *m*-by-*n* random matrix (of density *d*) with iid non-zero elements distributed according to the standard normal (Gaussian) distribution. | diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 0073510805c5f..03669888f18f9 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -188,8 +188,6 @@ Base.SparseArrays.issparse Base.SparseArrays.nnz Base.SparseArrays.spzeros Base.SparseArrays.spones -Base.SparseArrays.speye(::Type, ::Integer, ::Integer) -Base.SparseArrays.speye(::SparseMatrixCSC) Base.SparseArrays.spdiagm Base.SparseArrays.sprand Base.SparseArrays.sprandn diff --git a/test/arrayops.jl b/test/arrayops.jl index 7fb1ef21890f3..89aeb76bf3201 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1008,7 +1008,7 @@ end @test m[1,2] == ([2,4],) # issue #21123 - @test mapslices(nnz, speye(3), 1) == [1 1 1] + @test mapslices(nnz, sparse(1.0I, 3, 3), 1) == [1 1 1] end @testset "single multidimensional index" begin diff --git a/test/hashing.jl b/test/hashing.jl index e2f277ab5b61c..703dcb9a41253 100644 --- a/test/hashing.jl +++ b/test/hashing.jl @@ -73,7 +73,7 @@ vals = Any[ Dict(x => x for x in 1:10), Dict(7=>7,9=>9,4=>4,10=>10,2=>2,3=>3,8=>8,5=>5,6=>6,1=>1), [], [1], [2], [1, 1], [1, 2], [1, 3], [2, 2], [1, 2, 2], [1, 3, 3], - zeros(2, 2), spzeros(2, 2), eye(2, 2), speye(2, 2), + zeros(2, 2), spzeros(2, 2), eye(2, 2), sparse(1.0I, 2, 2), sparse(ones(2, 2)), ones(2, 2), sparse([0 0; 1 0]), [0 0; 1 0], [-0. 0; -0. 0.], SparseMatrixCSC(2, 2, [1, 3, 3], [1, 2], [-0., -0.]) ] diff --git a/test/linalg/arnoldi.jl b/test/linalg/arnoldi.jl index e6950f32255d4..7ec173bcd018b 100644 --- a/test/linalg/arnoldi.jl +++ b/test/linalg/arnoldi.jl @@ -178,7 +178,7 @@ let # Adjust the tolerance a bit since matrices with repeated eigenvalues # can be very stressful to ARPACK and this may therefore fail with # info = 3 if the tolerance is too small - @test eigs(speye(50), nev=10, tol = 5e-16)[1] ≈ ones(10) #Issue 4246 + @test eigs(sparse(1.0I, 50, 50), nev=10, tol = 5e-16)[1] ≈ ones(10) #Issue 4246 end @testset "real svds" begin diff --git a/test/linalg/special.jl b/test/linalg/special.jl index bc1cfb0f34baf..295882508eaf8 100644 --- a/test/linalg/special.jl +++ b/test/linalg/special.jl @@ -178,7 +178,7 @@ end annotations = testfull ? (triannotations..., symannotations...) : (LowerTriangular, Symmetric) # Concatenations involving these types, un/annotated, should yield sparse arrays spvec = spzeros(N) - spmat = speye(N) + spmat = sparse(1.0I, N, N) diagmat = Diagonal(ones(N)) bidiagmat = Bidiagonal(ones(N), ones(N-1), :U) tridiagmat = Tridiagonal(ones(N-1), ones(N), ones(N-1)) diff --git a/test/linalg/uniformscaling.jl b/test/linalg/uniformscaling.jl index c506f7c7ef5e7..82833b19f9fb7 100644 --- a/test/linalg/uniformscaling.jl +++ b/test/linalg/uniformscaling.jl @@ -82,7 +82,7 @@ let @test B + I == B + eye(B) @test I + B == B + eye(B) AA = randn(2, 2) - for SS in (sprandn(3,3, 0.5), speye(Int, 3)) + for SS in (sprandn(3,3, 0.5), sparse(Int(1)I, 3, 3)) for (A, S) in ((AA, SS), (view(AA, 1:2, 1:2), view(SS, 1:3, 1:3))) @test @inferred(A + I) == A + eye(A) @test @inferred(I + A) == A + eye(A) diff --git a/test/perf/kernel/getdivgrad.jl b/test/perf/kernel/getdivgrad.jl index 04ba5663d8ab6..d0db3b0eca6a9 100644 --- a/test/perf/kernel/getdivgrad.jl +++ b/test/perf/kernel/getdivgrad.jl @@ -5,9 +5,9 @@ #----------------- Get the A matrix function getDivGrad(n1,n2,n3) # the Divergence - D1 = kron(speye(n3),kron(speye(n2),ddx(n1))) - D2 = kron(speye(n3),kron(ddx(n2),speye(n1))) - D3 = kron(ddx(n3),kron(speye(n2),speye(n1))) + D1 = kron(sparse(1.0I, n3, n3), kron(sparse(1.0I, n2), ddx(n1))) + D2 = kron(sparse(1.0I, n3, n3), kron(ddx(n2), sparse(1.0I, n1, n1))) + D3 = kron(ddx(n3), kron(sparse(1.0I, n2, n2), sparse(1.0I, n1, n1))) # DIV from faces to cell-centers Div = [D1 D2 D3] diff --git a/test/perf/sparse/fem.jl b/test/perf/sparse/fem.jl index 80559921c9eba..63940cdf1cf9d 100644 --- a/test/perf/sparse/fem.jl +++ b/test/perf/sparse/fem.jl @@ -7,7 +7,7 @@ function fdlaplacian(N) # create a 1D laplacian and a sparse identity fdl1 = spdiagm(-1 => ones(N-1), 0 => -2*ones(N), 1 => ones(N-1)) # laplace operator on the full grid - return kron(speye(N), fdl1) + kron(fdl1, speye(N)) + return kron(sparse(1.0I, N, N), fdl1) + kron(fdl1, sparse(1.0I, N, N)) end # get the list of boundary dof-indices diff --git a/test/show.jl b/test/show.jl index a3075d722ba16..97aba3140b2ff 100644 --- a/test/show.jl +++ b/test/show.jl @@ -492,7 +492,7 @@ end # issue #12960 mutable struct T12960 end let - A = speye(3) + A = sparse(1.0I, 3, 3) B = similar(A, T12960) @test sprint(show, B) == "\n [1, 1] = #undef\n [2, 2] = #undef\n [3, 3] = #undef" @test sprint(print, B) == "\n [1, 1] = #undef\n [2, 2] = #undef\n [3, 3] = #undef" diff --git a/test/sparse/cholmod.jl b/test/sparse/cholmod.jl index 256732c71c308..410d54090e8d1 100644 --- a/test/sparse/cholmod.jl +++ b/test/sparse/cholmod.jl @@ -188,7 +188,7 @@ end end @testset "Issue #9915" begin - @test speye(2)\speye(2) == eye(2) + @test sparse(1.0I, 2, 2) \ sparse(1.0I, 2, 2) == eye(2) end @testset "test Sparse constructor Symmetric and Hermitian input (and issymmetric and ishermitian)" begin @@ -660,12 +660,12 @@ end end @testset "Further issue with promotion #14894" begin - @test cholfact(speye(Float16, 5))\ones(5) == ones(5) - @test cholfact(Symmetric(speye(Float16, 5)))\ones(5) == ones(5) - @test cholfact(Hermitian(speye(Complex{Float16}, 5)))\ones(5) == ones(Complex{Float64}, 5) - @test_throws MethodError cholfact(speye(BigFloat, 5)) - @test_throws MethodError cholfact(Symmetric(speye(BigFloat, 5))) - @test_throws MethodError cholfact(Hermitian(speye(Complex{BigFloat}, 5))) + @test cholfact(sparse(Float16(1)I, 5, 5))\ones(5) == ones(5) + @test cholfact(Symmetric(sparse(Float16(1)I, 5, 5)))\ones(5) == ones(5) + @test cholfact(Hermitian(sparse(Complex{Float16}(1)I, 5, 5)))\ones(5) == ones(Complex{Float64}, 5) + @test_throws MethodError cholfact(sprase(BigFloat(1)I, 5, 5)) + @test_throws MethodError cholfact(Symmetric(sparse(BigFloat(1)I, 5, 5))) + @test_throws MethodError cholfact(Hermitian(sparse(Complex{BigFloat}(1)I, 5, 5))) end @testset "test \\ for Factor and StridedVecOrMat" begin @@ -684,7 +684,7 @@ end @testset "Make sure that ldltfact performs an LDLt (Issue #19032)" begin m, n = 400, 500 A = sprandn(m, n, .2) - M = [speye(n) A'; A -speye(m)] + M = [I A'; A -I] b = M * ones(m + n) F = ldltfact(M) s = unsafe_load(pointer(F)) @@ -730,11 +730,12 @@ end end @testset "sparse right multiplication of Symmetric and Hermitian matrices #21431" begin - @test issparse(speye(2)*speye(2)*speye(2)) + S = sparse(1.0I, 2, 2) + @test issparse(S*S*S) for T in (Symmetric, Hermitian) - @test issparse(speye(2)*T(speye(2))*speye(2)) - @test issparse(speye(2)*(T(speye(2))*speye(2))) - @test issparse((speye(2)*T(speye(2)))*speye(2)) + @test issparse(S*T(S)*S) + @test issparse(S*(T(S)*S)) + @test issparse((S*T(S))*S) end end @@ -780,7 +781,7 @@ end @testset "Issue #22335" begin local A, F - A = speye(3) + A = sparse(1.0I, 3, 3) @test LinAlg.issuccess(cholfact(A)) A[3, 3] = -1 F = cholfact(A) diff --git a/test/sparse/higherorderfns.jl b/test/sparse/higherorderfns.jl index 4eddb17ed1605..3e716a95bbd80 100644 --- a/test/sparse/higherorderfns.jl +++ b/test/sparse/higherorderfns.jl @@ -269,7 +269,7 @@ end @testset "sparse map/broadcast with result eltype not a concrete subtype of Number (#19561/#19589)" begin N = 4 - A, fA = speye(N), eye(N) + A, fA = sparse(1.0I, N, N), eye(N) B, fB = spzeros(1, N), zeros(1, N) intorfloat_zeropres(xs...) = all(iszero, xs) ? zero(Float64) : Int(1) stringorfloat_zeropres(xs...) = all(iszero, xs) ? zero(Float64) : "hello" diff --git a/test/sparse/sparse.jl b/test/sparse/sparse.jl index 29a611454affa..769c88f66a090 100644 --- a/test/sparse/sparse.jl +++ b/test/sparse/sparse.jl @@ -52,7 +52,7 @@ end @test SparseMatrixCSC{Float64,Int32}(0I, 3, 3)::SparseMatrixCSC{Float64,Int32} == spzeros(Float64, Int32, 3, 3) end -se33 = speye(3) +se33 = SparseMatrixCSC{Float64}(I, 3, 3) do33 = ones(3) @testset "sparse binary operations" begin @@ -75,7 +75,7 @@ do33 = ones(3) end @testset "concatenation tests" begin - sp33 = speye(3, 3) + sp33 = sparse(1.0I, 3, 3) @testset "horizontal concatenation" begin @test all([se33 se33] == sparse([1, 2, 3, 1, 2, 3], [1, 2, 3, 4, 5, 6], ones(6))) @@ -89,11 +89,11 @@ end @test length(([sp33; 0I]).nzval) == 3 end - se44 = speye(4) + se44 = sparse(1.0I, 4, 4) sz42 = spzeros(4, 2) sz41 = spzeros(4, 1) sz34 = spzeros(3, 4) - se77 = speye(7) + se77 = sparse(1.0I, 7, 7) @testset "h+v concatenation" begin @test all([se44 sz42 sz41; sz34 se33] == se77) @test length(([sp33 0I; 1I 0I]).nzval) == 6 @@ -105,7 +105,7 @@ end @testset "concatenation promotion" begin sz41_f32 = spzeros(Float32, 4, 1) - se33_i32 = speye(Int32, 3, 3) + se33_i32 = sparse(Int32(1)I, 3, 3) @test all([se44 sz42 sz41_f32; sz34 se33_i32] == se77) end @@ -181,7 +181,7 @@ end @testset "complex matrix-vector multiplication and left-division" begin if Base.USE_GPL_LIBS for i = 1:5 - a = speye(5) + 0.1*sprandn(5, 5, 0.2) + a = I + 0.1*sprandn(5, 5, 0.2) b = randn(5,3) + im*randn(5,3) c = randn(5) + im*randn(5) d = randn(5) + im*randn(5) @@ -204,7 +204,7 @@ end @test_throws DimensionMismatch α*a.'*c + β*c @test_throws DimensionMismatch α*a.'*ones(5) + β*c - a = speye(5) + 0.1*sprandn(5, 5, 0.2) + 0.1*im*sprandn(5, 5, 0.2) + a = I + 0.1*sprandn(5, 5, 0.2) + 0.1*im*sprandn(5, 5, 0.2) b = randn(5,3) @test (maximum(abs.(a*b - Array(a)*b)) < 100*eps()) @test (maximum(abs.(a'b - Array(a)'b)) < 100*eps()) @@ -213,7 +213,7 @@ end @test (maximum(abs.(a'\b - Array(a')\b)) < 1000*eps()) @test (maximum(abs.(a.'\b - Array(a.')\b)) < 1000*eps()) - a = speye(5) + tril(0.1*sprandn(5, 5, 0.2)) + a = I + tril(0.1*sprandn(5, 5, 0.2)) b = randn(5,3) + im*randn(5,3) @test (maximum(abs.(a*b - Array(a)*b)) < 100*eps()) @test (maximum(abs.(a'b - Array(a)'b)) < 100*eps()) @@ -222,7 +222,7 @@ end @test (maximum(abs.(a'\b - Array(a')\b)) < 1000*eps()) @test (maximum(abs.(a.'\b - Array(a.')\b)) < 1000*eps()) - a = speye(5) + tril(0.1*sprandn(5, 5, 0.2) + 0.1*im*sprandn(5, 5, 0.2)) + a = I + tril(0.1*sprandn(5, 5, 0.2) + 0.1*im*sprandn(5, 5, 0.2)) b = randn(5,3) @test (maximum(abs.(a*b - Array(a)*b)) < 100*eps()) @test (maximum(abs.(a'b - Array(a)'b)) < 100*eps()) @@ -231,7 +231,7 @@ end @test (maximum(abs.(a'\b - Array(a')\b)) < 1000*eps()) @test (maximum(abs.(a.'\b - Array(a.')\b)) < 1000*eps()) - a = speye(5) + triu(0.1*sprandn(5, 5, 0.2)) + a = I + triu(0.1*sprandn(5, 5, 0.2)) b = randn(5,3) + im*randn(5,3) @test (maximum(abs.(a*b - Array(a)*b)) < 100*eps()) @test (maximum(abs.(a'b - Array(a)'b)) < 100*eps()) @@ -240,7 +240,7 @@ end @test (maximum(abs.(a'\b - Array(a')\b)) < 1000*eps()) @test (maximum(abs.(a.'\b - Array(a.')\b)) < 1000*eps()) - a = speye(5) + triu(0.1*sprandn(5, 5, 0.2) + 0.1*im*sprandn(5, 5, 0.2)) + a = I + triu(0.1*sprandn(5, 5, 0.2) + 0.1*im*sprandn(5, 5, 0.2)) b = randn(5,3) @test (maximum(abs.(a*b - Array(a)*b)) < 100*eps()) @test (maximum(abs.(a'b - Array(a)'b)) < 100*eps()) @@ -249,7 +249,7 @@ end @test (maximum(abs.(a'\b - Array(a')\b)) < 1000*eps()) @test (maximum(abs.(a.'\b - Array(a.')\b)) < 1000*eps()) - a = speye(5) + triu(0.1*sprandn(5, 5, 0.2)) + a = I + triu(0.1*sprandn(5, 5, 0.2)) b = randn(5,3) + im*randn(5,3) @test (maximum(abs.(a*b - Array(a)*b)) < 100*eps()) @test (maximum(abs.(a'b - Array(a)'b)) < 100*eps()) @@ -259,10 +259,10 @@ end @test (maximum(abs.(a.'\b - Array(a.')\b)) < 1000*eps()) # UpperTriangular/LowerTriangular solve - a = UpperTriangular(speye(5) + triu(0.1*sprandn(5, 5, 0.2))) + a = UpperTriangular(I + triu(0.1*sprandn(5, 5, 0.2))) b = sprandn(5, 5, 0.2) @test (maximum(abs.(a\b - Array(a)\Array(b))) < 1000*eps()) - a = LowerTriangular(speye(5) + tril(0.1*sprandn(5, 5, 0.2))) + a = LowerTriangular(I + tril(0.1*sprandn(5, 5, 0.2))) b = sprandn(5, 5, 0.2) @test (maximum(abs.(a\b - Array(a)\Array(b))) < 1000*eps()) @@ -523,7 +523,7 @@ end end @testset "issue described in https://groups.google.com/d/msg/julia-users/Yq4dh8NOWBQ/GU57L90FZ3EJ" begin - A = speye(Bool, 5) + A = sparse(I, 5, 5) @test find(A) == find(x -> x == true, A) == find(Array(A)) end @@ -838,66 +838,68 @@ end @test count(!iszero, A) == nA @test A == B - A = speye(Int, 5) - I=1:10 + A = sparse(Int(1)I, 5, 5) + lininds = 1:10 X=reshape([trues(10); falses(15)],5,5) - @test A[I] == A[X] == [1,0,0,0,0,0,1,0,0,0] - A[I] = [1:10;] - @test A[I] == A[X] == collect(1:10) - A[I] = zeros(Int, 10) + @test A[lininds] == A[X] == [1,0,0,0,0,0,1,0,0,0] + A[lininds] = [1:10;] + @test A[lininds] == A[X] == collect(1:10) + A[lininds] = zeros(Int, 10) @test nnz(A) == 13 @test count(!iszero, A) == 3 - @test A[I] == A[X] == zeros(Int, 10) + @test A[lininds] == A[X] == zeros(Int, 10) c = collect(11:20); c[1] = c[3] = 0 - A[I] = c + A[lininds] = c @test nnz(A) == 13 @test count(!iszero, A) == 11 - @test A[I] == A[X] == c - A = speye(Int, 5) - A[I] = c + @test A[lininds] == A[X] == c + A = sparse(Int(1)I, 5) + A[lininds] = c @test nnz(A) == 12 @test count(!iszero, A) == 11 - @test A[I] == A[X] == c - - S = sprand(50, 30, 0.5, x -> round.(Int, rand(x) * 100)) - I = sprand(Bool, 50, 30, 0.2) - FS = Array(S) - FI = Array(I) - @test sparse(FS[FI]) == S[I] == S[FI] - @test sum(S[FI]) + sum(S[.!FI]) == sum(S) - @test count(!iszero, I) == count(I) - - sumS1 = sum(S) - sumFI = sum(S[FI]) - nnzS1 = nnz(S) - S[FI] = 0 - sumS2 = sum(S) - cnzS2 = count(!iszero, S) - @test sum(S[FI]) == 0 - @test nnz(S) == nnzS1 - @test (sum(S) + sumFI) == sumS1 - - S[FI] = 10 - nnzS3 = nnz(S) - @test sum(S) == sumS2 + 10*sum(FI) - S[FI] = 0 - @test sum(S) == sumS2 - @test nnz(S) == nnzS3 - @test count(!iszero, S) == cnzS2 - - S[FI] = [1:sum(FI);] - @test sum(S) == sumS2 + sum(1:sum(FI)) - - S = sprand(50, 30, 0.5, x -> round.(Int, rand(x) * 100)) - N = length(S) >> 2 - I = randperm(N) .* 4 - J = randperm(N) - sumS1 = sum(S) - sumS2 = sum(S[I]) - S[I] = 0 - @test sum(S) == (sumS1 - sumS2) - S[I] = J - @test sum(S) == (sumS1 - sumS2 + sum(J)) + @test A[lininds] == A[X] == c + + let # prevent assignment to I from overwriting UniformSampling in enclosing scope + S = sprand(50, 30, 0.5, x -> round.(Int, rand(x) * 100)) + I = sprand(Bool, 50, 30, 0.2) + FS = Array(S) + FI = Array(I) + @test sparse(FS[FI]) == S[I] == S[FI] + @test sum(S[FI]) + sum(S[.!FI]) == sum(S) + @test count(!iszero, I) == count(I) + + sumS1 = sum(S) + sumFI = sum(S[FI]) + nnzS1 = nnz(S) + S[FI] = 0 + sumS2 = sum(S) + cnzS2 = count(!iszero, S) + @test sum(S[FI]) == 0 + @test nnz(S) == nnzS1 + @test (sum(S) + sumFI) == sumS1 + + S[FI] = 10 + nnzS3 = nnz(S) + @test sum(S) == sumS2 + 10*sum(FI) + S[FI] = 0 + @test sum(S) == sumS2 + @test nnz(S) == nnzS3 + @test count(!iszero, S) == cnzS2 + + S[FI] = [1:sum(FI);] + @test sum(S) == sumS2 + sum(1:sum(FI)) + + S = sprand(50, 30, 0.5, x -> round.(Int, rand(x) * 100)) + N = length(S) >> 2 + I = randperm(N) .* 4 + J = randperm(N) + sumS1 = sum(S) + sumS2 = sum(S[I]) + S[I] = 0 + @test sum(S) == (sumS1 - sumS2) + S[I] = J + @test sum(S) == (sumS1 - sumS2 + sum(J)) + end end @testset "dropstored!" begin @@ -1129,7 +1131,7 @@ for (tup, rval, rind) in [((1,), ["b"], [2])] end @testset "findn" begin - b = findn( speye(4) ) + b = findn( sparse(1.0I, 4, 4) ) @test (length(b[1]) == 4) @test (length(b[2]) == 4) end @@ -1148,7 +1150,7 @@ end #ensure we have preserved the correct dimensions! - a = speye(3,5) + a = sparse(1.0I, 3, 5) @test size(rot180(a)) == (3,5) @test size(rotr90(a)) == (5,3) @test size(rotl90(a)) == (5,3) @@ -1291,7 +1293,7 @@ end @test Array(sparse([])) == zeros(0) @test_throws BoundsError sparse([])[1] @test_throws BoundsError sparse([])[1] = 1 - x = speye(100) + x = sparse(1.0I, 100, 100) @test_throws BoundsError x[-10:10] end @@ -1303,7 +1305,7 @@ end @testset "issue #10411" begin for (m,n) in ((2,-2),(-2,2),(-2,-2)) @test_throws ArgumentError spzeros(m,n) - @test_throws ArgumentError speye(m,n) + @test_throws ArgumentError sparse(1.0I, m, n) @test_throws ArgumentError sprand(m,n,0.2) end end @@ -1350,16 +1352,15 @@ end @testset "sparse" begin local A = sparse(ones(5, 5)) @test sparse(A) == A - @test sparse([1:5;], [1:5;], 1) == speye(5) + @test sparse([1:5;], [1:5;], 1) == sparse(1.0I, 5, 5) end -@testset "speye and one" begin +@testset "sparse(I, ...) and one" begin local A = sparse(ones(5, 5)) - @test speye(A) == speye(5) - @test eye(A) == speye(5) - @test one(A) == speye(5) + @test sparse(I, 5, 5) == eye(A) + @test one(A) == eye(A) @test_throws DimensionMismatch one(sprand(5, 6, 0.2)) - @test eltype(speye(Real, 5, 5)) === Real + @test eltype(SparseMatrixCSC{Real}(I, 5, 5)) === Real end @testset "istriu/istril" begin @@ -1425,7 +1426,7 @@ end @testset "trace" begin @test_throws DimensionMismatch trace(sparse(ones(5,6))) - @test trace(speye(5)) == 5 + @test trace(sparse(1.0I, 5, 5)) == 5 end @testset "spdiagm" begin @@ -1465,7 +1466,7 @@ end end @testset "expandptr" begin - local A = speye(5) + local A = sparse(1.0I, 5, 5) @test Base.SparseArrays.expandptr(A.colptr) == collect(1:5) A[1,2] = 1 @test Base.SparseArrays.expandptr(A.colptr) == [1; 2; 2; 3; 4; 5] @@ -1506,7 +1507,7 @@ end @testset "ishermitian/issymmetric" begin local A # real matrices - A = speye(5,5) + A = sparse(1.0I, 5, 5) @test ishermitian(A) == true @test issymmetric(A) == true A[1,3] = 1.0 @@ -1517,14 +1518,14 @@ end @test issymmetric(A) == true # complex matrices - A = speye(5,5) + im*speye(5,5) + A = sparse((1.0 + 1.0im)I, 5, 5) @test ishermitian(A) == false @test issymmetric(A) == true A[1,4] = 1.0 + im @test ishermitian(A) == false @test issymmetric(A) == false - A = speye(Complex128, 5,5) + A = sparse(Complex128(1)I, 5, 5) A[3,2] = 1.0 + im @test ishermitian(A) == false @test issymmetric(A) == false @@ -1537,7 +1538,7 @@ end @test issymmetric(A) == true # explicit zeros - A = speye(Complex128, 5,5) + A = sparse(Complex128(1)I, 5, 5) A[3,1] = 2 A.nzval[2] = 0.0 @test ishermitian(A) == true @@ -1563,8 +1564,8 @@ end end @testset "equality ==" begin - A1 = speye(10) - A2 = speye(10) + A1 = sparse(1.0I, 10, 10) + A2 = sparse(1.0I, 10, 10) nonzeros(A1)[end]=0 @test A1!=A2 nonzeros(A1)[end]=1 @@ -1642,7 +1643,7 @@ end # make certain entries in nzval beyond # the range specified in colptr do not # impact vecnorm of a sparse matrix - foo = speye(4) + foo = sparse(1.0I, 4, 4) resize!(foo.nzval, 5) setindex!(foo.nzval, NaN, 5) @test vecnorm(foo) == 2.0 @@ -1719,7 +1720,7 @@ end end @testset "spones" begin - local A = 2. * speye(5, 5) + local A = sparse(2.0I, 5, 5) @test Array(spones(A)) == eye(Array(A)) end @@ -1896,7 +1897,7 @@ end end @testset "issue #14398" begin - @test transpose(view(speye(10), 1:5, 1:5)) ≈ eye(5,5) + @test transpose(view(sparse(I, 10, 10), 1:5, 1:5)) ≈ eye(5) end @testset "dropstored issue #20513" begin @@ -2065,7 +2066,7 @@ end end @testset "similar with type conversion" begin - local A = speye(5) + local A = sparse(1.0I, 5, 5) @test size(similar(A, Complex128, Int)) == (5, 5) @test typeof(similar(A, Complex128, Int)) == SparseMatrixCSC{Complex128, Int} @test size(similar(A, Complex128, Int8)) == (5, 5) @@ -2075,7 +2076,7 @@ end end @testset "similar for SparseMatrixCSC" begin - local A = speye(5) + local A = sparse(1.0I, 5, 5) # test similar without specifications (preserves stored-entry structure) simA = similar(A) @test typeof(simA) == typeof(A) diff --git a/test/sparse/sparsevector.jl b/test/sparse/sparsevector.jl index c622a67bf83ef..8e9e908eb075b 100644 --- a/test/sparse/sparsevector.jl +++ b/test/sparse/sparsevector.jl @@ -904,9 +904,9 @@ end sparsecomplexvecs = SparseVector[SparseVector(m, sprvec.nzind, complex.(sprvec.nzval, sprvec.nzval)) for sprvec in sparsefloatvecs] sprmat = sprand(m, m, 0.2) - sparsefloatmat = speye(m) + sprmat/(2m) - sparsecomplexmat = speye(m) + SparseMatrixCSC(m, m, sprmat.colptr, sprmat.rowval, complex.(sprmat.nzval, sprmat.nzval)/(4m)) - sparseintmat = speye(Int, m)*10m + SparseMatrixCSC(m, m, sprmat.colptr, sprmat.rowval, round.(Int, sprmat.nzval*10)) + sparsefloatmat = I + sprmat/(2m) + sparsecomplexmat = I + SparseMatrixCSC(m, m, sprmat.colptr, sprmat.rowval, complex.(sprmat.nzval, sprmat.nzval)/(4m)) + sparseintmat = 10m*I + SparseMatrixCSC(m, m, sprmat.colptr, sprmat.rowval, round.(Int, sprmat.nzval*10)) denseintmat = I*10m + rand(1:m, m, m) densefloatmat = I + randn(m, m)/(2m) diff --git a/test/sparse/umfpack.jl b/test/sparse/umfpack.jl index a99dac1c92413..fe4193f82cefb 100644 --- a/test/sparse/umfpack.jl +++ b/test/sparse/umfpack.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license @testset "UMFPACK wrappers" begin - se33 = speye(3) + se33 = sparse(1.0I, 3, 3) do33 = ones(3) @test isequal(se33 \ do33, do33) @@ -98,7 +98,7 @@ end @testset "Issue #4523 - complex sparse \\" begin - x = speye(2) + im * speye(2) + x = sparse((1.0 + 1.0im)I, 2, 2) @test (x*(lufact(x) \ ones(2))) ≈ ones(2) @test det(sparse([1,3,3,1], [1,1,3,3], [1,1,1,1])) == 0 @@ -142,13 +142,13 @@ @testset "Test aliasing" begin a = rand(5) - @test_throws ArgumentError Base.SparseArrays.UMFPACK.solve!(a, lufact(speye(5,5)), a, Base.SparseArrays.UMFPACK.UMFPACK_A) + @test_throws ArgumentError Base.SparseArrays.UMFPACK.solve!(a, lufact(sparse(1.0I, 5, 5)), a, Base.SparseArrays.UMFPACK.UMFPACK_A) aa = complex(a) - @test_throws ArgumentError Base.SparseArrays.UMFPACK.solve!(aa, lufact(complex(speye(5,5))), aa, Base.SparseArrays.UMFPACK.UMFPACK_A) + @test_throws ArgumentError Base.SparseArrays.UMFPACK.solve!(aa, lufact(sparse((1.0im)I, 5, 5)), aa, Base.SparseArrays.UMFPACK.UMFPACK_A) end @testset "Issues #18246,18244 - lufact sparse pivot" begin - A = speye(4) + A = sparse(1.0I, 4, 4) A[1:2,1:2] = [-.01 -200; 200 .001] F = lufact(A) @test F[:p] == [3 ; 4 ; 2 ; 1] @@ -157,7 +157,7 @@ @testset "Test that A[c|t]_ldiv_B!{T<:Complex}(X::StridedMatrix{T}, lu::UmfpackLU{Float64}, B::StridedMatrix{T}) works as expected." begin N = 10 p = 0.5 - A = N*speye(N) + sprand(N, N, p) + A = N*I + sprand(N, N, p) X = zeros(Complex{Float64}, N, N) B = complex.(rand(N, N), rand(N, N)) luA, lufA = lufact(A), lufact(Array(A))