Given a line in a 2d coordinate system, we can create a dependency graph like this:
The black nodes represent a logical graph that is a-cyclic and therefore "stable" in the sense that if we provide real number values at all of the blue text nodes, we can parse resulting "Point" structures and a "Line" structure.
The orange node represents an external observer interested in the Y value it is pointing at. In this graph, the orange node would observe whatever real number is provided in the Y node.
Now if we introduce the concept of transformation, let's say we logically want to rotate the line. We modify the graph to show the transformation (see below image). The line node is now dependent on the transformation. BUT, as the edges are shown, all of the nodes are now dependent on the transformation.
Given real numbers at the blue text nodes, the orange observer will expect to see the transformed value, because the node's value changes upstream at the transformation node.This image proves the graph can be drawn to maintain stability (a-cyclic).
In the first image, the space complexity is linear, but in this second image, the space complexity becomes worse, (appears to be O(mn)) along with the time complexity to assign edges.
Let's add another transformation.
In the above image, the stability is maintained, such that the orange observer will now expect to see the rotated and translated Y value. But in practical terms, we needed to update 7 edges to point at the new head transformation node.
Given an arbitrary number of transforms, we would be forced to visit 7 edges per each transform, which appears to me to be O(mn) time complexity where m is the number of nodes and n is the number of transforms.
My current understanding is the below image. The better solution is to store parent references in each node such that when the orange node is created, the graph is walked upstream until the head is reached. Therefore the orange node dependency is a "logical" dependency on Y but a practical dependency on the translate node.
Even if this is a better solution, it has a flaw in my application, which is the temporal aspect. In my application, there is no guarantee all of the transform nodes have been resolved before the orange node. Which means that the orange node would need to be updated every time a transform node is added. In my application, there could be an arbitrary number of orange observers, so we are back to O(mn) where m is the number of observers and n is the total number of transform nodes. Every time a transform node is added, all observers have to be updated to the new "Head".
My question is if an elegant solution in graph theory or graph algorithms exists to make this more elegant. At a further stage in my application, I am successfully performing a topological sort, but the issue I am struggling with is as this post shows, i'm struggling to represent my graph a-cylically which is necessary for the topological sort.
EDIT: If you would like to give me a sketch in your response, I used tldraw, the original sketches can be found here:
EDIT 2: Filip asked if I can dynamically calculate the orange nodes target value.
instead of dynamically fetching value I perform a topological sort of the dependencies. That puts the list of all nodes in such an order that the value of the preceeding dependency has already been calculated at the perfect correct time for the next node to use. O(n) time complexity to sort versus O(mn) to fetch/calculate dynamically where m is number of orange nodes and n is number of ancestor transforms to iterate.
My proposed solution of walking the parents didn’t fully explain that I believe the intersection of lineages of the orange target lineage (not shown) and the Y node lineage is the architecturally correct ancestor to call the “practical dependency”. If an orange node is deeply nested in a sibling object graph, it seems i have no choice but to walk the two lineages to find the common root which is the earliest point that all transforms are complete. I can sketch this in a few days to help with the visualizing.



