Architecture¶
In this part, the generic architecture of tree value will be introduced. You can try to understand its core ideas by reading this page.
Like that in numpy and pytorch, tree value’s data layer and logic layer are separated with each other.
The data layer is called Tree
and the logic layer is called TreeValue
.
One or more tree values may be pointed to one tree, they share the same memory.
In order to support the tree-to-tree operations (tree-to-value or value-to-value are special cases of tree-to-tree) ,func_treelize
are provided to transform a common value-based function to a tree-based function.
The core architecture is like this:
Tree¶
Tree
class is the data layer of the TreeValue
. The relation with Tree
and TreeValue
is similar to that of torch.Tensor
and torch.Buffer
. Actually, there can be multiple TreeValue
nodes pointed at the same Tree
node, they will share the same memory and when one the them are modified, the other TreeValue
pointed at the same position will be effected all at a time.
Here is a example with the usage of the same memories.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from treevalue import TreeValue if __name__ == '__main__': t1 = TreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}}) t2 = TreeValue(t1) # use the same memory with t1 print("Initial t1:") print(t1) print("Initial t2:") print(t2) print() t1.a, t1.x.c = 7, 5 # only t1 is updated in code print("Updated t1:") print(t1) print("Updated t2:") print(t2) print() |
The output is like the following, you can see the memory address of t1
and t2
are the same, when t1
is updated, t2
will be changed together.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | Initial t1: <TreeValue 0x7fe96bf59a30> ├── a --> 1 ├── b --> 2 └── x --> <TreeValue 0x7fe96bfd3ee0> ├── c --> 3 └── d --> 4 Initial t2: <TreeValue 0x7fe96bf59a30> ├── a --> 1 ├── b --> 2 └── x --> <TreeValue 0x7fe96bfd3ee0> ├── c --> 3 └── d --> 4 Updated t1: <TreeValue 0x7fe96bf59a30> ├── a --> 7 ├── b --> 2 └── x --> <TreeValue 0x7fe96bfd3ee0> ├── c --> 5 └── d --> 4 Updated t2: <TreeValue 0x7fe96bf59a30> ├── a --> 7 ├── b --> 2 └── x --> <TreeValue 0x7fe96bfd3ee0> ├── c --> 5 └── d --> 4 |
For contributor, you can get the tree instance inside of a TreeValue
instance, by using _detach()
method, like the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from treevalue import TreeValue if __name__ == '__main__': t1 = TreeValue({'a': 1, 'b': 2, 'x': {'c': 3, 'd': 4}}) storage = t1._detach() # tree is the data tree data = storage.detach() print('t1:') print(t1) print('tree storage:') print(storage) print('data:') print(data) |
1 2 3 4 5 6 7 8 9 10 11 12 | t1: <TreeValue 0x7f39c2f7fa30> ├── a --> 1 ├── b --> 2 └── x --> <TreeValue 0x7f39c2fd3ee0> ├── c --> 3 └── d --> 4 tree storage: <TreeStorage at 0x7f39c2f7fa30, keys: ('a', 'b', 'x')> data: {'a': 1, 'b': 2, 'x': <TreeStorage at 0x7f39c2fd3ee0, keys: ('c', 'd')>} |
For further information, take a look at API documentation of BaseTree.
TreeValue¶
The TreeValue
is the logic layer, all the operations and calculations are performed on this layer.
There are 2 primitive TreeValue
classes in this project, which are called TreeValue
and FastTreeValue
. In class TreeValue
, only the least necessary features are implemented, while the common and frequently-used features are all implement in the FastTreeValue
, which inherits from TreeValue
.
When coding, if you need to define a kind of tree which has the common convenient features, just implement class FastTreeValue
or use function general_tree_value
is okay. But if you need to define your own operations from an empty template, just inherit the raw TreeValue
class.
For further information of TreeValue
, take a look at:
func_treelize¶
Function func_treelize
is the core feature of this project, almost all the convenient calculations and operations are based on this function.
For further information of func_treelize
, take a look at the following pages and their source code implements: