1use super::aggregate::GraphAggregate;
2use crate::{
3 Arity, Factory, NodeStore,
4 collections::{Graph, GraphNode, NodeType},
5};
6
7impl<T: Clone + Default> Graph<T> {
8 pub fn directed(
39 input_size: usize,
40 output_size: usize,
41 values: impl Into<NodeStore<T>>,
42 ) -> Graph<T> {
43 let builder = NodeBuilder::new(values);
44
45 let input_nodes = builder.input(input_size);
46 let output_nodes = builder.output(output_size);
47
48 GraphAggregate::new()
49 .all_to_all(&input_nodes, &output_nodes)
50 .build()
51 }
52
53 pub fn recurrent(
91 input_size: usize,
92 output_size: usize,
93 values: impl Into<NodeStore<T>>,
94 ) -> Graph<T> {
95 let builder = NodeBuilder::new(values);
96
97 let input = builder.input(input_size);
98 let aggregate = builder.vertices(input_size);
99 let output = builder.output(output_size);
100
101 GraphAggregate::new()
102 .one_to_one(&input, &aggregate)
103 .cycle(&aggregate)
104 .all_to_all(&aggregate, &output)
105 .build()
106 }
107
108 pub fn weighted_directed(
120 input_size: usize,
121 output_size: usize,
122 values: impl Into<NodeStore<T>>,
123 ) -> Graph<T> {
124 let builder = NodeBuilder::new(values);
125
126 let input = builder.input(input_size);
127 let output = builder.output(output_size);
128 let weights = builder.edge(input_size * output_size);
129
130 GraphAggregate::new()
131 .one_to_many(&input, &weights)
132 .many_to_one(&weights, &output)
133 .build()
134 }
135
136 pub fn weighted_recurrent(
147 input_size: usize,
148 output_size: usize,
149 values: impl Into<NodeStore<T>>,
150 ) -> Graph<T> {
151 let builder = NodeBuilder::new(values);
152
153 let input = builder.input(input_size);
154 let aggregate = builder.vertices(input_size);
155 let output = builder.output(output_size);
156 let weights = builder.edge(input_size * output_size);
157
158 GraphAggregate::new()
159 .one_to_one(&input, &aggregate)
160 .cycle(&aggregate)
161 .one_to_many(&aggregate, &weights)
162 .many_to_one(&weights, &output)
163 .build()
164 }
165
166 pub fn lstm(input_size: usize, output_size: usize, store: impl Into<NodeStore<T>>) -> Graph<T> {
185 let builder = NodeBuilder::new(store);
186
187 let input = builder.input(input_size);
188 let output = builder.output(output_size);
189
190 let cell_state = builder.vertices_with_arity(1, Arity::Any);
191 let hidden_state = builder.vertices_with_arity(1, Arity::Any);
192
193 let forget_gate = builder.vertices_with_arity(1, Arity::Any);
194 let input_gate = builder.vertices_with_arity(1, Arity::Any);
195 let output_gate = builder.vertices_with_arity(1, Arity::Any);
196 let candidate = builder.vertices_with_arity(1, Arity::Any);
197
198 GraphAggregate::new()
199 .all_to_all(&input, &forget_gate)
200 .all_to_all(&input, &input_gate)
201 .all_to_all(&input, &output_gate)
202 .all_to_all(&input, &candidate)
203 .one_to_one(&hidden_state, &forget_gate)
204 .one_to_one(&hidden_state, &input_gate)
205 .one_to_one(&hidden_state, &output_gate)
206 .one_to_one(&hidden_state, &candidate)
207 .one_to_one(&forget_gate, &cell_state)
208 .one_to_one(&input_gate, &candidate)
209 .one_to_one(&candidate, &cell_state)
210 .one_to_one(&cell_state, &hidden_state)
211 .one_to_one(&output_gate, &hidden_state)
212 .all_to_all(&hidden_state, &output)
213 .build()
214 }
215
216 pub fn gru(input_size: usize, output_size: usize, values: impl Into<NodeStore<T>>) -> Graph<T> {
235 let builder = NodeBuilder::new(values);
236
237 let input = builder.input(input_size);
238 let output = builder.output(output_size);
239
240 let hidden = builder.vertices_with_arity(1, Arity::Any);
241
242 let update = builder.vertices_with_arity(1, Arity::Any);
243 let reset = builder.vertices_with_arity(1, Arity::Any);
244 let candidate = builder.vertices_with_arity(1, Arity::Any);
245
246 let blend = builder.vertices_with_arity(1, Arity::Any);
247 let gate_flip = builder.vertices_with_arity(1, Arity::Any);
248
249 GraphAggregate::new()
250 .many_to_one(&input, &reset)
251 .many_to_one(&input, &update)
252 .many_to_one(&input, &candidate)
253 .one_to_one(&hidden, &reset)
254 .one_to_one(&hidden, &update)
255 .one_to_one(&hidden, &candidate)
256 .one_to_one(&update, &blend)
257 .one_to_one(&candidate, &blend)
258 .one_to_one(&reset, &hidden)
259 .one_to_one(&update, &gate_flip)
260 .one_to_one(&hidden, &gate_flip)
261 .one_to_one(&gate_flip, &hidden)
262 .one_to_one(&blend, &hidden)
263 .one_to_many(&hidden, &output)
264 .build()
265 }
266
267 pub fn mesh(
281 input_size: usize,
282 output_size: usize,
283 width: usize,
284 height: usize,
285 values: impl Into<NodeStore<T>>,
286 ) -> Graph<T> {
287 let builder = NodeBuilder::new(values);
288
289 let inputs = builder.input(input_size);
290 let outputs = builder.output(output_size);
291 let nodes = (0..width * height)
292 .map(|_| builder.vertices(1))
293 .collect::<Vec<Vec<GraphNode<T>>>>();
294
295 let mut aggregate = GraphAggregate::new();
296
297 for y in 0..height {
298 for x in 0..width {
299 let index = y * width + x;
300 let current = &nodes[index];
301
302 if x + 1 < width {
303 let right = &nodes[y * width + (x + 1)];
304 aggregate = aggregate.one_to_one(current, right);
305 }
306
307 if y + 1 < height {
308 let down = &nodes[(y + 1) * width + x];
309 aggregate = aggregate.one_to_one(current, down);
310 }
311 }
312 }
313
314 aggregate
315 .many_to_one(&inputs, &nodes[0])
316 .one_to_many(&nodes[nodes.len() - 1], &outputs)
317 .build()
318 }
319}
320
321pub struct NodeBuilder<T> {
324 store: NodeStore<T>,
325}
326
327impl<T: Clone + Default> NodeBuilder<T> {
328 pub fn new(store: impl Into<NodeStore<T>>) -> Self {
329 NodeBuilder {
330 store: store.into(),
331 }
332 }
333
334 pub fn input(&self, size: usize) -> Vec<GraphNode<T>> {
335 self.new_nodes(NodeType::Input, size, Arity::Zero)
336 }
337
338 pub fn output(&self, size: usize) -> Vec<GraphNode<T>> {
339 self.new_nodes(NodeType::Output, size, Arity::Any)
340 }
341
342 pub fn edge(&self, size: usize) -> Vec<GraphNode<T>> {
343 self.new_nodes(NodeType::Edge, size, Arity::Exact(1))
344 }
345
346 pub fn vertices(&self, size: usize) -> Vec<GraphNode<T>> {
347 self.new_nodes(NodeType::Vertex, size, Arity::Any)
348 }
349
350 pub fn vertices_with_arity(&self, size: usize, arity: Arity) -> Vec<GraphNode<T>> {
351 (0..size)
352 .filter_map(|idx| {
353 self.store
354 .new_instance((idx, NodeType::Vertex, |a| a == arity))
355 })
356 .collect()
357 }
358
359 fn new_nodes(
360 &self,
361 node_type: NodeType,
362 size: usize,
363 fallback_arity: Arity,
364 ) -> Vec<GraphNode<T>> {
365 if self.store.contains_type(node_type) {
366 (0..size)
367 .filter_map(|idx| self.store.new_instance((idx, node_type)))
368 .collect()
369 } else {
370 (0..size)
371 .filter_map(|idx| {
372 self.store
373 .new_instance((idx, node_type, |arity| arity == fallback_arity))
374 })
375 .collect()
376 }
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use super::*;
383 use crate::{Node, Op, node_store};
384 use radiate_core::Valid;
385
386 #[test]
387 fn test_graph_builder() {
388 let graph = Graph::directed(3, 3, Op::sigmoid());
389
390 assert_eq!(graph.len(), 6);
391
392 for node in graph.iter() {
393 if node.node_type() == NodeType::Input {
394 assert_eq!(node.arity(), Arity::Zero);
395 assert_eq!(node.incoming().iter().count(), 0);
396 assert_eq!(node.outgoing().iter().count(), 3);
397 } else if node.node_type() == NodeType::Output {
398 assert_eq!(node.arity(), Arity::Any);
399 assert_eq!(node.incoming().iter().count(), 3);
400 assert_eq!(node.outgoing().iter().count(), 0);
401 assert_eq!(node.value(), &Op::sigmoid());
402 }
403 }
404 }
405
406 #[test]
407 fn test_graph_builder_recurrent() {
408 let graph = Graph::recurrent(3, 3, Op::sigmoid());
409
410 assert_eq!(graph.len(), 9);
411
412 for node in graph.iter() {
413 if node.node_type() == NodeType::Input {
414 assert_eq!(node.arity(), Arity::Zero);
415 assert_eq!(node.incoming().iter().count(), 0);
416 assert_eq!(node.outgoing().iter().count(), 1);
417 } else if node.node_type() == NodeType::Vertex {
418 assert_eq!(node.arity(), Arity::Any);
419 assert!(node.is_recurrent());
420 assert_eq!(node.value(), &Op::sigmoid());
421 } else if node.node_type() == NodeType::Output {
422 assert_eq!(node.arity(), Arity::Any);
423 assert_eq!(node.incoming().iter().count(), 3);
424 assert_eq!(node.outgoing().iter().count(), 0);
425 assert_eq!(node.value(), &Op::sigmoid());
426 }
427 }
428 }
429
430 #[test]
431 fn test_graph_builder_with_no_any() {
432 let graph = Graph::directed(3, 3, Op::add());
433
434 assert_eq!(graph.len(), 6);
435 assert!(graph.is_valid());
436 }
437
438 #[test]
439 fn test_graph_builder_weighted() {
440 let store = vec![
441 (NodeType::Input, vec![Op::var(0), Op::var(1), Op::var(2)]),
442 (NodeType::Output, vec![Op::sigmoid()]),
443 (NodeType::Edge, vec![Op::weight_with(1.0)]),
444 ];
445
446 let graph = Graph::weighted_directed(3, 3, store);
447
448 assert_eq!(graph.len(), 15);
449 assert!(graph.is_valid());
450
451 for node in graph.iter() {
452 if node.node_type() == NodeType::Input {
453 assert_eq!(node.arity(), Arity::Zero);
454 assert_eq!(node.incoming().iter().count(), 0);
455 assert_eq!(node.outgoing().iter().count(), 3);
456 } else if node.node_type() == NodeType::Edge {
457 assert_eq!(node.arity(), Arity::Exact(1));
458 assert_eq!(node.incoming().iter().count(), 1);
459 assert_eq!(node.outgoing().iter().count(), 1);
460 assert_eq!(node.value(), &Op::weight_with(1.0));
461 } else if node.node_type() == NodeType::Output {
462 assert_eq!(node.arity(), Arity::Any);
463 assert_eq!(node.incoming().iter().count(), 3);
464 assert_eq!(node.outgoing().iter().count(), 0);
465 assert_eq!(node.value(), &Op::sigmoid());
466 }
467 }
468 }
469
470 #[test]
471 fn test_graph_builder_weighted_recurrent() {
472 let store = node_store![
473 Input => vec![Op::var(0), Op::var(1), Op::var(2)],
474 Output => vec![Op::sigmoid()],
475 Edge => vec![Op::weight_with(1.0)]
476 ];
477
478 let graph = Graph::weighted_recurrent(3, 3, store);
479
480 assert_eq!(graph.len(), 18);
481 assert!(graph.is_valid());
482
483 for node in graph.iter() {
484 if node.node_type() == NodeType::Input {
485 assert_eq!(node.arity(), Arity::Zero);
486 assert_eq!(node.incoming().iter().count(), 0);
487 assert_eq!(node.outgoing().iter().count(), 1);
488 } else if node.node_type() == NodeType::Edge {
489 assert_eq!(node.arity(), Arity::Exact(1));
490 assert_eq!(node.incoming().iter().count(), 1);
491 assert_eq!(node.outgoing().iter().count(), 1);
492 assert_eq!(node.value(), &Op::weight_with(1.0));
493 } else if node.node_type() == NodeType::Output {
494 assert_eq!(node.arity(), Arity::Any);
495 assert_eq!(node.incoming().iter().count(), 3);
496 assert_eq!(node.outgoing().iter().count(), 0);
497 assert_eq!(node.value(), &Op::sigmoid());
498 } else if node.node_type() == NodeType::Vertex {
499 assert_eq!(node.arity(), Arity::Any);
500 assert!(node.is_recurrent());
501 assert_eq!(node.value(), &Op::sigmoid());
502 }
503 }
504 }
505
506 #[test]
507 fn test_graph_builder_lstm() {
508 let store = node_store![
509 Input => vec![Op::var(0)],
510 Output => vec![Op::sigmoid()],
511 Vertex => vec![Op::sigmoid(), Op::tanh(), Op::mul(), Op::add()],
512 Edge => vec![Op::weight_with(1.0)]
513 ];
514
515 let graph = Graph::lstm(1, 1, store);
516 assert_eq!(graph.len(), 8);
517 assert!(graph.is_valid());
518
519 for node in graph.iter() {
520 if node.node_type() == NodeType::Input {
521 assert_eq!(node.arity(), Arity::Zero);
522 assert_eq!(node.incoming().iter().count(), 0);
523 assert_eq!(node.outgoing().iter().count(), 4);
524 } else if node.node_type() == NodeType::Output {
525 assert_eq!(node.arity(), Arity::Any);
526 assert_eq!(node.incoming().iter().count(), 1);
527 assert_eq!(node.outgoing().iter().count(), 0);
528 assert_eq!(node.value(), &Op::sigmoid());
529 } else if node.node_type() == NodeType::Vertex {
530 assert_eq!(node.arity(), Arity::Any);
531 assert!(
532 vec![Op::sigmoid(), Op::tanh(), Op::mul(), Op::add()].contains(&node.value())
533 );
534 } else if node.node_type() == NodeType::Edge {
535 assert_eq!(node.arity(), Arity::Exact(1));
536 assert_eq!(node.incoming().iter().count(), 1);
537 assert_eq!(node.outgoing().iter().count(), 1);
538 assert_eq!(node.value(), &Op::weight_with(1.0));
539 }
540 }
541 }
542
543 #[test]
544 fn test_graph_builder_gru() {
545 let store = node_store![
546 Input => vec![Op::var(0)],
547 Output => vec![Op::sigmoid()],
548 Vertex => vec![Op::sigmoid(), Op::tanh(), Op::mul(), Op::add()],
549 Edge => vec![Op::weight_with(1.0)]
550 ];
551
552 let graph = Graph::gru(1, 1, store);
553
554 assert_eq!(graph.len(), 8);
555 assert!(graph.is_valid());
556
557 for node in graph.iter() {
558 if node.node_type() == NodeType::Input {
559 assert_eq!(node.arity(), Arity::Zero);
560 assert_eq!(node.incoming().iter().count(), 0);
561 assert_eq!(node.outgoing().iter().count(), 3);
562 } else if node.node_type() == NodeType::Output {
563 assert_eq!(node.arity(), Arity::Any);
564 assert_eq!(node.incoming().iter().count(), 1);
565 assert_eq!(node.outgoing().iter().count(), 0);
566 assert_eq!(node.value(), &Op::sigmoid());
567 } else if node.node_type() == NodeType::Vertex {
568 assert_eq!(node.arity(), Arity::Any);
569 assert!(
570 vec![Op::sigmoid(), Op::tanh(), Op::mul(), Op::add()].contains(&node.value())
571 );
572 } else if node.node_type() == NodeType::Edge {
573 assert_eq!(node.arity(), Arity::Exact(1));
574 assert_eq!(node.incoming().iter().count(), 1);
575 assert_eq!(node.outgoing().iter().count(), 1);
576 assert_eq!(node.value(), &Op::weight_with(1.0));
577 }
578 }
579 }
580}