-
-
Notifications
You must be signed in to change notification settings - Fork 396
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
Fixing KeyIterator used for JuMPArray to work also when index sets are non indexable. #836
Changes from 7 commits
734b58e
4204781
5e59724
452743c
cc53bdc
ab0d709
ee819c7
c980b33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -151,8 +151,10 @@ Base.ndims{T,N}(x::JuMPDict{T,N}) = N | |
Base.abs(x::JuMPDict) = map(abs, x) | ||
# avoid dangerous behavior with "end" (#730) | ||
Base.endof(x::JuMPArray) = error("endof() (and \"end\" syntax) not implemented for JuMPArray objects.") | ||
Base.size(x::JuMPArray) = error("size (and \"end\" syntax) not implemented for JuMPArray objects. Use JuMP.size if you want to access the dimensions.") | ||
Base.size(x::JuMPArray,k) = error("size (and \"end\" syntax) not implemented for JuMPArray objects. Use JuMP.size if you want to access the dimensions.") | ||
Base.size(x::JuMPArray) = error(string("size (and \"end\" syntax) not implemented for JuMPArray objects.", | ||
"Use JuMP.size if you want to access the dimensions.")) | ||
Base.size(x::JuMPArray,k) = error(string("size (and \"end\" syntax) not implemented for JuMPArray objects.", | ||
" Use JuMP.size if you want to access the dimensions.")) | ||
size(x::JuMPArray) = size(x.innerArray) | ||
size(x::JuMPArray,k) = size(x.innerArray,k) | ||
# for uses of size() within JuMP | ||
|
@@ -183,13 +185,86 @@ Base.length(it::ValueIterator) = length(it.x) | |
|
||
type KeyIterator{JA<:JuMPArray} | ||
x::JA | ||
dim::Int | ||
next_k_cache::Array{Any,1} | ||
function KeyIterator(d) | ||
n = ndims(d.innerArray) | ||
new(d, n, Array(Any, n+1)) | ||
end | ||
end | ||
|
||
KeyIterator{JA}(d::JA) = KeyIterator{JA}(d) | ||
|
||
immutable IndexableIndexsets end | ||
immutable NotIndexableIndexsets end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of using types here, a simple true/false should suffice. Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that traits are cleaner for dispatch. I agree that now it isn't impacting performance because the dispatch of the function There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Personally I would prefer booleans, as this code is already a bit difficult for me to parse. We can always add complexity later on as-needed |
||
|
||
function indexability(x::JuMPArray) | ||
allsetsindexable = true | ||
for i in 1:length(x.indexsets) | ||
if !method_exists(getindex, (typeof(x.indexsets[i]),)) | ||
allsetsindexable = false | ||
break | ||
end | ||
end | ||
|
||
if allsetsindexable | ||
IndexableIndexsets() | ||
else | ||
NotIndexableIndexsets() | ||
end | ||
end | ||
|
||
Base.start(it::KeyIterator) = _start(it, indexability(it.x)) | ||
_start(it::KeyIterator, ::IndexableIndexsets) = start(it.x.innerArray) | ||
_start(it::KeyIterator, ::NotIndexableIndexsets) = notindexable_start(it.x) | ||
|
||
@generated function notindexable_start{T,N,NT}(x::JuMPArray{T,N,NT}) | ||
quote | ||
$(Expr(:tuple, 0, [:(start(x.indexsets[$i])) for i in 1:N]...)) | ||
end | ||
end | ||
Base.start(it::KeyIterator) = start(it.x.innerArray) | ||
@generated __next{T,N,NT}(x::JuMPArray{T,N,NT}, k) = | ||
|
||
@generated function _next{T,N,NT}(x::JuMPArray{T,N,NT}, k::Tuple) | ||
quote | ||
$(Expr(:tuple, [:(next(x.indexsets[$i], k[$i+1])[1]) for i in 1:N]...)) | ||
end | ||
end | ||
|
||
function Base.next(it::KeyIterator, k::Tuple) | ||
cartesian_key = JuMPKey(_next(it.x, k)) | ||
pos = -1 | ||
for i in 1:it.dim | ||
if(!done(it.x.indexsets[i], next(it.x.indexsets[i], k[i+1])[2] ) ) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style point: it's preferred to use whitespace, not parentheses, in if statements: if !done(it.x.indexsets[i], next(it.x.indexsets[i], k[i+1])[2]) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry about that. I will fix it in my next commit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No worries, just a minor point |
||
pos = i | ||
break | ||
end | ||
end | ||
if pos == - 1 | ||
return cartesian_key, (1,1) #The second 1 is needed to ambiguity with ints | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you explain this further please? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first element of the tuple is used for indicating that we are done. The second 1 here is added because otherwise the dispatch of the function n̶e̶x̶t̶ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Won't this mess with the type stability of the function? Could there a cleaner way to implement this without using a sentinel value return type? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You re right. It s not very clean. The following modification doesn't change the performance (I tested it with several containers as index sets) but I will still do it to keep the exact return type everywhere
|
||
end | ||
it.next_k_cache[1] = 0 | ||
for i in 1:it.dim | ||
if i < pos | ||
it.next_k_cache[i+1] = start(it.x.indexsets[i]) | ||
elseif i == pos | ||
it.next_k_cache[i+1] = next(it.x.indexsets[i], k[i+1])[2] | ||
else | ||
it.next_k_cache[i+1] = k[i+1] | ||
end | ||
end | ||
cartesian_key, tuple(it.next_k_cache...) | ||
end | ||
|
||
function Base.done(it::KeyIterator, k::Tuple) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can just be Base.done(it::KeyIterator, k::Tuple) = (k[1] == 1) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. I will fix that as well on my next commit. |
||
return k[1] == 1 | ||
end | ||
|
||
@generated __next{T,N,NT}(x::JuMPArray{T,N,NT}, k::Integer) = | ||
quote | ||
subidx = ind2sub(size(x),k) | ||
$(Expr(:tuple, [:(x.indexsets[$i][subidx[$i]]) for i in 1:N]...)), next(x.innerArray,k)[2] | ||
JuMPKey($(Expr(:tuple, [:(x.indexsets[$i][subidx[$i]]) for i in 1:N]...))), next(x.innerArray,k)[2] | ||
end | ||
Base.next(it::KeyIterator, k) = __next(it.x,k) | ||
Base.done(it::KeyIterator, k) = done(it.x.innerArray, k) | ||
Base.next(it::KeyIterator, k) = __next(it.x,k::Integer) | ||
Base.done(it::KeyIterator, k) = done(it.x.innerArray, k::Integer) | ||
|
||
Base.length(it::KeyIterator) = length(it.x.innerArray) |
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.
Is this method necessary with the inner constructor above?
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.
deleting line 196 gives this error
ERROR: LoadError: MethodError: Cannot
convert
an object of type JuMP.JuMPArray{Float64,2,Tuple{UnitRange{Int32},UnitRange{Int32}}} to an object of type JuMP.KeyIterator{JA<:JuMP.JuMPArray}This may have arisen from a call to the constructor JuMP.KeyIterator{JA<:JuMP.JuMPArray}(...),
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.
Hmm, strange. OK, thanks.