-
Notifications
You must be signed in to change notification settings - Fork 2
address issue #147 (standard PPE) #148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov Report
@@ Coverage Diff @@
## master #148 +/- ##
==========================================
+ Coverage 50% 50.16% +0.16%
==========================================
Files 28 28
Lines 1764 1766 +2
==========================================
+ Hits 882 886 +4
+ Misses 882 880 -2
Continue to review full report at Codecov.
|
fix depreciation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my changes for tests also
hold off on merging till the |
I'm really worried about performance hit of using |
Sorry, I don’t understand. I didn’t make it a container of abstract variables because you were worried about performance. And didn't make parametric type, because i agree its unnecessary complications and restricting. Guess I need to learn some more julia then. Is it because of the the second outer Dict? |
I think we are close to a decision (Sam's suggestions seems like best mix of both). Dict of VariableEstimate containing a Dict). Mentioned on PR #148 and can discuss in person before merge.
We can continue discussion post decision to make sure we are speaking the same language and that you are happy with the result -- when you did this in af107a6
isn't that |
I don't mind the new changes sam is proposing. Just note they will be breaking, but rather sooner than later. abstract type AbstractTypeAB end
struct TypeA <: AbstractTypeAB
a::Symbol
b::Int
end
struct TypeB <: AbstractTypeAB
a::Symbol
b::Int
end
struct Option1
s::Symbol
d::Dict{Symbol, AbstractTypeAB}
end
struct Option2
s::Symbol
d::Dict{Symbol, <:AbstractTypeAB}
end
julia> o1 = Option1(:bla, Dict{Symbol,TypeA}())
Option1(:bla, Dict{Symbol,AbstractTypeAB}())
julia> push!(o1.d, :a=>TypeA(:a,1))
Dict{Symbol,AbstractTypeAB} with 1 entry:
:a => TypeA(:a, 1)
julia> push!(o1.d, :b=>TypeB(:b,1))
Dict{Symbol,AbstractTypeAB} with 2 entries:
:a => TypeA(:a, 1)
:b => TypeB(:b, 1)
julia> o2 = Option2(:bla, Dict{Symbol,TypeA}())
Option2(:bla, Dict{Symbol,TypeA}())
julia> push!(o2.d, :a=>TypeA(:a,1))
Dict{Symbol,TypeA} with 1 entry:
:a => TypeA(:a, 1)
julia> push!(o2.d, :b=>TypeB(:b,1))
ERROR: MethodError: Cannot `convert` an object of type TypeB to an object of type TypeA
Closest candidates are:
convert(::Type{T}, ::T) where T at essentials.jl:167
TypeA(::Any, ::Any) at untitled-b44b38864b48aa54882edecfec5246ba:5
Stacktrace:
[1] setindex!(::Dict{Symbol,TypeA}, ::TypeB, ::Symbol) at ./dict.jl:380
[2] push!(::Dict{Symbol,TypeA}, ::Pair{Symbol,TypeB}) at ./abstractdict.jl:479
[3] top-level scope at none:0 But I guess you can force it with the second (outer) dict: :default=>Dict{Symbol, VariableEstimate}} # can only contain VariableEstimate
:other=>Dict{Symbol, OtherVariableEstimateType}}#can only contain OtherVariableEstimateType
but Dict{Symbol, Dict{Symbol, <: AbstractVariableEstimate}} would then contain both. I still don't see the problem with it. I also think it happens in the Variables dictionary, since not all variables are of the same type. ie pose point |
My opinion is that this is the part that is bad for performance: Dict{Symbol, <: AbstractVariableEstimate}} or d::Dict{Symbol, AbstractTypeAB} |
Functionally yes they will work, but that requires dynamic type inference. |
Why is it different than templating? It also fixes the type when it is created, after the type cannot be changed and can only be of TypeA in this example. |
Julia syntax specific thing.
|
Thanks, I think you finally get why I struggle to understand you. |
Maybe this is related:
|
if anonymous parameter are reliable in all cases (not just small ones) --- e.g. |
Sure, but as before -- that is for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not aware of anonymous static types in Julia, so Johan has convinced me of the alternative.
timestamp::DateTime | ||
tags::Vector{Symbol} | ||
estimateDict::Dict{Symbol, Dict{Symbol, VariableEstimate}} | ||
estimateDict::Dict{Symbol, Dict{Symbol, <: AbstractVariableEstimate}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
EDIT
know we have been going back and forth on this -- but I'm not convinced about adding an abstract inheritance here unfortunately. Going to create more boiler plate at the top level for DFGVariable{T,P}
. There are already 2 dictionaries here.
Will ask again why this is not simpler -- just make it a dictionary and be done?
estimateDict::Dict{Symbol, Dict{Symbol, Float64}}(
:default => Dict{Symbol, Vector{Float64}}(
:max => [1;2;3],
:mean => [3;2;1],
:modefit1 => [2;2;2],
:modefit1_weight => [4;],
...
:lastupdated => [2019;10;13;22;20;00.000]
))
Better solution
Can use anonymous static types which avoids the boiler plate. See Option 2 here and further:
#148 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
easier to link discussions from Issue, so repeated here #147 (comment)
Closed by design decision from #147 last few comments |
Sorry for the slow reply here. Is the main worry the overhead from dynamic dispatch because the compiler can't infer the types or is the worry excessive compile time because of the use of type parameters? Let me try to modify the example slightly to highlight what's inferred and what is not. Here are three Option structs that all store dicts with julia> struct Option1
x::Dict{Symbol,Real}
end
julia> struct Option2
x::Dict{Symbol,<:Real}
end
julia> struct Option3{T}
x::Dict{Symbol,T}
end To see what gets inferred and what doesn't, consider the two helper functions julia> foo1(o) = o.x
foo1 (generic function with 1 method)
julia> foo2(d) = d[:a]
foo2 (generic function with 1 method)
julia> foo3(o) = o.x[:a]
foo3 (generic function with 1 method) and the dict julia> d = Dict(:a =>1)
Dict{Symbol,Int64} with 1 entry:
:a => 1 From julia> @code_warntype foo1(Option1(d))
Variables
#self#::Core.Compiler.Const(foo1, false)
o::Option1
Body::Dict{Symbol,Real}
1 ─ %1 = Base.getproperty(o, :x)::Dict{Symbol,Real}
└── return %1
julia> @code_warntype foo1(Option2(d))
Variables
#self#::Core.Compiler.Const(foo1, false)
o::Option2
Body::Dict{Symbol,#s1} where #s1<:Real
1 ─ %1 = Base.getproperty(o, :x)::Dict{Symbol,#s1} where #s1<:Real
└── return %1
julia> @code_warntype foo1(Option3(d))
Variables
#self#::Core.Compiler.Const(foo1, false)
o::Option3{Int64}
Body::Dict{Symbol,Int64}
1 ─ %1 = Base.getproperty(o, :x)::Dict{Symbol,Int64}
└── return %1 we can see that 1 and 3 are inferred when calling julia> @code_warntype foo2(Option2(d).x)
Variables
#self#::Core.Compiler.Const(foo2, false)
d::Dict{Symbol,Int64}
Body::Int64
1 ─ %1 = Base.getindex(d, :a)::Int64
└── return %1
julia> @code_warntype foo2(Option3(d).x)
Variables
#self#::Core.Compiler.Const(foo2, false)
d::Dict{Symbol,Int64}
Body::Int64
1 ─ %1 = Base.getindex(d, :a)::Int64
└── return %1 we see that extracting the element from the dict is only inferred in 2 and 3. Finally, since failure of inference propagates we get julia> @code_warntype foo3(Option1(d))
Variables
#self#::Core.Compiler.Const(foo3, false)
o::Option1
Body::Real
1 ─ %1 = Base.getproperty(o, :x)::Dict{Symbol,Real}
│ %2 = Base.getindex(%1, :a)::Real
└── return %2
julia> @code_warntype foo3(Option2(d))
Variables
#self#::Core.Compiler.Const(foo3, false)
o::Option2
Body::Real
1 ─ %1 = Base.getproperty(o, :x)::Dict{Symbol,#s1} where #s1<:Real
│ %2 = Base.getindex(%1, :a)::Real
└── return %2
julia> @code_warntype foo3(Option3(d))
Variables
#self#::Core.Compiler.Const(foo3, false)
o::Option3{Int64}
Body::Int64
1 ─ %1 = Base.getproperty(o, :x)::Dict{Symbol,Int64}
│ %2 = Base.getindex(%1, :a)::Int64
└── return %2 i.e. only 3 is inferred. If you want to avoid the dynamic dispatch, you should therefore use 3. However, it might not always be beneficial since it can cause extra compile time. In particular if the types can't be inferred. If the is a concern then it might actually be worth considering julia> struct Option4
x::Dict{Symbol,Any}
end since it might have the minimal compile overhead. |
@andreasnoack Thanks for the great writeup! |
resolves #147