From aec8a85fd55fb9dccb5f1eeefb0f944593bbd0dc Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Tue, 14 Apr 2020 19:48:46 +0100 Subject: [PATCH 1/9] Start transition --- Project.toml | 6 + appveyor.yml | 43 - src/ApproxFunBase.jl | 18 +- src/Caching/almostbanded.jl | 473 ---------- src/Caching/banded.jl | 178 ---- src/Caching/bandedblockbanded.jl | 60 -- src/Caching/blockbanded.jl | 317 ------- src/Caching/caching.jl | 19 - src/Caching/matrix.jl | 147 --- src/Caching/ragged.jl | 333 ------- src/Domain.jl | 186 ---- src/Domains/Domains.jl | 82 -- src/Domains/Grids.jl | 29 - src/Domains/PiecewiseSegment.jl | 89 -- src/Domains/Point.jl | 39 - src/Domains/ProductDomain.jl | 48 - src/Domains/Segment.jl | 154 ---- src/Domains/UnionDomain.jl | 83 -- src/Domains/multivariate.jl | 29 - src/Fun.jl | 199 ++--- src/LinearAlgebra/AlmostBandedMatrix.jl | 58 -- src/LinearAlgebra/LinearAlgebra.jl | 17 - src/LinearAlgebra/LowRankMatrix.jl | 45 - src/LinearAlgebra/RaggedMatrix.jl | 248 ------ src/LinearAlgebra/blas.jl | 102 --- src/LinearAlgebra/clenshaw.jl | 293 ------ src/LinearAlgebra/helper.jl | 752 ---------------- src/LinearAlgebra/hesseneigs.jl | 81 -- src/LinearAlgebra/lyap.jl | 92 -- src/LinearAlgebra/rowvector.jl | 127 --- src/LinearAlgebra/standardchop.jl | 106 --- src/Multivariate/LowRankFun.jl | 426 --------- src/Multivariate/Multivariate.jl | 99 -- src/Multivariate/ProductFun.jl | 346 ------- src/Multivariate/TensorSpace.jl | 587 ------------ src/Multivariate/VectorFun.jl | 202 ----- src/Operators/Operator.jl | 843 ------------------ src/Operators/SubOperator.jl | 351 -------- src/Operators/almostbanded/LowRankOperator.jl | 85 -- .../almostbanded/LowRankPertOperator.jl | 75 -- src/Operators/almostbanded/almostbanded.jl | 2 - src/Operators/banded/CalculusOperator.jl | 299 ------- src/Operators/banded/ConstantOperator.jl | 160 ---- src/Operators/banded/Conversion.jl | 85 -- src/Operators/banded/DiagonalOperator.jl | 0 src/Operators/banded/Multiplication.jl | 171 ---- src/Operators/banded/PermutationOperator.jl | 62 -- src/Operators/banded/Reverse.jl | 18 - src/Operators/banded/ToeplitzOperator.jl | 270 ------ src/Operators/banded/TridiagonalOperator.jl | 45 - src/Operators/banded/banded.jl | 11 - .../functionals/CalculusFunctional.jl | 91 -- src/Operators/functionals/Evaluation.jl | 251 ------ src/Operators/functionals/functionals.jl | 2 - src/Operators/general/CachedOperator.jl | 130 --- src/Operators/general/FiniteOperator.jl | 64 -- src/Operators/general/InterlaceOperator.jl | 535 ----------- src/Operators/general/OperatorFunction.jl | 54 -- src/Operators/general/OperatorLayout.jl | 113 --- .../general/PartialInverseOperator.jl | 69 -- src/Operators/general/algebra.jl | 671 -------------- src/Operators/general/general.jl | 7 - src/Operators/ldiv.jl | 61 -- src/Operators/nullspace.jl | 55 -- src/Operators/qr.jl | 213 ----- src/Operators/spacepromotion.jl | 182 ---- src/Operators/systems.jl | 72 -- src/PDE/KroneckerOperator.jl | 424 --------- src/PDE/PDE.jl | 61 -- src/Space.jl | 548 ------------ src/Spaces/ArraySpace.jl | 293 ------ src/Spaces/ConstantSpace.jl | 248 ------ src/Spaces/DiracSpace.jl | 216 ----- src/Spaces/HeavisideSpace.jl | 114 --- src/Spaces/ProductSpaceOperators.jl | 325 ------- src/Spaces/QuotientSpace.jl | 299 ------- src/Spaces/Spaces.jl | 197 ---- src/Spaces/SubSpace.jl | 247 ----- src/Spaces/SumSpace.jl | 504 ----------- src/constructors.jl | 196 ---- src/hacks.jl | 77 -- src/specialfunctions.jl | 594 ------------ src/testing.jl | 236 ----- test/runtests.jl | 52 +- 84 files changed, 96 insertions(+), 15695 deletions(-) delete mode 100644 appveyor.yml delete mode 100644 src/Caching/almostbanded.jl delete mode 100644 src/Caching/banded.jl delete mode 100644 src/Caching/bandedblockbanded.jl delete mode 100644 src/Caching/blockbanded.jl delete mode 100644 src/Caching/caching.jl delete mode 100644 src/Caching/matrix.jl delete mode 100644 src/Caching/ragged.jl delete mode 100644 src/Domain.jl delete mode 100644 src/Domains/Domains.jl delete mode 100644 src/Domains/Grids.jl delete mode 100644 src/Domains/PiecewiseSegment.jl delete mode 100644 src/Domains/Point.jl delete mode 100644 src/Domains/ProductDomain.jl delete mode 100644 src/Domains/Segment.jl delete mode 100644 src/Domains/UnionDomain.jl delete mode 100644 src/Domains/multivariate.jl delete mode 100644 src/LinearAlgebra/AlmostBandedMatrix.jl delete mode 100644 src/LinearAlgebra/LinearAlgebra.jl delete mode 100644 src/LinearAlgebra/LowRankMatrix.jl delete mode 100644 src/LinearAlgebra/RaggedMatrix.jl delete mode 100644 src/LinearAlgebra/blas.jl delete mode 100644 src/LinearAlgebra/clenshaw.jl delete mode 100644 src/LinearAlgebra/helper.jl delete mode 100644 src/LinearAlgebra/hesseneigs.jl delete mode 100644 src/LinearAlgebra/lyap.jl delete mode 100644 src/LinearAlgebra/rowvector.jl delete mode 100644 src/LinearAlgebra/standardchop.jl delete mode 100644 src/Multivariate/LowRankFun.jl delete mode 100644 src/Multivariate/Multivariate.jl delete mode 100644 src/Multivariate/ProductFun.jl delete mode 100644 src/Multivariate/TensorSpace.jl delete mode 100644 src/Multivariate/VectorFun.jl delete mode 100644 src/Operators/Operator.jl delete mode 100644 src/Operators/SubOperator.jl delete mode 100644 src/Operators/almostbanded/LowRankOperator.jl delete mode 100644 src/Operators/almostbanded/LowRankPertOperator.jl delete mode 100644 src/Operators/almostbanded/almostbanded.jl delete mode 100644 src/Operators/banded/CalculusOperator.jl delete mode 100644 src/Operators/banded/ConstantOperator.jl delete mode 100644 src/Operators/banded/Conversion.jl delete mode 100644 src/Operators/banded/DiagonalOperator.jl delete mode 100644 src/Operators/banded/Multiplication.jl delete mode 100644 src/Operators/banded/PermutationOperator.jl delete mode 100644 src/Operators/banded/Reverse.jl delete mode 100644 src/Operators/banded/ToeplitzOperator.jl delete mode 100644 src/Operators/banded/TridiagonalOperator.jl delete mode 100644 src/Operators/banded/banded.jl delete mode 100644 src/Operators/functionals/CalculusFunctional.jl delete mode 100644 src/Operators/functionals/Evaluation.jl delete mode 100644 src/Operators/functionals/functionals.jl delete mode 100644 src/Operators/general/CachedOperator.jl delete mode 100644 src/Operators/general/FiniteOperator.jl delete mode 100644 src/Operators/general/InterlaceOperator.jl delete mode 100644 src/Operators/general/OperatorFunction.jl delete mode 100644 src/Operators/general/OperatorLayout.jl delete mode 100644 src/Operators/general/PartialInverseOperator.jl delete mode 100644 src/Operators/general/algebra.jl delete mode 100644 src/Operators/general/general.jl delete mode 100644 src/Operators/ldiv.jl delete mode 100644 src/Operators/nullspace.jl delete mode 100644 src/Operators/qr.jl delete mode 100644 src/Operators/spacepromotion.jl delete mode 100644 src/Operators/systems.jl delete mode 100644 src/PDE/KroneckerOperator.jl delete mode 100644 src/PDE/PDE.jl delete mode 100644 src/Space.jl delete mode 100644 src/Spaces/ArraySpace.jl delete mode 100644 src/Spaces/ConstantSpace.jl delete mode 100644 src/Spaces/DiracSpace.jl delete mode 100644 src/Spaces/HeavisideSpace.jl delete mode 100644 src/Spaces/ProductSpaceOperators.jl delete mode 100644 src/Spaces/QuotientSpace.jl delete mode 100644 src/Spaces/Spaces.jl delete mode 100644 src/Spaces/SubSpace.jl delete mode 100644 src/Spaces/SumSpace.jl delete mode 100644 src/constructors.jl delete mode 100644 src/hacks.jl delete mode 100644 src/specialfunctions.jl delete mode 100644 src/testing.jl diff --git a/Project.toml b/Project.toml index cd86cd85..73bb2b16 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ BandedMatrices = "aae01518-5342-5314-be14-df237901396f" BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" Calculus = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" +ContinuumArrays = "7ae1f121-cc2c-504b-ac30-9b923412ae5c" DSP = "717857b8-e6f2-59f4-9121-6e50c889abd2" DomainSets = "5b8099bc-c8ec-5219-889f-1d9e522a28bf" DualNumbers = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" @@ -15,10 +16,13 @@ FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" FastGaussQuadrature = "442a2c76-b920-505d-bb47-c5924d526838" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" InfiniteArrays = "4858937d-0d70-526a-a4dd-2d5cb5dd786c" +InfiniteLinearAlgebra = "cde9dba0-b1de-11e9-2c62-0bab9446c55c" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02" +LazyBandedMatrices = "d7e5e226-e90b-4449-9968-0f923699bf6f" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LowRankApprox = "898213cb-b102-5a47-900c-97e73b919f73" +OrthogonalPolynomialsQuasi = "aa41a628-2c43-45df-899b-83ab96621781" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" @@ -32,6 +36,7 @@ BandedMatrices = "0.14, 0.15" BlockArrays = "0.11, 0.12" BlockBandedMatrices = "0.7, 0.8" Calculus = "0.5" +ContinuumArrays = "0.2.2" DSP = "0.6" DomainSets = "0.1, 0.2, 0.3" DualNumbers = "0.6.2" @@ -39,6 +44,7 @@ FFTW = "0.3, 1" FastGaussQuadrature = "0.4" FillArrays = "0.8" InfiniteArrays = "0.4, 0.5, 0.6, 0.7" +InfiniteLinearAlgebra = "0.3.1" IntervalSets = "0.3.1, 0.4" LazyArrays = "0.14, 0.15, 0.16" LowRankApprox = "0.2, 0.3, 0.4" diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index b137f7c4..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,43 +0,0 @@ -environment: - matrix: - - julia_version: 1.3 - - julia_version: 1.4 - - julia_version: nightly - -platform: - - x86 # 32-bit - - x64 # 64-bit - -# Uncomment the following lines to allow failures on nightly julia -# (tests will run but not make your overall status red) -matrix: - allow_failures: - - julia_version: nightly - -branches: - only: - - master - - /release-.*/ - -notifications: - - provider: Email - on_build_success: false - on_build_failure: false - on_build_status_changed: false - -install: - - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1")) - -build_script: - - echo "%JL_BUILD_SCRIPT%" - - C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%" - -test_script: - - echo "%JL_TEST_SCRIPT%" - - C:\julia\bin\julia -e "%JL_TEST_SCRIPT%" - -# # Uncomment to support code coverage upload. Should only be enabled for packages -# # which would have coverage gaps without running on Windows -# on_success: -# - echo "%JL_CODECOV_SCRIPT%" -# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%" diff --git a/src/ApproxFunBase.jl b/src/ApproxFunBase.jl index 2dad55dd..7addb162 100644 --- a/src/ApproxFunBase.jl +++ b/src/ApproxFunBase.jl @@ -1,7 +1,7 @@ module ApproxFunBase using Base, BlockArrays, BandedMatrices, BlockBandedMatrices, DomainSets, IntervalSets, SpecialFunctions, AbstractFFTs, FFTW, SpecialFunctions, DSP, DualNumbers, - LinearAlgebra, SparseArrays, LowRankApprox, FillArrays, InfiniteArrays #, Arpack + LinearAlgebra, SparseArrays, LowRankApprox, FillArrays, InfiniteArrays, ContinuumArrays import StaticArrays, Calculus import DomainSets: Domain, indomain, UnionDomain, ProductDomain, FullSpace, Point, elements, DifferenceDomain, @@ -25,7 +25,7 @@ import Base: values, convert, getindex, setindex!, *, +, -, ==, <, <=, >, |, !, getproperty, findfirst, unsafe_getindex, fld, cld, div, real, imag, @_inline_meta, eachindex, firstindex, lastindex, keys, isreal, OneTo, Array, Vector, Matrix, view, ones, @propagate_inbounds, print_array, - split, iszero + split, iszero, vec import Base.Broadcast: BroadcastStyle, Broadcasted, AbstractArrayStyle, broadcastable, DefaultArrayStyle, broadcasted @@ -39,6 +39,8 @@ import LinearAlgebra: BlasInt, BlasFloat, norm, ldiv!, mul!, det, eigvals, dot, import SparseArrays: blockdiag + +import ContinuumArrays: AbstractQuasiMatrix, AbstractQuasiVector, AbstractQuasiArray, arguments # import Arpack: eigs # we need to import all special functions to use Calculus.symbolic_derivatives_1arg @@ -77,6 +79,7 @@ import InfiniteArrays: Infinity, InfRanges, AbstractInfUnitRange, OneToInf # convenience for 1-d block ranges const BlockRange1 = BlockRange{1,Tuple{UnitRange{Int}}} +const Space{T} = AbstractQuasiMatrix{T} import Base: view @@ -94,18 +97,9 @@ export pad!, pad, chop!, sample, export .., Interval, ChebyshevInterval, leftendpoint, rightendpoint, endpoints, cache +export bilinearform, linebilinearform, innerproduct, lineinnerproduct -include("LinearAlgebra/LinearAlgebra.jl") include("Fun.jl") -include("Domains/Domains.jl") -include("Multivariate/Multivariate.jl") -include("Operators/Operator.jl") -include("Caching/caching.jl") -include("PDE/PDE.jl") -include("Spaces/Spaces.jl") -include("hacks.jl") -include("testing.jl") -include("specialfunctions.jl") end #module diff --git a/src/Caching/almostbanded.jl b/src/Caching/almostbanded.jl deleted file mode 100644 index 5205e80e..00000000 --- a/src/Caching/almostbanded.jl +++ /dev/null @@ -1,473 +0,0 @@ -## Caches - -function CachedOperator(io::InterlaceOperator{T,1};padding::Bool=false) where T - ds=domainspace(io) - rs=rangespace(io) - - ind=findall(op->isinf(size(op,1)), io.ops) - if length(ind) ≠ 1 || !isbanded(io.ops[ind[1]]) # is almost banded - return default_CachedOperator(io;padding=padding) - end - i=ind[1] - bo=io.ops[i] - lin,uin=bandwidths(bo) - - - - # calculate number of rows interlaced - # each each row in the rangespace increases the lower bandwidth by 1 - nds=0 - md=0 - for k=1:length(io.ops) - if k ≠ i - d=dimension(rs[k]) - nds+=d - md=max(md,d) - end - end - - - isend=true - for k=i+1:length(io.ops) - if dimension(rs[k]) == md - isend=false - end - end - - numoprows=isend ? md-1 : md - n=nds+numoprows - - (l,u) = (max(lin+nds,n-1),max(0,uin+1-ind[1])) - - # add extra rows for QR - if padding - u+=l - end - - - ret=AlmostBandedMatrix(Zeros{T}(n,n+u),(l,u),nds) - - # populate the finite rows - jr=1:n+u - ioM=io[1:n,jr] - - - bcrow=1 - oprow=0 - for k=1:n - K,J=io.rangeinterlacer[k] - - if K ≠ i - # fill the fill matrix - ret.fill.V[:,bcrow] = Matrix(view(io.ops[K],J:J,jr)) - ret.fill.U[k,bcrow] = 1 - bcrow += 1 - else - oprow+=1 - end - - - for j=rowrange(ret.bands,k) - ret[k,j] = ioM[k,j] - end - end - - - CachedOperator(io,ret,(n,n+u),ds,rs,(l,∞)) -end - - -function CachedOperator(io::InterlaceOperator{T,2};padding::Bool=false) where T - ds=domainspace(io) - rs=rangespace(io) - di=io.domaininterlacer - ri=io.rangeinterlacer - ddims=dimensions(di.iterator) - rdims=dimensions(ri.iterator) - - # we are only almost banded if every operator is either finite - # range or banded, and if the # of ∞ spaces is the same - # for the domain and range - isab=all(op->isfinite(size(op,1)) || isbanded(op),io.ops) && - count(isinf,ddims) == count(isinf,rdims) - - if !isab - return default_CachedOperator(io;padding=padding) - end - - - # these are the bandwidths if we only had ∞-dimensional operators - d∞=findall(isinf,collect(ddims)) - r∞=findall(isinf,collect(rdims)) - p=length(d∞) - - # we only support block size 1 for now - for k in d∞ - bl = blocklengths(ds[k]) - if !(bl isa AbstractFill) || getindex_value(bl) ≠ 1 - return default_CachedOperator(io;padding=padding) - end - end - for k in r∞ - bl = blocklengths(rs[k]) - if !(bl isa AbstractFill) || getindex_value(bl) ≠ 1 - return default_CachedOperator(io;padding=padding) - end - end - - l∞,u∞ = 0,0 - for k=1:p,j=1:p - l∞=max(l∞,p*bandwidth(io.ops[r∞[k],d∞[j]],1)+k-j) - end - for k=1:p,j=1:p - u∞=max(u∞,p*bandwidth(io.ops[r∞[k],d∞[j]],2)+j-k) - end - - # now we move everything by the finite rank - ncols=mapreduce(d->isfinite(d) ? d : 0,+,ddims) - nbcs=mapreduce(d->isfinite(d) ? d : 0,+,rdims) - shft=ncols-nbcs - l∞,u∞=l∞-shft,u∞+shft - - # iterate through finite rows to find worst case bandwidth - l,u=l∞,u∞ - for k=1+nbcs+p,j=1:ncols+p - N,n=ri[k] - M,m=di[j] - l=max(l,bandwidth(io.ops[N,M],1)+m-n+k-j) - u=max(u,bandwidth(io.ops[N,M],2)+n-m+j-k) - end - - - # add extra rows for QR - if padding - u += l - end - - n=1+nbcs+p - ret=AlmostBandedMatrix(Zeros{T}(n,n+u),(l,u),nbcs) - - # populate entries and fill functionals - bcrow=1 - oprow=0 - jr=1:n+u - for k=1:n - K,J=io.rangeinterlacer[k] - - if isfinite(rdims[K] ) - # fill the fill matrix - ret.fill.V[:,bcrow] = Matrix(view(io,k:k,jr)) - ret.fill.U[k,bcrow] = 1 - bcrow += 1 - else - oprow+=1 - end - - - for j=rowrange(ret.bands,k) - ret[k,j] = io[k,j] - end - end - - CachedOperator(io,ret,(n,n+u),ds,rs,(l,∞)) -end - - - -# Grow cached interlace operator - -function resizedata!(co::CachedOperator{T,AlmostBandedMatrix{T}, - InterlaceOperator{T,1,DS,RS,DI,RI,BI}}, - n::Integer,::Colon) where {T<:Number,DS,RS,DI,RI,BI} - if n ≤ co.datasize[1] - return co - end - - (l,u)=bandwidths(co.data.bands) - pad!(co.data,n,n+u) - - r=rank(co.data.fill) - ind=findfirst(op->isinf(size(op,1)),co.op.ops) - - k=1 - for (K,J) in co.op.rangeinterlacer - if K ≠ ind - co.data.fill.V[co.datasize[2]:end,k] = co.op.ops[K][J,co.datasize[2]:n+u] - k += 1 - if k > r - break - end - end - end - - kr=co.datasize[1]+1:n - jr=max(1,kr[1]-l):n+u - BLAS.axpy!(1.0,view(co.op.ops[ind],kr .- r,jr), - view(co.data.bands,kr,jr)) - - co.datasize=(n,n+u) - co -end - - - -function resizedata!(co::CachedOperator{T,AlmostBandedMatrix{T}, - InterlaceOperator{T,2,DS,RS,DI,RI,BI}}, - n::Integer,::Colon) where {T<:Number,DS,RS,DI,RI,BI} - if n ≤ co.datasize[1] - return co - end - - io=co.op - ds=domainspace(io) - rs=rangespace(io) - di=io.domaininterlacer - ri=io.rangeinterlacer - ddims=dimensions(di.iterator) - rdims=dimensions(ri.iterator) - - d∞=findall(isinf,collect(ddims)) - r∞=findall(isinf,collect(rdims)) - p=length(d∞) - - (l,u)=bandwidths(co.data.bands) - pad!(co.data,n,n+u) - co.data - # r is number of extra rows, ncols is number of extra columns - r=rank(co.data.fill) - ncols=mapreduce(d->isfinite(d) ? d : 0,+,ddims) - - - # fill rows - K=k=1 - while k ≤ r - if isfinite(dimension(rs[ri[K][1]])) - co.data.fill.V[co.datasize[2]:end,k] = co.op[K,co.datasize[2]:n+u] - k += 1 - end - K += 1 - end - - kr=co.datasize[1]+1:n - jr=max(ncols+1,kr[1]-l):n+u - io∞=InterlaceOperator(io.ops[r∞,d∞]) - - BLAS.axpy!(1.0,view(io∞,kr.-r,jr.-ncols),view(co.data.bands,kr,jr)) - - co.datasize=(n,n+u) - co -end - - -resizedata!(co::CachedOperator{T,AlmostBandedMatrix{T}, - InterlaceOperator{T,1,DS,RS,DI,RI,BI}}, -n::Integer,m::Integer) where {T<:Number,DS,RS,DI,RI,BI} = resizedata!(co,max(n,m+bandwidth(co.data.bands,1)),:) - - -resizedata!(co::CachedOperator{T,AlmostBandedMatrix{T}, - InterlaceOperator{T,2,DS,RS,DI,RI,BI}}, -n::Integer,m::Integer) where {T<:Number,DS,RS,DI,RI,BI} = resizedata!(co,max(n,m+bandwidth(co.data.bands,1)),:) - - - -## -# These give highly optimized routines for delaying with Cached -# - -## QR - - -function QROperator(R::CachedOperator{T,AlmostBandedMatrix{T}}) where T - M = R.data.bands.l+1 # number of diag+subdiagonal bands - H = Matrix{T}(undef,M,100) - QROperator(R,H,0) -end - - -function resizedata!(QR::QROperator{CachedOperator{T,AlmostBandedMatrix{T}, - MM,DS,RS,BI}}, - ::Colon,col) where {T,MM,DS,RS,BI} - if col ≤ QR.ncols - return QR - end - - MO=QR.R_cache - W=QR.H - - R=MO.data.bands - M=R.l+1 # number of diag+subdiagonal bands - - if col+M-1 ≥ MO.datasize[1] - resizedata!(MO,(col+M-1)+100,:) # double the last rows - end - - if col > size(W,2) - W=QR.H=unsafe_resize!(W,:,2col) - end - - F=MO.data.fill.U - - for k=QR.ncols+1:col - W[:,k] = view(R.data,R.u+1:R.u+R.l+1,k) # diagonal and below - wp=view(W,:,k) - W[1,k]+= flipsign(norm(wp),W[1,k]) - normalize!(wp) - - # scale banded entries - for j=k:k+R.u - dind=R.u+1+k-j - v=view(R.data,dind:dind+M-1,j) - dt=dot(wp,v) - LinearAlgebra.axpy!(-2*dt,wp,v) - end - - # scale banded/filled entries - for j=k+R.u+1:k+R.u+M-1 - p=j-k-R.u - v=view(R.data,1:M-p,j) # shift down each time - wp2=view(wp,p+1:M) - dt=dot(wp2,v) - for ℓ=k:k+p-1 - @inbounds dt=muladd(conj(W[ℓ-k+1,k]), - unsafe_getindex(MO.data.fill,ℓ,j),dt) - end - LinearAlgebra.axpy!(-2*dt,wp2,v) - end - - # scale filled entries - - for j=1:size(F,2) - v=view(F,k:k+M-1,j) # the k,jth entry of F - dt=dot(wp,v) - LinearAlgebra.axpy!(-2*dt,wp,v) - end - end - QR.ncols=col - QR -end - - - -# BLAS versions, requires BlasFloat - -function resizedata!(QR::QROperator{CachedOperator{T,AlmostBandedMatrix{T}, - MM,DS,RS,BI}}, -::Colon,col) where {T<:BlasFloat,MM,DS,RS,BI} - if col ≤ QR.ncols - return QR - end - - MO=QR.R_cache - W=QR.H - - R=MO.data.bands - M=R.l+1 # number of diag+subdiagonal bands - - if col+M-1 ≥ MO.datasize[1] - resizedata!(MO,(col+M-1)+100,:) # double the last rows - end - - if col > size(W,2) - W=QR.H=unsafe_resize!(W,:,2col) - end - - F=MO.data.fill.U - - f=pointer(F) - m,n=size(R) - w=pointer(W) - r=pointer(R.data) - sz=sizeof(T) - st=stride(R.data,2) - stw=stride(W,2) - - for k=QR.ncols+1:col - v=r+sz*(R.u + (k-1)*st) # diagonal entry - wp=w+stw*sz*(k-1) # k-th column of W - BLAS.blascopy!(M,v,1,wp,1) - W[1,k]+= flipsign(BLAS.nrm2(M,wp,1),W[1,k]) - normalize!(M,wp) - - for j=k:k+R.u - v=r+sz*(R.u + (k-1)*st + (j-k)*(st-1)) - dt = BandedMatrices.dot(M,wp,1,v,1) - BLAS.axpy!(M,-2*dt,wp,1,v,1) - end - - for j=k+R.u+1:k+R.u+M-1 - p=j-k-R.u - v=r+sz*((j-1)*st) # shift down each time - dt = BandedMatrices.dot(M-p,wp+p*sz,1,v,1) - for ℓ=k:k+p-1 - @inbounds dt=muladd(conj(W[ℓ-k+1,k]), - unsafe_getindex(MO.data.fill,ℓ,j),dt) - end - BLAS.axpy!(M-p,-2*dt,wp+p*sz,1,v,1) - end - - fp=f+(k-1)*sz - fst=stride(F,2) - for j=1:size(F,2) - v=fp+fst*(j-1)*sz # the k,jth entry of F - dt = BandedMatrices.dot(M,wp,1,v,1) - BLAS.axpy!(M,-2*dt,wp,1,v,1) - end - end - QR.ncols=col - QR -end - - -## back substitution -# loop to avoid ambiguity with AbstractTRiangular -for ArrTyp in (:AbstractVector, :AbstractMatrix) - @eval function ldiv!(U::UpperTriangular{T, SubArray{T, 2, AlmostBandedMatrix{T}, Tuple{UnitRange{Int}, UnitRange{Int}}, false}}, - u::$ArrTyp{T}) where T - n = size(u,1) - n == size(U,1) || throw(DimensionMismatch()) - - V = parent(U) - @assert parentindices(V)[1][1] == 1 - @assert parentindices(V)[2][1] == 1 - - B = parent(V) - - A = B.bands - F = B.fill - b=bandwidth(A,2) - nbc = rank(B.fill) - - pk = zeros(T,nbc) - - for c=1:size(u,2) - fill!(pk,zero(T)) - - # before we get to filled rows - for k=n:-1:max(1,n-b) - @simd for j=k+1:n - @inbounds u[k,c] = muladd(-A.data[k-j+A.u+1,j],u[j,c],u[k,c]) - end - - @inbounds u[k,c] /= A.data[A.u+1,k] - end - - #filled rows - for k=n-b-1:-1:1 - @simd for j=1:nbc - @inbounds pk[j] = muladd(u[k+b+1,c],F.V[k+b+1,j],pk[j]) - end - - @simd for j=k+1:k+b - @inbounds u[k,c]=muladd(-A.data[k-j+A.u+1,j],u[j,c],u[k,c]) - end - - @simd for j=1:nbc - @inbounds u[k,c] = muladd(-F.U[k,j],pk[j],u[k,c]) - end - - @inbounds u[k,c] /= A.data[A.u+1,k] - end - end - u - end -end diff --git a/src/Caching/banded.jl b/src/Caching/banded.jl deleted file mode 100644 index 9a22e6be..00000000 --- a/src/Caching/banded.jl +++ /dev/null @@ -1,178 +0,0 @@ -function CachedOperator(::Type{BandedMatrix},op::Operator;padding::Bool=false) - l,u=bandwidths(op) - padding && (u+=l) - data = BandedMatrix{eltype(op)}(undef, (0,0), (l,u)) - CachedOperator(op,data,size(data),domainspace(op),rangespace(op),(-l,u),padding) -end - - - -## Grow cached operator - -function resizedata!(B::CachedOperator{T,<:BandedMatrix{T}},n::Integer,m_in::Integer) where T<:Number - m = max(m_in,n+B.data.u) - N,M = size(B) - n = min(n, N) - - if n > B.datasize[1] - pad!(B.data,min(N,2n),m) - - kr=B.datasize[1]+1:n - jr=max(B.datasize[1]+1-B.data.l,1):min(n+B.data.u,M) - BLAS.axpy!(1.0,view(B.op,kr,jr),view(B.data,kr,jr)) - - B.datasize = (n,m) - end - - B -end - - -resizedata!(B::CachedOperator{T,<:BandedMatrix{T}},n::Integer,::Colon) where T<:Number = - resizedata!(B, n, n+B.data.u) - -## Grow QR - -function QROperator(R::CachedOperator{T,<:BandedMatrix{T}}) where T - M = R.data.l+1 # number of diag+subdiagonal bands - H = Array{T}(undef,M,100) - QROperator(R,H,0) -end - - -function resizedata!(QR::QROperator{<:CachedOperator{T,<:BandedMatrix{T}, - MM,DS,RS,BI}}, - ::Colon,col) where {T,MM,DS,RS,BI} - if col ≤ QR.ncols - return QR - end - - MO=QR.R_cache - W=QR.H - - R=MO.data - M=R.l+1 # number of diag+subdiagonal bands - - if col+M-1 ≥ MO.datasize[1] - resizedata!(MO,(col+M-1)+100,:) # double the last rows - end - - if col > size(W,2) - W=QR.H=unsafe_resize!(W,:,2col) - end - - for k=QR.ncols+1:col - W[:,k] = view(R.data,R.u+1:R.u+R.l+1,k) # diagonal and below - wp=view(W,:,k) - W[1,k]+= flipsign(norm(wp),W[1,k]) - normalize!(wp) - - # scale banded entries - for j=k:k+R.u - dind=R.u+1+k-j - v=view(R.data,dind:dind+M-1,j) - dt=dot(wp,v) - LinearAlgebra.axpy!(-2*dt,wp,v) - end - - # scale banded/filled entries - for j=k+R.u+1:k+R.u+M-1 - p=j-k-R.u - v=view(R.data,1:M-p,j) # shift down each time - wp2=view(wp,p+1:M) - dt=dot(wp2,v) - LinearAlgebra.axpy!(-2*dt,wp2,v) - end - end - QR.ncols=col - QR -end - - -# BLAS versions, requires BlasFloat - - - -function resizedata!(QR::QROperator{<:CachedOperator{T,<:BandedMatrix{T}, - MM,DS,RS,BI}}, - ::Colon,col) where {T<:BlasFloat,MM,DS,RS,BI} - if col ≤ QR.ncols - return QR - end - - col = min(col, size(QR,2)) - - MO=QR.R_cache - W=QR.H - - R=MO.data - M=R.l+1 # number of diag+subdiagonal bands - - if col+M-1 ≥ MO.datasize[1] - resizedata!(MO,(col+M-1)+100,:) # double the last rows - end - - if col > size(W,2) - W=QR.H=unsafe_resize!(W,:,2col) - end - - m,n=size(R) - w=pointer(W) - r=pointer(R.data) - sz=sizeof(T) - st=stride(R.data,2) - stw=stride(W,2) - - for k=QR.ncols+1:col - v=r+sz*(R.u + (k-1)*st) # diagonal entry - wp=w+stw*sz*(k-1) # k-th column of W - BLAS.blascopy!(M,v,1,wp,1) - W[1,k]+= flipsign(BLAS.nrm2(M,wp,1),W[1,k]) - normalize!(M,wp) - - for j=k:k+R.u - v=r+sz*(R.u + (k-1)*st + (j-k)*(st-1)) - dt = BandedMatrices.dot(M,wp,1,v,1) - BLAS.axpy!(M,-2*dt,wp,1,v,1) - end - - for j=k+R.u+1:k+R.u+M-1 - p=j-k-R.u - v=r+sz*((j-1)*st) # shift down each time - dt = BandedMatrices.dot(M-p,wp+p*sz,1,v,1) - BLAS.axpy!(M-p,-2*dt,wp+p*sz,1,v,1) - end - end - QR.ncols=col - QR -end - - -## back substitution -# loop to avoid ambiguity with AbstractTRiangular -for ArrTyp in (:AbstractVector, :AbstractMatrix) - @eval function ldiv!(U::UpperTriangular{T, <:SubArray{T, 2, <:BandedMatrix{T}, Tuple{UnitRange{Int}, UnitRange{Int}}, false}}, - u::$ArrTyp{T}) where T - n = size(u,1) - n == size(U,1) || throw(DimensionMismatch()) - - V = parent(U) - @assert first(parentindices(V)[1]) == 1 - @assert first(parentindices(V)[2]) == 1 - - A = parent(V) - - b=bandwidth(A,2) - - for c=1:size(u,2) - for k=n:-1:1 - @simd for j=k+1:min(n,k+b) - @inbounds u[k,c] = muladd(-A.data[k-j+A.u+1,j],u[j,c],u[k,c]) - end - - @inbounds u[k,c] /= A.data[A.u+1,k] - end - end - u - end -end diff --git a/src/Caching/bandedblockbanded.jl b/src/Caching/bandedblockbanded.jl deleted file mode 100644 index eebd2cf7..00000000 --- a/src/Caching/bandedblockbanded.jl +++ /dev/null @@ -1,60 +0,0 @@ - -function CachedOperator(::Type{BandedBlockBandedMatrix}, op::Operator) - l,u = blockbandwidths(op) - λ,μ = subblockbandwidths(op) - data = BandedBlockBandedMatrix{eltype(op)}(undef, - blocklengths(rangespace(op))[1:0],blocklengths(domainspace(op))[1:0], - (l,u), (λ,μ)) - - CachedOperator(op,data,size(data),domainspace(op),rangespace(op),(-l,u),false) -end - -# Grow cached operator -# -function resizedata!(B::CachedOperator{T,<:BandedBlockBandedMatrix{T}}, ::Colon, col::Integer) where {T<:Number} - if col > size(B,2) - throw(ArgumentError("Cannot resize beyound size of operator")) - end - - if col > B.datasize[2] - l,u,λ,μ = B.data.l,B.data.u,B.data.λ,B.data.μ - J = Int(block(domainspace(B),col)) - - rows = blocklengths(rangespace(B.op))[1:J+l] - cols = blocklengths(domainspace(B.op))[1:J] - - # B.data = _BandedBlockBandedMatrix(PseudoBlockArray - - blocks = PseudoBlockArray(pad(B.data.data.blocks,:,(l+u+1)*sum(cols)), rows, cols) - B.data = _BandedBlockBandedMatrix(blocks, rows, cols, (l, u), (λ, μ)) - - jr=B.datasize[2]+1:col - kr=colstart(B.data,jr[1]):colstop(B.data,jr[end]) - - isempty(kr) || BLAS.axpy!(1.0,view(B.op,kr,jr),view(B.data,kr,jr)) - - B.datasize = (last(kr),col) - end - - B -end - -function resizedata!(B::CachedOperator{T,<:BandedBlockBandedMatrix{T}},n::Integer,m::Integer) where {T<:Number} - resizedata!(B, :, m) - if n < B.datasize[1] - return B - end - - l,u,λ,μ = B.data.l,B.data.u,B.data.λ,B.data.μ - - l,u,λ,μ = B.data.l,B.data.u,B.data.λ,B.data.μ - - # make sure we have enough rows - K = Int(block(rangespace(B),n)) - rows = blocklengths(rangespace(B.op))[1:K] - - B.data = _BandedBlockBandedMatrix(B.data.data, blockedrange(rows), (l,u), (λ,μ)) - B.datasize = (n,B.datasize[2]) - - B -end diff --git a/src/Caching/blockbanded.jl b/src/Caching/blockbanded.jl deleted file mode 100644 index 40ee4d24..00000000 --- a/src/Caching/blockbanded.jl +++ /dev/null @@ -1,317 +0,0 @@ - - - -# # default copy is to loop through -# # override this for most operators. -function default_BlockBandedMatrix(S::Operator) - ret = BlockBandedMatrix(Zeros, S) - - @inbounds for J=blockaxes(ret,2), K=blockcolrange(ret,Int(J)) - ret[K,J] = view(S,K,J) - end - ret -end -# - - -# diagblockshift gives the shift for the diagonal block of an operator -# that is, trace an operator down the diagonal. What blocks correspond to the -# block diagonal? -# this is used to determine how many blocks to pad in the QR decomposition, as -# every lower block gets added to the upper daigonal - -diagblockshift(a,b) = error("Developer: Not implemented for blocklengths $a, $b") - -function diagblockshift(a::AbstractRange, b::AbstractRange) - @assert step(a) == step(b) - first(b)-first(a) -end -diagblockshift(a::AbstractUnitRange, b::AbstractUnitRange) = first(b)-first(a) - -#TODO: generalize -function diagblockshift(a::AbstractFill{Int},b::AbstractFill{Int}) - @assert getindex_value(a) == getindex_value(b) - 0 -end - -diagblockshift(a::AbstractFill{Int},b::Vcat{Int,1,<:Tuple{V1,<:AbstractFill{Int}}}) where {V1 <: AbstractVector{Int}} = - max(0,-diagblockshift(b,a)) - - -function diagblockshift(a::Vcat{Int,1,<:Tuple{V1,<:AbstractFill{Int}}},b::AbstractFill{Int}) where V1 <: AbstractVector{Int} - @assert getindex_value(a.args[end]) == getindex_value(b) - isempty(a.args[1]) && return diagblockshift(a.args[2],b) - a1, b1 = a[1],b[1] - a1 == b1 && return diagblockshift(Vcat(a.args[1][2:end],a.args[2]),b) - a1 > b1 && length(a.args[1]) == 1 && return 0 - a1 > b1 && return max(0,-1+diagblockshift(flatten(([a1-b1;a.args[1][2:end]],a.args[2]),b))) - a1 < b1 && length(a.args[1]) == 1 && return 1 - # a1 < b1 && - return 1+diagblockshift(Vcat(a.args[1][2:end],a.args[2]),Vcat([b1-a1],b)) -end - -function diagblockshift(a::Vcat{Int,1,<:Tuple{V1,<:AbstractFill{Int}}}, - b::Vcat{Int,1,<:Tuple{V2,<:AbstractFill{Int}}}) where {V1 <: AbstractVector{Int},V2 <: AbstractVector{Int}} - isempty(a.args[1]) && return diagblockshift(a.args[2],b) - isempty(b.args[1]) && return diagblockshift(a,b.args[2]) - a1, b1 = a[1],b[1] - a1 == b1 && return diagblockshift(Vcat(a.args[1][2:end],a.args[2]),Vcat(b.args[1][2:end],b.args[2])) - a1 > b1 && return max(0,-1+diagblockshift(Vcat([a1-b1;a.args[1][2:end]],a.args[2]), - Vcat(b.args[1][2:end],b.args[2]))) - # a1 < b1 && - return 1+diagblockshift(Vcat(a.args[1][2:end],a.args[2]),Vcat([b1-a1;b.args[1][2:end]],b.args[2])) -end - - -diagblockshift(op::Operator) = - diagblockshift(blocklengths(domainspace(op)),blocklengths(rangespace(op))) - - -function CachedOperator(::Type{BlockBandedMatrix},op::Operator;padding::Bool=false) - l,u=blockbandwidths(op) - padding && (u+=l+diagblockshift(op)) - data=BlockBandedMatrix{eltype(op)}(undef, - Vector{Int}(), Vector{Int}(), - (l,u)) - CachedOperator(op,data,(0,0),domainspace(op),rangespace(op),(-l,u),padding) -end - - - - - -## Grow cached operator -# -function resizedata!(B::CachedOperator{T,BlockBandedMatrix{T}},::Colon,col::Integer) where {T<:Number} - if col > size(B,2) - throw(ArgumentError("Cannot resize beyound size of operator")) - end - - if col > B.datasize[2] - if B.datasize[2] == 0 - datablocksize = Block(0) - else - datablocksize = block(domainspace(B),B.datasize[2]) - bs = blockstop(domainspace(B),datablocksize) - if bs ≠ B.datasize[2] - error("Internal Error: $(B.datasize) is not lined up with the block $datablocksize as the last column doesn't end at $bs") - end - end - - - l,u = blockbandwidths(B.data) - J = block(domainspace(B),col) - col = blockstop(domainspace(B),J) # pad to end of block - - rows = blocklengths(rangespace(B.op))[1:Int(J)+l] - cols = blocklengths(domainspace(B.op))[1:Int(J)] - - b_size = BlockBandedSizes(Vector{Int}(rows), Vector{Int}(cols), l, u) - - resize!(B.data.data, bb_numentries(b_size)) - B.data = _BlockBandedMatrix(B.data.data, b_size) - - JR = datablocksize+1:J - KR=blockcolstart(B.data,first(JR)):blockcolstop(B.data,last(JR)) - copyto!(view(B.data,KR,JR), view(B.op,KR,JR)) - - B.datasize = (blockstop(rangespace(B),last(KR)),col) - end - - B -end - -function resizedata!(B::CachedOperator{T,BlockBandedMatrix{T}},n::Integer,m::Integer) where {T<:Number} - N = block(rangespace(B),n) - m̃ = blockstart(domainspace(B),N) - resizedata!(B,:,max(m,m̃)) -end - - -## QR -# we use a RaggedMatrix to represent the growing lengths of the -# householder reflections -QROperator(R::CachedOperator{T,BlockBandedMatrix{T}}) where {T} = - QROperator(R,RaggedMatrix{T}(undef, 0, Int[]),0) - - -# function resizedata!(QR::QROperator{CachedOperator{T,BlockBandedMatrix{T}, -# MM,DS,RS,BI}}, -# ::Colon, col) where {T,MM,DS,RS,BI} -# if col ≤ QR.ncols -# return QR -# end -# -# MO=QR.R_cache -# W=QR.H -# -# R=MO.data -# cs=colstop(MO,col) -# -# if cs ≥ MO.datasize[1] -# resizedata!(MO,cs+100,:) # add 100 rows -# R=MO.data -# end -# -# if col > size(W,2) -# m=size(W,2) -# resize!(W.cols,2col+1) -# -# for j=m+1:2col -# cs=colstop(MO,j) -# W.cols[j+1]=W.cols[j] + cs-j+1 -# W.m=max(W.m,cs-j+1) -# end -# -# resize!(W.data,W.cols[end]-1) -# end -# -# for k=QR.ncols+1:col -# cs = colstop(R,k) -# W[1:cs-k+1,k] = view(R,k:cs,k) # diagonal and below -# wp=view(W,1:cs-k+1,k) -# W[1,k]+= flipsign(norm(wp),W[1,k]) -# normalize!(wp) -# -# # scale banded entries -# for j=k:rowstop(R,k) -# v=view(R,k:cs,j) -# dt=dot(wp,v) -# LinearAlgebra.axpy!(-2*dt,wp,v) -# end -# -# # scale banded/filled entries -# for j=rowstop(R,k)+1:rowstop(R,cs) -# csrt=colstart(R,j) -# v=view(R,csrt:cs,j) # shift down each time -# wp2=view(wp,csrt-k+1:cs-k+1) -# dt=dot(wp2,v) -# LinearAlgebra.axpy!(-2*dt,wp2,v) -# end -# end -# QR.ncols=col -# QR -# end - -# always resize by column -resizedata!(QR::QROperator{CachedOperator{T,BlockBandedMatrix{T}, - MM,DS,RS,BI}}, - ::Colon, col::Int) where {T,MM,DS,RS,BI} = - resizedata!(QR, :, block(domainspace(QR.R_cache),col)) - -function resizedata!(QR::QROperator{CachedOperator{T,BlockBandedMatrix{T}, - MM,DS,RS,BI}}, - ::Colon, COL::Block) where {T<:BlasFloat,MM,DS,RS,BI} - MO = QR.R_cache - W = QR.H - R = MO.data - - l,u = blockbandwidths(R) - - ds = domainspace(QR) - col = blockstop(ds, COL) # last column - - if col ≤ QR.ncols - return QR - end - - J_start = Int(block(ds, QR.ncols+1)) # first block-column not factorized - @assert blockstart(ds, J_start) == QR.ncols+1 # we want to make sure we haven't partially factorized a column - - # last block, convoluted def to match blockbandedmatrix - J_col = Int(COL) - K_end = Int(blockcolstop(MO, J_col)) # last row block in last column - J_end = Int(blockrowstop(MO, K_end)) # QR will affect up to this column - j_end = blockstop(domainspace(MO), J_end) # we need to resize up this column - sz = sizeof(T) - - if j_end ≥ MO.datasize[2] - # add columns up to column rs, which is last column affected by QR - resizedata!(MO, :, j_end) - R = MO.data - end - - if col > size(W,2) - # resize Householder matrix - m = size(W,2) - resize!(W.cols, 2col+1) - - for j=m+1:2col - cs = blockstop(rangespace(MO), blockcolstop(MO, block(domainspace(MO),j))) - W.cols[j+1]=W.cols[j] + cs-j+1 - W.m=max(W.m,cs-j+1) - end - - resize!(W.data, W.cols[end]-1) - end - - w = pointer(W.data) - r = pointer(R.data) - - bs = R.block_sizes - - for j =QR.ncols+1 : col # first column of block - bi = findblockindex.(bs.axes, (j, j)) # converts from global indices to block indices - K1, J1 = Int.(block.(bi)) # this is the diagonal block corresponding to j - κ, ξ = blockindex.(bi) - - st = bs.block_strides[J1] # the stride of the matrix - shft = bs.block_starts[K1,J1]-1 + st*(ξ-1) + κ-1 # the index of the pointer to the j, j entry - - - K_CS = Int(blockcolstop(R,J1)) # last row in J-th blockcolumn - k_end = last(bs.axes[1][Block(K_CS)]) - - w_j = W.cols[j] # the data index for the j-th column of W - wp = w+sz*(w_j-1) # j-th column of W - - M = k_end - j + 1 # the number of entries we are diagonalizing. we know the stride tells us the total number of rows - - BLAS.blascopy!(M, r+sz*shft, 1, wp, 1) # copy the column into W - - - # we need to scale the first entry and then normalize - W.data[w_j] += flipsign(BLAS.nrm2(M,wp,1), W.data[w_j]) - normalize!(M, wp) - - # scale rest of columns in first block - # for ξ_2 = 2: - - for ξ_2 = ξ:length(bs.axes[2][Block(J1)]) - # we now apply I-2v*v' in place - r_sh = r+sz*(shft + st*(ξ_2-ξ)) # the pointer the (j,ξ_2)-th entry - dt = BandedMatrices.dot(M, wp, 1, r_sh, 1) - BLAS.axpy!(M, -2*dt, wp, 1, r_sh ,1) - end - - for J = J1+1:min(K1+u,J_end) - st = bs.block_strides[J] - shft = bs.block_starts[K1,J] + κ-2 # the index of the pointer to the j, j entry - for ξ_2 = axes(bs.axes[2][Block(J)],1) - # we now apply I-2v*v' in place - # r_sh = r+sz*(shft + st*(ξ_2-1)) # the pointer the (j,ξ_2)-th entry - - # TODO: remove these debugging statement - # @assert w_j-1 + M ≤ length(W.data) - # @assert shft + st*(ξ_2-1) + M ≤ length(R.data) - # @assert 0 ≤ w_j-1 - # if ! (0 ≤ shft + st*(ξ_2-1)) - # @show shft, st, ξ_2, l, u - # @show κ, bs.block_starts[K1,J] - # @show K1, J - # @show MO.op - # end - # dt = dot(M, wp, 1, r_sh, 1) - # BLAS.axpy!(M, -2*dt, wp, 1, r_sh ,1) - - dt = dot(view(W.data, w_j:w_j+M-1) , - view(R.data, shft + st*(ξ_2-1) +1:shft + st*(ξ_2-1) +M)) - BLAS.axpy!(-2*dt, view(W.data, w_j:w_j+M-1) , - view(R.data, shft + st*(ξ_2-1) +1:shft + st*(ξ_2-1) +M)) - end - end - end - - QR.ncols=col - QR -end diff --git a/src/Caching/caching.jl b/src/Caching/caching.jl deleted file mode 100644 index 2d4f48ed..00000000 --- a/src/Caching/caching.jl +++ /dev/null @@ -1,19 +0,0 @@ -#### Caching - - - -## Ac'* for QROperatorQ - -function mul_pars(Ac::Adjoint{<:Any,<:QROperatorQ},B::AbstractVector,tolerance,maxlength) - A = parent(Ac) - T = promote_type(eltype(A),eltype(B)) - mulpars(Operator{T}(A)',Array{T}(B),tolerance,maxlength) -end - - -include("matrix.jl") -include("ragged.jl") -include("banded.jl") -include("almostbanded.jl") -include("blockbanded.jl") -include("bandedblockbanded.jl") diff --git a/src/Caching/matrix.jl b/src/Caching/matrix.jl deleted file mode 100644 index 18f0636e..00000000 --- a/src/Caching/matrix.jl +++ /dev/null @@ -1,147 +0,0 @@ -CachedOperator(::Type{Matrix},op::Operator;padding::Bool=false) = - CachedOperator(op,Array{eltype(op)}(0,0),padding) - - -# Grow cached operator - -function resizedata!(B::CachedOperator{T,Matrix{T}},n::Integer,m::Integer) where T<:Number - if n > size(B,1) || m > size(B,2) - throw(ArgumentError("Cannot resize beyound size of operator")) - end - - # this does nothing if already in dimensions - N,M=size(B.data) - if n > N && m > M - B.data = unsafe_resize!(B.data,n,m) - elseif n > N - B.data = unsafe_resize!(B.data,n,:) - elseif m > M - B.data = unsafe_resize!(B.data,:,m) - end - - if n ≤ B.datasize[1] && m ≤ B.datasize[2] - # do nothing - B - elseif n ≤ B.datasize[1] - kr,jr=1:B.datasize[1],B.datasize[2]+1:m - B.data[kr,jr] = B.op[kr,jr] - B.datasize = (B.datasize[1],m) - B - elseif m ≤ B.datasize[2] - kr,jr=B.datasize[1]+1:n,1:B.datasize[2] - B.data[kr,jr] = B.op[kr,jr] - B.datasize = (n,B.datasize[2]) - B - else - # resize rows then columns - resizedata!(resizedata!(B,n,B.datasize[2]),n,m) - end -end - -# Apply Householder - - -function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,Matrix{T},T},T}}, - B::AbstractVector{T},tolerance,maxlength) where {RR,T} - A = parent(Ac) - if length(B) > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,length(B)+size(A.QR.H,1)+10) - end - - H=A.QR.H - M=size(H,1) - m=length(B) - Y=pad(B,m+M+10) - - k=1 - yp=view(Y,1:M) - while (k ≤ m+M || norm(yp) > tolerance ) - if k > maxlength - @warn "Maximum length $maxlength reached." - break - end - - if k+M-1>length(Y) - pad!(Y,2*(k+M)) - end - if k > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,k+M+50) - H=A.QR.H - end - - wp=view(H,:,k) - yp=view(Y,k:k+M-1) - - dt=dot(wp,yp) - LinearAlgebra.axpy!(-2*dt,wp,yp) - k+=1 - end - resize!(Y,k) # chop off zeros -end - - -# BLAS apply Q - -function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,Matrix{T},T},T}}, - B::AbstractVector{T}, - tolerance,maxlength) where {RR,T<:BlasFloat} - - A = parent(Ac) - - A_dim = size(A,1) - - if length(B) > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,length(B)+size(A.QR.H,1)+10) - end - - if size(A.QR.H,1) == 1 # diagonal scaling, avoid growing - diagv=view(A.QR.H,1,1:length(B)) - ret=similar(B) - @simd for k=1:length(ret) - @inbounds ret[k]=(1-2A.QR.H[1,k]^2)*B[k] - end - return ret - end - - H=A.QR.H - h=pointer(H) - M=size(H,1) - st=stride(H,2) - - sz=sizeof(T) - - m=length(B) - Y=pad(B,m+M+10) - y=pointer(Y) - - k=1 - yp=y - while (k ≤ min(m+M,A_dim) || BLAS.nrm2(M,yp,1) > tolerance ) - if k > maxlength - @warn "Maximum length $maxlength reached." - break - end - - if k+M-1>length(Y) - pad!(Y,2*(k+M)) - y=pointer(Y) - end - if k > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,2*(k+M)) - H=A.QR.H - h=pointer(H) - end - - wp=h+sz*st*(k-1) - yp=y+sz*(k-1) - - dt = BandedMatrices.dot(M,wp,1,yp,1) - BLAS.axpy!(M,-2*dt,wp,1,yp,1) - k+=1 - end - resize!(Y,min(k,A_dim)) # chop off zeros -end diff --git a/src/Caching/ragged.jl b/src/Caching/ragged.jl deleted file mode 100644 index 2825dcc9..00000000 --- a/src/Caching/ragged.jl +++ /dev/null @@ -1,333 +0,0 @@ - -CachedOperator(::Type{RaggedMatrix},op::Operator;padding::Bool=false) = - CachedOperator(op,RaggedMatrix{eltype(op)}(undef, 0, Int[]),padding) - -## Grow cached operator - -function resizedata!(B::CachedOperator{T,RaggedMatrix{T}},::Colon,n::Integer) where T<:Number - if n > size(B,2) - throw(ArgumentError("Cannot resize beyond size of operator")) - end - - if n > B.datasize[2] - resize!(B.data.cols,n+1) - - if B.padding - # K is largest colstop. We get previous largest by looking at precalulated - # cols - K = B.datasize[2]==0 ? 0 : B.data.cols[B.datasize[2]+1]-B.data.cols[B.datasize[2]] - - for j = B.datasize[2]+1:n - K = max(K,colstop(B.op,j)) - B.data.cols[j+1] = B.data.cols[j] + K - end - else - K = B.datasize[2]==0 ? 0 : B.data.m# more robust but slower: maximum(diff(B.data.cols)) - - for j = B.datasize[2]+1:n - cs = colstop(B.op,j) - K = max(K,cs) - B.data.cols[j+1] = B.data.cols[j] + cs - end - end - - # avoid padding with negative length - if B.data.cols[n+1] ≤ 0 - return B - end - - pad!(B.data.data,B.data.cols[n+1]-1) - B.data.m = K - - jr=B.datasize[2]+1:n - kr=1:K - BLAS.axpy!(1.0,view(B.op,kr,jr),view(B.data,kr,jr)) - - B.datasize = (K,n) - - end - - B -end - -function resizedata!(B::CachedOperator{T,RaggedMatrix{T}},n::Integer,m::Integer) where T<:Number - resizedata!(B,:,m) - B.data.m = max(B.data.m,n) # make sure we have at least n rows - - B -end - - -## Grow QR - -QROperator(R::CachedOperator{T,RaggedMatrix{T}}) where {T} = - QROperator(R,RaggedMatrix{T}(undef,0,Int[]),0) - -function resizedata!(QR::QROperator{CachedOperator{T,RaggedMatrix{T}, - MM,DS,RS,BI}}, - ::Colon,col) where {T,MM,DS,RS,BI} - if col ≤ QR.ncols - return QR - end - - MO=QR.R_cache - W=QR.H - - if col > MO.datasize[2] - m = MO.datasize[2] - resizedata!(MO,:,col+100) # last rows plus a bunch more - - # apply previous Householders to new columns of R - for J=1:QR.ncols - wp=view(W,1:colstop(W,J),J) - for j=m+1:MO.datasize[2] - kr=J:J+length(wp)-1 - v=view(MO.data,kr,j) - dt=BandedMatrices.dot(wp,v) - LinearAlgebra.axpy!(-2*dt,wp,v) - end - end - end - - - if col > size(W,2) - m=size(W,2) - resize!(W.cols,col+101) - - for j=m+1:col+100 - cs=colstop(MO.data,j) - W.cols[j+1]=W.cols[j] + cs-j+1 - W.m=max(W.m,cs-j+1) - end - - resize!(W.data,W.cols[end]-1) - end - - for k=QR.ncols+1:col - cs = colstop(MO.data,k) - W[1:cs-k+1,k] = view(MO.data,k:cs,k) # diagonal and below - wp=view(W,1:cs-k+1,k) - W[1,k]+= flipsign(norm(wp),W[1,k]) - normalize!(wp) - - # scale rows entries - kr=k:k+length(wp)-1 - for j=k:MO.datasize[2] - v=view(MO.data,kr,j) - dt=BandedMatrices.dot(wp,v) - LinearAlgebra.axpy!(-2*dt,wp,v) - end - end - QR.ncols=col - QR -end - - -# BLAS versions, requires BlasFloat - - -function resizedata!(QR::QROperator{CachedOperator{T,RaggedMatrix{T}, - MM,DS,RS,BI}}, -::Colon,col) where {T<:BlasFloat,MM,DS,RS,BI} - if col ≤ QR.ncols - return QR - end - - MO=QR.R_cache - W=QR.H - - sz=sizeof(T) - - w=pointer(W.data) - R=MO.data - r=pointer(R.data) - - if col > MO.datasize[2] - m = MO.datasize[2] - resizedata!(MO,:,col+100) # last rows plus a bunch more - - R=MO.data - r=pointer(R.data) - - # apply previous Householders to new columns of R - for k=1:QR.ncols - M=colstop(W,k) # length of wp - wp=w+(W.cols[k]-1)*sz # shift by first index of col J - - for j=m+1:MO.datasize[2] - v=r+(R.cols[j]+k-2)*sz - dt=BandedMatrices.dot(M,wp,1,v,1) - BLAS.axpy!(M,-2*dt,wp,1,v,1) - end - end - end - - - if col > size(W,2) - m=size(W,2) - resize!(W.cols,col+101) - - for j=m+1:col+100 - cs=colstop(R,j) - q_len=cs-j+1 # number of entries in j-th column manipulated - @assert q_len > 0 # Otherwise, diagonal is not included - W.cols[j+1]=W.cols[j] + q_len - W.m=max(W.m,q_len) - end - - resize!(W.data,W.cols[end]-1) - w=pointer(W.data) - end - - for k=QR.ncols+1:col - cs= colstop(R,k) - M=cs-k+1 - - v=r+sz*(R.cols[k]+k-2) # diagonal entry of R - wp=w+sz*(W.cols[k]-1) # k-th column of W - BLAS.blascopy!(M,v,1,wp,1) - W.data[W.cols[k]] += flipsign(BLAS.nrm2(M,wp,1),W.data[W.cols[k]]) - normalize!(M,wp) - - # scale rows entries - for j=k:MO.datasize[2] - v=r+(R.cols[j]+k-2)*sz - dt=BandedMatrices.dot(M,wp,1,v,1) - BLAS.axpy!(M,-2*dt,wp,1,v,1) - end - end - QR.ncols=col - QR -end - - - - - -## back substitution -for ArrTyp in (:AbstractVector, :AbstractMatrix) - @eval function ldiv!(U::UpperTriangular{T, SubArray{T, 2, RaggedMatrix{T}, Tuple{UnitRange{Int}, UnitRange{Int}}, false}}, - u::$ArrTyp{T}) where T - n = size(u,1) - n == size(U,1) || throw(DimensionMismatch()) - - V = parent(U) - @assert parentindices(V)[1][1] == 1 - @assert parentindices(V)[2][1] == 1 - - A = parent(V) - - for c=1:size(u,2) - for k=n:-1:1 - @inbounds ck = A.cols[k] - @inbounds u[k,c] /= A.data[ck+k-1] - BLAS.axpy!(-u[k,c], view(A.data,ck:ck+k-2), view(u,1:k-1,c)) - end - end - u - end -end - - - -## Apply Q - - -function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,RaggedMatrix{T},T},T}}, - B::AbstractVector{T},tolerance,maxlength) where {RR,T} - A = parent(Ac) - if length(B) > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,length(B)+size(A.QR.H,1)+10) - end - - H=A.QR.H - M=size(H,1) - m=length(B) - Y=pad(B,m+M+10) - - k=1 - yp=view(Y,1:length(B)) - while (k ≤ m || norm(yp) > tolerance ) - if k > maxlength - @warn "Maximum length $maxlength reached." - break - end - if k > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,k+M+50) - H=A.QR.H - M=size(H,1) - end - - cr=colrange(H,k) - - if k+length(cr)-1>length(Y) - pad!(Y,2*(k+M)) - end - - wp=view(H,cr,k) - yp=view(Y,k-1+(cr)) - - dt=BandedMatrices.dot(wp,yp) - LinearAlgebra.axpy!(-2*dt,wp,yp) - k+=1 - end - resize!(Y,k) # chop off zeros -end - - - -# BLAS apply Q - -function mulpars(Ac::Adjoint{T,<:QROperatorQ{QROperator{RR,RaggedMatrix{T},T},T}}, - B::AbstractVector{T},tolerance,maxlength) where {RR,T<:BlasFloat} - A = parent(Ac) - if length(B) > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,length(B)+size(A.QR.H,1)+10) - end - - H=A.QR.H - h=pointer(H.data) - - M=size(H,1) - m=length(B) - Y=pad(B,m+M+10) - - sz=sizeof(T) - - k=1 - y=pointer(Y) - - yp=y - while (k ≤ m || BLAS.nrm2(M,yp,1) > tolerance ) - if k > maxlength - @warn "Maximum length $maxlength reached." - break - end - if k > A.QR.ncols - # upper triangularize extra columns to prepare for \ - resizedata!(A.QR,:,k+M+50) - H=A.QR.H - h=pointer(H.data) - end - - - M=colstop(H,k) - - if k+M-1>length(Y) - pad!(Y,2*(k+M)) - y=pointer(Y) - end - - - wp=h + sz*(H.cols[k]-1) - yp=y+sz*(k-1) - - dt = BandedMatrices.dot(M,wp,1,yp,1) - BLAS.axpy!(M,-2*dt,wp,1,yp,1) - k+=1 - end - resize!(Y,k) # chop off zeros -end diff --git a/src/Domain.jl b/src/Domain.jl deleted file mode 100644 index de0103d3..00000000 --- a/src/Domain.jl +++ /dev/null @@ -1,186 +0,0 @@ - - -export Domain, SegmentDomain, tocanonical, fromcanonical, fromcanonicalD, ∂ -export chebyshevpoints, fourierpoints, isambiguous, arclength -export components, component, ncomponents - - - - -# add indexing for all spaces, not just DirectSumSpace -# mimicking scalar vs vector - -# prectype gives the precision, including for Vec -prectype(::Type{D}) where {D<:Domain} = float(eltype(eltype(D))) -prectype(d::Domain) = prectype(typeof(d)) - -#TODO: bivariate AnyDomain -struct AnyDomain <: Domain{UnsetNumber} end -struct EmptyDomain <: Domain{Nothing} end - -isambiguous(::AnyDomain) = true -dimension(::AnyDomain) = 1 - -complexlength(::AnyDomain) = NaN -arclength(::AnyDomain) = NaN -arclength(::EmptyDomain) = false -arclength(::DomainSets.EmptySpace) = false - -isempty(::AnyDomain) = false - -reverseorientation(a::Union{AnyDomain,EmptyDomain}) = a - -canonicaldomain(a::Union{AnyDomain,EmptyDomain}) = a - -indomain(x::Domain,::EmptyDomain) = false - -convert(::Type{Domain{T}}, ::AnyDomain) where T = Domain(T) - - -union(::AnyDomain, d::Domain) = d -union(d::Domain, ::AnyDomain) = d - -union(::EmptyDomain, ::EmptyDomain) = EmptyDomain() -union(::EmptyDomain, a::Domain) = a -union(a::Domain, ::EmptyDomain) = a - -##General routines -isempty(::EmptyDomain) = true - - -## Interval DomainSets - -abstract type SegmentDomain{T} <: Domain{T} end -abstract type AbstractSegment{T} <: SegmentDomain{T} end -const IntervalOrSegment{T} = Union{AbstractInterval{T}, AbstractSegment{T}} -const IntervalOrSegmentDomain{T} = Union{AbstractInterval{T}, SegmentDomain{T}} - -canonicaldomain(d::IntervalOrSegmentDomain) = ChebyshevInterval{real(prectype(d))}() - -domainscompatible(a,b) = domainscompatible(domain(a),domain(b)) -domainscompatible(a::Domain,b::Domain) = isambiguous(a) || isambiguous(b) || - isapprox(a,b) - -##TODO: Should fromcanonical be fromcanonical!? - -#TODO consider moving these -leftendpoint(d::IntervalOrSegmentDomain{T}) where {T} = fromcanonical(d,-one(eltype(T))) -rightendpoint(d::IntervalOrSegmentDomain{T}) where {T} = fromcanonical(d,one(eltype(T))) - -indomain(x,::AnyDomain) = true -function indomain(x,d::SegmentDomain) - T=float(real(prectype(d))) - y=tocanonical(d,x) - ry=real(y) - iy=imag(y) - sc=norm(fromcanonicalD(d,ry<-1 ? -one(ry) : (ry>1 ? one(ry) : ry))) # scale based on stretch of map on projection to interal - dy=fromcanonical(d,y) - # TODO: use isapprox once keywords are fast - ((isinf(norm(dy)) && isinf(norm(x))) || norm(dy-x) ≤ 1000eps(T)*max(norm(x),1)) && - -one(T)-100eps(T)/sc ≤ ry ≤ one(T)+100eps(T)/sc && - -100eps(T)/sc ≤ iy ≤ 100eps(T)/sc -end - -ncomponents(s::Domain) = 1 -components(s::Domain) = [s] -function components(s::Domain,k) - k ≠ 1 && throw(BoundsError()) - s -end - -issubcomponent(a::Domain,b::Domain) = a in components(b) - - -##### canoncial -""" - canonicaldomain(d) - -returns a domain which we map to for operations. For example, -the canonical domain for an interval [a,b] is [-1,1] -""" -function canonicaldomain end - - -""" - tocanonical(d, x) - -maps the point `x` in `d` to a point in `canonical(d,x)` -""" -function tocanonical end - -issubset(a::Domain,b::Domain) = a==b - - -## conveninece routines - -ones(d::Domain) = ones(prectype(d),Space(d)) -zeros(d::Domain) = zeros(prectype(d),Space(d)) - - -function commondomain(P::AbstractVector) - ret = AnyDomain() - - for op in P - d = domain(op) - @assert ret == AnyDomain() || d == AnyDomain() || ret == d - - if d != AnyDomain() - ret = d - end - end - - ret -end - -commondomain(P::AbstractVector,g::AbstractArray{T}) where {T<:Number} = commondomain(P) -commondomain(P::AbstractVector,g) = commondomain([P;g]) - - -domain(::Number) = AnyDomain() - - -## rand - - -rand(d::IntervalOrSegmentDomain,k...) = fromcanonical.(Ref(d),2rand(k...)-1) - - -checkpoints(d::IntervalOrSegmentDomain) = fromcanonical.(Ref(d),[-0.823972,0.01,0.3273484]) - -## boundary - -boundary(d::SegmentDomain) = [leftendpoint(d),rightendpoint(d)] #TODO: Points domain - - - - - -## map domains -# we auto vectorize arguments -tocanonical(d::Domain,x,y,z...) = tocanonical(d,Vec(x,y,z...)) -fromcanonical(d::Domain,x,y,z...) = fromcanonical(d,Vec(x,y,z...)) - - -mappoint(d1::Domain,d2::Domain,x...) = fromcanonical(d2,tocanonical(d1,x...)) -invfromcanonicalD(d::Domain,x...) = 1/fromcanonicalD(d,x...) - - - -## domains in higher dimensions - - -## sorting -# we sort spaces lexigraphically by default - -for OP in (:<,:(<=),:>,:(>=),:(isless)) - @eval $OP(a::Domain,b::Domain)=$OP(string(a),string(b)) -end - - -## Other special domains - -struct PositiveIntegers <: Domain{Int} end -struct Integers <: Domain{Int} end - -const ℕ = PositiveIntegers() -const ℤ = Integers() diff --git a/src/Domains/Domains.jl b/src/Domains/Domains.jl deleted file mode 100644 index 0d61ceb3..00000000 --- a/src/Domains/Domains.jl +++ /dev/null @@ -1,82 +0,0 @@ -include("Segment.jl") -include("UnionDomain.jl") -include("PiecewiseSegment.jl") -include("Point.jl") - - - - -convert(::Type{Space},d::ClosedInterval) = Space(Domain(d)) - -#issubset between domains - -issubset(a::IntervalOrSegment{T}, b::PiecewiseSegment{T}) where {T<:Real} = - a⊆Segment(first(b.points),last(b.points)) - - -function setdiff(b::Segment,a::Point) - if !(a ⊆ b) - b - elseif leftendpoint(b) == a.x || rightendpoint(b) == a.x - b - else - Segment(leftendpoint(b),a.x) ∪ Segment(a.x,rightendpoint(b)) - end -end - -# sort - - - -function Base.setdiff(d::SegmentDomain,p::Point) - x = Number(p) - (x ∉ d || x ≈ leftendpoint(d) || x ≈ rightendpoint(d)) && return d - DifferenceDomain(d,p) -end - - - -# multivariate domainxs - -include("multivariate.jl") - -affine_setdiff(d::Domain, ptsin::UnionDomain) = - _affine_setdiff(d, Number.(elements(ptsin))) - -affine_setdiff(d::Domain, ptsin::WrappedDomain{<:AbstractVector}) = - _affine_setdiff(d, ptsin.domain) - -function _affine_setdiff(d::Domain, p::Number) - isempty(d) && return d - p ∉ d && return d - a,b = endpoints(d) - if leftendpoint(d) > rightendpoint(d) - a,b = b,a - end - UnionDomain(a..p, p..b) # TODO: Clopen interval -end - -function _affine_setdiff(d::Domain, pts) - isempty(pts) && return d - tol=sqrt(eps(arclength(d))) - da=leftendpoint(d) - isapprox(da,pts[1];atol=tol) && popfirst!(pts) - isempty(pts) && return d - db=rightendpoint(d) - isapprox(db,pts[end];atol=tol) && pop!(pts) - - sort!(pts) - leftendpoint(d) > rightendpoint(d) && reverse!(pts) - filter!(p->p ∈ d,pts) - - isempty(pts) && return d - length(pts) == 1 && return d \ pts[1] - - ret = Array{Domain}(undef, length(pts)+1) - ret[1] = Domain(leftendpoint(d) .. pts[1]) - for k = 2:length(pts) - ret[k] = Domain(pts[k-1]..pts[k]) - end - ret[end] = Domain(pts[end] .. rightendpoint(d)) - UnionDomain(ret) -end \ No newline at end of file diff --git a/src/Domains/Grids.jl b/src/Domains/Grids.jl deleted file mode 100644 index 2a51de8f..00000000 --- a/src/Domains/Grids.jl +++ /dev/null @@ -1,29 +0,0 @@ - - -# Represent evenly spaced points on [0,2π) -struct FourierGrid{T} <: AbstractVector{T} - n::Int - π_over_n::T - FourierGrid{T}(n::Int) where T = new{T}(n, convert(T,π)/n) -end - -size(g::FourierGrid) = (g.n,) -@inline function getindex(g::FourierGrid, k::Int) - @boundscheck Base.checkbounds(g, k) - (2k-2)*g.π_over_n -end -Base.IndexStyle(::Type{<:FourierGrid}) = IndexLinear() - - -FourierGrid(10) - -using BenchmarkTools -g = FourierGrid{Float64}(10_000) - -fp(n) = collect(FourierGrid{Float64}(n)) -@btime collect(fp(10_000)) -@btime fourierpoints(10_000) - -n=10 -T=Float64 - convert(T,π)*(0:2:2n-2)/n - [convert(T,π)*(2k-2)/n for k=1:n] diff --git a/src/Domains/PiecewiseSegment.jl b/src/Domains/PiecewiseSegment.jl deleted file mode 100644 index 0dbb58f2..00000000 --- a/src/Domains/PiecewiseSegment.jl +++ /dev/null @@ -1,89 +0,0 @@ -export PiecewiseSegment - -struct PiecewiseSegment{T} <: Domain{T} - points::Vector{T} - PiecewiseSegment{T}(d::Vector{T}) where {T} = new{T}(d) -end -PiecewiseSegment(d::AbstractVector) = PiecewiseSegment{eltype(d)}(collect(d)) -PiecewiseSegment(d...) = PiecewiseSegment(collect(mapreduce(eltype,promote_type,d),d)) - -function PiecewiseSegment(pcsin::AbstractVector{IT}) where IT<:IntervalOrSegment - pcs=collect(pcsin) - p=∂(pop!(pcs)) - successful=true - while successful - successful=false - for k=1:length(pcs) - if leftendpoint(pcs[k]) == last(p) - push!(p,rightendpoint(pcs[k])) - deleteat!(pcs,k) - successful=true - break - end - end - end - @assert isempty(pcs) - PiecewiseSegment(p) -end - -==(a::PiecewiseSegment,b::PiecewiseSegment) = a.points==b.points - -indomain(x, d::PiecewiseSegment) = any(in.(x, components(d))) - - -canonicaldomain(d::PiecewiseSegment)=d -ncomponents(d::PiecewiseSegment)=length(d.points)-1 -component(d::PiecewiseSegment,j::Integer) = Segment(d.points[j],d.points[j+1]) -components(d::PiecewiseSegment{T}) where {T} = Segment{T}[component(d,k) for k=1:ncomponents(d)] - -for OP in (:arclength,:complexlength) - @eval $OP(d::PiecewiseSegment) = mapreduce($OP,+,components(d)) -end - -isperiodic(_) = false -isperiodic(d::PiecewiseSegment) = first(d.points) == last(d.points) - -reverseorientation(d::PiecewiseSegment) = PiecewiseSegment(reverse(d.points)) - -isambiguous(d::PiecewiseSegment) = isempty(d.points) -convert(::Type{PiecewiseSegment{T}},::AnyDomain) where {T<:Number} = PiecewiseSegment{T}([]) -convert(::Type{IT},::AnyDomain) where {IT<:PiecewiseSegment}=PiecewiseSegment(Float64[]) - - -function points(d::PiecewiseSegment,n) - k=div(n,ncomponents(d)) - r=n-ncomponents(d)*k - - float(eltype(d))[vcat([points(component(d,j),k+1) for j=1:r]...); - vcat([points(component(d,j),k) for j=r+1:ncomponents(d)]...)] -end - - - -rand(d::PiecewiseSegment) = rand(d[rand(1:ncomponents(d))]) -checkpoints(d::PiecewiseSegment{T}) where {T} = mapreduce(checkpoints,union,components(d)) - -leftendpoint(d::PiecewiseSegment) = first(d.points) -rightendpoint(d::PiecewiseSegment) = last(d.points) - - -# Comparison with UnionDomain -for OP in (:(isapprox),:(==)) - @eval begin - $OP(a::PiecewiseSegment,b::UnionDomain) = $OP(UnionDomain(components(a)),b) - $OP(b::UnionDomain,a::PiecewiseSegment) = $OP(UnionDomain(components(a)),b) - end -end - - -function union(S::PiecewiseSegment{<:Real}, D::IntervalOrSegment{<:Real}) - isempty(D) && return S - a,b = endpoints(D) - (a ∈ S || b ∈ S) && return PiecewiseSegment(sort!(union(S.points, a, b))) - UnionDomain(S, D) -end -union(D::IntervalOrSegment{<:Real}, S::PiecewiseSegment{<:Real}) = union(S,D) - -for OP in (:minimum, :maximum) - @eval $OP(d::PiecewiseSegment) = $OP(d.points) -end diff --git a/src/Domains/Point.jl b/src/Domains/Point.jl deleted file mode 100644 index bc49d7be..00000000 --- a/src/Domains/Point.jl +++ /dev/null @@ -1,39 +0,0 @@ -Point(::AnyDomain) = Point(NaN) -Point{T}(x::AnyDomain) where {T} = new(T(NaN)) - -convert(::Type{Point},::AnyDomain) = Point(NaN) -convert(::Type{Point{T}},::AnyDomain) where T = Point{T}(NaN) - - -isambiguous(d::Point) = isnan(d.x) - - - - -norm(p::Point) = norm(p.x) - -getindex(p::Point,k...) = p.x[k...] -first(p::Point) = p.x -last(p::Point) = p.x - -Base.isnan(d::Point) = false - - -issubset(a::Point,d::UnionDomain) = a.x in d - -intersect(a::Point,b::Point) = b.x in a ? b : EmptyDomain() -intersect(a::UnionDomain,b::Point) = b.x in a ? b : EmptyDomain() -intersect(a::Domain,b::Point) = b.x in a ? b : EmptyDomain() -intersect(b::Point,a::UnionDomain) = b.x in a ? b : EmptyDomain() -intersect(b::Point,a::Domain) = b.x in a ? b : EmptyDomain() - -setdiff(a::Point,b::Point) = a==b ? EmptyDomain() : a -reverseorientation(a::Point) = a - - -canonicaldomain(a::Point) = Point(0.) -tocanonical(a::Point,x) = x-a.x -fromcanonical(a::Point,x) = x+a.x - -points(a::Point,n) = eltype(a)[a.x] -checkpoints(a::Point) = eltype(a)[a.x] diff --git a/src/Domains/ProductDomain.jl b/src/Domains/ProductDomain.jl deleted file mode 100644 index c06a6e37..00000000 --- a/src/Domains/ProductDomain.jl +++ /dev/null @@ -1,48 +0,0 @@ - -canonicaldomain(d::ProductDomain) = ProductDomain(map(canonicaldomain,d.domains)...) - - -# product domains are their own canonical domain -for OP in (:fromcanonical,:tocanonical) - @eval begin - $OP(d::ProductDomain, x::Vec) = Vec(map($OP,d.domains,x)...) - $OP(d::ProductDomain, x::Vec{2}) = Vec($OP(first(d.domains), first(x)), $OP(last(d.domains), last(x))) - end -end - -nfactors(d::ProductDomain) = length(d.domains) -factors(d::ProductDomain) = d.domains -factor(d::ProductDomain,k::Integer) = d.domains[k] - -function pushappendpts!(ret, xx, pts) - if isempty(pts) - push!(ret,Vec(xx...)) - else - for x in pts[1] - pushappendpts!(ret,(xx...,x...),pts[2:end]) - end - end - ret -end - -function checkpoints(d::ProductDomain) - pts = checkpoints.(d.domains) - ret=Vector{Vec{sum(dimension.(d.domains)),float(promote_type(eltype.(eltype.(d.domains))...))}}(undef, 0) - - pushappendpts!(ret,(),pts) - ret -end - -function points(d::ProductDomain,n::Tuple) - @assert length(d.domains) == length(n) - pts=map(points,d.domains,n) - ret=Vector{Vec{length(d.domains),mapreduce(eltype,promote_type,d.domains)}}(undef, 0) - pushappendpts!(ret,Vec(x),pts) - ret -end - -reverseorientation(d::ProductDomain) = ProductDomain(map(reverseorientation, d.domains)) - -domainscompatible(a::ProductDomain,b::ProductDomain) = - length(a.domains)==length(b.domains) && - all(map(domainscompatible,a.domains,b.domains)) diff --git a/src/Domains/Segment.jl b/src/Domains/Segment.jl deleted file mode 100644 index 4ac4eebf..00000000 --- a/src/Domains/Segment.jl +++ /dev/null @@ -1,154 +0,0 @@ -export Segment - - -convert(::Type{ChebyshevInterval}, ::AnyDomain) = ChebyshevInterval() -convert(::Type{ChebyshevInterval{T}}, ::AnyDomain) where T = ChebyshevInterval{T}() - - -## Standard interval -# T Must be a Vector space -""" - Segment(a,b) - -represents a line segment from `a` to `b`. In the case where `a` and `b` -are real and `a < b`, then this is is equivalent to an `Interval(a,b)`. -""" -struct Segment{T} <: AbstractSegment{T} - a::T - b::T - Segment{T}(a,b) where {T} = new{T}(a,b) -end - -Segment(a::Complex{IT1}, b::Complex{IT2}) where {IT1<:Integer,IT2<:Integer} = - Segment(ComplexF64(a), ComplexF64(b)) #convenience method -Segment(a::Integer, b::Integer) = Segment(Float64(a),Float64(b)) #convenience method -Segment(a::Complex{IT}, b) where {IT<:Integer} = Segment(ComplexF64(a),b) #convenience method -Segment(a, b::Complex{IT}) where {IT<:Integer} = Segment(a,ComplexF64(b)) #convenience method -Segment(a, b) = Segment{promote_type(typeof(a),typeof(b))}(a,b) -Segment(a::Tuple, b::Tuple) = Segment(Vec(a...),Vec(b...)) - - -convert(::Type{Domain{T}}, d::Segment) where {T<:Number} = Segment{T}(leftendpoint(d),rightendpoint(d)) -convert(::Type{Domain{T}}, d::Segment) where {T<:SVector} = Segment{T}(leftendpoint(d),rightendpoint(d)) -convert(::Type{Segment{T}}, d::Segment) where {T<:Number} = Segment{T}(leftendpoint(d),rightendpoint(d)) -convert(::Type{Segment}, d::AbstractInterval) = Segment(leftendpoint(d), rightendpoint(d)) -convert(::Type{Segment{T}}, d::AbstractInterval) where T =convert(Segment{T}, convert(Segment, d)) - - - -Segment(d::AbstractInterval) = convert(Segment, d) - - - -AnySegment(::Type{T}) where {T} = Segment{T}(NaN,NaN) -AnySegment() = AnySegment(Float64) -isambiguous(d::IntervalOrSegmentDomain) = all(isnan(leftendpoint(d))) && all(isnan(rightendpoint(d))) -convert(::Type{Segment{T}},::AnyDomain) where {T<:Number} = AnySegment(T) -convert(::Type{Segment},::AnyDomain) = AnySegment() -convert(::Type{Interval}, d::Segment{<:Real}) = d.a < d.b ? d.a .. d.b : d.b .. d.a -convert(::Type{ClosedInterval}, ::AnyDomain) = NaN..NaN -convert(::Type{ClosedInterval{T}}, ::AnyDomain) where T = T(NaN)..T(NaN) -Interval(d::Segment) = convert(Interval, d) - - -## Information -@inline leftendpoint(d::Segment) = d.a -@inline rightendpoint(d::Segment) = d.b -@inline endpoints(d::Segment) = d.a, d.b - -@inline minimum(d::Segment) = min(leftendpoint(d),rightendpoint(d)) -@inline maximum(d::Segment) = max(leftendpoint(d),rightendpoint(d)) - -isempty(d::Segment) = isapprox(leftendpoint(d), rightendpoint(d); atol=200eps(eltype(d))) - -issubset(a::Segment,b::Segment) = leftendpoint(a)∈b && rightendpoint(a)∈b - - - -arclength(d::AbstractInterval) = width(d) -arclength(d::Segment) = norm(complexlength(d)) -complexlength(d::IntervalOrSegment) = rightendpoint(d)-leftendpoint(d) -mean(d::IntervalOrSegment) = (rightendpoint(d)+leftendpoint(d))/2 -angle(d::IntervalOrSegment) = angle(complexlength(d)) -sign(d::IntervalOrSegment) = sign(complexlength(d)) - -## Map interval -# The first definition is the more general - -mobius(S::Space,x...) = mobius(domain(S),x...) - -mobius(d::ChebyshevInterval{T},x) where {T<:Number} = x -fromcanonical(d::ChebyshevInterval{T},x) where {T<:Number} = x -fromcanonicalD(d::ChebyshevInterval{T},x) where {T<:Number} = one(x) -tocanonical(d::ChebyshevInterval{T},x) where {T<:Number} = x -tocanonicalD(d::ChebyshevInterval{T},x) where {T<:Number} = one(x) - -tocanonical(d::IntervalOrSegment{T},x) where {T} = 2norm(x-leftendpoint(d))/arclength(d)-1 -tocanonical(d::IntervalOrSegment{T},x::Number) where {T<:Complex} = 2norm(x-leftendpoint(d))/arclength(d)-1 -mobius(d::IntervalOrSegment,x) = (2x - leftendpoint(d) - rightendpoint(d))/complexlength(d) -tocanonical(d::IntervalOrSegment{T},x) where {T<:Real} = mobius(d,x) -tocanonicalD(d::IntervalOrSegment{T},x) where {T<:Real} = 2/complexlength(d) -fromcanonical(d::IntervalOrSegment{T},x) where {T<:Number} = mean(d) + complexlength(d)x/2 -fromcanonical(d::IntervalOrSegment{T},x) where {T<:Vec} = mean(d) + complexlength(d)x/2 -fromcanonicalD(d::IntervalOrSegment,x) = complexlength(d) / 2 - - - -==(d::Segment, m::Segment) = (isambiguous(d) && isambiguous(m)) || (leftendpoint(d) == leftendpoint(m) && rightendpoint(d) == rightendpoint(m)) -function isapprox(d::Segment, m::Segment) - tol=10E-12 - norm(leftendpoint(d)-leftendpoint(m))D(AnyDomain()),DD.parameters)) -convert(::Type{IT},::AnyDomain) where {IT<:UnionDomain} = UnionDomain(tuple()) - - -#support tuple set -components(d::AbstractVector) = d -components(d::UnionDomain) = elements(d) -component(d::UnionDomain,k) = components(d)[k] -ncomponents(d::UnionDomain) = length(elements(d)) - -pieces(d::UnionDomain) = elements(d) -piece(d::UnionDomain,k) = pieces(d)[k] -npieces(d::UnionDomain) = length(elements(d)) - - -union(::AnyDomain, d::UnionDomain) = d -union(d::UnionDomain, ::AnyDomain) = d - -arclength(d::UnionDomain) = mapreduce(arclength,+,d.domains) - - -reverseorientation(d::UnionDomain) = UnionDomain(reverse(map(reverseorientation,d.domains))) - -leftendpoint(d::UnionDomain) = leftendpoint(first(elements(d))) -rightendpoint(d::UnionDomain) = rightendpoint(last(elements(d))) - -# determine the number of points per piece -function components_npoints(d, n::Int) - N = ncomponents(d) - k = n ÷ N - r = n - N*k - [fill(k+1, r); fill(k, N-r)] -end - - -pieces_npoints(d, n::Int) = components_npoints(d, n) - -points(d::UnionDomain,n) = vcat(points.(pieces(d), pieces_npoints(d,n))...) - -rand(d::UnionDomain) = rand(component(d,rand(1:length(d)))) -checkpoints(d::UnionDomain) = mapreduce(checkpoints,union,d.domains) - - -## to move over -function Base.merge(d1::UnionDomain,m::IntervalOrSegment) - T = float(promote_type(eltype(d1), eltype(m))) - ret=d1.domains - - for k=length(ret):-1:1 - it=intersect(ret[k],m) - if arclength(it) ≠ 0 - sa=setdiff(ret[k],it) - m=setdiff(m,it) - if arclength(sa) ≤ eps(T) - ret = [ret[1:k-1]...; it; ret[k+1:end]...] - else - ret = [ret[1:k-1]...; sa; it; ret[k+1:end]...] - end - if arclength(m) == 0 - break - end - end - end - if !isempty(m) - ret = [ret...; m] - end - - UnionDomain(sort!(ret,by=leftendpoint)) -end - -function merge(d1::UnionDomain,d2::UnionDomain) - ret=d1 - for m in d2.domains - ret=merge(ret,m) - end - ret -end diff --git a/src/Domains/multivariate.jl b/src/Domains/multivariate.jl deleted file mode 100644 index 163c7bb1..00000000 --- a/src/Domains/multivariate.jl +++ /dev/null @@ -1,29 +0,0 @@ -include("ProductDomain.jl") - - -## boundary - -boundary(d::ProductDomain{Tuple{A,B}}) where {A<:IntervalOrSegment,B<:IntervalOrSegment} = - PiecewiseSegment([Vec(leftendpoint(factor(d,1)),leftendpoint(factor(d,2))), - Vec(rightendpoint(factor(d,1)),leftendpoint(factor(d,2))), - Vec(rightendpoint(factor(d,1)),rightendpoint(factor(d,2))), - Vec(leftendpoint(factor(d,1)),rightendpoint(factor(d,2))), - Vec(leftendpoint(factor(d,1)),leftendpoint(factor(d,2)))]) - - -## Union -function Base.join(p1::AbstractVector{IT},p2::AbstractVector{IT}) where IT<:IntervalOrSegment - for k=length(p1):-1:1,j=length(p2):-1:1 - if p1[k]==reverse(p2[j]) - deleteat!(p1,k) - deleteat!(p2,j) - break - end - end - [p1;p2] -end - -function boundary(d::UnionDomain{Tuple{PD1,PD2}}) where {PD1<:ProductDomain,PD2<:ProductDomain} - bnd=map(d->components(∂(d)),d.domains) - PiecewiseSegment(reduce(join,bnd)) -end diff --git a/src/Fun.jl b/src/Fun.jl index 0fc0b4e6..3fd389c6 100644 --- a/src/Fun.jl +++ b/src/Fun.jl @@ -2,97 +2,68 @@ export Fun, evaluate, values, points, extrapolate, setdomain export coefficients, ncoefficients, coefficient export integrate, differentiate, domain, space, linesum, linenorm -include("Domain.jl") -include("Space.jl") - ## Constructors mutable struct Fun{S,T,VT} <: Function space::S coefficients::VT - function Fun{S,T,VT}(sp::S,coeff::VT) where {S,T,VT} - @assert length(coeff) ≤ dimension(sp) + function Fun{S,T,VT}(sp::S, coeff::VT) where {S,T,VT} + axes(sp,2) == axes(coeff,1) || throw(DimensionMismatch("")) new{S,T,VT}(sp,coeff) end end const VFun{S,T} = Fun{S,T,Vector{T}} - -Fun(sp::Space,coeff::AbstractVector) = Fun{typeof(sp),eltype(coeff),typeof(coeff)}(sp,coeff) +Fun(sp::Space, coeff::AbstractVector) = Fun{typeof(sp),eltype(coeff),typeof(coeff)}(sp, coeff) Fun() = Fun(identity) -Fun(d::Domain) = Fun(identity,d) -Fun(d::Space) = Fun(identity,d) +Fun(d::Domain) = Fun(identity, d) +Fun(d::Space) = Fun(identity, d) + +Fun(v::AbstractQuasiVector) = Fun(arguments(v)...) +Fun(f::Function, S::Space) = Fun(S, S \ f.(axes(S,1))) -function Fun(sp::Space,v::AbstractVector{Any}) +function Fun(sp::Space, v::AbstractVector{Any}) if isempty(v) - Fun(sp,Float64[]) + Fun(sp, Float64[]) elseif all(x->isa(x,Number),v) - Fun(sp,Vector{mapreduce(typeof,promote_type,v)}(v)) + Fun(sp, Vector{mapreduce(typeof,promote_type,v)}(v)) else error("Cannot construct Fun with coefficients $v and space $sp") end end -hasnumargs(f::Fun,k) = k == 1 || domaindimension(f) == k # all funs take a single argument as a Vec +hasnumargs(f::Fun, k) = k == 1 || domaindimension(f) == k # all funs take a single argument as a Vec ##Coefficient routines #TODO: domainscompatible? -function coefficients(f::Fun,msp::Space) - #zero can always be converted - if ncoefficients(f)==1 && f.coefficients[1]==0 - f.coefficients - else - coefficients(f.coefficients,space(f),msp) - end -end -coefficients(f::Fun,::Type{T}) where {T<:Space} = coefficients(f,T(domain(f))) -coefficients(f::Fun) = f.coefficients -coefficients(c::Number,sp::Space) = Fun(c,sp).coefficients - -function coefficient(f::Fun,k::Integer) - if k > dimension(space(f)) || k < 1 - throw(BoundsError()) - elseif k > ncoefficients(f) - zero(cfstype(f)) - else - f.coefficients[k] - end -end - -function coefficient(f::Fun,kr::AbstractRange) - b = maximum(kr) - if b ≤ ncoefficients(f) - f.coefficients[kr] - else - [coefficient(f,k) for k in kr] - end -end +coefficients(f::Fun) = f.coefficients -coefficient(f::Fun,K::Block) = coefficient(f,blockrange(space(f),K.n[1])) -coefficient(f::Fun,::Colon) = coefficient(f,1:dimension(space(f))) +vec(f::Fun) = f.space * coefficients(f) +coefficients(f::Fun, msp::Space) = msp \ vec(f) +coefficients(c::Number,sp::Space) = coefficients(Fun(c,sp)) ##Convert routines -convert(::Type{Fun{S,T,VT}},f::Fun{S}) where {T,S,VT} = +convert(::Type{Fun{S,T,VT}}, f::Fun{S}) where {T,S,VT} = Fun(f.space,convert(VT,f.coefficients)) -convert(::Type{Fun{S,T,VT}},f::Fun) where {T,S,VT} = +convert(::Type{Fun{S,T,VT}}, f::Fun) where {T,S,VT} = Fun(Fun(f.space,convert(VT,f.coefficients)),convert(S,space(f))) -convert(::Type{Fun{S,T}},f::Fun{S}) where {T,S} = +convert(::Type{Fun{S,T}}, f::Fun{S}) where {T,S} = Fun(f.space,convert(AbstractVector{T},f.coefficients)) -convert(::Type{VFun{S,T}},x::Number) where {T,S} = +convert(::Type{VFun{S,T}}, x::Number) where {T,S} = x==0 ? zeros(T,S(AnyDomain())) : x*ones(T,S(AnyDomain())) -convert(::Type{Fun{S}},x::Number) where {S} = +convert(::Type{Fun{S}}, x::Number) where {S} = x==0 ? zeros(S(AnyDomain())) : x*ones(S(AnyDomain())) -convert(::Type{IF},x::Number) where {IF<:Fun} = convert(IF,Fun(x)) +convert(::Type{IF}, x::Number) where {IF<:Fun} = convert(IF,Fun(x)) Fun{S,T,VT}(f::Fun) where {S,T,VT} = convert(Fun{S,T,VT}, f) Fun{S,T}(f::Fun) where {S,T} = convert(Fun{S,T}, f) @@ -104,7 +75,7 @@ Base.promote_rule(::Type{Fun{S,T,VT1}},::Type{Fun{S,V,VT2}}) where {T,V,S,VT1,VT # TODO: Never assume! -Base.promote_op(::typeof(*),::Type{F1},::Type{F2}) where {F1<:Fun,F2<:Fun} = +Base.promote_op(::typeof(*), ::Type{F1}, ::Type{F2}) where {F1<:Fun,F2<:Fun} = promote_type(F1,F2) # assume multiplication is defined between same types # we know multiplication by numbers preserves types @@ -143,14 +114,15 @@ coefficients(f::AbstractArray) = f #supports broadcasting and scalar iterator -const ScalarFun = Fun{S} where S<:Space{D,R} where {D,R<:Number} -const ArrayFun = Fun{S} where {S<:Space{D,R}} where {D,R<:AbstractArray} -const MatrixFun = Fun{S} where {S<:Space{D,R}} where {D,R<:AbstractMatrix} -const VectorFun = Fun{S} where {S<:Space{D,R}} where {D,R<:AbstractVector} - -size(f::Fun,k...) = size(space(f),k...) -length(f::Fun) = length(space(f)) +const ScalarSpace = Space{<:Number} +const RealSpace = Space{<:Real} +const ScalarFun = Fun{<:ScalarSpace} +const ArrayFun = Fun{<:Space{<:AbstractArray}} +const MatrixFun = Fun{<:Space{<:AbstractMatrix}} +const VectorFun = Fun{<:Space{<:AbstractVector}} +size(f::Fun, k...) = size(first(axes(space(f),1)), k...) +length(f::Fun) = length(first(axes(space(f),1))) getindex(f::ScalarFun, ::CartesianIndex{0}) = f getindex(f::ScalarFun, k::Integer) = k == 1 ? f : throw(BoundsError()) @@ -163,12 +135,7 @@ iterate(A::ArrayFun, i=1) = (@_inline_meta; (i % UInt) - 1 < length(A) ? (@inbou in(x::ScalarFun, y::ScalarFun) = x == y - - - - -setspace(v::AbstractVector,s::Space) = Fun(s,v) -setspace(f::Fun,s::Space) = Fun(s,f.coefficients) +setspace(f::Fun, s::Space) = Fun(s, coefficients(f)) ## domain @@ -177,61 +144,33 @@ setspace(f::Fun,s::Space) = Fun(s,f.coefficients) ## General routines -domain(f::Fun) = domain(f.space) -domain(v::AbstractMatrix{T}) where {T<:Fun} = map(domain,v) +domain(f::Fun) = axes(f.space, 1) +domain(v::AbstractMatrix{T}) where {T<:Fun} = map(domain, v) domaindimension(f::Fun) = domaindimension(f.space) setdomain(f::Fun,d::Domain) = Fun(setdomain(space(f),d),f.coefficients) -for op in (:tocanonical,:tocanonicalD,:fromcanonical,:fromcanonicalD,:invfromcanonicalD) - @eval $op(f::Fun,x...) = $op(space(f),x...) -end - -for op in (:tocanonical,:tocanonicalD) - @eval $op(d::Domain) = $op(d,Fun(identity,d)) -end -for op in (:fromcanonical,:fromcanonicalD,:invfromcanonicalD) - @eval $op(d::Domain) = $op(d,Fun(identity,canonicaldomain(d))) -end - - space(f::Fun) = f.space -spacescompatible(f::Fun,g::Fun) = spacescompatible(space(f),space(g)) -pointscompatible(f::Fun,g::Fun) = pointscompatible(space(f),space(g)) +spacescompatible(f::Fun, g::Fun) = spacescompatible(space(f),space(g)) +pointscompatible(f::Fun, g::Fun) = pointscompatible(space(f),space(g)) canonicalspace(f::Fun) = canonicalspace(space(f)) canonicaldomain(f::Fun) = canonicaldomain(space(f)) ##Evaluation -function evaluate(f::AbstractVector,S::Space,x...) - csp=canonicalspace(S) - if spacescompatible(csp,S) - error("Override evaluate for " * string(typeof(csp))) - else - evaluate(coefficients(f,S,csp),csp,x...) - end -end - -evaluate(f::Fun,x) = evaluate(f.coefficients,f.space,x) -evaluate(f::Fun,x,y,z...) = evaluate(f.coefficients,f.space,Vec(x,y,z...)) - - -(f::Fun)(x...) = evaluate(f,x...) +evaluate(f::Fun, x) = vec(f)[x] +evaluate(f::Fun, x, y, z...) = evaluate(f, Vec(x,y,z...)) +(f::Fun)(x...) = evaluate(f, x...) dynamic(f::Fun) = f # Fun's are already dynamic in that they compile by type -for (op,dop) in ((:first,:leftendpoint),(:last,:rightendpoint)) - @eval $op(f::Fun{S,T}) where {S,T} = f($dop(domain(f))) -end - - ## Extrapolation # Default extrapolation is evaluation. Override this function for extrapolation enabled spaces. -extrapolate(f::AbstractVector,S::Space,x...) = evaluate(f,S,x...) +extrapolate(f::AbstractVector, S::Space, x...) = evaluate(f, S, x...) # Do not override these extrapolate(f::Fun,x) = extrapolate(f.coefficients,f.space,x) @@ -243,38 +182,19 @@ extrapolate(f::Fun,x,y,z...) = extrapolate(f.coefficients,f.space,Vec(x,y,z...)) values(f::Fun,dat...) = itransform(f.space,f.coefficients,dat...) points(f::Fun) = points(f.space,ncoefficients(f)) -ncoefficients(f::Fun) = length(f.coefficients) -blocksize(f::Fun) = (block(space(f),ncoefficients(f)).n[1],) - -function stride(f::Fun) - # Check only for stride 2 at the moment - # as higher stride is very rare anyways - M=maximum(abs,f.coefficients) - for k=2:2:ncoefficients(f) - if abs(f.coefficients[k])>40*M*eps() - return 1 - end - end - - 2 -end - - +ncoefficients(f::Fun) = nzeros(f.coefficients) ## Manipulate length -pad!(f::Fun,n::Integer) = (pad!(f.coefficients,n);f) -pad(f::Fun,n::Integer) = Fun(f.space,pad(f.coefficients,n)) - -function chop!(sp::UnivariateSpace,cfs,tol::Real) - n=standardchoplength(cfs,tol) +function chop!(sp::ScalarSpace, cfs, tol::Real) + n = standardchoplength(cfs, tol) resize!(cfs,n) cfs end -chop!(sp::Space,cfs,tol::Real) = chop!(cfs,maximum(abs,cfs)*tol) -chop!(sp::Space,cfs) = chop!(sp,cfs,10eps()) +chop!(sp::Space, cfs, tol::Real) = chop!(cfs,maximum(abs,cfs)*tol) +chop!(sp::Space, cfs) = chop!(sp,cfs,10eps()) function chop!(f::Fun,tol...) chop!(space(f),f.coefficients,tol...) @@ -290,18 +210,15 @@ copy(f::Fun) = Fun(space(f),copy(f.coefficients)) for op in (:+,:-) @eval begin - function $op(f::Fun,g::Fun) - if spacescompatible(f,g) - n = max(ncoefficients(f),ncoefficients(g)) - f2 = pad(f,n); g2 = pad(g,n) - - Fun(isambiguous(domain(f)) ? g.space : f.space,($op)(f2.coefficients,g2.coefficients)) + function $op(f::Fun, g::Fun) + if space(f) == space(g) + Fun(space(f), ($op)(coefficients(f), coefficients(g))) else - m=union(f.space,g.space) + m = union(f.space,g.space) if isa(m,NoSpace) error("Cannot "*string($op)*" because no space is the union of "*string(typeof(f.space))*" and "*string(typeof(g.space))) end - $op(Fun(f,m),Fun(g,m)) # convert to same space + $op(Fun(f,m), Fun(g,m)) # convert to same space end end $op(f::Fun{S,T},c::T) where {S,T<:Number} = c==0 ? f : $op(f,Fun(c)) @@ -313,7 +230,7 @@ end # equivalent to Y+=a*X -axpy!(a,X::Fun,Y::Fun)=axpy!(a,coefficients(X,space(Y)),Y) +axpy!(a, X::Fun, Y::Fun)=axpy!(a,coefficients(X,space(Y)),Y) function axpy!(a,xcfs::AbstractVector,Y::Fun) if a!=0 n=ncoefficients(Y); m=length(xcfs) @@ -338,7 +255,7 @@ end --(f::Fun) = Fun(f.space,-f.coefficients) +-(f::Fun) = Fun(f.space, -f.coefficients) -(c::Number,f::Fun) = -(f-c) @@ -372,7 +289,6 @@ inv(f::Fun) = 1/f # Integrals over two Funs, which are fast with the orthogonal weight. -export bilinearform, linebilinearform, innerproduct, lineinnerproduct # Having fallbacks allow for the fast implementations. @@ -405,7 +321,7 @@ for (OP,SUM) in ((:(norm),:(sum)),(:linenorm,:linesum)) @eval begin $OP(f::Fun) = $OP(f,2) - function $OP(f::Fun{S},p::Real) where S<:Space{D,R} where {D,R<:Number} + function $OP(f::ScalarFun, p::Real) if p < 1 return error("p should be 1 ≤ p ≤ ∞") elseif 1 ≤ p < Inf @@ -415,7 +331,7 @@ for (OP,SUM) in ((:(norm),:(sum)),(:linenorm,:linesum)) end end - function $OP(f::Fun{S},p::Int) where S<:Space{D,R} where {D,R<:Number} + function $OP(f::ScalarFun,p::Int) if 1 ≤ p < Inf return iseven(p) ? abs($SUM(abs2(f)^(p÷2)))^(1/p) : abs($SUM(abs2(f)^(p/2)))^(1/p) else @@ -458,6 +374,11 @@ function differentiate(f::Fun,k::Integer) end # use conj(transpose(f)) for ArraySpace +function differentiate(f) + v = vec(f) + D = Derivative(axes(v,1)) + Fun(D*v) +end adjoint(f::Fun) = differentiate(f) @@ -601,5 +522,3 @@ function copyto!(dest::Fun, bc::Broadcasted{FunStyle}) dest.coefficients[:] = cfs dest end - -include("constructors.jl") diff --git a/src/LinearAlgebra/AlmostBandedMatrix.jl b/src/LinearAlgebra/AlmostBandedMatrix.jl deleted file mode 100644 index 914b1349..00000000 --- a/src/LinearAlgebra/AlmostBandedMatrix.jl +++ /dev/null @@ -1,58 +0,0 @@ -## AlmostBandedMatrix - - - -struct AlmostBandedMatrix{T} <: AbstractMatrix{T} - bands::BandedMatrix{T} - fill::LowRankMatrix{T} - function AlmostBandedMatrix{T}(bands::BandedMatrix{T}, fill::LowRankMatrix{T}) where T - if size(bands) ≠ size(fill) - error("Data and fill must be compatible size") - end - new{T}(bands,fill) - end -end - -AlmostBandedMatrix(bands::BandedMatrix, fill::LowRankMatrix) = - AlmostBandedMatrix{promote_type(eltype(bands),eltype(fill))}(bands,fill) - -AlmostBandedMatrix{T}(nm::NTuple{2,Integer}, lu::NTuple{2,Integer}, r::Integer) where {T} = - AlmostBandedMatrix(BandedMatrix{T}(nm,lu), LowRankMatrix{T}(nm,r)) - - -AlmostBandedMatrix{T}(Z::Zeros, lu::NTuple{2,Integer}, r::Integer) where {T} = - AlmostBandedMatrix(BandedMatrix{T}(Z, lu), LowRankMatrix{T}(Z, r)) - -AlmostBandedMatrix(Z::AbstractMatrix, lu::NTuple{2,Integer}, r::Integer) = - AlmostBandedMatrix{eltype(Z)}(Z, lu, r) - -for MAT in (:AlmostBandedMatrix, :AbstractMatrix, :AbstractArray) - @eval convert(::Type{$MAT{T}}, A::AlmostBandedMatrix) where {T} = - AlmostBandedMatrix(AbstractMatrix{T}(A.bands),AbstractMatrix{T}(A.fill)) -end - - -size(A::AlmostBandedMatrix) = size(A.bands) -Base.IndexStyle(::Type{ABM}) where {ABM<:AlmostBandedMatrix} = - IndexCartesian() - - -function getindex(B::AlmostBandedMatrix,k::Integer,j::Integer) - if j > k + bandwidth(B.bands,2) - B.fill[k,j] - else - B.bands[k,j] - end -end - -# can only change the bands, not the fill -function setindex!(B::AlmostBandedMatrix,v,k::Integer,j::Integer) - B.bands[k,j] = v -end - - -function pad!(B::AlmostBandedMatrix,n::Integer,m::Integer) - pad!(B.bands,n,m) - pad!(B.fill,n,m) - B -end diff --git a/src/LinearAlgebra/LinearAlgebra.jl b/src/LinearAlgebra/LinearAlgebra.jl deleted file mode 100644 index 9a65968c..00000000 --- a/src/LinearAlgebra/LinearAlgebra.jl +++ /dev/null @@ -1,17 +0,0 @@ -include("helper.jl") - -include("standardchop.jl") - -include("blas.jl") -include("lyap.jl") - -include("clenshaw.jl") - -include("hesseneigs.jl") - -include("LowRankMatrix.jl") -include("AlmostBandedMatrix.jl") -include("RaggedMatrix.jl") - - -include("rowvector.jl") diff --git a/src/LinearAlgebra/LowRankMatrix.jl b/src/LinearAlgebra/LowRankMatrix.jl deleted file mode 100644 index 6e5cc040..00000000 --- a/src/LinearAlgebra/LowRankMatrix.jl +++ /dev/null @@ -1,45 +0,0 @@ - - -## -# Represent an m x n rank-r matrix -# A = U V^T -## - - -balance!(U::Matrix{T},V::Matrix{T},m::Int,n::Int,r::Int) where {T<:Union{Integer,Rational}} = U,V -function balance!(U::Matrix{T},V::Matrix{T},m::Int,n::Int,r::Int) where T - for k=1:r - uk = zero(T) - for i=1:m - @inbounds uk += abs2(U[i,k]) - end - vk = zero(T) - for j=1:n - @inbounds vk += abs2(V[j,k]) - end - uk,vk = sqrt(uk),sqrt(vk) - σk = sqrt(uk*vk) - if abs2(uk) ≥ eps(T)^2 && abs2(vk) ≥ eps(T)^2 - uk,vk = σk/uk,σk/vk - for i=1:m - @inbounds U[i,k] *= uk - end - for j=1:n - @inbounds V[j,k] *= vk - end - end - end - U,V -end - -# constructors - -function pad!(L::LowRankMatrix,n::Integer,::Colon) - L.U=pad(L.U,n,:) - L -end -function pad!(L::LowRankMatrix,::Colon,m::Integer) - L.V=pad(L.V,m,:) - L -end -pad!(L::LowRankMatrix,n::Integer,m::Integer) = pad!(pad!(L,n,:),:,m) diff --git a/src/LinearAlgebra/RaggedMatrix.jl b/src/LinearAlgebra/RaggedMatrix.jl deleted file mode 100644 index 65fea5d3..00000000 --- a/src/LinearAlgebra/RaggedMatrix.jl +++ /dev/null @@ -1,248 +0,0 @@ -# FiniteRange gives the nonzero entries in a row/column -struct FiniteRange end - -getindex(A::AbstractMatrix,::Type{FiniteRange},j::Integer) = A[1:colstop(A,j),j] -getindex(A::AbstractMatrix,k::Integer,::Type{FiniteRange}) = A[k,1:rowstop(A,k)] - -const ⤓ = FiniteRange - -mutable struct RaggedMatrix{T} <: AbstractMatrix{T} - data::Vector{T} # a Vector of non-zero entries - cols::Vector{Int} # a Vector specifying the first index of each column - m::Int #Number of rows - function RaggedMatrix{T}(data::Vector{T}, cols::Vector{Int}, m::Int) where T - # make sure the cols are monitonically increasing - @assert 1==cols[1] - for j=1:length(cols)-1 - @assert cols[j] ≤ cols[j+1] - end - @assert cols[end] == length(data)+1 - - # make sure we have less entries than the size of the matrix - @assert length(data) ≤ m*(length(cols)-1) - - new{T}(data,cols,m) - end -end - -RaggedMatrix(dat::Vector,cols::Vector{Int},m::Int) = - RaggedMatrix{eltype(dat)}(dat,cols,m) - -RaggedMatrix{T}(::UndefInitializer, m::Int, colns::AbstractVector{Int}) where {T} = - RaggedMatrix(Vector{T}(undef, sum(colns)),Int[1;1 .+ cumsum(colns)],m) - - -Base.size(A::RaggedMatrix) = (A.m,length(A.cols)-1) - -colstart(A::RaggedMatrix,j::Integer) = 1 -colstop(A::RaggedMatrix,j::Integer) = min(A.cols[j+1]-A.cols[j],size(A,1)) - -Base.IndexStyle(::Type{RM}) where {RM<:RaggedMatrix} = IndexCartesian() - -function getindex(A::RaggedMatrix,k::Int,j::Int) - if k>size(A,1) || k < 1 || j>size(A,2) || j < 1 - throw(BoundsError(A,(k,j))) - end - - if A.cols[j]+k-1 < A.cols[j+1] - A.data[A.cols[j]+k-1] - else - zero(eltype(A)) - end -end - -function Base.setindex!(A::RaggedMatrix,v,k::Int,j::Int) - if k>size(A,1) || k < 1 || j>size(A,2) || j < 1 - throw(BoundsError(A,(k,j))) - end - - if A.cols[j]+k-1 < A.cols[j+1] - A.data[A.cols[j]+k-1]=v - elseif v ≠ 0 - throw(BoundsError(A,(k,j))) - end - v -end - -convert(::Type{RaggedMatrix{T}}, R::RaggedMatrix{T}) where T = R - -convert(::Type{RaggedMatrix{T}}, R::RaggedMatrix) where T = - RaggedMatrix{T}(Vector{T}(R.data), R.cols, R.m) - - -function convert(::Type{Matrix{T}}, A::RaggedMatrix) where T - ret = zeros(T,size(A,1),size(A,2)) - for j=1:size(A,2) - ret[1:colstop(A,j),j] = view(A,1:colstop(A,j),j) - end - ret -end - -convert(::Type{Matrix}, A::RaggedMatrix) = Matrix{eltype(A)}(A) - -function convert(::Type{RaggedMatrix{T}}, B::BandedMatrix) where T - l = bandwidth(B,1) - ret = RaggedMatrix(Zeros{T}(size(B)), Int[colstop(B,j) for j=1:size(B,2)]) - for j=1:size(B,2),k=colrange(B,j) - ret[k,j] = B[k,j] - end - ret -end - -convert(::Type{RaggedMatrix}, B::BandedMatrix) = RaggedMatrix{eltype(B)}(B) - -function convert(::Type{RaggedMatrix{T}}, B::AbstractMatrix) where T - ret = RaggedMatrix(Zeros{T}(size(B)), Int[colstop(B,j) for j=1:size(B,2)]) - for j=1:size(B,2), k=colrange(B,j) - ret[k,j] = B[k,j] - end - ret -end - -convert(::Type{RaggedMatrix}, B::AbstractMatrix) = RaggedMatrix{eltype(B)}(B) - -RaggedMatrix(B::AbstractMatrix) = convert(RaggedMatrix, B) -RaggedMatrix{T}(B::AbstractMatrix) where T = convert(RaggedMatrix{T}, B) - -Base.similar(B::RaggedMatrix,::Type{T}) where {T} = RaggedMatrix(Vector{T}(length(B.data)),copy(B.cols),B.m) - -for (op,bop) in ((:(Base.rand), :rrand),) - @eval begin - $bop(::Type{T}, m::Int, colns::AbstractVector{Int}) where {T} = - RaggedMatrix($op(T,sum(colns)),[1; (1 .+ cumsum(colns))],m) - $bop(m::Int, colns::AbstractVector{Int}) = $bop(Float64, m, colns) - end -end - -function RaggedMatrix{T}(Z::Zeros, colns::AbstractVector{Int}) where {T} - if size(Z,2) ≠ length(colns) - throw(DimensionMismatch()) - end - RaggedMatrix(zeros(T,sum(colns)), [1; (1 .+cumsum(colns))], size(Z,1)) -end - -function RaggedMatrix{T}(A::AbstractMatrix, colns::AbstractVector{Int}) where T - ret = RaggedMatrix{T}(undef, size(A,1), colns) - @inbounds for j = 1:length(colns), k = 1:colns[j] - ret[k,j] = A[k,j] - end - ret -end - -RaggedMatrix(A::AbstractMatrix, colns::AbstractVector{Int}) = RaggedMatrix{eltype(A)}(A, colns) - - -## BLAS - -function mul!(y::Vector, A::RaggedMatrix, b::Vector) - m=size(A,2) - - if m ≠ length(b) || size(A,1) ≠ length(y) - throw(BoundsError()) - end - T=eltype(y) - fill!(y,zero(T)) - for j=1:m - kr=A.cols[j]:A.cols[j+1]-1 - BLAS.axpy!(b[j],view(A.data,kr),view(y,1:length(kr))) - end - y -end - - -function BLAS.axpy!(a, X::RaggedMatrix, Y::RaggedMatrix) - if size(X) ≠ size(Y) - throw(BoundsError()) - end - - if X.cols == Y.cols - BLAS.axpy!(a,X.data,Y.data) - else - for j = 1:size(X,2) - Xn = colstop(X,j) - Yn = colstop(Y,j) - if Xn > Yn # check zeros otherwise - for k = Yn+1:Xn - @assert iszero(X[k,j]) - end - end - cs = min(Xn,Yn) - BLAS.axpy!(a,view(X.data,X.cols[j]:X.cols[j]+cs-1), - view(Y.data,Y.cols[j]:Y.cols[j]+cs-1)) - end - end - Y -end - -colstop(X::SubArray{T,2,RaggedMatrix{T},Tuple{UnitRange{Int},UnitRange{Int}}}, - j::Integer) where {T} = min(colstop(parent(X),j + first(parentindices(X)[2])-1) - - first(parentindices(X)[1]) + 1, - size(X,1)) - -function BLAS.axpy!(a,X::RaggedMatrix, - Y::SubArray{T,2,RaggedMatrix{T},Tuple{UnitRange{Int},UnitRange{Int}}}) where T - if size(X) ≠ size(Y) - throw(BoundsError()) - end - - P = parent(Y) - ksh = first(parentindices(Y)[1]) - 1 # how much to shift - jsh = first(parentindices(Y)[2]) - 1 # how much to shift - - for j=1:size(X,2) - cx=colstop(X,j) - cy=colstop(Y,j) - if cx > cy - for k=cy+1:cx - if X[k,j] ≠ 0 - throw(BoundsError("Trying to add a non-zero to a zero.")) - end - end - kr = X.cols[j]:X.cols[j]+cy-1 - else - kr = X.cols[j]:X.cols[j+1]-1 - end - - - BLAS.axpy!(a,view(X.data,kr), - view(P.data,(P.cols[j + jsh] + ksh-1) .+ (1:length(kr)))) - end - - Y -end - - -function *(A::RaggedMatrix,B::RaggedMatrix) - cols = zeros(Int,size(B,2)) - T = promote_type(eltype(A),eltype(B)) - for j=1:size(B,2),k=1:colstop(B,j) - cols[j] = max(cols[j],colstop(A,k)) - end - - unsafe_mul!(RaggedMatrix{T}(undef, size(A,1), cols), A, B) -end - -function unsafe_mul!(Y::RaggedMatrix,A::RaggedMatrix,B::RaggedMatrix) - fill!(Y.data,0) - - for j=1:size(B,2),k=1:colstop(B,j) - BLAS.axpy!(B[k,j],view(A,1:colstop(A,k),k),view(Y.data,Y.cols[j] .- 1 .+ (1:colstop(A,k)))) - end - - Y -end - -function mul!(Y::RaggedMatrix,A::RaggedMatrix,B::RaggedMatrix) - for j=1:size(B,2) - col = 0 - for k=1:colstop(B,j) - col = max(col,colstop(A,k)) - end - - if col > colstop(Y,j) - throw(BoundsError()) - end - end - - unsafe_mul!(Y,A,B) -end diff --git a/src/LinearAlgebra/blas.jl b/src/LinearAlgebra/blas.jl deleted file mode 100644 index acdcdf28..00000000 --- a/src/LinearAlgebra/blas.jl +++ /dev/null @@ -1,102 +0,0 @@ -import LinearAlgebra.BLAS: @blasfunc, libblas -import LinearAlgebra.LAPACK: liblapack -# Level 2 -## mv -### gemv - -gemv!(x...) = BLAS.gemv!(x...) - -for (fname, elty) in ((:dgemv_,:Float64), - (:sgemv_,:Float32), - (:zgemv_,:ComplexF64), - (:cgemv_,:ComplexF32)) - @eval begin - #SUBROUTINE DGEMV(TRANS,M,N,ALPHA,A,LDA,X,INCX,BETA,Y,INCY) - #* .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER INCX,INCY,LDA,M,N - # CHARACTER TRANS - #* .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),X(*),Y(*) - function gemv!(trans::Char, m::Int, n::Int, alpha::($elty), A::Ptr{$elty}, stA::Int, - X::Ptr{$elty}, incX::Int, beta::($elty), Y::Ptr{$elty}, incY::Int) - ccall((@blasfunc($fname), libblas), Nothing, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ref{$elty}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ref{$elty}, Ptr{$elty}, Ref{BlasInt}), - trans, m, n, alpha, - A, stA, X, incX, - beta, Y, incY) - Y - end - - function gemv!(trans::Char, alpha::($elty), A, X, beta::($elty), Y) - m,n = size(A,1),size(A,2) - if trans == 'N' && (length(X) != n || length(Y) != m) - throw(DimensionMismatch("A has dimensions $(size(A)), X has length $(length(X)) and Y has length $(length(Y))")) - elseif trans == 'C' && (length(X) != m || length(Y) != n) - throw(DimensionMismatch("A' has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) - elseif trans == 'T' && (length(X) != m || length(Y) != n) - throw(DimensionMismatch("transpose(A) has dimensions $n, $m, X has length $(length(X)) and Y has length $(length(Y))")) - end - gemv!(trans, m, n, alpha, pointer(A), max(1,stride(A,2)), - pointer(X), stride(X,1), beta, pointer(Y), stride(Y,1)) - Y - end - end -end - -## Level 3 - - -# We add some missing Ptr variants - -gemm!(x...) = BLAS.gemm!(x...) - -for (gemm, elty) in - ((:dgemm_,:Float64), - (:sgemm_,:Float32), - (:zgemm_,:ComplexF64), - (:cgemm_,:ComplexF32)) - @eval begin - # SUBROUTINE DGEMM(TRANSA,TRANSB,M,N,K,ALPHA,A,LDA,B,LDB,BETA,C,LDC) - # * .. Scalar Arguments .. - # DOUBLE PRECISION ALPHA,BETA - # INTEGER K,LDA,LDB,LDC,M,N - # CHARACTER TRANSA,TRANSB - # * .. Array Arguments .. - # DOUBLE PRECISION A(LDA,*),B(LDB,*),C(LDC,*) - function gemm!(transA::Char, transB::Char, m::Int, n::Int, k::Int, alpha::($elty), - A::Ptr{$elty}, stA::Int, B::Ptr{$elty}, stB::Int, - beta::($elty), C::Ptr{$elty},stC::Int) - ccall((@blasfunc($gemm), libblas), Nothing, - (Ref{UInt8}, Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, - Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ref{$elty}, Ptr{$elty}, - Ref{BlasInt}), - transA, transB, m, n, - k, alpha, A, stA, - B, stB, beta, C, - stC) - C - end - - function gemm!(transA::Char, transB::Char, alpha::($elty), A, B, beta::($elty), C) -# if any([stride(A,1), stride(B,1), stride(C,1)] .!= 1) -# error("gemm!: BLAS module requires contiguous matrix columns") -# end # should this be checked on every call? - m = size(A, transA == 'N' ? 1 : 2) - ka = size(A, transA == 'N' ? 2 : 1) - kb = size(B, transB == 'N' ? 1 : 2) - n = size(B, transB == 'N' ? 2 : 1) - if ka != kb || m != size(C,1) || n != size(C,2) - throw(DimensionMismatch("A has size ($m,$ka), B has size ($kb,$n), C has size $(size(C))")) - end - gemm!(transA, transB, m, n, ka, alpha, - pointer(A), max(1,stride(A,2)), - pointer(B), max(1,stride(B,2)), beta, - pointer(C), max(1,stride(C,2))) - C - end - end -end diff --git a/src/LinearAlgebra/clenshaw.jl b/src/LinearAlgebra/clenshaw.jl deleted file mode 100644 index 842c78c0..00000000 --- a/src/LinearAlgebra/clenshaw.jl +++ /dev/null @@ -1,293 +0,0 @@ -export clenshaw - - -## -# ClenshawPlan can be used for multiple evaluations with different functions -# that have the same length -## - -mutable struct ClenshawPlan{S,T} - sp::S - bk::Vector{T} - bk1::Vector{T} - bk2::Vector{T} - A::Vector{T} - B::Vector{T} - C::Vector{T} -end - -function recA end -function recB end -function recC end - -function ClenshawPlan(::Type{T},sp,N::Int,n::Int) where T - A = T[recA(T,sp,k) for k=0:N-1] - B = T[recB(T,sp,k) for k=0:N-1] - C = T[recC(T,sp,k) for k=1:N] - ClenshawPlan(sp,Array{T}(undef,n),Array{T}(undef,n),Array{T}(undef,n),A,B,C) -end - -clenshaw(x,c) = clenshaw_halved(2*x, c) - -@generated function clenshaw_halved(x, c::StaticArrays.SVector{N,R}) where {N, R} - a, b = :(zero(R)), :(zero(R)) - as = [] - for k = N:-1:2 - ak = Symbol("a",k) - push!(as, :($ak = $a)) - a = :(muladd(x,$a,c[$k]-$b)) - b = :($ak) - end - Expr(:block, - as..., - :(muladd(x/2,$a,c[1]-$b))) -end - -for TYP in (:AbstractVector,:AbstractMatrix) - @eval clenshaw(c::$TYP,x::AbstractArray,plan::ClenshawPlan) = reshape(clenshaw(c,vec(x),plan),size(x)) -end - - -function clenshaw(c::AbstractVector,x::AbstractVector,plan::ClenshawPlan{S,V}) where {S,V} - N,n = length(c),length(x) - if isempty(c) - return zeros(V,length(x)) - end - - bk=plan.bk - bk1=plan.bk1 - bk2=plan.bk2 - A=plan.A - B=plan.B - C=plan.C - - @inbounds for i = 1:n - bk1[i] = zero(V) - bk2[i] = zero(V) - end - - @inbounds for k = N:-1:2 - ck,Ak,Bk,Ck = c[k],A[k],B[k],C[k] - for i = 1:n - bk[i] = muladd(muladd(Ak,x[i],Bk),bk1[i],muladd(-Ck,bk2[i],ck)) - end - bk2, bk1, bk = bk1, bk, bk2 - end - - ck,Ak,Bk,Ck = c[1],A[1],B[1],C[1] - @inbounds for i = 1:n - bk[i] = muladd(muladd(Ak,x[i],Bk),bk1[i],muladd(-Ck,bk2[i],ck)) - end - - bk -end - - -#Clenshaw routine for many Funs, x is a vector of same number of funs -#each fun is a column - -function clenshaw(c::AbstractMatrix,x,plan::ClenshawPlan{S,V}) where {S,V} - N,n = size(c) - if isempty(c) - return zeros(V,n) - end - - bk=plan.bk - bk1=plan.bk1 - bk2=plan.bk2 - A=plan.A - B=plan.B - C=plan.C - - @inbounds for i = 1:n - bk1[i] = zero(V) - bk2[i] = zero(V) - end - - @inbounds for k = N:-1:2 - Ak,Bk,Ck = A[k],B[k],C[k] - for i = 1:n - cki = c[k,i] - bk[i] = muladd(muladd(Ak,x,Bk),bk1[i],muladd(-Ck,bk2[i],cki)) - end - bk2, bk1, bk = bk1, bk, bk2 - end - - Ak,Bk,Ck = A[1],B[1],C[1] - @inbounds for i = 1:n - ci = c[1,i] - bk[i] = muladd(muladd(Ak,x,Bk),bk1[i],muladd(-Ck,bk2[i],ci)) - end - - bk -end - -function clenshaw(c::AbstractMatrix,x::AbstractVector,plan::ClenshawPlan{S,V}) where {S,V} - N,n = size(c) - @assert n == length(x) - if isempty(c) - return zeros(V,length(x)) - end - - bk=plan.bk - bk1=plan.bk1 - bk2=plan.bk2 - A=plan.A - B=plan.B - C=plan.C - - @inbounds for i = 1:n - bk1[i] = zero(V) - bk2[i] = zero(V) - end - - @inbounds for k = N:-1:2 - Ak,Bk,Ck = A[k],B[k],C[k] - for i = 1:n - cki = c[k,i] - bk[i] = muladd(muladd(Ak,x[i],Bk),bk1[i],muladd(-Ck,bk2[i],cki)) - end - bk2, bk1, bk = bk1, bk, bk2 - end - - Ak,Bk,Ck = A[1],B[1],C[1] - @inbounds for i = 1:n - ci = c[1,i] - bk[i] = muladd(muladd(Ak,x[i],Bk),bk1[i],muladd(-Ck,bk2[i],ci)) - end - - bk -end - - -# overwrite x - -function clenshaw!(c::AbstractVector,x::AbstractVector,plan::ClenshawPlan{S,V}) where {S,V} - N,n = length(c),length(x) - if isempty(c) - for k=1:n - x[k]=zero(V) - end - end - - bk=plan.bk - bk1=plan.bk1 - bk2=plan.bk2 - A=plan.A - B=plan.B - C=plan.C - - @inbounds for i = 1:n - bk1[i] = zero(V) - bk2[i] = zero(V) - end - - @inbounds for k = N:-1:2 - ck,Ak,Bk,Ck = c[k],A[k],B[k],C[k] - for i = 1:n - bk[i] = muladd(muladd(Ak,x[i],Bk),bk1[i],muladd(-Ck,bk2[i],ck)) - end - bk2, bk1, bk = bk1, bk, bk2 - end - - ck,Ak,Bk,Ck = c[1],A[1],B[1],C[1] - @inbounds for i = 1:n - x[i] = muladd(muladd(Ak,x[i],Bk),bk1[i],muladd(-Ck,bk2[i],ck)) - end - - x -end - - -function sineshaw(c::AbstractVector,θ) - if isempty(c) - return zero(θ) - end - - x = 2cos(θ) - bk1,bk2 = zero(x),zero(x) - @inbounds for k = length(c):-1:1 - bk2, bk1 = bk1, muladd(x,bk1,c[k]-bk2) - end - - sin(θ)*bk1 -end -sineshaw(c::AbstractVector,θ::AbstractVector) = promote_type(eltype(c),eltype(θ))[sineshaw(c,θ[k]) for k=1:length(θ)] -sineshaw(c::AbstractVector,θ::AbstractMatrix) = promote_type(eltype(c),eltype(θ))[sineshaw(c,θ[k,j]) for k=1:size(θ,1),j=1:size(θ,2)] - - -function chebyshev_clenshaw(c::AbstractVector, x) - N,T = length(c),promote_type(eltype(c),typeof(x)) - if N == 0 - return zero(x) - elseif N == 1 # avoid issues with NaN x - return first(c)*one(x) - end - - x = 2x - bk1,bk2 = zero(T),zero(T) - @inbounds for k = N:-1:2 - bk2, bk1 = bk1, muladd(x,bk1,c[k]-bk2) - end - - muladd(x/2,bk1,c[1]-bk2) -end - - -function chebyshev_clenshaw(c::AbstractVector,x::Vector,plan::ClenshawPlan{<:Any,V}) where V - N,n = length(c),length(x) - if isempty(c) - return zeros(V,n) - end - - bk=plan.bk - bk1=plan.bk1 - bk2=plan.bk2 - - @inbounds for i = 1:n - x[i] = 2x[i] - bk1[i] = zero(V) - bk2[i] = zero(V) - end - - @inbounds for k = N:-1:2 - ck = c[k] - for i = 1:n - bk[i] = muladd(x[i],bk1[i],ck-bk2[i]) - end - bk2, bk1, bk = bk1, bk, bk2 - end - - ck = c[1] - @inbounds for i = 1:n - x[i] = x[i]/2 - bk[i] = muladd(x[i],bk1[i],ck-bk2[i]) - end - - bk -end - -# entries for Chebyshev/Cos multiplication -function chebmult_getindex(cfs::AbstractVector,k::Integer,j::Integer) - n=length(cfs) - - ret = zero(eltype(cfs)) - - n == 0 && return ret - - # Toeplitz part - if k == j - ret += cfs[1] - elseif k > j && k-j+1 ≤ n - ret += cfs[k-j+1]/2 - elseif k < j && j-k+1 ≤ n - ret += cfs[j-k+1]/2 - end - - # Hankel part - if k ≥ 2 && k+j-1 ≤ n - ret += cfs[k+j-1]/2 - end - - ret -end \ No newline at end of file diff --git a/src/LinearAlgebra/helper.jl b/src/LinearAlgebra/helper.jl deleted file mode 100644 index 35de1d71..00000000 --- a/src/LinearAlgebra/helper.jl +++ /dev/null @@ -1,752 +0,0 @@ -import Base: chop - -# BLAS/linear algebra overrides - -@inline dot(x...) = LinearAlgebra.dot(x...) -@inline dot(M::Int,a::Ptr{T},incx::Int,b::Ptr{T},incy::Int) where {T<:Union{Float64,Float32}} = - BLAS.dot(M,a,incx,b,incy) -@inline dot(M::Int,a::Ptr{T},incx::Int,b::Ptr{T},incy::Int) where {T<:Union{ComplexF64,ComplexF32}} = - BLAS.dotc(M,a,incx,b,incy) - -dotu(f::StridedVector{T},g::StridedVector{T}) where {T<:Union{ComplexF32,ComplexF64}} = - BLAS.dotu(f,g) -dotu(f::AbstractVector{Complex{Float64}},g::AbstractVector{N}) where {N<:Real} = dot(conj(f),g) -dotu(f::AbstractVector{N},g::AbstractVector{T}) where {N<:Real,T<:Number} = dot(f,g) - - -normalize!(w::AbstractVector) = rmul!(w,inv(norm(w))) -normalize!(w::Vector{T}) where {T<:BlasFloat} = normalize!(length(w),w) -normalize!(n,w::Union{Vector{T},Ptr{T}}) where {T<:Union{Float64,Float32}} = - BLAS.scal!(n,inv(BLAS.nrm2(n,w,1)),w,1) -normalize!(n,w::Union{Vector{T},Ptr{T}}) where {T<:Union{ComplexF64,ComplexF32}} = - BLAS.scal!(n,T(inv(BLAS.nrm2(n,w,1))),w,1) - - -flipsign(x,y) = Base.flipsign(x,y) -flipsign(x,y::Complex) = y==0 ? x : x*sign(y) - -# Used for spaces not defined yet -struct UnsetNumber <: Number end -promote_rule(::Type{UnsetNumber},::Type{N}) where {N<:Number} = N -promote_rule(::Type{Bool}, ::Type{UnsetNumber}) = Bool - -# Test the number of arguments a function takes -hasnumargs(f,k) = applicable(f,zeros(k)...) - - -# fast implementation of isapprox with atol a non-keyword argument in most cases -isapprox_atol(a,b,atol;kwds...) = isapprox(a,b;atol=atol,kwds...) -isapprox_atol(a::Vec,b::Vec,atol::Real=0;kwds...) = isapprox_atol(collect(a),collect(b),atol;kwds...) -function isapprox_atol(x::Number, y::Number, atol::Real=0; rtol::Real=Base.rtoldefault(x,y)) - x == y || (isfinite(x) && isfinite(y) && abs(x-y) <= atol + rtol*max(abs(x), abs(y))) -end -function isapprox_atol(x::AbstractArray{T}, y::AbstractArray{S},atol::Real=0; rtol::Real=Base.rtoldefault(T,S), norm::Function=vecnorm) where {T<:Number,S<:Number} - d = norm(x - y) - if isfinite(d) - return d <= atol + rtol*max(norm(x), norm(y)) - else - # Fall back to a component-wise approximate comparison - return all(ab -> isapprox(ab[1], ab[2]; rtol=rtol, atol=atol), zip(x, y)) - end -end - -# The second case handles zero -isapproxinteger(x) = isapprox(x,round(Int,x)) || isapprox(x+1,round(Int,x+1)) - - -# This creates ApproxFunBase.real, ApproxFunBase.eps and ApproxFunBase.dou -# which we override for default julia types -real(x...) = Base.real(x...) -real(::Type{UnsetNumber}) = UnsetNumber -real(::Type{Array{T,n}}) where {T<:Real,n} = Array{T,n} -real(::Type{Array{T,n}}) where {T<:Complex,n} = Array{real(T),n} -real(::Type{Vec{N,T}}) where {N,T<:Real} = Vec{N,T} -real(::Type{Vec{N,T}}) where {N,T<:Complex} = Vec{N,real(T)} - -float(x) = Base.float(x) -Base.float(::UnsetNumber) = UnsetNumber() -Base.float(::Type{UnsetNumber}) = UnsetNumber -float(::Type{Array{T,N}}) where {T,N} = Array{float(T),N} -float(::Type{SVector{N,T}}) where {T,N} = SVector{N,float(T)} - - - -eps(x...) = Base.eps(x...) -eps(x) = Base.eps(x) - -eps(::Type{T}) where T<:Integer = zero(T) -eps(::Type{T}) where T<:Rational = zero(T) -eps(::T) where T<:Integer = eps(T) - -eps(::Type{Complex{T}}) where {T<:Real} = eps(real(T)) -eps(z::Complex{T}) where {T<:Real} = eps(abs(z)) -eps(::Type{Dual{Complex{T}}}) where {T<:Real} = eps(real(T)) -eps(z::Dual{Complex{T}}) where {T<:Real} = eps(abs(z)) - - -eps(::Type{Vector{T}}) where {T<:Number} = eps(T) -eps(::Type{Vec{k,T}}) where {k,T<:Number} = eps(T) - - -isnan(x) = Base.isnan(x) -isnan(x::Vec) = map(isnan,x) - - -# BLAS - - -# implement muladd default -muladd(a,b,c) = a*b+c -muladd(a::Number,b::Number,c::Number) = Base.muladd(a,b,c) - - -for TYP in (:Float64,:Float32,:ComplexF64,:ComplexF32) - @eval scal!(n::Integer,cst::$TYP,ret::DenseArray{T},k::Integer) where {T<:$TYP} = - BLAS.scal!(n,cst,ret,k) -end - - -scal!(n::Integer,cst::BlasFloat,ret::DenseArray{T},k::Integer) where {T<:BlasFloat} = - BLAS.scal!(n,convert(T,cst),ret,k) - -function scal!(n::Integer,cst::Number,ret::AbstractArray,k::Integer) - @assert k*n ≤ length(ret) - @simd for j=1:k:k*(n-1)+1 - @inbounds ret[j] *= cst - end - ret -end - -scal!(cst::Number,v::AbstractArray) = scal!(length(v),cst,v,1) - - - -# Helper routines - -function reverseeven!(x::AbstractVector) - n = length(x) - if iseven(n) - @inbounds @simd for k=2:2:n÷2 - x[k],x[n+2-k] = x[n+2-k],x[k] - end - else - @inbounds @simd for k=2:2:n÷2 - x[k],x[n+1-k] = x[n+1-k],x[k] - end - end - x -end - -function negateeven!(x::AbstractVector) - @inbounds @simd for k = 2:2:length(x) - x[k] *= -1 - end - x -end - -#checkerboard, same as applying negativeeven! to all rows then all columns -function negateeven!(X::AbstractMatrix) - for j = 1:2:size(X,2) - @inbounds @simd for k = 2:2:size(X,1) - X[k,j] *= -1 - end - end - for j = 2:2:size(X,2) - @inbounds @simd for k = 1:2:size(X,1) - X[k,j] *= -1 - end - end - X -end - -const alternatesign! = negateeven! - -alternatesign(v::AbstractVector) = alternatesign!(copy(v)) - -alternatingvector(n::Integer) = 2*mod([1:n],2) .- 1 - -function alternatingsum(v::AbstractVector) - ret = zero(eltype(v)) - s = 1 - @inbounds for k=1:length(v) - ret+=s*v[k] - s*=-1 - end - - ret -end - -# Sum Hadamard product of vectors up to minimum over lengths -function mindotu(a::AbstractVector,b::AbstractVector) - ret,m = zero(promote_type(eltype(a),eltype(b))),min(length(a),length(b)) - @inbounds @simd for i=m:-1:1 ret += a[i]*b[i] end - ret -end - - -# efficiently resize a Matrix. Note it doesn't change the input ptr -function unsafe_resize!(W::AbstractMatrix,::Colon,m::Integer) - if m == size(W,2) - W - else - n=size(W,1) - reshape(resize!(vec(W),n*m),n,m) - end -end - -function unsafe_resize!(W::AbstractMatrix,n::Integer,::Colon) - N=size(W,1) - if n == N - W - elseif n < N - W[1:n,:] - else - m=size(W,2) - ret=Matrix{eltype(W)}(n,m) - ret[1:N,:] = W - ret - end -end - -function unsafe_resize!(W::AbstractMatrix,n::Integer,m::Integer) - N=size(W,1) - if n == N - unsafe_resize!(W,:,m) - else - unsafe_resize!(unsafe_resize!(W,n,:),:,m) - end -end - - -function pad!(f::AbstractVector{T},n::Integer) where T - if n > length(f) - append!(f,zeros(T,n - length(f))) - else - resize!(f,n) - end -end - - -function pad(f::AbstractVector{T},n::Integer) where T - if n > length(f) - ret=Vector{T}(undef, n) - ret[1:length(f)]=f - for j=length(f)+1:n - ret[j]=zero(T) - end - ret - else - f[1:n] - end -end - -function pad(f::AbstractVector{Any},n::Integer) - if n > length(f) - Any[f...,zeros(n - length(f))...] - else - f[1:n] - end -end - -pad(x::Number, n::Int) = [x; zeros(typeof(x), n-1)] - -function pad(v::AbstractVector,n::Integer,m::Integer) - @assert m==1 - pad(v,n) -end - -function pad(A::AbstractMatrix,n::Integer,m::Integer) - T=eltype(A) - if n <= size(A,1) && m <= size(A,2) - A[1:n,1:m] - elseif n==0 || m==0 - Matrix{T}(undef,n,m) #fixes weird julia bug when T==None - else - ret = Matrix{T}(undef,n,m) - minn=min(n,size(A,1)) - minm=min(m,size(A,2)) - for k=1:minn,j=1:minm - @inbounds ret[k,j]=A[k,j] - end - for k=minn+1:n,j=1:minm - @inbounds ret[k,j]=zero(T) - end - for k=1:n,j=minm+1:m - @inbounds ret[k,j]=zero(T) - end - for k=minn+1:n,j=minm+1:m - @inbounds ret[k,j]=zero(T) - end - - ret - end -end - -pad(A::AbstractMatrix,::Colon,m::Integer) = pad(A,size(A,1),m) -pad(A::AbstractMatrix,n::Integer,::Colon) = pad(A,n,size(A,2)) - - -function pad(v, ::Infinity) - if isinf(length(v)) - v - else - Vcat(v, Zeros{Int}(∞)) - end -end - -function pad(v::AbstractVector{T}, ::Infinity) where T - if isinf(length(v)) - v - else - Vcat(v, Zeros{T}(∞)) - end -end - - -#TODO:padleft! - -function padleft(f::AbstractVector,n::Integer) - if (n > length(f)) - [zeros(n - length(f)); f] - else - f[end-n+1:end] - end -end - - - -##chop! -function chop!(c::AbstractVector,tol::Real) - @assert tol >= 0 - - for k=length(c):-1:1 - if abs(c[k]) > tol - resize!(c,k) - return c - end - end - - resize!(c,0) - c -end - -chop(f::AbstractVector,tol) = chop!(copy(f),tol) -chop!(f::AbstractVector) = chop!(f,eps()) - - -function chop!(A::AbstractArray,tol) - for k=size(A,1):-1:1 - if norm(A[k,:])>tol - A=A[1:k,:] - break - end - end - for k=size(A,2):-1:1 - if norm(A[:,k])>tol - A=A[:,1:k] - break - end - end - return A -end -chop(A::AbstractArray,tol)=chop!(A,tol)#replace by chop!(copy(A),tol) when chop! is actually in-place. - - - -## interlace - - - -function interlace(v::Union{Vector{Any},Tuple}) - #determine type - T=Float64 - for vk in v - if isa(vk,Vector{Complex{Float64}}) - T=Complex{Float64} - end - end - b=Vector{Vector{T}}(undef, length(v)) - for k=1:length(v) - b[k]=v[k] - end - interlace(b) -end - -function interlace(a::AbstractVector{S},b::AbstractVector{V}) where {S<:Number,V<:Number} - na=length(a);nb=length(b) - T=promote_type(S,V) - if nb≥na - ret=zeros(T,2nb) - ret[1:2:1+2*(na-1)]=a - ret[2:2:end]=b - ret - else - ret=zeros(T,2na-1) - ret[1:2:end]=a - if !isempty(b) - ret[2:2:2+2*(nb-1)]=b - end - ret - end -end - -function interlace(a::AbstractVector,b::AbstractVector) - na=length(a);nb=length(b) - T=promote_type(eltype(a),eltype(b)) - if nb≥na - ret=Vector{T}(undef, 2nb) - ret[1:2:1+2*(na-1)]=a - ret[2:2:end]=b - ret - else - ret=Vector{T}(undef, 2na-1) - ret[1:2:end]=a - if !isempty(b) - ret[2:2:2+2*(nb-1)]=b - end - ret - end -end - - -### In-place O(n) interlacing - -function highestleader(n::Int) - i = 1 - while 3i < n i *= 3 end - i -end - -function nextindex(i::Int,n::Int) - i <<= 1 - while i > n - i -= n + 1 - end - i -end - -function cycle_rotate!(v::AbstractVector, leader::Int, it::Int, twom::Int) - i = nextindex(leader, twom) - while i != leader - idx1, idx2 = it + i - 1, it + leader - 1 - @inbounds v[idx1], v[idx2] = v[idx2], v[idx1] - i = nextindex(i, twom) - end - v -end - -function right_cyclic_shift!(v::AbstractVector, it::Int, m::Int, n::Int) - itpm = it + m - itpmm1 = itpm - 1 - itpmpnm1 = itpmm1 + n - reverse!(v, itpm, itpmpnm1) - reverse!(v, itpm, itpmm1 + m) - reverse!(v, itpm + m, itpmpnm1) - v -end - -""" -This function implements the algorithm described in: - - P. Jain, "A simple in-place algorithm for in-shuffle," arXiv:0805.1598, 2008. -""" -function interlace!(v::AbstractVector,offset::Int) - N = length(v) - if N < 2 + offset - return v - end - - it = 1 + offset - m = 0 - n = 1 - - while m < n - twom = N + 1 - it - h = highestleader(twom) - m = h > 1 ? h÷2 : 1 - n = twom÷2 - - right_cyclic_shift!(v,it,m,n) - - leader = 1 - while leader < 2m - cycle_rotate!(v, leader, it, 2m) - leader *= 3 - end - - it += 2m - end - v -end - -## slnorm gives the norm of a slice of a matrix - -function slnorm(u::AbstractMatrix,r::AbstractRange,::Colon) - ret = zero(real(eltype(u))) - for k=r - @simd for j=1:size(u,2) - #@inbounds - ret=max(norm(u[k,j]),ret) - end - end - ret -end - - -function slnorm(m::AbstractMatrix,kr::AbstractRange,jr::AbstractRange) - ret=zero(real(eltype(m))) - for j=jr - nrm=zero(real(eltype(m))) - for k=kr - @inbounds nrm+=abs2(m[k,j]) - end - ret=max(sqrt(nrm),ret) - end - ret -end - -slnorm(m::AbstractMatrix,kr::AbstractRange,jr::Integer) = slnorm(m,kr,jr:jr) -slnorm(m::AbstractMatrix,kr::Integer,jr::AbstractRange) = slnorm(m,kr:kr,jr) - - -function slnorm(B::BandedMatrix{T},r::AbstractRange,::Colon) where T - ret = zero(real(T)) - m=size(B,2) - for k=r - @simd for j=max(1,k-B.l):min(k+B.u,m) - #@inbounds - ret=max(norm(B[k,j]),ret) - end - end - ret -end - - -slnorm(m::AbstractMatrix,k::Integer,::Colon) = slnorm(m,k,1:size(m,2)) -slnorm(m::AbstractMatrix,::Colon,j::Integer) = slnorm(m,1:size(m,1),j) - - -## Infinity - - - -Base.isless(x::Block{1}, y::Infinity) = isless(Int(x), y) -Base.isless(x::Infinity, y::Block{1}) = isless(x, Int(y)) - - -## BandedMatrix - - - -pad!(A::BandedMatrix,n,::Colon) = pad!(A,n,n+A.u) # Default is to get all columns -columnrange(A,row::Integer) = max(1,row-bandwidth(A,1)):row+bandwidth(A,2) - - - -## Store iterator -mutable struct CachedIterator{T,IT} - iterator::IT - storage::Vector{T} - state - length::Int -end - -CachedIterator{T,IT}(it::IT, state) where {T,IT} = CachedIterator{T,IT}(it,T[],state,0) -CachedIterator(it::IT) where IT = CachedIterator{eltype(it),IT}(it, ()) - -function resize!(it::CachedIterator,n::Integer) - m = it.length - if n > m - if n > length(it.storage) - resize!(it.storage,2n) - end - - @inbounds for k = m+1:n - xst = iterate(it.iterator,it.state...) - if xst == nothing - it.length = k-1 - return it - end - it.storage[k] = xst[1] - it.state = (xst[2],) - end - - it.length = n - end - it -end - - -eltype(it::CachedIterator{T}) where {T} = T - -iterate(it::CachedIterator) = iterate(it,1) -function iterate(it::CachedIterator,st::Int) - if st == it.length + 1 && iterate(it.iterator,it.state...) == nothing - nothing - else - (it[st],st+1) - end -end - -function getindex(it::CachedIterator, k) - mx = maximum(k) - if mx > length(it) || mx < 1 - throw(BoundsError(it,k)) - end - resize!(it,isempty(k) ? 0 : mx).storage[k] -end - -function findfirst(f::Function,A::CachedIterator) - k=1 - for c in A - if f(c) - return k - end - k+=1 - end - return 0 -end - -function findfirst(A::CachedIterator,x) - k=1 - for c in A - if c == x - return k - end - k+=1 - end - return 0 -end - -length(A::CachedIterator) = length(A.iterator) - -## nocat -vnocat(A...) = Base.vect(A...) -hnocat(A...) = Base.typed_hcat(mapreduce(typeof,promote_type,A),A...) -hvnocat(rows,A...) = Base.typed_hvcat(mapreduce(typeof,promote_type,A),rows,A...) -macro nocat(x) - ex = expand(x) - if ex.args[1] == :vcat - ex.args[1] = :(ApproxFunBase.vnocat) - elseif ex.args[1] == :hcat - ex.args[1] = :(ApproxFunBase.hnocat) - else - @assert ex.args[1] == :hvcat - ex.args[1] = :(ApproxFunBase.hvnocat) - end - esc(ex) -end - - - -## Dynamic functions - -struct DFunction <: Function - f -end -(f::DFunction)(args...) = f.f(args...) - -hasnumargs(f::DFunction, k) = hasnumargs(f.f, k) - -dynamic(f) = f -dynamic(f::Function) = DFunction(f) # Assume f has to compile every time - - -# Matrix inputs - - - - -## conv - -conv(x::AbstractVector, y::AbstractVector) = DSP.conv(x, y) -@generated function conv(x::SVector{N}, y::SVector{M}) where {N,M} - NM = N+M-1 - quote - convert(SVector{$NM}, DSP.conv(Vector(x), Vector(y))) - end -end - -conv(x::SVector{1}, y::SVector{1}) = x.*y -conv(x::AbstractVector, y::SVector{1}) = x*y[1] -conv(y::SVector{1}, x::AbstractVector) = y[1]*x -conv(x::AbstractFill, y::SVector{1}) = x*y[1] -conv(y::SVector{1}, x::AbstractFill) = y[1]*x -conv(x::AbstractFill, y::AbstractFill) = DSP.conv(x, y) - - -## BlockInterlacer -# interlaces coefficients by blocks -# this has the property that all the coefficients of a block of a subspace -# are grouped together, starting with the first bloc -# -# TODO: cache sums - - -struct BlockInterlacer{DMS<:Tuple} - blocks::DMS -end - - -const TrivialInterlacer{d} = BlockInterlacer{NTuple{d,<:Ones}} - -BlockInterlacer(v::AbstractVector) = BlockInterlacer(tuple(v...)) - -Base.eltype(it::BlockInterlacer) = Tuple{Int,Int} - -dimensions(b::BlockInterlacer) = map(sum,b.blocks) -dimension(b::BlockInterlacer,k) = sum(b.blocks[k]) -Base.length(b::BlockInterlacer) = mapreduce(sum,+,b.blocks) - - -# the state is always (whichblock,curblock,cursubblock,curcoefficients) -# start(it::BlockInterlacer) = (1,1,map(start,it.blocks),ntuple(zero,length(it.blocks))) - - - -# are all Ints, so finite dimensional -function done(it::BlockInterlacer,st) - for k=1:length(it.blocks) - if st[end][k] < sum(it.blocks[k]) - return false - end - end - return true -end - -iterate(it::BlockInterlacer) = - iterate(it, (1,1,ntuple(_ -> tuple(), length(it.blocks)), - ntuple(zero,length(it.blocks)))) - -function iterate(it::BlockInterlacer, (N,k,blkst,lngs)) - done(it, (N,k,blkst,lngs)) && return nothing - - if N > length(it.blocks) - # increment to next block - blkst = map(function(blit,blst) - xblst = iterate(blit, blst...) - xblst == nothing ? blst : (xblst[2],) - end,it.blocks,blkst) - return iterate(it,(1,1,blkst,lngs)) - end - - Bnxtb = iterate(it.blocks[N],blkst[N]...) # B is block size - - if Bnxtb == nothing - # increment to next N - return iterate(it,(N+1,1,blkst,lngs)) - end - - B,nxtb = Bnxtb - - if k > B - #increment to next N - return iterate(it,(N+1,1,blkst,lngs)) - end - - - lngs = tuple(lngs[1:N-1]...,lngs[N]+1,lngs[N+1:end]...) - return (N,lngs[N]),(N,k+1,blkst,lngs) -end - -cache(Q::BlockInterlacer) = CachedIterator(Q) \ No newline at end of file diff --git a/src/LinearAlgebra/hesseneigs.jl b/src/LinearAlgebra/hesseneigs.jl deleted file mode 100644 index 210e798b..00000000 --- a/src/LinearAlgebra/hesseneigs.jl +++ /dev/null @@ -1,81 +0,0 @@ - - - -import LinearAlgebra.BLAS: BlasInt, @blasfunc - - -for (hseqr,elty) in ((:zhseqr_,:ComplexF64),) - @eval function hesseneigvals(M::Matrix{$elty}) - if isempty(M) - return $elty[] - end - A=vec(M) - - N=size(M,1) - info =0 - - ilo = 1; ihi = N; ldh=N;ldz=N;lwork = N - - z=zero($elty) - work = Array{$elty}(undef, N*N) - w=Array{$elty}(undef, N) - - Ec='E' - Nc='N' - ccall((@blasfunc($hseqr),LAPACK.liblapack), - Nothing, - (Ref{UInt8}, Ref{UInt8}, - Ref{BlasInt}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, #A - Ref{BlasInt}, Ptr{$elty}, Ref{$elty}, #z - Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, Ref{BlasInt}), - Ec, Nc, - N , ilo, ihi, A, - ldh, w, z, - ldz, work, lwork, info) - w - end -end - -for (hseqr,elty) in ((:dhseqr_,:Float64),) - @eval function hesseneigvals(M::Matrix{$elty}) - if isempty(M) - return $elty[] - end - - A=vec(M) - - N=size(M,1) - info =0 - - ilo = 1; ihi = N; ldh=N;ldz=N; - - lwork = -1 - - z=zero($elty) - work = Array{$elty}(undef, 1) - wr=Array{$elty}(undef, N) - wi=Array{$elty}(undef, N) - - Ec='E' - Nc='N' - for i=1:2 - ccall((@blasfunc($hseqr),LAPACK.liblapack), - Nothing, - (Ref{UInt8},Ref{UInt8}, - Ref{BlasInt},Ref{BlasInt},Ref{BlasInt},Ptr{$elty}, #A - Ref{BlasInt},Ptr{$elty},Ptr{$elty},Ref{$elty}, #z - Ref{BlasInt},Ptr{$elty},Ref{BlasInt},Ref{BlasInt}), - Ec, Nc, - N , ilo, ihi, A, - ldh, wr,wi, z, - ldz, work, lwork, info) - - if lwork < 0 - lwork=Int(real(work[1])) - work=Array{$elty}(undef, lwork) - end - end - - wr+im*wi - end -end diff --git a/src/LinearAlgebra/lyap.jl b/src/LinearAlgebra/lyap.jl deleted file mode 100644 index 56d5dc25..00000000 --- a/src/LinearAlgebra/lyap.jl +++ /dev/null @@ -1,92 +0,0 @@ -function lyapdiag!(A::Vector,B::Vector,F) - for i=1:size(F,1),j=1:size(F,2) - F[i,j] *= 1/(A[i]+B[j]) - end - - F -end - -# Solve P*Y*R' + S*Y*T' = F -# where P,R,S and T are upper triangular -function lyapuptriang(P,R,S,T,F::AbstractArray{N}) where N - m=size(P,2); n = size(T,2) - Y=Matrix{N}(m,n) - PY = Matrix{N}(m,n); SY = Matrix{N}(m,n) - - k=n - while k > 1 - if T[k,k-1] == 0 && R[k,k-1] == 0 - rhs = F[:,k] - if k < n - for j = k+1:n, l=1:m - rhs[l] -= R[k,j]*PY[l,j] + T[k,j]*SY[l,j] - end - end - - Y[:,k] = (R[k,k]*P + T[k,k]*S)\rhs - PY[:,k] = P*Y[:,k];SY[:,k] = S*Y[:,k] - - k -= 1 - else - rhs1 = F[:,k-1];rhs2=F[:,k] - for j = k+1:n, l=1:m - rhs1[l] -= R[k-1,j]*PY[l,j] + T[k-1,j]*SY[l,j] - rhs2[l] -= R[k,j]*PY[l,j] + T[k,j]*SY[l,j] - end - - SM = Matrix{N}(2m,2m) - - for i=1:m,j=1:m - SM[i,j] = R[k-1,k-1]*P[i,j] + T[k-1,k-1]*S[i,j] - SM[i,j+m] = R[k-1,k]*P[i,j] + T[k-1,k]*S[i,j] - SM[i+m,j] = R[k,k-1]*P[i,j] + T[k,k-1]*S[i,j] - SM[i+m,j+m] = R[k,k]*P[i,j] + T[k,k]*S[i,j] - end - - UM = SM\[rhs1;rhs2] - - Y[:,k-1] = UM[1:m];Y[:,k]=UM[m+1:end] - - PY[:,k]=P*Y[:,k];PY[:,k-1]=P*Y[:,k-1] - SY[:,k]=S*Y[:,k];SY[:,k-1]=S*Y[:,k-1] - - k -=2 - end - end - - if k == 1 - rhs =F[:,1] - PY[:,2]=P*Y[:,2] - SY[:,2]=S*Y[:,2] - for j=2:n - rhs -= R[1,j]*PY[:,j] + T[1,j]*SY[:,j] - end - - Y[:,1]=(R[1,1]*P + T[1,1]*S)\rhs - end - Y -end - - -##Solves A*X*transpose(B) + C*X*transpose(D) = E -function lyap(A,B,C,D,E) - AC=schurfact(Matrix(A),Matrix(C)) - BD=schurfact(Matrix(B),Matrix(D)) - Q1=AC[:left];Q2=BD[:left] - Z1=AC[:right];Z2=BD[:right] - - Y=lyapuptriang(AC[:S],BD[:S],AC[:T],BD[:T],Q1'*E*Q2) - Z1*Y*Z2' -end - - -# function lyap(A,B,F) -# Λ,V=eig(A) -# Ω,W=eig(B) -# -# F=inv(V)*F*W -# -# Y=lyapdiag!(Λ,Ω,F) -# -# real(V*Y*inv(W)) -# end diff --git a/src/LinearAlgebra/rowvector.jl b/src/LinearAlgebra/rowvector.jl deleted file mode 100644 index bc1e7cb9..00000000 --- a/src/LinearAlgebra/rowvector.jl +++ /dev/null @@ -1,127 +0,0 @@ -# This file is based on rowvector.jl in Julia. License is MIT: https://julialang.org/license -# The motivation for this file is to allow RowVector which doesn't transpose the entries - -import Base: convert, similar, length, size, axes, IndexStyle, - IndexLinear, @propagate_inbounds, getindex, setindex!, - broadcast, hcat, typed_hcat, map, parent - -""" - RowVector(vector) - -A lazy-view wrapper of an `AbstractVector`, which turns a length-`n` vector into a `1×n` -shaped row vector and represents the transpose of a vector (the elements are also transposed -recursively). - -By convention, a vector can be multiplied by a matrix on its left (`A * v`) whereas a row -vector can be multiplied by a matrix on its right (such that `transpose(v) * A = transpose(transpose(A) * v)`). It -differs from a `1×n`-sized matrix by the facts that its transpose returns a vector and the -inner product `transpose(v1) * v2` returns a scalar, but will otherwise behave similarly. -""" -struct RowVector{T,V<:AbstractVector} <: AbstractMatrix{T} - vec::V - function RowVector{T,V}(v::V) where V<:AbstractVector where T - new(v) - end -end - -# Constructors that take a vector -@inline RowVector(vec::AbstractVector{T}) where {T} = RowVector{T,typeof(vec)}(vec) -@inline RowVector{T}(vec::AbstractVector{T}) where {T} = RowVector{T,typeof(vec)}(vec) - -# Constructors that take a size and default to Array -@inline RowVector{T}(n::Int) where {T} = RowVector{T}(Vector{T}(n)) -@inline RowVector{T}(n1::Int, n2::Int) where {T} = n1 == 1 ? - RowVector{T}(Vector{T}(n2)) : - error("RowVector expects 1×N size, got ($n1,$n2)") -@inline RowVector{T}(n::Tuple{Int}) where {T} = RowVector{T}(Vector{T}(n[1])) -@inline RowVector{T}(n::Tuple{Int,Int}) where {T} = n[1] == 1 ? - RowVector{T}(Vector{T}(n[2])) : - error("RowVector expects 1×N size, got $n") - -# Conversion of underlying storage -convert(::Type{RowVector{T,V}}, rowvec::RowVector) where {T,V<:AbstractVector} = - RowVector{T,V}(convert(V,rowvec.vec)) - -# similar tries to maintain the RowVector wrapper and the parent type -@inline similar(rowvec::RowVector) = RowVector(similar(parent(rowvec))) -@inline similar(rowvec::RowVector, ::Type{T}) where {T} = RowVector(similar(parent(rowvec), transpose_type(T))) - -# Resizing similar currently loses its RowVector property. -@inline similar(rowvec::RowVector, ::Type{T}, dims::Dims{N}) where {T,N} = similar(parent(rowvec), T, dims) - -parent(rowvec::RowVector) = rowvec.vec - -# AbstractArray interface -@inline length(rowvec::RowVector) = length(rowvec.vec) -@inline size(rowvec::RowVector) = (1, length(rowvec.vec)) -@inline size(rowvec::RowVector, d) = ifelse(d==2, length(rowvec.vec), 1) -@inline axes(rowvec::RowVector) = (Base.OneTo(1), axes(rowvec.vec)[1]) -@inline axes(rowvec::RowVector, d) = ifelse(d == 2, axes(rowvec.vec)[1], Base.OneTo(1)) -IndexStyle(::RowVector) = IndexLinear() -IndexStyle(::Type{<:RowVector}) = IndexLinear() - - -@propagate_inbounds getindex(rowvec::RowVector, i) = rowvec.vec[i] -@propagate_inbounds setindex!(rowvec::RowVector, v, i) = setindex!(rowvec.vec, v, i) - -# Cartesian indexing is distorted by getindex -# Furthermore, Cartesian indexes don't have to match shape, apparently! -@inline function getindex(rowvec::RowVector, i::CartesianIndex) - @boundscheck if !(i.I[1] == 1 && i.I[2] ∈ axes(rowvec.vec)[1] && check_tail_indices(i.I...)) - throw(BoundsError(rowvec, i.I)) - end - @inbounds return rowvec.vec[i.I[2]] -end -@inline function setindex!(rowvec::RowVector, v, i::CartesianIndex) - @boundscheck if !(i.I[1] == 1 && i.I[2] ∈ axes(rowvec.vec)[1] && check_tail_indices(i.I...)) - throw(BoundsError(rowvec, i.I)) - end - @inbounds rowvec.vec[i.I[2]] = v -end - -@propagate_inbounds getindex(rowvec::RowVector, ::CartesianIndex{0}) = getindex(rowvec) -@propagate_inbounds getindex(rowvec::RowVector, i::CartesianIndex{1}) = getindex(rowvec, i.I[1]) - -@propagate_inbounds setindex!(rowvec::RowVector, v, ::CartesianIndex{0}) = setindex!(rowvec, v) -@propagate_inbounds setindex!(rowvec::RowVector, v, i::CartesianIndex{1}) = setindex!(rowvec, v, i.I[1]) - -# helper function for below -@inline to_vec(rowvec::RowVector) = rowvec.vec - -# map: Preserve the RowVector by un-wrapping and re-wrapping, but note that `f` -# expects to operate within the transposed domain, so to_vec transposes the elements -@inline map(f, rowvecs::RowVector...) = RowVector(map(f, to_vecs(rowvecs...)...)) - -# broacast (other combinations default to higher-dimensional array) -@inline broadcast(f, rowvecs::Union{Number,RowVector}...) = - RowVector(broadcast(f, to_vecs(rowvecs...)...)) - -# Horizontal concatenation # - -@inline hcat(X::RowVector...) = RowVector(vcat(X...)) -@inline hcat(X::Union{RowVector,Number}...) = RowVector(vcat(X...)) - -@inline typed_hcat(::Type{T}, X::RowVector...) where {T} = - RowVector(typed_vcat(T, X...)) -@inline typed_hcat(::Type{T}, X::Union{RowVector,Number}...) where {T} = - RowVector(typed_vcat(T, X...)) - -# Multiplication # - -# inner product -> dot product specializations -@inline *(rowvec::RowVector{T}, vec::AbstractVector{T}) where {T<:Real} = dotu(parent(rowvec), vec) - -# Generic behavior -@inline function *(rowvec::RowVector, vec::AbstractVector) - if length(rowvec) != length(vec) - throw(DimensionMismatch("A has dimensions $(size(rowvec)) but B has dimensions $(size(vec))")) - end - sum(@inbounds(rowvec[i]*vec[i]) for i = 1:length(vec)) -end -@inline *(rowvec::RowVector, mat::AbstractMatrix) = RowVector(transpose(transpose()mat) * rowvec.vec) -*(::RowVector, ::RowVector) = throw(DimensionMismatch("Cannot multiply two transposed vectors")) -@inline *(vec::AbstractVector, rowvec::RowVector) = vec .* rowvec - - - -## Removed A_* overrides for now diff --git a/src/LinearAlgebra/standardchop.jl b/src/LinearAlgebra/standardchop.jl deleted file mode 100644 index 8544a01d..00000000 --- a/src/LinearAlgebra/standardchop.jl +++ /dev/null @@ -1,106 +0,0 @@ -# Contains code that is based in part on Chebfun v5's chebfun/standardChop.m, -# which is distributed with the following license: - -# Copyright (c) 2017, The Chancellor, Masters and Scholars of the University -# of Oxford, and the Chebfun Developers. All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of the University of Oxford nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -# Jared Aurentz and Nick Trefethen, July 2015. -# -# Copyright 2017 by The University of Oxford and The Chebfun Developers. -# See http://www.chebfun.org/ for Chebfun information. - -function standardchoplength(coeffs, tol) - # Check magnitude of TOL: - if tol ≥ 1 - throw(ArgumentError("tolerance must be less than 1")) - end - - # Make sure COEFFS has length at least 17: - n = length(coeffs) - - if n < 17 - # resort to naive chop - mx = maximum(abs,coeffs) - if mx == 0 - return 0 - end - for k=n:-1:1 - if abs(coeffs[k]) > tol*mx - return k - end - end - return 0 - end - - # Step 1: Convert COEFFS to a new monotonically nonincreasing - # vector ENVELOPE normalized to begin with the value 1. - - b = abs.(coeffs) - m = b[end]*fill(1.0,n) - for j = n-1:-1:1 - m[j] = max(b[j], m[j+1]); - end - if m[1] == 0 - return 1 - end - envelope = m/m[1]; - - plateauPoint = 0 - - for j = 2:n - j2 = round(Int,1.25*j + 5); - if j2 > n - # there is no plateau: exit - return n - end - e1 = envelope[j] - e2 = envelope[j2] - r = 3*(1 - log(e1)/log(tol)) - plateau = (e1 == 0) || (e2/e1 > r) - if plateau - # a plateau has been found: go to Step 3 - plateauPoint = j - 1 - break - end - end - - # Step 3: fix CUTOFF at a point where ENVELOPE, plus a linear function - # included to bias the result towards the left end, is minimal. - # - - if plateauPoint ≠ 0 && envelope[plateauPoint] == 0 - return plateauPoint - end - - j3 = sum(envelope .≥ tol^(7/6)) - if j3 < j2 - j2 = j3 + 1 - envelope[j2] = tol^(7/6) - end - cc = log10.(envelope[1:j2]) - cc .+= range(0, stop=(-1/3)*log10(tol), length=j2) - d = argmin(cc) - return max(d - 1, 1) -end diff --git a/src/Multivariate/LowRankFun.jl b/src/Multivariate/LowRankFun.jl deleted file mode 100644 index 43822fca..00000000 --- a/src/Multivariate/LowRankFun.jl +++ /dev/null @@ -1,426 +0,0 @@ -## -# LowRankFun represents f(x,y) by r column and row slices stored as Funs. -## - -export LowRankFun - -""" -`LowRankFun` gives an approximation to a bivariate function in low rank form. -""" -mutable struct LowRankFun{S<:Space,M<:Space,SS<:AbstractProductSpace,T<:Number} <: BivariateFun{T} - A::Vector{VFun{S,T}} - B::Vector{VFun{M,T}} - space::SS - - function LowRankFun{S,M,SS,T}(A::Vector{VFun{S,T}}, - B::Vector{VFun{M,T}}, - space::SS) where {S,M,SS,T} - @assert length(A) == length(B) - @assert length(A) > 0 - new{S,M,SS,T}(A,B,space) - end -end - - -LowRankFun(A::Vector{VFun{S,T}},B::Vector{VFun{M,T}},space::SS) where {S,M,SS,T} = - LowRankFun{S,M,SS,T}(A,B,space) -LowRankFun(A::Vector{VFun{S,T}},B::Vector{VFun{M,T}}) where {S,M,T} = - LowRankFun(A,B,space(first(A))⊗space(first(B))) -LowRankFun(A::Vector{VFun{S,T}},B::Vector{VFun{M,V}}) where {S,M,T,V} = - LowRankFun(convert(Vector{VFun{S,promote_type(T,V)}},A), - convert(Vector{VFun{M,promote_type(T,V)}},B), - space(first(A))⊗space(first(B))) -rank(f::LowRankFun) = length(f.A) -size(f::LowRankFun,k::Integer) = k==1 ? mapreduce(length,max,f.A) : mapreduce(length,max,f.B) -size(f::LowRankFun) = size(f,1),size(f,2) - -## Construction via a Matrix of coefficients - -function LowRankFun(X::Array{T},dx::S,dy::M) where {S<:Space,M<:Space,T<:Number} - U,Σ,V=svd(X) - m=max(1,count(s->s>10eps(T),Σ)) - - A=VFun{S,T}[Fun(dx,U[:,k].*sqrt(Σ[k])) for k=1:m] - B=VFun{M,T}[Fun(dy,conj(V[:,k]).*sqrt(Σ[k])) for k=1:m] - - LowRankFun(A,B) -end - -## Construction in a TensorSpace via a Vector of Funs - -function LowRankFun(X::Vector{VFun{S,T}},d::TensorSpace{SV,DD}) where {S,T,DD<:EuclideanDomain{2},SV} - @assert d[1] == space(X[1]) - LowRankFun(X,d[2]) -end - -function LowRankFun(X::Vector{VFun{S,T}},dy::Space) where {S,T} - m=mapreduce(ncoefficients,max,X) - M=zeros(T,m,length(X)) - for k=1:length(X) - M[1:ncoefficients(X[k]),k]=X[k].coefficients - end - - LowRankFun(M,space(X[1]),dy) -end - - -## Adaptive constructor selector - -function LowRankFun(fin::Function,dx::Space,dy::Space; - method::Symbol=:standard,tolerance::Union{Symbol,Tuple{Symbol,Number}}=:relative, - retmax::Bool=false,gridx::Integer=64,gridy::Integer=64,maxrank::Integer=100) - f = dynamic(fin) - if method == :standard - F,maxabsf=standardLowRankFun(f,dx,dy;tolerance=tolerance,gridx=gridx,gridy=gridy,maxrank=maxrank) - elseif method == :Cholesky - @assert domain(dx) == domain(dy) - if dx == dy - F,maxabsf=CholeskyLowRankFun(f,dx;tolerance=tolerance,grid=max(gridx,gridy),maxrank=maxrank) - else - G,maxabsf=CholeskyLowRankFun(f,dx;tolerance=tolerance,grid=max(gridx,gridy),maxrank=maxrank) - F=LowRankFun(copy(G.A),map(b->Fun(b,dy),G.B)) - end - end - retmax ? (F,maxabsf) : F -end - -## Standard adaptive construction - -function standardLowRankFun(f::Function,dx::Space,dy::Space;tolerance::Union{Symbol,Tuple{Symbol,Number}}=:relative,gridx::Integer=64,gridy::Integer=64,maxrank::Integer=100) - xy = checkpoints(dx⊗dy) - T = promote_type(eltype(f(first(xy)...)),prectype(dx),prectype(dy)) - - # We start by sampling on the given grid, find the approximate maximum and create the first rank-one approximation. - ptsx,ptsy=points(dx,gridx),points(dy,gridy) - X = zeros(T,gridx,gridy) - maxabsf,r=findapproxmax!(f,X,ptsx,ptsy,gridx,gridy) - if maxabsf < eps(zero(T))/eps(T) return LowRankFun([Fun(dx,[zero(T)])],[Fun(dy,[zero(T)])]),maxabsf end - a,b=Fun(x->f(x,r[2]),dx),Fun(y->f(r[1],y),dy) - - # If necessary, we resize the grid to be at least as large as the - # lengths of the first row and column Funs and we recompute the values of X. - if gridx < ncoefficients(a) || gridy < ncoefficients(b) - gridx,gridy = max(gridx,ncoefficients(a)),max(gridy,ncoefficients(b)) - ptsx,ptsy=points(dx,gridx),points(dy,gridy) - X = zeros(T,gridx,gridy) - maxabsf,r=findapproxmax!(f,X,ptsx,ptsy,gridx,gridy) - a,b=Fun(x->f(x,r[2]),dx),Fun(y->f(r[1],y),dy) - end - - A,B=typeof(a)[],typeof(b)[] - if tolerance == :relative - tol = 100maxabsf*eps(T) - elseif tolerance[1] == :absolute - tol = 100*tolerance[2]*eps(T) - end - tol10 = tol/10 - Avals,Bvals = zeros(T,gridx),zeros(T,gridy) - p₁,p₂ = plan_transform(dx,Avals),plan_transform(dy,Bvals) - - # Eat, drink, subtract rank-one, repeat. - for k=1:maxrank - if (norm(a.coefficients,Inf) < tol || norm(b.coefficients,Inf) < tol) - return LowRankFun(A,B),maxabsf - end - A,B =[A;a/sqrt(abs(a(r[1])))],[B;b/(sqrt(abs(b(r[2])))*sign(b(r[2])))] - r=findapproxmax!(A[k],B[k],X,ptsx,ptsy,gridx,gridy) - Ar,Br=evaluate(A,r[1]),evaluate(B,r[2]) - for i=1:gridx - @inbounds Avals[i] = f(ptsx[i],r[2]) - end - for j=1:gridy - @inbounds Bvals[j] = f(r[1],ptsy[j]) - end - a,b = Fun(dx,p₁*Avals) - dotu(Br,A),Fun(dy,p₂*Bvals) - dotu(Ar,B) - chop!(a,tol10),chop!(b,tol10) - end - @warn "Maximum rank of " * string(maxrank) * " reached" - return LowRankFun(A,B),maxabsf -end - -## Adaptive Cholesky decomposition, when f is Hermitian positive (negative) definite - -function CholeskyLowRankFun(f::Function,dx::Space;tolerance::Union{Symbol,Tuple{Symbol,Number}}=:relative,grid::Integer=64,maxrank::Integer=100) - xy = checkpoints(dx⊗dx) - T = promote_type(eltype(f(first(xy)...)),prectype(dx)) - - # We start by sampling on the given grid, find the approximate maximum and create the first rank-one approximation. - pts=points(dx,grid) - X = zeros(T,grid) - maxabsf,r=findcholeskyapproxmax!(f,X,pts,grid) - if maxabsf < eps(zero(T))/eps(T) return LowRankFun([Fun(dx,[zero(T)])],[Fun(dx,[zero(T)])]),maxabsf end - a=Fun(x->f(x,r),dx) - - # If necessary, we resize the grid to be at least as large as the - # ncoefficients of the first row/column Fun and we recompute the values of X. - if grid < ncoefficients(a) - grid = max(grid,ncoefficients(a)) - pts=points(dx,grid) - X = zeros(T,grid) - maxabsf,r=findcholeskyapproxmax!(f,X,pts,grid) - a=Fun(x->f(x,r),dx) - end - - A,B=typeof(a)[],typeof(a)[] - if tolerance == :relative - tol = 100maxabsf*eps(T) - elseif tolerance[1] == :absolute - tol = 100*tolerance[2]*eps(T) - end - tol10 = tol/10 - Avals = zeros(T,grid) - p₁ = plan_transform(dx,Avals) - - # Eat, drink, subtract rank-one, repeat. - for k=1:maxrank - if norm(a.coefficients,Inf) < tol return LowRankFun(A,B),maxabsf end - A,B = [A;a/sqrt(abs(a(r)))],[B;a/(sqrt(abs(a(r)))*sign(a(r)))] - r=findcholeskyapproxmax!(A[k],B[k],X,pts,grid) - Br=evaluate(B,r) - for i=1:grid - @inbounds Avals[i] = f(pts[i],r) - end - a = Fun(dx,p₁*Avals) - dotu(Br,A) - chop!(a,tol10) - end - @warn "Maximum rank of " * string(maxrank) * " reached" - return LowRankFun(A,B),maxabsf -end - - -## Construction via TensorSpaces and ProductDomains - -LowRankFun(f::Function,S::TensorSpace{SV,DD,RR};kwds...) where {SV,DD<:EuclideanDomain{2},RR} = - LowRankFun(dynamic(f),factor(S,1),factor(S,2);kwds...) -LowRankFun(f::Function,dx::Domain,dy::Domain;kwds...) = - LowRankFun(dynamic(f),Space(dx),Space(dy);kwds...) -LowRankFun(f::Function,d::ProductDomain;kwds...) = - LowRankFun(dynamic(f),d.domains...;kwds...) - -LowRankFun(f::Function;kwds...) = LowRankFun(dynamic(f),ChebyshevInterval(),ChebyshevInterval();kwds...) - -## Construction from values - -LowRankFun(A::Array{T}) where {T<:Number} = LowRankFun(A,Interval{T}(),Interval{T}()) -LowRankFun(c::Number,etc...) = LowRankFun((x,y)->c,etc...) - -## Construction from other LowRankFuns - -LowRankFun(f::LowRankFun,d1::IntervalOrSegment,d2::IntervalOrSegment) = - LowRankFun(map(g->Fun(d1,g.coefficients),f.A), - map(g->Fun(d2,g.coefficients),f.B)) -LowRankFun(f::LowRankFun) = LowRankFun(f,ChebyshevInterval(),ChebyshevInterval()) - - - -## Utilities - -function findapproxmax!(f::Function,X::AbstractMatrix,ptsx::AbstractVector,ptsy::Vector,gridx,gridy) - for j=1:gridy - ptsyj = ptsy[j] - @simd for k=1:gridx - @inbounds X[k,j]+=f(ptsx[k],ptsyj) - end - end - maxabsf,impt = findmaxabs(X) - imptple = CartesianIndices((gridx,gridy))[impt] - maxabsf,[ptsx[imptple[1]],ptsy[imptple[2]]] -end - -function findapproxmax!(A::Fun,B::Fun,X::AbstractMatrix,ptsx::Vector,ptsy::Vector,gridx,gridy) - Ax,By = A.(ptsx),B.(ptsy) - subtractrankone!(Ax,By,X,gridx,gridy) - maxabsf,impt = findmaxabs(X) - imptple = CartesianIndices((gridx,gridy))[impt] - [ptsx[imptple[1]],ptsy[imptple[2]]] -end - -function findcholeskyapproxmax!(f::Function,X::AbstractVector,pts::Vector,grid) - @simd for k=1:grid - @inbounds X[k]+=f(pts[k],pts[k]) - end - maxabsf,impt = findmaxabs(X) - maxabsf,pts[impt] -end - -function findcholeskyapproxmax!(A::Fun,B::Fun,X::Vector,pts::Vector,grid) - Ax,By = A.(pts),B.(pts) - subtractrankone!(Ax,By,X,grid) - maxabsf,impt = findmaxabs(X) - pts[impt] -end - -function subtractrankone!(A::AbstractVector,B::AbstractVector,X::AbstractMatrix,gridx::Int,gridy::Int) - for j=1:gridy - @inbounds Bj = B[j] - @simd for k=1:gridx - @inbounds X[k,j] -= A[k]*Bj - end - end -end - -function subtractrankone!(A::AbstractVector,B::AbstractVector,X::AbstractVector,grid::Int) - @simd for k=1:grid - @inbounds X[k] -= A[k]*B[k] - end -end - -## TODO: in Julia base? -function findmaxabs(a) - if isempty(a) - throw(ArgumentError("collection must be non-empty")) - end - m = abs(a[1]) - mi = 1 - for i in eachindex(a) - ai = abs(a[i]) - if ai > m || m!=m - m = ai - mi = i - end - end - return (m, mi) -end - -(f::LowRankFun)(x,y)=evaluate(f,x,y) - -domain(f::LowRankFun,k::Integer) = k==1 ? domain(first(f.A)) : domain(first(f.B)) -space(f::LowRankFun,k::Integer) = k==1 ? space(first(f.A)) : space(first(f.B)) -space(f::LowRankFun)=f.space - -transpose(f::LowRankFun{S,M,SS,T}) where {S,M,SS,T}=LowRankFun(f.B,f.A,transpose(space(f))) - -function values(f::LowRankFun) - xm=mapreduce(ncoefficients,max,f.A) - ym=mapreduce(ncoefficients,max,f.B) - ret=zeros(xm,ym) - for k=1:length(f.A) - ret+=values(pad(f.A[k],xm))*transpose(values(pad(f.B[k],ym))) - end - ret -end - -#TODO: this is inconsistent with 1D where it does canonical -function coefficients(f::LowRankFun) - xm=mapreduce(ncoefficients,max,f.A) - ym=mapreduce(ncoefficients,max,f.B) - ret=zeros(xm,ym) - for k=1:length(f.A) - ret+=pad(f.A[k].coefficients,xm)*transpose(pad(f.B[k].coefficients,ym)) - end - ret -end - -function coefficients(f::LowRankFun,n::Space,m::Space) - xm=mapreduce(ncoefficients,max,f.A) - ym=mapreduce(ncoefficients,max,f.B) - ret=zeros(xm,ym) - for k=1:length(f.A) - ret+=pad(coefficients(f.A[k],n),xm)*transpose(pad(coefficients(f.B[k],m),ym)) - end - ret -end - -function vecpoints(f::LowRankFun,k::Integer) - if k==1 - xm=mapreduce(ncoefficients,max,f.A) - points(space(first(f.A)),xm) - else - ym=mapreduce(ncoefficients,max,f.B) - points(space(first(f.B)),ym) - end -end - - -evaluate(A::Vector{T},B::Vector{M},x,y) where {T<:Fun,M<:Fun}=dotu(evaluate(A,x),evaluate(B,y)) -evaluate(A::Vector{T},B::Vector{M},x::AbstractVector,y::AbstractVector) where {T<:Fun,M<:Fun}=evaluate.(transpose(A),x)*evaluate.(B,transpose(y)) - -evaluate(f::LowRankFun,x,y)=evaluate(f.A,f.B,x,y) -evaluate(f::LowRankFun,::Colon,::Colon)=f -evaluate(f::LowRankFun,x::Number,::Colon)=dotu(f.B,evaluate(f.A,x)) - -function evaluate(f::LowRankFun,::Colon,y::Number) - m = maximum(map(ncoefficients,f.A)) - r=rank(f) - ret = zeros(m) - - for k=1:r - for j=1:ncoefficients(f.A[k]) - @inbounds ret[j] += f.A[k].coefficients[j]*f.B[k](y) - end - end - - Fun(first(f.A).space,ret) -end - -""" - f.(x::AbstractVector, y':::AbstractMatrix) -Fast evaluation of a LowRankFun on a cartesian grid x ⨂ y. -""" -function broadcast(f::LowRankFun, x::AbstractVector, y::AbstractMatrix) - ret = zeros(length(y), length(x)) - #println(InRed * "FROM BROADCAST" * InDefault) - for k = 1:rank(f) - ret += (f.A[k].(x) * f.B[k].(y))' - end - ret -end - -## Truncate -#TODO: should reduce rank if needed -chop(f::LowRankFun,tol)=LowRankFun(map(g->chop(g,tol),f.A),map(g->chop(g,tol),f.B),f.space) -function pad(f::LowRankFun,m::Integer,n::Integer) - A,B = deepcopy(f.A),deepcopy(f.B) - - for k=1:rank(f) - pad!(A[k],m);pad!(B[k],n) - end - - LowRankFun(A,B,f.space) -end - - -## Algebra - -for op = (:*,:/) - @eval ($op)(A::Array{T,1},c::Number) where {T<:Fun}=map(f->($op)(f,c),A) - @eval ($op)(f::LowRankFun,c::Number) = LowRankFun(($op)(f.A,c),f.B) - @eval ($op)(c::Number,f::LowRankFun) = LowRankFun(($op)(c,f.A),f.B) -end - -# Let K be a LowRankFun and f be a Fun. -# op(f,K) acts as operating in the x variable, and -# op(K,f) acts as operating in the y variable. - -for op = (:*,:/) - @eval ($op)(f::Fun,K::LowRankFun) = LowRankFun(($op).(f,K.A),K.B) - @eval ($op)(K::LowRankFun,f::Fun) = LowRankFun(K.A,($op).(K.B,f)) -end - -+(f::LowRankFun,g::LowRankFun) = LowRankFun([f.A;g.A],[f.B;g.B]) --(f::LowRankFun) = LowRankFun(-f.A,f.B) --(f::LowRankFun,g::LowRankFun) = f+(-g) - -## QR factorization of a LowRankFun - -function qr(f::LowRankFun) - sp,r = space(f),rank(f) - Q,R = qr(coefficients(f.A)) - BR = coefficients(f.B)*transpose(R) - LowRankFun(map(i->Fun(sp[1],Q[:,i]),1:r),map(i->Fun(sp[2],BR[:,i]),1:r),sp) -end - -## Special functions - -real(u::LowRankFun)=LowRankFun([map(real,u.A),map(imag,u.A)],[map(real,u.B),-map(imag,u.B)]) -imag(u::LowRankFun)=LowRankFun([map(real,u.A),map(imag,u.A)],[map(imag,u.B),map(real,u.B)]) - - -## Calculus - - -sum(g::LowRankFun)=dotu(map(sum,g.A),map(sum,g.B)) -sum(g::LowRankFun,n::Integer)=(n==1) ? dotu(g.B,map(sum,g.A)) : dotu(g.A,map(sum,g.B)) -cumsum(g::LowRankFun,n::Integer)=(n==1) ? LowRankFun(map(cumsum,g.A),copy(g.B)) : LowRankFun(copy(g.A),map(cumsum,g.B)) -differentiate(g::LowRankFun,n::Integer)=(n==1) ? LowRankFun(map(differentiate,g.A),copy(g.B)) : LowRankFun(copy(g.A),map(differentiate,g.B)) -integrate(g::LowRankFun,n::Integer)=(n==1) ? LowRankFun(map(integrate,g.A),copy(g.B)) : LowRankFun(copy(g.A),map(integrate,g.B)) diff --git a/src/Multivariate/Multivariate.jl b/src/Multivariate/Multivariate.jl deleted file mode 100644 index 54d7915f..00000000 --- a/src/Multivariate/Multivariate.jl +++ /dev/null @@ -1,99 +0,0 @@ -abstract type MultivariateFun{T,N} <: Function end -const BivariateFun{T} = MultivariateFun{T,2} - -export grad, lap, curl - -#implements coefficients/values/evaluate -space(f::MultivariateFun{T,N}) where {T,N}=mapreduce(k->space(f,k),⊗,1:N) -domain(f::MultivariateFun{T,N}) where {T,N}=mapreduce(k->domain(f,k),×,1:N) - -domain(f::MultivariateFun,k::Integer)=domain(space(f,k)) - -differentiate(u::BivariateFun,i::Integer,j::Integer) = - j==0 ? u : differentiate(differentiate(u,i),i,j-1) -grad(u::BivariateFun) = [differentiate(u,1),differentiate(u,2)] -lap(u::BivariateFun) = differentiate(u,1,2)+differentiate(u,2,2) -Base.div(u::AbstractVector{B}) where {B<:BivariateFun} = - differentiate(u[1],1)+differentiate(u[2],2) -curl(u::AbstractVector{B}) where {B<:BivariateFun} = differentiate(u[2],1)-differentiate(u[1],2) - -Base.chop(f::MultivariateFun) = chop(f,10eps()) -cfstype(::MultivariateFun{T}) where {T} = T -cfstype(::Type{MultivariateFun{T,N}}) where {T,N} = T -cfstype(::Type{MF}) where {MF<:MultivariateFun} = cfstype(supertype(MF)) - -include("VectorFun.jl") -include("TensorSpace.jl") -include("LowRankFun.jl") -include("ProductFun.jl") - - -arglength(f)=length(Base.uncompressed_ast(f.code.def).args[1]) - - - -## Convert between Fun and MultivariateFun -# need to chop extra zeros -Fun(f::ProductFun) = - Fun(space(f),chop!(fromtensor(space(f),coefficients(f)),0)) -Fun(f::ProductFun,sp::TensorSpace) = Fun(ProductFun(f,sp)) -Fun(f::LowRankFun) = Fun(ProductFun(f)) - - -Fun(f::MultivariateFun,sp::Space) = Fun(Fun(f),sp) - -Fun(f,d1::Domain,d2::Domain) = Fun(f,d1*d2) - -coefficients(f::BivariateFun,sp::TensorSpace)=coefficients(f,sp[1],sp[2]) - - - -points(f::BivariateFun,k...)=points(space(f),size(f,1),size(f,2),k...) - - -function *(vx::LowRankFun,u0::ProductFun) - ret=zero(space(u0)) - for k=1:length(vx.A) - a,b=vx.A[k],vx.B[k] - ret+=transpose(b*(transpose(a*u0))) - end - ret -end - -*(a::ProductFun,b::LowRankFun)=b*a -*(a::MultivariateFun,b::MultivariateFun)=LowRankFun(a)*ProductFun(b) - -for OP in (:+,:-,:*,:/) - @eval begin - $OP(f::Fun,g::MultivariateFun)=$OP(ProductFun(f),g) - $OP(f::MultivariateFun,g::Fun)=$OP(f,ProductFun(g)) - end -end - - -Base.sum(f::Fun{TS},k::Integer) where {TS<:TensorSpace}=sum(ProductFun(f),k) -Base.sum(f::Fun{TS}) where {TS<:TensorSpace}=sum(ProductFun(f)) - - -## kron -# TODO: generalize -function Base.kron(f::Fun,g::Fun) - sp=space(f)⊗space(g) - it=tensorizer(sp) - N=ncoefficients(f);M=ncoefficients(g) - cfs=Array{promote_type(cfstype(f),cfstype(g))}(undef,0) - for (k,j) in it - # Tensor product is N x M, so if we are outside - # the (N+M)th diagonal we have no more entries - if k+j > N+M - break - elseif k ≤ N && j ≤ M - push!(cfs,f.coefficients[k]*g.coefficients[j]) - else - push!(cfs,0) - end - end - Fun(sp,cfs) -end -Base.kron(f::Fun,g::Number) = kron(f,Fun(g)) -Base.kron(f::Number,g::Fun) = kron(Fun(f),g) diff --git a/src/Multivariate/ProductFun.jl b/src/Multivariate/ProductFun.jl deleted file mode 100644 index 4bafc1a2..00000000 --- a/src/Multivariate/ProductFun.jl +++ /dev/null @@ -1,346 +0,0 @@ -## -# ProductFun represents f(x,y) by Fun(.coefficients[k](x),.space[2])(y) -# where all coefficients are in the same space -## - -export ProductFun - -struct ProductFun{S<:UnivariateSpace,V<:UnivariateSpace,SS<:AbstractProductSpace,T} <: BivariateFun{T} - coefficients::Vector{VFun{S,T}} # coefficients are in x - space::SS -end - -ProductFun(cfs::Vector{VFun{S,T}},sp::AbstractProductSpace{Tuple{S,V},DD}) where {S<:UnivariateSpace,V<:UnivariateSpace,T<:Number,DD} = - ProductFun{S,V,typeof(sp),T}(cfs,sp) -ProductFun(cfs::Vector{VFun{S,T}},sp::AbstractProductSpace{Tuple{W,V},DD}) where {S<:UnivariateSpace,V<:UnivariateSpace, - W<:UnivariateSpace,T<:Number,DD} = - ProductFun{W,V,typeof(sp),T}(VFun{W,T}[Fun(cfs[k],columnspace(sp,k)) for k=1:length(cfs)],sp) - -Base.size(f::ProductFun,k::Integer) = - k==1 ? mapreduce(ncoefficients,max,f.coefficients) : length(f.coefficients) -Base.size(f::ProductFun) = (size(f,1),size(f,2)) - -## Construction in an AbstractProductSpace via a Matrix of coefficients - -function ProductFun(cfs::AbstractMatrix{T},sp::AbstractProductSpace{Tuple{S,V},DD}; - tol::Real=100eps(T),chopping::Bool=false) where {S<:UnivariateSpace,V<:UnivariateSpace,T<:Number,DD} - if chopping - ncfs,kend=norm(cfs,Inf),size(cfs,2) - if kend > 1 while isempty(chop(cfs[:,kend],ncfs*tol)) kend-=1 end end - ret=VFun{S,T}[Fun(columnspace(sp,k),chop(cfs[:,k],ncfs*tol)) for k=1:max(kend,1)] - ProductFun{S,V,typeof(sp),T}(ret,sp) - else - ret=VFun{S,T}[Fun(columnspace(sp,k),cfs[:,k]) for k=1:size(cfs,2)] - ProductFun{S,V,typeof(sp),T}(ret,sp) - end -end - -## Construction in a ProductSpace via a Vector of Funs - -function ProductFun(M::Vector{VFun{S,T}},dy::V) where {S<:UnivariateSpace,V<:UnivariateSpace,T<:Number} - funs=VFun{S,T}[Mk for Mk in M] - ProductFun{S,V,ProductSpace{S,V},T}(funs,ProductSpace(S[space(fun) for fun in funs],dy)) -end - -## Adaptive construction - -function ProductFun(f::Function,sp::AbstractProductSpace{Tuple{S,V}};tol=100eps()) where {S<:UnivariateSpace,V<:UnivariateSpace} - for n = 50:100:5000 - X = coefficients(ProductFun(dynamic(f),sp,n,n;tol=tol)) - if size(X,1)size(B,2) - B=pad(B,size(B,1),length(ccfs)) - end - B[k,1:length(ccfs)]=ccfs - for j=length(ccfs)+1:size(B,2) - B[k,j]=zero(T) - end - end - - B -end - -(f::ProductFun)(x,y) = evaluate(f,x,y) -(f::ProductFun)(x,y,z) = evaluate(f,x,y,z) - -coefficients(f::ProductFun,ox::TensorSpace) = coefficients(f,ox[1],ox[2]) - - - - -values(f::ProductFun{S,V,SS,T}) where {S,V,SS,T} = itransform!(space(f),coefficients(f)) - - -vecpoints(f::ProductFun{S,V,SS},k) where {S,V,SS<:TensorSpace} = points(f.space[k],size(f,k)) - -space(f::ProductFun) = f.space -columnspace(f::ProductFun,k) = columnspace(space(f),k) - -domain(f::ProductFun) = domain(f.space) -#domain(f::ProductFun,k)=domain(f.space,k) -canonicaldomain(f::ProductFun) = canonicaldomain(space(f)) - - - -function canonicalevaluate(f::ProductFun{S,V,SS,T},x::Number,::Colon) where {S,V,SS,T} - cd = canonicaldomain(f) - Fun(setdomain(factor(space(f),2),factor(cd,2)), - T[setdomain(fc,factor(cd,1))(x) for fc in f.coefficients]) -end -canonicalevaluate(f::ProductFun,x::Number,y::Number) = canonicalevaluate(f,x,:)(y) -canonicalevaluate(f::ProductFun{S,V,SS},x::Colon,y::Number) where {S,V,SS<:TensorSpace} = - evaluate(transpose(f),y,:) # doesn't make sense For general product fon without specifying space - -canonicalevaluate(f::ProductFun,xx::AbstractVector,yy::AbstractVector) = - transpose(hcat([evaluate(f,x,:)(yy) for x in xx]...)) - - -evaluate(f::ProductFun,x,y) = canonicalevaluate(f,tocanonical(f,x,y)...) -evaluate(f::ProductFun,x,y,z) = canonicalevaluate(f,tocanonical(f,x,y,z)...) - -# TensorSpace does not use map -evaluate(f::ProductFun{S,V,SS,T},x::Number,::Colon) where {S<:UnivariateSpace,V<:UnivariateSpace,SS<:TensorSpace,T} = - Fun(factor(space(f),2),T[g(x) for g in f.coefficients]) - -evaluate(f::ProductFun{S,V,SS,T},x::Number,y::Number) where {S<:UnivariateSpace,V<:UnivariateSpace,SS<:TensorSpace,T} = - evaluate(f,x,:)(y) - - -evaluate(f::ProductFun,x) = evaluate(f,x...) - -*(c::Number,f::F) where {F<:ProductFun} = F(c*f.coefficients,f.space) -*(f::ProductFun,c::Number) = c*f - - -function chop(f::ProductFun{S},es...) where S - kend=size(f,2) - while kend > 1 && isempty(chop(f.coefficients[kend].coefficients,es...)) - kend-=1 - end - ret=VFun{S,cfstype(f)}[Fun(space(f.coefficients[k]),chop(f.coefficients[k].coefficients,es...)) for k=1:max(kend,1)] - - typeof(f)(ret,f.space) -end - - -##TODO: following assumes f is never changed....maybe should be deepcopy? -function +(f::F,c::Number) where F<:ProductFun - cfs=copy(f.coefficients) - cfs[1]+=c - F(cfs,f.space) -end -+(c::Number,f::ProductFun) = f+c --(f::ProductFun,c::Number) = f+(-c) --(c::Number,f::ProductFun) = c+(-f) - - -function +(f::ProductFun,g::ProductFun) - if f.space == g.space - if size(f,2) >= size(g,2) - @assert f.space==g.space - cfs = copy(f.coefficients) - for k=1:size(g,2) - cfs[k]+=g.coefficients[k] - end - - ProductFun(cfs,f.space) - else - g+f - end - else - s=conversion_type(f.space,g.space) - ProductFun(f,s)+ProductFun(g,s) - end -end - --(f::ProductFun) = (-1)*f --(f::ProductFun,g::ProductFun) = f+(-g) - -*(B::Fun,f::ProductFun) = ProductFun(map(c->B*c,f.coefficients),space(f)) -*(f::ProductFun,B::Fun) = transpose(B*transpose(f)) - - -LowRankFun(f::ProductFun{S,V,SS}) where {S,V,SS<:TensorSpace} = LowRankFun(f.coefficients,factor(space(f),2)) -LowRankFun(f::Fun) = LowRankFun(ProductFun(f)) - -function differentiate(f::ProductFun{S,V,SS},j::Integer) where {S,V,SS<:TensorSpace} - if j==1 - df=map(differentiate,f.coefficients) - ProductFun(df,space(first(df)),factor(space(f),2)) - else - transpose(differentiate(transpose(f),1)) - end -end - -# If the transpose of the space exists, then the transpose of the ProductFun exists -Base.transpose(f::ProductFun{S,V,SS,T}) where {S,V,SS,T} = - ProductFun(transpose(coefficients(f)),transpose(space(f))) - - - - - -for op in (:(Base.sin),:(Base.cos)) - @eval ($op)(f::ProductFun) = - Fun(space(f),transform!(space(f),$op(values(pad(f,size(f,1)+20,size(f,2)))))) -end - -^(f::ProductFun,k::Integer) = - Fun(space(f),transform!(space(f),values(pad(f,size(f,1)+20,size(f,2))).^k)) - -for op = (:(Base.real),:(Base.imag),:(Base.conj)) - @eval ($op)(f::ProductFun{S,V,SS}) where {S,V<:RealSpace,SS<:TensorSpace} = - ProductFun(map($op,f.coefficients),space(f)) -end - -#For complex bases -Base.real(f::ProductFun{S,V,SS}) where {S,V,SS<:TensorSpace} = - transpose(real(transpose(ProductFun(real(u.coefficients),space(u)))))-transpose(imag(transpose(ProductFun(imag(u.coefficients),space(u))))) -Base.imag(f::ProductFun{S,V,SS}) where {S,V,SS<:TensorSpace} = - transpose(real(transpose(ProductFun(imag(u.coefficients),space(u)))))+transpose(imag(transpose(ProductFun(real(u.coefficients),space(u))))) - - - -## Call LowRankFun version -# TODO: should cumsum and integrate return TensorFun or lowrankfun? -for op in (:(Base.sum),:(Base.cumsum),:integrate) - @eval $op(f::ProductFun{S,V,SS},n...) where {S,V,SS<:TensorSpace} = $op(LowRankFun(f),n...) -end - - -## ProductFun transform - -# function transform{ST<:Space,N<:Number}(::Type{N},S::Vector{ST},T::Space,V::AbstractMatrix) -# @assert length(S)==size(V,2) -# # We assume all S spaces have same domain/points -# C=Vector{N}(size(V)...) -# for k=1:size(V,1) -# C[k,:]=transform(T,vec(V[k,:])) -# end -# for k=1:size(C,2) -# C[:,k]=transform(S[k],C[:,k]) -# end -# C -# end -# transform{ST<:Space,N<:Real}(S::Vector{ST},T::Space{Float64},V::AbstractMatrix{N})=transform(Float64,S,T,V) -# transform{ST<:Space}(S::Vector{ST},T::Space,V::AbstractMatrix)=transform(Complex{Float64},S,T,V) - - - - -for op in (:tocanonical,:fromcanonical) - @eval $op(f::ProductFun,x...) = $op(space(f),x...) -end diff --git a/src/Multivariate/TensorSpace.jl b/src/Multivariate/TensorSpace.jl deleted file mode 100644 index 5f1d108d..00000000 --- a/src/Multivariate/TensorSpace.jl +++ /dev/null @@ -1,587 +0,0 @@ - -export TensorSpace, ⊗, ProductSpace, factor, factors, nfactors - -# SV is a tuple of d spaces -abstract type AbstractProductSpace{SV,DD,RR} <: Space{DD,RR} end - - -spacetype(::AbstractProductSpace{SV},k) where {SV} = SV.parameters[k] - - -##### Tensorizer -# This gives the map from coefficients to the -# tensor entry of a tensor product of d spaces -# findfirst is overriden to get efficient inverse -# blocklengths is a tuple of block lengths, e.g., Chebyshev()^2 -# would be Tensorizer((1:∞,1:∞)) -# ConstantSpace() ⊗ Chebyshev() -# would be Tensorizer((1:1,1:∞)) -# and Chebyshev() ⊗ ArraySpace([Chebyshev(),Chebyshev()]) -# would be Tensorizer((1:∞,2:2:∞)) - - -struct Tensorizer{DMS<:Tuple} - blocks::DMS -end - -const TrivialTensorizer{d} = Tensorizer{NTuple{d,Ones{Int,1,Tuple{OneToInf{Int}}}}} - -Base.eltype(a::Tensorizer) = NTuple{length(a.blocks),Int} -Base.eltype(::Tensorizer{NTuple{d,T}}) where {d,T} = NTuple{d,Int} -dimensions(a::Tensorizer) = map(sum,a.blocks) -Base.length(a::Tensorizer) = mapreduce(sum,*,a.blocks) - -# (blockrow,blockcol), (subrow,subcol), (rowshift,colshift), (numblockrows,numblockcols), (itemssofar, length) -start(a::Tensorizer{Tuple{AA,BB}}) where {AA,BB} = (1,1), (1,1), (0,0), (a.blocks[1][1],a.blocks[2][1]), (0,length(a)) - -function next(a::Tensorizer{Tuple{AA,BB}}, ((K,J), (k,j), (rsh,csh), (n,m), (i,tot))) where {AA,BB} - ret = k+rsh,j+csh - if k==n && j==m # end of block - if J == 1 || K == length(a.blocks[1]) # end of new block - B = K+J # next block - J = min(B, length(a.blocks[2]))::Int # don't go past new block - K = B-J+1 # K+J-1 == B - else - K,J = K+1,J-1 - end - k = j = 1 - if i+1 < tot # not done yet - n,m = a.blocks[1][K], a.blocks[2][J] - rsh,csh = sum(a.blocks[1][1:K-1]), sum(a.blocks[2][1:J-1]) - end - elseif k==n - k = 1 - j += 1 - else - k += 1 - end - ret, ((K,J), (k,j), (rsh,csh), (n,m), (i+1,tot)) -end - - -done(a::Tensorizer, ((K,J), (k,j), (rsh,csh), (n,m), (i,tot))) = i ≥ tot - -iterate(a::Tensorizer) = next(a, start(a)) -function iterate(a::Tensorizer, st) - done(a,st) && return nothing - next(a, st) -end - - -cache(a::Tensorizer) = CachedIterator(a) - -function Base.findfirst(::TrivialTensorizer{2},kj::Tuple{Int,Int}) - k,j=kj - if k > 0 && j > 0 - n=k+j-2 - (n*(n+1))÷2+k - else - 0 - end -end - -function Base.findfirst(sp::Tensorizer{Tuple{<:AbstractFill{S},<:AbstractFill{T}}},kj::Tuple{Int,Int}) where {S,T} - k,j=kj - - if k > 0 && j > 0 - a,b = getindex_value(sp.blocks[1]),getindex_value(sp.blocks[2]) - kb1,kr = fldmod(k-1,a) - jb1,jr = fldmod(j-1,b) - nb=kb1+jb1 - a*b*(nb*(nb+1)÷2+kb1)+a*jr+kr+1 - else - 0 - end -end - -# which block of the tensor -# equivalent to sum of indices -1 - -# block(it::Tensorizer,k) = Block(sum(it[k])-length(it.blocks)+1) -block(ci::CachedIterator{T,TrivialTensorizer{2}},k::Int) where {T} = - Block(k == 0 ? 0 : sum(ci[k])-length(ci.iterator.blocks)+1) - -block(::TrivialTensorizer{2},n::Int) = - Block(floor(Integer,sqrt(2n) + 1/2)) - -block(sp::Tensorizer{<:Tuple{<:AbstractFill{S},<:AbstractFill{T}}},n::Int) where {S,T} = - Block(floor(Integer,sqrt(2floor(Integer,(n-1)/(getindex_value(sp.blocks[1])*getindex_value(sp.blocks[2])))+1) + 1/2)) -_cumsum(x) = cumsum(x) -_cumsum(x::Number) = x -block(sp::Tensorizer,k::Int) = Block(findfirst(x->x≥k, _cumsum(blocklengths(sp)))) -block(sp::CachedIterator,k::Int) = block(sp.iterator,k) - -blocklength(it,k) = blocklengths(it)[k] -blocklength(it,k::Block) = blocklength(it,k.n[1]) -blocklength(it,k::BlockRange) = blocklength(it,Int.(k)) - -blocklengths(::TrivialTensorizer{2}) = 1:∞ - - - -blocklengths(it::Tensorizer) = tensorblocklengths(it.blocks...) -blocklengths(it::CachedIterator) = blocklengths(it.iterator) - -function getindex(it::TrivialTensorizer{2},n::Integer) - m=block(it,n) - p=findfirst(it,(1,m)) - j=1+n-p - j,m-j+1 -end - -# could be cleaned up using blocks -function getindex(it::Tensorizer{<:Tuple{<:AbstractFill{S},<:AbstractFill{T}}},n::Integer) where {S,T} - a,b = getindex_value(it.blocks[1]),getindex_value(it.blocks[2]) - nb1,nr = fldmod(n-1,a*b) # nb1 = "nb" - 1, i.e. using zero-base - m1=block(it,n).n[1]-1 - pb1=fld(findfirst(it,(1,b*m1+1))-1,a*b) - jb1=nb1-pb1 - kr1,jr1 = fldmod(nr,a) - b*jb1+jr1+1,a*(m1-jb1)+kr1+1 -end - - -blockstart(it,K)::Int = K==1 ? 1 : sum(blocklengths(it)[1:K-1])+1 -blockstop(it,::Infinity) = ∞ -_K_sum(bl::AbstractVector, K) = sum(bl[1:K]) -_K_sum(bl::Integer, K) = bl -blockstop(it, K)::Int = _K_sum(blocklengths(it), K) - -blockstart(it,K::Block) = blockstart(it,K.n[1]) -blockstop(it,K::Block) = blockstop(it,K.n[1]) - - -blockrange(it,K) = blockstart(it,K):blockstop(it,K) -blockrange(it,K::BlockRange) = blockstart(it,first(K)):blockstop(it,last(K)) - - - - -# convert from block, subblock to tensor -subblock2tensor(rt::TrivialTensorizer{2},K,k) = - (k,K.n[1]-k+1) - -subblock2tensor(rt::CachedIterator{II,TrivialTensorizer{2}},K,k) where {II} = - (k,K.n[1]-k+1) - - -subblock2tensor(rt::CachedIterator,K,k) = rt[blockstart(rt,K)+k-1] - -# tensorblocklengths gives calculates the block sizes of each tensor product -# Tensor product degrees are taken to be the sum of the degrees -# a degree is which block you are in - - -tensorblocklengths(a) = a # a single block is not modified -tensorblocklengths(a, b) = conv(a,b) -tensorblocklengths(a,b,c,d...) = tensorblocklengths(tensorblocklengths(a,b),c,d...) - - -# TensorSpace -# represents the tensor product of several subspaces -""" - TensorSpace(a::Space,b::Space) - -represents a tensor product of two 1D spaces `a` and `b`. -The coefficients are interlaced in lexigraphical order. - -For example, consider -```julia -Fourier()*Chebyshev() # returns TensorSpace(Fourier(),Chebyshev()) -``` -This represents functions on `[-π,π) x [-1,1]`, using the Fourier basis for the first argument -and Chebyshev basis for the second argument, that is, `φ_k(x)T_j(y)`, where -``` -φ_0(x) = 1, -φ_1(x) = sin x, -φ_2(x) = cos x, -φ_3(x) = sin 2x, -φ_4(x) = cos 2x -… -``` -By Choosing `(k,j)` appropriately, we obtain a single basis: -``` -φ_0(x)T_0(y) (= 1), -φ_0(x)T_1(y) (= y), -φ_1(x)T_0(y) (= sin x), -φ_0(x)T_2(y), … -``` -""" -struct TensorSpace{SV,D,R} <:AbstractProductSpace{SV,D,R} - spaces::SV -end - -tensorizer(sp::TensorSpace) = Tensorizer(map(blocklengths,sp.spaces)) -blocklengths(S::TensorSpace) = tensorblocklengths(map(blocklengths,S.spaces)...) - - -# the evaluation is *, so the type will be the same as * -# However, this fails for some any types -tensor_eval_type(a,b) = Base.promote_op(*,a,b) -tensor_eval_type(::Type{Vector{Any}},::Type{Vector{Any}}) = Vector{Any} -tensor_eval_type(::Type{Vector{Any}},_) = Vector{Any} -tensor_eval_type(_,::Type{Vector{Any}}) = Vector{Any} - - -TensorSpace(sp::Tuple) = - TensorSpace{typeof(sp),typeof(mapreduce(domain,×,sp)), - mapreduce(rangetype,(a,b)->tensor_eval_type(a,b),sp)}(sp) - - -dimension(sp::TensorSpace) = mapreduce(dimension,*,sp.spaces) - -for OP in (:spacescompatible,:(==)) - @eval $OP(A::TensorSpace{SV,D,R},B::TensorSpace{SV,D,R}) where {SV,D,R} = - all(Bool[$OP(A.spaces[k],B.spaces[k]) for k=1:length(A.spaces)]) -end - -canonicalspace(T::TensorSpace) = TensorSpace(map(canonicalspace,T.spaces)) - - - -TensorSpace(A...) = TensorSpace(tuple(A...)) -TensorSpace(A::ProductDomain) = TensorSpace(tuple(map(Space,A.domains)...)) -⊗(A::TensorSpace,B::TensorSpace) = TensorSpace(A.spaces...,B.spaces...) -⊗(A::TensorSpace,B::Space) = TensorSpace(A.spaces...,B) -⊗(A::Space,B::TensorSpace) = TensorSpace(A,B.spaces...) -⊗(A::Space,B::Space) = TensorSpace(A,B) - -domain(f::TensorSpace) = ×(domain.(f.spaces)...) -Space(sp::ProductDomain) = TensorSpace(sp) - -setdomain(sp::TensorSpace, d::ProductDomain) = TensorSpace(setdomain.(factors(sp), factors(d))) - -*(A::Space, B::Space) = A⊗B -^(A::Space, p::Integer) = p == 1 ? A : A*A^(p-1) - - -## TODO: generalize -components(sp::TensorSpace{Tuple{S1,S2}}) where {S1<:Space{D,R},S2} where {D,R<:AbstractArray} = - [s ⊗ sp.spaces[2] for s in components(sp.spaces[1])] - -components(sp::TensorSpace{Tuple{S1,S2}}) where {S1,S2<:Space{D,R}} where {D,R<:AbstractArray} = - [sp.spaces[1] ⊗ s for s in components(sp.spaces[2])] - -Base.size(sp::TensorSpace{Tuple{S1,S2}}) where {S1<:Space{D,R},S2} where {D,R<:AbstractArray} = - size(sp.spaces[1]) - -Base.size(sp::TensorSpace{Tuple{S1,S2}}) where {S1,S2<:Space{D,R}} where {D,R<:AbstractArray} = - size(sp.spaces[2]) - -# TODO: Generalize to higher dimensions -getindex(sp::TensorSpace{Tuple{S1,S2}},k::Integer) where {S1<:Space{D,R},S2} where {D,R<:AbstractArray} = - sp.spaces[1][k] ⊗ sp.spaces[2] - -getindex(sp::TensorSpace{Tuple{S1,S2}},k::Integer) where {S1,S2<:Space{D,R}} where {D,R<:AbstractArray} = - sp.spaces[1] ⊗ sp.spaces[2][k] - - -length(sp::TensorSpace{Tuple{S1,S2}}) where {S1<:Space{D,R},S2} where {D,R<:AbstractArray} = - length(sp.spaces[1]) - -length(sp::TensorSpace{Tuple{S1,S2}}) where {S1,S2<:Space{D,R}} where {D,R<:AbstractArray} = - length(sp.spaces[2]) - - -iterate(sp::TensorSpace{Tuple{S1,S2}},k...) where {S1<:Space{D,R},S2} where {D,R<:AbstractArray} = - iterate(components(sp),k...) - -iterate(sp::TensorSpace{Tuple{S1,S2}},k...) where {S1,S2<:Space{D,R}} where {D,R<:AbstractArray} = - iterate(components(sp),k...) - - -# every column is in the same space for a TensorSpace -# TODO: remove -columnspace(S::TensorSpace,_) = S.spaces[1] - - -struct ProductSpace{S<:Space,V<:Space,D,R} <: AbstractProductSpace{Tuple{S,V},D,R} - spacesx::Vector{S} - spacey::V -end - -ProductSpace(spacesx::Vector,spacey) = - ProductSpace{eltype(spacesx),typeof(spacey),typeof(mapreduce(domain,×,sp)), - mapreduce(s->eltype(domain(s)),promote_type,sp)}(spacesx,spacey) - -# TODO: This is a weird definition -⊗(A::Vector{S},B::Space) where {S<:Space} = ProductSpace(A,B) -domain(f::ProductSpace) = domain(f.spacesx[1])×domain(f.spacesy) - - -nfactors(d::AbstractProductSpace) = length(d.spaces) -factors(d::AbstractProductSpace) = d.spaces -factor(d::AbstractProductSpace,k) = factors(d)[k] - - -isambiguous(A::TensorSpace) = isambiguous(A.spaces[1]) || isambiguous(A.spaces[2]) - - -Base.transpose(d::TensorSpace) = TensorSpace(d.spaces[2],d.spaces[1]) - - - - - -## Transforms - -for (plan, plan!, Typ) in ((:plan_transform, :plan_transform!, :TransformPlan), - (:plan_itransform, :plan_itransform!, :ITransformPlan)) - @eval begin - $plan!(S::TensorSpace, M::AbstractMatrix) = $Typ(S,(($plan(S.spaces[1],size(M,1)),size(M,1)), - ($plan(S.spaces[2],size(M,2)),size(M,2))), - Val{true}) - - function *(T::$Typ{<:Any,<:TensorSpace,true}, M::AbstractMatrix) - n=size(M,1) - - for k=1:size(M,2) - M[:,k]=T.plan[1][1]*M[:,k] - end - for k=1:n - M[k,:]=T.plan[2][1]*M[k,:] - end - M - end - - function *(T::$Typ{TT,SS,false},v::AbstractVector) where {SS<:TensorSpace,TT} - P = $Typ(T.space,T.plan,Val{true}) - P*AbstractVector{rangetype(SS)}(v) - end - end -end - -function plan_transform(sp::TensorSpace, ::Type{T}, n::Integer) where {T} - NM=n - if isfinite(dimension(sp.spaces[1])) && isfinite(dimension(sp.spaces[2])) - N,M=dimension(sp.spaces[1]),dimension(sp.spaces[2]) - elseif isfinite(dimension(sp.spaces[1])) - N=dimension(sp.spaces[1]) - M=NM÷N - elseif isfinite(dimension(sp.spaces[2])) - M=dimension(sp.spaces[2]) - N=NM÷M - else - N=M=round(Int,sqrt(n)) - end - - TransformPlan(sp,((plan_transform(sp.spaces[1],T,N),N), - (plan_transform(sp.spaces[2],T,M),M)), - Val{false}) -end - -function plan_transform!(sp::TensorSpace, ::Type{T}, n::Integer) where {T} - P = plan_transform(sp, T, n) - TransformPlan(sp, P.plan, Val{true}) -end - -plan_transform(sp::TensorSpace, v::AbstractVector) = plan_transform(sp,eltype(v),length(v)) -plan_transform!(sp::TensorSpace, v::AbstractVector) = plan_transform!(sp,eltype(v),length(v)) - -function plan_itransform(sp::TensorSpace, v::AbstractVector{T}) where {T} - N,M = size(totensor(sp, v)) # wasteful - ITransformPlan(sp,((plan_itransform(sp.spaces[1],T,N),N), - (plan_itransform(sp.spaces[2],T,M),M)), - Val{false}) -end - - -function *(T::TransformPlan{TT,<:TensorSpace,true},v::AbstractVector) where TT # need where TT - N,M = T.plan[1][2],T.plan[2][2] - V=reshape(v,N,M) - fromtensor(T.space,T*V) -end - -*(T::ITransformPlan{TT,<:TensorSpace,true},v::AbstractVector) where TT = - vec(T*totensor(T.space,v)) - - -## points - -points(d::Union{EuclideanDomain{2},BivariateSpace},n,m) = points(d,n,m,1),points(d,n,m,2) - -function points(d::BivariateSpace,n,m,k) - ptsx=points(columnspace(d,1),n) - ptst=points(factor(d,2),m) - - promote_type(eltype(ptsx),eltype(ptst))[fromcanonical(d,x,t)[k] for x in ptsx, t in ptst] -end - - - - -## Fun routines - -fromtensor(S::Space,M::AbstractMatrix) = fromtensor(tensorizer(S),M) -totensor(S::Space,M::AbstractVector) = totensor(tensorizer(S),M) - -# we only copy upper triangular of coefficients -function fromtensor(it::Tensorizer,M::AbstractMatrix) - n,m=size(M) - ret=zeros(eltype(M),blockstop(it,max(n,m))) - k = 1 - for (K,J) in it - if k > length(ret) - break - end - if K ≤ n && J ≤ m - ret[k] = M[K,J] - end - k += 1 - end - ret -end - - -function totensor(it::Tensorizer,M::AbstractVector) - n=length(M) - B=block(it,n) - ds = dimensions(it) - - ret=zeros(eltype(M),sum(it.blocks[1][1:min(B.n[1],length(it.blocks[1]))]), - sum(it.blocks[2][1:min(B.n[1],length(it.blocks[2]))])) - k=1 - for (K,J) in it - if k > n - break - end - ret[K,J] = M[k] - k += 1 - end - ret -end - -for OP in (:block,:blockstart,:blockstop) - @eval begin - $OP(s::TensorSpace, ::Infinity) = ∞ - $OP(s::TensorSpace, M::Block) = $OP(tensorizer(s),M) - $OP(s::TensorSpace, M) = $OP(tensorizer(s),M) - end -end - -function points(sp::TensorSpace,n) - pts=Array{float(eltype(domain(sp)))}(undef,0) - a,b = sp.spaces - if isfinite(dimension(a)) && isfinite(dimension(b)) - N,M=dimension(a),dimension(b) - elseif isfinite(dimension(a)) - N=dimension(a) - M=n÷N - elseif isfinite(dimension(b)) - M=dimension(b) - N=n÷M - else - N=M=round(Int,sqrt(n)) - end - - for y in points(b,M), - x in points(a,N) - push!(pts,Vec(x...,y...)) - end - pts -end - - -itransform(sp::TensorSpace,cfs) = vec(itransform!(sp,coefficientmatrix(Fun(sp,cfs)))) - -evaluate(f::AbstractVector,S::AbstractProductSpace,x) = ProductFun(totensor(S,f),S)(x...) -evaluate(f::AbstractVector,S::AbstractProductSpace,x,y) = ProductFun(totensor(S,f),S)(x,y) - - - -coefficientmatrix(f::Fun{<:AbstractProductSpace}) = totensor(space(f),f.coefficients) - - - -#TODO: Implement -# function ∂(d::TensorSpace{<:IntervalOrSegment{Float64}}) -# @assert length(d.spaces) ==2 -# PiecewiseSpace([d[1].a+im*d[2],d[1].b+im*d[2],d[1]+im*d[2].a,d[1]+im*d[2].b]) -# end - - -union_rule(a::TensorSpace,b::TensorSpace) = TensorSpace(map(union,a.spaces,b.spaces)) - - - -## Convert from 1D to 2D - - -# function isconvertible{T,TT}(sp::Space{Segment{Vec{2,TT}},<:Real},ts::TensorSpace) -# d1 = domain(sp) -# d2 = domain(ts) -# if d2 -# length(ts.spaces) == 2 && -# ((domain(ts)[1] == Point(0.0) && isconvertible(sp,ts.spaces[2])) || -# (domain(ts)[2] == Point(0.0) && isconvertible(sp,ts.spaces[1]))) -# end - -isconvertible(sp::UnivariateSpace,ts::TensorSpace{SV,D,R}) where {SV,D<:EuclideanDomain{2},R} = length(ts.spaces) == 2 && - ((domain(ts)[1] == Point(0.0) && isconvertible(sp,ts.spaces[2])) || - (domain(ts)[2] == Point(0.0) && isconvertible(sp,ts.spaces[1]))) - - -# coefficients(f::AbstractVector,sp::ConstantSpace,ts::TensorSpace{SV,D,R}) where {SV,D<:EuclideanDomain{2},R} = -# f[1]*ones(ts).coefficients - -# -# function coefficients(f::AbstractVector,sp::Space{IntervalOrSegment{Vec{2,TT}}},ts::TensorSpace{Tuple{S,V},D,R}) where {S,V<:ConstantSpace,D<:EuclideanDomain{2},R,TT} where {T<:Number} -# a = domain(sp) -# b = domain(ts) -# # make sure we are the same domain. This will be replaced by isisomorphic -# @assert first(a) ≈ Vec(first(factor(b,1)),factor(b,2).x) && -# last(a) ≈ Vec(last(factor(b,1)),factor(b,2).x) -# -# coefficients(f,sp,setdomain(factor(ts,1),a)) -# end - - -function coefficients(f::AbstractVector,sp::UnivariateSpace,ts::TensorSpace{SV,D,R}) where {SV,D<:EuclideanDomain{2},R} - @assert length(ts.spaces) == 2 - - if factor(domain(ts),1) == Point(0.0) - coefficients(f,sp,ts.spaces[2]) - elseif factor(domain(ts),2) == Point(0.0) - coefficients(f,sp,ts.spaces[1]) - else - error("Cannot convert coefficients from $sp to $ts") - end -end - - -function isconvertible(sp::Space{Segment{Vec{2,TT}}},ts::TensorSpace{SV,D,R}) where {TT,SV,D<:EuclideanDomain{2},R} - d1 = domain(sp) - d2 = domain(ts) - if length(ts.spaces) ≠ 2 - return false - end - if d1.a[2] ≈ d1.b[2] - isa(factor(d2,2),Point) && factor(d2,2).x ≈ d1.a[2] && - isconvertible(setdomain(sp,Segment(d1.a[1],d1.b[1])),ts[1]) - elseif d1.a[1] ≈ d1.b[1] - isa(factor(d2,1),Point) && factor(d2,1).x ≈ d1.a[1] && - isconvertible(setdomain(sp,Segment(d1.a[2],d1.b[2])),ts[2]) - else - return false - end -end - - -function coefficients(f::AbstractVector,sp::Space{Segment{Vec{2,TT}}}, - ts::TensorSpace{SV,D,R}) where {TT,SV,D<:EuclideanDomain{2},R} - @assert length(ts.spaces) == 2 - d1 = domain(sp) - d2 = domain(ts) - if d1.a[2] ≈ d1.b[2] - coefficients(f,setdomain(sp,Segment(d1.a[1],d1.b[1])),factor(ts,1)) - elseif d1.a[1] ≈ d1.b[1] - coefficients(f,setdomain(sp,Segment(d1.a[2],d1.b[2])),factor(ts,2)) - else - error("Cannot convert coefficients from $sp to $ts") - end -end - - - - -Fun(::typeof(identity), S::TensorSpace) = Fun(xyz->collect(xyz),S) diff --git a/src/Multivariate/VectorFun.jl b/src/Multivariate/VectorFun.jl deleted file mode 100644 index d6561d33..00000000 --- a/src/Multivariate/VectorFun.jl +++ /dev/null @@ -1,202 +0,0 @@ - - - -convert(::Type{Array}, f::ArrayFun) = reshape(vec(f), size(space(f))...) -Array(f::ArrayFun) = convert(Array, f) -Vector(f::VectorFun) = Array(f) -Matrix(f::MatrixFun) = Array(f) - -map(f,A::ArrayFun) = Base.collect_similar(A, Base.Generator(f,A)) - -similar(a::VectorFun, S::Type) = Array{S,1}(undef, size(a,1)) -similar(a::MatrixFun, S::Type) = Array{S,2}(undef, size(a,1), size(a,2)) - - -getindex(f::MatrixFun, - k::Union{Integer,AbstractRange,Colon}, - j::Union{Integer,AbstractRange,Colon}) = - Fun(Array(f)[k,j]) - - -const FunTypes = Union{Fun,Number} -const ScalarFunTypes = Union{ScalarFun,Number} -function vcat(vin::FunTypes...) - # remove tuple spaces - v=Vector{Fun}(undef,0) - for f in vin - if rangetype(space(f)) <: AbstractVector - append!(v,vec(f)) - else - push!(v,f) - end - end - - - S = Space(space.(v)) - Fun(S,interlace(v,S)) -end - - -function hcat(v::ScalarFunTypes...) - ff = vcat(v...) # A vectorized version - transpose(ff) -end - -hvcat(rows::Tuple{Vararg{Int}},v::FunTypes...) = Fun(hvnocat(rows,v...)) - - -function hcat(v::VectorFun...) - N = length(v[1]) - M = length(v) - - V = Array{Fun}(undef, N,M) - for J=1:M - V[:,J] = vec(v[J]) - end - Fun(V) -end - - - -function Fun(v::AbstractVector{F}) where F<:Fun - S = Space(space.(v)) - Fun(S,interlace(v,S)) -end - - -#TODO: rewrite -function Fun(v::AbstractArray{<:Fun}) - ff=Fun(vec(v)) # A vectorized version - Fun(Space(map(space,v)),coefficients(ff)) -end - -Fun(v::AbstractArray{NN}) where {NN<:Number} = - Fun(v,Space(fill(ConstantSpace(NN),size(v)))) -Fun(v::AbstractArray) = Fun(Fun.(Array(v)) :: AbstractArray{<:Fun}) - -Fun(f::ArrayFun, d::Space{D,R}) where {D,R<:AbstractArray} = space(f)==d ? f : Fun(d,coefficients(f,d)) -Fun(f::ArrayFun, d::Space) = Fun(f,Space(fill(d,size(space(f))))) - -Fun(M::AbstractMatrix{<:Number},sp::Space) = Fun([Fun(M[:,k],sp) for k=1:size(M,2)]) - -for OP in (:(transpose),) - @eval begin - $OP(f::ArrayFun) = Fun($OP(Array(f))) - $OP(sp::Space{D,R}) where {D,R<:AbstractArray} = Space($OP(Array(sp))) - end -end - -## calculus - -for op in (:differentiate,:integrate,:(cumsum),:(real),:(imag),:(conj)) - @eval $op(f::ArrayFun) = Fun(map($op,f)) -end - -# TODO: use QR -function det(f::MatrixFun) - @assert size(space(f))==(2,2) - m=Array(f) - m[1,1]*m[2,2]-m[1,2]*m[2,1] -end - -function inv(V::MatrixFun) - n,m = size(space(V)) - if n ≠ m - throw(DimensionMismatch("space $(space(V)) is not square")) - end - - # TODO: This assumes other columns have same spaces - M=Multiplication(V,Space(space(V).spaces[:,1])) - # convert I to the rangespace of M - M\Fun(Matrix(I,m,m), repeat(rangespace(M),1,m)) -end - -## Algebra - - - - -for OP in (:*,:+,:-) - @eval begin - $OP(A::AbstractArray{<:Number}, f::ArrayFun) = Fun($OP(A,Array(f))) - $OP(f::ArrayFun, A::AbstractArray{<:Number}) = Fun($OP(Array(f),A)) - $OP(A::AbstractArray{<:Fun}, f::ArrayFun) = Fun($OP(A,Array(f))) - $OP(f::ArrayFun, A::AbstractArray{<:Fun}) = Fun($OP(Array(f),A)) - $OP(A::UniformScaling, f::ArrayFun) = Fun($OP(A,Array(f))) - $OP(f::ArrayFun, A::UniformScaling) = Fun($OP(Array(f),A)) - $OP(A::Number, f::ArrayFun) = Fun($OP(A,Array(f))) - $OP(f::ArrayFun, A::Number) = Fun($OP(Array(f),A)) - - $OP(f::ScalarFun, A::AbstractArray) = Fun(broadcast($OP,f,A)) - $OP(A::AbstractArray, f::ScalarFun) = Fun(broadcast($OP,A,f)) - - $OP(f::ScalarFun, A::ArrayFun) = $OP(f,Array(A)) - $OP(A::ArrayFun, f::ScalarFun) = $OP(Array(A),f) - end -end - -# use standard +, - -*(A::ArrayFun,f::ArrayFun) = Fun(Array(A)*Array(f)) - -norm(A::VectorFun, p::Real) = norm(norm.(Array(A)),p) - - - - - -## Vector of fun routines - -function coefficientmatrix(::Type{N},f::AbstractVector{F},o...) where {N,F} - if isempty(f) - return Matrix{N}(0,0) - end - - n=mapreduce(ncoefficients,max,f) - m=length(f) - R=zeros(N,n,m) - for k=1:m - R[1:ncoefficients(f[k]),k]=coefficients(f[k],o...) - end - R -end - - -scalarorfuntype(::Fun{S,T}) where {S,T<:Number} = T -scalarorfuntype(::T) where {T<:Number} = T -scalarorfuntype(b::AbstractVector{T}) where {T<:Number} = T -scalarorfuntype(b::AbstractVector{Any}) = promote_type(map(scalarorfuntype,b)...) -scalarorfuntype(b::AbstractVector{F}) where {F<:Fun} = promote_type(map(scalarorfuntype,b)...) - - -coefficientmatrix(Q::AbstractVector{F},o...) where {F<:Fun}=coefficientmatrix(scalarorfuntype(Q),Q,o...) -coefficientmatrix(Q::AbstractVector{Any})=(@assert isempty(Q); zeros(0,0)) - - -function values(f::AbstractVector{Fun{D,N,VN}}) where {D,N,VN} - n=mapreduce(ncoefficients,max,f) - m=length(f) - R=zeros(N,n,m) - for k=1:m - R[:,k] = values(pad(f[k],n)) - end - R -end - -function values(p::AbstractMatrix{Fun{D,T,VT}}) where {D,T,VT} - @assert size(p)[1] == 1 - - values(vec(p)) -end - - - - - - - -## evaluation - - -#TODO: fix for complex -evaluate(A::AbstractArray{T},x::Number) where {T<:Fun} = - typeof(first(A)(x))[Akj(x) for Akj in A] diff --git a/src/Operators/Operator.jl b/src/Operators/Operator.jl deleted file mode 100644 index fea0ef12..00000000 --- a/src/Operators/Operator.jl +++ /dev/null @@ -1,843 +0,0 @@ -export Operator -export bandwidths, bandrange, \, periodic -export neumann -export ldirichlet,rdirichlet,lneumann,rneumann -export ldiffbc,rdiffbc,diffbcs -export domainspace,rangespace - - -abstract type Operator{T} end #T is the entry type, Float64 or Complex{Float64} - -eltype(::Operator{T}) where {T} = T -eltype(::Type{Operator{T}}) where {T} = T -eltype(::Type{OT}) where {OT<:Operator} = eltype(supertype(OT)) - - -# default entry type -# we assume entries depend on both the domain and the basis -# realdomain case doesn't use - - -prectype(sp::Space) = promote_type(prectype(domaintype(sp)),eltype(rangetype(sp))) - - #Operators are struct -copy(A::Operator) = A - - -BroadcastStyle(::Type{<:Operator}) = DefaultArrayStyle{2}() -broadcastable(A::Operator) = A - -## We assume operators are T->T -rangespace(A::Operator) = error("Override rangespace for $(typeof(A))") -domainspace(A::Operator) = error("Override domainspace for $(typeof(A))") -spaces(A::Operator) = (rangespace(A), domainspace(A)) # order is consistent with size(::Matrix) -domain(A::Operator) = domain(domainspace(A)) - - -isconstspace(_) = false -## Functionals -isafunctional(A::Operator) = size(A,1)==1 && isconstspace(rangespace(A)) - - -isonesvec(A) = A isa AbstractFill && getindex_value(A) == 1 -# block lengths of a space are 1 -hastrivialblocks(A::Space) = isonesvec(blocklengths(A)) -hastrivialblocks(A::Operator) = hastrivialblocks(domainspace(A)) && - hastrivialblocks(rangespace(A)) - -# blocklengths are constant lengths -hasconstblocks(A::Space) = isa(blocklengths(A),AbstractFill) -hasconstblocks(A::Operator) = hasconstblocks(domainspace(A)) && hasconstblocks(rangespace(A)) && - getindex_value(blocklengths(domainspace(A))) == getindex_value(blocklengths(rangespace(A))) - - -macro functional(FF) - quote - Base.size(A::$FF,k::Integer) = k==1 ? 1 : dimension(domainspace(A)) - ApproxFunBase.rangespace(F::$FF) = ConstantSpace(eltype(F)) - ApproxFunBase.isafunctional(::$FF) = true - ApproxFunBase.blockbandwidths(A::$FF) = 0,hastrivialblocks(domainspace(A)) ? bandwidth(A,2) : ∞ - function ApproxFunBase.defaultgetindex(f::$FF,k::Integer,j::Integer) - @assert k==1 - f[j]::eltype(f) - end - function ApproxFunBase.defaultgetindex(f::$FF,k::Integer,j::AbstractRange) - @assert k==1 - f[j] - end - function ApproxFunBase.defaultgetindex(f::$FF,k::Integer,j) - @assert k==1 - f[j] - end - function ApproxFunBase.defaultgetindex(f::$FF,k::AbstractRange,j::Integer) - @assert k==1:1 - f[j] - end - function ApproxFunBase.defaultgetindex(f::$FF,k::AbstractRange,j::AbstractRange) - @assert k==1:1 - reshape(f[j],1,length(j)) - end - function ApproxFunBase.defaultgetindex(f::$FF,k::AbstractRange,j) - @assert k==1:1 - reshape(f[j],1,length(j)) - end - end -end - - -blocksize(A::Operator,k) = k==1 ? length(blocklengths(rangespace(A))) : length(blocklengths(domainspace(A))) -blocksize(A::Operator) = (blocksize(A,1),blocksize(A,2)) - - -Base.size(A::Operator) = (size(A,1),size(A,2)) -Base.size(A::Operator,k::Integer) = k==1 ? dimension(rangespace(A)) : dimension(domainspace(A)) -Base.length(A::Operator) = size(A,1) * size(A,2) - - -# used to compute "end" for last index -function lastindex(A::Operator, n::Integer) - if n > 2 - 1 - elseif n==2 - size(A,2) - elseif isinf(size(A,2)) || isinf(size(A,1)) - ∞ - else - size(A,1) - end -end -lastindex(A::Operator) = size(A,1)*size(A,2) - -Base.ndims(::Operator) = 2 - - - - - - -## bandrange and indexrange -isbandedbelow(A::Operator) = isfinite(bandwidth(A,1)) -isbandedabove(A::Operator) = isfinite(bandwidth(A,2)) -isbanded(A::Operator) = isbandedbelow(A) && isbandedabove(A) - - -isbandedblockbandedbelow(_) = false -isbandedblockbandedabove(_) = false - -isbandedblockbanded(A::Operator) = isbandedblockbandedabove(A) && isbandedblockbandedbelow(A) - - -# this should be determinable at compile time -#TODO: I think it can be generalized to the case when the domainspace -# blocklengths == rangespace blocklengths, in which case replace the definition -# of p with maximum(blocklength(domainspace(A))) -function blockbandwidths(A::Operator) - hastrivialblocks(A) && return bandwidths(A) - - if hasconstblocks(A) - a,b = bandwidths(A) - p = getindex_value(blocklengths(domainspace(A))) - return (-fld(-a,p),-fld(-b,p)) - end - - #TODO: Generalize to finite dimensional - if size(A,2) == 1 - rs = rangespace(A) - - if hasconstblocks(rs) - a = bandwidth(A,1) - p = getindex_value(blocklengths(rs)) - return (-fld(-a,p),0) - end - end - - return (length(blocklengths(rangespace(A)))-1,length(blocklengths(domainspace(A)))-1) -end - -# assume dense blocks -subblockbandwidths(K::Operator) = maximum(blocklengths(rangespace(K)))-1, maximum(blocklengths(domainspace(K)))-1 - -isblockbandedbelow(A) = isfinite(blockbandwidth(A,1)) -isblockbandedabove(A) = isfinite(blockbandwidth(A,2)) -isblockbanded(A::Operator) = isblockbandedbelow(A) && isblockbandedabove(A) - -israggedbelow(A::Operator) = isbandedbelow(A) || isbandedblockbanded(A) || isblockbandedbelow(A) - - -blockbandwidth(K::Operator, k::Integer) = blockbandwidths(K)[k] -subblockbandwidth(K::Operator,k::Integer) = subblockbandwidths(K)[k] - - -bandwidth(A::Operator, k::Integer) = bandwidths(A)[k] -# we are always banded by the size -bandwidths(A::Operator) = (size(A,1)-1,size(A,2)-1) -bandwidths(A::Operator, k::Integer) = bandwidths(A)[k] - - - -## Strides -# lets us know if operators decouple the entries -# to split into sub problems -# A diagonal operator has essentially infinite stride -# which we represent by a factorial, so that -# the gcd with any number < 10 is the number -stride(A::Operator) = - isdiag(A) ? factorial(10) : 1 - -isdiag(A::Operator) = bandwidths(A)==(0,0) -istriu(A::Operator) = bandwidth(A, 1) == 0 -istril(A::Operator) = bandwidth(A, 2) == 0 - - -## Construct operators - - -include("SubOperator.jl") - - -# -# sparse(B::Operator,n::Integer)=sparse(BandedMatrix(B,n)) -# sparse(B::Operator,n::AbstractRange,m::AbstractRange)=sparse(BandedMatrix(B,n,m)) -# sparse(B::Operator,n::Colon,m::AbstractRange)=sparse(BandedMatrix(B,n,m)) -# sparse(B::Operator,n::AbstractRange,m::Colon)=sparse(BandedMatrix(B,n,m)) - -## geteindex - - - -getindex(B::Operator,k,j) = defaultgetindex(B,k,j) -getindex(B::Operator,k) = defaultgetindex(B,k) -getindex(B::Operator,k::Block{2}) = B[Block.(k.n)...] - - - - -## override getindex. - -defaultgetindex(B::Operator,k::Integer) = error("Override [k] for $(typeof(B))") -defaultgetindex(B::Operator,k::Integer,j::Integer) = error("Override [k,j] for $(typeof(B))") - - -# Ranges - - -defaultgetindex(op::Operator,kr::AbstractRange) = eltype(op)[op[k] for k in kr] -defaultgetindex(B::Operator,k::Block,j::Block) = AbstractMatrix(view(B,k,j)) -defaultgetindex(B::Operator,k::AbstractRange,j::Block) = AbstractMatrix(view(B,k,j)) -defaultgetindex(B::Operator,k::Block,j::AbstractRange) = AbstractMatrix(view(B,k,j)) -defaultgetindex(B::Operator,k::AbstractRange,j::AbstractRange) = AbstractMatrix(view(B,k,j)) - -defaultgetindex(op::Operator,k::Integer,jr::AbstractRange) = eltype(op)[op[k,j] for j in jr] -defaultgetindex(op::Operator,kr::AbstractRange,j::Integer) = eltype(op)[op[k,j] for k in kr] - -defaultgetindex(B::Operator,k::Block,j::BlockRange) = AbstractMatrix(view(B,k,j)) -defaultgetindex(B::Operator,k::BlockRange,j::BlockRange) = AbstractMatrix(view(B,k,j)) - -defaultgetindex(op::Operator,k::Integer,jr::BlockRange) = eltype(op)[op[k,j] for j in jr] -defaultgetindex(op::Operator,kr::BlockRange,j::Integer) = eltype(op)[op[k,j] for k in kr] - - -# Colon casdes -defaultgetindex(A::Operator,kj::CartesianIndex{2}) = A[kj[1],kj[2]] -defaultgetindex(A::Operator,kj::CartesianIndex{1}) = A[kj[1]] -defaultgetindex(A::Operator,k,j) = view(A,k,j) - - - -# TODO: finite dimensional blocks -blockcolstart(A::Operator,J::Integer) = Block(max(1,J-blockbandwidth(A,2))) -blockrowstart(A::Operator,K::Integer) = Block(max(1,K-blockbandwidth(A,1))) -blockcolstop(A::Operator,J::Integer) = Block(min(J+blockbandwidth(A,1),blocksize(A,1))) -blockrowstop(A::Operator,K::Integer) = Block(min(K+blockbandwidth(A,2),blocksize(A,2))) - -blockrows(A::Operator,K::Integer) = blockrange(rangespace(A),K) -blockcols(A::Operator,J::Integer) = blockrange(domainspace(A),J) - - -# default is to use bandwidth -# override for other shaped operators -#TODO: Why size(A,2) in colstart? -banded_colstart(A::Operator, i::Integer) = min(max(i-bandwidth(A,2), 1), size(A, 2)) -banded_colstop(A::Operator, i::Integer) = max(0,min(i+bandwidth(A,1), size(A, 1))) -banded_rowstart(A::Operator, i::Integer) = min(max(i-bandwidth(A,1), 1), size(A, 1)) -banded_rowstop(A::Operator, i::Integer) = max(0,min(i+bandwidth(A,2), size(A, 2))) - -blockbanded_colstart(A::Operator, i::Integer) = - blockstart(rangespace(A), block(domainspace(A),i)-blockbandwidth(A,2)) -blockbanded_colstop(A::Operator, i::Integer) = - min(blockstop(rangespace(A), block(domainspace(A),i)+blockbandwidth(A,1)), - size(A, 1)) -blockbanded_rowstart(A::Operator, i::Integer) = - blockstart(domainspace(A), block(rangespace(A),i)-blockbandwidth(A,1)) -blockbanded_rowstop(A::Operator, i::Integer) = - min(blockstop(domainspace(A), block(rangespace(A),i)+blockbandwidth(A,2)), - size(A, 2)) - - -function bandedblockbanded_colstart(A::Operator, i::Integer) - ds = domainspace(A) - B = block(ds,i) - ξ = i - blockstart(ds,B) + 1 # col in block - bs = blockstart(rangespace(A), B-blockbandwidth(A,2)) - max(bs,bs + ξ - 1 - subblockbandwidth(A,2)) -end - -function bandedblockbanded_colstop(A::Operator, i::Integer) - i ≤ 0 && return 0 - ds = domainspace(A) - rs = rangespace(A) - B = block(ds,i) - ξ = i - blockstart(ds,B) + 1 # col in block - Bend = B+blockbandwidth(A,1) - bs = blockstart(rs, Bend) - min(blockstop(rs,Bend),bs + ξ - 1 + subblockbandwidth(A,1)) -end - -function bandedblockbanded_rowstart(A::Operator, i::Integer) - rs = rangespace(A) - B = block(rs,i) - ξ = i - blockstart(rs,B) + 1 # row in block - bs = blockstart(domainspace(A), B-blockbandwidth(A,1)) - max(bs,bs + ξ - 1 - subblockbandwidth(A,1)) -end - -function bandedblockbanded_rowstop(A::Operator, i::Integer) - ds = domainspace(A) - rs = rangespace(A) - B = block(rs,i) - ξ = i - blockstart(rs,B) + 1 # row in block - Bend = B+blockbandwidth(A,2) - bs = blockstart(ds, Bend) - min(blockstop(ds,Bend),bs + ξ - 1 + subblockbandwidth(A,2)) -end - - -unstructured_colstart(A, i) = 1 -unstructured_colstop(A, i) = size(A,1) -unstructured_rowstart(A, i) = 1 -unstructured_rowstop(A, i) = size(A,2) - - -function default_colstart(A::Operator, i::Integer) - if isbandedabove(A) - banded_colstart(A,i) - elseif isbandedblockbanded(A) - bandedblockbanded_colstart(A, i) - elseif isblockbanded(A) - blockbanded_colstart(A, i) - else - unstructured_colstart(A, i) - end -end - -function default_colstop(A::Operator, i::Integer) - if isbandedbelow(A) - banded_colstop(A,i) - elseif isbandedblockbanded(A) - bandedblockbanded_colstop(A, i) - elseif isblockbanded(A) - blockbanded_colstop(A, i) - else - unstructured_colstop(A, i) - end -end - -function default_rowstart(A::Operator, i::Integer) - if isbandedbelow(A) - banded_rowstart(A,i) - elseif isbandedblockbanded(A) - bandedblockbanded_rowstart(A, i) - elseif isblockbanded(A) - blockbanded_rowstart(A, i) - else - unstructured_rowstart(A, i) - end -end - -function default_rowstop(A::Operator, i::Integer) - if isbandedabove(A) - banded_rowstop(A,i) - elseif isbandedblockbanded(A) - bandedblockbanded_rowstop(A, i) - elseif isblockbanded(A) - blockbanded_rowstop(A, i) - else - unstructured_rowstop(A, i) - end -end - - - -for OP in (:colstart,:colstop,:rowstart,:rowstop) - defOP = Meta.parse("default_"*string(OP)) - @eval begin - $OP(A::Operator,i::Integer) = $defOP(A,i) - $OP(A::Operator,i::Infinity) = ∞ - end -end - - - - -function defaultgetindex(A::Operator,::Type{FiniteRange},::Type{FiniteRange}) - if isfinite(size(A,1)) && isfinite(size(A,2)) - A[1:size(A,1),1:size(A,2)] - else - error("Only exists for finite operators.") - end -end - -defaultgetindex(A::Operator,k::Type{FiniteRange},J::Block) = A[k,blockcols(A,J)] -function defaultgetindex(A::Operator,::Type{FiniteRange},jr::AbstractVector{Int}) - cs = (isbanded(A) || isblockbandedbelow(A)) ? colstop(A,maximum(jr)) : mapreduce(j->colstop(A,j),max,jr) - A[1:cs,jr] -end - -function defaultgetindex(A::Operator,::Type{FiniteRange},jr::BlockRange{1}) - cs = (isbanded(A) || isblockbandedbelow(A)) ? blockcolstop(A,maximum(jr)) : mapreduce(j->blockcolstop(A,j),max,jr) - A[Block(1):cs,jr] -end - -function view(A::Operator,::Type{FiniteRange},jr::AbstractVector{Int}) - cs = (isbanded(A) || isblockbandedbelow(A)) ? colstop(A,maximum(jr)) : mapreduce(j->colstop(A,j),max,jr) - view(A,1:cs,jr) -end - -function view(A::Operator,::Type{FiniteRange},jr::BlockRange{1}) - cs = (isbanded(A) || isblockbandedbelow(A)) ? blockcolstop(A,maximum(jr)) : mapreduce(j->blockcolstop(A,j),max,jr) - view(A,Block(1):cs,jr) -end - - -defaultgetindex(A::Operator,K::Block,j::Type{FiniteRange}) = A[blockrows(A,K),j] -defaultgetindex(A::Operator,kr,::Type{FiniteRange}) = - A[kr,1:rowstop(A,maximum(kr))] - - - - - -## Composition with a Fun, LowRankFun, and ProductFun - -getindex(B::Operator,f::Fun) = B*Multiplication(domainspace(B),f) -getindex(B::Operator,f::LowRankFun{S,M,SS,T}) where {S,M,SS,T} = mapreduce(i->f.A[i]*B[f.B[i]],+,1:rank(f)) -getindex(B::Operator{BT},f::ProductFun{S,V,SS,T}) where {BT,S,V,SS,T} = - mapreduce(i->f.coefficients[i]*B[Fun(f.space[2],[zeros(promote_type(BT,T),i-1); - one(promote_type(BT,T))])], - +,1:length(f.coefficients)) - - - -# Convenience for wrapper ops -unwrap_axpy!(α,P,A) = BLAS.axpy!(α,view(parent(P).op,P.indexes[1],P.indexes[2]),A) -iswrapper(_) = false -haswrapperstructure(_) = false - -# use this for wrapper operators that have the same structure but -# not necessarily the same entries -# -# Ex: c*op or real(op) -macro wrapperstructure(Wrap) - ret = quote - haswrapperstructure(::$Wrap) = true - end - - for func in (:(ApproxFunBase.bandwidths),:(LinearAlgebra.stride), - :(ApproxFunBase.isbandedblockbanded),:(ApproxFunBase.isblockbanded), - :(ApproxFunBase.israggedbelow),:(Base.size),:(ApproxFunBase.isbanded), - :(ApproxFunBase.blockbandwidths),:(ApproxFunBase.subblockbandwidths), - :(LinearAlgebra.issymmetric)) - ret = quote - $ret - - $func(D::$Wrap) = $func(D.op) - end - end - - for func in (:(ApproxFunBase.bandwidth),:(ApproxFunBase.colstart),:(ApproxFunBase.colstop), - :(ApproxFunBase.rowstart),:(ApproxFunBase.rowstop),:(ApproxFunBase.blockbandwidth), - :(Base.size),:(ApproxFunBase.subblockbandwidth)) - ret = quote - $ret - - $func(D::$Wrap,k::Integer) = $func(D.op,k) - $func(A::$Wrap,i::ApproxFunBase.Infinity) = $func(D.op,k) - end - end - - esc(ret) -end - - - -# use this for wrapper operators that have the same entries but -# not necessarily the same spaces -# -macro wrappergetindex(Wrap) - ret = quote - Base.getindex(OP::$Wrap,k::Integer...) = - OP.op[k...]::eltype(OP) - - Base.getindex(OP::$Wrap,k::Union{Number,AbstractArray,Colon}...) = OP.op[k...] - Base.getindex(OP::$Wrap,k::ApproxFunBase.InfRanges, j::ApproxFunBase.InfRanges) = view(OP, k, j) - Base.getindex(OP::$Wrap,k::ApproxFunBase.InfRanges, j::Colon) = view(OP, k, j) - Base.getindex(OP::$Wrap,k::Colon, j::ApproxFunBase.InfRanges) = view(OP, k, j) - Base.getindex(OP::$Wrap,k::Colon, j::Colon) = view(OP, k, j) - - BLAS.axpy!(α,P::ApproxFunBase.SubOperator{T,OP},A::AbstractMatrix) where {T,OP<:$Wrap} = - ApproxFunBase.unwrap_axpy!(α,P,A) - - ApproxFunBase.mul_coefficients(A::$Wrap,b) = ApproxFunBase.mul_coefficients(A.op,b) - ApproxFunBase.mul_coefficients(A::ApproxFunBase.SubOperator{T,OP,Tuple{UnitRange{Int},UnitRange{Int}}},b) where {T,OP<:$Wrap} = - ApproxFunBase.mul_coefficients(view(parent(A).op,S.indexes[1],S.indexes[2]),b) - ApproxFunBase.mul_coefficients(A::ApproxFunBase.SubOperator{T,OP},b) where {T,OP<:$Wrap} = - ApproxFunBase.mul_coefficients(view(parent(A).op,S.indexes[1],S.indexes[2]),b) - end - - for TYP in (:(ApproxFunBase.BandedMatrix),:(ApproxFunBase.RaggedMatrix), - :Matrix,:Vector,:AbstractVector) - ret = quote - $ret - - $TYP(P::ApproxFunBase.SubOperator{T,OP}) where {T,OP<:$Wrap} = - $TYP(view(parent(P).op,P.indexes[1],P.indexes[2])) - $TYP(P::ApproxFunBase.SubOperator{T,OP,NTuple{2,UnitRange{Int}}}) where {T,OP<:$Wrap} = - $TYP(view(parent(P).op,P.indexes[1],P.indexes[2])) - end - end - - ret = quote - $ret - - # fast converts to banded matrices would be based on indices, not blocks - function ApproxFunBase.BandedMatrix(S::ApproxFunBase.SubOperator{T,OP,NTuple{2,ApproxFunBase.BlockRange1}}) where {T,OP<:$Wrap} - A = parent(S) - ds = domainspace(A) - rs = rangespace(A) - KR,JR = parentindices(S) - ApproxFunBase.BandedMatrix(view(A, - ApproxFunBase.blockstart(rs,first(KR)):ApproxFunBase.blockstop(rs,last(KR)), - ApproxFunBase.blockstart(ds,first(JR)):ApproxFunBase.blockstop(ds,last(JR)))) - end - - - # if the spaces change, then we need to be smarter - function ApproxFunBase.BlockBandedMatrix(S::ApproxFunBase.SubOperator{T,OP}) where {T,OP<:$Wrap} - P = parent(S) - if ApproxFunBase.blocklengths(domainspace(P)) === ApproxFunBase.blocklengths(domainspace(P.op)) && - ApproxFunBase.blocklengths(rangespace(P)) === ApproxFunBase.blocklengths(rangespace(P.op)) - ApproxFunBase.BlockBandedMatrix(view(parent(S).op,S.indexes[1],S.indexes[2])) - else - ApproxFunBase.default_BlockBandedMatrix(S) - end - end - - function ApproxFunBase.PseudoBlockMatrix(S::ApproxFunBase.SubOperator{T,OP}) where {T,OP<:$Wrap} - P = parent(S) - if ApproxFunBase.blocklengths(domainspace(P)) === ApproxFunBase.blocklengths(domainspace(P.op)) && - ApproxFunBase.blocklengths(rangespace(P)) === ApproxFunBase.blocklengths(rangespace(P.op)) - ApproxFunBase.PseudoBlockMatrix(view(parent(S).op,S.indexes[1],S.indexes[2])) - else - ApproxFunBase.default_blockmatrix(S) - end - end - - function ApproxFunBase.BandedBlockBandedMatrix(S::ApproxFunBase.SubOperator{T,OP}) where {T,OP<:$Wrap} - P = parent(S) - if ApproxFunBase.blocklengths(domainspace(P)) === ApproxFunBase.blocklengths(domainspace(P.op)) && - ApproxFunBase.blocklengths(rangespace(P)) === ApproxFunBase.blocklengths(rangespace(P.op)) - ApproxFunBase.BandedBlockBandedMatrix(view(parent(S).op,S.indexes[1],S.indexes[2])) - else - ApproxFunBase.default_BandedBlockBandedMatrix(S) - end - end - - ApproxFunBase.@wrapperstructure($Wrap) # structure is automatically inherited - end - - esc(ret) -end - -# use this for wrapper operators that have the same spaces but -# not necessarily the same entries or structure -# -macro wrapperspaces(Wrap) - ret = quote end - - for func in (:(ApproxFunBase.rangespace),:(ApproxFunBase.domain), - :(ApproxFunBase.domainspace),:(ApproxFunBase.isconstop)) - ret = quote - $ret - - $func(D::$Wrap) = $func(D.op) - end - end - - esc(ret) -end - - -# use this for wrapper operators that have the same entries and same spaces -# -macro wrapper(Wrap) - ret = quote - ApproxFunBase.@wrappergetindex($Wrap) - ApproxFunBase.@wrapperspaces($Wrap) - - ApproxFunBase.iswrapper(::$Wrap) = true - end - - - esc(ret) -end - -## Standard Operators and linear algebra - - - -include("ldiv.jl") - -include("spacepromotion.jl") -include("banded/banded.jl") -include("general/general.jl") - -include("functionals/functionals.jl") -include("almostbanded/almostbanded.jl") - -include("systems.jl") - -include("qr.jl") -include("nullspace.jl") - - - - -## Conversion - - - -zero(::Type{Operator{T}}) where {T<:Number} = ZeroOperator(T) -zero(::Type{O}) where {O<:Operator} = ZeroOperator(eltype(O)) - - -Operator(L::UniformScaling) = ConstantOperator(L, UnsetSpace()) -Operator(L::UniformScaling, s::Space) = ConstantOperator(L, s) -Operator(L::UniformScaling{Bool}, s::Space) = L.λ ? IdentityOperator(s) : ZeroOperator(s) -Operator(L::UniformScaling, d::Domain) = Operator(L, Space(d)) - -Operator{T}(f::Fun) where {T} = - norm(f.coefficients)==0 ? zero(Operator{T}) : convert(Operator{T}, Multiplication(f)) - -Operator(f::Fun) = norm(f.coefficients)==0 ? ZeroOperator() : Multiplication(f) - -convert(::Type{O}, f::Fun) where O<:Operator = O(f) -Operator{T}(A::Operator) where T = convert(Operator{T}, A) - - -## Promotion - - - - - -promote_rule(::Type{N},::Type{Operator}) where {N<:Number} = Operator{N} -promote_rule(::Type{UniformScaling{N}},::Type{Operator}) where {N<:Number} = - Operator{N} -promote_rule(::Type{Fun{S,N,VN}},::Type{Operator}) where {S,N<:Number,VN} = Operator{N} -promote_rule(::Type{N},::Type{O}) where {N<:Number,O<:Operator} = - Operator{promote_type(N,eltype(O))} # float because numbers are promoted to Fun -promote_rule(::Type{UniformScaling{N}},::Type{O}) where {N<:Number,O<:Operator} = - Operator{promote_type(N,eltype(O))} -promote_rule(::Type{Fun{S,N,VN}},::Type{O}) where {S,N<:Number,O<:Operator,VN} = - Operator{promote_type(N,eltype(O))} - -promote_rule(::Type{BO1},::Type{BO2}) where {BO1<:Operator,BO2<:Operator} = - Operator{promote_type(eltype(BO1),eltype(BO2))} - - - - -## Wrapper - -#TODO: Should cases that modify be included? -const WrapperOperator = Union{SpaceOperator,MultiplicationWrapper,DerivativeWrapper,IntegralWrapper, - ConversionWrapper,ConstantTimesOperator,TransposeOperator} - - - - - -# The following support converting an Operator to a Matrix or BandedMatrix - -## BLAS and matrix routines -# We assume that copy may be overriden - -BLAS.axpy!(a, X::Operator, Y::AbstractMatrix) = (Y .= a .* AbstractMatrix(X) .+ Y) -copyto!(dest::AbstractMatrix, src::Operator) = copyto!(dest, AbstractMatrix(src)) - -# this is for operators that implement copy via axpy! - -BandedMatrix(::Type{Zeros}, V::Operator) = BandedMatrix(Zeros{eltype(V)}(size(V)), bandwidths(V)) -Matrix(::Type{Zeros}, V::Operator) = Matrix(Zeros{eltype(V)}(size(V))) -BandedBlockBandedMatrix(::Type{Zeros}, V::Operator) = - BandedBlockBandedMatrix(Zeros{eltype(V)}(size(V)), - blocklengths(rangespace(V)), blocklengths(domainspace(V)), - blockbandwidths(V), subblockbandwidths(V)) -BlockBandedMatrix(::Type{Zeros}, V::Operator) = - BlockBandedMatrix(Zeros{eltype(V)}(size(V)), - AbstractVector{Int}(blocklengths(rangespace(V))), - AbstractVector{Int}(blocklengths(domainspace(V))), - blockbandwidths(V)) -RaggedMatrix(::Type{Zeros}, V::Operator) = - RaggedMatrix(Zeros{eltype(V)}(size(V)), - Int[max(0,colstop(V,j)) for j=1:size(V,2)]) - - -convert_axpy!(::Type{MT}, S::Operator) where {MT <: AbstractMatrix} = - BLAS.axpy!(one(eltype(S)), S, MT(Zeros, S)) - - - -BandedMatrix(S::Operator) = default_BandedMatrix(S) - -function BlockBandedMatrix(S::Operator) - if isbandedblockbanded(S) - BlockBandedMatrix(BandedBlockBandedMatrix(S)) - else - default_BlockBandedMatrix(S) - end -end - -function default_BlockMatrix(S::Operator) - ret = PseudoBlockArray(zeros(size(S)), - AbstractVector{Int}(blocklengths(rangespace(S))), - AbstractVector{Int}(blocklengths(domainspace(S)))) - ret .= S - ret -end - -function PseudoBlockMatrix(S::Operator) - if isbandedblockbanded(S) - PseudoBlockMatrix(BandedBlockBandedMatrix(S)) - elseif isblockbanded(S) - PseudoBlockMatrix(BlockBandedMatrix(S)) - else - default_BlockMatrix(S) - end -end - - -# TODO: Unify with SubOperator -for TYP in (:RaggedMatrix, :Matrix) - def_TYP = Meta.parse("default_" * string(TYP)) - @eval function $TYP(S::Operator) - if isinf(size(S,1)) || isinf(size(S,2)) - error("Cannot convert $S to a $TYP") - end - - if isbanded(S) - $TYP(BandedMatrix(S)) - else - $def_TYP(S) - end - end -end - -function Vector(S::Operator) - if size(S,2) ≠ 1 || isinf(size(S,1)) - error("Cannot convert $S to a AbstractVector") - end - - eltype(S)[S[k] for k=1:size(S,1)] -end - -convert(::Type{AA}, B::Operator) where AA<:AbstractArray = AA(B) - - -# TODO: template out fully -arraytype(::Operator) = Matrix -function arraytype(V::SubOperator{T,B,Tuple{KR,JR}}) where {T, B, KR <: Union{BlockRange, Block}, JR <: Union{BlockRange, Block}} - P = parent(V) - isbandedblockbanded(P) && return BandedBlockBandedMatrix - isblockbanded(P) && return BlockBandedMatrix - return PseudoBlockMatrix -end - -function arraytype(V::SubOperator{T,B,Tuple{KR,JR}}) where {T, B, KR <: Block, JR <: Block} - P = parent(V) - isbandedblockbanded(V) && return BandedMatrix - return Matrix -end - - -function arraytype(V::SubOperator) - P = parent(V) - isbanded(P) && return BandedMatrix - # isbandedblockbanded(P) && return BandedBlockBandedMatrix - isinf(size(P,1)) && israggedbelow(P) && return RaggedMatrix - return Matrix -end - -AbstractMatrix(V::Operator) = arraytype(V)(V) -AbstractVector(S::Operator) = Vector(S) - - - - -# default copy is to loop through -# override this for most operators. -function default_BandedMatrix(S::Operator) - Y=BandedMatrix{eltype(S)}(undef, size(S), bandwidths(S)) - - for j=1:size(S,2),k=colrange(Y,j) - @inbounds inbands_setindex!(Y,S[k,j],k,j) - end - - Y -end - - -# default copy is to loop through -# override this for most operators. -function default_RaggedMatrix(S::Operator) - data=Array{eltype(S)}(undef, 0) - cols=Array{Int}(undef, size(S,2)+1) - cols[1]=1 - for j=1:size(S,2) - cs=colstop(S,j) - K=cols[j]-1 - cols[j+1]=cs+cols[j] - resize!(data,cols[j+1]-1) - - for k=1:cs - data[K+k]=S[k,j] - end - end - - RaggedMatrix(data,cols,size(S,1)) -end - -function default_Matrix(S::Operator) - n, m = size(S) - if isinf(n) || isinf(m) - error("Cannot convert $S to a Matrix") - end - - eltype(S)[S[k,j] for k=1:n, j=1:m] -end - - - - -# The diagonal of the operator may not be the diagonal of the sub -# banded matrix, so the following calculates the row of the -# Banded matrix corresponding to the diagonal of the original operator - - -diagindshift(S,kr,jr) = first(kr)-first(jr) -diagindshift(S::SubOperator) = diagindshift(S,parentindices(S)[1],parentindices(S)[2]) - - -#TODO: Remove -diagindrow(S,kr,jr) = bandwidth(S,2)+first(jr)-first(kr)+1 -diagindrow(S::SubOperator) = diagindrow(S,parentindices(S)[1],parentindices(S)[2]) \ No newline at end of file diff --git a/src/Operators/SubOperator.jl b/src/Operators/SubOperator.jl deleted file mode 100644 index 54a9113e..00000000 --- a/src/Operators/SubOperator.jl +++ /dev/null @@ -1,351 +0,0 @@ -Vcheckbounds(A::Operator,kr::Colon) = nothing - -checkbounds(A::Operator,kr) = - (maximum(kr) > length(A) || minimum(kr) < 1) && throw(BoundsError(A,kr)) - - -checkbounds(A::Operator,kr::Union{Colon,InfRanges},jr::Union{Colon,InfRanges}) = nothing - -checkbounds(A::Operator,kr::Union{Colon,InfRanges},jr) = - (maximum(jr) > size(A,2) || minimum(jr) < 1) && throw(BoundsError(A,(kr,jr))) - -checkbounds(A::Operator,kr,jr::Union{Colon,InfRanges}) = - (maximum(kr) > size(A,1) || minimum(kr) < 1 ) && throw(BoundsError(A,(kr,jr))) - -checkbounds(A::Operator,kr,jr) = - (!isempty(kr) && (maximum(kr) > size(A,1) || minimum(kr) < 1)) || - (!isempty(jr) && (maximum(jr) > size(A,2) || minimum(jr) < 1)) && - throw(BoundsError(A,(kr,jr))) - - -checkbounds(A::Operator,K::Block,J::Block) = - 1 ≤ first(K.n[1]) ≤ length(blocklengths(rangespace(A))) && - 1 ≤ first(J.n[1]) ≤ length(blocklengths(domainspace(A))) - -checkbounds(A::Operator,K::BlockRange{1},J::BlockRange{1}) = - isempty(K) || isempty(J) || - checkbounds(A, Block(maximum(K.indices[1])), Block(maximum(J.indices[1]))) - - - -## SubOperator - -struct SubOperator{T,B,I,DI,BI} <: Operator{T} - parent::B - indexes::I - dims::DI - bandwidths::BI -end - - - -function SubOperator(A,inds,dims,lu) - checkbounds(A,inds...) - SubOperator{eltype(A),typeof(A),typeof(inds), - typeof(dims),typeof(lu)}(A,inds,dims,lu) -end - -# work around strange bug with bool size -SubOperator(A,inds,dims::Tuple{Bool,Bool},lu) = SubOperator(A,inds,Int.(dims),lu) - -function SubOperator(A,inds::Tuple{Block,Block},lu) - checkbounds(A,inds...) - SubOperator(A,inds,(blocklengths(rangespace(A))[inds[1].n[1]],blocklengths(domainspace(A))[inds[2].n[1]]),lu) -end - -SubOperator(A, inds::Tuple{Block,Block}) = SubOperator(A,inds,subblockbandwidths(A)) -function SubOperator(A, inds::Tuple{BlockRange{1,R},BlockRange{1,R}}) where R - checkbounds(A,inds...) - dims = (sum(blocklengths(rangespace(A))[inds[1].indices[1]]), - sum(blocklengths(domainspace(A))[inds[2].indices[1]])) - SubOperator(A,inds,dims,(dims[1]-1,dims[2]-1)) -end - -# cannot infer ranges -SubOperator(A,inds,dims) = SubOperator(A,inds,dims,(dims[1]-1,dims[2]-1)) -SubOperator(A,inds) = SubOperator(A,inds,map(length,inds)) - - -convert(::Type{Operator{T}},SO::SubOperator) where {T} = - SubOperator(Operator{T}(SO.parent),SO.indexes,SO.dims,SO.bandwidths)::Operator{T} - -function view(A::Operator,kr::InfRanges,jr::InfRanges) - @assert isinf(size(A,1)) && isinf(size(A,2)) - st=step(kr) - if isbanded(A) && st==step(jr) # Otherwise, its not a banded operator - kr1=first(kr) - jr1=first(jr) - l,u=(bandwidth(A,1)+jr1-kr1)÷st,(bandwidth(A,2)+kr1-jr1)÷st - else - l,u=∞,∞ - end - SubOperator(A,(kr,jr),size(A),(l,u)) -end - -view(V::SubOperator, kr::AbstractRange, jr::AbstractRange) = - view(V.parent,reindex(V,parentindices(V),(kr,jr))...) - -function view(A::Operator, kr::AbstractRange, jr::AbstractRange) - st=step(kr) - if isbanded(A) && st == step(jr) - kr1=first(kr) - jr1=first(jr) - l,u=(bandwidth(A,1)+jr1-kr1)÷st,(bandwidth(A,2)+kr1-jr1)÷st - SubOperator(A,(kr,jr),(length(kr),length(jr)),(l,u)) - else - SubOperator(A,(kr,jr)) - end -end - - -function view(A::Operator,kr::UnitRange,jr::UnitRange) - if isbanded(A) - shft=first(kr)-first(jr) - l,u=bandwidth(A,1)-shft,bandwidth(A,2)+shft - SubOperator(A,(kr,jr),(length(kr),length(jr)),(l,u)) - else - SubOperator(A,(kr,jr)) - end -end - -view(A::Operator,::Colon,::Colon) = view(A,1:size(A,1),1:size(A,2)) -view(A::Operator,::Colon,jr) = view(A,1:size(A,1),jr) -view(A::Operator,kr,::Colon) = view(A,kr,1:size(A,2)) - - -view(A::Operator,K::Block,J::Block) = SubOperator(A,(K,J)) -view(A::Operator,K::Block,j::Colon) = view(A,blockrows(A,K),j) -view(A::Operator,k::Colon,J::Block) = view(A,k,blockcols(A,J)) -view(A::Operator, K::Block, j) = view(A,blockrows(A,Int(K)),j) -view(A::Operator, k, J::Block) = view(A,k,blockcols(A,Int(J))) #TODO: fix view -view(A::Operator,KR::BlockRange,JR::BlockRange) = SubOperator(A,(KR,JR)) - -view(A::Operator,k,j) = SubOperator(A,(k,j)) - -defaultgetindex(B::Operator,k::InfRanges, j::InfRanges) = view(B, k, j) -defaultgetindex(B::Operator,k::AbstractRange, j::InfRanges) = view(B, k, j) -defaultgetindex(B::Operator,k::InfRanges, j::AbstractRange) = view(B, k, j) - - -if VERSION < v"1.2-" - reindex(V, idxs, subidxs) = Base.reindex(V, idxs, subidxs) -else - reindex(V, idxs, subidxs) = Base.reindex(idxs, subidxs) -end - -reindex(A::Operator, B::Tuple{Block,Any}, kj::Tuple{Any,Any}) = - (reindex(rangespace(A),(B[1],), (kj[1],))[1], reindex(domainspace(A),tail(B), tail(kj))[1]) -# always reindex left-to-right, so if we have only a single tuple, then -# we must be the domainspace -reindex(A::Operator, B::Tuple{Block{1}}, kj::Tuple{Any}) = reindex(domainspace(A),B,kj) - -reindex(A::Operator, B::Tuple{BlockRange1,Any}, kj::Tuple{Any,Any}) = - (reindex(rangespace(A),(B[1],), (kj[1],))[1], reindex(domainspace(A),tail(B), tail(kj))[1]) -# always reindex left-to-right, so if we have only a single tuple, then -# we must be the domainspace -reindex(A::Operator, B::Tuple{BlockRange1}, kj::Tuple{Any}) = - reindex(domainspace(A),B,kj) -# Blocks are preserved under ranges -for TYP in (:Block,:BlockRange1,:(AbstractVector{Block{1}})) - @eval begin - reindex(A::Operator, B::Tuple{AbstractVector{Int},Any}, kj::Tuple{$TYP,Any}) = - (reindex(rangespace(A), (B[1],), (kj[1],))[1], reindex(domainspace(A),tail(B), tail(kj))[1]) - reindex(A::Operator, B::Tuple{AbstractVector{Int}}, kj::Tuple{$TYP}) = - reindex(domainspace(A),B,kj) - end -end - - - -view(V::SubOperator,kr::UnitRange,jr::UnitRange) = view(V.parent,reindex(V,parentindices(V),(kr,jr))...) -view(V::SubOperator,K::Block,J::Block) = view(V.parent,reindex(V,parentindices(V),(K,J))...) -view(V::SubOperator,KR::BlockRange,JR::BlockRange) = view(V.parent, reindex(V,parentindices(V),(KR,JR))...) -function view(V::SubOperator,::Type{FiniteRange},jr::AbstractVector{Int}) - cs = (isbanded(V) || isblockbandedbelow(V)) ? colstop(V,maximum(jr)) : mapreduce(j->colstop(V,j),max,jr) - view(V,1:cs,jr) -end - -view(V::SubOperator, kr, jr) = view(V.parent,reindex(V,parentindices(V),(kr,jr))...) -view(V::SubOperator,kr::InfRanges,jr::InfRanges) = view(V.parent,reindex(V,parentindices(V),(kr,jr))...) - -bandwidths(S::SubOperator) = S.bandwidths -function colstop(S::SubOperator{T,OP,Tuple{UnitRange{Int},UnitRange{Int}}},j::Integer) where {T,OP} - cs = colstop(parent(S),parentindices(S)[2][j]) - kr = parentindices(S)[1] - n = size(S,1) - if cs < first(kr) - 0 - elseif cs ≥ last(kr) - n - else - min(n,findfirst(isequal(cs),kr)) - end -end -colstart(S::SubOperator{T,OP,Tuple{UnitRange{Int},UnitRange{Int}}},j::Integer) where {T,OP} = - max(findfirst(parentindices(S)[1],colstart(parent(S),parentindices(S)[2][j])),1) -rowstart(S::SubOperator{T,OP,Tuple{UnitRange{Int},UnitRange{Int}}},j::Integer) where {T,OP} = - max(1,findfirst(parentindices(S)[2],rowstart(parent(S),parentindices(S)[1][j]))) -rowstop(S::SubOperator{T,OP,Tuple{UnitRange{Int},UnitRange{Int}}},j::Integer) where {T,OP} = - findfirst(parentindices(S)[2],rowstop(parent(S),parentindices(S)[1][j])) - - -# blocks don't change -blockcolstop(S::SubOperator{T,OP,Tuple{II,JJ}},J::Integer) where {T,OP,II<:AbstractRange{Int},JJ<:AbstractRange{Int}} = - blockcolstop(parent(S),J) - -israggedbelow(S::SubOperator) = israggedbelow(parent(S)) - -# since blocks don't change with indexex, neither do blockbandwidths -blockbandwidths(S::SubOperator{T,OP,Tuple{II,JJ}}) where {T,OP,II<:AbstractRange{Int},JJ<:AbstractRange{Int}} = - blockbandwidths(parent(S)) -function blockbandwidths(S::SubOperator{T,B,Tuple{BlockRange1,BlockRange1}}) where {T,B} - KR,JR = parentindices(S) - l,u = blockbandwidths(parent(S)) - sh = first(KR).n[1]-first(JR).n[1] - l-sh,u+sh -end - -isblockbanded(S::SubOperator{T,B,Tuple{Block,Block}}) where {T,B} = false -isbanded(S::SubOperator{T,B,Tuple{Block,Block}}) where {T,B} = isbandedblockbanded(parent(S)) -bandwidths(S::SubOperator{T,B,Tuple{Block,Block}}) where {T,B} = subblockbandwidths(parent(S)) -blockbandwidths(S::SubOperator{T,B,Tuple{Block,Block}}) where {T,B} = 0,0 - -function BandedBlockBandedMatrix(::Type{Zeros}, S::SubOperator) - kr,jr=parentindices(S) - KO=parent(S) - l,u=blockbandwidths(KO) - λ,μ=subblockbandwidths(KO) - - rt=rangespace(KO) - dt=domainspace(KO) - k1,j1=isempty(kr) || isempty(jr) ? (first(kr),first(jr)) : - reindex(S,parentindices(S),(1,1)) - - # each row/column that we differ from the the block start shifts - # the sub block inds - J = block(dt,j1) - K = block(rt,k1) - jsh=j1-blockstart(dt,J) - ksh=k1-blockstart(rt,K) - - rows,cols = blocklengths(rangespace(S)), blocklengths(domainspace(S)) - - BandedBlockBandedMatrix(Zeros{eltype(KO)}(sum(rows),sum(cols)), - rows,cols, (l,u), (λ-jsh,μ+ksh)) -end - -function BandedBlockBandedMatrix(::Type{Zeros}, S::SubOperator{T,B,Tuple{BlockRange1,BlockRange1}}) where {T,B} - KR,JR = parentindices(S) - KO = parent(S) - l,u = blockbandwidths(KO)::Tuple{Int,Int} - λ,μ = subblockbandwidths(KO)::Tuple{Int,Int} - - rt = rangespace(KO) - dt = domainspace(KO) - J = first(JR) - K = first(KR) - bl_sh = Int(J) - Int(K) - - KBR = blocklengthrange(rt,KR) - KJR = blocklengthrange(dt,JR) - - BandedBlockBandedMatrix(Zeros{eltype(KO)}(sum(KBR),sum(KJR)), - AbstractVector{Int}(KBR),AbstractVector{Int}(KJR), (l+bl_sh,u-bl_sh), (λ,μ)) -end - - -function domainspace(S::SubOperator) - P =parent(S) - sp=domainspace(P) - kr=parentindices(S)[2] - - SubSpace{typeof(sp),typeof(kr),domaintype(sp),rangetype(sp)}(sp,kr) -end -function rangespace(S::SubOperator) - P =parent(S) - sp=rangespace(P) - kr=parentindices(S)[1] - - SubSpace{typeof(sp),typeof(kr),domaintype(sp),rangetype(sp)}(sp,kr) -end - -size(V::SubOperator) = V.dims -size(V::SubOperator,k::Int) = V.dims[k] - -unsafe_getindex(V::SubOperator,k::Integer,j::Integer) = V.parent[reindex(V,parentindices(V),(k,j))...] -getindex(V::SubOperator,k::Integer,j::Integer) = V.parent[reindex(V,parentindices(V),(k,j))...] -getindex(V::SubOperator,k::Integer,j::AbstractRange) = V.parent[reindex(V,parentindices(V),(k,j))...] -getindex(V::SubOperator,k::AbstractRange,j::Integer) = V.parent[reindex(V,parentindices(V),(k,j))...] -getindex(V::SubOperator,k::AbstractRange,j::AbstractRange) = V.parent[reindex(V,parentindices(V),(k,j))...] -Base.parent(S::SubOperator) = S.parent -Base.parentindices(S::SubOperator) = S.indexes - - - -for OP in (:isblockbanded,:isblockbandedabove,:isblockbandedbelow, - :isbandedblockbanded,:isbandedblockbandedabove, - :isbandedblockbandedbelow) - @eval $OP(S::SubOperator) = $OP(parent(S)) -end - -# TODO: These should be removed as the general purpose case will work, -# once the notion of bandedness of finite dimensional operators is made sense of - - -_colstops(V) = Int[max(0,colstop(V,j)) for j=1:size(V,2)] - -for TYP in (:RaggedMatrix, :Matrix) - def_TYP = Meta.parse("default_" * string(TYP)) - @eval begin - function $TYP(V::SubOperator) - if isinf(size(V,1)) || isinf(size(V,2)) - error("Cannot convert $V to a $TYP") - end - A = parent(V) - if isbanded(A) - $TYP(BandedMatrix(V)) - else - $def_TYP(V) - end - end - - function $TYP(V::SubOperator{T,BB,NTuple{2,UnitRange{Int}}}) where {T,BB} - if isinf(size(V,1)) || isinf(size(V,2)) - error("Cannot convert $V to a $TYP") - end - A = parent(V) - if isbanded(A) - $TYP(BandedMatrix(V)) - elseif isbandedblockbanded(A) - N = block(rangespace(A), last(parentindices(V)[1])) - M = block(domainspace(A), last(parentindices(V)[2])) - B = A[Block(1):N, Block(1):M] - RaggedMatrix(view(B, parentindices(V)...), _colstops(V)) - else - $def_TYP(V) - end - end - end -end - -# fast converts to banded matrices would be based on indices, not blocks -function BandedMatrix(S::SubOperator{T,B,Tuple{BlockRange1,BlockRange1}}) where {T,B} - A = parent(S) - ds = domainspace(A) - rs = rangespace(A) - KR,JR = parentindices(S) - BandedMatrix(view(A, - blockstart(rs,first(KR)):blockstop(rs,last(KR)), - blockstart(ds,first(JR)):blockstop(ds,last(JR)))) -end - - - - -function mul_coefficients(A::SubOperator{T,B,Tuple{UnitRange{Int},UnitRange{Int}}},b) where {T,B} - if size(A,2) == length(b) - AbstractMatrix(A)*b - else - view(A,:,1:length(b))*b - end -end diff --git a/src/Operators/almostbanded/LowRankOperator.jl b/src/Operators/almostbanded/LowRankOperator.jl deleted file mode 100644 index 7d515471..00000000 --- a/src/Operators/almostbanded/LowRankOperator.jl +++ /dev/null @@ -1,85 +0,0 @@ -export AbstractLowRankOperator, LowRankOperator - -abstract type AbstractLowRankOperator{T} <: Operator{T} end - -struct LowRankOperator{S<:Space,T} <: AbstractLowRankOperator{T} - U::Vector{VFun{S,T}} - V::Vector{Operator{T}} - - function LowRankOperator{S,T}(U::Vector{VFun{S,T}},V::Vector{Operator{T}}) where {S,T} - @assert all(isafunctional,V) - - @assert length(U) == length(V) - @assert length(U) > 0 - ds=domainspace(first(V)) - for k=2:length(V) - @assert domainspace(V[k])==ds - end - rs=space(first(U)) - for k=2:length(U) - @assert space(U[k])==rs - end - new{S,T}(U,V) - end -end - - - -LowRankOperator(U::Vector{VFun{S,T}},V::Vector{Operator{T}}) where {S,T} = LowRankOperator{S,T}(U,V) -LowRankOperator(U::Vector{VFun{S,T1}},V::Vector{Operator{T2}}) where {S,T1,T2} = - LowRankOperator(convert(Vector{VFun{S,promote_type(T1,T2)}},U), - convert(Vector{Operator{promote_type(T1,T2)}},V)) -LowRankOperator(U::Vector{FF},V::Vector{FT}) where {FF<:Fun,FT<:Operator} = - LowRankOperator(U,convert(Vector{Operator{eltype(FT)}},V)) - - - -LowRankOperator(B::AbstractVector,S...) = LowRankOperator(convert(Vector{Operator{Float64}},B),S...) - -LowRankOperator(A::Fun,B::Operator) = LowRankOperator([A],[B]) - - -convert(::Type{Operator{T}},L::LowRankOperator{S}) where {S,T} = - LowRankOperator{S,T}(convert(Vector{VFun{S,T}},L.U), - convert(Vector{Operator{T}},L.V)) - - -datasize(L::LowRankOperator,k) = - k==1 ? mapreduce(ncoefficients,max,L.U) : mapreduce(a -> bandwidth(a,1) + bandwidth(a,2)+1,max,L.V) -datasize(L::LowRankOperator) = datasize(L,1),datasize(L,2) -bandwidths(L::LowRankOperator) = datasize(L,1)-1,datasize(L,2)-1 - -domainspace(L::LowRankOperator) = domainspace(first(L.V)) -rangespace(L::LowRankOperator) = space(first(L.U)) -promoterangespace(L::LowRankOperator,sp::Space) = LowRankOperator(map(u->Fun(u,sp),L.U),L.V) -promotedomainspace(L::LowRankOperator,sp::Space) = LowRankOperator(L.U,map(v->promotedomainspace(v,sp),L.V)) - -function Base.getindex(L::LowRankOperator,k::Integer,j::Integer) - ret=zero(eltype(L)) - for p=1:length(L.U) - if k≤ncoefficients(L.U[p]) - ret+=L.U[p].coefficients[k]*L.V[p][j] - end - end - ret -end - - - -rank(L::LowRankOperator) = length(L.U) - - --(L::LowRankOperator) = LowRankOperator(-L.U,L.V) - -*(L::LowRankOperator,f::Fun) = sum(map((u,v)->u*(v*f),L.U,L.V)) - - -*(A::LowRankOperator,B::LowRankOperator) = LowRankOperator(transpose(A.V*B.transpose(U))*A.U,B.V) -# avoid ambiguituy -for TYP in (:TimesOperator,:PlusOperator,:Conversion,:Operator) - @eval *(L::LowRankOperator,B::$TYP) = LowRankOperator(L.U,map(v->v*B,L.V)) - @eval *(B::$TYP,L::LowRankOperator) = LowRankOperator(map(u->B*u,L.U),L.V) -end - -+(A::LowRankOperator,B::LowRankOperator) = LowRankOperator([A.U;B.U],[A.V;B.V]) --(A::LowRankOperator,B::LowRankOperator) = LowRankOperator([A.U;-B.U],[A.V;B.V]) diff --git a/src/Operators/almostbanded/LowRankPertOperator.jl b/src/Operators/almostbanded/LowRankPertOperator.jl deleted file mode 100644 index aa789841..00000000 --- a/src/Operators/almostbanded/LowRankPertOperator.jl +++ /dev/null @@ -1,75 +0,0 @@ -struct LowRankPertOperator{OO,LR,T} <: Operator{T} - op::OO - pert::LR - - function LowRankPertOperator{OO,LR,T}(a::OO,b::LR) where {OO,LR,T} - @assert domainspace(a)==domainspace(b) - @assert rangespace(a)==rangespace(b) - new{OO,LR,T}(a,b) - end -end - -function LowRankPertOperator(Bin::Operator,Lin::LowRankOperator) - B,L2=promotedomainspace([Bin,Lin]) - rsp=rangespace(B) # use rangespace of B because LowRankOperator only - # needs convert, and its unlikely that the rangespaces - # will be inferred from L - L=promoterangespace(L2,rsp) - - LowRankPertOperator{typeof(B),typeof(L),promote_type(eltype(L),eltype(B))}(B,L) -end - - - -convert(::Type{Operator{T}},L::LowRankPertOperator) where {T} = - LowRankPertOperator(Operator{T}(L.op),Operator{T}(L.pert))::Operator{T} -convert(::Type{Operator},V::AbstractVector{OT}) where {OT<:Operator}=LowRankPertOperator(V) - - -Base.getindex(L::LowRankPertOperator,k::Integer,j::Integer)=L.op[k,j]+L.pert[k,j] - - -domainspace(L::LowRankPertOperator)=domainspace(L.op) -rangespace(L::LowRankPertOperator)=rangespace(L.op) -datasize(L::LowRankPertOperator,k...)=datasize(L.pert,k...) - -israggedbelow(P::LowRankPertOperator) = israggedbelow(P.op) && israggedbelow(P.pert) -bandwidths(P::LowRankPertOperator) = bandwidth(P.op,1)+bandwidth(P.pert,1) , bandwidth(P.op,2)+bandwidth(P.pert,2) - -colstop(L::LowRankPertOperator,k::Integer) = max(colstop(L.op,k),colstop(L.pert,k)) - -for OP in (:promotedomainspace,:promoterangespace) - @eval $OP(L::LowRankPertOperator,sp::Space)=LowRankPertOperator($OP(L.op,sp),$OP(L.pert,sp)) -end - - - -## algebra - -for OP in (:+,:-) - @eval $OP(A::LowRankPertOperator,B::LowRankPertOperator) = - LowRankPertOperator($OP(A.op,B.op),$OP(A.pert,B.pert)) -end - -*(L::LowRankPertOperator,f::Fun)=L.op*f+L.pert*f - -*(L::LowRankPertOperator,B::LowRankOperator)=L.op*B+L.pert*B -*(B::LowRankOperator,L::LowRankPertOperator)=B*L.op+B*L.pert - - - -*(A::LowRankPertOperator,B::LowRankPertOperator)=A.op*B + A.pert*B - -# ambiguity -for TYP in (:TimesOperator,:ZeroOperator,:PlusOperator,:Conversion,:Operator) - @eval begin - +(L::LowRankOperator,B::$TYP) = LowRankPertOperator(B,L) - +(B::$TYP,L::LowRankOperator) = LowRankPertOperator(B,L) - - -(L::LowRankOperator,B::$TYP)=LowRankPertOperator(-B,L) - -(B::$TYP,L::LowRankOperator)=LowRankPertOperator(B,-L) - - *(L::LowRankPertOperator,B::$TYP)=LowRankPertOperator(L.op*B,L.pert*B) - *(B::$TYP,L::LowRankPertOperator)=LowRankPertOperator(B*L.op,B*L.pert) - end -end diff --git a/src/Operators/almostbanded/almostbanded.jl b/src/Operators/almostbanded/almostbanded.jl deleted file mode 100644 index f7be156b..00000000 --- a/src/Operators/almostbanded/almostbanded.jl +++ /dev/null @@ -1,2 +0,0 @@ -include("LowRankOperator.jl") -include("LowRankPertOperator.jl") diff --git a/src/Operators/banded/CalculusOperator.jl b/src/Operators/banded/CalculusOperator.jl deleted file mode 100644 index fcdb4891..00000000 --- a/src/Operators/banded/CalculusOperator.jl +++ /dev/null @@ -1,299 +0,0 @@ -export Derivative,Integral,Laplacian,Volterra - - -abstract type CalculusOperator{S,OT,T}<:Operator{T} end - - -## Note that all functions called in calculus_operator must be exported - -macro calculus_operator(Op) - ConcOp=Meta.parse("Concrete"*string(Op)) - WrappOp=Meta.parse(string(Op)*"Wrapper") - DefaultOp=Meta.parse("Default"*string(Op)) - return esc(quote - # The SSS, TTT are to work around #9312 - abstract type $Op{SSS,OT,TTT} <: CalculusOperator{SSS,OT,TTT} end - - struct $ConcOp{S<:Space,OT,T} <: $Op{S,OT,T} - space::S # the domain space - order::OT - end - struct $WrappOp{BT<:Operator,S<:Space,OT,T} <: $Op{S,OT,T} - op::BT - order::OT - end - - @wrapper $WrappOp - - - ## Constructors - $ConcOp(sp::Space,k) = $ConcOp{typeof(sp),typeof(k),prectype(sp)}(sp,k) - - $Op(sp::UnsetSpace,k) = $ConcOp(sp,k) - $Op(sp::UnsetSpace,k::Real) = $ConcOp(sp,k) - $Op(sp::UnsetSpace,k::Integer) = $ConcOp(sp,k) - - function $DefaultOp(sp::Space,k) - csp=canonicalspace(sp) - if conversion_type(csp,sp)==csp # Conversion(sp,csp) is not banded, or sp==csp - error("Implement $(string($Op))($(string(sp)),$k)") - end - $WrappOp(TimesOperator([$Op(csp,k),Conversion(sp,csp)]),k) - end - - $DefaultOp(d,k) = $Op(Space(d),k) - - $DefaultOp(sp) = $Op(sp,1) - $DefaultOp() = $Op(UnsetSpace()) - $DefaultOp(k::Number) = $Op(UnsetSpace(),k) - $DefaultOp(k::Vector) = $Op(UnsetSpace(),k) - - $Op(x...) = $DefaultOp(x...) - $ConcOp(S::Space) = $ConcOp(S,1) - - function Base.convert(::Type{Operator{T}},D::$ConcOp) where T - if T==eltype(D) - D - else - $ConcOp{typeof(D.space),typeof(D.order),T}(D.space,D.order) - end - end - - $WrappOp(op::Operator,order) = - $WrappOp{typeof(op),typeof(domainspace(op)),typeof(order),eltype(op)}(op,order) - $WrappOp(op::Operator) = $WrappOp(op,1) - - function Base.convert(::Type{Operator{T}},D::$WrappOp) where T - if T==eltype(D) - D - else - # work around typeinfernece bug - op=convert(Operator{T},D.op) - $WrappOp{typeof(op),typeof(domainspace(op)),typeof(D.order),T}(op,D.order) - end - end - - ## Routines - ApproxFunBase.domain(D::$ConcOp) = domain(D.space) - ApproxFunBase.domainspace(D::$ConcOp) = D.space - - Base.getindex(::$ConcOp{UnsetSpace,OT,T},k::Integer,j::Integer) where {OT,T} = - error("Spaces cannot be inferred for operator") - ApproxFunBase.rangespace(D::$ConcOp{UnsetSpace,T}) where {T} = UnsetSpace() - - #promoting domain space is allowed to change range space - # for integration, we fall back on existing conversion for now - ApproxFunBase.promotedomainspace(D::$Op,sp::UnsetSpace) = D - - - function ApproxFunBase.promotedomainspace(D::$Op,sp::Space) - if isambiguous(domain(sp)) - $Op(typeof(sp)(domain(D)),D.order) - else - $Op(sp,D.order) - end - end - end) -# for func in (:rangespace,:domainspace,:bandwidths) -# # We assume the operator wrapped has the correct spaces -# @eval $func(D::$WrappOp)=$func(D.op) -# end -end - -choosedomainspace(M::CalculusOperator{UnsetSpace},sp::Space) = - iswrapper(M) ? choosedomainspace(M.op,sp) : sp # we assume the space itself will work - - - -@calculus_operator(Derivative) -@calculus_operator(Integral) -@calculus_operator(Volterra) - - - - - - - - -## Overrideable - - -## Convenience routines - - -integrate(d::Domain) = Integral(d,1) - - -# Default is to use ops -differentiate(f::Fun)=Derivative(space(f))*f -function integrate(f::Fun) - d=domain(f) - cd=canonicaldomain(d) - if typeof(d)==typeof(cd) || isperiodic(d) - Integral(space(f))*f - else - # map to canonical domain - setdomain(integrate(setdomain(f,cd)*fromcanonicalD(f)),d) - end -end - -function Base.sum(f::Fun) - d=domain(f) - cd=canonicaldomain(d) - if typeof(cd)==typeof(d) || isperiodic(d) - g=integrate(f) - last(g)-first(g) - else - # map first - sum(setdomain(f,cd)*fromcanonicalD(f)) - end -end - -function linesum(f::Fun) - cd=canonicaldomain(f) - d=domain(f) - - if isreal(d) - a,b=leftendpoint(d),rightendpoint(d) - sign(last(b)-first(a))*sum(f) - elseif typeof(cd)==typeof(d) || isperiodic(d) - error("override linesum for $(f.space)") - else - # map first - linesum(setdomain(f,cd)*abs(fromcanonicalD(f))) - end -end - - - - -# Multivariate - - - -@calculus_operator(Laplacian) - - -## Map to canonical -function DefaultDerivative(sp::Space,k::Integer) - if typeof(canonicaldomain(sp)).name==typeof(domain(sp)).name - # this is the normal default constructor - csp=canonicalspace(sp) - if conversion_type(csp,sp)==csp # Conversion(sp,csp) is not banded, or sp==csp - error("Implement Derivative($(string(sp)),$k)") - end - DerivativeWrapper(TimesOperator([Derivative(csp,k),Conversion(sp,csp)]),k) - else - D1=invfromcanonicalD(sp)*Derivative(setdomain(sp,canonicaldomain(sp))) - D=DerivativeWrapper(SpaceOperator(D1,sp,setdomain(rangespace(D1),domain(sp))),1) - if k==1 - D - else - DerivativeWrapper(TimesOperator(Derivative(rangespace(D),k-1),D),k) - end - end -end - - -function DefaultIntegral(sp::Space,k::Integer) - if typeof(canonicaldomain(sp)).name==typeof(domain(sp)).name - # this is the normal default constructor - csp=canonicalspace(sp) - if conversion_type(csp,sp)==csp # Conversion(sp,csp) is not banded, or sp==csp - # we require that Integral is overridden - error("Implement Integral($(string(sp)),$k)") - end - IntegralWrapper(TimesOperator([Integral(csp,k),Conversion(sp,csp)]),k) - elseif k > 1 - Q=Integral(sp,1) - IntegralWrapper(TimesOperator(Integral(rangespace(Q),k-1),Q),k) - else # k==1 - csp=setdomain(sp,canonicaldomain(sp)) - - x=Fun(identity,domain(csp)) - M=Multiplication(fromcanonicalD(sp,x),csp) - Q=Integral(rangespace(M))*M - IntegralWrapper(SpaceOperator(Q,sp,setdomain(rangespace(Q),domain(sp))),1) - end -end - - -for TYP in (:Derivative,:Integral,:Laplacian) - @eval begin - function *(D1::$TYP,D2::$TYP) - @assert domain(D1) == domain(D2) - - $TYP(domainspace(D2),D1.order+D2.order) - end - end -end - - -""" -`Derivative(sp::Space,k::Int)` represents the `k`-th derivative on `sp`. -""" -Derivative(::Space,::Int) - -""" -`Derivative(sp::Space,k::Vector{Int})` represents a partial derivative on a multivariate space. -For example, -```julia -Dx = Derivative(Chebyshev()^2,[1,0]) # ∂/∂x -Dy = Derivative(Chebyshev()^2,[0,1]) # ∂/∂y -``` -""" -Derivative(::Space,::Vector{Int}) - -""" -`Derivative(sp::Space)` represents the first derivative `Derivative(sp,1)`. -""" -Derivative(::Space) - -""" -`Derivative(k)` represents the `k`-th derivative, acting on an unset space. -Spaces will be inferred when applying or manipulating the operator. -""" -Derivative(k) - -""" -`Derivative()` represents the first derivative on an unset space. -Spaces will be inferred when applying or manipulating the operator. -""" -Derivative() - - -""" -`Integral(sp::Space,k::Int)` represents a `k`-th integral on `sp`. -There is no guarantee on normalization. -""" -Integral(::Space,::Int) - - -""" -`Integral(sp::Space)` represents the first integral `Integral(sp,1)`. -""" -Integral(::Space) - -""" -Integral(k)` represents the `k`-th integral, acting on an unset space. -Spaces will be inferred when applying or manipulating the operator. -""" -Integral(k) - -""" -`Intergral()` represents the first integral on an unset space. -Spaces will be inferred when applying or manipulating the operator. -""" -Integral() - -""" -`Laplacian(sp::Space)` represents the laplacian on space `sp`. -""" -Laplacian(::Space) - -""" -`Laplacian()` represents the laplacian on an unset space. -Spaces will be inferred when applying or manipulating the operator. -""" -Laplacian() diff --git a/src/Operators/banded/ConstantOperator.jl b/src/Operators/banded/ConstantOperator.jl deleted file mode 100644 index 5a0d9f7a..00000000 --- a/src/Operators/banded/ConstantOperator.jl +++ /dev/null @@ -1,160 +0,0 @@ -export ConstantOperator, IdentityOperator, BasisFunctional - -struct ConstantOperator{T,DS} <: Operator{T} - λ::T - space::DS - ConstantOperator{T,DS}(c::Number,sp::DS) where {T,DS} = new{T,DS}(convert(T,c),sp) - ConstantOperator{T,DS}(L::UniformScaling,sp::DS) where {T,DS} = new{T,DS}(convert(T,L.λ),sp) -end - - -ConstantOperator(::Type{T},c,sp::Space) where {T} = ConstantOperator{T,typeof(sp)}(c,sp) -ConstantOperator(::Type{T},c) where {T} = ConstantOperator(T,c,UnsetSpace()) -ConstantOperator(c::Number,sp::Space) = ConstantOperator(typeof(c),c,sp) -ConstantOperator(c::Number) = ConstantOperator(typeof(c),c) -ConstantOperator(L::UniformScaling) = ConstantOperator(L.λ) -ConstantOperator(L::UniformScaling,sp::Space) = ConstantOperator(L.λ,sp) -IdentityOperator() = ConstantOperator(1.0) -IdentityOperator(S::Space) = ConstantOperator(1.0,S) - -for OP in (:domainspace,:rangespace) - @eval $OP(C::ConstantOperator) = C.space -end - -promotedomainspace(C::ConstantOperator,sp::Space) = ConstantOperator(C.λ,sp) -promoterangespace(C::ConstantOperator,sp::Space,cursp::UnsetSpace) = ConstantOperator(C.λ,sp) - -bandwidths(T::ConstantOperator) = 0,0 - -isbandedblockbanded(::ConstantOperator) = true -blockbandwidths(::ConstantOperator) = 0,0 -subblockbandwidths(::ConstantOperator) = 0,0 - -getindex(C::ConstantOperator,k::Integer,j::Integer) = - k==j ? eltype(C)(C.λ) : zero(eltype(C)) - - -==(C1::ConstantOperator, C2::ConstantOperator) = C1.λ==C2.λ - -function convert(::Type{Operator{T}}, C::ConstantOperator) where T - if T == eltype(C) - C - else - ConstantOperator{T,typeof(C.space)}(C.λ,C.space) - end -end - -# zero needs to be different since it can take a space to -# a ConstantSpace, in creating functionals -convert(::Type{Operator{T}}, x::Number) where {T} = - x==0 ? ZeroOperator(T) : Multiplication(convert(T,x)) -convert(::Type{Operator{T}}, L::UniformScaling) where {T} = - ConstantOperator(T,L.λ) - -convert(::Type{Operator},n::Number) = convert(Operator{typeof(n)}, n) -convert(::Type{Operator},L::UniformScaling) = ConstantOperator(L.λ) - -## Algebra - -for op in (:+,:-,:*) - @eval ($op)(A::ConstantOperator,B::ConstantOperator) = ConstantOperator($op(A.λ,B.λ)) -end - - -## Basis Functional - -struct BasisFunctional{T,DS} <: Operator{T} - k::Int - space::DS -end - -@functional BasisFunctional - -BasisFunctional(k,sp) = BasisFunctional{Float64,typeof(sp)}(k, sp) -BasisFunctional(k) = BasisFunctional(k, ℓ⁰) - -bandwidths(B::BasisFunctional) = 0,B.k-1 -domainspace(B::BasisFunctional) = B.space - -convert(::Type{Operator{T}},B::BasisFunctional) where {T} = BasisFunctional{T,typeof(B.space)}(B.k,B.space) - -Base.getindex(op::BasisFunctional{T},k::Integer) where {T} = (k==op.k) ? one(T) : zero(T) -Base.getindex(op::BasisFunctional{T},k::AbstractRange) where {T} = convert(Vector{T},k.==op.k) - -struct FillFunctional{T} <: Operator{T} - λ::T -end - -@functional FillFunctional - -domainspace(B::FillFunctional) = ℓ⁰ - -Base.getindex(op::FillFunctional,k::Integer)=op.λ -Base.getindex(op::FillFunctional,k::AbstractRange)=fill(op.λ,length(k)) - -## Zero is a special operator: it makes sense on all spaces, and between all spaces - -struct ZeroOperator{T,S,V} <: Operator{T} - domainspace::S - rangespace::V -end - -ZeroOperator(::Type{T},d::Space,v::Space) where {T} = ZeroOperator{T,typeof(d),typeof(v)}(d,v) -ZeroOperator(::Type{T},S::Space) where {T} = ZeroOperator(T,S,ZeroSpace(S)) -ZeroOperator(d::Space,v::Space) = ZeroOperator(Float64,d,v) -ZeroOperator(S::Space) = ZeroOperator(S,ZeroSpace(S)) -ZeroOperator() = ZeroOperator(UnsetSpace(),UnsetSpace()) -ZeroOperator(::Type{T}) where {T} = ZeroOperator(T,UnsetSpace(),UnsetSpace()) - - -convert(::Type{Operator{T}},Z::ZeroOperator) where {T} = - ZeroOperator(T,Z.domainspace,Z.rangespace) - - -Base.zero(A::Operator) = ZeroOperator(domainspace(A),rangespace(A)) - - -domainspace(Z::ZeroOperator)=Z.domainspace -rangespace(Z::ZeroOperator)=Z.rangespace - -bandwidths(T::ZeroOperator) = -720,-720 # 6! -blockbandwidths(T::ZeroOperator) = -720,-720 # 6! -subblockbandwidths(T::ZeroOperator) = -720, -720 - -isbandedblockbandedabove(::ZeroOperator) = true -isbandedblockbandedbelow(::ZeroOperator) = true - -getindex(C::ZeroOperator,k::Integer) = zero(eltype(C)) -getindex(C::ZeroOperator,k::Integer,j::Integer) = zero(eltype(C)) - -promotedomainspace(Z::ZeroOperator,sp::UnsetSpace) = Z -promoterangespace(Z::ZeroOperator,sp::UnsetSpace) = Z -promotedomainspace(Z::ZeroOperator,sp::Space) = ZeroOperator(sp,rangespace(Z)) -promoterangespace(Z::ZeroOperator,sp::Space) = ZeroOperator(domainspace(Z),sp) - - - - -isconstop(::Union{ZeroOperator,ConstantOperator}) = true -isconstop(S::SpaceOperator) = isconstop(S.op) -isconstop(_) = false - -iszeroop(::ZeroOperator) = true -iszeroop(A::ConstantOperator) = A.λ==0.0 -iszeroop(A) = false - -convert(::Type{T},::ZeroOperator) where {T<:Number} = zero(T) -convert(::Type{T},C::ConstantOperator) where {T<:Number} = convert(T,C.λ) -convert(::Type{T},S::SpaceOperator) where {T<:Number} = convert(T,S.op) - - - -## SubOperator convert -## Special case for ZeroOperator -for TYP in (:RaggedMatrix,:Matrix,:BandedMatrix, - :BlockBandedMatrix,:BandedBlockBandedMatrix) - @eval begin - $TYP(S::SubOperator{T,ZO}) where {T,ZO<:ZeroOperator} = $TYP(Zeros, S) - $TYP(S::SubOperator{T,ZO,NTuple{2,UnitRange{Int}}}) where {T,ZO<:ZeroOperator} = $TYP(Zeros, S) - end -end diff --git a/src/Operators/banded/Conversion.jl b/src/Operators/banded/Conversion.jl deleted file mode 100644 index e418e187..00000000 --- a/src/Operators/banded/Conversion.jl +++ /dev/null @@ -1,85 +0,0 @@ -export Conversion - -abstract type Conversion{T}<:Operator{T} end - -struct ConcreteConversion{S<:Space,V<:Space,T} <: Conversion{T} - domainspace::S - rangespace::V -end - - -ConcreteConversion(a::Space,b::Space)=ConcreteConversion{typeof(a),typeof(b), - promote_type(rangetype(a),rangetype(b))}(a,b) - - -function convert(::Type{Operator{T}},C::ConcreteConversion{S,V}) where {T,S,V} - if T==eltype(C) - C - else - ConcreteConversion{S,V,T}(C.domainspace,C.rangespace) - end -end - -domainspace(C::ConcreteConversion)=C.domainspace -rangespace(C::ConcreteConversion)=C.rangespace - - - - -function defaultConversion(a::Space,b::Space)::Any - if a==b - Conversion(a) - elseif conversion_type(a,b)==NoSpace() - sp=canonicalspace(a) - if typeof(sp) == typeof(a) - error("Implement Conversion from " * string(typeof(sp)) * " to " * string(typeof(b))) - elseif typeof(sp) == typeof(b) - error("Implement Conversion from " * string(typeof(a)) * " to " * string(typeof(sp))) - else - Conversion(a,sp,b) - end - else - error("Implement Conversion from " * string(typeof(a)) * " to " * string(typeof(b))) - end -end - -Conversion(a::Space,b::Space) = defaultConversion(a,b) -Conversion(a::Space) = ConversionWrapper(Operator(I,a)) -Conversion() = ConversionWrapper(Operator(I,UnsetSpace())) - - -## Wrapper -# this allows for a Derivative implementation to return another operator, use a SpaceOperator containing -# the domain and range space -# but continue to know its a derivative - -struct ConversionWrapper{S<:Operator,T} <: Conversion{T} - op::S -end - -@wrapper ConversionWrapper - - -ConversionWrapper(::Type{T},op) where {T} = ConversionWrapper{typeof(op),T}(op) -ConversionWrapper(B::Operator) = - ConversionWrapper{typeof(B),eltype(B)}(B) -Conversion(A::Space,B::Space,C::Space) = - ConversionWrapper(Conversion(B,C)*Conversion(A,B)) -Conversion(A::Space,B::Space,C::Space,D::Space...) = - ConversionWrapper(Conversion(C,D...)*Conversion(B,C)*Conversion(A,B)) - -==(A::ConversionWrapper,B::ConversionWrapper) = A.op==B.op - - -function convert(::Type{Operator{T}},D::ConversionWrapper) where T - if T==eltype(D) - D - else - BO=convert(Operator{T},D.op) - ConversionWrapper{typeof(BO),T}(BO) - end -end - - - -#promotedomainspace(P::Conversion,sp::Space)=ConversionWrapper(Operator(I, sp)) diff --git a/src/Operators/banded/DiagonalOperator.jl b/src/Operators/banded/DiagonalOperator.jl deleted file mode 100644 index e69de29b..00000000 diff --git a/src/Operators/banded/Multiplication.jl b/src/Operators/banded/Multiplication.jl deleted file mode 100644 index 29a117d4..00000000 --- a/src/Operators/banded/Multiplication.jl +++ /dev/null @@ -1,171 +0,0 @@ -export Multiplication - -abstract type Multiplication{D,S,T} <:Operator{T} end - -struct ConcreteMultiplication{D<:Space,S<:Space,T} <: Multiplication{D,S,T} - f::VFun{D,T} - space::S - - ConcreteMultiplication{D,S,T}(f::Fun{D,T},sp::S) where {D,S,T} = new{D,S,T}(f,sp) -end - -function ConcreteMultiplication(::Type{V},f::Fun{D,T},sp::Space) where {V,D,T} - if !domainscompatible(space(f),sp) - error("Domain mismatch: cannot multiply function on $(domain(f)) to function on $(domain(sp))") - end - ConcreteMultiplication{D,typeof(sp),V}( - convert(Fun{D,V},chop(f,40*eps(cfstype(f)))),sp) -end - - -function ConcreteMultiplication(f::Fun{D,T},sp::Space) where {D,T} - if !domainscompatible(space(f),sp) - error("Domain mismatch: cannot multiply function on $(domain(f)) to function on $(domain(sp))") - end - V = promote_type(T,rangetype(sp)) - ConcreteMultiplication{D,typeof(sp),V}(convert(Fun{D,V},chop(f,40*eps(cfstype(f)))),sp) -end - -# We do this in two stages to support Modifier spaces -# without ambiguity errors -function defaultMultiplication(f::Fun,sp::Space) - csp=space(f) - if csp==sp || !hasconversion(sp,csp) - error("Implement Multiplication(::Fun{$(typeof(space(f)))},::$(typeof(sp)))") - end - MultiplicationWrapper(f,Multiplication(f,csp)*Conversion(sp,csp)) -end - -Multiplication(f::Fun,sp::Space) = defaultMultiplication(f,sp) - - -Multiplication(f::Fun,sp::UnsetSpace) = ConcreteMultiplication(f,sp) -Multiplication(f::Fun) = Multiplication(f,UnsetSpace()) - -Multiplication(c::Number,sp::Space) = Multiplication(Fun(c),sp) -Multiplication(sp::Space,c::Number) = Multiplication(sp,Fun(c)) -Multiplication(c::Number) = Multiplication(Fun(c) ) - -# This covers right multiplication unless otherwise specified. -Multiplication(S::Space,f::Fun) = Multiplication(f,S) - - -function convert(::Type{Operator{T}},C::ConcreteMultiplication{S,V}) where {S,V,T} - if T==eltype(C) - C - else - ConcreteMultiplication{S,V,T}(Fun{S,T}(C.f),C.space) - end -end - -domainspace(M::ConcreteMultiplication{D,S,T}) where {D,S,T} = M.space -domain(T::ConcreteMultiplication) = domain(T.f) - - -## Default implementation: try converting to space of M.f - -# avoid ambiguity -rangespace(D::ConcreteMultiplication{F,UnsetSpace,T}) where {F,T} = UnsetSpace() -getindex(D::ConcreteMultiplication{F,UnsetSpace,T},k::Integer,j::Integer) where {F,T} = - error("No range space attached to Multiplication") - - - - - - -##multiplication can always be promoted, range space is allowed to change -promotedomainspace(D::Multiplication,sp::UnsetSpace) = D -promotedomainspace(D::Multiplication,sp::Space) = Multiplication(D.f,sp) -promoterangespace(D::ConcreteMultiplication{P,UnsetSpace},sp::UnsetSpace) where {P} = D -promoterangespace(D::ConcreteMultiplication{P,UnsetSpace},sp::Space) where {P} = - promoterangespace(Multiplication(D.f,ConstantSpace(domain(sp))), sp) - -choosedomainspace(M::ConcreteMultiplication{D,UnsetSpace},::UnsetSpace) where {D} = space(M.f) -# we assume multiplication maps spaces to themselves -choosedomainspace(M::ConcreteMultiplication{D,UnsetSpace},sp::Space) where {D} = sp - - -diagm(a::Fun) = Multiplication(a) - -struct MultiplicationWrapper{D<:Space,S<:Space,O<:Operator,T} <: Multiplication{D,S,T} - f::VFun{D,T} - op::O -end - -MultiplicationWrapper(T::Type,f::Fun{D,V},op::Operator) where {D<:Space,V} = MultiplicationWrapper{D,typeof(domainspace(op)),typeof(op),T}(f,op) -MultiplicationWrapper(f::Fun{D,V},op::Operator) where {D<:Space,V} = MultiplicationWrapper(eltype(op),f,op) - -@wrapper MultiplicationWrapper - -function convert(::Type{Operator{TT}},C::MultiplicationWrapper{S,V,O,T}) where {TT,S,V,O,T} - if TT==T - C - else - MultiplicationWrapper(Fun{S,TT}(C.f),Operator{TT}(C.op))::Operator{TT} - end -end - - -## Multiplication operators allow us to multiply two spaces - - - -hasfasttransform(_) = false -hasfasttransform(f::Fun) = hasfasttransform(space(f)) -hasfasttransformtimes(f,g) = pointscompatible(f,g) && hasfasttransform(f) && hasfasttransform(g) - - -# This should be overriden whenever the multiplication space is different -function default_mult(f::Fun,g::Fun) - # When the spaces differ we promote and multiply - if domainscompatible(space(f),space(g)) - m,n = ncoefficients(f),ncoefficients(g) - # Heuristic division of parameter space between value-space and coefficient-space multiplication. - if hasfasttransformtimes(f,g) && log10(m)*log10(n)>4 - transformtimes(f,g) - elseif m≤n - coefficienttimes(f,g) - else - coefficienttimes(g,f) - end - else - sp=union(space(f),space(g)) - Fun(f,sp)*Fun(g,sp) - end -end - -*(f::Fun,g::Fun) = default_mult(f,g) - -coefficienttimes(f::Fun,g::Fun) = Multiplication(f,space(g))*g - -function transformtimes(f::Fun,g::Fun,n) - @assert pointscompatible(space(f),space(g)) - isempty(f.coefficients) && return f - isempty(g.coefficients) && return g - f2,g2,sp = pad(f,n),pad(g,n),space(f) - hc = transform(sp,values(f2).*values(g2)) - chop!(Fun(sp,hc),10eps(eltype(hc))) -end -transformtimes(f::Fun,g::Fun) = transformtimes(f,g,ncoefficients(f) + ncoefficients(g) - 1) - - - -*(a::Fun,L::UniformScaling) = Multiplication(a*L.λ,UnsetSpace()) -*(L::UniformScaling,a::Fun) = L.λ*a - - -## docs - -""" -`Multiplication(f::Fun,sp::Space)` is the operator representing multiplication by -`f` on functions in the space `sp`. -""" -Multiplication(::Fun,::Space) - -""" -`Multiplication(f::Fun)` is the operator representing multiplication by -`f` on an unset space of functions. Spaces will be inferred when applying or -manipulating the operator. -""" -Multiplication(::Fun) diff --git a/src/Operators/banded/PermutationOperator.jl b/src/Operators/banded/PermutationOperator.jl deleted file mode 100644 index 01a7a653..00000000 --- a/src/Operators/banded/PermutationOperator.jl +++ /dev/null @@ -1,62 +0,0 @@ -# Creates a operator that permutes rows, in blocks of size -# length(perm) -struct PermutationOperator{T,DS,RS} <: Operator{T} - perm::Vector{Int} - domainspace::DS - rangespace::RS -end -PermutationOperator{T}(prm, ds::DS, rs::RS) where {T, DS<:Space,RS<:Space} = - PermutationOperator{T,DS,RS}(prm, ds, rs) -PermutationOperator(prm, ds, rs) = PermutationOperator{Int}(prm, ds, rs) -PermutationOperator(prm) = PermutationOperator(prm, ℓ⁰, ℓ⁰) - -domainspace(P::PermutationOperator) = P.domainspace -rangespace(P::PermutationOperator) = P.rangespace - - -convert(::Type{Operator{T}},P::PermutationOperator) where {T} = - PermutationOperator{T}(P.perm, P.domainspace, P.rangespace) - -function bandwidths(P::PermutationOperator) - dfs=P.perm-(1:length(P.perm)) - -minimum(dfs),maximum(dfs) -end - -function getindex(P::PermutationOperator{T},k::Integer,j::Integer) where T - n=length(P.perm) - if (k-1)÷n == (j-1)÷n # diagonal blocks - k=mod(k-1,n)+1 - j=mod(j-1,n)+1 - convert(T,P.perm[k]==j) - else - zero(T) - end -end - -# the permutation that rearranges a to be b -multiplyperm(b,a) = Int[a[bk] for bk in b] -perm(a::Vector,b::Vector) = multiplyperm(invperm(sortperm(b)),sortperm(a)) -perm(a::Tuple,b::Tuple) = perm(collect(a),collect(b)) - - -struct NegateEven{T,DS,RS} <: Operator{T} - domainspace::DS - rangespace::RS -end - -NegateEven{T}(ds::DS, rs::RS) where {DS<:Space,RS<:Space,T} = - NegateEven{T,DS,RS}(ds, rs) -NegateEven(ds, rs) = NegateEven{Int}(ds, rs) -NegateEven() = NegateEven(ℓ⁰,ℓ⁰) - -domainspace(P::NegateEven) = P.domainspace -rangespace(P::NegateEven) = P.rangespace - -convert(::Type{Operator{T}},P::NegateEven) where {T} = - NegateEven{T}(P.domainspace, P.rangespace) - -bandwidths(P::NegateEven) = (0,0) - - -getindex(P::NegateEven{T},k::Integer,j::Integer) where {T} = - k==j ? (iseven(k) ? -one(T) : one(T)) : zero(T) diff --git a/src/Operators/banded/Reverse.jl b/src/Operators/banded/Reverse.jl deleted file mode 100644 index d4f725c8..00000000 --- a/src/Operators/banded/Reverse.jl +++ /dev/null @@ -1,18 +0,0 @@ - - -for TYP in (:ReverseOrientation,:Reverse) - WRAP = Meta.parse(string(TYP)*"Wrapper") - @eval begin - abstract type $TYP{T} <: Operator{T} end - - struct $WRAP{OS,T} <: Operator{T} - op::OS - end - - $WRAP(op::Operator) = $WRAP{typeof(op),eltype(op)}(op) - convert(::Type{Operator{T}},op::$TYP) where {T} = $TYP{T}() - convert(::Type{Operator{T}},op::$WRAP) where {T} = $WRAP(Operator{T}(op.op))::Operator{T} - - @wrapper $WRAP - end -end diff --git a/src/Operators/banded/ToeplitzOperator.jl b/src/Operators/banded/ToeplitzOperator.jl deleted file mode 100644 index b9091de5..00000000 --- a/src/Operators/banded/ToeplitzOperator.jl +++ /dev/null @@ -1,270 +0,0 @@ -export ToeplitzOperator, HankelOperator - - -mutable struct ToeplitzOperator{T<:Number} <: Operator{T} - negative::Vector{T} - nonnegative::Vector{T} -end - - -ToeplitzOperator(V::Vector{T},W::Vector{Q}) where {T<:Number,Q<:Number} = - ToeplitzOperator{promote_type(T,Q)}(V,W) -ToeplitzOperator(V::AbstractVector,W::AbstractVector) = - ToeplitzOperator(collect(V),collect(W)) - -convert(::Type{Operator{TT}},T::ToeplitzOperator) where {TT} = - ToeplitzOperator(convert(Vector{TT},T.negative),convert(Vector{TT},T.nonnegative)) - -for op in (:(Base.real), :(Base.imag)) - @eval $op(T::ToeplitzOperator) = ToeplitzOperator($op(T.negative), $op(T.nonnegative)) -end - -function SymToeplitzOperator(V::Vector) - W=V[2:end] - V=copy(V) - V[1]*=2 - ToeplitzOperator(W,V) -end - -for OP in (:domainspace,:rangespace) - @eval $OP(T::ToeplitzOperator) = ℓ⁰ -end - -getindex(T::ToeplitzOperator,k::Integer,j::Integer) = - toeplitz_getindex(T.negative,T.nonnegative,k,j) - -function toeplitz_getindex(negative::AbstractVector{T},nonnegative::AbstractVector{T},k::Integer,j::Integer) where T - if 0isa(i,AbstractFill) && getindex_value(i) == 1, dsi.blocks) && - all(i->isa(i,AbstractFill) && getindex_value(i) == 1, rsi.blocks) - l,u = 0,0 - for k=1:p,j=1:p - l=max(l,p*bandwidth(ops[k,j],1)+k-j) - end - for k=1:p,j=1:p - u=max(u,p*bandwidth(ops[k,j],2)+j-k) - end - elseif p == 1 && size(ops,2) == 2 && size(ops[1],2) == 1 - # special case for example - l,u = max(bandwidth(ops[1],1),bandwidth(ops[2],1)-1),bandwidth(ops[2],2)+1 - else - l,u = (1-dimension(rs),dimension(ds)-1) # not banded - end - - - InterlaceOperator(ops,ds,rs, - cache(dsi), - cache(rsi), - (l,u)) -end - - -function InterlaceOperator(ops::Vector{Operator{T}},ds::Space,rs::Space) where T - # calculate bandwidths - p=size(ops,1) - if all(isbanded,ops) - l,u = 0,0 - #TODO: this code assumes an interlace strategy that might not be right - for k=1:p - l=max(l,p*bandwidth(ops[k],1)+k-1) - end - for k=1:p - u=max(u,p*bandwidth(ops[k],2)+1-k) - end - else - l,u = (1-dimension(rs),dimension(ds)-1) # not banded - end - - - InterlaceOperator(ops,ds,rs, - cache(BlockInterlacer(tuple(blocklengths(ds)))), - cache(interlacer(rs)), - (l,u)) -end - -function InterlaceOperator(opsin::AbstractMatrix{Operator{T}}) where {T} - isempty(opsin) && throw(ArgumentError("Cannot create InterlaceOperator from empty Matrix")) - ops=promotespaces(opsin) - InterlaceOperator(ops,domainspace(ops),rangespace(ops[:,1])) -end - -function InterlaceOperator(opsin::AbstractMatrix{Operator{T}},::Type{DS},::Type{RS}) where {T,DS<:Space,RS<:Space} - isempty(opsin) && throw(ArgumentError("Cannot create InterlaceOperator from empty Matrix")) - ops=promotespaces(opsin) - InterlaceOperator(ops,DS(components(domainspace(ops))),RS(rangespace(ops[:,1]).spaces)) -end - - -function InterlaceOperator(opsin::RowVector{Operator{T}}) where {T} - isempty(opsin) && throw(ArgumentError("Cannot create InterlaceOperator from empty Matrix")) - - ops=promotespaces(opsin) - InterlaceOperator(ops,domainspace(ops),rangespace(ops[1])) -end - -function InterlaceOperator(opsin::RowVector{Operator{T}},::Type{DS},::Type{RS}) where {T,DS<:Space,RS<:Space} - isempty(opsin) && throw(ArgumentError("Cannot create InterlaceOperator from empty Matrix")) - ops=promotespaces(opsin) - InterlaceOperator(ops,DS(components(domainspace(ops))),rangespace(ops[1])) -end - - - - -InterlaceOperator(opsin::AbstractMatrix{Operator{T}},::Type{DS}) where {T,DS<:Space} = - InterlaceOperator(opsin,DS,DS) - -InterlaceOperator(opsin::AbstractMatrix,S...) = - InterlaceOperator(Matrix{Operator{mapreduce(eltype,promote_type,opsin)}}(promotespaces(opsin)),S...) - -function InterlaceOperator(opsin::Vector{Operator{T}}) where T - ops=promotedomainspace(opsin) - InterlaceOperator(ops,domainspace(first(ops)),rangespace(ops)) -end - -InterlaceOperator(ops::AbstractArray{T,p}) where {T,p} = - InterlaceOperator(Array{Operator{mapreduce(eltype,promote_type,ops)},p}(ops)) - - -function convert(::Type{Operator{T}},S::InterlaceOperator) where T - if T == eltype(S) - S - else - ops=Array{Operator{T}}(undef, size(S.ops)...) - for j=1:size(S.ops,2),k=1:size(S.ops,1) - ops[k,j]=S.ops[k,j] - end - InterlaceOperator(ops,domainspace(S),rangespace(S), - S.domaininterlacer,S.rangeinterlacer,S.bandwidths) - end -end - - - -#TODO: More efficient to save bandwidth -bandwidths(M::InterlaceOperator) = M.bandwidths - -blockbandwidths(M::InterlaceOperator) = - (mapreduce(op->blockbandwidth(op,1),max,M.ops), - mapreduce(op->blockbandwidth(op,2),max,M.ops)) - -isblockbanded(M::InterlaceOperator) = all(isblockbanded,M.ops) - -function blockcolstop(M::InterlaceOperator,J::Integer) - if isblockbandedbelow(M) - Block(J + blockbandwidth(M,1)) - else - mapreduce(op->blockcolstop(op,J),max,M.ops) - end -end - - - -function colstop(M::InterlaceOperator{T},j::Integer) where T -# b=bandwidth(M,1) - if isbandedbelow(M) - min(j+bandwidth(M,1)::Int,size(M,1))::Int - elseif isblockbandedbelow(M) - J=block(domainspace(M), j)::Block{1} - blockstop(rangespace(M), blockcolstop(M,J)::Block{1})::Int - else #assume is raggedbelow - K = 0 - (J,jj) = M.domaininterlacer[j] - for N = 1:size(M.ops,1) - cs = colstop(M.ops[N,J],jj)::Int - if cs > 0 - K = max(K,findfirst(M.rangeinterlacer,(N,cs))::Int) - end - end - K - end -end - -israggedbelow(M::InterlaceOperator) = all(israggedbelow,M.ops) - -getindex(op::InterlaceOperator,k::Integer,j::Integer) = - error("Higher tensor InterlaceOperators not supported") - -function getindex(op::InterlaceOperator{T,2},k::Integer,j::Integer) where {T} - M,J = op.domaininterlacer[j] - N,K = op.rangeinterlacer[k] - op.ops[N,M][K,J]::T -end - -# the domain is not interlaced -function getindex(op::InterlaceOperator{T,1},k::Integer,j::Integer) where T - N,K = op.rangeinterlacer[k] - op.ops[N][K,j]::T -end - -function getindex(op::InterlaceOperator{T},k::Integer) where T - if size(op,1) == 1 - op[1,k] - elseif size(op,2) == 1 - op[k,1] - else - error("Only implemented for row/column operators.") - end -end - - -findsub(cr,ν) = findall(x->x[1]==ν,cr) - -function getindex(L::InterlaceOperator{T},kr::UnitRange) where T - ret=zeros(T,length(kr)) - - if size(L,1) == 1 - ds=domainspace(L) - cr=cache(interlacer(ds))[kr] - elseif size(L,2) == 1 - rs=rangespace(L) - cr=cache(interlacer(rs))[kr] - else - error("Only implemented for row/column operators.") - end - - for ν=1:length(L.ops) - # indicies of ret - ret_kr=findsub(cr,ν) - - # block indices - if !isempty(ret_kr) - sub_kr=cr[ret_kr[1]][2]:cr[ret_kr[end]][2] - - LinearAlgebra.axpy!(1.0,L.ops[ν][sub_kr],view(ret,ret_kr)) - end - end - ret -end - -# overwritten for functions -# this won't work in 0.4 as expected, though the user -# should call vec anyways for 0.5 compatibility -function getindex(L::InterlaceOperator,k::Integer,j) - if k==1 && size(L,1) == 1 - L[j] - else - defaultgetindex(L,k,j) - end -end - -function getindex(L::InterlaceOperator,k,j::Integer) - if j==1 && size(L,2) == 1 - L[k] - else - defaultgetindex(L,k,j) - end -end - -##### -# optimized copy routine for when there is a single domainspace -# and no interlacing of the columns is necessary -# this is especially important for \ -###### -for TYP in (:BandedMatrix, :BlockBandedMatrix, :BandedBlockBandedMatrix, :RaggedMatrix, - :Matrix) - @eval begin - function $TYP(S::SubOperator{T,InterlaceOperator{T,1,SS,PS,DI,RI,BI}, - Tuple{UnitRange{Int},UnitRange{Int}}}) where {SS,PS,DI,RI,BI,T} - kr,jr=parentindices(S) - L=parent(S) - - ret=$TYP(Zeros, S) - - ds=domainspace(L) - rs=rangespace(L) - cr=cache(interlacer(rs))[kr] - for ν=1:length(L.ops) - # indicies of ret - ret_kr=findsub(cr,ν) - - # block indices - if !isempty(ret_kr) - sub_kr=cr[ret_kr[1]][2]:cr[ret_kr[end]][2] - - LinearAlgebra.axpy!(1.0,view(L.ops[ν],sub_kr,jr),view(ret,ret_kr,:)) - end - end - ret - end - - function $TYP(S::SubOperator{T,InterlaceOperator{T,2,SS,PS,DI,RI,BI}, - Tuple{UnitRange{Int},UnitRange{Int}}}) where {SS,PS,DI,RI,BI,T} - kr,jr=parentindices(S) - L=parent(S) - - ret=$TYP(Zeros, S) - - if isempty(kr) || isempty(jr) - return ret - end - - ds=domainspace(L) - rs=rangespace(L) - cr=L.rangeinterlacer[kr] - cd=L.domaininterlacer[jr] - for ν=1:size(L.ops,1),μ=1:size(L.ops,2) - # indicies of ret - ret_kr=findsub(cr,ν) - ret_jr=findsub(cd,μ) - - # block indices - if !isempty(ret_kr) && !isempty(ret_jr) - sub_kr=cr[ret_kr[1]][2]:cr[ret_kr[end]][2] - sub_jr=cd[ret_jr[1]][2]:cd[ret_jr[end]][2] - - LinearAlgebra.axpy!(1.0,view(L.ops[ν,μ],sub_kr,sub_jr), - view(ret,ret_kr,ret_jr)) - end - end - ret - end - end -end - - -## Build block-by-block -function blockbanded_interlace_convert!(S,ret) - T = eltype(S) - KR,JR = parentindices(S) - l,u=blockbandwidths(S)::Tuple{Int,Int} - - M = map(op -> begin - KR_size = Block.(Int(first(KR)):min(Int(last(KR)),blocksize(op,1))) - JR_size = Block.(Int(first(JR)):min(Int(last(JR)),blocksize(op,2))) - BlockBandedMatrix(view(op, KR_size, JR_size)) - end, parent(S).ops) - - for J=blockaxes(ret,2),K=blockcolrange(ret,Int(J)) - Bs=view(ret,K,J) - j = 0 - for ξ=1:size(M,2) - k = 0 - m = 0 - for κ=1:size(M,1) - if K.n[1] ≤ blocksize(M[κ,ξ],1) && J.n[1] ≤ blocksize(M[κ,ξ],2) - MKJ = M[κ,ξ][K,J]::Matrix{T} - n,m = size(MKJ) - Bs[k+1:k+n,j+1:j+m] = MKJ - k += n - end - end - j += m - end - end - ret -end - -for d in (:1,:2) - @eval BlockBandedMatrix(S::SubOperator{T,InterlaceOperator{T,$d,SS,PS,DI,RI,BI}, - Tuple{BlockRange1,BlockRange1}}) where {SS,PS,DI,RI,BI,T} = - blockbanded_interlace_convert!(S, BlockBandedMatrix(Zeros, S)) -end - - - - - -domainspace(IO::InterlaceOperator) = IO.domainspace -rangespace(IO::InterlaceOperator) = IO.rangespace - -#tests whether an operator can be made into a column -iscolop(op) = isconstop(op) -iscolop(::Multiplication) = true - -promotedomainspace(A::InterlaceOperator{T,1},sp::Space) where {T} = - InterlaceOperator(map(op->promotedomainspace(op,sp),A.ops)) - - -interlace(A::AbstractArray{T}) where {T<:Operator} = InterlaceOperator(A) - -const OperatorTypes = Union{Operator,Fun,Number,UniformScaling} - - - -operators(A::InterlaceOperator) = A.ops -operators(A::Matrix{<:Operator}) = A -operators(A::Operator) = [A] - -Base.vcat(A::MatrixInterlaceOperator...) = - InterlaceOperator(vcat(map(operators,A)...)) -function _vcat(A::OperatorTypes...) - Av = Vector{Operator{mapreduce(eltype,promote_type,A)}}() - for a in A - if a isa VectorInterlaceOperator - append!(Av,a.ops) - else - push!(Av,a) - end - end - InterlaceOperator(vnocat(Av...)) -end - - - -Base.hcat(A::Union{VectorInterlaceOperator,MatrixInterlaceOperator}...) = - InterlaceOperator(hcat(map(A->A.ops,A)...)) -_hcat(A::OperatorTypes...) = InterlaceOperator(hnocat(A...)) -function _hvcat(rows::Tuple{Vararg{Int}},as::OperatorTypes...) - # Based on Base - nbr = length(rows) # number of block rows - rs = Array{Any,1}(undef, nbr) - a = 1 - for i = 1:nbr - rs[i] = hcat(map(op -> convert(Operator,op),as[a:a-1+rows[i]])...) - a += rows[i] - end - vcat(rs...) -end - -Base.vcat(A::Operator, B::OperatorTypes...) = _vcat(A, B...) -Base.hcat(A::Operator, B::OperatorTypes...) = _hcat(A, B...) -Base.hvcat(rows::Tuple{Vararg{Int}}, A::Operator, B::OperatorTypes...) = - _hvcat(rows, A, B...) - -Base.vcat(C::Union{Fun,Number,UniformScaling}, A::Operator, B::OperatorTypes...) = _vcat(C, A, B...) -Base.hcat(C::Union{Fun,Number,UniformScaling}, A::Operator, B::OperatorTypes...) = _hcat(C, A, B...) -Base.hvcat(rows::Tuple{Vararg{Int}}, C::Union{Fun,Number,UniformScaling}, A::Operator, B::OperatorTypes...) = - _hvcat(rows, C, A, B...) - -Base.vcat(D::Union{Fun,Number,UniformScaling}, C::Union{Fun,Number,UniformScaling}, A::Operator, B::OperatorTypes...) = _vcat(D, C, A, B...) -Base.hcat(D::Union{Fun,Number,UniformScaling}, C::Union{Fun,Number,UniformScaling}, A::Operator, B::OperatorTypes...) = _hcat(D, C, A, B...) -Base.hvcat(rows::Tuple{Vararg{Int}}, D::Union{Fun,Number,UniformScaling}, C::Union{Fun,Number,UniformScaling}, A::Operator, B::OperatorTypes...) = - _hvcat(rows, D, C, A, B...) - - -## Convert Matrix operator to operators - -Operator(M::AbstractArray{OO}) where {OO<:Operator} = InterlaceOperator(M) - - - - -function interlace_choosedomainspace(ops,sp::UnsetSpace) - # this ensures correct dispatch for unino - sps = Vector{Space}( - filter(x->!isambiguous(x),map(choosedomainspace,ops))) - if isempty(sps) - UnsetSpace() - else - union(sps...) - end -end - - -function interlace_choosedomainspace(ops,rs::Space) - # this ensures correct dispatch for unino - sps = Vector{Space}( - filter(x->!isambiguous(x),map((op)->choosedomainspace(op,rs),ops))) - if isempty(sps) - UnsetSpace() - else - union(sps...) - end -end - - -choosedomainspace(A::InterlaceOperator{T,1},rs::Space) where {T} = - interlace_choosedomainspace(A.ops,rs) - - -choosedomainspace(A::InterlaceOperator{T,2},rs::Space) where {T} = - Space([interlace_choosedomainspace(A.ops[:,k],rs) for k=1:size(A.ops,2)]) diff --git a/src/Operators/general/OperatorFunction.jl b/src/Operators/general/OperatorFunction.jl deleted file mode 100644 index b1cfd663..00000000 --- a/src/Operators/general/OperatorFunction.jl +++ /dev/null @@ -1,54 +0,0 @@ -export OperatorFunction - - -abstract type OperatorFunction{BT,FF,T} <: Operator{T} end - -struct ConcreteOperatorFunction{BT<:Operator,FF,T} <: OperatorFunction{BT,FF,T} - op::BT - f::FF -end - -ConcreteOperatorFunction(op::Operator,f::Function) = - ConcreteOperatorFunction{typeof(op),typeof(f),eltype(op)}(op,f) -OperatorFunction(op::Operator,f::Function) = ConcreteOperatorFunction(op,f) - -for op in (:domainspace,:rangespace,:domain,:bandwidths) - @eval begin - $op(OF::ConcreteOperatorFunction) = $op(OF.op) - end -end - -function getindex(OF::ConcreteOperatorFunction,k::Integer,j::Integer) - @assert isdiag(OF.op) - if k==j - OF.f(OF.op[k,k])::eltype(OF) - else - zero(eltype(OF)) - end -end - -function convert(::Type{Operator{T}},D::ConcreteOperatorFunction) where T - if T==eltype(D) - D - else - ConcreteOperatorFunction{typeof(D.op),T}(D.op,D.f) - end -end - - -for OP in (:(Base.inv),:(Base.sqrt)) - @eval begin - $OP(D::DiagonalOperator) = OperatorFunction(D,$OP) - $OP(C::ConstantTimesOperator) = $OP(C.λ)*$OP(C.op) - function $OP(D::ConcreteOperatorFunction) - @assert isdiag(D) - OperatorFunction(D.op,x->$OP(D.f(x))) - end - $OP(A::Operator) = isdiag(A) ? OperatorFunction(A,$OP) : - error("Not implemented.") - end -end - - -Base.sqrt(S::SpaceOperator) = SpaceOperator(sqrt(S.op),S.domainspace,S.rangespace) -Base.inv(S::SpaceOperator) = SpaceOperator(inv(S.op),S.rangespace,S.domainspace) diff --git a/src/Operators/general/OperatorLayout.jl b/src/Operators/general/OperatorLayout.jl deleted file mode 100644 index 078c6684..00000000 --- a/src/Operators/general/OperatorLayout.jl +++ /dev/null @@ -1,113 +0,0 @@ - - -export HermitianOperator, SymmetricOperator, AdjointOperator, TransposeOperator - - -struct HermitianOperator{T<:Number,B<:Operator} <: Operator{T} - op::B - uplo::Char -end - -HermitianOperator(B::Operator{T}, uplo::Symbol=:U) where {T<:Number} = HermitianOperator{T,typeof(B)}(B, char_uplo(uplo)) -convert(::Type{Operator{T}},A::HermitianOperator) where {T}=HermitianOperator(convert(Operator{T},A.op), A.uplo) - -domainspace(P::HermitianOperator)=domainspace(P.op) -rangespace(P::HermitianOperator)=rangespace(P.op) -domain(P::HermitianOperator)=domain(P.op) -bandwidths(P::HermitianOperator) = (b = bandwidth(P.op, P.uplo == 'L' ? 1 : P.uplo == 'U' ? 2 : 0); (b, b)) - -function getindex(P::HermitianOperator,k::Integer,j::Integer) - if P.uplo == 'L' - if j > k - conj(P.op[j,k]) - else - P.op[k,j] - end - elseif P.uplo == 'U' - if j < k - conj(P.op[j,k]) - else - P.op[k,j] - end - end -end - -Hermitian(A::Operator, uplo::Symbol)=HermitianOperator(A, uplo) -Hermitian(A::Operator)=HermitianOperator(A) - - -struct SymmetricOperator{T<:Number,B<:Operator} <: Operator{T} - op::B - uplo::Char -end - -SymmetricOperator(B::Operator{T}, uplo::Symbol=:U) where {T<:Number} = SymmetricOperator{T,typeof(B)}(B, char_uplo(uplo)) -convert(::Type{Operator{T}},A::SymmetricOperator) where {T}=SymmetricOperator(convert(Operator{T},A.op), A.uplo) - -domainspace(P::SymmetricOperator)=domainspace(P.op) -rangespace(P::SymmetricOperator)=rangespace(P.op) -domain(P::SymmetricOperator)=domain(P.op) -bandwidths(P::SymmetricOperator) = (b = bandwidth(P.op, P.uplo == 'L' ? 1 : P.uplo == 'U' ? 2 : 0); (b, b)) - -function getindex(P::SymmetricOperator,k::Integer,j::Integer) - if P.uplo == 'L' - if j > k - P.op[j,k] - else - P.op[k,j] - end - elseif P.uplo == 'U' - if j < k - P.op[j,k] - else - P.op[k,j] - end - end -end - -Symmetric(A::Operator, uplo::Symbol)=SymmetricOperator(A, uplo) -Symmetric(A::Operator)=SymmetricOperator(A) - - -struct AdjointOperator{T<:Number,B<:Operator} <: Operator{T} - op::B -end - -AdjointOperator(B::Operator{T}) where {T<:Number}=AdjointOperator{T,typeof(B)}(B) -convert(::Type{Operator{T}},A::AdjointOperator) where {T}=AdjointOperator(convert(Operator{T},A.op)) - -domainspace(P::AdjointOperator)=rangespace(P.op) -rangespace(P::AdjointOperator)=domainspace(P.op) -domain(P::AdjointOperator)=domain(P.op) -bandwidths(P::AdjointOperator) = reverse(bandwidths(P.op)) - -getindex(P::AdjointOperator,k::Integer,j::Integer) = conj(P.op[j,k]) - -function BandedMatrix(S::SubOperator{T,TO}) where {T,TO<:AdjointOperator} - kr,jr=parentindices(S) - adjoint(BandedMatrix(view(parent(S).op,jr,kr))) -end - -adjoint(A::Operator)=AdjointOperator(A) - - -struct TransposeOperator{T<:Number,B<:Operator} <: Operator{T} - op::B -end - -TransposeOperator(B::Operator{T}) where {T<:Number}=TransposeOperator{T,typeof(B)}(B) -convert(::Type{Operator{T}},A::TransposeOperator) where {T}=TransposeOperator(convert(Operator{T},A.op)) - -domainspace(P::TransposeOperator)=rangespace(P.op) -rangespace(P::TransposeOperator)=domainspace(P.op) -domain(P::TransposeOperator)=domain(P.op) -bandwidths(P::TransposeOperator) = reverse(bandwidths(P.op)) - -getindex(P::TransposeOperator,k::Integer,j::Integer) = P.op[j,k] - -function BandedMatrix(S::SubOperator{T,TO}) where {T,TO<:TransposeOperator} - kr,jr=parentindices(S) - transpose(BandedMatrix(view(parent(S).op,jr,kr))) -end - -transpose(A::Operator)=TransposeOperator(A) diff --git a/src/Operators/general/PartialInverseOperator.jl b/src/Operators/general/PartialInverseOperator.jl deleted file mode 100644 index b27f2b89..00000000 --- a/src/Operators/general/PartialInverseOperator.jl +++ /dev/null @@ -1,69 +0,0 @@ - - -export PartialInverseOperator - - -struct PartialInverseOperator{T<:Number,CO<:CachedOperator,BI} <: Operator{T} - cache::CO - bandwidths::BI -end - -function PartialInverseOperator(CO::CachedOperator{T},bandwidths) where T<:Number - @assert istriu(CO) # || istril(CO) - return PartialInverseOperator{T,typeof(CO),typeof(bandwidths)}(CO,bandwidths) -end - -PartialInverseOperator(B::Operator, bandwidths) = PartialInverseOperator(cache(B), bandwidths) -PartialInverseOperator(B::Operator) = PartialInverseOperator(B, bandwidths(B)) - -convert(::Type{Operator{T}},A::PartialInverseOperator) where {T}=PartialInverseOperator(convert(Operator{T},A.cache), A.bandwidths) - -domainspace(P::PartialInverseOperator)=rangespace(P.cache) -rangespace(P::PartialInverseOperator)=domainspace(P.cache) -domain(P::PartialInverseOperator)=domain(domainspace(P)) -bandwidths(P::PartialInverseOperator) = P.bandwidths - -function getindex(P::PartialInverseOperator,k::Integer,j::Integer) - b = bandwidth(P.cache, 2) - if k == j - return inv(P.cache[k,k]) - elseif j > k - t = zero(T) - for i = max(k,j-b-1):j-1 - t += ret[k,i]*P.cache[i,j] - end - return -t/P.cache[j,j] - else - return zero(eltype(P)) - end -end - - -## These are both hacks that apparently work - -function BandedMatrix(S::SubOperator{T,PP,Tuple{UnitRange{Int},UnitRange{Int}}}) where {T,PP<:PartialInverseOperator} - kr,jr = parentindices(S) - P = parent(S) - #ret = BandedMatrix{eltype(S)}(undef, size(S), bandwidths(S)) - ret = BandedMatrix{eltype(S)}(undef, (last(kr),last(jr)), bandwidths(P)) - b = bandwidth(P, 2) - #@assert first(kr) == first(jr) == 1 - - @inbounds for j in 1:last(jr) - kk = colrange(ret, j) - if j in kk - ret[j,j] = inv(P.cache[j,j]) - end - for k in first(kk):min(last(kk),j-1) - t = zero(T) - for i = max(k,j-b-1):j-1 - t += ret[k,i]*P.cache[i,j] - end - ret[k,j] = -t/P.cache[j,j] - end - end - - ret[kr,jr] -end - - diff --git a/src/Operators/general/algebra.jl b/src/Operators/general/algebra.jl deleted file mode 100644 index 3eb7c2f5..00000000 --- a/src/Operators/general/algebra.jl +++ /dev/null @@ -1,671 +0,0 @@ - - -export PlusOperator, TimesOperator, mul_coefficients - - - -struct PlusOperator{T,BI} <: Operator{T} - ops::Vector{Operator{T}} - bandwidths::BI - function PlusOperator{T,BI}(opsin::Vector{Operator{T}},bi::BI) where {T,BI} - n,m=size(first(opsin)) - for k=2:length(opsin) - @assert size(opsin[k],1)==n && size(opsin[k],2)==m - end - new{T,BI}(opsin,bi) - end -end - -Base.size(P::PlusOperator,k::Integer) = size(first(P.ops),k) - - -PlusOperator(opsin::Vector{Operator{T}},bi::Tuple{UT,VT}) where {T,UT,VT} = - PlusOperator{T,typeof(bi)}(opsin,bi) - -bandwidths(P::PlusOperator) = P.bandwidths - -israggedbelow(P::PlusOperator) = isbandedbelow(P) || all(israggedbelow,P.ops) - -for (OP,mn) in ((:colstart,:min),(:colstop,:max),(:rowstart,:min),(:rowstop,:max)) - defOP = Meta.parse("default_"*string(OP)) - @eval function $OP(P::PlusOperator,k::Integer) - if isbanded(P) - $defOP(P,k) - else - mapreduce(op->$OP(op,k),$mn,P.ops) - end - end -end - -function PlusOperator(ops::Vector) - # calculate bandwidths - b1,b2=-720,-720 # approximates ∞,-∞ - for op in ops - br=bandwidths(op) - b1=max(br[1],b1) - b2=max(br[end],b2) - end - PlusOperator(ops,(b1,b2)) -end - -function convert(::Type{Operator{T}},P::PlusOperator) where T - if T==eltype(P) - P - else - PlusOperator{T,typeof(P.bandwidths)}(Vector{Operator{T}}(P.ops),P.bandwidths) - end -end - -function promoteplus(opsin::Vector{Operator{T}}) where T - ops=Vector{Operator{T}}() - # prune zero ops - for op in opsin - if !iszeroop(op) - push!(ops,op) - end - end - PlusOperator(promotespaces(ops)) -end - -for OP in (:domainspace,:rangespace) - @eval $OP(P::PlusOperator) = $OP(first(P.ops)) -end - -domain(P::PlusOperator) = commondomain(P.ops) - - -+(A::PlusOperator,B::PlusOperator) = - promoteplus(Operator{promote_type(eltype(A),eltype(B))}[A.ops...,B.ops...]) -+(A::PlusOperator,B::PlusOperator,C::PlusOperator) = - promoteplus(Operator{promote_type(eltype(A),eltype(B),eltype(C))}[A.ops...,B.ops...,C.ops...]) -+(A::PlusOperator,B::Operator) = - promoteplus(Operator{promote_type(eltype(A),eltype(B))}[A.ops...,B]) -+(A::PlusOperator,B::ZeroOperator) = A -+(A::PlusOperator,B::Operator,C::Operator) = - promoteplus(Operator{promote_type(eltype(A),eltype(B),eltype(C))}[A.ops...,B,C]) -+(A::Operator,B::PlusOperator) = - promoteplus(Operator{promote_type(eltype(A),eltype(B))}[A,B.ops...]) -+(A::ZeroOperator,B::PlusOperator) = B -+(A::Operator,B::Operator) = - promoteplus(Operator{promote_type(eltype(A),eltype(B))}[A,B]) -+(A::Operator,B::Operator,C::Operator) = - promoteplus(Operator{promote_type(eltype(A),eltype(B),eltype(C))}[A,B,C]) - - - - -Base.stride(P::PlusOperator)=mapreduce(stride,gcd,P.ops) - - -function getindex(P::PlusOperator{T},k::Integer...) where T - ret=P.ops[1][k...]::T - for j=2:length(P.ops) - ret+=P.ops[j][k...]::T - end - ret -end - - -for TYP in (:RaggedMatrix,:Matrix,:BandedMatrix, - :BlockBandedMatrix,:BandedBlockBandedMatrix) - @eval begin - $TYP(P::SubOperator{T,PP,NTuple{2,BlockRange1}}) where {T,PP<:PlusOperator} = - convert_axpy!($TYP,P) # use axpy! to copy - $TYP(P::SubOperator{T,PP}) where {T,PP<:PlusOperator} = - convert_axpy!($TYP,P) # use axpy! to copy - $TYP(P::SubOperator{T,PP,NTuple{2,UnitRange{Int}}}) where {T,PP<:PlusOperator} = - convert_axpy!($TYP,P) # use axpy! to copy - end -end - -function BLAS.axpy!(α,P::SubOperator{T,PP},A::AbstractMatrix) where {T,PP<:PlusOperator} - for op in parent(P).ops - BLAS.axpy!(α, view(op,P.indexes[1],P.indexes[2]), A) - end - - A -end - - -+(A::Operator,f::Fun) = A+Multiplication(f,domainspace(A)) -+(f::Fun,A::Operator) = Multiplication(f,domainspace(A))+A --(A::Operator,f::Fun) = A+Multiplication(-f,domainspace(A)) --(f::Fun,A::Operator) = Multiplication(f,domainspace(A))-A - -for TYP in (:ZeroOperator,:Operator) - @eval function +(A::$TYP,B::ZeroOperator) - if spacescompatible(A,B) - A - else - promotespaces(A,B)[1] - end - end -end -+(A::ZeroOperator,B::Operator) = B+A - - - - -# We need to support A+1 in addition to A+I primarily for matrix case: A+Matrix(I,2,2) -for OP in (:+,:-) - @eval begin - $OP(c::Union{UniformScaling,Number},A::Operator) = - $OP(convert(Operator{promote_type(eltype(A),eltype(c))},c),A) - $OP(A::Operator,c::Union{UniformScaling,Number}) = - $OP(A,convert(Operator{promote_type(eltype(A),eltype(c))},c)) - end -end - - - -## Times Operator - -struct ConstantTimesOperator{B,T} <: Operator{T} - λ::T - op::B - ConstantTimesOperator{B,T}(c,op) where {B,T} = new{B,T}(c,op) -end -function ConstantTimesOperator(c::Number,op::Operator{TT}) where TT<:Number - T=promote_type(typeof(c),eltype(op)) - B=convert(Operator{T},op) - ConstantTimesOperator{typeof(B),T}(T(c),B) -end - -ConstantTimesOperator(c::Number,op::ConstantTimesOperator) = - ConstantTimesOperator(c*op.λ,op.op) - -@wrapperstructure ConstantTimesOperator -@wrapperspaces ConstantTimesOperator - -convert(::Type{T},C::ConstantTimesOperator) where {T<:Number} = T(C.λ)*convert(T,C.op) - -choosedomainspace(C::ConstantTimesOperator,sp::Space) = choosedomainspace(C.op,sp) - - -for OP in (:promotedomainspace,:promoterangespace),SP in (:UnsetSpace,:Space) - @eval $OP(C::ConstantTimesOperator,k::$SP) = ConstantTimesOperator(C.λ,$OP(C.op,k)) -end - - -function convert(::Type{Operator{T}},C::ConstantTimesOperator) where T - if T==eltype(C) - C - else - op=convert(Operator{T},C.op) - ConstantTimesOperator{typeof(op),T}(T(C.λ),op) - end -end - -getindex(P::ConstantTimesOperator,k::Integer...) = - P.λ*P.op[k...] - - -for TYP in (:RaggedMatrix,:Matrix,:BandedMatrix, - :BlockBandedMatrix,:BandedBlockBandedMatrix) - @eval begin - $TYP(S::SubOperator{T,OP,NTuple{2,BlockRange1}}) where {T,OP<:ConstantTimesOperator} = - convert_axpy!($TYP, S) - $TYP(S::SubOperator{T,OP,NTuple{2,UnitRange{Int}}}) where {T,OP<:ConstantTimesOperator} = - convert_axpy!($TYP, S) - $TYP(S::SubOperator{T,OP}) where {T,OP<:ConstantTimesOperator} = - convert_axpy!($TYP, S) - end -end - - - -BLAS.axpy!(α,S::SubOperator{T,OP},A::AbstractMatrix) where {T,OP<:ConstantTimesOperator} = - unwrap_axpy!(α*parent(S).λ,S,A) - - - - - -struct TimesOperator{T,BI} <: Operator{T} - ops::Vector{Operator{T}} - bandwidths::BI - - function TimesOperator{T,BI}(ops::Vector{Operator{T}},bi::BI) where {T,BI} - # check compatible - for k=1:length(ops)-1 - @assert size(ops[k],2) == size(ops[k+1],1) - end - - # remove TimesOperators buried inside ops - hastimes = false - for k=1:length(ops)-1 - @assert spacescompatible(domainspace(ops[k]),rangespace(ops[k+1])) - hastimes = hastimes || isa(ops[k],TimesOperator) - end - - if hastimes - newops=Vector{Operator{T}}(0) - for op in ops - if isa(op,TimesOperator) - for op2 in op.ops - push!(newops,op2) - end - else - push!(newops,op) - end - end - ops=newops - end - - - new{T,BI}(ops,bi) - end -end - - -function bandwidthsum(P,k) - ret=0 - for op in P - ret+=bandwidths(op)[k] - end - ret -end - -bandwidthssum(P) = (bandwidthsum(P,1),bandwidthsum(P,2)) - -TimesOperator(ops::Vector{Operator{T}},bi::Tuple{N1,N2}) where {T,N1,N2} = - TimesOperator{T,typeof(bi)}(ops,bi) - -TimesOperator(ops::Vector{Operator{T}}) where {T} = TimesOperator(ops,bandwidthssum(ops)) -TimesOperator(ops::Vector{OT}) where {OT<:Operator} = - TimesOperator(convert(Vector{Operator{eltype(OT)}},ops),bandwidthssum(ops)) - -TimesOperator(A::TimesOperator,B::TimesOperator) = - TimesOperator(Operator{promote_type(eltype(A),eltype(B))}[A.ops...,B.ops...]) -TimesOperator(A::TimesOperator,B::Operator) = - TimesOperator(Operator{promote_type(eltype(A),eltype(B))}[A.ops...,B]) -TimesOperator(A::Operator,B::TimesOperator) = - TimesOperator(Operator{promote_type(eltype(A),eltype(B))}[A,B.ops...]) -TimesOperator(A::Operator,B::Operator) = - TimesOperator(Operator{promote_type(eltype(A),eltype(B))}[A,B]) - - -==(A::TimesOperator,B::TimesOperator)=A.ops==B.ops - -function convert(::Type{Operator{T}},P::TimesOperator) where T - if T==eltype(P) - P - else - TimesOperator(Operator{T}[P.ops...]) - end -end - - - -function promotetimes(opsin::Vector{B},dsp) where B<:Operator - ops=Vector{Operator{mapreduce(eltype,promote_type,opsin)}}(undef,0) - - for k=length(opsin):-1:1 - if !isa(opsin[k],Conversion) - op=promotedomainspace(opsin[k],dsp) - if op==() - # do nothing - elseif isa(op,TimesOperator) - for j=length(op.ops):-1:1 - push!(ops,op.ops[j]) - end - dsp=rangespace(op) - else - push!(ops,op) - dsp=rangespace(op) - end - end - end - if isempty(ops) - ConstantOperator(1.0,dsp) - elseif length(ops)==1 - first(ops) - else - TimesOperator(reverse!(ops)) # change order in TImesOperator if this is slow - end -end - -promotetimes(opsin::Vector{B}) where {B<:Operator}=promotetimes(opsin,domainspace(last(opsin))) - - - -domainspace(P::TimesOperator)=domainspace(last(P.ops)) -rangespace(P::TimesOperator)=rangespace(first(P.ops)) - -domain(P::TimesOperator)=commondomain(P.ops) - - -bandwidths(P::TimesOperator) = P.bandwidths - -israggedbelow(P::TimesOperator) = isbandedbelow(P) || all(israggedbelow,P.ops) - -Base.stride(P::TimesOperator) = mapreduce(stride,gcd,P.ops) - -for OP in (:rowstart,:rowstop) - defOP=Meta.parse("default_"*string(OP)) - @eval function $OP(P::TimesOperator,k::Integer) - if isbanded(P) - return $defOP(P,k) - end - for j=eachindex(P.ops) - k=$OP(P.ops[j],k) - end - k - end -end - -for OP in (:colstart,:colstop) - defOP=Meta.parse("default_"*string(OP)) - @eval function $OP(P::TimesOperator, k::Integer) - if isbanded(P) - return $defOP(P, k) - end - for j=reverse(eachindex(P.ops)) - k=$OP(P.ops[j],k) - end - k - end -end - -getindex(P::TimesOperator,k::Integer,j::Integer) = P[k:k,j:j][1,1] -function getindex(P::TimesOperator,k::Integer) - @assert isafunctional(P) - P[1:1,k:k][1,1] -end - -function getindex(P::TimesOperator,k::AbstractVector) - @assert isafunctional(P) - vec(Matrix(P[1:1,k])) -end - -for TYP in (:Matrix, :BandedMatrix, :RaggedMatrix) - @eval function $TYP(V::SubOperator{T,TO,Tuple{UnitRange{Int},UnitRange{Int}}}) where {T,TO<:TimesOperator} - P = parent(V) - - if isbanded(P) - if $TYP ≠ BandedMatrix - return $TYP(BandedMatrix(V)) - end - elseif isbandedblockbanded(P) - N = block(rangespace(P), last(parentindices(V)[1])) - M = block(domainspace(P), last(parentindices(V)[2])) - B = P[Block(1):N, Block(1):M] - return $TYP(view(B, parentindices(V)...), _colstops(V)) - end - - kr,jr = parentindices(V) - - (isempty(kr) || isempty(jr)) && return $TYP(Zeros, V) - - if maximum(kr) > size(P,1) || maximum(jr) > size(P,2) || - minimum(kr) < 1 || minimum(jr) < 1 - throw(BoundsError()) - end - - @assert length(P.ops) ≥ 2 - if size(V,1)==0 - return $TYP(Zeros, V) - end - - - # find optimal truncations for each operator - # by finding the non-zero entries - krlin = Matrix{Union{Int,Infinity}}(undef,length(P.ops),2) - - krlin[1,1],krlin[1,2]=kr[1],kr[end] - for m=1:length(P.ops)-1 - krlin[m+1,1]=rowstart(P.ops[m],krlin[m,1]) - krlin[m+1,2]=rowstop(P.ops[m],krlin[m,2]) - end - krlin[end,1]=max(krlin[end,1],colstart(P.ops[end],jr[1])) - krlin[end,2]=min(krlin[end,2],colstop(P.ops[end],jr[end])) - for m=length(P.ops)-1:-1:2 - krlin[m,1]=max(krlin[m,1],colstart(P.ops[m],krlin[m+1,1])) - krlin[m,2]=min(krlin[m,2],colstop(P.ops[m],krlin[m+1,2])) - end - - - krl = Matrix{Int}(krlin) - - # Check if any range is invalid, in which case return zero - for m=1:length(P.ops) - if krl[m,1]>krl[m,2] - return $TYP(Zeros, V) - end - end - - - - # The following returns a banded Matrix with all rows - # for large k its upper triangular - BA = convert($TYP{T}, P.ops[end][krl[end,1]:krl[end,2],jr]) - for m = (length(P.ops)-1):-1:1 - BA = convert($TYP{T}, P.ops[m][krl[m,1]:krl[m,2],krl[m+1,1]:krl[m+1,2]])*BA - end - - $TYP{T}(BA) - end -end - -for TYP in (:BlockBandedMatrix, :BandedBlockBandedMatrix) - @eval function $TYP(V::SubOperator{T,TO,Tuple{BlockRange1,BlockRange1}}) where {T,TO<:TimesOperator} - P = parent(V) - KR,JR = parentindices(V) - - @assert length(P.ops) ≥ 2 - if size(V,1)==0 || isempty(KR) || isempty(JR) - return $TYP(Zeros, V) - end - - if Int(maximum(KR)) > blocksize(P,1) || Int(maximum(JR)) > blocksize(P,2) || - Int(minimum(KR)) < 1 || Int(minimum(JR)) < 1 - throw(BoundsError()) - end - - - # find optimal truncations for each operator - # by finding the non-zero entries - KRlin = Matrix{Union{Block,Infinity}}(undef,length(P.ops),2) - - KRlin[1,1],KRlin[1,2] = first(KR),last(KR) - for m=1:length(P.ops)-1 - KRlin[m+1,1]=blockrowstart(P.ops[m],KRlin[m,1]) - KRlin[m+1,2]=blockrowstop(P.ops[m],KRlin[m,2]) - end - KRlin[end,1]=max(KRlin[end,1],blockcolstart(P.ops[end],first(JR))) - KRlin[end,2]=min(KRlin[end,2],blockcolstop(P.ops[end],last(JR))) - for m=length(P.ops)-1:-1:2 - KRlin[m,1]=max(KRlin[m,1],blockcolstart(P.ops[m],KRlin[m+1,1])) - KRlin[m,2]=min(KRlin[m,2],blockcolstop(P.ops[m],KRlin[m+1,2])) - end - - - KRl = Matrix{Block{1}}(KRlin) - - # Check if any range is invalid, in which case return zero - for m=1:length(P.ops) - if KRl[m,1]>KRl[m,2] - return $TYP(Zeros, V) - end - end - - - - # The following returns a banded Matrix with all rows - # for large k its upper triangular - BA = convert($TYP, view(P.ops[end],KRl[end,1]:KRl[end,2],JR)) - for m = (length(P.ops)-1):-1:1 - BA = convert($TYP, view(P.ops[m],KRl[m,1]:KRl[m,2],KRl[m+1,1]:KRl[m+1,2]))*BA - end - - convert($TYP, BA) - end -end - - -## Algebra: assume we promote - - -for OP in (:(adjoint),:(transpose)) - @eval $OP(A::TimesOperator)=TimesOperator(reverse!(map($OP,A.ops))) -end - -*(A::TimesOperator,B::TimesOperator) = - promotetimes(Operator{promote_type(eltype(A),eltype(B))}[A.ops...,B.ops...]) -function *(A::TimesOperator,B::Operator) - if isconstop(B) - promotedomainspace(convert(Number,B)*A,domainspace(B)) - else - promotetimes(Operator{promote_type(eltype(A),eltype(B))}[A.ops...,B]) - end -end -function *(A::Operator,B::TimesOperator) - if isconstop(A) - promoterangespace(convert(Number,A)*B,rangespace(A)) - else - promotetimes(Operator{promote_type(eltype(A),eltype(B))}[A,B.ops...]) - end -end -function *(A::Operator,B::Operator) - if isconstop(A) - promoterangespace(convert(Number,A)*B,rangespace(A)) - elseif isconstop(B) - promotedomainspace(convert(Number,B)*A,domainspace(B)) - else - promotetimes(Operator{promote_type(eltype(A),eltype(B))}[A,B]) - end -end - - - -# Conversions we always assume are intentional: no need to promote - -*(A::ConversionWrapper{TO1},B::ConversionWrapper{TO}) where {TO1<:TimesOperator,TO<:TimesOperator} = - ConversionWrapper(TimesOperator(A.op,B.op)) -*(A::ConversionWrapper{TO},B::Conversion) where {TO<:TimesOperator} = - ConversionWrapper(TimesOperator(A.op,B)) -*(A::Conversion,B::ConversionWrapper{TO}) where {TO<:TimesOperator} = - ConversionWrapper(TimesOperator(A,B.op)) - -*(A::Conversion,B::Conversion) = ConversionWrapper(TimesOperator(A,B)) -*(A::Conversion,B::TimesOperator) = TimesOperator(A,B) -*(A::TimesOperator,B::Conversion) = TimesOperator(A,B) -*(A::Operator,B::Conversion) = - isconstop(A) ? promoterangespace(convert(Number,A)*B,rangespace(A)) : TimesOperator(A,B) -*(A::Conversion,B::Operator) = - isconstop(B) ? promotedomainspace(convert(Number,B)*A,domainspace(B)) : TimesOperator(A,B) - -^(A::Operator, p::Integer) = Base.power_by_squaring(A, p) - - -+(A::Operator) = A --(A::Operator) = ConstantTimesOperator(-1,A) --(A::Operator,B::Operator) = A+(-B) - - -function *(f::Fun, A::Operator) - if isafunctional(A) && (isinf(bandwidth(A,1)) || isinf(bandwidth(A,2))) - LowRankOperator(f,A) - else - TimesOperator(Multiplication(f,rangespace(A)),A) - end -end - -*(c::Number,A::Operator) = ConstantTimesOperator(c,A) -*(A::Operator,c::Number) = c*A - -\(c::Number,B::Operator) = inv(c)*B -\(c::Fun,B::Operator) = inv(c)*B - -/(B::Operator,c::Number) = B*inv(c) -/(B::Operator,c::Fun) = B*inv(c) - - - - - -## Operations -function mul_coefficients(A::Operator,b) - n=size(b,1) - ret = n>0 ? mul_coefficients(view(A,FiniteRange,1:n),b) : b -end - -function mul_coefficients(A::TimesOperator,b) - ret = b - for k=length(A.ops):-1:1 - ret = mul_coefficients(A.ops[k],ret) - end - - ret -end - - -function *(A::Operator, b) - ds = domainspace(A) - rs = rangespace(A) - if isambiguous(ds) - promotedomainspace(A,space(b))*b - elseif isambiguous(rs) - error("Assign spaces to $A before multiplying.") - else - Fun(rs, - mul_coefficients(A,coefficients(b,ds))) - end -end - -mul_coefficients(A::PlusOperator,b::Fun) = - mapreduce(x->mul_coefficients(x,b),+,A.ops) - -*(A::Operator, b::AbstractMatrix{<:Fun}) = A*Fun(b) -*(A::Vector{<:Operator}, b::Fun) = map(a->a*b,convert(Array{Any,1},A)) - - - - - - -## promotedomain - - -function promotedomainspace(P::PlusOperator{T},sp::Space,cursp::Space) where T - if sp==cursp - P - else - ops = [promotedomainspace(op,sp) for op in P.ops] - promoteplus(Vector{Operator{mapreduce(eltype,promote_type,ops)}}(ops)) - end -end - - -function choosedomainspace(P::PlusOperator,sp::Space) - ret=UnsetSpace() - for op in P.ops - sp2=choosedomainspace(op,sp) - if !isa(sp2,AmbiguousSpace) # we will ignore this result in hopes another opand - # tells us a good space - ret=union(ret,sp2) - end - end - ret -end - - - -function promotedomainspace(P::TimesOperator,sp::Space,cursp::Space) - if sp==cursp - P - elseif length(P.ops)==2 - P.ops[1]*promotedomainspace(P.ops[end],sp) - else - promotetimes([P.ops[1:end-1];promotedomainspace(P.ops[end],sp)]) - end -end - - - -function choosedomainspace(P::TimesOperator,sp::Space) - for op in P.ops - sp=choosedomainspace(op,sp) - end - sp -end diff --git a/src/Operators/general/general.jl b/src/Operators/general/general.jl deleted file mode 100644 index f4924e02..00000000 --- a/src/Operators/general/general.jl +++ /dev/null @@ -1,7 +0,0 @@ -include("algebra.jl") -include("CachedOperator.jl") -include("FiniteOperator.jl") -include("OperatorLayout.jl") -include("PartialInverseOperator.jl") -include("InterlaceOperator.jl") -include("OperatorFunction.jl") diff --git a/src/Operators/ldiv.jl b/src/Operators/ldiv.jl deleted file mode 100644 index abbbb090..00000000 --- a/src/Operators/ldiv.jl +++ /dev/null @@ -1,61 +0,0 @@ -## Linear Solve -for TYP in (:Fun,:StridedVector,:AbstractVector,:Any) - @eval function \(A::Operator,b::$TYP;kwds...) - if isambiguous(domainspace(A)) - A=choosespaces(A,b) - if isambiguous(domainspace(A)) - error("Cannot infer spaces") - end - \(A,b;kwds...) - else - Fun(domainspace(A), - ldiv_coefficients(A,coefficients(b,rangespace(A));kwds...)) - end - end -end - -""" - \\(A::Operator,b;tolerance=tol,maxlength=n) - -solves a linear equation, usually differential equation, where `A` is an operator -or array of operators and `b` is a `Fun` or array of funs. The result `u` -will approximately satisfy `A*u = b`. -""" -\(::Operator,_) - -# Solve each column separately -function \(A::Operator, B::AbstractMatrix; kwds...) - ds=domainspace(A) - if isambiguous(ds) - return choosespaces(A,B[:,1])\B - end - - ret=Matrix{VFun{typeof(ds), - promote_type(eltype(A),mapreduce(cfstype,promote_type,B))}}(undef,1,size(B,2)) - - QR = factorize(A) # reuse computation - for j=1:size(B,2) - ret[1,j] = \(QR,B[:,j];kwds...) - end - Fun(ret) -end - -\(A::Operator,B::MatrixFun;kwds...) = \(A,Array(B);kwds...) - -ldiv_coefficients(A::Operator,b;kwds...) = ldiv_coefficients(qr(A),b;kwds...) - -\(A::Operator,B::Operator) = TimesOperator(inv(A),B) - -#TODO: Remove these when interlace is automatic -for TYP in (:Vector,:Matrix) - @eval begin - \(A::$TYP{OO},b::StridedVecOrMat;kwds...) where {OO<:Operator} = - \(interlace(A),b;kwds...) - \(A::$TYP{OO},b::AbstractVecOrMat;kwds...) where {OO<:Operator} = - \(interlace(A),b;kwds...) - \(A::$TYP{OO},b::Fun;kwds...) where {OO<:Operator} = - \(interlace(A),b;kwds...) - end -end -ldiv_coefficients(A::AbstractArray{OO},b;kwds...) where {OO<:Operator} = - ldiv_coefficients(interlace(A),b;kwds...) diff --git a/src/Operators/nullspace.jl b/src/Operators/nullspace.jl deleted file mode 100644 index da2127d9..00000000 --- a/src/Operators/nullspace.jl +++ /dev/null @@ -1,55 +0,0 @@ - - -function nullspace(A::Operator{T};tolerance=10eps(real(T)),maxlength=1_000_000) where T - K=transpose_nullspace(qr(A'),tolerance,maxlength) - # drop extra rows, and use QR to determine rank - Q,R = qr(K,Val(true)) - ind=findfirst(r->abs(r)≤100tolerance,diag(R)) - Kret=ind==0 ? Q : Q[:,1:ind-1] - Fun(Space(fill(domainspace(A),(1,ind-1))),vec(Kret')) -end - - -function transpose_nullspace(QR::QROperator,tolerance,maxlength) - T=eltype(QR) - resizedata!(QR,:,100) - - m=size(QR.H,1) - K=zeros(100,m-1) - K[1:m-1,1:m-1]=Matrix(I,m-1,m-1) - - for k=1:10 - v=QR.H[:,k] - QQ=Matrix{T}(I,m,m)-2v*v' - K[:,:]=K*QQ[1:end-1,2:end] - K[k+m-1,:]=QQ[end,2:end] - end - k=11 - α=0.9 - - while slnorm(K,floor(Int,k^α),:) >tolerance*k - if k > maxlength - @warn "Max length of $maxlength reached." - break - end - - if k ≥ QR.ncols - resizedata!(QR,:,k+100) - end - - v=QR.H[:,k] - QQ=(Matrix(I,m,m)-2v*v') - K[:,:]=K*QQ[1:end-1,2:end] - - if k+m-1 > size(K,1) - K=pad(K,k+m+100,:) - end - K[k+m-1,:]=QQ[end,2:end] - k+=1 - end - - K[1:floor(Int,k^α),:] -end - - -nullspace(A::AbstractArray{OO};kwds...) where {OO<:Operator} = nullspace(interlace(A);kwds...) diff --git a/src/Operators/qr.jl b/src/Operators/qr.jl deleted file mode 100644 index 23e31ab4..00000000 --- a/src/Operators/qr.jl +++ /dev/null @@ -1,213 +0,0 @@ - - - - -mutable struct QROperator{CO,MT,T} <: Operator{T} - R_cache::CO - H::MT # Contains the Householder reflections - ncols::Int # number of cols already upper triangularized -end - -QROperator(R::CachedOperator,H::AbstractArray,ncs::Int) = - QROperator{typeof(R),typeof(H),eltype(H)}(R,H,ncs) - - -convert(::Type{Operator{T}},QR::QROperator) where {T} = - QROperator(convert(Operator{T},QR.R_cache), convert(AbstractArray{T}, QR.H),QR.ncols) - -qr(QR::QROperator) = QR -factorize(QR::QROperator) = QR - -for OP in (:domainspace,:rangespace) - @eval $OP(QR::QROperator) = $OP(QR.R_cache) -end - -getindex(QR::QROperator, k::Integer, j::Integer) = QR.R_cache.op[k,j] - - - -struct QROperatorR{QRT,T} <: Operator{T} - QR::QRT -end - -QROperatorR(QR) = QROperatorR{typeof(QR),eltype(QR)}(QR) -domainspace(R::QROperatorR) = domainspace(R.QR) -rangespace(R::QROperatorR) = ℓ⁰ - -function getindex(R::QROperatorR,k::Integer,j::Integer) - if j < k - zero(eltype(R)) - else - resizedata!(R.QR,:,j) - R.QR.R_cache[k,j] - end -end - -bandwidths(R::QROperatorR) = (0, bandwidth(R.QR.R_cache,1) + bandwidth(R.QR.R_cache,2)) - -struct QROperatorQ{QRT,T} <: Operator{T} - QR::QRT -end - - -QROperatorQ(QR) = QROperatorQ{typeof(QR),eltype(QR)}(QR) - - -domainspace(Q::QROperatorQ) = ℓ⁰ -rangespace(Q::QROperatorQ) = rangespace(Q.QR) - -getindex(Q::QROperatorQ, k::Integer, j::Integer) = (mul_coefficients(Q',eltype(Q)[zeros(k-1);1]))[j] - - -function getproperty(F::QROperator, d::Symbol) - if d == :R - return QROperatorR(F) - elseif d == :Q - return QROperatorQ(F) - else - getfield(F, d) - end -end - -# iteration for destructuring into components -Base.iterate(S::QROperator) = (S.Q, Val(:R)) -Base.iterate(S::QROperator, ::Val{:R}) = (S.R, Val(:done)) -Base.iterate(S::QROperator, ::Val{:done}) = nothing - - -# override for custom data types -QROperator(R::CachedOperator{T,AM}) where {T,AM<:AbstractMatrix} = - error("Cannot create a QR factorization for $(typeof(R))") - - -adjoint(Q::QROperatorQ) = Adjoint(Q) -size(Q::Adjoint{<:Any,<:QROperatorQ}) = (size(parent(Q),2), size(parent(Q),1)) - -function qr!(A::CachedOperator; cached::Int=0) - QR = QROperator(A) - if cached ≠ 0 - resizedata!(QR,:,cached) - end - QR -end - -""" - qr(A::Operator) - -returns a cached QR factorization of the Operator `A`. The result `QR` -enables solving of linear equations: if `u=QR\b`, then `u` -approximately satisfies `A*u = b`. -""" -function qr(A::Operator; cached::Int=0) - if isambiguous(domainspace(A)) || isambiguous(rangespace(A)) - throw(ArgumentError("Only non-ambiguous operators can be factorized.")) - end - qr!(cache(A;padding=true);cached=cached) -end - - -factorize(A::Operator) = qr(A) - -for OP in (:qr, :factorize) - @eval begin - $OP(A::AbstractVector{<:Operator}) = $OP(interlace(A)) - $OP(A::AbstractMatrix{<:Operator}) = $OP(interlace(A)) - $OP(A::AbstractArray{<:Operator}) = $OP(interlace(A)) - end -end - - -function det(R::QROperatorR;maxiterations::Int=10_000) - QR = R.QR - RD = R.QR.R_cache - resizedata!(QR,:,1) - ret = -RD[1,1] - k = 2 - while abs(abs(RD[k-1,k-1])-1) > eps(eltype(R)) - resizedata!(QR,:,k) - ret *= -RD[k,k] - k+=1 - k > maxiterations && error("Determinant unlikely to converge after 10_000 iterations.") - end - - ret -end -det(QR::QROperatorQ) = 1 - -det(QR::QROperator) = det(QR.R) -det(A::Operator) = det(qr(A)) - - - - -## Multiplication routines - - -# Q - -mul_coefficients(At::Transpose{T,<:QROperatorQ{T}},B::AbstractVector{T}) where {T<:Real} = parent(At)'*B -mul_coefficients(At::Transpose{T,<:QROperatorQ{T}},B::AbstractMatrix{T}) where {T<:Real} = parent(At)'*B - -mul_coefficients(Ac::Adjoint{T,<:QROperatorQ{QR,T}},B::AbstractVector{T};tolerance=eps(eltype(Ac))/10,maxlength=1000000) where {QR,T} = - mulpars(Ac,B,tolerance,maxlength) - -mul_coefficients(Ac::Adjoint{T,<:QROperatorQ{QR,T}},B::AbstractVector{V};opts...) where {QR,T,V} = - mul_coefficients(Ac,AbstractVector{T}(B); opts...) - - -function *(Ac::Adjoint{<:Any,<:QROperatorQ}, b::AbstractVector; kwds...) - A = parent(Ac) - Fun(domainspace(A),mul_coefficients(Ac,coefficients(b,rangespace(A));kwds...)) -end - -function *(Ac::Adjoint{<:Any,<:QROperatorQ}, b; kwds...) - A = parent(Ac) - Fun(domainspace(A),mul_coefficients(Ac,coefficients(b,rangespace(A));kwds...)) -end - - -ldiv_coefficients(A::QROperatorQ, B; opts...) = mul_coefficients(A', B; opts...) -\(A::QROperatorQ, B::Fun; opts...) = *(A', B; opts...) - - -# R -function ldiv_coefficients(R::QROperatorR, b::AbstractVector) - if length(b) > R.QR.ncols - # upper triangularize columns - resizedata!(R.QR, :, length(b)) - end - UpperTriangular(view(R.QR.R_cache.data, 1:length(b), 1:length(b))) \ b -end - -\(R::QROperatorR,b::Fun{SequenceSpace};kwds...) = - Fun(domainspace(R),ldiv_coefficients(R,b.coefficients;kwds...)) -\(A::QROperatorR,b::Fun;kwds...) = error("\\ not implement for $(typeof(b)) right-hand sides") - - -# QR - -for TYP in (:Real,:Complex,:Number) - @eval ldiv_coefficients(QR::QROperator{CO,MT,T},b::AbstractVector{T}; kwds...) where {CO,MT,T<:$TYP} = - ldiv_coefficients(QR.R, mul_coefficients(QR.Q',b;kwds...)) -end - - -function ldiv_coefficients(QR::QROperator{CO,MT,T},b::AbstractVector{V};kwds...) where {CO,MT,T,V<:Number} - TV = promote_type(T,V) - ldiv_coefficients(convert(Operator{TV}, QR),convert(Vector{TV}, b);kwds...) -end - -function ldiv_coefficients(QR::QROperator{CO,MT,T},b::AbstractVector{V};kwds...) where {CO,MT,T<:Real,V<:Complex} - a=ldiv_coefficients(QR,real(b);kwds...) - b=im*ldiv_coefficients(QR,imag(b);kwds...) - n=max(length(a),length(b)) - pad!(a,n)+pad!(b,n) -end -ldiv_coefficients(QR::QROperator{CO,MT,T},b::AbstractVector{V};kwds...) where {CO,MT,T<:Complex,V<:Real} = - ldiv_coefficients(QR,Vector{T}(b);kwds...) - - -\(A::QROperator,b::Fun;kwds...) = - Fun(domainspace(A),ldiv_coefficients(A,coefficients(b,rangespace(A));kwds...)) - -\(A::QROperator,B::MatrixFun;kwds...) = \(A,Array(B);kwds...) diff --git a/src/Operators/spacepromotion.jl b/src/Operators/spacepromotion.jl deleted file mode 100644 index 7490e332..00000000 --- a/src/Operators/spacepromotion.jl +++ /dev/null @@ -1,182 +0,0 @@ -export → - - -## Space Operator is used to wrap other operators -# and change the domain/range space -struct SpaceOperator{O<:Operator,S<:Space,V<:Space,T} <: Operator{T} - op::O - domainspace::S - rangespace::V -end - -# The promote_type is needed to fix a bug in promotetimes -# not sure if its the right long term solution -SpaceOperator(o::Operator,s::Space,rs::Space) = - SpaceOperator{typeof(o),typeof(s),typeof(rs),eltype(o)}(o,s,rs) -SpaceOperator(o,s) = SpaceOperator(o,s,s) - -function convert(::Type{Operator{T}},S::SpaceOperator) where T - if T==eltype(S) - S - else - op=convert(Operator{T},S.op) - SpaceOperator{typeof(op),typeof(S.domainspace),typeof(S.rangespace),T}(op,S.domainspace,S.rangespace) - end -end - - - -# Similar to wrapper, but different domain/domainspace/rangespace - -@wrappergetindex SpaceOperator - -# SpaceOperator can change blocks, so we need to override this -getindex(A::SpaceOperator,KR::BlockRange, JR::BlockRange) = defaultgetindex(A,KR,JR) - - -getindex(A::SpaceOperator,K::Block,J::Block) = A[blockrows(A,K),blockcols(A,J)] -getindex(A::SpaceOperator,K::Block,j) = A[blockrows(A,K),j] -getindex(A::SpaceOperator,k,J::Block) = A[k,blockcols(A,J)] - - - -domain(S::SpaceOperator) = domain(domainspace(S)) -domainspace(S::SpaceOperator) = S.domainspace -rangespace(S::SpaceOperator) = S.rangespace - - - -##TODO: Do we need both max and min? -function findmindomainspace(ops::AbstractVector)::Any - sp = UnsetSpace() - - for op in ops - sp = union(sp,domainspace(op)) - end - - sp -end - -function findmaxrangespace(ops::AbstractVector)::Any - sp = UnsetSpace() - - for op in ops - sp = maxspace(sp,rangespace(op)) - end - - sp -end - - -# The coolest definitions ever!! -# supports Derivative():Chebyshev()→Ultraspherical(1) -(:)(A::Operator,b::Space) = promotedomainspace(A,b) -→(A::Operator,b::Space) = promoterangespace(A,b) -(:)(A::UniformScaling,b::Space) = Operator(A,b) -→(A::UniformScaling,b::Space) = Operator(A,b) - - -promoterangespace(P::Operator,sp::Space) = promoterangespace(P,sp,rangespace(P)) -promotedomainspace(P::Operator,sp::Space) = promotedomainspace(P,sp,domainspace(P)) - - -promoterangespace(P::Operator,sp::Space,cursp::Space)::Any = - (sp==cursp) ? P : Conversion(cursp,sp)*P -promotedomainspace(P::Operator,sp::Space,cursp::Space)::Any = - (sp==cursp) ? P : P*Conversion(sp,cursp) - - - - - -function promoterangespace(ops::AbstractVector{O}) where O<:Operator - isempty(ops) && return ops - k=findmaxrangespace(ops) - #TODO: T might be incorrect - T=mapreduce(eltype,promote_type,ops) - Operator{T}[promoterangespace(op,k) for op in ops] -end -function promotedomainspace(ops::AbstractVector{O}) where O<:Operator - isempty(ops) && return ops - k=findmindomainspace(ops) - #TODO: T might be incorrect - T=mapreduce(eltype,promote_type,ops) - Operator{T}[promotedomainspace(op,k) for op in ops] -end -function promotedomainspace(ops::AbstractVector{O},S::Space) where O<:Operator - isempty(ops) && return ops - k=conversion_type(findmindomainspace(ops),S) - #TODO: T might be incorrect - T=promote_type(mapreduce(eltype,promote_type,ops),prectype(S)) - Operator{T}[promotedomainspace(op,k) for op in ops] -end - - - -#### -# choosedomainspace returns a potental domainspace -# where the second argument is a target rangespace -# it defaults to the true domainspace, but if this is ambiguous -# it tries to decide a space. -### - -function default_choosedomainspace(A::Operator,sp::Space) - sp2=domainspace(A) - isambiguous(sp2) ? sp : sp2 -end - -choosedomainspace(A::Operator,sp::Space) = default_choosedomainspace(A,sp) - -choosedomainspace(A::Operator,f::Fun) = choosedomainspace(A,space(f)) -choosedomainspace(A::Operator,f::AbstractVector{FF}) where {FF<:Fun} = - choosedomainspace(A,Fun(f)) -choosedomainspace(A::Operator,_) = choosedomainspace(A) - -choosedomainspace(A) = choosedomainspace(A,UnsetSpace()) - -function choosedomainspace(ops::AbstractVector,spin)::Any - sp = UnsetSpace() - - for op in ops - sp = conversion_type(sp,choosedomainspace(op,spin)) - end - - sp -end - -choosespaces(A::Operator,b) = promotedomainspace(A,choosedomainspace(A,b)) - - -spacescompatible(A::Operator,B::Operator) = - spacescompatible(domainspace(A),domainspace(B)) && - spacescompatible(rangespace(A),rangespace(B)) - - -#It's important that domain space is promoted first as it might impact range space -promotespaces(ops::AbstractVector) = promoterangespace(promotedomainspace(ops)) -function promotespaces(ops::AbstractVector,b::Fun) - A=promotespaces(ops) - if isa(rangespace(A),AmbiguousSpace) - # try setting the domain space - A=promoterangespace(promotedomainspace(ops,space(b))) - end - A,Fun(b,rangespace(A[end])) -end - - -function promotespaces(A::Operator,B::Operator) - if spacescompatible(A,B) - A,B - else - tuple(promotespaces([A,B])...) - end -end - - - - -## algebra - - -ldiv_coefficients(A::SpaceOperator,b;kwds...) = - ldiv_coefficients(A.op,b;kwds...) diff --git a/src/Operators/systems.jl b/src/Operators/systems.jl deleted file mode 100644 index c5417a7c..00000000 --- a/src/Operators/systems.jl +++ /dev/null @@ -1,72 +0,0 @@ -##Operators -# TODO: REMOVE! -for op in (:Derivative,:Integral) - @eval begin - function ($op)(d::AbstractVector{T}) where T<:IntervalOrSegment - n=length(d) - R=zeros(Operator{mapreduce(eltype,promote_type,d)},n,n) - for k=1:n - R[k,k]=$op(d[k]) - end - - R - end - end -end - -function Evaluation(d::AbstractVector{T},x...) where T<:IntervalOrSegment - n=length(d) - R=zeros(Operator{mapreduce(eltype,promote_type,d)},n,n) - for k=1:n - R[k,k]=Evaluation(d[k],x...) - end - - R -end - - -## Construction -if VERSION < v"1.3-" - function diagm_container(kv::Pair{<:Integer,<:AbstractVector{O}}...) where O<:Operator - T = mapreduce(x -> mapreduce(eltype,promote_type,x.second), - promote_type, kv) - n = mapreduce(x -> length(x.second) + abs(x.first), max, kv) - zeros(Operator{T}, n, n) - end -else - function diagm_container(size, kv::Pair{<:Integer,<:AbstractVector{O}}...) where O<:Operator - T = mapreduce(x -> mapreduce(eltype,promote_type,x.second), - promote_type, kv) - n = mapreduce(x -> length(x.second) + abs(x.first), max, kv) - zeros(Operator{T}, n, n) - end -end - -##TODO: unify with other blockdiag -function blockdiag(d1::AbstractVector{T},d2::AbstractVector{T}) where T<:Operator - if isempty(d1)&&isempty(d2) - error("Empty blockdiag") - end - if isempty(d1) - TT=mapreduce(eltype,promote_type,d2) - elseif isempty(d2) - TT=mapreduce(eltype,promote_type,d1) - else - TT=promote_type(mapreduce(eltype,promote_type,d1), - mapreduce(eltype,promote_type,d2)) - end - - D=zeros(Operator{TT},length(d1)+length(d2),2) - D[1:length(d1),1]=d1 - D[length(d1)+1:end,2]=d2 - D -end - -blockdiag(a::Operator,b::Operator) = blockdiag(Operator{promote_type(eltype(a),eltype(b))}[a], - Operator{promote_type(eltype(a),eltype(b))}[b]) - -## broadcase - -broadcast(::typeof(*),A::AbstractArray{N},D::Operator) where {N<:Number} = - Operator{promote_type(N,eltype(D))}[A[k,j]*D for k=1:size(A,1),j=1:size(A,2)] -broadcast(::typeof(*),D::Operator,A::AbstractArray{N}) where {N<:Number}=A.*D diff --git a/src/PDE/KroneckerOperator.jl b/src/PDE/KroneckerOperator.jl deleted file mode 100644 index 13d61390..00000000 --- a/src/PDE/KroneckerOperator.jl +++ /dev/null @@ -1,424 +0,0 @@ -export KroneckerOperator - - - -########## -# KroneckerOperator gives the kronecker product of two 1D operators -######### - -struct KroneckerOperator{S,V,DS,RS,DI,RI,T} <: Operator{T} - ops::Tuple{S,V} - domainspace::DS - rangespace::RS - domaintensorizer::DI - rangetensorizer::RI -end - - -KroneckerOperator(A,B,ds::Space,rs::Space,di,ri) = - KroneckerOperator{typeof(A),typeof(B),typeof(ds),typeof(rs),typeof(di),typeof(ri), - promote_type(eltype(A),eltype(B))}((A,B),ds,rs,di,ri) - -KroneckerOperator(A,B,ds::Space,rs::Space) = KroneckerOperator(A,B,ds,rs, - CachedIterator(tensorizer(ds)),CachedIterator(tensorizer(rs))) -function KroneckerOperator(A,B) - ds=domainspace(A)⊗domainspace(B) - rs=rangespace(A)⊗rangespace(B) - KroneckerOperator(A,B,ds,rs) -end -KroneckerOperator(A::UniformScaling,B::UniformScaling) = - KroneckerOperator(ConstantOperator(A.λ),ConstantOperator(B.λ)) -KroneckerOperator(A,B::UniformScaling) = KroneckerOperator(A,ConstantOperator(B.λ)) -KroneckerOperator(A::UniformScaling,B) = KroneckerOperator(ConstantOperator(A.λ),B) -KroneckerOperator(A::Fun,B::Fun) = KroneckerOperator(Multiplication(A),Multiplication(B)) -KroneckerOperator(A::UniformScaling,B::Fun) = KroneckerOperator(ConstantOperator(A.λ),Multiplication(B)) -KroneckerOperator(A::Fun,B::UniformScaling) = KroneckerOperator(Multiplication(A),ConstantOperator(B.λ)) -KroneckerOperator(A,B::Fun) = KroneckerOperator(A,Multiplication(B)) -KroneckerOperator(A::Fun,B) = KroneckerOperator(Multiplication(A),B) - - - -function promotedomainspace(K::KroneckerOperator,ds::TensorSpace) - A=promotedomainspace(K.ops[1],ds.spaces[1]) - B=promotedomainspace(K.ops[2],ds.spaces[2]) - KroneckerOperator(A,B,ds,rangespace(A)⊗rangespace(B)) -end - -function promoterangespace(K::KroneckerOperator,rs::TensorSpace) - A=promoterangespace(K.ops[1],rs.spaces[1]) - B=promoterangespace(K.ops[2],rs.spaces[2]) - KroneckerOperator(A,B,domainspace(K),rs) -end - - -function convert(::Type{Operator{T}},K::KroneckerOperator) where T<:Number - if T == eltype(K) - K - else - ops=Operator{T}(K.ops[1]),Operator{T}(K.ops[2]) - KroneckerOperator{typeof(ops[1]),typeof(ops[2]),typeof(K.domainspace),typeof(K.rangespace), - typeof(K.domaintensorizer),typeof(K.rangetensorizer),T}(ops, - K.domainspace,K.rangespace, - K.domaintensorizer,K.rangetensorizer) - end -end - - -function colstart(A::KroneckerOperator,k::Integer) - K=block(A.domaintensorizer,k) - M = blockbandwidth(A,2) - if isfinite(M) - blockstart(A.rangetensorizer,max(Block(1),K-M)) - else - blockstart(A.rangetensorizer,Block(1)) - end -end - -function colstop(A::KroneckerOperator,k::Integer) - k == 0 && return 0 - K=block(A.domaintensorizer,k) - st=blockstop(A.rangetensorizer,blockcolstop(A,K)) - # zero indicates above dimension - min(size(A,1),st) -end - -function rowstart(A::KroneckerOperator,k::Integer) - K=block(rangespace(A),k) - K2 = Int(K)-blockbandwidth(A,1) - K2 ≤ 1 && return 1 - ds = domainspace(A) - K2 ≥ blocksize(ds,1) && return size(A,2) - blockstart(ds,K2) -end - -function rowstop(A::KroneckerOperator,k::Integer) - K=block(rangespace(A),k) - ds = domainspace(A) - K2 = Int(K)+blockbandwidth(A,2) - K2 ≥ blocksize(ds) && return size(A,2) - st=blockstop(ds,K2) - # zero indicates above dimension - st==0 ? size(A,2) : min(size(A,2),st) -end - - -bandwidths(K::KroneckerOperator) = (∞,∞) - -isblockbanded(K::KroneckerOperator) = all(isblockbanded,K.ops) -isbandedblockbanded(K::KroneckerOperator) = - all(op->isbanded(op) && isinf(size(op,1)) && isinf(size(op,2)),K.ops) -israggedbelow(K::KroneckerOperator) = all(israggedbelow,K.ops) - - -blockbandwidths(K::KroneckerOperator) = - (blockbandwidth(K.ops[1],1)+blockbandwidth(K.ops[2],1), - blockbandwidth(K.ops[1],2)+blockbandwidth(K.ops[2],2)) - -# If each block were in turn BlockBandedMatrix, these would -# be the bandwidths -subblock_blockbandwidths(K::KroneckerOperator) = - (max(blockbandwidth(K.ops[1],1),blockbandwidth(K.ops[2],2)) , - max(blockbandwidth(K.ops[1],2),blockbandwidth(K.ops[2],1))) - - -# If each block were in turn BandedMatrix, these are the bandwidths -function subblockbandwidths(K::KroneckerOperator) - isbandedblockbanded(K) || return (∞,∞) - - if all(hastrivialblocks,domainspace(K).spaces) && - all(hastrivialblocks,rangespace(K).spaces) - subblock_blockbandwidths(K) - else - dt = domaintensorizer(K).iterator - rt = rangetensorizer(K).iterator - # assume block size is repeated and square - @assert all(b->isa(b,AbstractFill),dt.blocks) - @assert rt.blocks ≡ dt.blocks - - sb = subblock_blockbandwidths(K) - # divide by the size of each block - sb_sz = mapreduce(getindex_value,*,dt.blocks) - # spread by sub block szie - (sb[1]+1)*sb_sz-1,(sb[2]+1)*sb_sz-1 - end -end - - -const Wrappers = Union{ConversionWrapper,MultiplicationWrapper,DerivativeWrapper,LaplacianWrapper, - SpaceOperator,ConstantTimesOperator} - - - -isbandedblockbanded(P::Union{PlusOperator,TimesOperator}) = all(isbandedblockbanded,P.ops) - - - -blockbandwidths(P::PlusOperator) = mapreduce(blockbandwidths, (a,b) -> max.(a,b), P.ops) -subblockbandwidths(P::PlusOperator) = mapreduce(subblockbandwidths, (a,b) -> max.(a,b), P.ops) - -blockbandwidths(P::TimesOperator) = mapreduce(blockbandwidths, (a,b) -> a .+ b, P.ops) -subblockbandwidths(P::TimesOperator) = mapreduce(subblockbandwidths, (a,b) -> a .+ b, P.ops) - -domaintensorizer(R::Operator) = tensorizer(domainspace(R)) -rangetensorizer(R::Operator) = tensorizer(rangespace(R)) - -domaintensorizer(P::PlusOperator) = domaintensorizer(P.ops[1]) -rangetensorizer(P::PlusOperator) = rangetensorizer(P.ops[1]) - -domaintensorizer(P::TimesOperator) = domaintensorizer(P.ops[end]) -rangetensorizer(P::TimesOperator) = rangetensorizer(P.ops[1]) - -for FUNC in (:blockbandwidths, :subblockbandwidths, :isbandedblockbanded,:domaintensorizer,:rangetensorizer) - @eval $FUNC(K::Wrappers) = $FUNC(K.op) -end - - -domainspace(K::KroneckerOperator) = K.domainspace -rangespace(K::KroneckerOperator) = K.rangespace - -domaintensorizer(K::KroneckerOperator) = K.domaintensorizer -rangetensorizer(K::KroneckerOperator) = K.rangetensorizer - - -# we suport 4-indexing with KroneckerOperator -# If A is K x J and B is N x M, then w -# index to match KO=reshape(kron(B,A),N,K,M,J) -# that is -# KO[k,n,j,m] = A[k,j]*B[n,m] -# TODO: arbitrary number of ops - -getindex(KO::KroneckerOperator,k::Integer,n::Integer,j::Integer,m::Integer) = - KO.ops[1][k,j]*KO.ops[2][n,m] - -function getindex(KO::KroneckerOperator,kin::Integer,jin::Integer) - j,m=KO.domaintensorizer[jin] - k,n=KO.rangetensorizer[kin] - KO[k,n,j,m] -end - -function getindex(KO::KroneckerOperator,k::Integer) - if size(KO,1) == 1 - KO[1,k] - elseif size(KO,2) == 1 - KO[k,1] - else - throw(ArgumentError("[k] only defined for 1 x ∞ and ∞ x 1 operators")) - end -end - - -*(A::KroneckerOperator,B::KroneckerOperator) = - KroneckerOperator(A.ops[1]*B.ops[1],A.ops[2]*B.ops[2]) - - - -## Shorthand - - -⊗(A,B) = kron(A,B) - -Base.kron(A::Operator,B::Operator) = KroneckerOperator(A,B) -Base.kron(A::Operator,B) = KroneckerOperator(A,B) -Base.kron(A,B::Operator) = KroneckerOperator(A,B) -Base.kron(A::AbstractVector{T},B::Operator) where {T<:Operator} = - Operator{promote_type(eltype(T),eltype(B))}[kron(a,B) for a in A] -Base.kron(A::Operator,B::AbstractVector{T}) where {T<:Operator} = - Operator{promote_type(eltype(T),eltype(A))}[kron(A,b) for b in B] -Base.kron(A::AbstractVector{T},B::UniformScaling) where {T<:Operator} = - Operator{promote_type(eltype(T),eltype(B))}[kron(a,1.0B) for a in A] -Base.kron(A::UniformScaling,B::AbstractVector{T}) where {T<:Operator} = - Operator{promote_type(eltype(T),eltype(A))}[kron(1.0A,b) for b in B] - - - - - - -## transpose - - -Base.transpose(K::KroneckerOperator)=KroneckerOperator(K.ops[2],K.ops[1]) - -for TYP in (:ConversionWrapper,:MultiplicationWrapper,:DerivativeWrapper,:IntegralWrapper,:LaplacianWrapper), - FUNC in (:domaintensorizer,:rangetensorizer) - @eval $FUNC(S::$TYP) = $FUNC(S.op) -end - - -Base.transpose(S::SpaceOperator) = - SpaceOperator(transpose(S.op), transpose(domainspace(S)), transpose(rangespace(S))) -Base.transpose(S::ConstantTimesOperator) = sp.c*transpose(S.op) - - - -### Calculus - -#TODO: general dimension -function Derivative(S::TensorSpace{SV,DD},order::Vector{Int}) where {SV,DD<:EuclideanDomain{2}} - @assert length(order)==2 - if order[1]==0 - Dy=Derivative(S.spaces[2],order[2]) - K=Operator(I,S.spaces[1])⊗Dy - T=eltype(Dy) - elseif order[2]==0 - Dx=Derivative(S.spaces[1],order[1]) - K=Dx⊗Operator(I,S.spaces[2]) - T=eltype(Dx) - else - Dx=Derivative(S.spaces[1],order[1]) - Dy=Derivative(S.spaces[2],order[2]) - K=Dx⊗Dy - T=promote_type(eltype(Dx),eltype(Dy)) - end - # try to work around type inference - DerivativeWrapper{typeof(K),typeof(domainspace(K)),Vector{Int},T}(K,order) -end - - -DefiniteIntegral(S::TensorSpace) = DefiniteIntegralWrapper(mapreduce(DefiniteIntegral,⊗,S.spaces)) - - - -### Copy - -# finds block lengths for a subrange -blocklengthrange(rt, B::Block) = [blocklength(rt,B)] -blocklengthrange(rt, B::BlockRange) = blocklength(rt,B) -function blocklengthrange(rt, kr) - KR=block(rt,first(kr)):block(rt,last(kr)) - Klengths=Vector{Int}(length(KR)) - for ν in eachindex(KR) - Klengths[ν]=blocklength(rt,KR[ν]) - end - Klengths[1]+=blockstart(rt,KR[1])-kr[1] - Klengths[end]+=kr[end]-blockstop(rt,KR[end]) - Klengths -end - -function bandedblockbanded_convert!(ret, S::SubOperator, KO, rt, dt) - pinds = parentindices(S) - kr,jr = pinds - - kr1,jr1 = reindex(S,pinds,(1,1)) - - Kshft = block(rt,kr1)-1 - Jshft = block(dt,jr1)-1 - - - - for J=blockaxes(ret,2) - jshft = (J==Block(1) ? jr1 : blockstart(dt,J+Jshft)) - 1 - for K=blockcolrange(ret,J) - Bs = view(ret,K,J) - Bspinds = parentindices(Bs) - kshft = (K==Block(1) ? kr1 : blockstart(rt,K+Kshft)) - 1 - for ξ=1:size(Bs,2),κ=colrange(Bs,ξ) - Bs[κ,ξ] = S[reindex(Bs,Bspinds,(κ,ξ))...] - end - end - end - - ret -end - - - -function default_BandedBlockBandedMatrix(S) - KO = parent(S) - rt=rangespace(KO) - dt=domainspace(KO) - ret = BandedBlockBandedMatrix(Zeros, S) - bandedblockbanded_convert!(ret, S, parent(S), rt, dt) -end - -BandedBlockBandedMatrix(S::SubOperator) = default_BandedBlockBandedMatrix(S) - - -const Trivial2DTensorizer = CachedIterator{Tuple{Int,Int}, - TrivialTensorizer{2}} - -# This routine is an efficient version of KroneckerOperator for the case of -# tensor product of trivial blocks - -function BandedBlockBandedMatrix(S::SubOperator{T,KroneckerOperator{SS,V,DS,RS, - Trivial2DTensorizer,Trivial2DTensorizer,T}, - Tuple{BlockRange1,BlockRange1}}) where {SS,V,DS,RS,T} - KR,JR = parentindices(S) - KR_i, JR_i = Int.(KR), Int.(JR) - - KO=parent(S) - - ret = BandedBlockBandedMatrix(Zeros, S) - - A,B = KO.ops - - - AA = convert(BandedMatrix, view(A, Block(1):last(KR),Block(1):last(JR)))::BandedMatrix{eltype(S)} - Al,Au = bandwidths(AA) - BB = convert(BandedMatrix, view(B, Block(1):last(KR),Block(1):last(JR)))::BandedMatrix{eltype(S)} - Bl,Bu = bandwidths(BB) - λ,μ = subblockbandwidths(ret) - - for J in blockaxes(ret,2), K in blockcolrange(ret,J) - n,m = KR_i[Int(K)],JR_i[Int(J)] - Bs = view(ret, K, J) - l = min(Al,Bu+n-m,λ) - u = min(Au,Bl+m-n,μ) - @inbounds for j=1:m, k=max(1,j-u):min(n,j+l) - a = AA[k,j] - b = BB[n-k+1,m-j+1] - c = a*b - inbands_setindex!(Bs,c,k,j) - end - end - ret -end - -convert(::Type{BandedBlockBandedMatrix}, S::SubOperator{T,KroneckerOperator{SS,V,DS,RS, - Trivial2DTensorizer,Trivial2DTensorizer,T}, - Tuple{BlockRange1,BlockRange1}}) where {SS,V,DS,RS,T} = - BandedBlockBandedMatrix(S) - -## TensorSpace operators - - -## Conversion - - - - -conversion_rule(a::TensorSpace,b::TensorSpace) = conversion_type(a.spaces[1],b.spaces[1])⊗conversion_type(a.spaces[2],b.spaces[2]) -maxspace(a::TensorSpace,b::TensorSpace) = maxspace(a.spaces[1],b.spaces[1])⊗maxspace(a.spaces[2],b.spaces[2]) - -# TODO: we explicetly state type to avoid type inference bug in 0.4 - -ConcreteConversion(a::BivariateSpace,b::BivariateSpace) = - ConcreteConversion{typeof(a),typeof(b), - promote_type(prectype(a),prectype(b))}(a,b) - -Conversion(a::TensorSpace,b::TensorSpace) = ConversionWrapper(promote_type(prectype(a),prectype(b)), - KroneckerOperator(Conversion(a.spaces[1],b.spaces[1]),Conversion(a.spaces[2],b.spaces[2]))) - - - -function Multiplication(f::Fun{TS},S::TensorSpace) where {TS<:TensorSpace} - lr=LowRankFun(f) - ops=map(kron,map(a->Multiplication(a,S.spaces[1]),lr.A),map(a->Multiplication(a,S.spaces[2]),lr.B)) - MultiplicationWrapper(f,+(ops...)) -end - -## Functionals -Evaluation(sp::TensorSpace,x::Vec) = EvaluationWrapper(sp,x,zeros(Int,length(x)),⊗(map(Evaluation,sp.spaces,x)...)) -Evaluation(sp::TensorSpace,x::Tuple) = Evaluation(sp,Vec(x...)) - - - -# it's faster to build the operators to the last b -function mul_coefficients(A::SubOperator{T,KKO,Tuple{UnitRange{Int},UnitRange{Int}}}, b) where {T,KKO<:KroneckerOperator} - P = parent(A) - kr,jr = parentindices(A) - dt,rt = domaintensorizer(P),rangetensorizer(P) - KR,JR = Block(1):block(rt,kr[end]),Block(1):block(dt,jr[end]) - M = P[KR,JR] - M*pad(b, size(M,2)) -end diff --git a/src/PDE/PDE.jl b/src/PDE/PDE.jl deleted file mode 100644 index 9cf60778..00000000 --- a/src/PDE/PDE.jl +++ /dev/null @@ -1,61 +0,0 @@ -export discretize,timedirichlet - -include("KroneckerOperator.jl") - -## PDE - -lap(d::Space) = Laplacian(d) -lap(d::Domain) = Laplacian(d) -lap(f::Fun) = Laplacian()*f - - -function Laplacian(d::BivariateSpace,k::Integer) - Dx2=Derivative(d,[2,0]) - Dy2=Derivative(d,[0,2]) - if k==1 - LaplacianWrapper(Dx2+Dy2,k) - else - @assert k > 0 - Δ=Laplacian(d,1) - LaplacianWrapper(TimesOperator(Laplacian(rangespace(Δ),k-1),Δ),k) - end -end - -Laplacian(d::EuclideanDomain{2}, k::Integer) = Laplacian(Space(d),k) -grad(d::ProductDomain) = [Derivative(d,[1,0]),Derivative(d,[0,1])] - - -function tensor_Dirichlet(d::Union{ProductDomain,TensorSpace},k) - @assert nfactors(d)==2 - - DirichletWrapper( - if isempty(∂(factor(d,1))) - I ⊗ Dirichlet(factor(d,2),k) - elseif isempty(∂(factor(d,2))) - Dirichlet(factor(d,1),k) ⊗ I - else - [Dirichlet(factor(d,1),k) ⊗ I;I ⊗ Dirichlet(factor(d,2),k)] - end, k) -end - -Dirichlet(d::Union{ProductDomain,TensorSpace},k) = tensor_Dirichlet(d,k) - - -function timedirichlet(d::Union{ProductDomain,TensorSpace}) - @assert nfactors(d)==2 - Bx=Dirichlet(factor(d,1)) - Bt=ldirichlet(factor(d,2)) - [I⊗Bt;Bx⊗I] -end - - - -function *(B::Operator,f::ProductFun) - if isafunctional(B) - Fun(factor(space(f),2),map(c->Number(B*c),f.coefficients)) - else - ProductFun(space(f),map(c->B*c,f.coefficients)) - end -end - -*(f::ProductFun,B::Operator) = transpose(B*(transpose(f))) diff --git a/src/Space.jl b/src/Space.jl deleted file mode 100644 index 084cb137..00000000 --- a/src/Space.jl +++ /dev/null @@ -1,548 +0,0 @@ - - -export Space, domainspace, rangespace, maxspace,Space,conversion_type, transform, - itransform, SequenceSpace, ConstantSpace - - - -# Space maps the Domain to the type R -# For example, we have -# Chebyshev{Interval{Float64}} <: Space{Interval{Float64},Float64} -# Laurent{PeriodicSegment{Float64}} <: Space{PeriodicSegment{Float64},ComplexF64} -# Fourier{Circle{ComplexF64}} <: Space{Circle{ComplexF64},Float64} -# Note for now Space doesn't contain any information about the coefficients - -abstract type Space{D,R} end - - - -const RealSpace = Space{D,R} where {D,R<:Real} -const ComplexSpace = Space{D,R} where {D,R<:Complex} -const UnivariateSpace = Space{D,R} where {D<:Domain{<:Number},R} -const BivariateSpace = Space{D,R} where {D<:EuclideanDomain{2},R} -const RealUnivariateSpace = RealSpace{D,R} where {D<:Domain{<:Number},R<:Real} - - - - -eltype(S::Space{T}) where {T} = error("Eltype has been changed to domaintype, rangetype or prectype") -eltype(::Type{Space{D,R}}) where {D,R} = error("Eltype has been changed to domaintype, rangetype or prectype") - -domaintype(::Space{D,R}) where {D,R} = D -domaintype(::Type{Space{D,R}}) where {D,R} = D -domaintype(::Type{FT}) where {FT<:Space} = domaintype(supertype(FT)) -rangetype(::Space{D,R}) where {D,R} = R -rangetype(::Type{Space{D,R}}) where {D,R} = R -rangetype(::Type{FT}) where {FT<:Space} = rangetype(supertype(FT)) - -domaindimension(sp::Space) = dimension(domain(sp)) -dimension(::Space) = ∞ # We assume infinite-dimensional spaces - - -# add indexing for all spaces, not just DirectSumSpace -# mimicking scalar vs vector - - - -#supports broadcasting, overloaded for ArraySpace -size(::Space) = () - -transpose(sp::Space) = sp # default no-op - - -# the default is all spaces have one-coefficient blocks -blocklengths(S::Space) = Ones{Int}(dimension(S)) -blocksize(S::Space) = (length(blocklengths(S)),) -blockaxes(S::Space) = (Block.(Base.OneTo(length(blocklengths(S)))),) -function blockaxes(A::Space, d) - @assert d == 1 - blockaxes(A)[1] -end - -block(S::Space,k) = Block(k) - -Space(s::Space) = s - - - -abstract type AmbiguousSpace <: Space{AnyDomain,UnsetNumber} end -domain(::AmbiguousSpace) = AnyDomain() - - -function setdomain(sp::Space{D},d::D) where D<:Domain - S = typeof(sp) - @assert length(fieldnames(S))==1 - S(d) -end - -# function setdomain(sp::Space,d::Domain) -# S=typeof(sp) -# @assert length(fieldnames(S))==1 -# # the domain is not compatible, but maybe we c -# # can drop the space depence. For example, -# # CosSpace{Circle{Float64}} -> CosSpace -# eval(Meta.parse(string(S.name.module)*"."*string(S.name)))(d) -# end - -setcanonicaldomain(s) = setdomain(s,canonicaldomain(s)) -reverseorientation(S::Space) = setdomain(S,reverseorientation(domain(S))) - - -# UnsetSpace dictates that an operator is not defined until -# its domainspace is promoted -# NoSpace is used to indicate no space exists for, e.g., -# conversion_type - -struct UnsetSpace <: AmbiguousSpace end -struct NoSpace <: AmbiguousSpace end - -isambiguous(_) = false -isambiguous(::Type{UnsetNumber}) = true -isambiguous(::Type{Array{T}}) where {T} = isambiguous(T) -isambiguous(sp::Space) = isambiguous(rangetype(sp)) - - -#TODO: should it default to canonicalspace? -points(d::Space,n) = points(domain(d),n) -points(d::Space) = points(d, dimension(d)) - - - -canonicalspace(T) = T -canonicaldomain(S::Space) = canonicaldomain(domain(S)) - - -# Check whether spaces are the same, override when you need to check parameters -# This is used in place of == to support AnyDomain -spacescompatible(f::D,g::D) where D<:Space = error("Override spacescompatible for "*string(D)) -spacescompatible(::UnsetSpace,::UnsetSpace) = true -spacescompatible(::NoSpace,::NoSpace) = true -spacescompatible(f,g) = false -==(A::Space,B::Space) = spacescompatible(A,B) && domain(A) == domain(B) -spacesequal(A::Space,B::Space) = A==B - -pointscompatible(f,g) = spacescompatible(f,g) - -# check a list of spaces for compatibility -for OP in (:spacescompatible,:domainscompatible,:spacesequal),TYP in (:AbstractArray,:Tuple) - @eval function $OP(v::$TYP) - for k=1:length(v)-1 - if !$OP(v[k],v[k+1]) - return false - end - end - true - end -end - - - -domain(A::Space) = A.domain # assume it has a field domain - - - -for op in (:tocanonical,:fromcanonical,:tocanonicalD,:fromcanonicalD,:invfromcanonicalD) - @eval ($op)(sp::Space,x...)=$op(domain(sp),x...) -end - -mappoint(a::Space,b::Space,x)=mappoint(domain(a),domain(b),x) -mappoint(a::Space,b::Domain,x)=mappoint(domain(a),b,x) -mappoint(a::Domain,b::Space,x)=mappoint(a,domain(b),x) - - - -for FUNC in (:conversion_rule,:maxspace_rule,:union_rule) - @eval begin - function $FUNC(a,b)::Any - if spacescompatible(a,b) - a - else - NoSpace() - end - end - end -end - - - -for FUNC in (:conversion_type,:maxspace) - @eval begin - $FUNC(a::UnsetSpace,b::UnsetSpace) = a - $FUNC(a::UnsetSpace,b::Space) = b - $FUNC(a::Space,b::UnsetSpace) = a - end -end - - -# gives a space c that has a banded conversion operator TO a and b -function conversion_type(a,b)::Any - if spacescompatible(a,b) - a - elseif !domainscompatible(a,b) - NoSpace() # this avoids having to check eachtime - else - cr=conversion_rule(a,b) - cr==NoSpace() ? conversion_rule(b,a) : cr - end -end - - - - - - - -# gives a space c that has a banded conversion operator FROM a and b -maxspace(a,b) = NoSpace() # TODO: this fixes weird bug with Nothing -function maxspace(a::Space, b::Space)::Any - if spacescompatible(a,b) - return a - elseif !domainscompatible(a,b) - return NoSpace() # this avoids having to check eachtime - end - - - - cr=maxspace_rule(a,b) - if !isa(cr,NoSpace) - return cr - end - - cr=maxspace_rule(b,a) - if !isa(cr,NoSpace) - return cr - end - - cr=conversion_type(a,b) - if cr==a - return b - elseif cr ==b - return a - end - - # check if its banded through canonicalspace - cspa=canonicalspace(a) - if spacescompatible(cspa,b) - # we can't call maxspace(cspa,a) - # maxspace/conversion_type should be implemented for canonicalspace - error("Override conversion_type or maxspace for "*string(a)*" and "*string(b)) - end - if cspa != a && maxspace(cspa,a)==cspa - return maxspace(b,cspa) - end - - cspb=canonicalspace(b) - if spacescompatible(cspb,a) - # we can't call maxspace(cspb,b) - error("Override conversion_type or maxspace for "*string(a)*" and "*string(b)) - end - if cspb !=b && maxspace(cspb,b)==cspb - return maxspace(a,cspb) - end - - NoSpace() -end - - - - -# union combines two spaces -# this is used primarily for addition of two funs -# that may be incompatible -union(a::AmbiguousSpace, b::AmbiguousSpace) = b -union(a::AmbiguousSpace, b::Space) = b -union(a::Space, b::AmbiguousSpace) = a - - -function union_by_union_rule(a::Space,b::Space)::Any - if spacescompatible(a,b) - if isambiguous(domain(a)) - return b - else - return a - end - end - - cr = union_rule(a,b) - cr isa NoSpace || return cr - - union_rule(b,a) -end - -function union(a::Space, b::Space)::Any - cr = union_by_union_rule(a,b) - cr isa NoSpace || return cr - - cspa=canonicalspace(a) - cspb=canonicalspace(b) - if cspa!=a || cspb!=b - cr = union_by_union_rule(cspa,cspb) - end - # TODO: Uncomment when Julia bug is fixed - cr=maxspace(a,b) #Max space since we can convert both to it - if !isa(cr,NoSpace) - return cr - end - - a ⊕ b -end - -union(a::Space) = a - -union(a::Space,b::Space,c::Space) = union(union(a,b),c) -union(a::Space,b::Space,c::Space,d::Space...) = - union(union(a,b),c,d...) - - -# tests whether a Conversion operator exists -hasconversion(a,b) = maxspace(a,b) == b - - -# tests whether a coefficients can be converted to b -isconvertible(a,b) = a == b || hasconversion(a,b) - -## Conversion routines -# coefficients(v::AbstractVector,a,b) -# converts from space a to space b -# coefficients(v::Fun,a) -# is equivalent to coefficients(v.coefficients,v.space,a) -# coefficients(v::AbstractVector,a,b,c) -# uses an intermediate space b - -coefficients(f,sp1,sp2,sp3) = coefficients(coefficients(f,sp1,sp2),sp2,sp3) - -coefficients(f::AbstractVector,::Type{T1},::Type{T2}) where {T1<:Space,T2<:Space} = - coefficients(f,T1(),T2()) -coefficients(f::AbstractVector,::Type{T1},sp2::Space) where {T1<:Space} = coefficients(f,T1(),sp2) -coefficients(f::AbstractVector,sp1::Space,::Type{T2}) where {T2<:Space} = coefficients(f,sp1,T2()) - -## coefficients defaults to calling Conversion, otherwise it tries to pipe through Chebyshev - - -function defaultcoefficients(f,a,b) - ct=conversion_type(a,b) # gives a space that has a banded conversion to both a and b - - if spacescompatible(a,b) - f - elseif hasconversion(a,b) - mul_coefficients(Conversion(a,b),f) - elseif hasconversion(b,a) - ldiv_coefficients(Conversion(b,a),f) - else - csp=canonicalspace(a) - - if spacescompatible(a,csp)# a is csp, so try b - csp=canonicalspace(b) - end - if spacescompatible(a,csp) || spacescompatible(b,csp) - # b is csp too, so we are stuck, try Fun constructor - coefficients(default_Fun(Fun(a,f),b)) - else - coefficients(f,a,csp,b) - end - end -end - -coefficients(f,a,b) = defaultcoefficients(f,a,b) - - - - - - - -## rand -# checkpoints is used to give a list of points to double check -# the expansion -rand(d::Space,k...) = rand(domain(d),k...) -checkpoints(d::Space) = checkpoints(domain(d)) - - - -## default transforms -abstract type AbstractTransformPlan{T} <: Plan{T} end - -space(P::AbstractTransformPlan) = P.space - -# These plans are use to wrap another plan -for Typ in (:TransformPlan,:ITransformPlan) - @eval begin - struct $Typ{T,SP,inplace,PL} <: AbstractTransformPlan{T} - space::SP - plan::PL - end - $Typ(space,plan,::Type{Val{inplace}}) where {inplace} = - $Typ{eltype(plan),typeof(space),inplace,typeof(plan)}(space,plan) - # *(P::$Typ, x::AbstractArray) = P.plan*x - end -end - - - -for Typ in (:CanonicalTransformPlan,:ICanonicalTransformPlan) - @eval begin - struct $Typ{T,SP,PL,CSP} <: AbstractTransformPlan{T} - space::SP - plan::PL - canonicalspace::CSP - end - $Typ(space,plan,csp) = - $Typ{eltype(plan),typeof(space),typeof(plan),typeof(csp)}(space,plan,csp) - $Typ(::Type{T},space,plan,csp) where {T} = - $Typ{T,typeof(space),typeof(plan),typeof(csp)}(space,plan,csp) - end -end - - -# Canonical plan uses coefficients -function CanonicalTransformPlan(space,v) - csp = canonicalspace(space) - CanonicalTransformPlan(eltype(v),space,plan_transform(csp,v),csp) -end -function plan_transform(sp::Space,vals) - csp = canonicalspace(sp) - if sp == csp - error("Override for $sp") - end - CanonicalTransformPlan(sp,plan_transform(csp,vals),csp) -end - -function ICanonicalTransformPlan(space,v) - csp = canonicalspace(space) - cfs = coefficients(v,space,csp) - ICanonicalTransformPlan(eltype(v),space,plan_itransform(csp,cfs),csp) -end -function plan_itransform(sp::Space,v) - csp = canonicalspace(sp) - if sp == csp - error("Override for $sp") - end - cfs = coefficients(v,sp,csp) - ICanonicalTransformPlan(sp,plan_itransform(csp,cfs),csp) -end - - -plan_transform!(sp::Space,vals) = error("Override for $sp") -plan_itransform!(sp::Space,cfs) = error("Override for $sp") - - - -# transform converts from values at points(S,n) to coefficients -# itransform converts from coefficients to values at points(S,n) - -transform(S::Space,vals) = plan_transform(S,vals)*vals -itransform(S::Space,cfs) = plan_itransform(S,cfs)*cfs - -itransform!(S::Space,cfs) = plan_itransform!(S,cfs)*cfs -transform!(S::Space,cfs) = plan_transform!(S,cfs)*cfs - - -*(P::CanonicalTransformPlan,vals::AbstractVector) = coefficients(P.plan*vals,P.canonicalspace,P.space) -*(P::ICanonicalTransformPlan,cfs::AbstractVector) = P.plan*coefficients(cfs,P.space,P.canonicalspace) - - - -for OP in (:plan_transform,:plan_itransform,:plan_transform!,:plan_itransform!) - # plan transform expects a vector - # this passes an empty Float64 array - @eval begin - $OP(S::Space,::Type{T},n::Integer) where {T} = $OP(S,Vector{T}(undef, n)) - $OP(S::Space,n::Integer) = $OP(S, Float64, n) - end -end - -## sorting -# we sort spaces lexigraphically by default - -for OP in (:<,:(<=),:>,:(>=),:(isless)) - @eval $OP(a::Space,b::Space)=$OP(string(a),string(b)) -end - -## Important special spaces - - -struct ZeroSpace{DD,R} <: Space{DD,R} - domain::DD - ZeroSpace{DD,R}(d::DD) where {DD,R} = new(d) - ZeroSpace{DD,R}(d::AnyDomain) where {DD,R} = new(convert(DD,d)) -end - - -ZeroSpace(S::Space) = ZeroSpace{domaintype(S),rangetype(S)}(domain(S)) -ZeroSpace() = ZeroSpace{AnyDomain,UnsetNumber}(AnyDomain()) -domain(S::ZeroSpace) = S.domain - -dimension(::ZeroSpace) = 0 - -spacescompatible(::ZeroSpace,::ZeroSpace) = true -for FUNC in (:conversion_type,:maxspace) - @eval begin - $FUNC(::ZeroSpace,::UnsetSpace) = UnsetSpace() - $FUNC(::UnsetSpace,::ZeroSpace) = UnsetSpace() - end -end - - -""" -`ConstantSpace` is the 1-dimensional scalar space. -""" -struct ConstantSpace{DD,R} <: Space{DD,R} - domain::DD - ConstantSpace{DD,R}(d::DD) where {DD,R} = new(d) - ConstantSpace{DD,R}(d::AnyDomain) where {DD,R} = new(convert(DD,d)) -end - -ConstantSpace(d::Domain) = ConstantSpace{typeof(d),real(prectype(d))}(d) - -ConstantSpace(::Type{N},d::Domain) where {N<:Number} = ConstantSpace{typeof(d),real(N)}(d) -ConstantSpace(::Type{N}) where {N<:Number} = ConstantSpace(N,AnyDomain()) -ConstantSpace() = ConstantSpace(Float64) - - -convert(::Type{Space}, z::Number) = ConstantSpace(convert(Domain, z)) # Spaces -convert(::Type{ConstantSpace}, d::Domain) = ConstantSpace(d) -Space(z::Number) = convert(Space, z) - -isconstspace(::ConstantSpace) = true - -for pl in (:plan_transform,:plan_transform!,:plan_itransform,:plan_itransform!) - @eval $pl(sp::ConstantSpace,vals::AbstractVector) = I -end - -# we override maxspace instead of maxspace_rule to avoid -# domainscompatible check. -for OP in (:maxspace,:(union)) - @eval begin - $OP(A::ConstantSpace{AnyDomain},B::ConstantSpace{AnyDomain}) = A - $OP(A::ConstantSpace{AnyDomain},B::ConstantSpace) = B - $OP(A::ConstantSpace,B::ConstantSpace{AnyDomain}) = A - $OP(A::ConstantSpace,B::ConstantSpace) = ConstantSpace(domain(A) ∪ domain(B)) - end -end - -space(x::Number) = ConstantSpace(typeof(x)) -space(f::AbstractArray{T}) where T<:Number = ArraySpace(ConstantSpace{T}(), size(f)...) - -setdomain(A::ConstantSpace{DD,R}, d) where {DD,R} = ConstantSpace{typeof(d),R}(d) - -blocklengths(::ConstantSpace) = Vec(1) - -# Range type is Nothing since function evaluation is not defined -struct SequenceSpace <: Space{PositiveIntegers,Nothing} end - -""" -`SequenceSpace` is the space of all sequences, i.e., infinite vectors. -Also denoted ℓ⁰. -""" -SequenceSpace() - - -const ℓ⁰ = SequenceSpace() -dimension(::SequenceSpace) = ∞ -domain(::SequenceSpace) = ℕ -spacescompatible(::SequenceSpace,::SequenceSpace) = true - - -## Boundary - -boundary(S::Space) = boundary(domain(S)) diff --git a/src/Spaces/ArraySpace.jl b/src/Spaces/ArraySpace.jl deleted file mode 100644 index cf59d860..00000000 --- a/src/Spaces/ArraySpace.jl +++ /dev/null @@ -1,293 +0,0 @@ -""" - ArraySpace(s::Space,dims...) - -is used to represent array-valued expansions in a space `s`. The -coefficients are of each entry are interlaced. - -For example, -```julia -f = Fun(x->[exp(x),sin(x)],-1..1) -space(f) == ArraySpace(Chebyshev(),2) -``` -""" -struct ArraySpace{S,n,DD,RR} <: DirectSumSpace{NTuple{n,S},DD,Array{RR,n}} - spaces::Array{S,n} -end - -const VectorSpace{S,DD,RR} = ArraySpace{S,1,DD,RR} -const MatrixSpace{S,DD,RR} = ArraySpace{S,2,DD,RR} - -#TODO: Think through domain/domaindominsion -ArraySpace(sp::AbstractArray{SS,N}) where {SS<:Space,N} = - ArraySpace{SS,N,domaintype(first(sp)),mapreduce(rangetype,promote_type,sp)}(sp) -ArraySpace(S::Space,n::NTuple{N,Int}) where {N} = ArraySpace(fill(S,n)) -ArraySpace(S::Space,n::Integer) = ArraySpace(S,(n,)) -ArraySpace(S::Space,n,m) = ArraySpace(fill(S,(n,m))) -ArraySpace(d::Domain,n...) = ArraySpace(Space(d),n...) - -Space(sp::AbstractArray{<:Space}) = ArraySpace(sp) -convert(::Type{Array}, sp::ArraySpace) = sp.spaces -convert(::Type{Vector}, sp::VectorSpace) = sp.spaces -convert(::Type{Matrix}, sp::MatrixSpace) = sp.spaces -Array(sp::ArraySpace) = sp.spaces -Vector(sp::VectorSpace) = sp.spaces -Matrix(sp::MatrixSpace) = sp.spaces - - -BlockInterlacer(sp::ArraySpace) = BlockInterlacer(blocklengths.(tuple(sp.spaces...))) -interlacer(sp::ArraySpace) = BlockInterlacer(sp) - -for OP in (:length,:firstindex,:lastindex,:size) - @eval begin - $OP(S::ArraySpace) = $OP(components(S)) - $OP(f::Fun{<:ArraySpace}) = $OP(space(f)) - end -end - -for OP in (:getindex,:iterate,:stride,:size,:lastindex,:firstindex) - @eval $OP(S::ArraySpace,k) = $OP(components(S),k) -end - -iterate(S::ArraySpace) = iterate(components(S)) -getindex(S::ArraySpace, kr::AbstractVector) = ArraySpace(components(S)[kr]) - -#support tuple set - -stride(f::Fun{<:ArraySpace},k) = stride(space(f),k) - -getindex(f::ArraySpace,k...) = Space(component(f,k...)) -iterate(f::Fun{<:ArraySpace}) = iterate(components(f)) - - -Base.reshape(AS::ArraySpace,k...) = ArraySpace(reshape(AS.spaces,k...)) -dimension(AS::ArraySpace) = mapreduce(dimension,+,AS.spaces) - -# TODO: union domain -domain(AS::ArraySpace) = domain(AS.spaces[1]) -setdomain(A::ArraySpace,d::Domain) = ArraySpace(map(sp->setdomain(sp,d),A.spaces)) - - - -# support for Array of PiecewiseSpace - - -## transforms - -#TODO: rework for different spaces -points(d::ArraySpace,n) = points(d.spaces[1],n) - - -transform(AS::ArraySpace{SS,1},vals::AbstractVector{Vector{V}}) where {SS,V} = - transform(AS,transpose(hcat(vals...))) - - -function transform(AS::ArraySpace{SS,1,T},M::AbstractArray{V,2}) where {SS,T,V<:Number} - n=length(AS) - - @assert size(M,2) == n - plan = plan_transform(AS.spaces[1],M[:,1]) - cfs=Vector{V}[plan*M[:,k] for k=1:size(M,2)] - - interlace(cfs,AS) -end - -# transform of array is same order as vectorizing and then transforming -transform(AS::ArraySpace{SS,n},vals::AbstractVector{Array{V,n}}) where {SS,n,V} = - transform(vec(AS),map(vec,vals)) -transform(AS::VectorSpace{SS},vals::AbstractVector{AV}) where {SS,AV<:AbstractVector} = - transform(AS,map(Vector,vals)) -transform(AS::VectorSpace{SS},vals::AbstractVector{Vec{V,n}}) where {SS,n,V} = - transform(AS,map(Vector,vals)) - -function itransform(AS::VectorSpace,cfs::AbstractVector) - vf = vec(Fun(AS, cfs)) - n = maximum(ncoefficients, vf) - vcat.(values.(pad!.(vf, n))...) -end - - -Base.vec(AS::ArraySpace) = ArraySpace(vec(AS.spaces)) -Base.vec(f::Fun{ArraySpace{S,n,DD,RR}}) where {S,n,DD,RR} = - [f[j] for j=1:length(f.space)] - -repeat(A::ArraySpace,n,m) = ArraySpace(repeat(A.spaces,n,m)) - -component(A::MatrixSpace,k::Integer,j::Integer) = A.spaces[k,j] - -Base.getindex(f::Fun{DSS},k::Integer) where {DSS<:ArraySpace} = component(f,k) - - -Base.getindex(f::Fun{MatrixSpace{S,DD,RR}},k::Integer,j::Integer) where {S,DD,RR} = - f[k+stride(f,2)*(j-1)] - -Base.getindex(f::Fun{DSS},kj::CartesianIndex{1}) where {DSS<:ArraySpace} = f[kj[1]] -Base.getindex(f::Fun{DSS},kj::CartesianIndex{2}) where {DSS<:ArraySpace} = f[kj[1],kj[2]] - - -function Fun(A::AbstractArray{Fun{VectorSpace{S,DD,RR},V,VV},2}) where {S,V,VV,DD,RR} - @assert size(A,1)==1 - - M = Matrix{Fun{S,V,VV}}(undef, length(space(A[1])),size(A,2)) - for k=1:size(A,2) - M[:,k]=vec(A[k]) - end - Fun(M) -end - -# Fun{SS,n}(v::AbstractArray{Any,n},sp::ArraySpace{SS,n}) = Fun(map((f,s)->Fun(f,s),v,sp)) - - -# convert a vector to a Fun with ArraySpace - - - -function Fun(v::AbstractVector,sp::Space{D,R}) where {D,R<:AbstractVector} - if size(v) ≠ size(sp) - throw(DimensionMismatch("Cannot convert $v to a Fun in space $sp")) - end - Fun(map(Fun,v,components(sp))) -end - -Fun(v::AbstractArray{TT,n},sp::Space{D,R}) where {D,R<:AbstractArray{SS,n}} where {TT,SS,n} = - reshape(Fun(vec(v),vec(sp)),size(sp)) - - -coefficients(v::AbstractArray{TT,n},sp::ArraySpace{SS,n}) where {TT,SS,n} = coefficients(Fun(v,sp)) - - -for (OPrule,OP) in ((:conversion_rule,:conversion_type),(:maxspace_rule,:maxspace), - (:union_rule,:union)) - # ArraySpace doesn't allow reordering - @eval function $OPrule(S1::ArraySpace,S2::ArraySpace) - sps = map($OP,S1.spaces,S2.spaces) - for s in sps - if isa(s,NoSpace) - return NoSpace() - end - end - ArraySpace(sps) - end -end - -## routines - -spacescompatible(AS::ArraySpace,BS::ArraySpace) = - size(AS) == size(BS) && all(spacescompatible.(AS.spaces,BS.spaces)) -canonicalspace(AS::ArraySpace) = ArraySpace(canonicalspace.(AS.spaces)) -evaluate(f::AbstractVector,S::ArraySpace,x) = map(g->g(x),Fun(S,f)) - - -## choosedomainspace - -function choosedomainspace(A::InterlaceOperator{T,1},sp::ArraySpace) where T - # this ensures correct dispatch for unino - sps = Vector{Space}( - filter(x->!isambiguous(x),map(choosedomainspace,A.ops,sp.spaces))) - if isempty(sps) - UnsetSpace() - else - union(sps...) - end -end - - -Base.reshape(f::Fun{AS},k...) where {AS<:ArraySpace} = Fun(reshape(space(f),k...),f.coefficients) - -Base.diff(f::Fun{AS,T},n...) where {AS<:ArraySpace,T} = Fun(diff(Array(f),n...)) - -## conversion - -function coefficients(f::AbstractVector, a::VectorSpace, b::VectorSpace) - if size(a) ≠ size(b) - throw(DimensionMismatch("dimensions must match")) - end - interlace(map(coefficients,Fun(a,f),b),b) -end - - -coefficients(Q::AbstractVector{F},rs::VectorSpace) where {F<:Fun} = - interlace(map(coefficients,Q,rs),rs) - -coefficients(Q::AbstractVector, rs::VectorSpace) = coefficients(Fun.(Q), rs) - - -Fun(f::AbstractVector{FF},d::VectorSpace) where {FF<:Fun} = Fun(d,coefficients(f,d)) -Fun(f::AbstractMatrix{FF},d::MatrixSpace) where {FF<:Fun} = Fun(d,coefficients(f,d)) - - - - - -## constructor - - - -# columns are coefficients -function Fun(M::AbstractMatrix{<:Number},sp::MatrixSpace) - if size(M) ≠ size(sp) - throw(DimensionMismatch()) - end - Fun(map((f,s)->Fun(f,s),M,sp.spaces)) -end - -Fun(M::UniformScaling,sp::MatrixSpace) = Fun(M.λ*Matrix(I,size(sp)...),sp) - - - -ones(::Type{T},A::ArraySpace) where {T<:Number} = Fun(ones.(T,spaces(A))) -ones(A::ArraySpace) = Fun(ones.(spaces(A))) - - -## EuclideanSpace - -const EuclideanSpace{RR} = VectorSpace{ConstantSpace{AnyDomain},AnyDomain,RR} -EuclideanSpace(n::Integer) = ArraySpace(ConstantSpace(Float64),n) - - - - -## support pieces - -npieces(f::Fun{<:ArraySpace}) = npieces(f[1]) -piece(f::Fun{<:ArraySpace}, k) = Fun(piece.(Array(f),k)) -pieces(f::Fun{<:ArraySpace}) = [piece(f,k) for k=1:npieces(f)] - - - -## TODO: This is a hack to get tests working - -fromcanonical(d::ProductDomain, f::Fun{<:ArraySpace}) = vcat(fromcanonical.(factors(d), vec(f))...) - -function coefficients(f::AbstractVector,sp::ArraySpace{<:ConstantSpace{AnyDomain}},ts::TensorSpace{SV,D,R}) where {SV,D<:EuclideanDomain{2},R} - @assert length(ts.spaces) == 2 - - if ts.spaces[1] isa ArraySpace - coefficients(f, sp, ts.spaces[1]) - elseif ts.spaces[2] isa ArraySpace - coefficients(f, sp, ts.spaces[2]) - else - error("Cannot convert coefficients from $sp to $ts") - end -end - - - -ArraySpace(sp::TensorSpace{Tuple{S1,S2}}) where {S1<:Space{D,R},S2} where {D,R<:AbstractArray} = - ArraySpace(map(a -> a ⊗ sp.spaces[2], sp.spaces[1])) - -ArraySpace(sp::TensorSpace{Tuple{S1,S2}},k...) where {S1,S2<:Space{D,R}} where {D,R<:AbstractArray} = - ArraySpace(map(a -> sp.spaces[1] ⊗ a, sp.spaces[2])) - -function coefficients(f::AbstractVector, a::VectorSpace, b::TensorSpace{Tuple{S1,S2},<:EuclideanDomain{2}}) where {S1<:Space{D,R},S2} where {D,R<:AbstractArray} - if size(a) ≠ size(b) - throw(DimensionMismatch("dimensions must match")) - end - interlace(map(coefficients,Fun(a,f),b),ArraySpace(b)) -end - -function coefficients(f::AbstractVector, a::VectorSpace, b::TensorSpace{Tuple{S1,S2},<:EuclideanDomain{2}}) where {S1,S2<:Space{D,R}} where {D,R<:AbstractArray} - if size(a) ≠ size(b) - throw(DimensionMismatch("dimensions must match")) - end - interlace(map(coefficients,Fun(a,f),b),ArraySpace(b)) -end diff --git a/src/Spaces/ConstantSpace.jl b/src/Spaces/ConstantSpace.jl deleted file mode 100644 index e5cba266..00000000 --- a/src/Spaces/ConstantSpace.jl +++ /dev/null @@ -1,248 +0,0 @@ -## Sequence space defintions - -# A Fun for SequenceSpace can be an iterator -iterate(::Fun{SequenceSpace}) = 1 -iterate(f::Fun{SequenceSpace}, st) = f[st], st+1 - -getindex(f::Fun{SequenceSpace}, k::Integer) = - k ≤ ncoefficients(f) ? f.coefficients[k] : zero(cfstype(f)) -getindex(f::Fun{SequenceSpace},K::CartesianIndex{0}) = f[1] -getindex(f::Fun{SequenceSpace},K) = cfstype(f)[f[k] for k in K] - -length(f::Fun{SequenceSpace}) = ∞ - - -dotu(f::Fun{SequenceSpace},g::Fun{SequenceSpace}) = - mindotu(f.coefficients,g.coefficients) -dotu(f::Fun{SequenceSpace},g::AbstractVector) = - mindotu(f.coefficients,g) -dotu(f::AbstractVector,g::Fun{SequenceSpace}) = - mindotu(f,g.coefficients) - -norm(f::Fun{SequenceSpace}) = norm(f.coefficients) -norm(f::Fun{SequenceSpace},k::Int) = norm(f.coefficients,k) -norm(f::Fun{SequenceSpace},k::Number) = norm(f.coefficients,k) - - -Fun(cfs::AbstractVector,S::SequenceSpace) = Fun(S,cfs) -coefficients(cfs::AbstractVector,::SequenceSpace) = cfs # all vectors are convertible to SequenceSpace - - - -## Constant space defintions - -# setup conversions for spaces that contain constants -macro containsconstants(SP) - esc(quote - ApproxFunBase.union_rule(A::(ApproxFunBase.ConstantSpace),B::$SP) = B - Base.promote_rule(A::Type{<:(ApproxFunBase.ConstantSpace)},B::Type{<:($SP)}) = B - - Base.promote_rule(::Type{ApproxFunBase.Fun{S,V,VV}},::Type{T}) where {T<:Number,S<:$SP,V,VV} = - ApproxFunBase.VFun{S,promote_type(V,T)} - Base.promote_rule(::Type{ApproxFunBase.Fun{S}},::Type{T}) where {T<:Number,S<:$SP} = ApproxFunBase.VFun{S,T} - Base.promote_rule(::Type{ApproxFunBase.Fun{S,V,VV}}, - ::Type{Fun{ApproxFunBase.ConstantSpace{ApproxFunBase.AnyDomain},T,VT}}) where {T,S<:$SP,V,VV,VT} = - ApproxFunBase.VFun{S,promote_type(V,T)} - end) -end - - - -Fun(c::Number) = Fun(ConstantSpace(typeof(c)),[c]) -Fun(c::Number,d::ConstantSpace) = Fun(d,[c]) - -dimension(::ConstantSpace) = 1 - -#TODO: Change -setdomain(f::Fun{CS},d::Domain) where {CS<:AnyDomain} = Number(f)*ones(d) - -canonicalspace(C::ConstantSpace) = C -spacescompatible(a::ConstantSpace,b::ConstantSpace)=domainscompatible(a,b) - -ones(S::ConstantSpace) = Fun(S,fill(1.0,1)) -ones(S::Union{AnyDomain,UnsetSpace}) = ones(ConstantSpace()) -zeros(S::AnyDomain) = zero(ConstantSpace()) -zero(S::UnsetSpace) = zero(ConstantSpace()) -evaluate(f::AbstractVector,::ConstantSpace,x...)=f[1] -evaluate(f::AbstractVector,::ZeroSpace,x...)=zero(eltype(f)) - - -convert(::Type{T}, f::Fun{CS}) where {CS<:ConstantSpace,T<:Number} = - convert(T, f.coefficients[1]) - -Number(f::Fun) = convert(Number, f) - - -# promoting numbers to Fun -# override promote_rule if the space type can represent constants -Base.promote_rule(::Type{Fun{CS}},::Type{T}) where {CS<:ConstantSpace,T<:Number} = Fun{CS,T} -Base.promote_rule(::Type{Fun{CS,V}},::Type{T}) where {CS<:ConstantSpace,T<:Number,V} = - Fun{CS,promote_type(T,V)} -Base.promote_rule(::Type{IF},::Type{T}) where {T<:Number,IF<:Fun} = Fun - - -# we know multiplication by constants preserves types -Base.promote_op(::typeof(*),::Type{Fun{CS,T,VT}},::Type{F}) where {CS<:ConstantSpace,T,VT,F<:Fun} = - promote_op(*,T,F) -Base.promote_op(::typeof(*),::Type{F},::Type{Fun{CS,T,VT}}) where {CS<:ConstantSpace,T,VT,F<:Fun} = - promote_op(*,F,T) - - - -Base.promote_op(::typeof(LinearAlgebra.matprod),::Type{Fun{S1,T1,VT1}},::Type{Fun{S2,T2,VT2}}) where {S1<:ConstantSpace,T1,VT1,S2<:ConstantSpace,T2,VT2} = - VFun{promote_type(S1,S2),promote_type(T1,T2)} -Base.promote_op(::typeof(LinearAlgebra.matprod),::Type{Fun{S1,T1,VT1}},::Type{Fun{S2,T2,VT2}}) where {S1<:ConstantSpace,T1,VT1,S2,T2,VT2} = - VFun{S2,promote_type(T1,T2)} -Base.promote_op(::typeof(LinearAlgebra.matprod),::Type{Fun{S1,T1,VT1}},::Type{Fun{S2,T2,VT2}}) where {S1,T1,VT1,S2<:ConstantSpace,T2,VT2} = - VFun{S1,promote_type(T1,T2)} - - -# When the union of A and B is a ConstantSpace, then it contains a one -conversion_rule(A::ConstantSpace,B::UnsetSpace)=NoSpace() -conversion_rule(A::ConstantSpace,B::Space)=(union_rule(A,B)==B||union_rule(B,A)==B) ? A : NoSpace() - -conversion_rule(A::ZeroSpace,B::Space) = A -maxspace_rule(A::ZeroSpace,B::Space) = B - -Conversion(A::ZeroSpace,B::ZeroSpace) = ConversionWrapper(ZeroOperator(A,B)) -Conversion(A::ZeroSpace,B::Space) = ConversionWrapper(ZeroOperator(A,B)) - -# TODO: this seems like it needs more thought -union_rule(A::ConstantSpace,B::Space) = ConstantSpace(domain(B))⊕B - - -## Special Multiplication and Conversion for constantspace - -# TODO: this is a special work around but really we want it to be blocks -Conversion(a::ConstantSpace,b::Space{D}) where {D<:EuclideanDomain{2}} = ConcreteConversion{typeof(a),typeof(b), - promote_type(real(prectype(a)),real(prectype(b)))}(a,b) - -Conversion(a::ConstantSpace,b::Space) = ConcreteConversion(a,b) -bandwidths(C::ConcreteConversion{CS,S}) where {CS<:ConstantSpace,S<:Space} = - ncoefficients(ones(rangespace(C)))-1,0 -function getindex(C::ConcreteConversion{CS,S,T},k::Integer,j::Integer) where {CS<:ConstantSpace,S<:Space,T} - if j != 1 - throw(BoundsError()) - end - on=ones(rangespace(C)) - k ≤ ncoefficients(on) ? convert(T,on.coefficients[k]) : zero(T) -end - - -coefficients(f::AbstractVector,sp::ConstantSpace{Segment{Vec{2,TT}}}, - ts::TensorSpace{SV,DD}) where {TT,SV,DD<:EuclideanDomain{2}} = - f[1]*ones(ts).coefficients -coefficients(f::AbstractVector,sp::ConstantSpace{<:Domain{<:Number}}, - ts::TensorSpace{SV,DD}) where {TT,SV,DD<:EuclideanDomain{2}} = - f[1]*ones(ts).coefficients -coefficients(f::AbstractVector, sp::ConstantSpace{<:Domain{<:Number}}, ts::Space) = - f[1]*ones(ts).coefficients -coefficients(f::AbstractVector, sp::ConstantSpace, ts::Space) = - f[1]*ones(ts).coefficients - - -######## -# Evaluation -######## - -######### -# Multiplication -######### - - -# this is identity operator, but we don't use MultiplicationWrapper to avoid -# ambiguity errors - -defaultMultiplication(f::Fun{CS},b::ConstantSpace) where {CS<:ConstantSpace} = - ConcreteMultiplication(f,b) -defaultMultiplication(f::Fun{CS},b::Space) where {CS<:ConstantSpace} = - ConcreteMultiplication(f,b) -defaultMultiplication(f::Fun,b::ConstantSpace) = ConcreteMultiplication(f,b) - -bandwidths(D::ConcreteMultiplication{CS1,CS2,T}) where {CS1<:ConstantSpace,CS2<:ConstantSpace,T} = - 0,0 -getindex(D::ConcreteMultiplication{CS1,CS2,T},k::Integer,j::Integer) where {CS1<:ConstantSpace,CS2<:ConstantSpace,T} = - k==j==1 ? convert(T,D.f.coefficients[1]) : one(T) - -rangespace(D::ConcreteMultiplication{CS1,CS2,T}) where {CS1<:ConstantSpace,CS2<:ConstantSpace,T} = - D.space - - -rangespace(D::ConcreteMultiplication{F,UnsetSpace,T}) where {F<:ConstantSpace,T} = - UnsetSpace() -bandwidths(D::ConcreteMultiplication{F,UnsetSpace,T}) where {F<:ConstantSpace,T} = - (∞,∞) -getindex(D::ConcreteMultiplication{F,UnsetSpace,T},k::Integer,j::Integer) where {F<:ConstantSpace,T} = - error("No range space attached to Multiplication") - - - -bandwidths(D::ConcreteMultiplication{CS,F,T}) where {CS<:ConstantSpace,F<:Space,T} = 0,0 -blockbandwidths(D::ConcreteMultiplication{CS,F,T}) where {CS<:ConstantSpace,F<:Space,T} = 0,0 -subblockbandwidths(D::ConcreteMultiplication{CS,F,T}) where {CS<:ConstantSpace,F<:Space,T} = 0,0 -subblockbandwidths(D::ConcreteMultiplication{CS,F,T}, k) where {CS<:ConstantSpace,F<:Space,T} = 0 -isbandedblockbanded(D::ConcreteMultiplication{CS,F,T}) where {CS<:ConstantSpace,F<:Space,T} = true -isblockbanded(D::ConcreteMultiplication{CS,F,T}) where {CS<:ConstantSpace,F<:Space,T} = true -getindex(D::ConcreteMultiplication{CS,F,T},k::Integer,j::Integer) where {CS<:ConstantSpace,F<:Space,T} = - k==j ? convert(T, D.f) : zero(T) -rangespace(D::ConcreteMultiplication{CS,F,T}) where {CS<:ConstantSpace,F<:Space,T} = D.space - - -bandwidths(D::ConcreteMultiplication{F,CS,T}) where {CS<:ConstantSpace,F<:Space,T} = ncoefficients(D.f)-1,0 -function getindex(D::ConcreteMultiplication{F,CS,T},k::Integer,j::Integer) where {CS<:ConstantSpace,F<:Space,T} - k≤ncoefficients(D.f) && j==1 ? convert(T,D.f.coefficients[k]) : zero(T) -end -rangespace(D::ConcreteMultiplication{F,CS,T}) where {CS<:ConstantSpace,F<:Space,T} = space(D.f) - - - -# functionals always map to Constant space -function promoterangespace(P::Operator,A::ConstantSpace,cur::ConstantSpace) - @assert isafunctional(P) - domain(A)==domain(cur) ? P : SpaceOperator(P,domainspace(P),A) -end - - -for op = (:*,:/) - @eval $op(f::Fun,c::Fun{CS}) where {CS<:ConstantSpace} = f*convert(Number,c) -end - - - -## Multivariate case -union_rule(a::TensorSpace,b::ConstantSpace{AnyDomain})=TensorSpace(map(sp->union(sp,b),a.spaces)) -## Special spaces - -function convert(::Type{T},f::Fun{TS}) where {TS<:TensorSpace,T<:Number} - if all(sp->isa(sp,ConstantSpace),space(f).spaces) - convert(T,f.coefficients[1]) - else - error("Cannot convert $f to type $T") - end -end - -convert(::Type{T}, - f::Fun{TensorSpace{Tuple{CS1,CS2},DD,RR}}) where {CS1<:ConstantSpace,CS2<:ConstantSpace,T<:Number,DD,RR} = - convert(T,f.coefficients[1]) - -isconstspace(sp::TensorSpace) = all(isconstspace,sp.spaces) - - -# Supports constants in operators -promoterangespace(M::ConcreteMultiplication{CS,UnsetSpace}, - ps::UnsetSpace) where {CS<:ConstantSpace} = M -promoterangespace(M::ConcreteMultiplication{CS,UnsetSpace}, - ps::Space) where {CS<:ConstantSpace} = - promoterangespace(Multiplication(M.f,space(M.f)),ps) - -# Possible hack: we try uing constant space for [1 Operator()] \ z. -choosedomainspace(M::ConcreteMultiplication{D,UnsetSpace},sp::UnsetSpace) where {D<:ConstantSpace} = space(M.f) -choosedomainspace(M::ConcreteMultiplication{D,UnsetSpace},sp::Space) where {D<:ConstantSpace} = space(M.f) - -Base.isfinite(f::Fun{CS}) where {CS<:ConstantSpace} = isfinite(Number(f)) - - - - - - diff --git a/src/Spaces/DiracSpace.jl b/src/Spaces/DiracSpace.jl deleted file mode 100644 index fa489890..00000000 --- a/src/Spaces/DiracSpace.jl +++ /dev/null @@ -1,216 +0,0 @@ -export DiracDelta, KroneckerDelta, DiracSpace, PointSpace - -for TYP in (:DiracSpace,:PointSpace) - @eval begin - struct $TYP{T,D,R} <: Space{D,R} - points::Vector{T} - $TYP{T,D,R}(pts::AbstractVector{T}) where {T,D,R} = new(sort(pts)) - end - - function $TYP(points::AbstractVector{T}) where T - $TYP{eltype(points),UnionDomain{Vector{Point{T}},Point{T}},real(T)}(points) - end - - $TYP(points::Tuple) = $TYP(collect(points)) - $TYP() = $TYP(Float64[]) - $TYP(point::Number) = $TYP([point]) - $TYP(p::Point)=$TYP(p.x) - - - dimension(d::$TYP)=length(d.points) - - # all points are equal, so only one block - blocklengths(C::$TYP) = [length(C.points)] - block(C::$TYP,k) = Block(1) - - domain(DS::$TYP) = UnionDomain(Point.(DS.points)) - setdomain(DS::$TYP,d::UnionDomain) = $TYP(map(d->d.x,components(d))) - points(sp::$TYP,n::Integer)=sp.points[1:n] - - spacescompatible(a::$TYP,b::$TYP) = a.points == b.points - canonicalspace(a::$TYP) = a - - union_rule(a::$TYP,b::$TYP) = $TYP(sort(union(a.points,b.points))) - - function coefficients(cfs::AbstractVector,fromspace::$TYP,tospace::$TYP) - if spacescompatible(fromspace,tospace) - return cfs - end - - @assert length(cfs) ≤ length(fromspace.points) - - # this first for-loop removes coefficients of Dirac points that are zero - nonzerofromspacepoints = eltype(fromspace.points)[] - nonzeroDiraccfs = eltype(cfs)[] - for i = 1:length(cfs) - if cfs[i] != 0 - push!(nonzerofromspacepoints, fromspace.points[i]) - push!(nonzeroDiraccfs, cfs[i]) - end - end - - - # if the points that remain can be represented in the tospace - if issubset(nonzerofromspacepoints,tospace.points) - finalDiraccfs = eltype(cfs)[] - j=1 #counter for the nonzerofromspacepoints - for i = 1:length(tospace.points) - if j > length(nonzerofromspacepoints) - break - elseif nonzerofromspacepoints[j]==tospace.points[i] - push!(finalDiraccfs,nonzeroDiraccfs[j]) - j += 1 - else - push!(finalDiraccfs,0) - end - end - finalDiraccfs - else - error("The domain of the space being converted from has points that cannot be represented in the space you are converting to.") - end - end - end -end - -Space(d::Point) = PointSpace(d) - -Fun(::typeof(identity), S::PointSpace) = Fun(S,S.points) -Fun(::typeof(identity), S::DiracSpace) = Fun(PointSpace(S.points),S.points) -transform(S::PointSpace,v::AbstractVector,plan...) = v -values(f::Fun{S}) where S<:PointSpace = coefficient(f,:) - -function evaluate(f::AbstractVector,PS::PointSpace,x::Number) - p = findfirst(y->isapprox(x,y),PS.points) - if p == 0 || p > length(f) - zero(eltype(f)) - else - f[p] - end -end - -function evaluate(f::AbstractVector, PS::DiracSpace, x::Number) - x ∉ domain(PS) && return zero(eltype(f)) - - p = findfirst(y->isapprox(x,y), PS.points) - if p == 0 || p > length(f) - zero(eltype(f)) - else - f[p]*Inf - end -end - -Base.sum(f::Fun{DS}) where DS<:DiracSpace = - sum(f.coefficients[1:dimension(space(f))]) - -DiracDelta(x::Number)=Fun(DiracSpace(x),[1.]) -DiracDelta()=DiracDelta(0.) - -KroneckerDelta(x::Number) = Fun(PointSpace(x),[1.]) -KroneckerDelta() = KroneckerDelta(0.) - -function Base.cumsum(f::Fun{S},d::IntervalOrSegment{T}) where {S<:DiracSpace,T<:Real} - pts=space(f).points - @assert pts ==sort(pts) - cfs=cumsum(f.coefficients) - if leftendpoint(d) < first(pts) && rightendpoint(d) > last(pts) - Fun(HeavisideSpace([leftendpoint(d);pts;rightendpoint(d)]),[0;cfs]) - elseif leftendpoint(d) == first(pts) && rightendpoint(d) > last(pts) - Fun(HeavisideSpace([pts;rightendpoint(d)]),cfs) - elseif leftendpoint(d) < first(pts) && rightendpoint(d) == last(pts) - Fun(HeavisideSpace([leftendpoint(d);pts]),[0;cfs]) - elseif leftendpoint(d) == first(pts) && rightendpoint(d) == last(pts) - Fun(HeavisideSpace(pts),cfs[1:end-1]) - else - error("Implement") - end -end - -# for TYP in (:ReSpace,:Space) -# @eval begin -# function coefficients(cfs::AbstractVector,fromspace::$TYP,tospace::DiracSpace) -# [0*tospace.points;coefficients(cfs,fromspace,tospace.space)] -# end -# end -# end - -# function coefficients(cfs::AbstractVector,fromspace::DiracSpace,tospace::Space) -# n = length(fromspace.points) -# if n == 0 || cfs[1:n] == 0*cfs[1:n] -# coefficients(fromspace.space,tospace) -# else -# error("The space you are converting from has Dirac deltas that cannot be represented in the space you are converting to.") -# end -# end - - -function Multiplication(f::Fun{PS}, PS2::PointSpace) where PS<:PointSpace - @assert space(f).points==PS2.points - FiniteOperator(Diagonal(values(f)),PS2,PS2) -end - -function Multiplication(f::Fun{PS}, DS::DiracSpace) where PS<:PointSpace - @assert space(f).points==DS.points - FiniteOperator(Diagonal(values(f)),DS,DS) -end - -function Multiplication(f::Fun{DS}, PS::PointSpace) where DS<:DiracSpace - @assert space(f).points==PS.points - FiniteOperator(Diagonal(coefficient(f,:)),PS,space(f)) -end - -function coefficienttimes(f::Fun{PS}, g::Fun{DS}) where {PS<:PointSpace,DS<:DiracSpace} - @assert space(f).points==space(g).points - Fun(space(g),f.coefficients.*g.coefficients) -end - -function coefficienttimes(f::Fun{DS}, g::Fun{PS}) where {PS<:PointSpace,DS<:DiracSpace} - @assert space(f).points==space(g).points - Fun(space(f),f.coefficients.*g.coefficients) -end - -function coefficienttimes(f::Fun{PS}, g::Fun{PS2}) where {PS<:PointSpace,PS2<:PointSpace} - @assert space(f).points==space(g).points - Fun(space(g),f.coefficients.*g.coefficients) -end - -/(f::Fun,g::Fun{PS}) where PS<:PointSpace = f*inv(g) - -inv(f::Fun{PS}) where PS<:PointSpace = Fun(space(f),1 ./ f.coefficients) - - -#DiracSpace sampling -function randweights(pts, cfs) - cs = cumsum(cfs) - r = rand() - if r≤cs[1] - return pts[1] - else - for n=1:length(cfs)-1 - if cs[n] c < 0, cfs) && throw(ArgumentError("All weights must be non-negative")) - randweights(f.space.points, cfs) -end - -function sample(f::Fun{<:DiracSpace,<:Real},n::Integer) - p=[] - for i=1:n - p=[p;sample(f)] - end - return p -end -#integrate DiracSpace -function integrate(f::Fun{<:DiracSpace}) - pts=f.space.points - cfs=f.coefficients - int=Fun(HeavisideSpace([pts;Inf]),cumsum(cfs)) - return int -end diff --git a/src/Spaces/HeavisideSpace.jl b/src/Spaces/HeavisideSpace.jl deleted file mode 100644 index 19770017..00000000 --- a/src/Spaces/HeavisideSpace.jl +++ /dev/null @@ -1,114 +0,0 @@ -# SplineSpace represents a Spline, right now piecewise constant HeavisideSpace is only implemented case -struct SplineSpace{order,T,R} <: Space{PiecewiseSegment{T},R} - domain::PiecewiseSegment{T} -end - -SplineSpace{m,T}(d::PiecewiseSegment{T}) where {m,T} = SplineSpace{m,T,real(eltype(T))}(d) -SplineSpace{m,T}(d::AbstractVector) where {m,T} = SplineSpace{m}(PiecewiseSegment(sort(d))) - -SplineSpace{m}(d::PiecewiseSegment{T}) where {m,T} = SplineSpace{m,T,real(eltype(T))}(d) -SplineSpace{m}(d::AbstractVector) where {m} = SplineSpace{m}(PiecewiseSegment(sort(d))) - -const HeavisideSpace{T,R} = SplineSpace{0,T,R} -dimension(h::SplineSpace{λ}) where {λ} = length(h.domain.points)+λ-1 - -convert(::Type{HeavisideSpace},d::PiecewiseSegment) = HeavisideSpace{eltype(d)}(d) - -convert(::Type{HeavisideSpace},d::AbstractVector) = - HeavisideSpace(PiecewiseSegment(sort(d))) - -spacescompatible(a::SplineSpace{λ},b::SplineSpace{λ}) where {λ} = domainscompatible(a,b) - - -function evaluate(c::AbstractVector{T}, s::HeavisideSpace{<:Real}, x::Real) where T - p = domain(s).points - for k=1:length(c) - if p[k] ≤ x ≤ p[k+1] - return c[k] - end - end - return zero(T) -end - - -function evaluate(c::AbstractVector{T}, s::SplineSpace{1,<:Real}, x::Real) where T - p = domain(f).points - c = f.coefficients - for k=1:length(p)-1 - if p[k] ≤ x ≤ p[k+1] - return (x-p[k])*c[k+1]/(p[k+1]-p[k]) + (p[k+1]-x)*c[k]/(p[k+1]-p[k]) - end - end - return zero(T) -end - - -function points(sp::HeavisideSpace,n) - x=sp.domain.points - (x[1:end-1] + diff(x)/2)[1:n] -end - -points(sp::SplineSpace{1},n) = sp.domain.points[1:n] - -for λ = [0,1] - @eval begin - function transform(S::SplineSpace{$λ},vals::AbstractVector,plan...) - @assert length(vals) ≤ dimension(S) - vals - end - itransform(S::SplineSpace{$λ},cfs::AbstractVector,plan...) = pad(cfs,dimension(S)) - end -end - - -bandwidths(D::ConcreteDerivative{H}) where {H<:HeavisideSpace} = (0,1) -rangespace(D::ConcreteDerivative{H}) where {H<:HeavisideSpace} = DiracSpace(domain(D).points[2:end-1]) - -function getindex(D::ConcreteDerivative{H,<:Any,T},k::Integer,j::Integer) where {H<:HeavisideSpace,T} - if k==j - -one(T) - elseif j==k+1 - one(T) - else - zero(T) - end -end - -Base.sum(f::Fun{HS}) where {HS<:HeavisideSpace} = dotu(f.coefficients,diff(space(f).domain.points)) -function Base.sum(f::Fun{SplineSpace{1,T,R}}) where {T,R} - vals=pad(f.coefficients,dimension(space(f))) - dfs=diff(space(f).domain.points) - ret=vals[1]*dfs[1]/2 - for k=2:length(vals)-1 - ret+=vals[k]*(dfs[k]+dfs[k+1])/2 - end - ret+=vals[end]*dfs[end]/2 - ret -end - -#diffentiate HeavisideSpace -function differentiate(f::Fun{<:HeavisideSpace}) - dp=domain(f).points - cfs=f.coefficients - diff=0.0 - for n=1:length(cfs) - diff=diff+cfs[n]*(DiracDelta(dp[n])-DiracDelta(dp[n+1])) - end - return diff -end - -#Derivative Operator for HeavisideSpace -function Derivative(H::HeavisideSpace, k::Int) - @assert k == 1 - ConcreteDerivative(H) -end - - - -differentiate(f::Fun{SplineSpace{1,T,R}}) where {T,R} = - Fun(HeavisideSpace(space(f).domain), - diff(pad(f.coefficients,dimension(space(f))))./diff(space(f).domain.points)) - -integrate(f::Fun{HeavisideSpace{T,R}}) where {T,R} = - Fun(SplineSpace{1,T,R}(space(f).domain), - [0;cumsum(f.coefficients).*diff(space(f).domain.points)]) diff --git a/src/Spaces/ProductSpaceOperators.jl b/src/Spaces/ProductSpaceOperators.jl deleted file mode 100644 index ccbe87c2..00000000 --- a/src/Spaces/ProductSpaceOperators.jl +++ /dev/null @@ -1,325 +0,0 @@ -export continuity - -## Space promotion for InterlaceOperator -# It's here because we need DirectSumSpace - -for TYP in (:PiecewiseSpace,:ArraySpace) - @eval begin - function promotedomainspace(A::InterlaceOperator{T,2},sp::$TYP)::Any where T - if domainspace(A) == sp - return A - end - @assert size(A.ops,2) == length(sp) - InterlaceOperator([promotedomainspace(A.ops[k,j],sp[j]) for k=1:size(A.ops,1),j=1:size(A.ops,2)],$TYP) - end - function interlace_choosedomainspace(ops,rs::$TYP)::Any - @assert length(ops) == length(rs) - # this ensures correct dispatch for unino - sps = Array{Space}( - filter(x->!isambiguous(x),map((op,s)->choosedomainspace(op,s),ops,rs))) - if isempty(sps) - UnsetSpace() - else - union(sps...) - end - end - end -end - - - - -function continuity(sp::PiecewiseSpace,order::Integer) - m=ncomponents(sp) - B=zeros(Operator{prectype(sp)},m-1,m) - - for k=1:m-1 - B[k,k] = Evaluation(component(sp,k),rightendpoint,order) - B[k,k+1] = -Evaluation(component(sp,k+1),leftendpoint,order) - end - - InterlaceOperator(B,PiecewiseSpace,ArraySpace) -end - -function continuity(sp::PiecewiseSpace,kr::UnitRange) - @assert first(kr)==0 - m=ncomponents(sp) - B=zeros(Operator{prectype(sp)},length(kr)*(m-1),m) - for r in kr - B[(m-1)*r+1:(m-1)*(r+1),:] = continuity(sp,r).ops - end - InterlaceOperator(B,PiecewiseSpace,ArraySpace) -end - - -continuity(d::UnionDomain,k) = continuity(Space(d),k) -continuity(d) = continuity(d,0) - -blockdiag(A::PlusOperator) = mapreduce(blockdiag, +, A.ops) -blockdiag(A::TimesOperator) = mapreduce(blockdiag, .*, A.ops) - -# TODO: general wrappers - -Evaluation(S::SumSpace,x,order) = - EvaluationWrapper(S,x,order, - InterlaceOperator(RowVector(vnocat(map(s->Evaluation(s,x,order),components(S))...)),SumSpace)) - - -ToeplitzOperator(G::Fun{MatrixSpace{S,DD,RR},V}) where {S,RR,V,DD} = interlace(map(ToeplitzOperator,Array(G))) - -## Sum Space - - - - -## Conversion - -function coefficients(v::AbstractVector,a::ArraySpace,b::ArraySpace) - if a==b - v - else - interlace(map((f,s)->Fun(f,s),Fun(a,v),b),b) - end -end - - -# ArraySpace is straight forward - -function Conversion(a::ArraySpace, b::ArraySpace) - @assert size(a) == size(b) - ConversionWrapper(InterlaceOperator(Diagonal(Conversion.(vec(a.spaces), vec(b.spaces))), a, b)) -end - - -# Sum Space and PiecewiseSpace need to allow permutation of space orders -for TYP in (:SumSpace,:PiecewiseSpace) - @eval function Conversion(S1::$TYP,S2::$TYP)::Any - v1 = collect(S1.spaces) - v2 = collect(S2.spaces) - - sort1 = sort(collect(v1)) - sort2 = sort(collect(v2)) - - T = promote_type(eltype(domain(S1)),eltype(domain(S2))) - - if any(s->!isinf(dimension(s)),v1) || any(s->!isinf(dimension(s)),v2) - @assert length(S1.spaces) == length(S2.spaces) == 2 - if hasconversion(S1, S2.spaces[1]) - ops = Operator{T}[Conversion(S1.spaces[1], S2.spaces[1]) Conversion(S1.spaces[2], S2.spaces[1]); - ZeroOperator(T, S1.spaces[1], S2.spaces[2]) ZeroOperator(T, S1.spaces[2], S2.spaces[2])] - elseif hasconversion(S1, S2.spaces[2]) - ops = Operator{T}[ZeroOperator(T, S1.spaces[1], S2.spaces[1]) ZeroOperator(T, S1.spaces[2], S2.spaces[1]); - Conversion(S1.spaces[1], S2.spaces[2]) Conversion(S1.spaces[2], S2.spaces[2])] - else - error("Not implemented") - end - ConversionWrapper(InterlaceOperator(ops, S1, S2)) - elseif sort1 == sort2 - # swaps sumspace order - ConversionWrapper(PermutationOperator{T}(perm(v1,v2),S1,S2)) - elseif all(map(hasconversion,v1,v2)) - # we can blocmk convert - ConversionWrapper(SpaceOperator( - InterlaceOperator(Diagonal([map(Conversion,v1,v2)...]),$TYP), - S1,S2)) - elseif all(map(hasconversion,sort1,sort2)) - # we can blocmk convert - P1 = PermutationOperator{T}(perm(v1,sort1),S1,$TYP(sort1)) - P2 = PermutationOperator{T}(perm(sort2,v2),$TYP(sort2),S2) - ConversionWrapper(TimesOperator( - [P2,InterlaceOperator(Diagonal([map(Conversion,sort1,sort2)...]),$TYP),P1])) - elseif map(canonicalspace,S1.spaces) == map(canonicalspace,S2.spaces) - error("Not implemented") - else - # try sorting canonicalspace - csort1 = sort(collect(map(canonicalspace,v1))) - csort2 = sort(collect(map(canonicalspace,v2))) - if csort1 == csort2 - # we can block convert after permuting - prm = perm(map(canonicalspace,v1),map(canonicalspace,v2)) - ds2 = $TYP(S1.spaces[perm]) - P = PermutationOperator{T}(prm, S1, ds2) - ConversionWrapper(TimesOperator(Conversion(ds2,S2),P)) - elseif all(map(hasconversion,csort1,csort2)) - C1 = Conversion(S1,$TYP(csort1)) - C2 = Conversion($TYP(csort1),$TYP(csort2)) - C3 = Conversion($TYP(csort2),S2) - - ConversionWrapper(TimesOperator([C3,C2,C1])) - else - # we don't know how to convert so go to default - defaultConversion(S1,S2) - end - end - end -end - -function hasconversion(a::SumSpace, b::Space) - for n=1:length(a.spaces) - if hasconversion(a.spaces[n],b) ≠ true - return false - end - end - return true -end - -function Conversion(a::SumSpace, b::Space) - if !hasconversion(a, b) - throw(ArgumentError("Cannot convert $a to $b")) - end - - m=zeros(Operator{promote_type(prectype(a), prectype(b))},1,length(a.spaces)) - for n=1:length(a.spaces) - m[1,n]=Conversion(a.spaces[n],b) - end - return ConversionWrapper(InterlaceOperator(m, a, b, cache(interlacer(a)), cache(BlockInterlacer((Fill(1,∞),))), (1-dimension(b),dimension(a)-1))) -end - - - -for (OPrule,OP) in ((:conversion_rule,:conversion_type),(:maxspace_rule,:maxspace), - (:union_rule,:union)) - for TYP in (:SumSpace,:PiecewiseSpace) - @eval function $OPrule(S1sp::$TYP,S2sp::$TYP)::Any - S1 = components(S1sp) - S2 = components(S2sp) - cs1,cs2=map(canonicalspace,S1),map(canonicalspace,S2) - if length(S1) != length(S2) - NoSpace() - elseif canonicalspace(S1sp) == canonicalspace(S2sp) # this sorts S1 and S2 - S1sp ≤ S2sp ? S1sp : S2sp # choose smallest space by sorting - elseif cs1 == cs2 - # we can just map down - # $TYP(map($OP,S1.spaces,S2.spaces)) - # this is commented out due to Issue #13261 - newspaces = [$OP(S1[k],S2[k]) for k=1:length(S1)] - if any(b->b==NoSpace(),newspaces) - NoSpace() - else - $TYP(newspaces) - end - elseif sort(collect(cs1)) == sort(collect(cs2)) - # sort S1 - p=perm(cs1,cs2) - $OP($TYP(S1[p]),S2sp) - elseif length(S1) == length(S2) == 2 && - $OP(S1[1],S2[1]) != NoSpace() && - $OP(S1[2],S2[2]) != NoSpace() - #TODO: general length - $TYP($OP(S1[1],S2[1]), - $OP(S1[2],S2[2])) - elseif length(S1) == length(S2) == 2 && - $OP(S1[1],S2[2])!=NoSpace() && - $OP(S1[2],S2[1])!=NoSpace() - #TODO: general length - $TYP($OP(S1[1],S2[2]), - $OP(S1[2],S2[1])) - else - NoSpace() - end - end - end -end - - - - -## Derivative - -#TODO: do in @calculus_operator? - -for (Op,OpWrap) in ((:Derivative,:DerivativeWrapper),(:Integral,:IntegralWrapper)) - @eval begin - $Op(S::PiecewiseSpace,k::Integer) = - $OpWrap(InterlaceOperator(Diagonal([map(s->$Op(s,k),components(S))...]),PiecewiseSpace),k) - function $Op(S::ArraySpace,k::Integer) - ops = map(s->$Op(s,k),S) - $OpWrap(InterlaceOperator(Diagonal(ops),S,ArraySpace(reshape(rangespace.(ops),size(S)))),k) - end - end -end - -function Derivative(S::SumSpace,k::Integer)::Any - # we want to map before we decompose, as the map may introduce - # mixed bases. - if typeof(canonicaldomain(S))==typeof(domain(S)) - DerivativeWrapper(InterlaceOperator(Diagonal([map(s->Derivative(s,k),components(S))...]),SumSpace),k) - else - DefaultDerivative(S,k) - end -end - -choosedomainspace(M::CalculusOperator{UnsetSpace},sp::SumSpace)=mapreduce(s->choosedomainspace(M,s),union,sp.spaces) - -## Multiplcation for Array*Vector - -function Multiplication(f::Fun{MatrixSpace{S,DD,RR}},sp::VectorSpace{S2,DD2,RR2}) where {S,DD,RR,S2,DD2,RR2} - @assert size(space(f),2)==length(sp) - m=Array(f) - MultiplicationWrapper(f,interlace(Operator{promote_type(cfstype(f),prectype(sp))}[Multiplication(m[k,j],sp[j]) for k=1:size(m,1),j=1:size(m,2)])) -end - - - - -## Multiply components - -function Multiplication(f::Fun{PW},sp::PiecewiseSpace) where PW<:PiecewiseSpace - p=perm(domain(f).domains,domain(sp).domains) # sort f - vf=components(f)[p] - - MultiplicationWrapper(f,InterlaceOperator(Diagonal([map(Multiplication,vf,sp.spaces)...]),PiecewiseSpace)) -end - -Multiplication(f::Fun{SumSpace{SV1,D,R1}},sp::SumSpace{SV2,D,R2}) where {SV1,SV2,D,R1,R2} = - MultiplicationWrapper(f,mapreduce(g->Multiplication(g,sp),+,components(f))) -Multiplication(f::Fun,sp::SumSpace) = - MultiplicationWrapper(f,InterlaceOperator(Diagonal([map(s->Multiplication(f,s),components(sp))...]),SumSpace)) - -Multiplication(f::Fun, sp::PiecewiseSpace) = MultiplicationWrapper(f, Multiplication(Fun(f,sp),sp)) - - -# we override coefficienttimes to split the multiplication down to components as union may combine spaes - -coefficienttimes(f::Fun{S1},g::Fun{S2}) where {S1<:SumSpace,S2<:SumSpace} = mapreduce(ff->ff*g,+,components(f)) -coefficienttimes(f::Fun{S1},g::Fun) where {S1<:SumSpace} = mapreduce(ff->ff*g,+,components(f)) -coefficienttimes(f::Fun,g::Fun{S2}) where {S2<:SumSpace} = mapreduce(gg->f*gg,+,components(g)) - - -coefficienttimes(f::Fun{S1},g::Fun{S2}) where {S1<:PiecewiseSpace,S2<:PiecewiseSpace} = - Fun(map(coefficienttimes,components(f),components(g)),PiecewiseSpace) - - -## Definite Integral - -# This makes sure that the defaults from a given Domain are respected for the UnionDomain. - -DefiniteIntegral(d::UnionDomain) = - DefiniteIntegral(PiecewiseSpace(map(domainspace,map(DefiniteIntegral,d.domains)))) -DefiniteLineIntegral(d::UnionDomain) = - DefiniteLineIntegral(PiecewiseSpace(map(domainspace,map(DefiniteLineIntegral,d.domains)))) - - -DefiniteIntegral(sp::PiecewiseSpace) = - DefiniteIntegralWrapper(InterlaceOperator(hnocat(map(DefiniteIntegral,sp.spaces)...),sp,ConstantSpace(rangetype(sp)))) -DefiniteLineIntegral(sp::PiecewiseSpace) = - DefiniteLineIntegralWrapper(InterlaceOperator(hnocat(map(DefiniteLineIntegral,sp.spaces)...),sp,ConstantSpace(rangetype(sp)))) - -## TensorSpace of two PiecewiseSpaces - -Base.getindex(d::TensorSpace{Tuple{PWS1,PWS2}},i::Integer,j::Integer) where {PWS1<:PiecewiseSpace,PWS2<:PiecewiseSpace} = - d[1][i]⊗d[2][j] -Base.getindex(d::TensorSpace{Tuple{PWS1,PWS2}},i::AbstractRange,j::AbstractRange) where {PWS1<:PiecewiseSpace,PWS2<:PiecewiseSpace} = - PiecewiseSpace(d[1][i])⊗PiecewiseSpace(d[2][j]) - -## ProductFun - -## Piecewise - -function components(U::ProductFun{PS}) where PS<:PiecewiseSpace - ps=space(U,1) - sp2=space(U,2) - m=length(ps) - C=coefficients(U) - [ProductFun(C[k:m:end,:],component(ps,k),sp2) for k=1:m] -end diff --git a/src/Spaces/QuotientSpace.jl b/src/Spaces/QuotientSpace.jl deleted file mode 100644 index 09da49cb..00000000 --- a/src/Spaces/QuotientSpace.jl +++ /dev/null @@ -1,299 +0,0 @@ -import LinearAlgebra: LU, checknonsingular - -export QuotientSpace - -struct QuotientSpace{S,O,D,R} <: Space{D,R} - space::S - bcs::O - F::LU{R,Matrix{R}} - x::Vector{R} - function QuotientSpace{S,O,D,R}(space::S, bcs::O) where {S,O,D,R} - n = size(bcs, 1) - @assert isfinite(n) - new{S,O,D,R}(space, bcs, lu!(Matrix{R}(I, n, n)), zeros(R, n)) - end -end - -QuotientSpace(sp::Space{D,R}, bcs::Operator{T}) where {D,R,T} = QuotientSpace{typeof(sp), typeof(bcs), D, promote_type(R, T)}(sp, bcs) -QuotientSpace(bcs::Operator) = QuotientSpace(domainspace(bcs), bcs) - -domain(QS::QuotientSpace) = domain(QS.space) -canonicalspace(QS::QuotientSpace) = QS.space - -spacescompatible(a::QuotientSpace,b::QuotientSpace) = spacescompatible(a.space,b.space) -hasconversion(a::Space,b::QuotientSpace) = hasconversion(a,b.space) -hasconversion(a::QuotientSpace,b::Space) = hasconversion(a.space,b) -hasconversion(a::QuotientSpace,b::QuotientSpace) = hasconversion(a.space,b) - - -Conversion(Q::QuotientSpace{S}, sp::S) where S<:Space = ConcreteConversion(Q, sp) -@inline bandwidths(C::ConcreteConversion{QuotientSpace{S,O,D,R},S}) where {S,O,D,R} = (size(C.domainspace.F, 1), 0) - -function getindex(C::ConcreteConversion{QuotientSpace{S,O,D,R},S}, i::Integer, j::Integer) where {S,O,D,R} - sp = domainspace(C) - B = sp.bcs - F = sp.F - A = F.factors - n = size(A, 1) - x = sp.x - if i == j - return one(R) - elseif j < i ≤ j+n - @inbounds for jj = 1:n, ii = 1:n - A[ii,jj] = B[ii,j+jj] - end - @inbounds for ii = 1:n - x[ii] = -B[ii,j] - end - if norm(x) > 8*norm(A)*eps(R) - mutable_lu!(F) - ldiv!(F, x) - end - return @inbounds x[i-j] - else - return zero(R) - end -end - -# This function is modified from Julia's lu.jl in stdlib to work on a single `LU`. -# License is MIT: https://julialang.org/license -function mutable_lu!(F::LU{T}, ::Val{Pivot} = Val(true); - check::Bool = true) where {T,Pivot} - A = F.factors - m, n = size(A) - minmn = min(m,n) - info = 0 - ipiv = F.ipiv - @inbounds begin - for k = 1:minmn - # find index max - kp = k - if Pivot - amax = abs(zero(T)) - for i = k:m - absi = abs(A[i,k]) - if absi > amax - kp = i - amax = absi - end - end - end - ipiv[k] = kp - if !iszero(A[kp,k]) - if k != kp - # Interchange - for i = 1:n - tmp = A[k,i] - A[k,i] = A[kp,i] - A[kp,i] = tmp - end - end - # Scale first column - Akkinv = inv(A[k,k]) - for i = k+1:m - A[i,k] *= Akkinv - end - elseif info == 0 - info = k - end - # Update the rest - for j = k+1:n - for i = k+1:m - A[i,j] -= A[i,k]*A[k,j] - end - end - end - end - check && checknonsingular(info) - return F -end - - - -import LinearAlgebra.BLAS.@blasfunc -import LinearAlgebra: chkstride1, BlasInt -import LinearAlgebra.LAPACK.chklapackerror - -export PathologicalQuotientSpace - -struct PathologicalQuotientSpace{S,O<:Operator,DD,T,RT} <: Space{DD,T} - space::S - bcs::O - A::Matrix{T} - x::Vector{T} - b::Vector{T} - c::Vector{T} - U::Matrix{T} - Σ::Diagonal{T,Vector{T}} - VT::Matrix{T} - work::Vector{T} - iwork::Vector{BlasInt} - rwork::Vector{RT} - info::Ref{BlasInt} - function PathologicalQuotientSpace{S,O,DD,T,RT}(space::S, bcs::O) where {S,O,DD,T,RT} - n = size(bcs, 1) - @assert isfinite(n) - A = zeros(T, n, 2n) - x = zeros(T, 2n) - b = zeros(T, n) - c = zeros(T, n) - U = zeros(T, n, n) - Σ = Diagonal(zeros(T, n)) - VT = zeros(T, n, 2n) - work = Vector{T}(undef,max((6n+7)*2n,68)) - iwork = Vector{BlasInt}(undef,16n) - rwork = Vector{RT}(undef,(10n+7)*2n) - info = Ref{BlasInt}() - new{S,O,DD,T,RT}(space, bcs, A, x, b, c, U, Σ, VT, work, iwork, rwork, info) - end -end - -PathologicalQuotientSpace(sp::Space, bcs::Operator) = PathologicalQuotientSpace{typeof(sp), typeof(bcs), domaintype(sp), rangetype(sp), real(rangetype(sp))}(sp, bcs) - -PathologicalQuotientSpace(bcs::Operator) = PathologicalQuotientSpace(domainspace(bcs), bcs) - -domain(QS::PathologicalQuotientSpace) = domain(QS.space) -dimension(QS::PathologicalQuotientSpace) = dimension(QS.space) - -Conversion(Q::PathologicalQuotientSpace{SP}, S::SP) where {SP<:Space} = ConcreteConversion(Q, S) - -bandwidths(C::ConcreteConversion{PathologicalQuotientSpace{SP,O,DD,T,RT},SP}) where {SP,O,DD,T,RT} = (2size(C.domainspace.A, 1), 0) - -function getindex(C::ConcreteConversion{PathologicalQuotientSpace{SP,O,DD,T,RT},SP}, i::Integer, j::Integer) where {SP,O,DD,T,RT} - sp = domainspace(C) - n = size(sp.A, 1) - A = sp.A - x = sp.x - b = sp.b - c = sp.c - B = sp.bcs - U = sp.U - Σ = sp.Σ - VT = sp.VT - work = sp.work - iwork = sp.iwork - rwork = sp.rwork - info = sp.info - - if i == j - one(T) - elseif j < i ≤ j+2n - for jj = 1:2n, ii = 1:n - A[ii,jj] = B[ii,j+jj] - end - for ii = 1:n - b[ii] = -B[ii,j] - end - in_place_gesdd!(A, U, Σ.diag, VT, work, iwork, rwork, info) - mul!(c, transpose(U), b) - mul!(b, pinv!(Σ), c) - mul!(x, transpose(VT), b) - x[i-j] - else - zero(T) - end -end - -function pinv!(D::Diagonal{T}) where T - d = D.diag - @inbounds @simd for i = 1:length(d) - isfinite(inv(d[i])) ? d[i]=inv(d[i]) : d[i]=zero(T) - end - D -end - -for (gesdd, elty, relty) in ((:dgesdd_,:Float64,:Float64), - (:sgesdd_,:Float32,:Float32), - (:zgesdd_,:ComplexF64,:Float64), - (:cgesdd_,:ComplexF32,:Float32)) - @eval begin - # SUBROUTINE DGESDD( JOBZ, M, N, A, LDA, S, U, LDU, VT, LDVT, WORK, - # LWORK, IWORK, INFO ) - #* .. Scalar Arguments .. - # CHARACTER JOBZ - # INTEGER INFO, LDA, LDU, LDVT, LWORK, M, N - #* .. - #* .. Array Arguments .. - # INTEGER IWORK( * ) - # DOUBLE PRECISION A( LDA, * ), S( * ), U( LDU, * ), - # VT( LDVT, * ), WORK( * ) - function in_place_gesdd!(A::Matrix{$elty}, U::Matrix{$elty}, S::Vector{$elty}, VT::Matrix{$elty}, work::Vector{$elty}, iwork::Vector{BlasInt}, rwork::Vector{$relty}, info::Ref{BlasInt}) - chkstride1(A) - m, n = size(A) - minmn = min(m, n) - job = 'S' - lwork = BlasInt(-1) - cmplx = $elty != $relty - for i = 1:2 - if cmplx - ccall((@blasfunc($gesdd), liblapack), Nothing, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$relty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$relty}, Ptr{BlasInt}, Ptr{BlasInt}), - job, m, n, A, - max(1,stride(A,2)), S, U, max(1,stride(U,2)), - VT, max(1,stride(VT,2)), work, lwork, - rwork, iwork, info) - else - ccall((@blasfunc($gesdd), liblapack), Nothing, - (Ref{UInt8}, Ref{BlasInt}, Ref{BlasInt}, Ptr{$elty}, - Ref{BlasInt}, Ptr{$elty}, Ptr{$elty}, Ref{BlasInt}, - Ptr{$elty}, Ref{BlasInt}, Ptr{$elty}, Ref{BlasInt}, - Ptr{BlasInt}, Ptr{BlasInt}), - job, m, n, A, - max(1,stride(A,2)), S, U, max(1,stride(U,2)), - VT, max(1,stride(VT,2)), work, lwork, - iwork, info) - end - chklapackerror(info[]) - if i == 1 - # Work around issue with truncated Float32 representation of lwork in - # sgesdd by using nextfloat. See - # http://icl.cs.utk.edu/lapack-forum/viewtopic.php?f=13&t=4587&p=11036&hilit=sgesdd#p11036 - # and - # https://github.com/scipy/scipy/issues/5401 - lwork = round(BlasInt, nextfloat(real(work[1]))) - #work = Vector{$elty}(lwork) - end - end - end - end -end - - -function BandedMatrix(S::SubOperator{T,ConcreteConversion{QuotientSpace{SP,O,D,R},SP,T},Tuple{UnitRange{Int},UnitRange{Int}}}) where {SP,O,D,R,T} - kr,jr = parentindices(S) - C = parent(S) - #ret = BandedMatrix{eltype(S)}(undef, size(S), bandwidths(S)) - ret = BandedMatrix{eltype(S)}(undef, (last(kr),last(jr)), bandwidths(C)) - #@assert first(kr) == first(jr) == 1 - - sp = domainspace(C) - F = sp.F - A = F.factors - n = size(A, 1) - B = sp.bcs[1:n,1:last(jr)+n] - x = sp.x - @inbounds for j in 1:last(jr) - kk = colrange(ret, j) - if j in kk - ret[j,j] = one(R) - end - for jj = 1:n, ii = 1:n - A[ii,jj] = B[ii,j+jj] - end - for ii = 1:n - x[ii] = -B[ii,j] - end - if norm(x) > 8*norm(A)*eps(R) - mutable_lu!(F) - ldiv!(F, x) - end - for k = first(kk)+1:last(kk) - ret[k,j] = x[k-j] - end - end - - ret[kr,jr] -end diff --git a/src/Spaces/Spaces.jl b/src/Spaces/Spaces.jl deleted file mode 100644 index 7169e40a..00000000 --- a/src/Spaces/Spaces.jl +++ /dev/null @@ -1,197 +0,0 @@ - - -include("ConstantSpace.jl") -include("SumSpace.jl") -include("ArraySpace.jl") -include("ProductSpaceOperators.jl") -include("SubSpace.jl") -include("QuotientSpace.jl") - - -⊕(A::Space,B::Space)::Any = domainscompatible(A,B) ? SumSpace(A,B) : PiecewiseSpace(A,B) -⊕(f::Fun,g::Fun) = Fun(space(f) ⊕ space(g), interlace(coefficients(f),coefficients(g))) - -⊕(f::Fun,g::Fun,h::Fun...) = ⊕((f ⊕ g), h...) - -+(A::Space,B::Space) = A ⊕ B - - - - -#split the cfs into component spaces -function coefficients(cfs::AbstractVector,A::SumSpace,B::SumSpace) - if spacescompatible(A,B) - cfs - else - mapreduce(f->Fun(f,B),+,components(Fun(A,cfs))).coefficients - end -end -function coefficients(cfs::AbstractVector,A::PiecewiseSpace,B::PiecewiseSpace) - if spacescompatible(A,B) - cfs - else - mapreduce(f->Fun(f,B),+,components(Fun(A,cfs))).coefficients - end -end - - -# spread a single space into a sum space by placing -# its coefficients depending on k -function interlacewithzeros(cfs::AbstractVector,k,it) - n = length(cfs) - - ret = Array{eltype(cfs)}(undef, 0) - n == 0 && return ret - - for (K,j) in it - if K == k - push!(ret,cfs[j]) - n == j && break - else - push!(ret,0) - end - end - - ret -end - -interlacewithzeros(cfs::AbstractVector,k,B::DirectSumSpace) = interlacewithzeros(cfs,k,interlacer(B)) - -function sumspacecoefficients(cfsin::AbstractVector,A::Space,B::SumSpace) - m=length(components(B)) - - for k=1:m - if isconvertible(A,component(B,k)) - cfs = coefficients(cfsin,A,component(B,k)) - return interlacewithzeros(cfs,k,B) - end - end - - defaultcoefficients(cfsin,A,B) -end - -function sumspacecoefficients(cfsin::AbstractVector,A::Space,B::PiecewiseSpace) - m=length(components(B)) - - for k=1:m - if domain(component(B,k)) == domain(A) && isconvertible(A,component(B,k)) - cfs = coefficients(cfsin,A,component(B,k)) - return interlacewithzeros(cfs,k,B) - end - end - - defaultcoefficients(cfsin,A,B) -end - -for TYP in (:SumSpace,:PiecewiseSpace), ATYP in (:ConstantSpace,:(ConstantSpace{<:Domain{<:Number}}),:Space) - @eval coefficients(cfsin::AbstractVector,A::$ATYP,B::$TYP) = sumspacecoefficients(cfsin,A,B) -end - - -## LowRank Constructors - -# convert a vector of functionals and an operator to a LowRnakPertOperator -# the rangespace is a DirectSumSpace specified by ST of the input rangespaces -# the default is a ArraySpace, but support is there for PiecewiseSpace -# for bcs -for TYP in (:PiecewiseSpace,:VectorSpace) - @eval function LowRankPertOperator(A::AbstractVector{OT},::Type{$TYP}) where OT<:Operator - A=promotedomainspace(A) - for k=1:length(A)-1 - @assert isafunctional(A[k]) - end - @assert isbanded(A[end]) - L=LowRankOperator(A[1:end-1],$TYP) - # add zero functionals to shift down - BB=[fill(ZeroOperator(domainspace(BB),ConstantSpace()),length(A)-1);A[end]] - S=InterlaceOperator(BB,domainspace(BB),$TYP(map(rangespace,A))) - L+S - end -end - -LowRankPertOperator(A::AbstractVector{OT}) where {OT<:Operator}=LowRankPertOperator(A,VectorSpace) - -function LowRankOperator(Bin::AbstractVector{FT},::Type{PiecewiseSpace}) where FT<:Operator - B=promotedomainspace(Bin) - rsp=PiecewiseSpace(map(rangespace,B)) - LowRankOperator( - VFun{typeof(rsp),Float64}[Fun(rsp,[zeros(k-1);1]) for k=1:length(B)], - B) -end - -function LowRankOperator(Bin::AbstractVector{FT},::Type{VectorSpace}) where FT<:Operator - B=promotedomainspace(Bin) - rsp=Space([map(rangespace,B);ZeroSpace()]) #TODO: Why the hack? - LowRankOperator( - VFun{typeof(rsp),Float64}[Fun(rsp,[zeros(k-1);1]) for k=1:length(B)], - B) -end - - - -LowRankOperator(Bin::AbstractVector{FT}) where {FT<:Operator} = LowRankOperator(Bin,VectorSpace) - - -""" - WeightSpace represents a space that weights another space. - Overload weight(S,x). -""" -abstract type WeightSpace{S,DD,RR} <: Space{DD,RR} end - - -domain(S::WeightSpace) = domain(S.space) -points(sp::WeightSpace,n) = points(sp.space,n) - -weight(S::WeightSpace, x...) = error("Override `weight(::$S, $x)`") - - -struct WeightSpacePlan{S,P,T,V} - space::S - plan::P - points::Vector{T} - weights::Vector{V} -end - -struct IWeightSpacePlan{S,P,T,V} - space::S - plan::P - points::Vector{T} - weights::Vector{V} -end - -function plan_transform(S::WeightSpace,vals::AbstractVector) - pts=points(S,length(vals)) - WeightSpacePlan(S,plan_transform(S.space,vals),pts,weight.(Ref(S),pts)) -end - -function plan_itransform(S::WeightSpace,vals::AbstractVector) - pts=points(S,length(vals)) - IWeightSpacePlan(S,plan_itransform(S.space,vals),pts,weight.(Ref(S),pts)) -end - -*(P::WeightSpacePlan,vals::AbstractVector) = P.plan*(vals./P.weights) -*(P::IWeightSpacePlan,cfs::AbstractVector) = P.weights.*(P.plan*cfs) - - -# used for ProductFun -transform(sp::WeightSpace,vals::AbstractVector,plan::WeightSpacePlan) = - transform(sp.space,vals./(sp==plan.space ? plan.weights : weight.(sp,plan.points)),plan.plan) -itransform(sp::WeightSpace,cfs::AbstractVector,plan::WeightSpacePlan) = - itransform(sp.space,cfs,plan.plan).*(sp==plan.space ? plan.weights : weight.(sp,plan.points)) - - - -evaluate(f::AbstractVector,S::WeightSpace,x) = weight(S,x)*evaluate(f,S.space,x) - -function evaluate(f::AbstractVector,S::WeightSpace,x...) - fv=evaluate(f,S.space,x...) - weight(S,x...).*fv -end - -# recurrence is inhereted -for FUNC in (:recα,:recβ,:recγ) - @eval $FUNC(T,ws::WeightSpace,k) = $FUNC(T,ws.space,k) -end - -include("HeavisideSpace.jl") -include("DiracSpace.jl") \ No newline at end of file diff --git a/src/Spaces/SubSpace.jl b/src/Spaces/SubSpace.jl deleted file mode 100644 index 75049bb9..00000000 --- a/src/Spaces/SubSpace.jl +++ /dev/null @@ -1,247 +0,0 @@ - -struct SubSpace{DS,IT,DD,RR} <: Space{DD,RR} - space::DS - indexes::IT - SubSpace{DS,IT,DD,RR}(sp::DS,ind::IT) where {DS,IT,DD,RR} = new(sp,ind) -end - -SubSpace(sp::Space,kr) = - SubSpace{typeof(sp),typeof(kr),domaintype(sp),rangetype(sp)}(sp,kr) - -SubSpace(sp::SubSpace,kr) = SubSpace(sp.space,reindex(sp,sp.indexes,to_indexes(kr))[1]) - -domain(DS::SubSpace) = domain(DS.space) -dimension(sp::SubSpace) = length(sp.indexes) - -|(sp::Space,kr::AbstractRange) = SubSpace(sp,kr) - - -function |(f::Fun,kr::AbstractInfUnitRange) - @assert dimension(space(f)) == ∞ - Fun(space(f)|kr,f.coefficients[kr[1]:end]) -end - -block(sp::SubSpace, k::Integer) = block(sp.space,reindex(sp,(sp.indexes,),(k,))[1]) - -function blocklengths(sp::SubSpace{DS,UnitRange{Int}}) where DS - N = first(sp.indexes) - M = last(sp.indexes) - B1=block(sp.space,N) - B2=block(sp.space,M) - # if the blocks are equal, we have only one bvlock - B1 == B2 && return [zeros(Int,B1.n[1]-1);length(sp.indexes)] - - [zeros(Int,B1.n[1]-1); - blockstop(sp.space,B1)-N+1;blocklengths(sp.space)[B1.n[1]+1:B2.n[1]-1]; - M-blockstart(sp.space,B2)+1] -end - -function blocklengths(sp::SubSpace{DS,<:AbstractInfUnitRange{Int}}) where DS - N = first(sp.indexes) - B1=block(sp.space,N) - - Vcat([zeros(Int,B1.n[1]-1); blockstop(sp.space,B1)-N+1], - blocklengths(sp.space)[B1.n[1]+1:∞]) -end - -blocklengths(sp::SubSpace{DS,Block{1,T}}) where {DS, T} = - [blocklengths(sp.space)[Int(sp.indexes)]] - -blocklengths(sp::SubSpace) = error("Not implemented for non-unitrange subspaces") - - -## Block reindexing for SubSpace -reindex(sp::SubSpace, b::Tuple{Block{1}}, ks::Tuple{Any}) = (blockstart(sp.space,b[1]).+ks[1].-1,) -reindex(sp::SubSpace, br::Tuple{BlockRange1}, ks::Tuple{Block{1}}) = (Block(Int.(br[1])[first(ks[1].n)]),) -reindex(sp::SubSpace, br::Tuple{BlockRange1}, ks::Tuple{Any}) = (blockstart(sp.space,first(Int.(br[1]))).+ks[1].-1,) - -# blocks stay the same with unit range indices -reindex(sp::SubSpace, br::Tuple{AbstractVector{Int}}, ks::Tuple{Block{1}}) = - reindex(sp, br, (blockrange(sp,first(ks)),)) -reindex(sp::SubSpace, br::Tuple{AbstractVector{Int}}, ks::Tuple{BlockRange1}) = - reindex(sp, br, (blockrange(sp,first(ks)),)) - - -## Block -blocklengths(sp::SubSpace{DS,Block}) where {DS} = [blocklengths(sp.space)[sp.indexes.n[1]]] -dimension(sp::SubSpace{DS,Block}) where {DS} = blocklengths(sp.space)[sp.indexes.n[1]] - - -blocklengths(sp::SubSpace{DS,BlockRange1}) where {DS} = blocklengths(sp.space)[Int.(sp.indexes)] -dimension(sp::SubSpace{DS,BlockRange1}) where {DS} = sum(blocklengths(sp.space)[Int.(sp.indexes)]) - - -block(::Space,B::Block) = B - - - -## - - -spacescompatible(S1::SubSpace{DS,IT,DD,RR},S2::SubSpace{DS,IT,DD,RR}) where {DS,IT,DD,RR} = - spacescompatible(S1.space,S2.space) && S1.indexes == S2.indexes - -==(S1::SubSpace{DS,IT,DD,RR},S2::SubSpace{DS,IT,DD,RR}) where {DS,IT,DD,RR} = - S1.space == S2.space && S1.indexes == S2.indexes - -canonicalspace(a::SubSpace) = a.space - - - -setdomain(DS::SubSpace,d::Domain) = SubSpace(setdomain(DS.space,d),DS.indexes) - -Conversion(a::SubSpace,b::Space) = ConcreteConversion(a,b) - -function Conversion(a::S,b::SubSpace{S,<:AbstractInfUnitRange{Int}}) where S<:Space - @assert first(b.indexes) == 1 - ConversionWrapper(SpaceOperator(Operator(I,a),a,b)) -end - -bandwidths(C::ConcreteConversion{<:SubSpace{<:Any,<:AbstractInfUnitRange{Int}}}) = - first(domainspace(C).indexes)-1,1-first(domainspace(C).indexes) - -getindex(C::ConcreteConversion{<:SubSpace}, k::Integer,j::Integer) = - domainspace(C).indexes[j]==k ? one(eltype(C)) : zero(eltype(C)) - - -# avoid ambiguity -for OP in (:leftendpoint,:rightendpoint) - @eval getindex(E::ConcreteEvaluation{SubSpace{DS,IT,DD,RR},typeof($OP)},k::Integer) where {IT,DS,DD,RR}= - Evaluation(E.space.space,E.x,E.order)[E.space.indexes[k]] -end -getindex(E::ConcreteEvaluation{SubSpace{DS,IT,DD,RR}},k::Integer) where {IT,DS,DD,RR}= - Evaluation(E.space.space,E.x,E.order)[E.space.indexes[k]] -getindex(E::ConcreteEvaluation{SubSpace{DS,IT,DD,RR}},kr::AbstractRange) where {IT,DS,DD,RR} = - Evaluation(E.space.space,E.x,E.order)[E.space.indexes[kr]] - - -function conversion_rule(a::SubSpace,b::SubSpace) - if a==b - a - else - NoSpace() - end -end -# return the space that has banded Conversion to the other -function conversion_rule(a::SubSpace,b::Space) - if a.space==b - a # we can write droping coefficients as a banded operator - else - NoSpace() - end -end - -function union_rule(a::SubSpace,b::SubSpace) - if a == b - a - else - NoSpace() - end -end -# return the space that has banded Conversion to the other -function union_rule(a::SubSpace,b::Space) - if a.space==b - b # we can write droping coefficients as a banded operator - else - NoSpace() - end -end - - -function subspace_coefficients(v::AbstractVector,sp::SubSpace,dropsp::SubSpace) - if sp == dropsp - v - else - coefficients(v,sp,canonicalspace(sp),dropsp) - end -end - - -function subspace_coefficients(v::AbstractVector,sp::Space,dropsp::SubSpace) - n=length(v) - if sp == dropsp.space - ret = Array{eltype(v)}(undef, 0) - for k in dropsp.indexes - if k > n - return ret - end - push!(ret,v[k]) - end - ret - else - coefficients(v,sp,canonicalspace(dropsp),dropsp) - end -end - -function subspace_coefficients(v::AbstractVector,dropsp::SubSpace,sp::Space) - if sp==dropsp.space - isempty(v) && return v - ret = zeros(eltype(v),dropsp.indexes[length(v)]) - for k = eachindex(v) - ret[dropsp.indexes[k]] = v[k] - end - ret - else - coefficients(v,dropsp,canonicalspace(dropsp),sp) - end -end - - -coefficients(v::AbstractVector,sp::SubSpace,dropsp::SubSpace) = subspace_coefficients(v,sp,dropsp) - - - -## points - - -## transform -function transform(sp::SubSpace, vals::AbstractVector) - ret = transform(sp.space,vals) - coefficients(ret,sp.space,sp) -end - -itransform(sp::SubSpace,cfs::AbstractVector) = - itransform(sp.space,coefficients(cfs,sp,sp.space)) - -for Tran in (:plan_transform, :plan_transform!, :plan_itransform, :plan_itransform!) - @eval $Tran(sp::SubSpace) = $Tran(sp, dimension(sp)) -end - -points(sp::SubSpace, n) = points(sp.space, n) -points(sp::SubSpace) = points(sp, dimension(sp)) - - -coefficients(v::AbstractVector,::SubSpace{DS,IT,Segment{Vec{2,TT}}},::TensorSpace{SV,DD}) where {DS,IT,TT,SV,DD<:EuclideanDomain{2}} = - error("Not callable, only defined for ambiguity errors.") -coefficients(v::AbstractVector,::SubSpace{DS,IT,D},::TensorSpace{SV,DD}) where {DS,IT,D,SV,DD<:EuclideanDomain{2}} = - error("Not callable, only defined for ambiguity errors.") - -for TYP in (:SumSpace,:PiecewiseSpace,:TensorSpace,:ConstantSpace,:Space) # Resolve conflict - @eval begin - coefficients(v::AbstractVector,sp::$TYP,dropsp::SubSpace) = subspace_coefficients(v,sp,dropsp) - coefficients(v::AbstractVector,dropsp::SubSpace,sp::$TYP) = subspace_coefficients(v,dropsp,sp) - end -end - -## ProductFUn - -# values{S<:SubSpace,V<:SubSpace}(f::ProductFun{S,V})=values(ProductFun(f,space(f,1).space,space(f,2).space)) -# values{S<:SubSpace}(f::ProductFun{S})=values(ProductFun(f,space(f,1).space,space(f,2))) -# -# -# function coefficients{n,DS,TT,DD}(f::ProductFun{SubSpace{n,1,DS,DD,RR}},ox::Space,oy::Space) -# T=eltype(f) -# m=size(f,1) -# A=[pad!(coefficients(fx,ox),m+n) for fx in f.coefficients] -# B=hcat(A...)::Array{T,2} -# for k=1:size(B,1) -# ccfs=coefficients(B[k,:],space(f,2),oy) -# if length(ccfs)>size(B,2) -# B=pad(B,size(B,1),length(ccfs)) -# end -# B[k,1:length(ccfs)]=ccfs -# #B[k,length(ccfs):1:end]=zero(T) -# end -# -# B -# end diff --git a/src/Spaces/SumSpace.jl b/src/Spaces/SumSpace.jl deleted file mode 100644 index d07f97e0..00000000 --- a/src/Spaces/SumSpace.jl +++ /dev/null @@ -1,504 +0,0 @@ -export ⊕, components, PiecewiseSpace - - - - - - - - - -## SumSpace encodes a space that can be decoupled as f(x) = a(x) + b(x) where a is in S and b is in V - - -abstract type DirectSumSpace{SV,D,R} <: Space{D,R} end - - -dimension(sp::DirectSumSpace) = mapreduce(dimension,+,sp.spaces) - -components(s::Space) = (s,) -components(sp::DirectSumSpace) = sp.spaces -component(s::Space,k...) = components(s)[k...] - -ncomponents(s::Space) = length(components(s)) -ncomponents(f::Fun) = ncomponents(space(f)) - -BlockInterlacer(sp::DirectSumSpace) = BlockInterlacer(map(blocklengths,sp.spaces)) -interlacer(sp::DirectSumSpace) = BlockInterlacer(sp) -interlacer(sp::Space) = BlockInterlacer(tuple(blocklengths(sp))) - -function blocklengths(sp::DirectSumSpace) - bl=map(blocklengths,components(sp)) - N=mapreduce(length,max,bl) - mapreduce(b->pad(b,N),+,bl) -end -block(sp::DirectSumSpace,k::Int) = - Block(findfirst(x->x≥k,cumsum(blocklengths(sp)))) - - - -isambiguous(sp::DirectSumSpace) = any(isambiguous,components(sp)) - - -struct SumSpace{SV,D,R} <: DirectSumSpace{SV,D,R} - spaces::SV - SumSpace{SV,D,R}(dom::Domain) where {SV,D,R} = - new(tuple(map(typ->typ(dom),SV.parameters)...)) - SumSpace{SV,D,R}(sp::Tuple) where {SV,D,R} = new(sp) -end - -SumSpace(sp::Tuple) = SumSpace{typeof(sp),domaintype(first(sp)), - mapreduce(rangetype,promote_type,sp)}(sp) - - -struct PiecewiseSpace{SV,D<:UnionDomain,R} <: DirectSumSpace{SV,D,R} - spaces::SV - PiecewiseSpace{SV,D,R}(dom::AnyDomain) where {SV,D,R} = - new{SV,D,R}(tuple(map(typ->typ(dom),SV.parameters)...)) - PiecewiseSpace{SV,D,R}(dom::UnionDomain) where {SV,D,R} = - new{SV,D,R}(tuple(map((typ,dom)->typ(dom),SV.parameters,dom.domains)...)) - PiecewiseSpace{SV,D,R}(sp::Tuple) where {SV,D,R} = - new{SV,D,R}(sp) -end - -function PiecewiseSpace(spin::Tuple) - sp=tuple(union(spin)...) # remove duplicates - - PiecewiseSpace{typeof(sp),typeof(UnionDomain(map(domain,sp))), - mapreduce(rangetype,promote_type,sp)}(sp) -end - -PiecewiseSpace(spin::Set) = PiecewiseSpace(collect(spin)) - - - -for TYP in (:SumSpace,:PiecewiseSpace) - @eval begin - $TYP(A::$TYP,B::$TYP) = $TYP(tuple(A.spaces...,B.spaces...)) - - $TYP(A::Space,B::$TYP) = $TYP(tuple(A,B.spaces...)) - $TYP(A::$TYP,B::Space) = $TYP(tuple(A.spaces...,B)) - $TYP(A::Space...) = $TYP(A) - $TYP(sp::AbstractArray) = $TYP(tuple(sp...)) - - canonicalspace(A::$TYP) = $TYP(sort(collect(A.spaces))) - end -end - -# TODO: Fix this Hack -SumSpace(A::ConstantSpace{AnyDomain}, B::ConstantSpace{AnyDomain}) = error("Should not happen") -SumSpace(A::SumSpace, B::ConstantSpace{AnyDomain}) = SumSpace(A, setdomain(B, domain(A))) -SumSpace(B::ConstantSpace{AnyDomain}, A::SumSpace) = SumSpace(setdomain(B, domain(A)), A) -SumSpace(A::Space, B::ConstantSpace{AnyDomain}) = SumSpace(A, setdomain(B, domain(A))) -SumSpace(B::ConstantSpace{AnyDomain}, A::Space) = SumSpace(setdomain(B, domain(A)), A) - -pieces(sp::PiecewiseSpace) = sp.spaces -piece(s::Space,k) = pieces(s)[k] -pieces(f::Fun{<:PiecewiseSpace}) = components(f) -piece(f::Fun{<:PiecewiseSpace},k) = component(f,k) - -npieces(s::Space) = length(pieces(s)) -npieces(f::Fun) = npieces(space(f)) - - - -setdomain(A::SumSpace,d::Domain) = SumSpace(map(sp->setdomain(sp,d),A.spaces)) - - -setdomain(A::PiecewiseSpace,d::UnionDomain) = - PiecewiseSpace(map((sp,dd)->setdomain(sp,dd),A.spaces,d.domains)) - - - -function spacescompatible(A::S,B::S) where S<:DirectSumSpace - if ncomponents(A) != ncomponents(B) - false - else - ret=true - for k=1:ncomponents(A) - if !spacescompatible(component(A,k),component(B,k)) - return false - end - end - true - end -end - -function Base.promote_rule(::Type{Fun{SumSpace{SV,D,R},V,VV}},::Type{T}) where {SV,D,R,V,T<:Number,VV} - for k=1:length(SV.parameters) - pt=promote_type(VFun{SV.parameters[k],V},T) - if pt != Fun - return VFun{SumSpace{Tuple{SV.parameters[1:k-1]...,pt.parameters[1],SV.parameters[k+1:end]...}, - D,R},promote_type(V,T)} - end - end - Fun -end - -Base.promote_rule(::Type{Fun{SumSpace{SV,D,R}}},::Type{T}) where {SV,D,R,T<:Number} = - promote_rule(VFun{SumSpace{SV,D,R},Float64},T) - -function Base.promote_rule(::Type{Fun{PiecewiseSpace{SV,D,R},V,VV}},::Type{T}) where {SV,D,R,V,VV,T<:Number} - # if any doesn't support promoting, just leave unpromoted - - newfsp=map(s->promote_type(VFun{s,V},T),SV.parameters) - if any(s->s==Fun,newfsp) - Fun - else - newsp=map(s->s.parameters[1],newfsp) - VFun{PiecewiseSpace{Tuple{newsp...},D,R},promote_type(V,T)} - end -end - -Base.promote_rule(::Type{Fun{PiecewiseSpace{SV,D,R}}},::Type{T}) where {SV,D,R,T<:Number} = - promote_rule(VFun{PiecewiseSpace{SV,D,R},Float64},T) - - - -# domain -domain(A::SumSpace) = domain(A.spaces[end]) # TODO: this assumes all spaces have the same domain - # we use end to avoid ConstantSpace - - -Space(d::UnionDomain) = PiecewiseSpace(map(Space,d.domains)) -domain(S::PiecewiseSpace) = UnionDomain(map(domain,S.spaces)) - - - -function spacescompatible(A::Tuple,B::Tuple) - if length(A) != length(B) - return false - end - #assumes domain doesn't impact sorting - asort=sort(collect(A));bsort=sort(collect(B)) - for k=1:length(asort) - if !spacescompatible(asort[k],bsort[k]) - return false - end - end - - return true -end - - -# avoids default ConstantSpace -function union_rule(B::ConstantSpace,A::SumSpace)::Any - if !domainscompatible(A,B) - NoSpace() - else - for sp in A.spaces - if isconvertible(B,sp) - return A - end - end - SumSpace(A,B) - end -end - -function union_rule(A::SumSpace, B::Space)::Any - if !domainscompatible(A,B) - NoSpace() - else - for sp in A.spaces - if isconvertible(B,sp) - return A - end - end - SumSpace(A,B) - end -end - - - -## components - -# We use a view when it's avaiable to avoid allocation -component_coefficients(it::TrivialInterlacer{d},cfs,k) where {d} = (@view cfs[k:d:end]) - -function component_coefficients(it,cfs,k) - N=length(cfs) - d=dimension(it,k) - - # preallocate: we know we have at most N coefficients - ret=Array{eltype(cfs)}(undef, N) - j=1 # current coefficient - p=0 # current length - for (n,m) in it - if j > N - break - end - if n==k - ret[m] = cfs[j] - p+=1 - if m ≥ d - # if we've reached the dimension, we are done - break - end - end - j+=1 - end - resize!(ret,p) # throw out extra coefficients -end - -component_coefficients(sp::Space,cfs,k) = component_coefficients(interlacer(sp),cfs,k) - -component(f::Fun{<:DirectSumSpace},k::Integer) = - Fun(component(space(f),k),component_coefficients(space(f),f.coefficients,k)) - - -## evaluate - - -for OP in (:(Base.last),:(Base.first)) - @eval begin - $OP(f::Fun{SS}) where {SS<:SumSpace} = mapreduce($OP,+,components(f)) - $OP(f::Fun{SS}) where {SS<:PiecewiseSpace} = $OP($OP(components(f))) - end -end - -# this is a type-stable version -# TODO: replace with generated function -function evaluate(f::AbstractVector,S::SumSpace{Tuple{A,B}},x) where {A,B} - it = interlacer(S) - a,b = S.spaces - evaluate(component_coefficients(it,f,1),a,x) + - evaluate(component_coefficients(it,f,2),b,x) -end - -function evaluate(f::AbstractVector,S::SumSpace,x) - ret = zero(rangetype(S)) - it = interlacer(S) - for k=1:ncomponents(S) - ret += evaluate(component_coefficients(it,f,k),component(S,k),x) - end - ret -end - - -function evaluate(f::AbstractVector,S::PiecewiseSpace,x) - g=Fun(S,f) - -# ret=zero(promote_type(eltype(f),eltype(S))) - for k=1:ncomponents(S) - if in(x,domain(component(S,k))) - return component(g,k)(x) - end - end - return 0*first(g) -end - - -## calculus -for TYP in (:SumSpace,:PiecewiseSpace) - for OP in (:differentiate,:integrate) - @eval function $OP(f::Fun{D,T}) where {D<:$TYP,T} - fs = map($OP,components(f)) - sp = $TYP(map(space,fs)) - Fun(sp,interlace(fs,sp)) - end - end - for OP in (:(Base.real),:(Base.imag),:(Base.conj)) - @eval begin - $OP(f::Fun{$TYP{SV,DD,RR}}) where {SV,DD,RR<:Real} = Fun(f.space,$OP(f.coefficients)) - function $OP(f::Fun{$TYP{SV,DD,RR}}) where {SV,DD,RR} - fs=map($OP,components(f)) - sp=$TYP(map(space,fs)) - Fun(sp,interlace(fs,sp)) - end - end - end -end - - - -@eval function Base.cumsum(f::Fun{D,T}) where {D<:SumSpace,T} - fs=map(cumsum,components(f)) - sp=SumSpace(map(space,fs)) - Fun(sp,interlace(fs,sp)) -end - - -for TYP in (:SumSpace,:PiecewiseSpace), OP in (:(Base.sum),:linesum) - @eval $OP(f::Fun{V}) where {V<:$TYP} = mapreduce($OP,+,components(f)) -end - -function Base.cumsum(f::Fun{V}) where V<:PiecewiseSpace - vf=components(f) - r=zero(cfstype(f)) - for k=1:length(vf) - vf[k]=cumsum(vf[k]) + r - r=last(vf[k]) - end - Fun(vf,PiecewiseSpace) -end - -Base.cumsum(f::Fun{V},d::Domain) where {V<:PiecewiseSpace} = - mapreduce(g->cumsum(g,d),+,components(f)) - - - -bilinearform(f::Fun{S},g::Fun{V}) where {S<:PiecewiseSpace,V<:PiecewiseSpace} = - sum(map(bilinearform,components(f),components(g))) -linebilinearform(f::Fun{S},g::Fun{V}) where {S<:PiecewiseSpace,V<:PiecewiseSpace} = - sum(map(linebilinearform,components(f),components(g))) - -# assume first domain has 1 as a basis element - - - -function ones(::Type{T}, S::SumSpace) where T<:Number - for sp in components(S) - if isconvertible(ConstantSpace(),sp) - return Fun(ones(T,sp), S) - end - end - - error("$S does not contain constants") -end - -ones(S::SumSpace) = ones(Float64,S) - -ones(::Type{T},S::PiecewiseSpace{SS,V}) where {T<:Number,SS,V} = - Fun(map(ones,components(S)),PiecewiseSpace) -ones(S::PiecewiseSpace) = ones(Float64,S) - - -Fun(::typeof(identity), S::PiecewiseSpace) = Fun(Fun.(identity,S.spaces),PiecewiseSpace) - - -# interlace coefficients according to iterator -function interlace(::Type{T},v::AbstractVector{V},it::BlockInterlacer) where {T,V<:AbstractVector} - ret=Array{T}(undef,0) - N=mapreduce(length,max,v) - cnts = Vector(map(length,v)) # convert to Vector to ensure mutable - - for (n,m) in it - if maximum(cnts) == 0 - break - end - - if m ≤ length(v[n]) - push!(ret,v[n][m]) - cnts[n] -= 1 - else - push!(ret,zero(T)) - end - end - ret -end - -interlace(v::AbstractVector{V},it::BlockInterlacer) where {V<:AbstractVector} = - interlace(mapreduce(eltype,promote_type,v),v,it) - -interlace(v::AbstractVector{V},sp::DirectSumSpace) where {V<:AbstractVector} = - interlace(v,interlacer(sp)) - -interlace(v::AbstractVector{F},sp::DirectSumSpace) where {F<:Fun} = - interlace(map(coefficients,v),sp) - - -function interlace(v::Union{Tuple,Vector{Any}},sp::DirectSumSpace) - V=Array{Vector{mapreduce(cfstype,promote_type,v)}}(undef, length(v)) - for k=1:length(v) - V[k]=coefficients(v[k]) - end - interlace(V,sp) -end - -components(f::Fun{S}) where {S<:DirectSumSpace} = Fun[component(f,j) for j=1:ncomponents(f)] - -function Fun(v::AbstractVector{F},::Type{PiecewiseSpace}) where F<:Fun - sp = PiecewiseSpace(map(space,v)) - Fun(sp,interlace(v,sp)) -end -function Fun(v::Tuple,::Type{PiecewiseSpace}) - sp=PiecewiseSpace(map(space,v)) - Fun(sp,interlace(v,sp)) -end - -Fun(v::AbstractVector{Any},::Type{PiecewiseSpace}) = Fun(tuple(v...),PiecewiseSpace) - -## transforms -points(d::PiecewiseSpace,n) = vcat(points.(pieces(d), pieces_npoints(d,n))...) - - -plan_transform(sp::PiecewiseSpace,vals::AbstractVector) = - TransformPlan{eltype(vals),typeof(sp),false,Nothing}(sp,nothing) - -plan_itransform(sp::PiecewiseSpace,vals::AbstractVector) = - ITransformPlan{eltype(vals),typeof(sp),false,Nothing}(sp,nothing) - - - -function *(P::TransformPlan{T,PS,false},vals::AbstractVector{T}) where {PS<:PiecewiseSpace,T} - S=components(P.space) - n=length(vals) - K=length(S) - k=div(n,K) - PT=promote_type(prectype(P.space),eltype(vals)) - if k==0 - M=Array{Vector{PT}}(undef, n) - for j=1:n - M[j]=transform(S[j],[vals[j]]) - end - else - r=n-K*k - M=Array{Vector{PT}}(undef, K) - - for j=1:r - M[j]=transform(S[j],vals[(j-1)*(k+1)+1:j*(k+1)]) - end - for j=r+1:length(S) - M[j]=transform(S[j],vals[r*(k+1)+(j-r-1)*k+1:r*(k+1)+(j-r)*k]) - end - end - - interlace(M,P.space) -end - -*(P::ITransformPlan{T,PS,false},cfs::AbstractVector{T}) where {T,PS<:PiecewiseSpace} = - vcat([itransform(P.space.spaces[j],component(Fun(P.space,cfs),j).coefficients) for j=1:ncomponents(P.space)]...) - - - -itransform(S::SumSpace,cfs) = Fun(S,cfs).(points(S,length(cfs))) -function itransform!(S::SumSpace,cfs) - f = Fun(S,cfs) - cfs .= f.(points(S,length(cfs))) -end - - - -## SumSpace{ConstantSpace} -# this space is special - -union_rule(P::PiecewiseSpace,C::ConstantSpace{AnyDomain}) = - PiecewiseSpace(map(sp->union(sp,C),P.spaces)) - - - -## Multiplication -function *(f::Fun{<:PiecewiseSpace,T},g::Fun{<:PiecewiseSpace,N}) where {T,N} - domain(f) ≠ domain(g) && return default_mult(f,g) - - Fun(map(*,components(f),components(g)),PiecewiseSpace) -end - - - -## Multivariate - -ncomponents(sp::TensorSpace) = mapreduce(s -> ncomponents(s), *, factors(sp)) - -component(sp::TensorSpace{Tuple{S1,S2}},k::Integer) where {S1<:DirectSumSpace,S2<:DirectSumSpace} = - error("Not defined. Used component(sp,k,j).") - -component(sp::TensorSpace{Tuple{S1,S2}},k::Integer) where {S1<:DirectSumSpace,S2} = - component(factor(sp,1),k) ⊗ factor(sp,2) - -component(sp::TensorSpace{Tuple{S1,S2}},k::Integer) where {S1,S2<:DirectSumSpace} = - factor(sp,1) ⊗ component(factor(sp,2),k) - - -component(sp::TensorSpace{Tuple{S1,S2}},k::Integer,j::Integer) where {S1<:DirectSumSpace,S2<:DirectSumSpace} = - component(factor(sp,1),k) ⊗ component(factor(sp,2),j) diff --git a/src/constructors.jl b/src/constructors.jl deleted file mode 100644 index 15e7d530..00000000 --- a/src/constructors.jl +++ /dev/null @@ -1,196 +0,0 @@ - -valsdomain_type_promote(::Type{T},::Type{T}) where {T<:Complex}=T,T -valsdomain_type_promote(::Type{T},::Type{T}) where {T<:Real}=T,T -valsdomain_type_promote(::Type{Int},::Type{Int})=Float64,Int -valsdomain_type_promote(::Type{T},::Type{Complex{V}}) where {T<:Real,V<:Real}=promote_type(T,V),Complex{promote_type(T,V)} -valsdomain_type_promote(::Type{Complex{T}},::Type{V}) where {T<:Real,V<:Real}=Complex{promote_type(T,V)},promote_type(T,V) -valsdomain_type_promote(::Type{T},::Type{Int}) where {T<:Integer}=Float64,Int -valsdomain_type_promote(::Type{T},::Type{Int}) where {T<:Real}=T,Int -valsdomain_type_promote(::Type{T},::Type{Int}) where {T<:Complex}=T,Int -valsdomain_type_promote(::Type{T},::Type{V}) where {T<:Integer,V<:Real}=valsdomain_type_promote(Float64,V) -valsdomain_type_promote(::Type{T},::Type{V}) where {T<:Integer,V<:Complex}=valsdomain_type_promote(Float64,V) -valsdomain_type_promote(::Type{T},::Type{Vector{T}}) where {T<:Real}=T,Vector{T} -valsdomain_type_promote(::Type{T},::Type{V}) where {T,V}=promote_type(T,V),promote_type(T,V) - - - -function choosefuncfstype(ftype,Td) - if !( ftype<: Number || ( ((ftype <: AbstractArray) || (ftype <: Vec)) && - (eltype(ftype) <: Number) ) ) - @warn "Function outputs type $(ftype), which is not a Number" - end - - Tprom = ftype - - if ftype <: Number #TODO should also work for array-valued functions - Tprom,Tpromd=valsdomain_type_promote(ftype,Td) - - if Tpromd != Td - @warn "Space domain number type $(Td) is not compatible with coefficient type $(Tprom)" - #TODO should construct a new Space that contains a domain where the numbers have been promoted - #and call constructor with this Space. - end - end - - Tprom -end - - -# default_Fun is the default constructor, based on evaluation and transforms -# last argument is whether to splat or not -default_Fun(::Type{T},f,d::Space{ReComp},pts::AbstractArray,::Type{Val{true}}) where {T,ReComp} = - Fun(d,transform(d,T[f(x...) for x in pts])) - -default_Fun(::Type{T},f,d::Space{ReComp},pts::AbstractArray,::Type{Val{false}}) where {T,ReComp} = - Fun(d,transform(d,broadcast!(f, similar(pts, T), pts))) - - -function default_Fun(f,d::Space{ReComp},n::Integer,::Type{Val{false}}) where ReComp - pts=points(d, n) - f1=f(pts[1]) - if isa(f1,AbstractArray) && size(d) ≠ size(f1) - return Fun(f,Space(fill(d,size(f1))),n) - end - - # we need 3 eltype calls for the case Interval(Point([1.,1.])) - Tprom=choosefuncfstype(typeof(f1),prectype(domain(d))) - default_Fun(Tprom,f,d,pts,Val{false}) -end - -function default_Fun(f,d::Space{ReComp},n::Integer,::Type{Val{true}}) where ReComp - pts=points(d, n) - f1=f(pts[1]...) - if isa(f1,AbstractArray) && size(d) ≠ size(f1) - return Fun(f,Space(fill(d,size(f1))),n) - end - - # we need 3 eltype calls for the case Interval(Point([1.,1.])) - Tprom=choosefuncfstype(typeof(f1),prectype(domain(d))) - default_Fun(Tprom,f,d,pts,Val{true}) -end - -default_Fun(f,d::Space{ReComp},n::Integer) where {ReComp} = default_Fun(f,d,n,Val{!hasnumargs(f,1)}) - -Fun(f::Function,d::Space{ReComp},n::Integer) where {ReComp} = default_Fun(dynamic(f),d,n) - -# the following is to avoid ambiguity -# Fun(f::Fun,d) should be equivalent to Fun(x->f(x),d) -Fun(f::Fun,d::Space) = Fun(d,coefficients(f,d)) -Fun(f::Fun,::Type{T}) where {T<:Space} = Fun(f,T(domain(f))) - - -Fun(f,T::Type) = Fun(dynamic(f),T()) -Fun(f::Function,T::Type,n::Integer) = Fun(dynamic(f),T(),n) - -Fun(f::AbstractVector,d::Domain) = Fun(f,Space(d)) -Fun(d::Domain,f::AbstractVector{T}) where {T<:Number} = Fun(Space(d),f) -Fun(d::Domain,f::AbstractVector) = Fun(Space(d),f) - - -Fun(f::Function,d::Domain,n) = Fun(dynamic(f),Space(d),n) - - -# We do zero special since zero exists even when one doesn't -Fun(c::Number,::Type{T}) where {T<:Space} = c==0 ? zeros(T(AnyDomain())) : c*ones(T(AnyDomain())) -Fun(c::Number,d::Domain) = c==0 ? c*zeros(d) : c*ones(d) -Fun(c::Number,d::Space) = c==0 ? c*zeros(prectype(d),d) : c*ones(prectype(d),d) - -## Adaptive constructors -function default_Fun(f, d::Space) - hasnumargs(f,1) || return Fun(xy->f(xy...),d) - isinf(dimension(d)) || return Fun(f,d,dimension(d)) # use exactly dimension number of sample points - - #TODO: reuse function values? - T = real(prectype(domain(d))) - - r=checkpoints(d) - f0=f(first(r)) - - isa(f0,AbstractArray) && size(d) ≠ size(f0) && return Fun(f,Space(fill(d,size(f0)))) - - - - tol =T==Any ? 20eps() : 20eps(T) - - - fr=map(f,r) - maxabsfr=norm(fr,Inf) - - for logn = 4:20 - #cf = Fun(f, d, 2^logn + 1) - cf = default_Fun(f, d, 2^logn) - maxabsc = maximum(abs,cf.coefficients) - if maxabsc == 0 && maxabsfr == 0 - return(zeros(d)) - end - - b = block(d,length(cf.coefficients)) - bs = blockstart(d,max(b.n[1]-2,1)) - - # we allow for transformed coefficients being a different size - ##TODO: how to do scaling for unnormalized bases like Jacobi? - if ncoefficients(cf) > 8 && maximum(abs,cf.coefficients[bs:end]) < 10tol*maxabsc && - all(k->norm(cf(r[k])-fr[k],1)1.0,S) -ones(::Type{T}, S::Space) where {T<:Number} = Fun(x->one(T),S) - -function Fun(::typeof(identity), d::Domain) - cd=canonicaldomain(d) - if typeof(d) == typeof(cd) - Fun(dynamic(x->x),d) # fall back to constructor, can't use `identity` as that creates a loop - else - # this allows support for singularities, that the constructor doesn't - sf=fromcanonical(d,Fun(identity,cd)) - Fun(setdomain(space(sf),d),coefficients(sf)) - end -end - -Fun(::typeof(identity), S::Space) = Fun(identity,domain(S)) - - - -Fun(f::typeof(zero), d::Space) = zeros(eltype(domain(d)),d) -Fun(f::typeof(one), d::Space) = ones(eltype(domain(d)),d) - -Fun(f::Type, d::Domain) = Fun(f,Space(d)) -Fun(f::Function, d::Domain) = Fun(f,Space(d)) - - -# this is the main constructor -Fun(f::Function, d::Space) = default_Fun(dynamic(f), d) - -# this supports expanding a Fun to a larger or smaller domain. -# we take the union and then intersection to get at any singularities -# TODO: singularities in space(f) -Fun(f::Fun, d::Domain) = Fun(f,Space((d ∪ domain(f)) ∩ d)) - - - - - -## Aliases - - - -Fun(T::Type,n::Integer) = Fun(T(),n) -Fun(f,n::Integer) = Fun(f,ChebyshevInterval(),n) -Fun(T::Type,d::AbstractVector) = Fun(T(),d) - -Fun(f::Fun{SequenceSpace},s::Space) = Fun(s,f.coefficients) diff --git a/src/hacks.jl b/src/hacks.jl deleted file mode 100644 index 337caa9c..00000000 --- a/src/hacks.jl +++ /dev/null @@ -1,77 +0,0 @@ -## Functions that depend on the structure of BandedMatrix - - -function pad!(A::BandedMatrix,n,m) - A.data = pad(A.data,size(A.data,1),m) - A.raxis = Base.OneTo(n) - A -end - - - -# linear algebra - - -## Constructors that involve MultivariateFun -Fun(f::Fun) = f # Fun of Fun should be like a conversion - -function Fun(fin::Function) - f = dynamic(fin) - - if hasnumargs(f,1) - # check for tuple - try - f(0) - catch ex - if isa(ex,BoundsError) - # assume its a tuple - return Fun(f,ChebyshevInterval()^2) - else - throw(ex) - end - end - - Fun(f,ChebyshevInterval()) - elseif hasnumargs(f,2) - Fun(f,ChebyshevInterval()^2) - else - error("Function not defined on interval or square") - end -end - - - - -## These hacks support PDEs with block matrices - - - -## dot for AbstractVector{Number} * AbstractVector{Fun} - -function dot(c::AbstractVector{T},f::AbstractVector{F}) where {T<:Union{Number,Fun,MultivariateFun},F<:Union{Fun,MultivariateFun}} - @assert length(c)==length(f) - ret = conj(first(c))*first(f) - for k = 2:length(c) - ret += conj(c[k])*f[k] - end - ret -end - - -function dotu(c::AbstractVector{T},f::AbstractVector{F}) where {T<:Union{Fun,MultivariateFun,Number},F<:Union{Fun,MultivariateFun,Number}} - @assert length(c)==length(f) - isempty(c) && return zero(Base.promote_op(*,T,F)) - ret = c[1]*f[1] - for k = 2:length(c) - ret += c[k]*f[k] - end - ret -end - - - -## Gets blockbandwidths working for SpectralMeasures - -# TODO: Is this a good definition? -blockbandwidths(T::FiniteOperator{AT,TT,SS1,SS2}) where {AT,TT,SS1<:Union{EuclideanSpace,SequenceSpace}, - SS2<:Union{EuclideanSpace,SequenceSpace}} = bandwidths(T) diff --git a/src/specialfunctions.jl b/src/specialfunctions.jl deleted file mode 100644 index 2492be89..00000000 --- a/src/specialfunctions.jl +++ /dev/null @@ -1,594 +0,0 @@ -## abs -splitmap(g,d::Domain,pts) = Fun(g,split(d , pts)) - -function split(d::IntervalOrSegment, pts) - a,b = endpoints(d) - isendpoint = true - for p in pts - if !(p ≈ a) && !(p ≈ b) - isendpoint = false - break - end - end - isendpoint && return d - - @assert all(in.(pts, Ref(d))) - PiecewiseSegment(sort!(union(endpoints(d), pts))) -end - -function split(d::PiecewiseSegment, pts) - @assert all(in.(pts, Ref(d))) - PiecewiseSegment(sort!(union(d.points, pts))) -end - -split(d::SegmentDomain, pts) = d - - -function splitatroots(f::Fun) - d=domain(f) - pts=union(roots(f)) # union removes multiplicities - splitmap(x->f(x),d,pts) -end - -function abs(f::Fun{S,T}) where {S<:RealUnivariateSpace,T<:Real} - d=domain(f) - pts = iszero(f) ? T[] : roots(f) - splitmap(x->abs(f(x)),d,pts) -end - -function abs(f::Fun) - - d=domain(f) - - pts = iszero(f) ? cfstype(f)[] : roots(f) - - if isempty(pts) - # This makes sure Laurent returns real type - real(Fun(abs∘f,space(f))) - else - splitmap(abs∘f,d,pts) - end -end - - -midpoints(d::IntervalOrSegment) = [mean(d)] -midpoints(d::Union{UnionDomain,PiecewiseSegment}) = mapreduce(midpoints,vcat,components(d)) - -for OP in (:sign,:angle) - @eval function $OP(f::Fun{S,T}) where {S<:RealUnivariateSpace,T<:Real} - d=domain(f) - - pts = iszero(f) ? T[] : roots(f) - - if isempty(pts) - $OP(first(f))*one(f) - else - d = split(d , pts) - midpts = midpoints(d) - Fun(UnionDomain(components(d)), $OP.(f.(midpts))) - end - end -end - -for op in (:(max),:(min)) - @eval begin - function $op(f::Fun{S,T1},g::Fun{V,T2}) where {S<:RealUnivariateSpace,V<:RealUnivariateSpace,T1<:Real,T2<:Real} - h=f-g - d=domain(h) - pts=iszero(h) ? cfstype(h)[] : roots(h) - splitmap(x->$op(f(x),g(x)),d,pts) - end - $op(f::Fun{S,T},g::Real) where {S<:RealUnivariateSpace,T<:Real} = $op(f,Fun(g,domain(f))) - $op(f::Real,g::Fun{S,T}) where {S<:RealUnivariateSpace,T<:Real} = $op(Fun(f,domain(g)),g) - end -end - - - -isfinite(f::Fun) = isfinite(maximum(abs,f)) && isfinite(minabs(f)) - -# division by fun - -function /(c::Fun,f::Fun) - d=domain(f) - @assert domain(c) == d - cd = canonicaldomain(f) - if typeof(d)!=typeof(cd) - # project first to simplify - return setdomain(setdomain(c,cd)/setdomain(f,cd),d) - end - - r=roots(f) - tol=10eps(promote_type(cfstype(c),cfstype(f))) - if length(r)==0 || norm(c.(r))$op(f),components(f)),PiecewiseSpace) - - # We remove the MappedSpace - # function $op{MS<:MappedSpace}(f::Fun{MS}) - # g=exp(Fun(f.coefficients,space(f).space)) - # Fun(g.coefficients,MappedSpace(domain(f),space(g))) - # end - function $op(fin::Fun{S,T}) where {S,T} - f=setcanonicaldomain(fin) # removes possible issues with roots - - xmax,opfxmax,opmax=specialfunctionnormalizationpoint($op,$growth,f) - # we will assume the result should be smooth on the domain - # even if f is not - # This supports Line/Rays - D=Derivative(domain(f)) - B=Evaluation(domainspace(D),xmax) - u=\([B,eval($L)],Any[opfxmax,eval($R)];tolerance=eps(T)*opmax) - - setdomain(u,domain(fin)) - end - end -end - - - -for (op,ODE,RHS,growth) in ((:(erf),"f'*D^2+(2f*f'^2-f'')*D","0",:(imag)), - (:(erfi),"f'*D^2-(2f*f'^2+f'')*D","0",:(real)), - (:(sin),"f'*D^2-f''*D+f'^3","0",:(imag)), - (:(cos),"f'*D^2-f''*D+f'^3","0",:(imag)), - (:(sinh),"f'*D^2-f''*D-f'^3","0",:(real)), - (:(cosh),"f'*D^2-f''*D-f'^3","0",:(real)), - (:(airyai),"f'*D^2-f''*D-f*f'^3","0",:(imag)), - (:(airybi),"f'*D^2-f''*D-f*f'^3","0",:(imag)), - (:(airyaiprime),"f'*D^2-f''*D-f*f'^3","airyai(f)*f'^3",:(imag)), - (:(airybiprime),"f'*D^2-f''*D-f*f'^3","airybi(f)*f'^3",:(imag))) - L,R = Meta.parse(ODE),Meta.parse(RHS) - @eval begin - function $op(fin::Fun{S,T}) where {S,T} - f=setcanonicaldomain(fin) - - g=chop($growth(f),eps(T)) - xmin = isempty(g.coefficients) ? leftendpoint(domain(g)) : argmin(g) - xmax = isempty(g.coefficients) ? rightendpoint(domain(g)) : argmax(g) - opfxmin,opfxmax = $op(f(xmin)),$op(f(xmax)) - opmax = maximum(abs,(opfxmin,opfxmax)) - while opmax≤10eps(T) || abs(f(xmin)-f(xmax))≤10eps(T) - xmin,xmax = rand(domain(f)),rand(domain(f)) - opfxmin,opfxmax = $op(f(xmin)),$op(f(xmax)) - opmax = maximum(abs,(opfxmin,opfxmax)) - end - D=Derivative(space(f)) - B=[Evaluation(space(f),xmin),Evaluation(space(f),xmax)] - u=\([B;eval($L)],[opfxmin;opfxmax;eval($R)];tolerance=10eps(T)*opmax) - - setdomain(u,domain(fin)) - end - end -end - -erfc(f::Fun) = 1-erf(f) - - -exp2(f::Fun) = exp(log(2)*f) -exp10(f::Fun) = exp(log(10)*f) -log2(f::Fun) = log(f)/log(2) -log10(f::Fun) = log(f)/log(10) - -##TODO: the spacepromotion doesn't work for tan/tanh for a domain including zeros of cos/cosh inside. -tan(f::Fun) = sin(f)/cos(f) #This is inaccurate, but allows space promotion via division. -tanh(f::Fun) = sinh(f)/cosh(f) #This is inaccurate, but allows space promotion via division. - -for (op,oprecip,opinv,opinvrecip) in ((:(sin),:(csc),:(asin),:(acsc)), - (:(cos),:(sec),:(acos),:(asec)), - (:(tan),:(cot),:(atan),:(acot)), - (:(sinh),:(csch),:(asinh),:(acsch)), - (:(cosh),:(sech),:(acosh),:(asech)), - (:(tanh),:(coth),:(atanh),:(acoth))) - @eval begin - $oprecip(f::Fun) = 1/$op(f) - $opinvrecip(f::Fun) = $opinv(1/f) - end -end - -rad2deg(f::Fun) = 180*f/π -deg2rad(f::Fun) = π*f/180 - -for (op,opd,opinv,opinvd) in ((:(sin),:(sind),:(asin),:(asind)), - (:(cos),:(cosd),:(acos),:(acosd)), - (:(tan),:(tand),:(atan),:(atand)), - (:(sec),:(secd),:(asec),:(asecd)), - (:(csc),:(cscd),:(acsc),:(acscd)), - (:(cot),:(cotd),:(acot),:(acotd))) - @eval begin - $opd(f::Fun) = $op(deg2rad(f)) - $opinvd(f::Fun) = rad2deg($opinv(f)) - end -end - -#Won't get the zeros exactly 0 anyway so at least this way the length is smaller. -sinpi(f::Fun) = sin(π*f) -cospi(f::Fun) = cos(π*f) - -function airy(k::Number,f::Fun) - if k == 0 - airyai(f) - elseif k == 1 - airyaiprime(f) - elseif k == 2 - airybi(f) - elseif k == 3 - airybiprime(f) - else - error("invalid argument") - end -end - -besselh(ν,k::Integer,f::Fun) = k == 1 ? hankelh1(ν,f) : k == 2 ? hankelh2(ν,f) : throw(Base.Math.AmosException(1)) - -for jy in ("j","y"), ν in (0,1) - bjy = Symbol(string("bessel",jy)) - bjynu = Meta.parse(string("SpecialFunctions.bessel",jy,ν)) - @eval begin - $bjynu(f::Fun) = $bjy($ν,f) - end -end - -## Miscellaneous -for op in (:(expm1),:(log1p),:(lfact),:(sinc),:(cosc), - :(erfinv),:(erfcinv),:(beta),:(lbeta), - :(eta),:(zeta),:(gamma),:(lgamma), - :(polygamma),:(invdigamma),:(digamma),:(trigamma)) - @eval begin - $op(f::Fun{S,T}) where {S,T}=Fun($op ∘ f,domain(f)) - end -end - - -## <,≤,>,≥ - -for op in (:<,:>) - @eval begin - function $op(f::Fun,c::Number) - if length(roots(f-c))==0 - $op(first(f),c) - else - false - end - end - function $op(c::Number,f::Fun) - if length(roots(f-c))==0 - $op(c,first(f)) - else - false - end - end - end -end - - - -for op in (:(<=),:(>=)) - @eval begin - function $op(f::Fun,c::Number) - rts=roots(f-c) - if length(rts)==0 - $op(first(f),c) - elseif length(rts)==1 - if isapprox(rts[1],leftendpoint(domain(f))) || isapprox(rts[1],rightendpoint(domain(f))) - $op(f(fromcanonical(f,0.)),c) - else - error("Implement for mid roots") - end - elseif length(rts)==2 - if isapprox(rts[1],leftendpoint(domain(f))) && isapprox(rts[2],rightendpoint(domain(f))) - $op(f(fromcanonical(f,0.)),c) - else - error("Implement for mid roots") - end - else - error("Implement for mid roots") - end - end - function $op(c::Number,f::Fun) - rts=sort(roots(f-c)) - if length(rts)==0 - $op(c,first(f)) - elseif length(rts)==1 - if isapprox(rts[1],leftendpoint(domain(f))) || isapprox(rts[1],leftendpoint(domain(f))) - $op(c,f(fromcanonical(f,0.))) - else - error("Implement for mid roots") - end - elseif length(rts)==2 - if isapprox(rts[1],leftendpoint(domain(f))) && isapprox(rts[2],leftendpoint(domain(f))) - $op(c,f(fromcanonical(f,0.))) - else - error("Implement for mid roots") - end - else - error("Implement for mid roots") - end - end - end -end - -/(c::Number,f::Fun{S}) where {S<:PiecewiseSpace} = Fun(map(f->c/f,components(f)),PiecewiseSpace) -^(f::Fun{S},c::Integer) where {S<:PiecewiseSpace} = Fun(map(f->f^c,components(f)),PiecewiseSpace) -^(f::Fun{S},c::Number) where {S<:PiecewiseSpace} = Fun(map(f->f^c,components(f)),PiecewiseSpace) - -for OP in (:abs,:sign,:log,:angle) - @eval begin - $OP(f::Fun{<:PiecewiseSpace{<:Any,<:Any,<:Real},<:Real}) = - Fun(map($OP,components(f)),PiecewiseSpace) - $OP(f::Fun{<:PiecewiseSpace{<:Any,<:Domain{<:Number}}}) = - Fun(map($OP,components(f)),PiecewiseSpace) - end -end - -## Special Multiplication -for f in (:+, :-, :*, :exp, :sin, :cos) - @eval $f(M::Multiplication) = Multiplication($f(M.f), domainspace(M)) -end - -for f in (:+, :-, :*, :/, :\) - @eval begin - $f(M::Multiplication, c::Number) = Multiplication($f(M.f, c), domainspace(M)) - $f(c::Number, M::Multiplication) = Multiplication($f(c, M.f), domainspace(M)) - end -end - -## ConstantSpace and PointSpace default overrides - -for SP in (:ConstantSpace,:PointSpace) - for OP in (:abs,:sign,:exp,:sqrt,:angle) - @eval begin - $OP(z::Fun{<:$SP,<:Complex}) = Fun(space(z),$OP.(coefficients(z))) - $OP(z::Fun{<:$SP,<:Real}) = Fun(space(z),$OP.(coefficients(z))) - $OP(z::Fun{<:$SP}) = Fun(space(z),$OP.(coefficients(z))) - end - end - - # we need to pad coefficients since 0^0 == 1 - for OP in (:^,) - @eval begin - function $OP(z::Fun{<:$SP},k::Integer) - k ≠ 0 && return Fun(space(z),$OP.(coefficients(z),k)) - Fun(space(z),$OP.(pad(coefficients(z),dimension(space(z))),k)) - end - function $OP(z::Fun{<:$SP},k::Number) - k ≠ 0 && return Fun(space(z),$OP.(coefficients(z),k)) - Fun(space(z),$OP.(pad(coefficients(z),dimension(space(z))),k)) - end - end - end -end - -for OP in (:<,:(Base.isless),:(<=),:>,:(>=)) - @eval begin - $OP(a::Fun{CS},b::Fun{CS}) where {CS<:ConstantSpace} = $OP(convert(Number,a),Number(b)) - $OP(a::Fun{CS},b::Number) where {CS<:ConstantSpace} = $OP(convert(Number,a),b) - $OP(a::Number,b::Fun{CS}) where {CS<:ConstantSpace} = $OP(a,convert(Number,b)) - end -end - -for OP in (:(Base.max),:(Base.min)) - @eval begin - $OP(a::Fun{CS1,T},b::Fun{CS2,V}) where {CS1<:ConstantSpace,CS2<:ConstantSpace,T<:Real,V<:Real} = - Fun($OP(Number(a),Number(b)),space(a) ∪ space(b)) - $OP(a::Fun{CS,T},b::Real) where {CS<:ConstantSpace,T<:Real} = - Fun($OP(Number(a),b),space(a)) - $OP(a::Real,b::Fun{CS,T}) where {CS<:ConstantSpace,T<:Real} = - Fun($OP(a,Number(b)),space(b)) - end -end - -# from DualNumbers -for (funsym, exp) in Calculus.symbolic_derivatives_1arg() - funsym == :abs && continue - funsym == :sign && continue - funsym == :exp && continue - funsym == :sqrt && continue - @eval begin - $(funsym)(z::Fun{CS,T}) where {CS<:ConstantSpace,T<:Real} = - Fun($(funsym)(Number(z)),space(z)) - $(funsym)(z::Fun{CS,T}) where {CS<:ConstantSpace,T<:Complex} = - Fun($(funsym)(Number(z)),space(z)) - $(funsym)(z::Fun{CS}) where {CS<:ConstantSpace} = - Fun($(funsym)(Number(z)),space(z)) - end -end - -# Roots - -for op in (:(argmax),:(argmin)) - @eval begin - function $op(f::Fun{S,T}) where {S<:RealSpace,T<:Real} - # need to check for zero as extremal_args is not defined otherwise - iszero(f) && return leftendpoint(domain(f)) - # the following avoids warning when differentiate(f)==0 - pts = extremal_args(f) - # the extra real avoids issues with complex round-off - pts[$op(real(f.(pts)))] - end - - function $op(f::Fun{S,T}) where {S,T} - # need to check for zero as extremal_args is not defined otherwise - iszero(f) && return leftendpoint(domain(f)) - # the following avoids warning when differentiate(f)==0 - pts = extremal_args(f) - fp=f.(pts) - @assert norm(imag(fp))<100eps() - pts[$op(real(fp))] - end - end -end - -for op in (:(findmax),:(findmin)) - @eval begin - function $op(f::Fun{S,T}) where {S<:RealSpace,T<:Real} - # the following avoids warning when differentiate(f)==0 - pts = extremal_args(f) - ext,ind = $op(f.(pts)) - ext,pts[ind] - end - end -end - -extremal_args(f::Fun{S}) where {S<:PiecewiseSpace} = cat(1,[extremal_args(fp) for fp in components(f)]..., dims=1) - -function extremal_args(f::Fun) - d = domain(f) - - dab = convert(Vector{Number}, collect(components(∂(domain(f))))) - if ncoefficients(f) <=2 #TODO this is only relevant for Polynomial bases - dab - else - [dab;roots(differentiate(f))] - end -end - -for op in (:(maximum),:(minimum),:(extrema)) - @eval function $op(f::Fun{S,T}) where {S<:RealSpace,T<:Real} - pts = iszero(f') ? [leftendpoint(domain(f))] : extremal_args(f) - - $op(f.(pts)) - end -end - - -for op in (:(maximum),:(minimum)) - @eval begin - function $op(::typeof(abs), f::Fun{S,T}) where {S<:RealSpace,T<:Real} - pts = iszero(f') ? [leftendpoint(domain(f))] : extremal_args(f) - $op(f.(pts)) - end - function $op(::typeof(abs), f::Fun) - # complex spaces/types can have different extrema - pts = extremal_args(abs(f)) - $op(f.(pts)) - end - $op(f::Fun{PiecewiseSpace{SV,DD,RR},T}) where {SV,DD<:UnionDomain,RR<:Real,T<:Real} = - $op(map($op,components(f))) - $op(::typeof(abs), f::Fun{PiecewiseSpace{SV,DD,RR},T}) where {SV,DD<:UnionDomain,RR<:Real,T<:Real} = - $op(abs, map(g -> $op(abs, g),components(f))) - end -end - - -extrema(f::Fun{PiecewiseSpace{SV,DD,RR},T}) where {SV,DD<:UnionDomain,RR<:Real,T<:Real} = - mapreduce(extrema,(x,y)->extrema([x...;y...]),components(f)) - -function complexroots end - -function roots(f::Fun{P}) where P<:PiecewiseSpace - rts=mapreduce(roots,vcat,components(f)) - k=1 - while k < length(rts) - if isapprox(rts[k],rts[k+1]) - rts=rts[[1:k;k+2:end]] - else - k+=1 - end - end - - rts -end - -roots(f::Fun{S,T}) where {S<:PointSpace,T} = space(f).points[values(f) .== 0] - - - -# -# These formulæ, appearing in Eq. (2.5) of: -# -# A.-K. Kassam and L. N. Trefethen, Fourth-order time-stepping for stiff PDEs, SIAM J. Sci. Comput., 26:1214--1233, 2005, -# -# are derived to implement ETDRK4 in double precision without numerical instability from cancellation. -# - -expα_asy(x) = (exp(x)*(4-3x+x^2)-4-x)/x^3 -expβ_asy(x) = (exp(x)*(x-2)+x+2)/x^3 -expγ_asy(x) = (exp(x)*(4-x)-4-3x-x^2)/x^3 - -# TODO: General types - -expα_taylor(x::Union{Float64,ComplexF64}) = @evalpoly(x,1/6,1/6,3/40,1/45,5/1008,1/1120,7/51840,1/56700,1/492800,1/4790016,11/566092800,1/605404800,13/100590336000,1/106748928000,1/1580833013760,1/25009272288000,17/7155594141696000,1/7508956815360000) -expβ_taylor(x::Union{Float64,ComplexF64}) = @evalpoly(x,1/6,1/12,1/40,1/180,1/1008,1/6720,1/51840,1/453600,1/4435200,1/47900160,1/566092800,1/7264857600,1/100590336000,1/1494484992000,1/23712495206400,1/400148356608000,1/7155594141696000,1/135161222676480000) -expγ_taylor(x::Union{Float64,ComplexF64}) = @evalpoly(x,1/6,0/1,-1/120,-1/360,-1/1680,-1/10080,-1/72576,-1/604800,-1/5702400,-1/59875200,-1/691891200,-1/8717829120,-1/118879488000,-1/1743565824000,-1/27360571392000,-1/457312407552000,-1/8109673360588800) - -expα(x::Float64) = abs(x) < 17/16 ? expα_taylor(x) : expα_asy(x) -expβ(x::Float64) = abs(x) < 19/16 ? expβ_taylor(x) : expβ_asy(x) -expγ(x::Float64) = abs(x) < 15/16 ? expγ_taylor(x) : expγ_asy(x) - -expα(x::ComplexF64) = abs2(x) < (17/16)^2 ? expα_taylor(x) : expα_asy(x) -expβ(x::ComplexF64) = abs2(x) < (19/16)^2 ? expβ_taylor(x) : expβ_asy(x) -expγ(x::ComplexF64) = abs2(x) < (15/16)^2 ? expγ_taylor(x) : expγ_asy(x) - -expα(x) = expα_asy(x) -expβ(x) = expβ_asy(x) -expγ(x) = expγ_asy(x) - - -for f in (:(exp),:(expm1),:expα,:expβ,:expγ) - @eval $f(op::Operator) = OperatorFunction(op,$f) -end - diff --git a/src/testing.jl b/src/testing.jl deleted file mode 100644 index 9819c467..00000000 --- a/src/testing.jl +++ /dev/null @@ -1,236 +0,0 @@ -## Testing -# These routines are for the unit tests - -using Test - -## Supports @test_approx_eq - - -Test.approx_full(f::Fun) = f - - - -## Spaces Tests - - -function testtransforms(S::Space;minpoints=1,invertibletransform=true) - # transform tests - v = rand(max(minpoints,min(100,ApproxFunBase.dimension(S)))) - plan = plan_transform(S,v) - @test transform(S,v) == plan*v - - iplan = plan_itransform(S,v) - @test itransform(S,v) == iplan*v - - if invertibletransform - for k=max(1,minpoints):min(5,dimension(S)) - v = [zeros(k-1);1.0] - @test transform(S,itransform(S,v)) ≈ v - end - - @test transform(S,itransform(S,v)) ≈ v - @test itransform(S,transform(S,v)) ≈ v - end -end - -function testcalculus(S::Space;haslineintegral=true,hasintegral=true) - for k=1:min(5,dimension(S)) - v = [zeros(k-1);1.0] - f = Fun(S,v) - @test abs(DefiniteIntegral()*f-sum(f)) < 100eps() - if haslineintegral - @test DefiniteLineIntegral()*f ≈ linesum(f) - end - @test norm(Derivative()*f-f') < 100eps() - if hasintegral - @test norm(differentiate(integrate(f))-f) < 100eps() - @test norm(differentiate(cumsum(f))-f) < 200eps() - @test norm(first(cumsum(f))) < 100eps() - end - end -end - -function testmultiplication(spa,spb) - for k=1:10 - a = Fun(spa,[zeros(k-1);1.]) - M = Multiplication(a,spb) - pts = ApproxFunBase.checkpoints(rangespace(M)) - for j=1:10 - b = Fun(spb,[zeros(j-1);1.]) - @test (M*b).(pts) ≈ a.(pts).*b.(pts) - end - end -end - -function testspace(S::Space; - minpoints=1,invertibletransform=true,haslineintegral=true,hasintegral=true, - dualspace=S) - testtransforms(S;minpoints=minpoints,invertibletransform=invertibletransform) - testcalculus(S;haslineintegral=haslineintegral,hasintegral=hasintegral) - if dualspace ≠ nothing - testmultiplication(dualspace,S) - end -end - - - - - -## Operator Tests - -function backend_testfunctional(A) - @test rowstart(A,1) ≥ 1 - @test colstop(A,1) ≤ 1 - @test bandwidth(A,1) ≤ 0 - @test blockbandwidth(A,1) ≤ 0 - - B=A[1:10] - @test eltype(B) == eltype(A) - for k=1:5 - @test B[k] ≈ A[k] - @test isa(A[k],eltype(A)) - end - @test isa(A[1,1:10],Vector) - @test isa(A[1:1,1:10],AbstractMatrix) - @test B ≈ A[1,1:10] - @test transpose(B) ≈ A[1:1,1:10] - @test B[3:10] ≈ A[3:10] - @test B ≈ [A[k] for k=1:10] - - - - co=cache(A) - @test co[1:10] ≈ A[1:10] - @test co[1:10] ≈ A[1:10] - @test co[20:30] ≈ A[1:30][20:30] ≈ A[20:30] -end - -# Check that the tests pass after conversion as well -function testfunctional(A::Operator{T}) where T<:Real - backend_testfunctional(A) - backend_testfunctional(Operator{Float64}(A)) - backend_testfunctional(Operator{Float32}(A)) - backend_testfunctional(Operator{ComplexF64}(A)) -end - -function testfunctional(A::Operator{T}) where T<:Complex - backend_testfunctional(A) - backend_testfunctional(Operator{ComplexF32}(A)) - backend_testfunctional(Operator{ComplexF64}(A)) -end - -function backend_testinfoperator(A) - @test isinf(size(A,1)) - @test isinf(size(A,2)) - B=A[1:5,1:5] - @test eltype(B) == eltype(A) - - for k=1:5,j=1:5 - @test B[k,j] ≈ A[k,j] - @test isa(A[k,j],eltype(A)) - end - - @test A[1:5,1:5][2:5,1:5] ≈ A[2:5,1:5] - @test A[1:5,2:5] ≈ A[1:5,1:5][:,2:end] - @test A[1:10,1:10][5:10,5:10] ≈ [A[k,j] for k=5:10,j=5:10] - @test A[1:10,1:10][5:10,5:10] ≈ A[5:10,5:10] - @test A[1:30,1:30][20:30,20:30] ≈ A[20:30,20:30] - - @test Matrix(A[Block(1):Block(3),Block(1):Block(3)]) ≈ Matrix(A[blockstart(rangespace(A),1):blockstop(rangespace(A),3),blockstart(domainspace(A),1):blockstop(domainspace(A),3)]) - @test Matrix(A[Block(3):Block(4),Block(2):Block(4)]) ≈ Matrix(A[blockstart(rangespace(A),3):blockstop(rangespace(A),4),blockstart(domainspace(A),2):blockstop(domainspace(A),4)]) - - for k=1:10 - @test isfinite(colstart(A,k)) && colstart(A,k) > 0 - @test isfinite(rowstart(A,k)) && colstart(A,k) > 0 - end - - co=cache(A) - @test co[1:10,1:10] ≈ A[1:10,1:10] - @test co[1:10,1:10] ≈ A[1:10,1:10] - @test co[20:30,20:30] ≈ A[1:30,1:30][20:30,20:30] - - let C=cache(A) - resizedata!(C,5,35) - resizedata!(C,10,35) - @test C.data[1:10,1:C.datasize[2]] ≈ A[1:10,1:C.datasize[2]] - end -end - -# Check that the tests pass after conversion as well -function testinfoperator(A::Operator{T}) where T<:Real - backend_testinfoperator(A) - backend_testinfoperator(convert(Operator{Float64}, A)) - backend_testinfoperator(convert(Operator{Float32}, A)) - backend_testinfoperator(convert(Operator{ComplexF64}, A)) -end - -function testinfoperator(A::Operator{T}) where T<:Complex - backend_testinfoperator(A) - backend_testinfoperator(convert(Operator{ComplexF32}, A)) - backend_testinfoperator(convert(Operator{ComplexF64}, A)) -end - -function testraggedbelowoperator(A) - @test israggedbelow(A) - for k=1:20 - @test isfinite(colstop(A,k)) - end - - R = RaggedMatrix(view(A, 1:10, 1:min(10,size(A,2)))) - for j=1:size(R,2) - @test colstop(R,j) == min(colstop(A,j),10) - end - - testinfoperator(A) -end - -function testbandedbelowoperator(A) - @test isbandedbelow(A) - @test isfinite(bandwidth(A,1)) - testraggedbelowoperator(A) - - for k=1:10 - @test colstop(A,k) ≤ max(0,k + bandwidth(A,1)) - end -end - - -function testalmostbandedoperator(A) - testbandedbelowoperator(A) -end - -function testbandedoperator(A) - @test isbanded(A) - @test isfinite(bandwidth(A,2)) - testalmostbandedoperator(A) - for k=1:10 - @test rowstop(A,k) ≤ k + bandwidth(A,2) - end - - @test isa(A[1:10,1:10],BandedMatrix) -end - - -function testblockbandedoperator(A) - @test isblockbanded(A) - testraggedbelowoperator(A) - @test isfinite(blockbandwidth(A,2)) - @test isfinite(blockbandwidth(A,1)) - - - if -blockbandwidth(A,1) ≤ blockbandwidth(A,2) - for K=1:10 - @test K - blockbandwidth(A,2) ≤ blockcolstop(A,K).n[1] ≤ K + blockbandwidth(A,1) < ∞ - @test K - blockbandwidth(A,1) ≤ blockrowstop(A,K).n[1] ≤ K + blockbandwidth(A,2) < ∞ - end - end -end - -function testbandedblockbandedoperator(A) - @test isbandedblockbanded(A) - testblockbandedoperator(A) - @test isfinite(subblockbandwidth(A,1)) - @test isfinite(subblockbandwidth(A,2)) - - @test isa(A[Block.(1:4),Block.(1:4)], BandedBlockBandedMatrix) -end diff --git a/test/runtests.jl b/test/runtests.jl index 09c91dc9..6c9a7539 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,33 +1,31 @@ -using ApproxFunBase, LinearAlgebra, Random, Test - import ApproxFunBase: Infinity, ∞ - -@testset "Helper" begin - @testset "interlace" begin - @test ApproxFunBase.interlace!([-1.0],0) == [-1.0] - @test ApproxFunBase.interlace!([1.0,2.0],0) == [2.0,1.0] - @test ApproxFunBase.interlace!([1,2,3],0) == [2,1,3] - @test ApproxFunBase.interlace!([1,2,3,4],0) == [3,1,4,2] - - @test ApproxFunBase.interlace!([-1.0],1) == [-1.0] - @test ApproxFunBase.interlace!([1.0,2.0],1) == [1.0,2.0] - @test ApproxFunBase.interlace!([1,2,3],1) == [1,3,2] - @test ApproxFunBase.interlace!([1,2,3,4],1) == [1,3,2,4] - - @test ApproxFunBase.interlace(collect(6:10),collect(1:5)) == ApproxFunBase.interlace!(collect(1:10),0) - @test ApproxFunBase.interlace(collect(1:5),collect(6:10)) == ApproxFunBase.interlace!(collect(1:10),1) - end - - @testset "Iterators" begin - @test cache(ApproxFunBase.BlockInterlacer((1:∞,[2],[2])))[1:6] == - [(1,1),(2,1),(2,2),(3,1),(3,2),(1,2)] - - @test collect(ApproxFunBase.BlockInterlacer(([2],[2],[2]))) == - [(1,1),(1,2),(2,1),(2,2),(3,1),(3,2)] - end +using ApproxFunBase, ContinuumArrays, OrthogonalPolynomialsQuasi, LinearAlgebra, Random, Test +import ApproxFunBase: Infinity, ∞ + +@testset "Spline" begin + L = LinearSpline(range(0,1; length=1000)) + f = Fun(exp, L) + @test f(0.1) ≈ exp(0.1) atol=1E-2 + @test_throws BoundsError f(1.1) + @test space(f') isa HeavisideSpline + @test f'(0.1) ≈ exp(0.1) atol=1E-2 + + @test (f+f)(0.1) ≈ 2exp(0.1) atol=1E-2 + @test (2f)(0.1) ≈ 2exp(0.1) atol=1E-2 +end - # TODO: Tensorizer tests +@testset "Chebyshev" begin + f = Fun(exp, Chebyshev()) + @test f(0.1) ≈ exp(0.1) + @test f'(0.1) ≈ exp(0.1) + @test space(f') isa ChebyshevU + @test f''(0.1) ≈ exp(0.1) + @test space(f'') == Ultraspherical(2) + + @test (f+f)(0.1) ≈ 2exp(0.1) + @test (2f)(0.1) ≈ 2exp(0.1) end + @testset "Domain" begin @test 0.45-0.65im ∉ Segment(-1,1) From ab069150fe2a89c16994f29f41b2f489008862ff Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Fri, 10 Jul 2020 09:53:58 +0100 Subject: [PATCH 2/9] update Project --- Project.toml | 2 +- test/runtests.jl | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index 79eafac7..aa7dc552 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ApproxFunBase" uuid = "fbd15aa5-315a-5a7d-a8a4-24992e37be05" -version = "0.3.5" +version = "0.4.0" [deps] AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c" diff --git a/test/runtests.jl b/test/runtests.jl index 54e93c8b..3447dc52 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -29,8 +29,6 @@ end @testset "Domain" begin @test 0.45-0.65im ∉ Segment(-1,1) - @test ApproxFunBase.AnySegment() == ApproxFunBase.AnySegment() - @test ApproxFunBase.dimension(ChebyshevInterval()) == 1 @test ApproxFunBase.dimension(ChebyshevInterval()^2) == 2 @test ApproxFunBase.dimension(ChebyshevInterval()^3) == 3 From ab3df24d292bdf4e0bc468fc7a7b3001ce286d44 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Sat, 11 Jul 2020 09:05:25 +0100 Subject: [PATCH 3/9] auto-pad coefficients --- Project.toml | 14 +++++++------- src/Fun.jl | 23 +++++++++++++++++------ test/MatrixTest.jl | 20 -------------------- test/runtests.jl | 21 ++++++--------------- 4 files changed, 30 insertions(+), 48 deletions(-) delete mode 100644 test/MatrixTest.jl diff --git a/Project.toml b/Project.toml index aa7dc552..784ab896 100644 --- a/Project.toml +++ b/Project.toml @@ -32,22 +32,22 @@ ToeplitzMatrices = "c751599d-da0a-543b-9d20-d0a503d91d24" [compat] AbstractFFTs = "0.4, 0.5" -BandedMatrices = "0.14, 0.15" -BlockArrays = "0.11, 0.12" -BlockBandedMatrices = "0.7, 0.8" +BandedMatrices = "0.15" +BlockArrays = "0.12" +BlockBandedMatrices = "0.8" Calculus = "0.5" ContinuumArrays = "0.2.2" DSP = "0.6" -DomainSets = "0.3, 0.4" +DomainSets = "0.5" DualNumbers = "0.6.2" FFTW = "0.3, 1" FastGaussQuadrature = "0.4" FillArrays = "0.8" -InfiniteArrays = "0.4, 0.5, 0.6, 0.7" +InfiniteArrays = "0.7" InfiniteLinearAlgebra = "0.3.1" IntervalSets = "0.5" -LazyArrays = "0.14, 0.15, 0.16" -LowRankApprox = "0.2, 0.3, 0.4" +LazyArrays = "0.16" +LowRankApprox = "0.4" SpecialFunctions = "0.8, 0.9, 0.10" StaticArrays = "0.12" ToeplitzMatrices = "0.6" diff --git a/src/Fun.jl b/src/Fun.jl index 3fd389c6..77969d0d 100644 --- a/src/Fun.jl +++ b/src/Fun.jl @@ -15,14 +15,22 @@ mutable struct Fun{S,T,VT} <: Function end const VFun{S,T} = Fun{S,T,Vector{T}} -Fun(sp::Space, coeff::AbstractVector) = Fun{typeof(sp),eltype(coeff),typeof(coeff)}(sp, coeff) + +_pad(c, _, n) = [c; zeros(eltype(c), n-length(c))] +_pad(c, ::Infinity, ::Infinity) = c +pad(c, n::Integer) = _pad(c, length(c), n) +function Fun(sp::Space, c::AbstractVector) + coeff = pad(c, size(sp,2)) + Fun{typeof(sp),eltype(coeff),typeof(coeff)}(sp, coeff) +end Fun() = Fun(identity) -Fun(d::Domain) = Fun(identity, d) -Fun(d::Space) = Fun(identity, d) +Fun(d) = Fun(identity, d) Fun(v::AbstractQuasiVector) = Fun(arguments(v)...) Fun(f::Function, S::Space) = Fun(S, S \ f.(axes(S,1))) +Fun(f::Function, d) = Fun(f, Space(d)) + function Fun(sp::Space, v::AbstractVector{Any}) if isempty(v) @@ -46,6 +54,7 @@ coefficients(f::Fun) = f.coefficients vec(f::Fun) = f.space * coefficients(f) coefficients(f::Fun, msp::Space) = msp \ vec(f) coefficients(c::Number,sp::Space) = coefficients(Fun(c,sp)) +Fun(f::Fun, S::Space) = Fun(S, coefficients(f, S)) ##Convert routines @@ -208,21 +217,23 @@ copy(f::Fun) = Fun(space(f),copy(f.coefficients)) ## Addition and multiplication + + for op in (:+,:-) @eval begin function $op(f::Fun, g::Fun) if space(f) == space(g) Fun(space(f), ($op)(coefficients(f), coefficients(g))) else - m = union(f.space,g.space) - if isa(m,NoSpace) + m = broadcastspace($op, f.space, g.space) + if m isa NoSpace error("Cannot "*string($op)*" because no space is the union of "*string(typeof(f.space))*" and "*string(typeof(g.space))) end $op(Fun(f,m), Fun(g,m)) # convert to same space end end $op(f::Fun{S,T},c::T) where {S,T<:Number} = c==0 ? f : $op(f,Fun(c)) - $op(f::Fun,c::Number) = $op(f,Fun(c)) + $op(f::Fun,c::Number) = $op(f,Fun(c,space(f))) $op(f::Fun,c::UniformScaling) = $op(f,c.λ) $op(c::UniformScaling,f::Fun) = $op(c.λ,f) end diff --git a/test/MatrixTest.jl b/test/MatrixTest.jl deleted file mode 100644 index 5c869010..00000000 --- a/test/MatrixTest.jl +++ /dev/null @@ -1,20 +0,0 @@ -using ApproxFunBase, Test - import ApproxFunBase: Block - - -@testset "RaggedMatrix" begin - cols=Int[rand(1:k+2) for k=1:5] - B=ApproxFunBase.rrand(Float64,maximum(cols),cols) - cols=Int[rand(1:k+2) for k=1:size(B,1)] - A=ApproxFunBase.rrand(Float64,maximum(cols),cols) - @test Matrix(A)*Matrix(B) ≈ Matrix(A*B) - - @test ApproxFunBase.RaggedMatrix(B) === B - @test ApproxFunBase.RaggedMatrix{Float64}(B) === B - @test Matrix(ApproxFunBase.RaggedMatrix{ComplexF64}(B)) == Matrix{ComplexF64}(Matrix(B)) - - B = ApproxFunBase.brand(10,10,2,3) - @test Matrix(B) == Matrix(ApproxFunBase.RaggedMatrix(B)) - @test ApproxFunBase.RaggedMatrix(B) == ApproxFunBase.RaggedMatrix{Float64}(B) - @test ApproxFunBase.RaggedMatrix(ApproxFunBase.BandedMatrix{ComplexF64}(B)) == ApproxFunBase.RaggedMatrix{ComplexF64}(B) -end diff --git a/test/runtests.jl b/test/runtests.jl index 3447dc52..f52ffc5c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -14,6 +14,12 @@ import ApproxFunBase: Infinity, ∞ end @testset "Chebyshev" begin + f = Fun(Chebyshev(), [1.,2.,3.]) + @test f(0.1) ≈ 1 + 2*0.1 + 3*cos(2acos(0.1)) + + @test Fun(Chebyshev(),Float64[]).([0.,1.]) ≈ [0.,0.] + @test Fun(Chebyshev(),[])(0.) ≈ 0. + f = Fun(exp, Chebyshev()) @test f(0.1) ≈ exp(0.1) @test f'(0.1) ≈ exp(0.1) @@ -25,21 +31,6 @@ end @test (2f)(0.1) ≈ 2exp(0.1) end - -@testset "Domain" begin - @test 0.45-0.65im ∉ Segment(-1,1) - - @test ApproxFunBase.dimension(ChebyshevInterval()) == 1 - @test ApproxFunBase.dimension(ChebyshevInterval()^2) == 2 - @test ApproxFunBase.dimension(ChebyshevInterval()^3) == 3 - - @test isambiguous(convert(ApproxFunBase.Point,ApproxFunBase.AnyDomain())) - @test isambiguous(ApproxFunBase.Point(ApproxFunBase.AnyDomain())) - - @test_skip ApproxFunBase.Point(NaN) == ApproxFunBase.Point(NaN) -end - -@time include("MatrixTest.jl") @time include("SpacesTest.jl") From 0538dd581dd9c3004330f6f9e11a99f5bd6f5736 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Sat, 16 Jan 2021 23:36:56 +0000 Subject: [PATCH 4/9] Update .travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f212a2a7..63955ab1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ os: - linux - osx julia: - - "1.3" - "1.5" - nightly matrix: From 2005459557fb835583d0daec4bf8595599cd63ab Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Thu, 21 Jan 2021 20:59:16 +0000 Subject: [PATCH 5/9] Update Project.toml --- Project.toml | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Project.toml b/Project.toml index 9595f713..3d04b3be 100644 --- a/Project.toml +++ b/Project.toml @@ -31,27 +31,27 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" ToeplitzMatrices = "c751599d-da0a-543b-9d20-d0a503d91d24" [compat] -AbstractFFTs = 0.4, 0.5, 1 -BandedMatrices = 0.16 -BlockArrays = 0.14 -BlockBandedMatrices = 0.10 -Calculus = 0.5 -ContinuumArrays = 0.2.2 -DSP = 0.6 -DomainSets = 0.5 -DualNumbers = 0.6.2 -FFTW = 0.3, 1 -FastGaussQuadrature = 0.4 -FillArrays = 0.11 -InfiniteArrays = 0.9 -InfiniteLinearAlgebra = 0.4 -IntervalSets = 0.5 -LazyArrays = 0.20 -LowRankApprox = 0.4 -SpecialFunctions = 0.8, 0.9, 0.10, 1 -StaticArrays = 0.12, 1 -ToeplitzMatrices = 0.6 -julia = 1.5 +AbstractFFTs = "0.4, 0.5, 1" +BandedMatrices = "0.16" +BlockArrays = "0.14" +BlockBandedMatrices = "0.10" +Calculus = "0.5" +ContinuumArrays = "0.4" +DSP = "0.6" +DomainSets = "0.4" +DualNumbers = "0.6.2" +FFTW = "0.3, 1" +FastGaussQuadrature = "0.4" +FillArrays = "0.11" +InfiniteArrays = "0.9" +InfiniteLinearAlgebra = "0.4" +IntervalSets = "0.5" +LazyArrays = "0.20" +LowRankApprox = "0.4" +SpecialFunctions = "0.8, 0.9, 0.10, 1" +StaticArrays = "0.12, 1" +ToeplitzMatrices = "0.6" +julia = "1.5" [extras] Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" From c68f486bef36fab63a1e9fc944df4d9a3c46b736 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Thu, 21 Jan 2021 21:39:31 +0000 Subject: [PATCH 6/9] import checkpoints --- src/ApproxFunBase.jl | 2 +- src/Fun.jl | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ApproxFunBase.jl b/src/ApproxFunBase.jl index 68c4cef2..ff29c814 100644 --- a/src/ApproxFunBase.jl +++ b/src/ApproxFunBase.jl @@ -40,7 +40,7 @@ import LinearAlgebra: BlasInt, BlasFloat, norm, ldiv!, mul!, det, eigvals, dot, import SparseArrays: blockdiag -import ContinuumArrays: AbstractQuasiMatrix, AbstractQuasiVector, AbstractQuasiArray, arguments +import ContinuumArrays: AbstractQuasiMatrix, AbstractQuasiVector, AbstractQuasiArray, arguments, checkpoints # import Arpack: eigs # we need to import all special functions to use Calculus.symbolic_derivatives_1arg diff --git a/src/Fun.jl b/src/Fun.jl index 88058352..73b5ee18 100644 --- a/src/Fun.jl +++ b/src/Fun.jl @@ -27,7 +27,13 @@ Fun() = Fun(identity) Fun(d) = Fun(identity, d) Fun(v::AbstractQuasiVector) = Fun(arguments(v)...) -Fun(f::Function, S::Space) = Fun(S, S \ f.(axes(S,1))) +function Fun(f::Function, S::Space) + if !applicable(f, checkpoints(S)[1]) + Fun(x -> f(x...), S) + else + Fun(S, S \ f.(axes(S,1))) + end +end Fun(f::Function, d) = Fun(f, Space(d)) From 387e021a5fa6c2b9341241f0799a9b668a85be62 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Tue, 26 Jan 2021 08:00:50 +0000 Subject: [PATCH 7/9] Update Project.toml --- Project.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 3d04b3be..73ac5ead 100644 --- a/Project.toml +++ b/Project.toml @@ -36,7 +36,7 @@ BandedMatrices = "0.16" BlockArrays = "0.14" BlockBandedMatrices = "0.10" Calculus = "0.5" -ContinuumArrays = "0.4" +ContinuumArrays = "0.4.2" DSP = "0.6" DomainSets = "0.4" DualNumbers = "0.6.2" @@ -54,6 +54,7 @@ ToeplitzMatrices = "0.6" julia = "1.5" [extras] +OrthogonalPolynomialsQuasi = "aa41a628-2c43-45df-899b-83ab96621781" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [targets] From acd48c56b048a414b7f98f0f2e9c2cffcb45efa5 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Tue, 29 Mar 2022 15:51:35 +0100 Subject: [PATCH 8/9] Update Project.toml --- Project.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 2421524d..7d0b742f 100644 --- a/Project.toml +++ b/Project.toml @@ -22,7 +22,6 @@ LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02" LazyBandedMatrices = "d7e5e226-e90b-4449-9968-0f923699bf6f" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" LowRankApprox = "898213cb-b102-5a47-900c-97e73b919f73" -OrthogonalPolynomialsQuasi = "aa41a628-2c43-45df-899b-83ab96621781" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" @@ -36,7 +35,7 @@ BandedMatrices = "0.16, 0.17" BlockArrays = "0.14, 0.15, 0.16" BlockBandedMatrices = "0.10, 0.11" Calculus = "0.5" -ContinuumArrays = "0.4.2" +ContinuumArrays = "0.10" DSP = "0.6, 0.7" DomainSets = "0.5" DualNumbers = "0.6.2" From f5c69d6bd9b058933ef5b755c724a88ca90f1d78 Mon Sep 17 00:00:00 2001 From: Sheehan Olver Date: Tue, 29 Mar 2022 16:23:28 +0100 Subject: [PATCH 9/9] some tests pass --- Project.toml | 4 ++-- src/Fun.jl | 2 +- test/runtests.jl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 7d0b742f..bb6fc5c1 100644 --- a/Project.toml +++ b/Project.toml @@ -26,7 +26,6 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" ToeplitzMatrices = "c751599d-da0a-543b-9d20-d0a503d91d24" [compat] @@ -55,6 +54,7 @@ julia = "1.7" [extras] ClassicalOrthogonalPolynomials = "b30e2e7b-c4ee-47da-9d5f-2c5c27239acd" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Random"] +test = ["Random", "ClassicalOrthogonalPolynomials", "Test"] diff --git a/src/Fun.jl b/src/Fun.jl index 73b5ee18..77fd3d21 100644 --- a/src/Fun.jl +++ b/src/Fun.jl @@ -17,7 +17,7 @@ end const VFun{S,T} = Fun{S,T,Vector{T}} _pad(c, _, n) = [c; zeros(eltype(c), n-length(c))] -_pad(c, ::Infinity, ::Infinity) = c +_pad(c, ::PosInfinity, ::PosInfinity) = c pad(c, n::Integer) = _pad(c, length(c), n) function Fun(sp::Space, c::AbstractVector) coeff = pad(c, size(sp,2)) diff --git a/test/runtests.jl b/test/runtests.jl index 5e1bb719..7ebf51bc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,5 @@ using ApproxFunBase, ContinuumArrays, ClassicalOrthogonalPolynomials, LinearAlgebra, Random, Test -import ApproxFunBase: Infinity, ∞ +import ApproxFunBase: ∞ @testset "Spline" begin L = LinearSpline(range(0,1; length=1000))