Big documentation bump. More to come.

This commit is contained in:
Stu Black 2019-07-23 17:55:35 -04:00
parent 889c4a6d00
commit 650b240dc5
4 changed files with 194 additions and 63 deletions

View File

@ -1,3 +1,17 @@
//! This crate provides support for search (in the sense of AI or optimization)
//! over a space of discrete, enumerated states. Its main data structure is
//! [Graph](struct.Graph.html), a directed graph with content-addressable
//! vertices.
//!
//! Several efficient, type-safe interfaces are provided in submodules:
//!
//! * [view](view/index.html) provides a type- and memory-safe view of a `Graph`.
//! * [nav](nav/index.html) provides a read-only cursor-based view of a
//! `Graph`. (If you have seen
//! [zippers](https://en.wikipedia.org/wiki/Zipper_(data_structure)) in other
//! contexts, this pattern should be familiar).
//! * [mutators](mutators/index.html) is a read-write analogue of `nav`.
pub(crate) mod base;
pub mod mutators;
pub mod nav;
@ -10,22 +24,34 @@ use base::{EdgeId, RawEdge, RawVertex, VertexId};
use symbol_map::indexing::{Indexing, Insertion};
use symbol_map::SymbolId;
/// A search graph.
/// A directed graph over a space of discrete, enumerated states.
///
/// Supports incremental rollout of game state topology and vertex
/// de-duplication with transposition tables. Limited support is provided for
/// deletion of components and compaction in memory after deletion.
/// In typical usage, vertices in the graph will correspond to game states, and
/// edges will correspond to game actions. Vertices have an associated state
/// (e.g., the configuration of pieces on a chessboard) and data (e.g., upper
/// and lower bounds of the score of the outcomes that can be reached from that
/// state), while edges simply have an associated data item (which may include
/// additional statistics statistics or game-specific data about the action that
/// the edge represents).
///
/// A `Graph` is parameterized by three types:
///
/// - `T`: The type of game states. It is required to derive `Hash` and `Eq` to
/// so that it may be stored in a hashtable, where game states are looked up to
/// support de-duplication of game states. It is required to derive `Clone` to
/// accommodate a limitation of the `HashMap` interface.
/// - `S`: The type of graph vertex data.
/// - `A`: The type of graph edge data.
/// - `S`: The vertex data type.
/// - `A`: The edge data type.
///
/// Vertices are addressed by content. To examine graph contents, obtain a node
/// handle with `find_node`. To modify graph contents, add new root vertices with
/// `add_node` and retrieve extant vertices with `find_node_mut`.
/// Vertices are addressable by content. Cursors into the graph may be obtained
/// with [find_node](struct.Graph.html#method.find_node) or
/// [find_node_mut](struct.Graph.html#method.find_node_mut).
///
/// Content can be added to a `Graph` directly with the
/// [add_node](struct.Graph.html#method.add_node and
/// [add_edge](struct.Graph.html#method.add_edge) methods. It may also be added
/// through the interfaces provided by the [mutators/index.html](mutators) and
/// [view/index.html](view) modules.
pub struct Graph<T: Hash + Eq + Clone, S, A> {
/// Lookup table that maps from game states to `VertexId`.
state_ids: symbol_map::indexing::HashIndexing<T, VertexId>,
@ -70,9 +96,8 @@ impl<T: Hash + Eq + Clone, S, A> Graph<T, S, A> {
/// Adds a new vertex with the given data, returning a mutable reference to it.
///
/// This method does not add incoming or outgoing edges (expanded or
/// not). That must be done by calling `add_arc` with the new vertex
/// `VertexId`.
/// This method does not add incoming or outgoing edges. That must be done by
/// calling `add_arc` with the new vertex `VertexId`.
fn add_raw_vertex(&mut self, data: S) -> &mut RawVertex<S> {
self.vertices.push(RawVertex {
data: data,

View File

@ -1,9 +1,8 @@
//! Support for navigation of a graph that allows modifications to graph
//! topology and full read-write access to graph data.
//!
//! The data structures in this module require a read-write borrow of an
//! underlying graph. As a result, only one handle may be active at any given
//! time.
//! The data structures in this module own a read-write borrow of an underlying
//! graph. As a result, only one handle may be active at any given time.
use std::clone::Clone;
use std::cmp::Eq;
@ -11,9 +10,7 @@ use std::hash::Hash;
use crate::base::{EdgeId, RawEdge, RawVertex, VertexId};
use crate::nav::{ChildList, ChildListIter, Edge, Node, ParentList, ParentListIter};
use crate::view;
use crate::Graph;
use r4::iterate;
use symbol_map::indexing::{Indexing, Insertion};
use symbol_map::SymbolId;
@ -72,8 +69,7 @@ impl<'a, T: Hash + Eq + Clone + 'a, S: 'a, A: 'a> MutNode<'a, T, S, A> {
&mut self.vertex_mut().data
}
/// Returns true iff this vertex has no outgoing edges (regardless of
/// whether they are expanded).
/// Returns true iff this vertex has no outgoing edges.
pub fn is_leaf(&self) -> bool {
self.vertex().children.is_empty()
}
@ -144,19 +140,6 @@ impl<'a, T: Hash + Eq + Clone + 'a, S: 'a, A: 'a> MutNode<'a, T, S, A> {
pub fn get_node<'s>(&'s self) -> Node<'s, T, S, A> {
Node::new(self.graph, self.id)
}
/// Prunes the underlying graph by removing components not reachable from
/// this node.
pub fn retain_reachable(self) -> MutNode<'a, T, S, A> {
let consumed = MutNode { id: self.id, graph: self.graph, };
view::of_node(consumed, |mut v, n| {
v.retain_reachable_from(iterate!(yield n));
});
MutNode {
id: VertexId(0),
graph: self.graph,
}
}
}
/// A traversible list of a vertex's outgoing edges.
@ -425,16 +408,13 @@ impl<'a, T: Hash + Eq + Clone + 'a, S: 'a, A: 'a> MutParentList<'a, T, S, A> {
}
}
/// Mutable handle to a graph edge ("edge handle") when edge expansion state is
/// unknown.
/// Mutable handle to a graph edge ("edge handle").
///
/// This zipper-like type enables traversal of a graph along the edge's source
/// and target vertices.
///
/// It enables local graph mutation, whether via mutation of edge data or
/// mutation of graph topology (adding vertices). Vertices may be added to
/// unexpanded edges using the handle returned by `get_target_mut` or
/// `to_target`.
/// mutation of graph topology (adding vertices).
pub struct MutEdge<'a, T: Hash + Eq + Clone + 'a, S: 'a, A: 'a> {
pub(crate) graph: &'a mut Graph<T, S, A>,
pub(crate) id: EdgeId,
@ -471,17 +451,14 @@ impl<'a, T: Hash + Eq + Clone + 'a, S: 'a, A: 'a> MutEdge<'a, T, S, A> {
&mut self.arc_mut().data
}
/// Returns the target of this edge. If the edge is unexpanded, no data will
/// be available. If it is expanded, a node handle will be available, with
/// its lifetime limited to a local borrow of `self`.
/// Returns the target of this edge. Returns a node handle, whose lifetime is
/// limited to a local borrow of `self`.
pub fn get_target<'s>(&'s self) -> Node<'s, T, S, A> {
Node::new(self.graph, self.arc().target)
}
/// Returns the target of this edge. If the edge is unexpanded, an
/// `EdgeExpander` will be provided. If it is expanded, a mutable node
/// handle will be available. In both cases, lifetimes will be limited to a
/// local borrow of `self`.
/// Returns the target of this edge. Returns a mutable node handle will be
/// available, whose lifetime is limited to a local borrow of `self`.
pub fn get_target_mut<'s>(&'s mut self) -> MutNode<'s, T, S, A> {
let id = self.arc().target;
MutNode {
@ -490,10 +467,8 @@ impl<'a, T: Hash + Eq + Clone + 'a, S: 'a, A: 'a> MutEdge<'a, T, S, A> {
}
}
/// Returns the target of this edge. If the edge is unexpanded, an
/// `EdgeExpander` will be provided. If it is expanded, a mutable node
/// handle will be available. In both cases `self` is consumed, and the
/// return value's lifetime will be the same as that of `self`.
/// Returns the target of this edge. Consumes `self` and returns a mutable
/// node handle whose lifetime will be the same as that of `self`.
pub fn to_target(self) -> MutNode<'a, T, S, A> {
let id = self.arc().target;
MutNode {

View File

@ -45,12 +45,10 @@ pub enum SearchError<E: Error> {
/// series of (vertex, edge) pairs, and a `Stack` encapsulates this
/// history.
///
/// A `Stack` points to a head, which is either a graph vertex (whose
/// incidental edges can then be traversed) or an unexpanded edge (if a
/// traversal operation chose to follow an unexpanded edge). Operations which
/// modify graph topology (such as expanding edges) may cause the search path's
/// internal state to fall out of sync with the graph's state, so graph elements
/// exposed using the read-only `Node` and `Edge` types.
/// A `Stack` points to a graph vertex, whose incidental edges can then be
/// traversed. Operations which modify graph topology may cause the search
/// path's internal state to fall out of sync with the graph's state, so graph
/// elements are exposed using the read-only `Node` and `Edge` types.
///
/// A path may be consumed to yield a read-write view of the underlying graph
/// with the `to_head` method.
@ -90,7 +88,7 @@ where
pub enum StackItem<'a, T: 'a + Hash + Eq + Clone, S: 'a, A: 'a> {
/// Non-head item, a (vertex, edge) pair.
Item(Edge<'a, T, S, A>),
/// The path head, which may resolve to a vertex or an unexpanded edge.
/// The path head, which resolves to a vertex.
Head(Node<'a, T, S, A>),
}

View File

@ -1,3 +1,90 @@
//! Provides an editable view of both graph topology and data, while
//! simultaneously allowing multiple live references into the graph.
//!
//! References into the graph are provided by [NodeRef](struct.NodeRef.html) and
//! [EdgeRef](struct.EdgeRef.html). They are created with respect to a
//! [View](struct.View.html), which wraps around a mutable borrow of a
//! [Graph](../struct.Graph.html). They may only be dereferenced with respect to
//! the view that created them, and operations on a `View` that would invalidate
//! these references consume the `View`.
//!
//! # Basic usage
//!
//! To create a `View`, use one of the functions defined in this module:
//! [of_graph](fn.of_graph.html), [of_node](fn.of_node.html), or
//! [of_edge](fn.of_edge.html).
//!
//! To use a `NodeRef` or `EdgeRef` with a `View`, pass the reference to an
//! appropriate function on the `View` or index into the `View` directly:
//!
//! ```rust
//! # use search_graph::Graph;
//! # use search_graph::view;
//! # fn main() {
//! let mut graph: Graph<u32, String, String> = Graph::new();
//! view::of_graph(&mut graph, |mut view| {
//! let root: view::NodeRef<'_> = view.append_node(0, "root_data".into());
//! assert_eq!(view[root], "root_data");
//! let child: view::NodeRef<'_> = view.append_node(10, "child_data".into());
//! assert_eq!(view.node_data(child), "child_data");
//! let edge: view::EdgeRef<'_> = view.append_edge(root, child, "edge_data".into());
//! assert_eq!(view[edge], "edge_data");
//! });
//! # }
//! ```
//!
//! # Relationship with `search_graph::mutators`
//!
//! The [mutators](../mutators/index.html) module provides another read-write
//! interface to a `Graph`, in which cursors into the graph directly own a
//! mutable reference to it. Because these cursors own a mutable reference to a
//! common underlying `Graph`, it is difficult to have more than one cursor
//! active at a time while still satisfying the borrow checker. This makes it
//! tricky to retain multiple cursors into a graph if even one of them allows
//! the graph to be mutated.
//!
//! The cursors in `mutators` may be converted into a `View` by the
//! [of_node](fn.of_node.html) and [of_edge](fn.of_edge.html) functions. For
//! example:
//!
//! ```rust
//! # use search_graph::Graph;
//! # use search_graph::view;
//! # use search_graph::mutators::MutNode;
//! # fn main() {
//! let mut graph: Graph<u32, String, String> = Graph::new();
//! let root: MutNode<'_, u32, String, String> =
//! graph.add_node(0, "root_data".into());
//! view::of_node(root, |view, node| {
//! assert_eq!(view[node], "root_data");
//! });
//! # }
//! ```
//!
//! A `View` may be transformed into a cursor from `mutators` by calling
//! [into_node](struct.View.html#method.into_node),
//! [into_edge](struct.View.html#method.into_edge),
//! [into_append_node](struct.View.html#method.into_append_node) or
//! [into_append_edge](struct.View.html#method.into_append_edge). These methods
//! consume a `View` and release its borrow of a `Graph` back to a stand-alone
//! cursor type. For example:
//!
//! ```rust
//! # use search_graph::Graph;
//! # use search_graph::view;
//! # use search_graph::mutators::MutNode;
//! # fn main() {
//! let mut graph: Graph<u32, String, String> = Graph::new();
//! let mut node: MutNode<'_, u32, String, String> =
//! graph.add_node(0, "root_data".into());
//! node = view::of_node(node, |view, node| {
//! assert_eq!(view[node], "root_data");
//! view.into_append_node(100, "child_data".into())
//! });
//! assert_eq!(node.get_data(), "child_data");
//! # }
//! ```
use r4::iterate;
use symbol_map::indexing::Indexing;
@ -32,7 +119,8 @@ where
lifetime: InvariantLifetime<'id>,
}
/// Applies a function over a view of `graph` and returns its result.
/// Applies a function over a view of [Graph](../struct.Graph.html) and returns
/// its result.
///
/// ```rust
/// # use search_graph::Graph;
@ -66,8 +154,8 @@ pub fn of_graph<
})
}
/// Applies a function over a node and a view of its containing graph and
/// returns the function's result.
/// Applies a function over a [MutNode](../mutators/struct.MutNode.html) and a
/// view of its containing graph and returns the function's result.
///
/// ```rust
/// # use search_graph::Graph;
@ -104,8 +192,8 @@ pub fn of_node<
)
}
/// Applies a function over a `MutEdge` and a view of its containing graph and
/// returns the function's result.
/// Applies a function over a [MutEdge](../mutators/struct.MutEdge.html) and a
/// view of its containing graph and returns the function's result.
///
/// ```rust
/// # use search_graph::Graph;
@ -203,6 +291,14 @@ where
}
}
/// Consumes this view and returns a `MutNode`.
pub fn into_node(self, node: NodeRef<'id>) -> mutators::MutNode<'a, T, S, A> {
mutators::MutNode {
graph: self.graph,
id: node.id,
}
}
/// Consumes this view and adds a node as if `append_node` had been
/// called. Returns a `MutNode` that points to the node that is created.
pub fn into_append_node(self, state: T, data: S) -> mutators::MutNode<'a, T, S, A> {
@ -213,6 +309,14 @@ where
}
}
/// Consumes this view and returns a `MutEdge`.
pub fn into_edge(self, edge: EdgeRef<'id>) -> mutators::MutEdge<'a, T, S, A> {
mutators::MutEdge {
graph: self.graph,
id: edge.id,
}
}
/// Adds an edge between the given nodes, returning a reference to it after it
/// is added.
pub fn append_edge(
@ -362,14 +466,14 @@ where
/// Deletes all graph components that are not reachable by a traversal
/// starting from each of `roots`.
pub fn retain_reachable_from<I: IntoIterator<Item = NodeRef<'id>>>(&mut self, roots: I) {
pub fn retain_reachable_from<I: IntoIterator<Item = NodeRef<'id>>>(self, roots: I) {
let root_ids: Vec<VertexId> = roots.into_iter().map(|n| n.id).collect();
self.retain_reachable_from_ids(&root_ids);
}
/// As `retain_reachable_from`, but working over raw `VertexId`s.
fn retain_reachable_from_ids(&mut self, root_ids: &[VertexId]) {
mutators::mark_compact::Collector::retain_reachable(self, root_ids);
fn retain_reachable_from_ids(mut self, root_ids: &[VertexId]) {
mutators::mark_compact::Collector::retain_reachable(&mut self.graph, root_ids);
}
}
@ -484,6 +588,35 @@ impl<'id> fmt::Debug for NodeRef<'id> {
}
}
/// Reference to a graph edge that is licensed by a `View`. Only the `View` that
/// an `EdgeRef` is associated with can dereference that `EdgeRef`.
///
/// ```rust
/// # use search_graph::Graph;
/// # use search_graph::view;
/// # fn main() {
/// let mut g1: Graph<String, String, String> = Graph::new();
/// let mut g2: Graph<String, String, String> = Graph::new();
/// view::of_graph(&mut g1, |mut v1| {
/// let root1 = v1.append_node("root1_state".into(), "root1_data".into());
/// let child1 = v1.append_node("child1_state".into(), "child1_data".into());
/// let edge1 = v1.append_edge(root1, child1, "edge1_data".into());
/// assert_eq!(v1[edge1], "edge1_data");
/// let escaped = view::of_graph(&mut g2, |mut v2| {
/// let root2 = v2.append_node("root2_state".into(), "root2_data".into());
/// let child2 = v2.append_node("child2_state".into(), "child2_data".into());
/// let edge2 = v2.append_edge(root2, child2, "edge2_data".into());
///
/// // An EdgeRef from one view cannot be used with another. This will not compile.
/// // assert_eq!(v2[edge1], "internal");
///
/// // An EdgeRef cannot escape the closure defining its associated view.
/// // Returning edge2 will not compile.
/// // edge2
/// });
/// });
/// # }
/// ```
#[derive(Clone, Copy)]
pub struct EdgeRef<'id> {
pub(crate) id: EdgeId,