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

Improve performance of calc_connected_components #914

Merged
merged 2 commits into from
Jun 23, 2024

Conversation

mtanneau
Copy link
Contributor

@mtanneau mtanneau commented Jun 11, 2024

See #913 and profile therein. This PR focuses on improving the performance of calc_connected_components by implementing a more efficient graph search and identification of connected components.

In particular, a significant amount of time is (currently) spent in this one line:

ccs = (Set(values(component_lookup)))

In the case where there is a single connected component, this ends up computing the union of N sets, all of which are identical and contain all the buses in the network. This is because the current implementation stores the entire connected component for every bus in the system, which also comes at a O(n^2) memory cost.

I replace the previous DFS search with a more memory-efficient search that indexes each connected component with the smallest bus index in that connected component. There is a O(n log n) cost for sorting bus IDs, and then a O(E) cost for the graph search. The memory cost is O(n). It also saves time because it avoids the costly computation of a set of (large) sets. Note that I kept the last step (converting to a Set{Set{Int}}) to comply with the previous function definition.

Timings of make_basic_network after the changes: times are improved, the impact on memory is marginal

Case #buses old time (s) new time (s) old memory (MiB) new memory (MiB)
1354_pegase 1354 0.21 0.30 28.5 28.9
2869_pegase 2869 0.62 0.43 66.8 65.3
9241_pegase 9241 2.50 1.67 208.4 204.2
13659_pegase 13659 5.72 2.47 303.1 306.2

and corresponding profile:
prof_new

@mtanneau
Copy link
Contributor Author

@ccoffrin I believe we can also remove the _cc_dfs function. I didn't because I was afraid it might break something downstream.

Side note: Graphs.jl has a lot of functionality to do these kind of graph operations, which are likely more efficient (at the cost of yet another dependency).

src/core/data.jl Outdated

# ⚠️ it is important to iterate over _sorted_ bus IDs to ensure that components are labeled correctly
for i in sorted_bus_ids
component_lookup[i] != i0 && continue # bus already flagged; skip
Copy link
Member

Choose a reason for hiding this comment

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

Can you remind me how this Julia && syntax works? I think it means, if the lhs is true then execute the rhs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is equivalent to

if component_lookup[i] != i0
    continue
end

Julia evaluates boolean expressions lazily. Thereby, given A && B, if A evaluates to false, then B is not evaluated.

Copy link
Collaborator

Choose a reason for hiding this comment

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

My rule is that you should always write out the if end version. It's much simpler to read.

Copy link
Member

Choose a reason for hiding this comment

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

That's my preference as well, if you don't mind @mtanneau.

src/core/data.jl Outdated
while length(V) > 0
j = pop!(V)
for k in neighbors[j]
component_lookup[k] == i0 || continue # already visited
Copy link
Member

Choose a reason for hiding this comment

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

Can you remind me how this Julia || syntax works? I am guessing that it means, if the lhs is false then execute the rhs?

Is it also the case that component_lookup[k] == i if it does not equal i0, if not, then we have an issue where a previous labeling did not explore the graph correctly, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The syntax is equivalent to

if component_lookup[k] == i0
    # do nothing
else
    continue
end

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it also the case that component_lookup[k] == i if it does not equal i0, if not, then we have an issue where a previous labeling did not explore the graph correctly, right?

If component_lookup[k] != i0, then it should be equal to the index of the bus with smallest index in that connected component. I believe it should then be equal to i, yes, but only 95% confident.

Copy link
Member

Choose a reason for hiding this comment

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

If it not the case 100% of the time then I worry the implementation has a correctness issue.

How about we add an assertion after this check that component_lookup[k] == i, this will help catch the issue down the road if we run into it.

@ccoffrin ccoffrin changed the base branch from master to release-prep June 23, 2024 19:26
@ccoffrin ccoffrin merged commit 17fc389 into lanl-ansi:release-prep Jun 23, 2024
7 checks passed
@mtanneau mtanneau deleted the FastBasicConversion branch June 24, 2024 20:03
ccoffrin added a commit that referenced this pull request Jul 4, 2024
* Improve performance of calc_connected_components (#914)

---------

Co-authored-by: mtanneau3 <[email protected]>

* Add in-place conversion to basic data format (#915)

* Add in-place conversion to basic data format

* Add unit test for in-place make_basic_network

---------

Co-authored-by: mtanneau3 <[email protected]>

* No deepcopy when renumbering components (#916)

* No deepcopy when renumbering components

* In-place _renumber_components and unit tests

* Fix typo

---------

Co-authored-by: mtanneau3 <[email protected]>
Co-authored-by: Carleton Coffrin <[email protected]>

* add notes to changelog and prep for release

* Update psse parser for 3winding transformers (#917)

* fix psse parser 3 winding tranformers
* update readme

* add note to changelog and readme

* fix quote counting check in psse parser

---------

Co-authored-by: Mathieu Tanneau <[email protected]>
Co-authored-by: mtanneau3 <[email protected]>
Co-authored-by: Rahmat Heidari <[email protected]>
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.

3 participants