[][src]Module zamm_yin::graph

Defines the interface and implementations for working with regular directed graphs with labeled edges.

Examples

We need to choose which graph implementation to ground our knowledge and reasoning on. All implementations should be logically equivalent. Let's use the in-memory one for simplicity:

use zamm_yin::graph::bind_in_memory_graph;

bind_in_memory_graph();

No matter which implementation you choose, InjectionGraph allows you to use that implementation via dependency injection:

use zamm_yin::graph::InjectionGraph;

let mut g = InjectionGraph::new();

Now we can create a new node:

use zamm_yin::graph::Graph;

let a_id = g.add_node();

We can set a name for the node. Note that names don't need to be unique.

use std::rc::Rc;

g.set_node_name(a_id, "A");
assert_eq!(g.node_name(a_id), Some(Rc::from("A")));

We can also set a value for the node. We use Rc here because Yin being the map and not the territory, we generally don't want to have Yin itself own the data being operated on.

use zamm_yin::graph::value_wrappers::{unwrap_value, WeakValue};
use std::rc::Rc;

let v = Rc::new(5);
g.set_node_value(a_id, Rc::new(WeakValue::new(&v)));
assert_eq!(unwrap_value::<i32>(g.node_value(a_id)), Some(v));

Let's create a few more nodes:

let b_id = g.add_node();
let c_id = g.add_node();
let d_id = g.add_node();
let edge_type1 = g.add_node();
let edge_type2 = g.add_node();

Let's now connect some of these nodes together. Note that while edge labels can technically be any integer, they are assumed to be node IDs by higher-level Yin abstractions:

g.add_edge(b_id, edge_type1, a_id);
g.add_edge(c_id, edge_type2, a_id);
g.add_edge(d_id, edge_type1, a_id);

assert_eq!(g.all_incoming_nodes(a_id), vec![b_id, c_id, d_id]);
assert_eq!(g.incoming_nodes(a_id, edge_type1), vec![b_id, d_id]);
assert_eq!(g.all_outgoing_nodes(b_id), vec![a_id]);
assert_eq!(g.outgoing_nodes(c_id, edge_type2), vec![a_id]);

We can also use the KB to invoke certain functionality. Note that we are passing in a Form concept to the callback function because that's the only supported function at this moment.

use zamm_yin::Wrapper;
use zamm_yin::tao::archetype::ArchetypeTrait;
use zamm_yin::tao::form::{Form, FormTrait};
use zamm_yin::graph::value_wrappers::{run_closure, StrongValue, KBClosure};
use zamm_yin::define_closure;
use zamm_yin::node_wrappers::BaseNodeTrait;
use std::any::Any;
use std::cell::{RefCell, RefMut};

let count_id = g.add_node();
let count_value: Rc<i64> = Rc::new(5);
g.set_node_value(count_id, Rc::new(WeakValue::new(&count_value)));

let mut triple_id = g.add_node();
g.set_node_value(triple_id, define_closure!(|t: Form| {
    Box::new(*unwrap_value::<i64>(t.essence().value()).unwrap() * 3)
}));
assert_eq!(
    run_closure::<i64>(&g.node_value(triple_id), Form::from(count_id)),
    Some(Box::new(15))
);

In general, it's recommended to only use the KB to decide what to do at a very high level, and not to actually do things via the KB. For example, perhaps we could use the KB to decide to run Dijkstra's. We could even use the KB to design and debug an implementation of Dijkstra's. But the actual computation of Dijkstra's algorithm should involve low-level data structures and logic outside of the KB.

Modules

value_wrappers

Wrappers around values associated with nodes in the KB. This differs from the other wrappers package because this abstraction only wraps the values associated with nodes, while the other one wraps the nodes themselves.

Structs

InjectionGraph

Graph usable with dependency injection.

Traits

Graph

A classic directed Graph with nodes and labeled links.

Functions

bind_cypher_graph

Bind GRAPH to an external Neo4j database.

bind_in_memory_graph

Bind GRAPH to a new graph that sits entirely in memory.

print_graph_debug

Print graph to stdout for debugging purposes.