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 support for directed graphs in A*; docs update included #30556

Merged
merged 4 commits into from
Nov 7, 2019

Conversation

ayuusweetfish
Copy link
Contributor

@ayuusweetfish ayuusweetfish commented Jul 13, 2019

Closes #30520. A bidirectional parameter is added to methods are_points_connected() and disconnect_points(). The documentation has been updated to match current method signatures and to be more specific. (ref: #8146)

Current implementation takes about 2x memory for storing edges. This can be optimized if concerns arise on memory usage.

I have used ERR_EXPLAINC to report warnings, but don't see them anywhere. DEBUG_ENABLED is defined. Is that normal? Fixed by using WARN_PRINT, rebased already.

Here is a test. Should it be added to main tests?
var a = AStar.new()
a.add_point(1, Vector3(0, 0, 0))
a.add_point(2, Vector3(0, 1, 0))
a.add_point(3, Vector3(1, 1, 0))
a.add_point(4, Vector3(2, 0, 0))

a.connect_points(1, 2, true)
a.connect_points(1, 3, true)
a.connect_points(1, 4, false)
print('------')
print(a.get_point_connections(1))	# [2, 3, 4]
print(a.get_point_connections(2))	# [1]
print(a.get_point_connections(3))	# [1]
print(a.get_point_connections(4))	# []
print(a.are_points_connected(2, 1))	# True
print(a.are_points_connected(4, 1))	# True
print(a.are_points_connected(2, 1, false))	# True
print(a.are_points_connected(4, 1, false))	# False

a.disconnect_points(1, 2)
print('------')
print(a.get_point_connections(1))	# [3, 4]
print(a.get_point_connections(2))	# []
print(a.get_point_connections(3))	# [1]
print(a.get_point_connections(4))	# []

a.disconnect_points(4, 1, false)
print('------')
print(a.get_point_connections(1))	# [3, 4]
print(a.get_point_connections(2))	# []
print(a.get_point_connections(3))	# [1]
print(a.get_point_connections(4))	# []

a.disconnect_points(4, 1, true)
print('------')
print(a.get_point_connections(1))	# [3]
print(a.get_point_connections(2))	# []
print(a.get_point_connections(3))	# [1]
print(a.get_point_connections(4))	# []

a.connect_points(2, 3, false)
print('------')
print(a.get_point_connections(1))	# [3]
print(a.get_point_connections(2))	# [3]
print(a.get_point_connections(3))	# [1]
print(a.get_point_connections(4))	# []

a.connect_points(2, 3, true)
print('------')
print(a.get_point_connections(1))	# [3]
print(a.get_point_connections(2))	# [3]
print(a.get_point_connections(3))	# [1, 2]
print(a.get_point_connections(4))	# []

a.disconnect_points(2, 3, false)
print('------')
print(a.get_point_connections(1))	# [3]
print(a.get_point_connections(2))	# []
print(a.get_point_connections(3))	# [1, 2]
print(a.get_point_connections(4))	# []

a.remove_point(3)
print('------')
print(a.get_point_connections(1))	# []
print(a.get_point_connections(2))	# []
print(a.get_point_connections(4))	# []

Would love to hear opinions ^ ^

@bojidar-bg
Copy link
Contributor

Does remove_point still work?
Also, get_closest_position_in_segment would be twice slower after the change, maybe make it assert that s.from < s.to?

@ayuusweetfish
Copy link
Contributor Author

Does remove_point still work?

unlinked_neighbours are kept valid so I think yes. Maybe an extensive test should be created to confirm that and prevent regression.

Also, get_closest_position_in_segment would be twice slower after the change

Thanks for pointing out! Taking performance and memory use into account, now I think it's better to return to the p_from < p_to assertion and just add another flag denoting its direction (either way or bidirectional). I will make changes soon.

@ayuusweetfish
Copy link
Contributor Author

Hi! Worked intermittently on this and I think it's good now.

I have added both manual tests and random batch tests, hope they are strong enough 😝

Set<Segment>::Element *element = segments.find(s);
s.direction = bidirectional ?
Segment::BIDIRECTIONAL :
(s.u == p_id ? Segment::FORWARD : Segment::BACKWARD);
Copy link
Contributor

@bojidar-bg bojidar-bg Jul 18, 2019

Choose a reason for hiding this comment

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

Can't this expression be replaced with if (bidirectional) s.direction = Segment::BIDIRECTIONAL?
Also, segments.find(s) should probably be moved to after fixing the segment direction.

const Set<Segment>::Element *element = segments.find(s);
int direction = bidirectional ?
0 : // Use 0 to accept any existing segment
(s.u == p_id ? Segment::FORWARD : Segment::BACKWARD);
Copy link
Contributor

Choose a reason for hiding this comment

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

Can't s.u == p_id ? Segment::FORWARD : Segment::BACKWARD be replaced by s.direction?

@bojidar-bg
Copy link
Contributor

Apart from those comments, I think it is good (assuming it works).

@ayuusweetfish
Copy link
Contributor Author

Thanks for your careful review! I have pushed changes and re-run tests.

assuming it works

Currently the list of neighbours are checked, and that should be enough for now since _solve() only depends on them. If desirable, I can add a simple test to generate random graphs and test our A* implementation against Floyd–Warshall.

@ayuusweetfish ayuusweetfish force-pushed the astar-directed branch 3 times, most recently from d377dbb to 76cc331 Compare July 23, 2019 14:44
@ayuusweetfish
Copy link
Contributor Author

ayuusweetfish commented Jul 23, 2019

I have added an intensive stress test against Floyd–Warshall, which has helped to catch a little bug (diff: original and current). I also manually merged changes in #30708 and fixed a compilation error on CI. (All changes are amended onto their respective commits and the codebase is kept from one stable state to another.)

Hope it's ready now 🚀🚀

(Update: fixed CI warnings again)

@StraToN
Copy link
Member

StraToN commented Jul 25, 2019

Just 1 tiny little error in documentation, all good otherwise for docs.

@profan
Copy link
Contributor

profan commented Sep 9, 2019

@kawa-yoiko I just happened upon this PR that would be quite nice to have indeed, there's some conflicts with changes I've made since but the tests you've written especially would be amazing to have around!

Any chance you'll pick it back up?

@ayuusweetfish
Copy link
Contributor Author

ayuusweetfish commented Sep 9, 2019

@profan Sure! I probably will check this tomorrow the day after (sorry... T-T). And thanks for the compliment ^ ^

@ayuusweetfish
Copy link
Contributor Author

@profan It ends up more complicated than I had expected because of bugs in OAHashMap... I shall try to understand and fix them soon. Meanwhile, in case you are interested, the rebased branch so far (where tests fail) is here.

@profan
Copy link
Contributor

profan commented Sep 11, 2019

@kawa-yoiko Heh, yes this is part of what I was afraid of, that there would be things that are still subtly broken.. (OAHashMap is after all only employed in A* and the Godot CSG stuff and then basically nowhere else) so it has had quite little testing overall..

and hopefully the current OAHashMap bugs are not of my doing when trying to fix it last time 🤣.. a robust set of tests is probably necessary there too (in the sense that the current ones pass, but given that your astar one here does not means there must be a problem still)

@profan
Copy link
Contributor

profan commented Sep 20, 2019

@kawa-yoiko I'm gonna take a look at your rebased branch now btw and try to fix it (OAHashMap), hopefully won't take longer than the weekend, with your battery of tests hopefully it can be worked out 👍

@ayuusweetfish
Copy link
Contributor Author

@profan Such a coincidence that I was just looking into it yesterday and had managed to fix the problem! Will push very soon 😊

@ayuusweetfish
Copy link
Contributor Author

The fix → #32230. Let's wait for that first and rebase from there, so that the engine is 'brought from one stable state to another' 😝

@ayuusweetfish
Copy link
Contributor Author

ayuusweetfish commented Sep 28, 2019

Rebased and all tests are passing now.

@profan
Copy link
Contributor

profan commented Sep 30, 2019

CC @akien-mga

@@ -227,10 +250,13 @@ PoolVector<int> AStar::get_point_connections(int p_id) {
return point_list;
}

bool AStar::are_points_connected(int p_id, int p_with_id) const {
bool AStar::are_points_connected(int p_id, int p_with_id, bool bidirectional) const {
Copy link
Member

Choose a reason for hiding this comment

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

Should be p_bidirectional.

ERR_FAIL_COND(!segments.has(s));

segments.erase(s);
void AStar::disconnect_points(int p_id, int p_with_id, bool bidirectional) {
Copy link
Member

Choose a reason for hiding this comment

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

Same here, p_bidirectional.

@akien-mga
Copy link
Member

We're passed release freeze now, but since it was well reviewed and includes tests, I guess we can make an exception to still merge this. I left a nitpick about the naming of a parameter, but let's merge as is to get more extensive testing as early as possible.

@akien-mga akien-mga merged commit ed373a6 into godotengine:master Nov 7, 2019
@akien-mga
Copy link
Member

Thanks!

@ayuusweetfish
Copy link
Contributor Author

Ohh glad to see this finally settled! Should there be further issues spotted I shall still be ready to help >ᴗ<

As for parameter naming, will the maintainers team fix it or do I open a new simple PR?

@akien-mga
Copy link
Member

As for parameter naming, will the maintainers team fix it or do I open a new simple PR?

There are quite a few occurrences where the convention is not well respected, so we'll do a pass at fixing everything while refactoring code for 4.0 I think.

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

Successfully merging this pull request may close these issues.

AStar bidirectional argument in methods
7 participants