Crate calc_graph

Source
Expand description

Use this crate to split a calculation into related sub-calculations, known as nodes.

You can push information from outside into one or more source nodes, and you can read results from one or more output nodes. Values are only calculated as they’re needed, and cached as long as their inputs don’t change. This means that recalculations are efficient when you only change some of the inputs, and if you don’t request the value from an output node, its value is never calculated.

§Example

let graph = Graph::new();                        // create a Graph object
let mut source = graph.source(42);               // define one or more nodes for your inputs
let mut output = source.clone().map(|x| x + 1);  // build one or more nodes for your outputs
assert_eq!(43, output.get_mut());                // read values from your output nodes

source.set(99);                                  // push new values to the input nodes...
assert_eq!(100, output.get_mut());               // ...and read the output nodes

§Sharing

Func nodes (created by Node::map, Node::zip and related methods) own their inputs (precedent nodes). When you have a node that acts as an input to two or more func nodes, you need to use shared() first. You can then use this shared node multiple times via clone():

let input_node = calc_graph::const_(42).shared();
let mut output1_node = input_node.clone().map(|x| x + 1);
let mut output2_node = input_node.map(|x| x * x);
assert_eq!(43, output1_node.get_mut());
assert_eq!(1764, output2_node.get_mut());

You can have multiple Graph objects in the same program, but when you define a new node, its precedents must come from the same graph.

§Boxing

A Node object remembers the full type information of its precedent nodes as well as the closure used to calculate its value. This means that the name of the Node type can be very long, or even impossible to write in the source code. In this situation you can use:

let func_node: Node<Func1<_, i32, _>> = input_node.map(|x| x + 1);
let output_node: BoxNode<i32> = func_node.boxed();

A call to boxed() is also needed if you want a variable that can hold either one or another node; these nodes can have different concrete types, and calling boxed() on each of them gives you a pair of nodes that have the same type.

§Threading

Node<Source>, SharedNode and BoxedNode objects are Send and Sync, meaning they can be passed between threads. Calculations are performed on the thread that calls node.get(). Calculations are not parallelised automatically, although you can read separate output nodes from separate threads, even if they share parts of the same graph as inputs.

let graph = Graph::new();
let input_node = graph.source(41);
let output_node = input_node.clone().map(|x| x * x).shared();
assert_eq!(1681, output_node.get());

let t = thread::spawn({
    let input_node = input_node.clone();
    let output_node = output_node.clone();
    move || {
        input_node.update(|n| {
            *n += 1;
            true
        });

        output_node.get()
    }
});

assert_eq!(1764, t.join().unwrap());

input_node.update(|n| {
    *n += 1;
    true
});

assert_eq!(1849, output_node.get());

§zip, zip_update and others

Use zip(), map2() and related functions to create a new node that calculates its value from a FnMut provided by you and the values from one or more other nodes. For large objects, recalculating these nodes can be inefficient, as your FnMut returns a fresh object every time, which is cloned wherever it is needed.

For more efficiency you can use zip_update(), map2_update() and related functions. These work the same as their non-update equivalents, except that:

  1. You provide the initial value of the new node when you create it
  2. Your FnMut takes a &mut T as its first parameter. You update this value in place.
  3. Your FnMut returns true if it changed value in the &mut T, or false otherwise. In turn, this determines whether dependent nodes are recalculated.

A useful technique for large objects is to put an Arc<T> in the node. When you recalculate the node, use Arc::make_mut to update the object in place where possible and avoid allocating a new Arc.

let input_node = graph.source(42);

let mut output_node = input_node.clone().map_update(Arc::new([0; 1024]), |big_array, x| {
    let new_value = x * x;
    let big_array_ref = Arc::make_mut(big_array);
    if big_array_ref[0] == new_value {
        false
    } else {
        big_array_ref[0] = new_value;
        true
    }
});

assert_eq!(1764, output_node.get_mut()[0]);

input_node.update(|n| {
    *n += 1;
    true
});

assert_eq!(1849, output_node.get_mut()[0]);

Structs§

Const
Calculates a node’s value by returning the same value every time.
Func1
Calculates a value from another node.
Func2
Calculates a value from 2 nodes.
Func3
Calculates a value from 3 nodes.
Func4
Calculates a value from 4 nodes.
Func5
Calculates a value from 5 nodes.
Func6
Calculates a value from 6 nodes.
Func7
Calculates a value from 7 nodes.
Func8
Calculates a value from 8 nodes.
Graph
Returns new Node<Source> objects, which act as inputs to the rest of the graph.
Inspect
Provides the opportunity to inspect a node’s value without changing it.
Lazy
Calculates a node’s value by calling a function on demand and caching the result.
Node
Represents a value within the graph.
Source
Holds a value that can be updated directly from outside the graph.
Update1
Calculates a value from the value currently in this node and another node.
Update2
Calculates a value from the value currently in this node and 2 nodes.
Update3
Calculates a value from the value currently in this node and 3 nodes.
Update4
Calculates a value from the value currently in this node and 4 nodes.
Update5
Calculates a value from the value currently in this node and 5 nodes.
Update6
Calculates a value from the value currently in this node and 6 nodes.
Update7
Calculates a value from the value currently in this node and 7 nodes.
Update8
Calculates a value from the value currently in this node and 8 nodes.

Traits§

Calc
Calculates a node’s value.

Functions§

const_
Returns a node whose value never changes.
lazy
Returns a node whose value is calculated once on demand and cached.

Type Aliases§

BoxNode
A node returned by Node::boxed.
SharedNode
A node returned by Node::shared.