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

Separate path optimization of disconnected components in the network graph #53

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

Todorbsc
Copy link

@Todorbsc Todorbsc commented Feb 9, 2024

Closes #51

Copy link
Member

@mofeing mofeing left a 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:

@@ -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}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change the order?

Copy link
Author

@Todorbsc Todorbsc Feb 13, 2024

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)
Copy link
Member

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
Comment on lines 315 to 324
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
Copy link
Member

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:

Suggested change
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

Copy link
Author

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
Comment on lines 326 to 341
# 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuing with above

Suggested change
# 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

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@@ -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)]]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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?

Copy link
Author

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

@Todorbsc
Copy link
Author

Todorbsc commented Feb 13, 2024

Very well with the implementation. But keep in count the following things:

I did the last two points. I have to check for the hyperindices and the output open indices cases.

@Todorbsc
Copy link
Author

Todorbsc commented Feb 16, 2024

I found something related to the hyperindices that may be a bug: when calling hyperinds with the expression, each hyperindex is repeated a number of times equal to the number of edges of the hyperindex. Also, there are no hyperindices in the obtained expression after the optimization (here it is exemplified with Greedy but the list of hyperindices is empty for all optimizers).

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[]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ease optimization by separately optimizing disconnected graph components
3 participants