Skip to content

Commit fcf57d6

Browse files
authored
Spaces in Conversion, Multiplication and Evaluation wrapper (#384)
* spaces in Conversion, Multiplication and Evaluation wrapper * version bump to v0.7.75 * fix MultiplicationWrapper dispatch * bump version to v0.8.0 * rangespace in DerivativeWrapper
1 parent 2b3ebb0 commit fcf57d6

File tree

10 files changed

+126
-76
lines changed

10 files changed

+126
-76
lines changed

ApproxFunBaseTest/Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1414
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
1515

1616
[compat]
17-
ApproxFunBase = "0.5, 0.6, 0.7"
17+
ApproxFunBase = "0.5, 0.6, 0.7, 0.8"
1818
BandedMatrices = "0.16, 0.17"
1919
BlockArrays = "0.14, 0.15, 0.16"
2020
BlockBandedMatrices = "0.10, 0.11"

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "ApproxFunBase"
22
uuid = "fbd15aa5-315a-5a7d-a8a4-24992e37be05"
3-
version = "0.7.74"
3+
version = "0.8.0"
44

55
[deps]
66
AbstractFFTs = "621f4979-c628-5d54-868e-fcf4e3e8185c"

src/Operators/Operator.jl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -611,16 +611,20 @@ end
611611
# use this for wrapper operators that have the same spaces but
612612
# not necessarily the same entries or structure
613613
#
614-
macro wrapperspaces(Wrap, forwarddomain = true)
615-
fns = [:(ApproxFunBase.rangespace),:(ApproxFunBase.domain), :(ApproxFunBase.isconstop)]
614+
macro wrapperspaces(Wrap, forwarddomain = true, forwardrange = true)
615+
fns = [:(ApproxFunBase.isconstop)]
616616
if forwarddomain
617617
fns = [fns; :(ApproxFunBase.domainspace)]
618618
end
619+
if forwardrange
620+
fns = [fns; :(ApproxFunBase.rangespace)]
621+
end
619622
v = map(fns) do func
620623
:($func(D::$Wrap) = $func(D.op))
621624
end
622625
ret = quote
623626
$(v...)
627+
ApproxFunBase.domain(D::$Wrap) = domain(domainspace(D))
624628
end
625629

626630
esc(ret)
@@ -629,10 +633,10 @@ end
629633

630634
# use this for wrapper operators that have the same entries and same spaces
631635
#
632-
macro wrapper(Wrap, forwarddomain = true)
636+
macro wrapper(Wrap, forwarddomain = true, forwardrange = true)
633637
ret = quote
634638
ApproxFunBase.@wrappergetindex($Wrap)
635-
ApproxFunBase.@wrapperspaces($Wrap, $forwarddomain)
639+
ApproxFunBase.@wrapperspaces($Wrap, $forwarddomain, $forwardrange)
636640

637641
ApproxFunBase.iswrapper(::$Wrap) = true
638642
end
@@ -641,6 +645,8 @@ macro wrapper(Wrap, forwarddomain = true)
641645
esc(ret)
642646
end
643647

648+
unwrap(A::Operator) = iswrapper(A) ? A.op : A
649+
644650
## Standard Operators and linear algebra
645651

646652

src/Operators/banded/CalculusOperator.jl

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,24 @@ macro calculus_operator(Op)
1010
ConcOp = Symbol(:Concrete, Op)
1111
WrappOp = Symbol(Op, :Wrapper)
1212
DefaultOp = Symbol(:Default, Op)
13-
return esc(quote
13+
q = quote
1414
# The SSS, TTT are to work around #9312
1515
abstract type $Op{SSS,OT,TTT} <: ApproxFunBase.CalculusOperator{SSS,OT,TTT} end
1616

1717
struct $ConcOp{S<:Space,OT,T} <: $Op{S,OT,T}
1818
space::S # the domain space
1919
order::OT
2020
end
21-
struct $WrappOp{BT<:Operator,S<:Space,OT,T} <: $Op{S,OT,T}
21+
struct $WrappOp{BT<:Operator,S<:Space,R<:Space,OT,T} <: $Op{S,OT,T}
2222
op::BT
2323
order::OT
24-
space::S
24+
domainspace::S
25+
rangespace::R
2526
end
2627

27-
ApproxFunBase.@wrapper $WrappOp false # the false doesn't forward the domain space
28-
ApproxFunBase.domainspace(A::$WrappOp) = A.space
28+
ApproxFunBase.@wrapper $WrappOp false false
29+
ApproxFunBase.domainspace(A::$WrappOp) = A.domainspace
30+
ApproxFunBase.rangespace(A::$WrappOp) = A.rangespace
2931

3032
## Constructors
3133
$ConcOp(sp::Space,k) = $ConcOp{typeof(sp),typeof(k),ApproxFunBase.prectype(sp)}(sp,k)
@@ -35,12 +37,15 @@ macro calculus_operator(Op)
3537
$Op(sp::ApproxFunBase.UnsetSpace,k::Real) = $ConcOp(sp,k)
3638
$Op(sp::ApproxFunBase.UnsetSpace,k::Integer) = $ConcOp(sp,k)
3739

38-
function $DefaultOp(sp::Space,k)
40+
function $DefaultOp(sp::Space, k)
3941
csp=ApproxFunBase.canonicalspace(sp)
4042
if ApproxFunBase.conversion_type(csp,sp)==csp # Conversion(sp,csp) is not banded, or sp==csp
4143
error("Implement $(string($Op))($(string(sp)),$k)")
4244
end
43-
$WrappOp(ApproxFunBase.TimesOperator([$Op(csp,k),Conversion(sp,csp)]),k)
45+
O = $Op(csp,k)
46+
C = Conversion_maybeconcrete(sp, csp, Val(:forward))
47+
Top = ApproxFunBase.TimesOperator([O,C])
48+
$WrappOp(Top, sp, k, rangespace(O))
4449
end
4550

4651
$DefaultOp(d,k) = $Op(Space(d),k)
@@ -57,33 +62,31 @@ macro calculus_operator(Op)
5762
if T==eltype(D)
5863
D
5964
else
60-
$ConcOp{typeof(D.space),typeof(D.order),T}(D.space,D.order)
65+
$ConcOp{typeof(D.space),typeof(D.order),T}(D.space, D.order)
6166
end
6267
end
6368

64-
$WrappOp(op::Operator,d,order) =
65-
$WrappOp{typeof(op),typeof(d),typeof(order),eltype(op)}(op,order,d)
66-
$WrappOp(op::Operator,order) = $WrappOp(op, domainspace(op), order)
67-
$WrappOp(op::Operator) = $WrappOp(op,1)
69+
$WrappOp(op::Operator, order = 1, d = domainspace(op), r = rangespace(op)) =
70+
$WrappOp{typeof(op),typeof(d),typeof(r),typeof(order),eltype(op)}(op,order,d,r)
6871

6972
function Base.convert(::Type{Operator{T}},D::$WrappOp) where T
7073
if T==eltype(D)
7174
D
7275
else
73-
# work around typeinfernece bug
7476
op=ApproxFunBase.strictconvert(Operator{T},D.op)
75-
S = domainspace(op)
76-
$WrappOp{typeof(op),typeof(S),typeof(D.order),T}(op,D.order,S)::Operator{T}
77+
S = domainspace(D)
78+
R = rangespace(D)
79+
$WrappOp(op,D.order,S,R)::Operator{T}
7780
end
7881
end
7982

8083
## Routines
81-
ApproxFunBase.domain(D::$ConcOp) = domain(D.space)
8284
ApproxFunBase.domainspace(D::$ConcOp) = D.space
8385

8486
Base.getindex(::$ConcOp{UnsetSpace,OT,T},k::Integer,j::Integer) where {OT,T} =
8587
error("Spaces cannot be inferred for operator")
86-
ApproxFunBase.rangespace(D::$ConcOp{UnsetSpace,T}) where {T} = UnsetSpace()
88+
89+
ApproxFunBase.rangespace(D::$ConcOp{UnsetSpace,T}) where {T} = UnsetSpace()
8790

8891
#promoting domain space is allowed to change range space
8992
# for integration, we fall back on existing conversion for now
@@ -97,11 +100,9 @@ macro calculus_operator(Op)
97100
$Op(sp,D.order)
98101
end
99102
end
100-
end)
101-
# for func in (:rangespace,:domainspace,:bandwidths)
102-
# # We assume the operator wrapped has the correct spaces
103-
# @eval $func(D::$WrappOp)=$func(D.op)
104-
# end
103+
end
104+
105+
return esc(q)
105106
end
106107

107108
choosedomainspace(M::CalculusOperator{UnsetSpace},sp::Space) =
@@ -194,7 +195,9 @@ end
194195
if conversion_type(csp,sp)==csp # Conversion(sp,csp) is not banded, or sp==csp
195196
error("Implement Derivative(", sp, ",", k,")")
196197
end
197-
DerivativeWrapper(TimesOperator(Derivative(csp,k),Conversion(sp,csp)), sp, k)
198+
D = Derivative(csp,k)
199+
C = Conversion_maybeconcrete(sp, csp, Val(:forward))
200+
DerivativeWrapper(TimesOperator(D, C), k, sp, rangespace(D))
198201
else
199202
csp = canonicalspace(sp)
200203
D1 = if csp == sp
@@ -204,13 +207,13 @@ end
204207
else
205208
Dcsp = Derivative(csp)
206209
rsp = rangespace(Dcsp)
207-
Dcsp * Conversion(sp, csp)
210+
Dcsp * Conversion_maybeconcrete(sp, csp, Val(:forward))
208211
end
209212
D=DerivativeWrapper(SpaceOperator(D1,sp,setdomain(rsp,domain(sp))),1)
210213
if k==1
211214
D
212215
else
213-
DerivativeWrapper(TimesOperator(Derivative(rangespace(D),k-1),D), sp, k)
216+
DerivativeWrapper(TimesOperator(Derivative(rangespace(D),k-1),D), k, sp)
214217
end
215218
end
216219
end

src/Operators/banded/Conversion.jl

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,25 @@ export Conversion
22

33
abstract type Conversion{T}<:Operator{T} end
44

5-
struct ConcreteConversion{S<:Space,V<:Space,T} <: Conversion{T}
6-
domainspace::S
7-
rangespace::V
5+
struct ConcreteConversion{D<:Space,R<:Space,T} <: Conversion{T}
6+
domainspace::D
7+
rangespace::R
88
end
99

1010

11-
ConcreteConversion(a::Space,b::Space)=ConcreteConversion{typeof(a),typeof(b),
12-
promote_type(rangetype(a),rangetype(b))}(a,b)
13-
11+
function ConcreteConversion(::Type{T}, a::Space,b::Space) where {T}
12+
ConcreteConversion{typeof(a),typeof(b),T}(a,b)
13+
end
14+
function ConcreteConversion(a::Space,b::Space)
15+
T = promote_type(rangetype(a),rangetype(b))
16+
ConcreteConversion(T, a, b)
17+
end
1418

15-
function convert(::Type{Operator{T}},C::ConcreteConversion{S,V}) where {T,S,V}
19+
function convert(::Type{Operator{T}}, C::ConcreteConversion) where {T}
1620
if T==eltype(C)
1721
C
1822
else
19-
ConcreteConversion{S,V,T}(C.domainspace,C.rangespace)
23+
ConcreteConversion(T, C.domainspace,C.rangespace)::Operator{T}
2024
end
2125
end
2226

@@ -54,6 +58,34 @@ function defaultConversion(a::Space,b::Space)
5458
end
5559
end
5660

61+
"""
62+
hasconcreteconversion_canonical(sp::Space, ::Val{:forward})
63+
64+
Return `Conversion(sp, canonicalspace(sp))` is known statically to be a `ConcreteConversion`.
65+
Assumed to be false by default.
66+
67+
hasconcreteconversion_canonical(sp::Space, ::Val{:backward})
68+
69+
Return `Conversion(canonicalspace(sp), sp)` is known statically to be a `ConcreteConversion`.
70+
Assumed to be false by default.
71+
"""
72+
hasconcreteconversion_canonical(@nospecialize(sp), @nospecialize(Valfwdback)) = false
73+
74+
function Conversion_maybeconcrete(sp, csp, v::Val{:forward})
75+
if hasconcreteconversion_canonical(sp, v)
76+
ConcreteConversion(sp,csp)
77+
else
78+
Conversion(sp,csp)
79+
end
80+
end
81+
function Conversion_maybeconcrete(sp, csp, v::Val{:backward})
82+
if hasconcreteconversion_canonical(sp, v)
83+
ConcreteConversion(csp,sp)
84+
else
85+
Conversion(csp,sp)
86+
end
87+
end
88+
5789
"""
5890
Conversion(fromspace::Space, tospace::Space)
5991
@@ -72,21 +104,25 @@ Conversion() = ConversionWrapper(Operator(I,UnsetSpace()))
72104
# the domain and range space
73105
# but continue to know its a derivative
74106

75-
struct ConversionWrapper{S<:Operator,T} <: Conversion{T}
76-
op::S
107+
struct ConversionWrapper{D<:Space,R<:Space,T,O<:Operator{T}} <: Conversion{T}
108+
domainspace::D
109+
rangespace::R
110+
op::O
77111
end
78112

79-
@wrapper ConversionWrapper
113+
@wrapper ConversionWrapper false false
80114

115+
domainspace(C::ConversionWrapper) = C.domainspace
116+
rangespace(C::ConversionWrapper) = C.rangespace
81117

82-
ConversionWrapper(::Type{T},op) where {T} = ConversionWrapper{typeof(op),T}(op)
83-
ConversionWrapper(B::Operator) =
84-
ConversionWrapper{typeof(B),eltype(B)}(B)
85-
ConversionWrapper(C::ConversionWrapper) = C
118+
function ConversionWrapper(B::Operator,
119+
d::Space=domainspace(B), r::Space=rangespace(B))
120+
ConversionWrapper(d, r, unwrap(B))
121+
end
86122
Conversion(A::Space,B::Space,C::Space) =
87-
ConversionWrapper(Conversion(B,C)*Conversion(A,B))
123+
ConversionWrapper(Conversion(B,C)*Conversion(A,B), A, C)
88124
Conversion(A::Space,B::Space,C::Space,D::Space...) =
89-
ConversionWrapper(Conversion(C,D...)*Conversion(B,C)*Conversion(A,B))
125+
ConversionWrapper(Conversion(C,D...)*Conversion(B,C)*Conversion(A,B), A, last(D))
90126

91127
==(A::ConversionWrapper,B::ConversionWrapper) = A.op==B.op
92128

@@ -96,7 +132,8 @@ function convert(::Type{Operator{T}},D::ConversionWrapper) where T
96132
D
97133
else
98134
BO=strictconvert(Operator{T},D.op)
99-
ConversionWrapper{typeof(BO),T}(BO)
135+
d, r = domainspace(D), rangespace(D)
136+
ConversionWrapper(d, r, BO)::Operator{T}
100137
end
101138
end
102139

src/Operators/banded/Multiplication.jl

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function defaultMultiplication(f::Fun,sp::Space)
3535
if csp==sp || !hasconversion(sp,csp)
3636
error("Implement Multiplication(::Fun{$(typeof(space(f)))},::$(typeof(sp)))")
3737
end
38-
MultiplicationWrapper(f,Multiplication(f,csp)*Conversion(sp,csp))
38+
MultiplicationWrapper(f, Multiplication(f,csp)*Conversion(sp,csp), sp)
3939
end
4040

4141
Multiplication(f::Fun,sp::Space) = defaultMultiplication(f,sp)
@@ -90,26 +90,34 @@ choosedomainspace(M::ConcreteMultiplication{D,UnsetSpace},sp::Space) where {D} =
9090

9191
diagm(a::Fun) = Multiplication(a)
9292

93-
struct MultiplicationWrapper{D<:Space,S<:Space,O<:Operator,T} <: Multiplication{D,S,T}
93+
struct MultiplicationWrapper{D<:Space,S<:Space,T,O<:Operator{T}} <: Multiplication{D,S,T}
9494
f::VFun{D,T}
9595
op::O
96+
space::S
97+
98+
function MultiplicationWrapper{D,S,T,O}(f::Fun{D,T},op::O,space::S) where {D,S,T,O<:Operator{T}}
99+
new{D,S,T,O}(f,op,space)
100+
end
96101
end
97102

98103
function MultiplicationWrapper(::Type{T}, f::Fun{D}, op::Operator,
99-
dspop::S = domainspace(op)) where {D,T,S<:Space}
100-
MultiplicationWrapper{D,S,typeof(op),T}(f,op)
104+
space::S = domainspace(op)) where {D,T,S<:Space}
105+
g = convert(VFun{D,T}, f)
106+
MultiplicationWrapper{D,S,T,typeof(op)}(g,op,space)
101107
end
102-
function MultiplicationWrapper(f::Fun, op::Operator, dspop::Space = domainspace(op))
103-
MultiplicationWrapper(eltype(op), f, op, dspop)
108+
function MultiplicationWrapper(f::Fun, op::Operator, space::Space = domainspace(op))
109+
MultiplicationWrapper(eltype(op), f, op, space)
104110
end
105111

106-
@wrapper MultiplicationWrapper
112+
domainspace(M::MultiplicationWrapper) = M.space
113+
114+
@wrapper MultiplicationWrapper false
107115

108116
function convert(::Type{Operator{TT}},C::MultiplicationWrapper{S,V,O,T}) where {TT,S,V,O,T}
109117
if TT==T
110118
C
111119
else
112-
MultiplicationWrapper(Fun{S,TT}(C.f),Operator{TT}(C.op))::Operator{TT}
120+
MultiplicationWrapper(Fun{S,TT}(C.f),Operator{TT}(C.op), C.space)::Operator{TT}
113121
end
114122
end
115123

src/Operators/functionals/Evaluation.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,12 @@ struct EvaluationWrapper{S<:Space,M,FS<:Operator,OT,T<:Number} <: Evaluation{T}
9797
end
9898

9999

100-
@wrapper EvaluationWrapper
100+
@wrapper EvaluationWrapper false
101101
EvaluationWrapper(sp::Space,x,order,func::Operator) =
102102
EvaluationWrapper{typeof(sp),typeof(x),typeof(func),typeof(order),eltype(func)}(sp,x,order,func)
103103

104104

105105
domainspace(E::Evaluation) = E.space
106-
domain(E::Evaluation) = domain(E.space)
107106
promotedomainspace(E::Evaluation,sp::Space) = Evaluation(sp,E.x,E.order)
108107

109108

src/Operators/general/algebra.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -608,11 +608,10 @@ end
608608

609609

610610
# Conversions we always assume are intentional: no need to promote
611-
612-
_unwrap_conversion(c) = c
613-
_unwrap_conversion(c::ConversionWrapper{<:TimesOperator}) = c.op
614-
615-
*(A::Conversion, B::Conversion) = ConversionWrapper(TimesOperator(_unwrap_conversion(A), _unwrap_conversion(B)))
611+
function *(A::Conversion, B::Conversion)
612+
T = TimesOperator(unwrap(A), unwrap(B))
613+
ConversionWrapper(T, domainspace(B), rangespace(A))
614+
end
616615
*(A::Conversion, B::TimesOperator) = TimesOperator(A, B)
617616
*(A::TimesOperator, B::Conversion) = TimesOperator(A, B)
618617
*(A::Operator, B::Conversion) =

0 commit comments

Comments
 (0)