We define the length of a path in a tree as the sum of the lengths of its edges, rather than the number of edges on the path. As a result, the evolutionary distance between two present-day species corresponding to leaves $$i$$ and $$j$$ in a tree $$\mathcal{T}$$ is equal to the length of the unique path connecting $$i$$ and $$j$$, denoted $$d_{i, j}(\mathcal{T})$$.

Assignment

Define a class DistanceMatrix that can be used to represent distance matrices. A distance matrix is a square symmetric matrix with diagonal elements equal to zero and positive off-diagonal elements. The initialisation method takes the same arguments as the numpy.array1 method. In case these arguments do not represent a valid distance matrix, a ValueError must be raised with the message invalid distance matrix. The class DistanceMatrix must support at least the following methods:

Make sure the built-in function repr and str return string representations of objects of the class DistanceMatrix that are formatted as a list of lists (see example below).

Define a class UnrootedTree that can be used to represent unrooted trees whose nodes are uniquely labeled and whose edges are weighted with a floating point number. The initialisation method takes zero or more edges. Each edge is represented as a tuple $$(i, j, w)$$, with $$i$$ and $$j$$ the labels of the nodes connected by the edge and $$w$$ the edge weight. The class UnrootedTree must support at least the following methods:

Make sure the built-in function repr returns string representations of objects of the class UnrootedTree that are formatted according to the example given below.

Example

In the following example we assume the text files distances.txt6 and edges.txt7 to be located in the current directory.

>>> D = DistanceMatrix([[0, 13, 21, 22], [13, 0, 12, 13], [21, 12, 0, 13], [22, 13, 13, 0]])
>>> D
DistanceMatrix([[0, 13, 21, 22], [13, 0, 12, 13], [21, 12, 0, 13], [22, 13, 13, 0]])
>>> print(D)
[[0, 13, 21, 22], [13, 0, 12, 13], [21, 12, 0, 13], [22, 13, 13, 0]]

>>> D = DistanceMatrix.loadtxt('distances.txt')
>>> D
DistanceMatrix([[0.0, 13.0, 21.0, 22.0], [13.0, 0.0, 12.0, 13.0], [21.0, 12.0, 0.0, 13.0], [22.0, 13.0, 13.0, 0.0]])
>>> D.savetxt('copy.txt', fmt='%g')
>>> print(open('copy.txt').read().rstrip())
0 13 21 22
13 0 12 13
21 12 0 13
22 13 13 0

>>> tree = UnrootedTree((0, 4, 11), (1, 4, 2), (2, 5, 6), (3, 5, 7), (4, 5, 4))
>>> tree
UnrootedTree((0, 4, 11.0), (1, 4, 2.0), (2, 5, 6.0), (3, 5, 7.0), (4, 5, 4.0))
>>> tree.path(0, 2)
[0, 4, 5, 2]
>>> tree.path(1, 3)
[1, 4, 5, 3]

>>> tree = UnrootedTree.loadtxt('edges.txt')
>>> tree
UnrootedTree((0, 4, 11.0), (1, 4, 2.0), (2, 5, 6.0), (3, 5, 7.0), (4, 5, 4.0))
>>> tree.distance_matrix().savetxt('distances.txt', fmt='%g')
>>> print(open('distances.txt').read().rstrip())
0 13 21 22
13 0 12 13
21 12 0 13
22 13 13 0