-
Notifications
You must be signed in to change notification settings - Fork 2
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
Separate path optimization of disconnected components in the network graph #53
base: master
Are you sure you want to change the base?
Conversation
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.
Very well with the implementation. But keep in count the following things:
- Hyperindices may appear
- For this case, it would be enough to consider them as all-to-all connections
- Current implementation doesn't preserver output open indices
- Check out how you
sum
theEinExpr
s in the end
- Check out how you
dfs
maybe suits better inUtils.jl
- There is another implementation in
Graphs.jl
that doesn't use recursion https://github.com/JuliaGraphs/Graphs.jl/blob/0c29f25a00967b575cf29b602f21fda8269a76f8/src/connectivity.jl#L13- This way, we wouldn't need
dfs
and its code could be all inside thecomponents
function
- This way, we wouldn't need
@@ -27,7 +27,7 @@ The implementation uses a binary heaptree to sort candidate pairwise tensor cont | |||
outer::Bool = false | |||
end | |||
|
|||
function einexpr(config::Greedy, path::EinExpr{L}, sizedict::Dict{L}) where {L} | |||
function einexpr(config::Greedy, sizedict::Dict{L}, path::EinExpr{L}) where {L} |
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.
Why change the order?
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.
Because of the multiple dispatching: this way, all optimizers' einexpr
function are called through Optimizers.einexpr
when the function call is like the one suggested in the issue #51:
network = [[:a, :b], [:b, :c], [:A, :B], [:B, :C]]
expr = sum(EinExpr.(network))
optdata = Dict(key => 2 for key in unique(vcat(network...)))
einexpr(Exhaustive(), expr, optdata)
Although I am not really convinced about this fix, it makes possible to call the optimizers with each of the non-interconnected subgraphs at the level of abstraction of the class Optimizers
avoiding the need to modify each specific optimizer.
Let me know what are your thoughts on this and whether there's another more-suitable way to implement this.
@@ -70,4 +70,4 @@ function einexpr(config::Greedy, path::EinExpr{L}, sizedict::Dict{L}) where {L} | |||
return path | |||
end | |||
|
|||
einexpr(config::Greedy, path::SizedEinExpr) = SizedEinExpr(einexpr(config, path.path, path.size), path.size) | |||
einexpr(config::Greedy, path::SizedEinExpr) = SizedEinExpr(einexpr(config, path.size, path.path), path.size) |
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.
Same as above: why change the order?
src/EinExpr.jl
Outdated
network = [args.head for args in path.args] | ||
# create adjacency graph of the network | ||
tengraph = zeros(Bool, length(network), length(network)) | ||
for i in 1:length(network) | ||
for j in 1:length(network) | ||
if !isempty(network[i] ∩ network[j]) && i ≠ j | ||
tengraph[i,j] = true | ||
end | ||
end | ||
end |
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.
Nice! But let's rewrite it like this:
network = [args.head for args in path.args] | |
# create adjacency graph of the network | |
tengraph = zeros(Bool, length(network), length(network)) | |
for i in 1:length(network) | |
for j in 1:length(network) | |
if !isempty(network[i] ∩ network[j]) && i ≠ j | |
tengraph[i,j] = true | |
end | |
end | |
end | |
network = map(head, path.args) | |
# create adjacency matrix of the network | |
n = nargs(path) | |
adjmat = falses(n,n) | |
for (i,j) in combinations(1:n, 2) | |
if !isdisjoint(network[i], network[j]) | |
adjmat[i,j] = true | |
adjmat[j,i] = true | |
end | |
end |
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.
Done!
src/EinExpr.jl
Outdated
# find disconneceted subgraphs | ||
subgraphs = Vector{Vector{Int}}() | ||
n = size(tengraph, 1) | ||
visited = falses(n) | ||
for v in 1:n | ||
if !visited[v] | ||
subgraph = Int[] | ||
dfs(tengraph, v, visited, subgraph) | ||
push!(subgraphs, subgraph) | ||
end | ||
end | ||
|
||
# create subnetworks | ||
indeppaths = [sum(EinExpr.([network[tns] for tns in subnet])) for subnet in subgraphs] | ||
|
||
return indeppaths |
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.
Continuing with above
# find disconneceted subgraphs | |
subgraphs = Vector{Vector{Int}}() | |
n = size(tengraph, 1) | |
visited = falses(n) | |
for v in 1:n | |
if !visited[v] | |
subgraph = Int[] | |
dfs(tengraph, v, visited, subgraph) | |
push!(subgraphs, subgraph) | |
end | |
end | |
# create subnetworks | |
indeppaths = [sum(EinExpr.([network[tns] for tns in subnet])) for subnet in subgraphs] | |
return indeppaths | |
# find disconneceted subgraphs | |
subgraphs = Vector{Vector{Int}}() | |
visited = falses(n) | |
for vertex in Iterators.filter(v -> !visited[v], 1:n) | |
subgraph = Int[] | |
dfs(adjmat, vertex, visited, subgraph) | |
push!(subgraphs, subgraph) | |
end | |
# create subnetworks | |
indeppaths = [sum(EinExpr.([network[tns] for tns in subnet])) for subnet in subgraphs] | |
return indeppaths |
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.
Done!
src/Optimizers/Optimizers.jl
Outdated
@@ -3,7 +3,7 @@ abstract type Optimizer end | |||
function einexpr end | |||
|
|||
einexpr(T::Type{<:Optimizer}, args...; kwargs...) = einexpr(T(; kwargs...), args...) | |||
einexpr(config::Optimizer, expr, sizedict) = einexpr(config, SizedEinExpr(expr, sizedict)) | |||
einexpr(config::Optimizer, expr, sizedict) = sum(einexpr.((config,), [SizedEinExpr(exp, sizedict) for exp in [comp for comp in components(expr)]])) |
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.
einexpr(config::Optimizer, expr, sizedict) = sum(einexpr.((config,), [SizedEinExpr(exp, sizedict) for exp in [comp for comp in components(expr)]])) | |
einexpr(config::Optimizer, expr, sizedict) = sum(einexpr.((config,), map(exp -> SizedEinExpr(exp, sizedict), components(expr)))) |
Maybe it could be rewritten like this?
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.
Yeah, I completely agree. I guess I am still getting used to Julia haha
Made the change! Thanks
I did the last two points. I have to check for the hyperindices and the output open indices cases. |
I found something related to the hyperindices that may be a bug: when calling Example with 1 hyperindex of 3 edges: julia> using EinExprs
julia> network = [[:a, :b], [:b, :c], [:b, :d]]
3-element Vector{Vector{Symbol}}:
[:a, :b]
[:b, :c]
[:b, :d]
julia> expr = sum(EinExpr.(network))
EinExpr{Symbol}([:a, :c, :d], EinExpr{Symbol}[EinExpr{Symbol}([:a, :b], EinExpr{Symbol}[]), EinExpr{Symbol}([:b, :c], EinExpr{Symbol}[]), EinExpr{Symbol}([:b, :d], EinExpr{Symbol}[])])
julia> optdata = Dict(key => 2 for key in unique(vcat(network...)))
Dict{Symbol, Int64} with 4 entries:
:a => 2
:b => 2
:d => 2
:c => 2
julia> greedy = einexpr(Greedy(), expr, optdata)
SizedEinExpr{Symbol}(EinExpr{Symbol}([:a, :c, :d], EinExpr{Symbol}[EinExpr{Symbol}([:a, :c, :d], EinExpr{Symbol}[EinExpr{Symbol}([:b, :d], EinExpr{Symbol}[]), EinExpr{Symbol}([:a, :b, :c], EinExpr{Symbol}[EinExpr{Symbol}([:a, :b], EinExpr{Symbol}[]), EinExpr{Symbol}([:b, :c], EinExpr{Symbol}[])])])]), Dict(:a => 2, :b => 2, :d => 2, :c => 2))
julia> hyperinds(expr)
3-element Vector{Symbol}:
:b
:b
:b
julia> hyperinds(greedy)
Symbol[] Example with 1 hyperindex of 4 edges: julia> network = [[:a, :b], [:b, :c], [:b, :d], [:b, :e]]
4-element Vector{Vector{Symbol}}:
[:a, :b]
[:b, :c]
[:b, :d]
[:b, :e]
julia> expr = sum(EinExpr.(network))
EinExpr{Symbol}([:a, :c, :d, :e], EinExpr{Symbol}[EinExpr{Symbol}([:a, :b], EinExpr{Symbol}[]), EinExpr{Symbol}([:b, :c], EinExpr{Symbol}[]), EinExpr{Symbol}([:b, :d], EinExpr{Symbol}[]), EinExpr{Symbol}([:b, :e], EinExpr{Symbol}[])])
julia> optdata = Dict(key => 2 for key in unique(vcat(network...)))
Dict{Symbol, Int64} with 5 entries:
:a => 2
:b => 2
:d => 2
:e => 2
:c => 2
julia> greedy = einexpr(Greedy(), expr, optdata)
SizedEinExpr{Symbol}(EinExpr{Symbol}([:a, :c, :d, :e], EinExpr{Symbol}[EinExpr{Symbol}([:a, :c, :d, :e], EinExpr{Symbol}[EinExpr{Symbol}([:b, :e], EinExpr{Symbol}[]), EinExpr{Symbol}([:a, :c, :d], EinExpr{Symbol}[EinExpr{Symbol}([:a, :b, :c], EinExpr{Symbol}[EinExpr{Symbol}([:a, :b], EinExpr{Symbol}[]), EinExpr{Symbol}([:b, :c], EinExpr{Symbol}[])]), EinExpr{Symbol}([:b, :d], EinExpr{Symbol}[])])])]), Dict(:a => 2, :b => 2, :d => 2, :e => 2, :c => 2))
julia> hyperinds(expr)
4-element Vector{Symbol}:
:b
:b
:b
:b
julia> hyperinds(greedy)
Symbol[] |
Closes #51