Skip to content

benjtinsley/ttree

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

53 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

T Tree data structure

All my little plans and schemes, lost like some forgotten dreams. Seems that all I really was doing was waiting for you.

John Lennon, Real Love

The T Tree data structure aims to blend human cognitive behaviors with data management, emulating learning, memory, focus, and the satisfaction that comes along with skill mastery. This readme discusses the T Tree’s framework, its invariants and unique constraints, and potential interdisciplinary applications. I've also included a narrative about my thought process as to why I wanted to build this in the first place.

Table of Contents

  1. Structures & Actions
  2. Example
  3. Tests
  4. Potential Applications
  5. Narrative
  6. Weird Thoughts and Places My Brain Went
  7. ChatGPT Summary (TLDR)

Structures and Actions

Data Structures

The T Tree is comprised of 2 data structures: Talent Nodes and Task Nodes, both of which are structured into binary trees, but with different properties.

T Tree

A singular T Tree is initialized with the following properties. Type denoted in parenthesis:

  • head: Pointer to an uncountable and unknown head with no name and rank of infinity, from which all Talent Nodes come (Talent Node)
  • time: The current time of the T Tree (integer)
  • total_nodes: The total Talent Nodes in the T Tree (integer)
  • lost_talents: A list of all Talent Nodes that must be cut from the tree, but cannot be totally removed (list: Talent Node)

Talent Nodes

Every Talent Node is initialized with the following properties. Type denoted in parenthesis:

  • name: The name of the talent (string)
  • rank: The current rank. The higher the rank, the closer the node is to the head. Talent Node Head initial value = ∞ All other Talent Node initial value = 0 (integer)
  • last_access: The time value in which the last access to this node happened (integer)
  • parent: Pointer to the parent (Talent Node)
  • child_left: Pointer to the left child (Talent Node)
  • child_right: Pointer to the right child (Talent Node)
  • recent_task_map: Key value mapping of recent tasks added to this talent as the time it was added and name of task (integer, string)
  • max_tasks: Number of tasks that can be added to the recent_task_map before they are all converted to Task Nodes (integer)
  • task_head: Pointer to the head of the Task Nodes (Task Node)
  • burnout_limit: The minimum number of steps in time between successive tasks in the Recent Task Map that must occur once to determine if Burnout should be set (integer)
  • is_burnout: Flag if this talent is burnt out or not (boolean)
  • is_mastered: Flag denoting if this talent has been mastered (boolean)

Task Nodes

Every Talent Node is initialized with the following properties. Type denoted in parenthesis:

  • task_name: The name of the task (string)
  • child_left: Pointer to the left child (Task Node)
  • child_right: Pointer to the right child (Task Node)
  • parent: Pointer to the parent (Task Node)
  • creation_time: Time when the task was created (integer)
  • last_access_time: Last time the task was accessed, initially set to creation time (integer)
  • is_burnt: Flag indicating if the Task Node was created or last accessed while the parent Talent Node was burnt out (boolean)

Actions

The T Tree only has two front end actions, add_task() and access_task(). The steps of which are defined:

add_task(task_name: string=required, talent_name: string=optional) -> void

  1. Using in-order recursive search, the tree fetches the Talent Node associated with talent_name given.
    • If no talent node is found, a new talent node with rank 0 is created, and the number of total nodes is incremented.
  2. The current time of the tree is incremented.
  3. The talent node is then shifted to the left most position at its rank, denoting it has been accessed the most recently.
    • If shifting it to the leftmost position creates too many nodes at this level (ie, the talent node is added to rank 0 and rank 0 is currently full), the right most node is cast to the Lost Talents list to make way for this new, exciting talent and the total number of nodes is decremented.
  4. The task is then added to the node as the final position in the Recent Task Map with the current time as its key and task_name as its value.
    • If the current time minus the time of the last item in the Recent Task Map is less than double the total nodes in the tree, the Recent Task Map is cleared out, denoting it's been too long since the last item was added, and everything must be relearned.
    • If the difference between any two time values for items in the Recent Task Map is less than the Burnout Limit on this Talent Node, the Is Burnout flag is set to true, otherwise it is set to false.
  5. If the length of the Recent Task Map is equal to the Max Tasks on this Talent Node, the items in the Recent Task Map are converted to Task Nodes and the Talent Node rank is increased by 1.
    • If Is Burnout is set to true, the Task Nodes are added in order as the right child of the right most Task Node in the tree. Each Task Node is initialized with Is Burnt set to true.
    • If Is Burnout is set to false, the Task Nodes are added in order as the first open position when traversing the tree top to bottom, left to right.
    • In both cases, the Creation Time and the Last Access Time are both set to the current time.
Talent Node Promotion
  1. If the Talent Node Rank increased in the last step, the Talent Node is promoted, meaning it joins the Talent Nodes of the new rank at their depth, if they exist, or is put onto a new depth all alone.
    • If the rank of the parent node is the same as the Talent Node rank, it means that the Talent Node will removed from its current rank depth and added into its parent's rank depth. In this case, there is a potential for sending nodes to the Lost Talents list at these levels, relative to the Talent Node:
      • Parent Rank
      • Child Rank
    • If the rank of the parent node is not the same as the Talent Node rank, it means that the Talent node will be removed from its current rank depth and put on a new depth between its parent's depth and its former depth. In this case, there is a potential for sending nodes to the Lost Talents list at these levels, relative to the Talent Node:
      • Current Rank
      • Child Rank
  2. The Talent Node is inserted to the left-most position at the new rank depth. Nodes that can no longer fit in the tree, due to lack of bonds will be cast to the Lost Talents list, along with any recursive child Talent Nodes they may have.

access_task(task_name: string=optional, talent_name: string=optional) -> boolean

  1. Using in-order recursive search, the tree fetches the Talent Node associated with talent_name given.
  2. The current time of the tree is incremented.
    • If no Talent Node of the given node was found (perhaps this Talent Node was placed in the Lost Talents list?) the function returns false.
  3. The task is sought out in the Talent Node. First, the Recent Task Map is searched for the task_name.
    • If the task is found, return true.
  4. The Task Nodes are searched via in-order search from the Task Head.
    • If no Task Head exists, or no Task Node exists with the matching task_name, return false.
    • If a Task Node exists with the matching task_name, it updates the Last Access Time on the matching Task Node.
  5. The Task Node tree is then converted to a max heap by the Last Access Time, only for the Task Nodes where Is Burnt is set to false. The Task Nodes where Is Burnt is set to true are put at the end of the heap. Finally, it returns true.

There is a third and lesser used action, die() which allows all Talent Nodes and Task Nodes to be removed from memory.

die(node: TalentNode, show_life: boolean=optional) -> void

It begins at the Head Node of the T Tree, and recursively searches for each of the Talent Node children, doing the following:

  1. Delete the Tasks in the Recent Task Map
  2. Recursively delete the Task Nodes starting with the task_head.
  3. Delete the Talent Node
  4. Go through each item in the Lost Talents list and repeat steps 1 and 2 for each Talent Node there.

If show_life is set to True, each Talent Node Name and task name will be printed into the console before it is deleted.

Example

(Recommended to read this at README.md with the file explorer turned off (command/control + B).

Let's show an example of adding tasks to a Talent Node to update its rank. Given the tree below, we can see the Talent Nodes D, E, F & G at the bottom at rank 0, Talent Nodes B and C above them at rank 2, above them is rank 4 Talent Node A, and at the top is a nameless Talent Node at rank infinity. The purpose of it is to ensure more than 1 node can be added to the tree, by offering a left and right position for the first 2 nodes. It's mysterious that it has to work like this.


                                 Talent Nodes

β”Œβ”€  ──  ──  ──  ──  ──  ──  ──  ──  ┳━━━┓
 Rank ∞                             ┃   ┃
 ──  ──  ──  ──  ──  ──  ──  ──  β”€β”€β”Œβ”»β”β”β”β”›
                                   β”‚
                                   β”‚
β”Œβ”€  ──  ──  ──  ──  ──  ──  ──  ─┳━┻━┓
 Rank 4                          ┃ A ┃
 ──  ──  ──  ──  ──  ──  ──  β”€β”€β”Œβ”€β”»β”β”β”β”»β”
                               β”‚      β”‚
β”Œβ”€  ──  ──  ──  ──  ──  β”³β”β”β”β”³β”€β”€β”˜β”€β”€  ──└──┳━━━┓
β”‚Rank 2                 ┃ B ┃            ┃ C ┃
β””  ──  ──  ──  ──  ── β”Œβ”€β”»β”β”β”β”»β” ──  ──  ┬─┻━━━┻─┐
β”Œβ”€  ──  ──  ──  ──┏━━━┫ ──  ─╋━━━┳ ┏━━━┫──  ── ┣━━━┓
β”‚Rank 0           ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
β””  ──  ──  ──  ── ┗━━━┛──  ──┗━━━┛ ┻━━━┻─  ──  ┻━━━┛

Now, we want to add 4 tasks to Talent Node C, which is currently at rank 2:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')    β”‚          Talent Nodes
β”‚add_task('1', 'C')    β”‚
β”‚add_task('2', 'C')    β”‚             ┏━━━┓
β”‚add_task('3', 'C')    β”‚             ┃   ┃
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”Œβ”»β”β”β”β”›
                                    β”‚
                                    β”‚
                                  ┏━┻━┓
                                  ┃ A ┃
                                β”Œβ”€β”»β”β”β”β”»β”
                                β”‚      β”‚
                         β”β”β”β”β”³β”€β”€β”˜      └──┳━━━┓
                         ┃ B ┃            ┃ C ┃
                       β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

When adding the first task, it recursively searches the tree for Talent Node "C" by first checking the head, then A, then B, D, E and finally C.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')    │─ ─ ─ ┐   Talent Nodes
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”” ─ ─ ▢┏━━━┓
                                     ┃   ┃
                                    β”Œβ”»β”β”β”β”›
                                   ─│
                                  β”‚ β”‚
                                  ▼━┻━┓
                           β”Œ ─ ─ ─┃ A ┃
                                β”Œβ”€β”»β”β”β”β”»β”
                           β–Ό    β”‚      β”‚
                         β”β”β”β”β”³β”€β”€β”˜      └──┳━━━┓
                      ─ ─┃ B ┃   ─ ─ ─ ─ ▢┃ C ┃
                     β–Ό β”Œβ”€β”»β”β”β”β”»β” β”‚       β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛
                     β”‚          β–²
                                β”‚
                     β”” ─ ─ ─ ─ ─

We will update the time:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')    β”‚          Talent Nodes
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                     ┏━━━┓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         ┃   ┃
β”‚ ttree.time += 1          β”‚        β”Œβ”»β”β”β”β”›
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
                                    β”‚
                                  ┏━┻━┓
                                  ┃ A ┃
                                β”Œβ”€β”»β”β”β”β”»β”
                                β”‚      β”‚
                         β”β”β”β”β”³β”€β”€β”˜      └──┳━━━┓
                         ┃ B ┃            ┃"C"┃
                       β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

We unhook it from its current bonds:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')    β”‚          Talent Nodes
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                     ┏━━━┓
                                     ┃   ┃
                                    β”Œβ”»β”β”β”β”›
                                    β”‚
                                    β”‚
                                  ┏━┻━┓
                                  ┃ A ┃
                                β”Œβ”€β”»β”β”β”β”›
                                β”‚
                         β”β”β”β”β”³β”€β”€β”˜         ┏━━━┓
                         ┃ B ┃            ┃"C"┃
                       β”Œβ”€β”»β”β”β”β”»β”           ┗━━━┛
                   ┏━━━┫      ┣━━━┓ ┏━━━┓       ┏━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

...and move it to the left-most position at this rank, shifting any other nodes to the right:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')    β”‚          Talent Nodes
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                     ┏━━━┓
                                     ┃   ┃
                                    β”Œβ”»β”β”β”β”›
                                    β”‚
                                    β”‚
                                  ┏━┻━┓
                                  ┃ A ┃
                                β”Œβ”€β”»β”β”β”β”»β”
                                β”‚      β”‚
                         β”β”β”β”β”³β”€β”€β”˜      └──┳━━━┓
                         ┃"C"┃            ┃ B ┃
                       β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

Now we look into the Recent Task Map of Talent C and add it with the current time as the key:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')    β”‚          Talent Nodes
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                     ┏━━━┓               β”‚       Recent Task Map        β”‚β–“
                                     ┃   ┃               β”‚                              β”‚β–“
                                    β”Œβ”»β”β”β”β”›               β”‚          {42: '0'}           β”‚β–“
                                    β”‚                β”Œ β–Ά β”‚                              β”‚β–“
                                    β”‚             β”Œ ─    β”‚                              β”‚β–“
                                  ┏━┻━┓        β”Œ ─       β”‚         Max Tasks: 5         β”‚β–“
                                  ┃ A ┃     β”Œ ─          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β–“
                                β”Œβ”€β”»β”β”β”β”»β” β”Œ ─              β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“
                                β”‚     ─│─
                         β”β”β”β”β”³β”€β”€β”˜β”Œ ─ β”˜ └──┳━━━┓
                         ┃"C"┃─ ─         ┃ B ┃
                       β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

Now when we add the next task, the tree doesn't have to be traversed as thoroughly in order to find our desired Talent Node. Let's go ahead and add the next task to the tree:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')β–€   β”‚          Talent Nodes
β”‚add_task('1', 'C')    │─ ─ ─ ┐                          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜       ─ ─ ─▢┏━━━┓               β”‚       Recent Task Map        β”‚β–“
                                     ┃   ┃               β”‚                              β”‚β–“
                                    β”Œβ”»β”β”β”β”›               β”‚      {42: '0', 43: '1'}      β”‚β–“
                                   ─│                β”Œ β–Ά β”‚                              β”‚β–“
                                  β”‚ β”‚             β”Œ ─    β”‚                              β”‚β–“
                                  ▼━┻━┓        β”Œ ─       β”‚         Max Tasks: 5         β”‚β–“
                           β”Œ ─ ─ ─┃ A ┃     β”Œ ─          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β–“
                                β”Œβ”€β”»β”β”β”β”»β” β”Œ ─              β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“
                           β–Ό    β”‚     ─│─
                         β”β”β”β”β”³β”€β”€β”˜β”Œ ─ β”˜ └──┳━━━┓
                         ┃"C"┃─ ─         ┃ B ┃
                       β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

Now, let's show how it would look with all the specified tasks added to Talent Node C:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('0', 'C')β–€   β”‚          Talent Nodes
β”‚add_task('1', 'C')β–€   β”‚                                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('2', 'C')β–€   │─ ─ ─ ─ ─ ─ ▢┏━━━┓               β”‚       Recent Task Map        β”‚β–“
β”‚add_task('3', 'C')β–€   β”‚             ┃   ┃               β”‚                              β”‚β–“
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”Œβ”»β”β”β”β”›               β”‚ {42: '0', 43: '1', 44: '2',  β”‚β–“
                                   ─│                β”Œ β–Ά β”‚           45: '3'}           β”‚β–“
                                  β”‚ β”‚             β”Œ ─    β”‚                              β”‚β–“
                                  ▼━┻━┓        β”Œ ─       β”‚                              β”‚β–“
                           β”Œ ─ ─ ─┃ A ┃     β”Œ ─          β”‚         Max Tasks: 5         β”‚β–“
                                β”Œβ”€β”»β”β”β”β”»β” β”Œ ─             β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β–“
                           β–Ό    β”‚     ─│─                 β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“
                         β”β”β”β”β”³β”€β”€β”˜β”Œ ─ β”˜ └──┳━━━┓
                         ┃"C"┃─ ─         ┃ B ┃
                       β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

Beautiful. Now the fun starts. Let's add one more task to Talent Node C:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚add_task('4', 'C')    │─ ─ ─ ┐   Talent Nodes
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                 β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                              β”” ─ ─ ▢┏━━━┓               β”‚       Recent Task Map        β”‚β–“
                                     ┃   ┃               β”‚                              β”‚β–“
                                    β”Œβ”»β”β”β”β”›               β”‚ {42: '0', 43: '1', 44: '2',  β”‚β–“
                                   ─│                β”Œ β–Ά β”‚      45: '3', 46: '4'}       β”‚β–“
                                  β”‚ β”‚             β”Œ ─    β”‚ ──┐                          β”‚β–“
                                  ▼━┻━┓        β”Œ ─       β”‚   └──┐                    β”‚  β”‚β–“
                           β”Œ ─ ─ ─┃ A ┃     β”Œ ─          β”‚      β””β–Ά Max Tasks: 5    β”Œβ”€β”˜  β”‚β–“
                                β”Œβ”€β”»β”β”β”β”»β” β”Œ ─             β”‚       Burnout Limit: 2 β—€β”˜    β”‚β–“
                           β–Ό    β”‚     ─│─                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β–“
                         β”β”β”β”β”³β”€β”€β”˜β”Œ ─ β”˜ └──┳━━━┓           β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“
                         ┃"C"┃─ ─         ┃ B ┃
                       β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
                   ┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
                   ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
                   ┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

A few checks are made here:

  1. We see that the Max Tasks now equals the total amount of tasks in the Recent Task Map
  2. We check the Burnout Limit, which is 2. This means that when looping through all the nodes in the Recent Task Map, the difference between the key of one task and the key of the next task in the list must be at least the Burnout Limit at least once in the list. Unfortunately for us, all these tasks were added in sequential order, so the greatest difference between any two nodes in order is 1.

Therefore, this Talent Node is burnt out. We need to now convert these tasks to Task Nodes, but since this node is burnt out, they will be added in a non-efficient log(n) manner, akin to a linked list. This will prioritize them on the far right side of the tree, ensuring they will be retrieved last when access_task is called on these tasks. There are already Task Nodes in this Talent Node, because it is at rank 2, meaning the Recent Task Map has been converted to Task Nodes twice already. For the sake of this example, we will assume the prior tasks were added when Talent Node C was not burnt out, meaning all the prior Task Nodes were added in a balanced tree, top to bottom, left to right.

For this step, all the tasks in this Recent Task Map will be added to the right-most position in the tree, relative to the Task Head. They will be saved along with their creation time and time they were last accessed as well as a flag marking this specific Task Node was added when the Talent Node was burnt out. For the purpose of space, we will only show the task name in the tree. We will also denote the prior added Task Nodes as a balanced tree in abbreviated form:

                                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
               Talent Nodes                         β”‚C Task Headβ”‚
                                                    β””β”€β”€β”€β”€β”€β–²β–²β”€β”€β”€β”€β”˜
                  ┏━━━┓                      β”Œ β–Ά         β•±  β•²
                  ┃   ┃                   β”Œ ─           β•±    β•²
                 β”Œβ”»β”β”β”β”›                β”Œ ─             β•±      β•²
                 β”‚                  β”Œ ─               β•±        β•²
                 β”‚               β”Œ ─                 β•±          β•²
               ┏━┻━┓          β”Œ ─                   β•±            β•²
               ┃ A ┃       ─ ─                     β•±              β•²
             β”Œβ”€β”»β”β”β”β”»β” β”Œ ─ β”˜                       β•±                β•²
             β”‚     ─│─                           β•±                  β•²
      β”β”β”β”β”³β”€β”€β”˜β”Œ ─ β”˜ └──┳━━━┓                    β•±                    β•²
      ┃"C"┃─ ─         ┃ B ┃                   β•±                      β•²
    β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”                β•±     (Balanced Tree)    β•²
┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓           β•±                          β•²
┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃          β•±                            β•²
┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛         ───────────────────────────────┐
                                                                          β”‚
                                                                        ┏━┻━┓
                                                                        ┃'0'┃
                                                                        ┗━━┳┛
                                                                           └─┐
                                                                             β”‚
                                                                           ┏━━━┓
                                                                           ┃'1'┃
                                                                           ┗━━┳┛
                                                                              └─┐
                                                                                β”‚
                                                                              ┏━━━┓
                                                                              ┃'2'┃
                                                                              ┗━━┳┛
                                                                                 └─┐
                                                                                   β”‚
                                                                                 ┏━━━┓
                                                                                 ┃'3'┃
                                                                                 ┗━━┳┛
                                                                                    └─┐
                                                                                      β”‚
                                                                                    ┏━━━┓
                                                                                    ┃'4'┃
                                                                                    ┗━━━┛

Yikes! How inefficient and ugly! I bet you had to scroll down to see it all. Note that the only way to bring these Task Nodes back into a balanced tree is to access them again individually, which will update its last access time and set the burnt out flag to false. The Task Node tree will then max heapify itself for all nodes with the burnt out flag set to false by the last accessed time.

Ok that was fun! But we aren't done. We need to handle the rank and promotion of this node.

We now clear out the Recent Task Map and increment the rank for Talent Node C, which now puts it at rank 3.

               Talent Nodes
                                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                  ┏━━━┓               β”‚       Recent Task Map        β”‚β–“
                  ┃   ┃               β”‚                              β”‚β–“
                 β”Œβ”»β”β”β”β”›               β”‚              {}              β”‚β–“
                 β”‚                β”Œ β–Ά β”‚                              β”‚β–“
                 β”‚             β”Œ ─    β”‚                              β”‚β–“
               ┏━┻━┓        β”Œ ─       β”‚         Max Tasks: 5         β”‚β–“
               ┃ A ┃     β”Œ ─          β”‚       Burnout Limit: 2       β”‚β–“
             β”Œβ”€β”»β”β”β”β”»β” β”Œ ─             β”‚          Rank: +=1           β”‚β–“
             β”‚     ─│─                β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β–“
      β”β”β”β”β”³β”€β”€β”˜β”Œ ─ β”˜ └──┳━━━┓           β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“
      ┃"C"┃─ ─         ┃ B ┃
    β”Œβ”€β”»β”β”β”β”»β”         β”Œβ”€β”»β”β”β”β”»β”€β”
┏━━━┫      ┣━━━┓ ┏━━━┫       ┣━━━┓
┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
┗━━━┛      ┗━━━┛ ┗━━━┛       ┗━━━┛

Just as a reminder, this was our starting ranks. So what do you think happens next?

                                 Talent Nodes

β”Œβ”€  ──  ──  ──  ──  ──  ──  ──  ──  ┳━━━┓
 Rank ∞                             ┃   ┃
 ──  ──  ──  ──  ──  ──  ──  ──  β”€β”€β”Œβ”»β”β”β”β”›
                                   β”‚
                                   β”‚
β”Œβ”€  ──  ──  ──  ──  ──  ──  ──  ─┳━┻━┓
 Rank 4                          ┃ A ┃
 ──  ──  ──  ──  ──  ──  ──  β”€β”€β”Œβ”€β”»β”β”β”β”»β”
                               β”‚      β”‚
β”Œβ”€  ──  ──  ──  ──  ──  β”³β”β”β”β”³β”€β”€β”˜β”€β”€  ──└──┳━━━┓
β”‚Rank 2                 ┃"C"┃            ┃ B ┃
β””  ──  ──  ──  ──  ── β”Œβ”€β”»β”β”β”β”»β” ──  ──  ┬─┻━━━┻─┐
β”Œβ”€  ──  ──  ──  ──┏━━━┫ ──  ─╋━━━┳ ┏━━━┫──  ── ┣━━━┓
β”‚Rank 0           ┃ D ┃      ┃ E ┃ ┃ F ┃       ┃ G ┃
β””  ──  ──  ──  ── ┗━━━┛──  ──┗━━━┛ ┻━━━┻─  ──  ┻━━━┛

First, check to see if Talent Node C's parent rank is the same rank. If it is, we know we will be inserting Talent Node C into the same rank as its parent, making it a sibling. Since it is not the same rank, as 4 != 3, we know that Talent Node C will keep its parent, but inherit its siblings as children.

Since Talent Node C only has a single sibling at rank 2, Talent Node B, it will inherit a new left child in Talent Node B, but will not have any other children.

However, in doing so, there aren't enough parents for all the nodes in rank 0 to stay in the tree. Unfortunately Talent Nodes F and G must be lost from the tree, but they aren't gone, just added to the Lost Talents list. They will never be accessible again, but will persist in memory.

Therefore, the final tree structure, with ranks will be as follows:

                                  Talent Nodes

β”Œβ”€  ──  ──  ──  ──  ──  ──  ──  ──  ─┳━━━┓
β”‚Rank ∞                              ┃   ┃
β””  ──  ──  ──  ──  ──  ──  ──  ──  ─┬┻━━━┛
                                    β”‚
                                    β”‚
β”Œβ”€  ──  ──  ──  ──  ──  ──  ──  ──┏━┻━┓
β”‚Rank 4                           ┃ A ┃
β””  ──  ──  ──  ──  ──  ──  ──  ─┬─┻━━━┛
                                β”‚
β”Œβ”€  ──  ──  ──  ──  ──  β”€β”³β”β”β”β”³β”€β”€β”˜
β”‚Rank 3                  ┃"C"┃
β””  ──  ──  ──  ──  ──  ┬─┻━━━┛          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”Œβ”€  ──  ──  ──  ── ┏━━━┫                β”‚            T Tree            β”‚β–“
β”‚Rank 2            ┃ B ┃                β”‚                              β”‚β–“
β””  ──  ──  ──  β”€β”€β”Œβ”€β”»β”β”β”β”»β”€β”              β”‚    Lost Talents: [ F, G ]    β”‚β–“
β”Œβ”€  ──  ──  ─┳━━━╋  ──  ─╋━━━┓          β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜β–“
β”‚Rank 0      ┃ D ┃       ┃ E ┃           β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“
β””  ──  ──  ──┗━━━┛ ──  ──┗━━━┛

[ All Diagrams Created with Monodraw ]

Tests

This repo uses pytest. You can view the tests in the /tests directory. To run the test suite, point to the root directory and run:

pip install -r requirements.txt # installs rich and colorama for better test formatting
python3 -m tests

It will generate the following results:

  Class            Test                                          Result  
 ─────────────────────────────────────────────────────────────────────── 
  TestTTree        test_add_single_talent                        PASS    
  TestTTree        test_add_three_talents_with_lost_node         PASS    
  TestTTree        test_add_two_talents                          PASS    
  TestTTree        test_kill_tree                                PASS    
  TestTTree        test_promote_talent_in_gapped_balanced_tree   PASS    
  TestTTree        test_promote_talent_in_robust_balanced_tree   PASS    
  TestTTree        test_time                                     PASS    
  TestTalentNode   test_check_talent_node_exists                 PASS    
  TestTalentNode   test_is_burnout_toggleable                    PASS    
  TestTalentNode   test_relearn                                  PASS    
  TestTalentNode   test_time_function_retrieval                  PASS    

Potential Applications

The T Tree’s cognitive emulation lends itself to several applications:

  1. Educational software that optimizes learning with cognitive patterns, to ensure mastery of knowledge in a short amount of time.
  2. Cognitive science for visualizing and treating memory-related disorders, where researchers and doctors can create a virtual representation of a patient’s memory to diagnose exceptions in their cognition.
  3. Game development, where character progression strategies can significantly impact performance, leading to faster and more powerful actions if the progressions are done in a deliberate clustered manner, or less responsive, weaker actions if the progressions are done with too much or too little focus.

This structure offers a framework for studying balanced task engagement and the cognitive aspects of learning and recovery as it relates to human endeavor. It doesn't focus on efficiency, instead it focuses on deliberate optimalization of the entity entering in data to the structure. It stands somewhere between an abstract and a concrete data type.

Narrative

This was my final project for CU's CSPB 2270 Data Structures in spring 2024. This class offered me something on a deeper level than I think any class or topic has ever had for me. Each week, we were given a puzzle to solve in the form of a toy data structure with their strict invariants, and we were set off to build out the features that allowed the tests to pass. Not to mention, the lectures were extremely succinct and quick to watch. I could spend less than hour watching the weekly lectures and be on my way, with a compass and a basic idea of where I needed to go.

The BTrees week, for me, reached a tipping point. That assignment consumed me in a way that I never anticipated. All I wanted to do was work on it. I'm the type of person who gets more enjoyment out of something by going my own way, versus following someone else's instructions. I'd much rather turn in something wrong than something I didn't have to think to complete. Therefore, the BTrees assignment gave me many paths to explore to make it feel like an adventure. Lots of late nights. Many moments where I had to force myself away from the computer just so I could eat or see my family. It consumed me.

I turned in the BTrees assignment incomplete. I left off the remove function, but still ended up with a score with well over 100% and I promised I'd come back to the assignment over spring break or some other time when I had a lighter schedule. But the further time passed from BTrees, the more I thought about what happened to me during that project and the less I liked it. Computer Science attracts problem solvers and certain problems can consume people. It's genuinely fun to solve these problems for them and being consumed by the problem feels good and right. But something bothered me about that - during the BTrees assignment, I was being trained to think like a computer. Ultimately, it didn't matter what the bigger picture for the problem was, or the greater application (efficient storage?), it was that I was given a hard problem and step by step, I could solve that.

A question started to bother me - could there be a problem that attracted me based on it's difficulty, in which the application was unethical? Could my desire to solve problems be used as a way to do evil things by other people? These questions seemed a bit dramatic, but it bothered me and for the rest of the class, I kept the assignments at arm's length. They were still fun but I wouldn't allow myself to be consumed by them. My life was my life, after all. I wasn't a computer.

But still, the idea of data structures seemed powerful to me. It was a way to create a universe of rules in which amazing things were possible.

The week after the BTrees, we studied ADTs, which was interesting to me. It felt like a recap of everything we had studied up to that point, but set it all under an umbrella. We didn't have to worry about how it worked, just that it behaved in a way that was expected. During the lectures, my mind started to wander and I scribbled down some "Custom ADTs", as I defined them.

Custom ADTs: 1. ADT of ADTs where the inner ADT is known only when looked at via timestamp for entry/exit from queue - as a way to track emotions. 2. ADT of attributes that automatically increment if accessed or decrement if unaccessed - for traits/growth. 3. ADT of population features that reveal outcomes due to feature contraints, as in, a way to predict wars or cultural phenomena.

These had my gears grinding in the background for some time as I continued the course. By the time the final project was announced, I had already gotten quite far in my mind on one that I had originally defined as incrementing or decrementing the ranks depending on access frequency, sort of like a Splay Tree, but with the explicit intent of tracking growth in traits. Perhaps it can increment but cannot decrement? Which structure would best be suited for this? Graph? Hash Table? Binary Tree? What if there were more than one dimension to the denote the proficiency? I kept a running log of my thoughts on my dry erase board and it continued to haunt me. It felt like it was writing itself and all I had to do was put my attention to it for a little longer and it would reveal something else to me. The rules were all already there, I just had to write them down.

I would define myself as a jack-of-all-trades type person. Mastery, for whatever reason, is very difficult for me, and I often change gears before going deeper. It seemed that every time I thought about this emerging data structure, it revealed something to me about myself and how I view my own talents. Promoting talents, which in the real world means spending more time doing one specific thing, is an opportunity cost. By spending time with one thing, it cuts you off from spending time with something else. Perhaps the reason why mastery was difficult for me is there are things I don't want to get rid of. Perhaps there are things I don't want to lose. It's easier to lose something flighty and temporal than lose something large amounts of time have been invested in, so I opt for the former because I'm afraid of losing the latter. As the John Lennon quote at the beginning states, perhaps I mire in the plans and schemes because I think I haven't found the ThingTM that will promote itself to the top yet.

This class focused on efficient getting, setting, deleting and editing of information. This is an important thing to learn, but I also believe that efficiency shouldn't always be the goal in life. Efficiency is selfish and ruthless. It bypasses all other desires to accomplish the goal in as little time as possible. Optimum, on the other hand, takes many things into account to accomplish in a wholistic approach. After all, if I were to ask you to consider the most efficient workday as you could imagine and the most optimal workday you could imagine, I think the former would have you feeling frazzled and burnt out, while the latter would leave you feeling accomplished and satisfied.

I like to think that this data structure forces optimalization. It forces a benevolent maintainer, someone who decides if a task is really worth adding because it could potentially lose other hard-earned information. Other data structures focus on keeping everything and putting it wherever, but callously deleting it when it's no longer needed. The T Tree forces us to consider, is this really worth my time? At the end of the day, I'd love to show a computer the limitations of human memory, so that we can both potentially make it more optimal.

I know what you are probably thinking. T Tree already exists as a data structure. Sure it does, but I think my name fits better, and here's why:

  • It's made up of Talent Nodes and Task Nodes
  • My last name is Tinsley and
  • It reflects the T-shaped skills, which is a necessary constraint of this data structure: very few Talents can have the depth of the highest ranked one, but many Talents can spread out horizontally underneath it.

Weird Thoughts and Places My Brain Went

  • Would it make sense for the God Node aka the Talent Node head to be infinitely scalable so that there's no limit to the amount of initial Talents one can add, but once one gets promoted, all but 3 are lost? Conversely, if possible, having a way for someone to multiple maxed out Talents at the top.
  • Should Talent Nodes in the Lost Talent list be preserved after tree death? The God Node is, so should they be as well? Although inaccessible, would it imply some sort of reincarnation or past life?
  • Mastery, which was never touched on, should bypass burnout and the relearn scenario, or at least minimize the effects of both.
  • As humans, we like to orient ourselves in pyramid shaped structures, with less people at the top than the bottom. However, after working through this, I've found that promotion in this structure shape is highly inefficient at best and ruthless at worst. It's really, really, really hard to promote, and this was by far, the most challenging aspect of this project. There are so many levels of organizational concerns to consider when promoting. Someone can be promoted to a level that someone else already has, which may create a harder communication pattern for the person on top to all the other members, or it may thrust someone out, or it could give someone "nothing to do" ie, no child nodes. Or it will create a bottleneck and the organization will potentially have to lose many divisions to accomodate. Is there a more optimal structure for humans to arrange ourselves in to ensure a more efficient flow of power, communication and responsibility? It's definitely not the pyramid shaped structures, even for those at the top.
  • Should there be a randomized final_time on the T Tree that forces die() when the current time reaches it?
  • Check out /notes_stack.md to view more stream of consciousness thoughts and unrealized desires for this project.

TLDR ChatGPT Summary

The T Tree data structure is designed to simulate aspects of human cognitive processes like learning, memory, and the satisfaction derived from mastering skills, in a data management context. It incorporates Talent Nodes and Task Nodes, organized as binary trees but each with distinct properties. Key operations within the T Tree include adding tasks to nodes, accessing these tasks, and a distinctive 'die()' function that removes nodes from memory. This structure supports a dynamic interplay between task addition and node management, ensuring tasks are updated or removed in a manner that mirrors cognitive functions.

Beyond its technical architecture, the T Tree reflects its creator's deep engagement with the philosophical and ethical dimensions of computing and problem solving. It was developed as part of a final project for a data structures class, where the creator pondered the human-like behavior of data structures and their broader implications. This contemplation extends into the potential uses of the T Tree in fields like educational software, where it could optimize learning patterns, cognitive science for memory disorder treatments, and game development to enhance character progression strategies. Through its design, the T Tree challenges traditional notions of efficiency, advocating for a more holistic and optimal approach to managing cognitive tasks in computational environments.