pub fn gen<T, U, F, G, H, E>(
(nodes, edges): Graph<T, U>,
n: usize,
f: F,
g: G,
h: H,
settings: &GenerateSettings,
) -> Result<Graph<T, U>, (Graph<T, U>, E)>
Expand description
Generates a graph from:
- an initial seed state
a
- maximum
n
edges - a function
f
to generate a new node with edge - a filter
g
to post-process nodes - a composer
h
to use for transforming edges when nodes are filtered - settings to control usage of memory
Returns a list of nodes and a list of edges.
Ok
if generation was successful without hitting memory limitsErr
if generation hit memory limits
Waiting with filtering until post-processing makes it possible to create an edge between nodes that require multiple steps.
The maximum number of edges is usually determined from the length of a list of valid operations.
§Error handling
The algorithm continues to post-processing when hitting memory limits. It is assumed that one wants to make use of the data generated, where memory limits are met often due to combinatorial explosions.
The algorithm assumes that there are other limits besides those in GenerateSettings
.
To handle errors, one adds a type that implements From<GenerateError>
.
The unit type ()
can be used when error handling is not needed.
Tip: Since this function requires 6 generic type arguments, it is easier to set the error type on the output type and let Rust infer the rest.
For example:
let (nodes, edges) = match gen(start, n, f, g, h, &settings) {
Ok(x) => x,
Err((x, ())) => x,
};
When an error happens during composing edges, one can choose whether to
report the error with Err(Some(err))
, or ignore it with Err(None)
.
This is useful because sometimes you want to filter edges without reporting errors.
The algorithm assumes that one wishes to continue generating the graph
when encountering an error. Only the first error will be reported.
Examples found in repository?
39fn main() {
40 // Change this to control the number of terms in the equation.
41 let n: usize = if let Some(x) = std::env::args_os().skip(1).take(1).next() {
42 usize::from_str(&x.into_string().expect("number")).unwrap()
43 } else {
44 3
45 };
46 // Change this to control how many
47 let solution_terms: usize = if let Some(x) = std::env::args_os().skip(2).take(1).next() {
48 usize::from_str(&x.into_string().expect("number")).unwrap()
49 } else {
50 1
51 };
52
53 // Putting all terms except the last one
54 let start = Eq {
55 side: {
56 if solution_terms == 1 && n > 0 {
57 let mut res = vec![true; n-1];
58 res.push(false);
59 res
60 } else {
61 vec![true; n]
62 }
63 },
64 positive: vec![true; n],
65 };
66
67 // Swap side and sign on the chosen term.
68 let f = |eq: &Eq, ind: usize| {
69 let mut eq = eq.clone();
70 eq.side[ind] = !eq.side[ind];
71 eq.positive[ind] = !eq.positive[ind];
72 Ok((eq, Swap(vec![ind])))
73 };
74 // Filter nodes to those with the specified number of solutions.
75 let g = |eq: &Eq| eq.len_right() == solution_terms;
76 // Join operations.
77 // Since these swap operations are commutative, require order.
78 let h = |a: &Swap, b: &Swap| if a.0 >= b.0 {Err(None)} else {Ok(Swap({
79 let mut a = a.0.clone();
80 a.extend_from_slice(&b.0);
81 a.sort();
82 a
83 }))};
84
85 let settings = GenerateSettings {
86 max_nodes: 1000,
87 max_edges: 1000,
88 };
89
90 let seed = (vec![start], vec![]);
91 // Generate graph.
92 let (eqs, mut edges) = match gen(seed, n, f, g, h, &settings) {
93 Ok(x) => x,
94 Err((x, ())) => x,
95 };
96
97 // Remove all edges that are not bidirectional.
98 bidir(&mut edges);
99 edges.sort();
100 for i in 0..eqs.len() {
101 println!("{}: {}", i, eqs[i]);
102 }
103 for i in 0..edges.len() {
104 println!("{:?}", edges[i]);
105 }
106
107 println!("(nodes, edges): ({}, {})", eqs.len(), edges.len());
108}