Skip to content

Commit

Permalink
Modified generate with tip names
Browse files Browse the repository at this point in the history
  • Loading branch information
fredericlemoine committed May 7, 2018
1 parent ea2bfed commit e8d1f22
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ You may go to the [doc](docs/index.md) for a more detailed documentation of the
* generate: Generate random trees, branch lengths are simply drawn from an expontential(1) law
* balancedtree
* caterpillartree
* topologies: all possible topologies
* uniformtree
* yuletree
* matrix: Print (patristic) distance matrix associated to the input tree
Expand Down
17 changes: 16 additions & 1 deletion cmd/topologies.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,27 @@ import (
"github.com/spf13/cobra"
)

var generateIntreeFile string

// binarytreeCmd represents the binarytree command
var topologiesCmd = &cobra.Command{
Use: "topologies",
Short: "Generates all possible tree topologies",
Long: `Generates all possible tree topologies.`,
Run: func(cmd *cobra.Command, args []string) {
if trees, err := tree.AllTopologies(generateNbTips, generateRooted); err != nil {
var tipNames []string = make([]string, 0)
// If we have an input tree, we get the tip names of that tree
if generateIntreeFile != "none" {
treefile, trees := readTrees(generateIntreeFile)
defer treefile.Close()
t := <-trees
if t.Err != nil {
io.ExitWithMessage(t.Err)
}
tipNames = t.Tree.AllTipNames()
generateNbTips = len(tipNames)
}
if trees, err := tree.AllTopologies(generateNbTips, generateRooted, tipNames...); err != nil {
io.ExitWithMessage(err)
} else {
f := openWriteFile(generateOutputfile)
Expand All @@ -27,4 +41,5 @@ var topologiesCmd = &cobra.Command{
func init() {
generateCmd.AddCommand(topologiesCmd)
topologiesCmd.PersistentFlags().IntVarP(&generateNbTips, "nbtips", "l", 10, "Number of tips/leaves of the trees to generate")
topologiesCmd.PersistentFlags().StringVarP(&generateIntreeFile, "input", "i", "none", "Input Tree: Tip names of generate trees are taken from it")
}
54 changes: 54 additions & 0 deletions docs/commands/generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
This command generates random trees according to different models:
* `gotree generate balancedtree` : perfectly balanced binary tree
* `gotree generate caterpillartree`: caterpillar tree
* `gotree generate topologies`: all topologies
* `gotree generate uniform tree` : uniform tree (edges are added randomly in the middle of any previous edge)
* `gotree generate yuletree`: Yule-Harding model (edges are added randomly in the middle of any external edge). If `-r` is not specified, the tree is unrooted.

Expand All @@ -21,6 +22,7 @@ Usage:
Available Commands:
balancedtree Generates a random balanced binary tree
caterpillartree Generates a random caterpilar binary tree
topologies Generates all possible tree topologies
uniformtree Generates a random uniform binary tree
yuletree Generates a random yule binary tree
Expand Down Expand Up @@ -60,3 +62,55 @@ gotree generate uniformtree -s 10 -l 1000 | gotree draw svg -r -w 200 -H 200 --n
```

![uniform](generate_4.svg)

* Generate all 5 tips unrooted trees
```
gotree generate topologies -l 5
```

```
((Tip5,(Tip4,Tip1)),Tip2,Tip3);
(((Tip5,Tip4),Tip1),Tip2,Tip3);
((Tip4,(Tip5,Tip1)),Tip2,Tip3);
((Tip4,Tip1),(Tip5,Tip2),Tip3);
((Tip4,Tip1),Tip2,(Tip5,Tip3));
((Tip5,Tip1),(Tip4,Tip2),Tip3);
(Tip1,(Tip5,(Tip4,Tip2)),Tip3);
(Tip1,((Tip5,Tip4),Tip2),Tip3);
(Tip1,(Tip4,(Tip5,Tip2)),Tip3);
(Tip1,(Tip4,Tip2),(Tip5,Tip3));
((Tip5,Tip1),Tip2,(Tip4,Tip3));
(Tip1,(Tip5,Tip2),(Tip4,Tip3));
(Tip1,Tip2,(Tip5,(Tip4,Tip3)));
(Tip1,Tip2,((Tip5,Tip4),Tip3));
(Tip1,Tip2,(Tip4,(Tip5,Tip3)));
```

* Generate all 5 tips unrooted trees with tip names taken from another tree

input.nw
```
(A,(B,D),(C,E));
```

```
gotree generate topologies -i input.nw
```

```
((E,(C,A)),B,D);
(((E,C),A),B,D);
((C,(E,A)),B,D);
((C,A),(E,B),D);
((C,A),B,(E,D));
((E,A),(C,B),D);
(A,(E,(C,B)),D);
(A,((E,C),B),D);
(A,(C,(E,B)),D);
(A,(C,B),(E,D));
((E,A),B,(C,D));
(A,(E,B),(C,D));
(A,B,(E,(C,D)));
(A,B,((E,C),D));
(A,B,(C,(E,D)));
```
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Command | Subcommand
[generate](commands/generate.md) ([api](api/generate.md)) | | Generates random trees, branch lengths are simply drawn from an expontential(0.1) law
-- | balancedtree | Randomly generates perfectly balanced trees
-- | caterpillartree | Randomly generates perfectly caterpillar trees
-- | topologies | Generates all possible tree topologies
-- | uniformtree | Randomly generates uniform trees
-- | yuletree | Randomly generates Yule-Harding trees
[matrix](commands/matrix.md) ([api](api/matrix.md)) | | Prints distance matrix associated to the input tree
Expand Down
22 changes: 22 additions & 0 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1021,3 +1021,25 @@ EOF
echo "(((9,10),8),(((1,(2,8)),(3,4)),5),(6,7));" | gotree rotate sort > result
diff expected result
rm -f expected result

#gotree generate all topologies
cat > expected <<EOF
(B,D,(E,(C,A)));
(B,D,(A,(E,C)));
(B,D,(C,(E,A)));
(D,(C,A),(E,B));
(B,(C,A),(E,D));
(D,(E,A),(C,B));
(A,D,(E,(C,B)));
(A,D,(B,(E,C)));
(A,D,(C,(E,B)));
(A,(C,B),(E,D));
(B,(E,A),(C,D));
(A,(E,B),(C,D));
(A,B,(E,(C,D)));
(A,B,(D,(E,C)));
(A,B,(C,(E,D)));
EOF
echo "(A,(B,D),(C,E));" | gotree generate topologies -i - | gotree rotate sort > result
diff expected result
rm -f expected result
27 changes: 27 additions & 0 deletions tests/tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,30 @@ func TestRotateSort(t *testing.T) {
}
}
}

// Generates a 1000 tip random tree, then rotate its node neighbors to sort
// them by number of tips
//
// Topology must be the same afterwards
func TestGenerateAllTopologie(t *testing.T) {
rooted := []int{1, 1, 3, 15, 105, 945, 10395}
unrooted := []int{1, 1, 1, 3, 15, 105, 945}
var topo []*tree.Tree
var err error
for i := 3; i <= 7; i++ {
//rooted topologies
if topo, err = tree.AllTopologies(i, true); err != nil {
t.Error(err)
}
if len(topo) != rooted[i-1] {
t.Error(fmt.Sprintf("Wrong number of rooted topologies for %d tips, is %d, must be %d", i, len(topo), rooted[i-1]))
}
//unrooted topologies
if topo, err = tree.AllTopologies(i, false); err != nil {
t.Error(err)
}
if len(topo) != unrooted[i-1] {
t.Error(fmt.Sprintf("Wrong number of unrooted topologies for %d tips, is %d, must be %d", i, len(topo), rooted[i-1]))
}
}
}
39 changes: 30 additions & 9 deletions tree/treegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ func BipartitionTree(leftTips []string, rightTips []string) (*Tree, error) {
}

// generate all possible topologies with nb tips rooted or not
func AllTopologies(nbTips int, rooted bool) (trees []*Tree, err error) {
func AllTopologies(nbTips int, rooted bool, tipNames ...string) (trees []*Tree, err error) {
trees = make([]*Tree, 0, 10)
t := NewTree()
if nbTips < 3 && !rooted {
Expand All @@ -390,33 +390,48 @@ func AllTopologies(nbTips int, rooted bool) (trees []*Tree, err error) {
if nbTips < 2 && rooted {
return nil, errors.New("Cannot create all rooted topologies with less than 2 tips")
}

if len(tipNames) > 0 && len(tipNames) != nbTips {
return nil, errors.New("Length of tip name array is different from desired number of tips")
}
// We add the first 2 or 3 nodes depending on
// if the tree is rooted or not
total := 1
n := t.NewNode()
n2 := t.NewNode()
n2.SetName(fmt.Sprintf("Tip%d", total))
if len(tipNames) > 0 {
n2.SetName(tipNames[total-1])
} else {
n2.SetName(fmt.Sprintf("Tip%d", total))
}
t.ConnectNodes(n, n2).SetLength(NIL_LENGTH)

if !rooted {
total++
n3 := t.NewNode()
n3.SetName(fmt.Sprintf("Tip%d", total))
if len(tipNames) > 0 {
n3.SetName(tipNames[total-1])
} else {
n3.SetName(fmt.Sprintf("Tip%d", total))
}
t.ConnectNodes(n, n3).SetLength(NIL_LENGTH)

total++
n4 := t.NewNode()
n4.SetName(fmt.Sprintf("Tip%d", total))
if len(tipNames) > 0 {
n4.SetName(tipNames[total-1])
} else {
n4.SetName(fmt.Sprintf("Tip%d", total))
}
t.ConnectNodes(n, n4).SetLength(NIL_LENGTH)
}

t.SetRoot(n)
err = allTopologies_recur(t, nbTips, total, &trees)
err = allTopologies_recur(t, nbTips, total, &trees, tipNames...)

return
}

func allTopologies_recur(t *Tree, nbTips, total int, trees *[]*Tree) (err error) {
func allTopologies_recur(t *Tree, nbTips, total int, trees *[]*Tree, tipNames ...string) (err error) {
if total == nbTips {
(*trees) = append(*trees, t.Clone())
} else {
Expand All @@ -434,14 +449,20 @@ func allTopologies_recur(t *Tree, nbTips, total int, trees *[]*Tree) (err error)
return
}
n = t.NewNode()
n.SetName("Tip" + strconv.Itoa(total+1))

if len(tipNames) > 0 {
n.SetName(tipNames[total])
} else {
n.SetName(fmt.Sprintf("Tip%d", total+1))
}

if e1, e2, n1, err = t.GraftTipOnEdge(n, e); err != nil {
return
}
e1.SetLength(NIL_LENGTH)
e2.SetLength(NIL_LENGTH)
e.SetLength(NIL_LENGTH)
if err = allTopologies_recur(t, nbTips, total+1, trees); err != nil {
if err = allTopologies_recur(t, nbTips, total+1, trees, tipNames...); err != nil {
return
}
// We remove the last added edges and nodes
Expand Down

0 comments on commit e8d1f22

Please sign in to comment.