Skip to content
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

reducedim allocates array of incorrect eltype #26709

Closed
blegat opened this issue Apr 5, 2018 · 5 comments · Fixed by #27457
Closed

reducedim allocates array of incorrect eltype #26709

blegat opened this issue Apr 5, 2018 · 5 comments · Fixed by #27457
Labels
arrays [a, r, r, a, y, s]

Comments

@blegat
Copy link
Contributor

blegat commented Apr 5, 2018

To compute the eltype of the array storing the result, reducedim does

Tr = typeof(z) == typeof(x) && !isbits(T) ? T : typeof(z)

I don't understand the reasoning behind the condition typeof(z) == typeof(x) && !isbits(T). Why not just do Tr = typeof(z) ?

Here is a MWE:

struct Variable
    name::Symbol
end
struct AffExpr
    vars::Vector{Variable}
end
Base.zero(::Union{Variable, Type{Variable}, AffExpr}) = AffExpr(Variable[])
Base.:+(v::Variable, w::Variable) = AffExpr([v, w])
Base.:+(aff::AffExpr, v::Variable) = AffExpr([aff.vars; v])
Base.:+(aff1::AffExpr, aff2::AffExpr) = AffExpr([aff1.vars; aff2.vars])

The reduction fails because Tr is Variable instead of AffExpr. typeof(z) and typeof(x) are AffExpr and isbits(Variable) is false.

julia> sum([Variable(:x), Variable(:y)], 1)
ERROR: MethodError: Cannot `convert` an object of type AffExpr to an object of type Variable
This may have arisen from a call to the constructor Variable(...),
since type constructors fall back to convert methods.
Stacktrace:
 [1] fill!(::Array{Variable,1}, ::AffExpr) at ./multidimensional.jl:841
 [2] reducedim_initarray(::Array{Variable,1}, ::Int64, ::AffExpr, ::Type{Variable}) at ./reducedim.jl:73
 [3] _reducedim_init(::Base.#identity, ::Base.#+, ::Base.#zero, ::Base.#sum, ::Array{Variable,1}, ::Int64) at ./reducedim.jl:102
 [4] reducedim_init(::Function, ::Base.#+, ::Array{Variable,1}, ::Int64) at ./reducedim.jl:87
 [5] mapreducedim(::Function, ::Function, ::Array{Variable,1}, ::Int64) at ./reducedim.jl:242
 [6] sum(::Array{Variable,1}, ::Int64) at ./reducedim.jl:585
 [7] macro expansion at ./REPL.jl:97 [inlined]
 [8] (::Base.REPL.##1#2{Base.REPL.REPLBackend})() at ./event.jl:73
@JeffBezanson
Copy link
Member

Agreed; I don't see why isbits would be relevant here.

@kshyatt kshyatt added the arrays [a, r, r, a, y, s] label May 28, 2018
@non-Jedi
Copy link
Contributor

non-Jedi commented Jun 5, 2018

Spent a little time digging into this, though I think I'm in over my head at this point. The check for isbitstype is necessary for sum(Real[1 2 3; 3.5 4 5], 2) to work as expected. In such a case, Tr should be Real rather than Int64 like it ends up since zero(Real) == 0. I think what really needs to be checked there is whether T is an abstract or concrete type. Will PR something to that effect once I've given myself time to mentally untangle things a bit more.

As far as I can tell, the typeof(z) == typeof(x) check is useless. Why would you want Tr = false anyway?
For what it's worth, just substituting the below for that line passes all existing tests and makes the above MWE function correctly:

Tr = isconcretetype(T) ? typeof(z) : T

@nalimilan
Copy link
Member

For the curious, this line comes from a generalization of a method which worked only for + (54e034a). That weird typeof(z) == typeof(x) check comes from that legacy, but it doesn't really make sense.

@non-Jedi
Copy link
Contributor

non-Jedi commented Oct 8, 2019

Fixed by #28089

@blegat
Copy link
Contributor Author

blegat commented Oct 8, 2019

Indeed, thanks for the fix @nalimilan

@blegat blegat closed this as completed Oct 8, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arrays [a, r, r, a, y, s]
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants