Big documentation bump. More to come.
This commit is contained in:
parent
889c4a6d00
commit
650b240dc5
49
src/lib.rs
49
src/lib.rs
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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>),
|
||||
}
|
||||
|
||||
|
149
src/view.rs
149
src/view.rs
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user