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

Boyer-Myrvold planarity testing algorithm #795

Merged
merged 8 commits into from
Nov 5, 2019

Conversation

Toptachamann
Copy link
Collaborator

Finally, implemented a linear time planarity testing and embedding algorithm with Kuratowski subdivision extraction option #730. The implemented algorithm is an iterative version of the Boyer-Myrvold algorithm. The implementation is tested on a large number of graph instances (10^5 triangulations and 10^5 random graphs). Here is a running time comparison with the left-right planarity testing algorithm:
benchmark

From the plot above it is clear that the Boyer-Myrvold algorithm is slightly faster than the left-right algorithm, but it has a more complicated logic. In the case of the left-right algorithm, the testing phase consists of 2 dfs traversals, embedding phase consists of 1 more dfs traversal. The dfs traversals in the left-right algorithm were also made iterative.

A couple of questions:

  • In the algorithm I needed a list which gives references of this list nodes to the users. This works similar to the heaps in the jheaps project. Currently, this class is located in the planar package. Should I move somewhere?
  • The interface PlanarityTestingAlgorithm has 3 methods for testing whether a graph is a Kuratowski subdivision. Should I move these methods to the GraphTests class?

@jsichi
Copy link
Member

jsichi commented Jun 17, 2019

Regarding the list implementation, I suggest moving it to org.jgrapht.util, and name it DoublyLinkedList. As a followup pull request after this one gets merged, use it to replace other instances where something similar appears (WarnsdorffRuleKnightTourHeuristic, and your BlossomV if applicable).

Regarding the Kuratowski subdivision checking methods, yes, they can go in GraphTests.

Copy link
Member

@d-michail d-michail left a comment

Choose a reason for hiding this comment

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

Great job! Some minor comments about typos and function names.


/**
* Computes combinatorial embedding of the input {@code graph}. This method will return
* a valid result only if the {@code graph} is planar. For more information on the combinarotial
Copy link
Member

Choose a reason for hiding this comment

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

Typo


/**
* Uses BFS to find all vertices of the {@code graph} which have a degree {@code degree}.
* This method doesn't advance to new nodes after the it finds a node with a degree {@code degree}
Copy link
Member

Choose a reason for hiding this comment

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

Typo

* @param <E> the list element type
* @author Timofey Chudakov
*/
public class ElementList<E> implements Iterable<E> {
Copy link
Member

Choose a reason for hiding this comment

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

If we are going to keep this, I suggest to add a modCount just like LinkedList and throw a ConcurrentModificationException when detected.

*
* @return the number of elements in this list
*/
public int getSize() {
Copy link
Member

Choose a reason for hiding this comment

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

Better to use similar names like the List interface.

*
* @param list the list to prepend
*/
public void prepend(ElementList<E> list) {
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be better to use similar names like the ArrayDeque. This would make it easier for other Java users. For example "prepend" -> "addFirst" and "append" -> "addLast". Same comment applies in other names also.

* @param element the element of this list to search the list node of.
* @return the list node allocate for the {@code element}, or {@code null} if this list doesn't contain
* the {@code element}
*/
Copy link
Member

Choose a reason for hiding this comment

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

Mention complexity of this method and the fact that equality checks uses method equals().

/**
* Performs sorting of the vertices by their lowpoints and adding them to the {@code separatedDfsChildList} lists
*/
private void sortVetices() {
Copy link
Member

Choose a reason for hiding this comment

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

Typo

}
}
}
// for (Node node : nodes) {
Copy link
Member

Choose a reason for hiding this comment

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

Remove commented code.

@Toptachamann
Copy link
Collaborator Author

I haven't renamed the methods DoubleLinkedList.append(DoublyLinkedList) and DoubleLinkedList.prepend(DoublyLinkedList). @d-michail let me know whether they should also be renamed.

@jsichi that's a good idea. I think using the DoublyLinkedList in the Blossom algorithm can increase the memory usage a bit, but it's definitely worth giving it a try.

@jsichi
Copy link
Member

jsichi commented Jun 24, 2019

(@Toptachamann we could also have an IntrusiveDoublyLinkedList to avoid the memory increase, like boost https://www.boost.org/doc/libs/1_64_0/doc/html/intrusive/list.html)

@jkinable
Copy link
Collaborator

jkinable commented Oct 9, 2019

@Toptachamann what's the status of this PR?

@Toptachamann
Copy link
Collaborator Author

The current issue is that I need to understand the idea about intrusive lists.

So, we need to implement something like

class IntrusiveListHook {
IntrusiveListHook prev, next;

public IntrusiveListHook next(){...}

public IntrusiveListHook prev(){...}

...
}

and then we might have

class Node extends IntrusiveListHook {
...
}

@jsichi Indeed, this is a good idea to try. The thing that I don't currently understand is how this can be used when we need to store the same node in multiple lists?

@jsichi
Copy link
Member

jsichi commented Oct 10, 2019

Hmm, I forgot about the fact that C++ can deal with multiple inheritance instantiation of the same base class, whereas Java can't. (Likewise, the Boost member technique relies on being able to calculate the memory offset from the beginning of the object to the member, which isn't applicable to Java.) So there may be no easy+clean way to do it.

In any case, this PR itself should not be held up for intrusive lists; my suggestion was to address that in a followup PR.

@d-michail
Copy link
Member

No further comments from me. This looks ready to be merged!

@jsichi
Copy link
Member

jsichi commented Oct 15, 2019

I'll go ahead and merge this in a few days unless I see new comments from @jkinable

Finally we can update our "open tasks" page!

@jkinable
Copy link
Collaborator

jkinable commented Oct 21, 2019 via email

Copy link
Collaborator

@jkinable jkinable left a comment

Choose a reason for hiding this comment

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

Beautiful, absolutely well done @Toptachamann ! I'm super happy that we can finally take this of our wish list.

* @param <E> the list element type
* @author Timofey Chudakov
*/
public class DoublyLinkedList<E> implements Iterable<E> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

For a future PR, it might be good to see whether we can also implement the List interface.

/**
* Returns a reverse iterator over this list, which starts from the {@code element}.
* Note: the returned iterator will iterate over the all list, meaning it won't
* stop at the end of the list unless the {@code element} is the beginning of the list.
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure I understand this. If an element is the first element of the list, why wouldn't the reverseIterator wrap around and walk the list back to the beginning?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point, this is a bit incorrect. The reverse iterator will stop at the beginning of the list in the case the start node is the last element.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I still find the description of reverseIteratorFrom(.) confusing. What about the following description:

Returns an iterator over this list, which traverses the list in the reverse direction. The returned iterator will iterate over the <i>entire</i> list, that is, the list is treated as if it were cyclic. The iterator starts from {@code element}, and walks the list backwards, while wrapping around at the beginning of the list. For instance, for the list [1,2,3,4], {@code reverseIteratorFrom(3)} would return 3,2,1,4.

This method throws {@link NoSuchElementException} in the case {@code element} doesn't belong to this list. This method invokes {@link #getNode(Object) getNode} to find the list node containing {@code element}.

}

/**
* Checks whether the {@code graph} is a $K_5$ subdivision.
Copy link
Collaborator

Choose a reason for hiding this comment

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

High level comment: $K_5$ subdivisions and K_{3,3}$ subdivision are quite specific. It might be a little too specific for for the general GraphTests class. For now let's leave it here, since I've no idea where else to put these.


/**
* Finds and returns the list node allocate for the {@code element}. If this list doesn't contain
* the {@code element}, returns {@code null}. The timer complexity of this method is linear in the
Copy link
Collaborator

Choose a reason for hiding this comment

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

typo: timer->time

@jkinable jkinable merged commit 5c607dc into jgrapht:master Nov 5, 2019
@jkinable
Copy link
Collaborator

jkinable commented Nov 5, 2019

Awesome work @Toptachamann ! We can finally remove this from our todo list. Keep up the good work :)

@syoon2 syoon2 mentioned this pull request Apr 12, 2024
7 tasks
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.

4 participants