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

Tree controller #492

Merged
merged 15 commits into from Mar 25, 2021
Merged

Tree controller #492

merged 15 commits into from Mar 25, 2021

Conversation

kwcantrell
Copy link
Collaborator

@kwcantrell kwcantrell commented Mar 17, 2021

This PR implements a new object TreeController that will allow empress to support dynamic shearing based on feature metadata or even sample metadata. TreeController will shear the tree based on a set of tip names (based on https://github.com/wasade/improved-octo-waddle/blob/0e9e75b77238acda6752f59d940620f89607ba6b/bp/_bp.pyx#L732). Also, to make integration of TreeController as seamless as possible, all input and output of TreeController will be w.r.t the original tree. In other words empress.js will not know if a tree has been sheared. Instead, TreeController will internally convert its input from the original tree to the sheared tree and back. This makes handling the metadata A LOT easier since empress indexes metadata using a nodes post-order position (w.r.t the non-sheared tree).

In addition, TreeController introduces iterators for the post-order and in-order traversals (now we no longer need to do for (i=1; i < this.tree.size; i++) to perform post-order traversal. Instead, we simply do for(node of this.tree.postorderTraversal()))

I've gone through a lot of different iterations of this PR so I am not sure how simple this final product turned out. So we can also setup a meeting if it makes reviewing this PR easier. 🙃

Copy link
Member

@ElDeveloper ElDeveloper left a comment

Choose a reason for hiding this comment

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

This is amazing. I am having quite a lot of fun with it. For those curious about it:

shearing


Recoloring needs to happen manually, etc but I think that's a glitch outside of the scope of this PR.

empress/support_files/js/bp-tree.js Outdated Show resolved Hide resolved
empress/support_files/js/bp-tree.js Show resolved Hide resolved
Comment on lines 1027 to 1028
var newToOld = {};
var oldToNew = {};
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this is a good opportunity to use the Map object?

empress/support_files/js/empress.js Show resolved Hide resolved
this._treeData[i][this._tdToInd.arcendangle] =
data.arcEndAngle[i];
data.arcEndAngle[j++];
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 put the j increment in a separate line? Seems a bit "hidden" this way.

@@ -929,7 +929,7 @@ require(["jquery", "ByteArray", "BPTree"], function ($, ByteArray, BPTree) {
);

var keep = new Set(["4", "6", "7", "10", "11"]);
var result = preShearBPTree.shear(keep);
var result = preShearBPTree.shear(keep).tree;
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 add tests for the oldToNew and newToOld maps?


// initialize
for (var i = 1; i <= this.currentTree.size; i++) {
this.origToCur[i] = i;
Copy link
Member

Choose a reason for hiding this comment

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

I suggest keeping the names consistent with the other classes.

This is just a suggestion but maybe consider these alternative names:

  • "fullToSheared" and "shearedToFull"
  • "pastToPresent" and "presentToPast"

Copy link
Member

Choose a reason for hiding this comment

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

Same renaming would need to apply to the properties for example this.full = tree and this.sheared = this.full.shear(tips) ....

* to Numbers representing the minimum, maximum, and
* average non-root node length in the tree.
*/
TreeController.prototype.getLengthStats = function () {
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for documenting this class so clearly. Although I am having a bit of a hard time figuring out when the methods refer to the current or to the original tree. What do you think about writing a property for "current" and "original" as part of the controller so that their methods are directly reused from those objects. Instead of having to replicate each method at this class' level.

For example using JavaScript's defineProperty. For example

Object.defineProperty(this, 'currentTree', {
  value: this.model.currentTree,
  writable: false
});

Other alternative I can think of is making the names of the methods more expressive. For example TreeController.prototype.getCurrentLengthStats instead of just getLengthStats.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Although I am having a bit of a hard time figuring out when the methods refer to the current or to the original tree.

In a way this is the point of TreeController. From a users perspective, everything is in relation to the "original" tree which is why each method from BPTree is replicated on the class level of TreeController. Essentially, I wanted empress to be completely unaware of tree shearing so that additional logic does not need to be added to empress.js to account for tree shearing.

The tldr is that empress is setup in such a way that it only interacts with tree through the various traversal methods (which handles the conversion between the sheared tree and original tree automatically). However, if you want more details, we can setup a quick meeting as it would be a lot easier to show you.

Copy link
Member

Choose a reason for hiding this comment

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

That makes sense, thanks for explaining. I think the only methods that threw me off were those related to "names" which I think are a special case based on our offline discussion.

empress/support_files/js/empress.js Outdated Show resolved Hide resolved
empress/support_files/js/empress.js Outdated Show resolved Hide resolved
@kwcantrell
Copy link
Collaborator Author

Thanks @ElDeveloper for posting a live demonstration! Like you said this PR is not meant to handle recoloring, the legend, etc. That will all be handled in a separate PR that deals with the UI.

// set up length getter
var branchMethod = this.branchMethod;
var checkLengthsChange = LayoutsUtil.shouldCheckBranchLengthsChanged(
branchMethod
);
var lengthGetter = LayoutsUtil.getLengthMethod(
branchMethod,
this._tree
this._tree.model.currentTree
Copy link
Member

Choose a reason for hiding this comment

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

You may want to expose a method for the TreeController that exposes the tree of the model. Having large chains where you need to know the class of an attributes' attributes, the system can unintendedly get somewhat complex.

@gwarmstrong
Copy link
Member

This looks awesome! Am I correct that it is not hooked up to the interface in any way yet?

@ElDeveloper
Copy link
Member

ElDeveloper commented Mar 18, 2021 via email

kwcantrell and others added 3 commits March 18, 2021 17:55
merged @ElDeveloper's suggestions

Co-authored-by: Yoshiki Vázquez Baeza <yoshiki@ucsd.edu>
@emperor-helper
Copy link

The following artifacts were built for this PR: empire-biplot.qzv, empire.qzv, empress-tree.qzv, just-fm.qzv, plain.qzv

Copy link
Member

@ElDeveloper ElDeveloper left a comment

Choose a reason for hiding this comment

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

Thanks so much @kwcantrell!

empress/support_files/js/tree-controller.js Outdated Show resolved Hide resolved
@kwcantrell kwcantrell mentioned this pull request Mar 25, 2021
@ElDeveloper ElDeveloper merged commit 1939236 into biocore:master Mar 25, 2021
@ElDeveloper
Copy link
Member

Thanks so much @kwcantrell !

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.

None yet

4 participants