Wrote parser and added tests for it.
Refactored tests a little.
This commit is contained in:
parent
6ae8385f7d
commit
6d79b7a7fa
32
Cargo.lock
generated
32
Cargo.lock
generated
|
|
@ -1,32 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "assert_approx_eq"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "code"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"assert_approx_eq",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
|
@ -5,4 +5,5 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
assert_approx_eq = "1.1.0"
|
||||
nom = "8.0.0"
|
||||
num-traits = "0.2"
|
||||
|
|
|
|||
127
src/main.rs
127
src/main.rs
|
|
@ -1,5 +1,7 @@
|
|||
use num_traits::Float;
|
||||
|
||||
mod parser;
|
||||
|
||||
/*
|
||||
* NOTES
|
||||
* - each alternative should appear only once in a tree. need to figure out a
|
||||
|
|
@ -7,22 +9,25 @@ use num_traits::Float;
|
|||
* - looks like this is like quite difficult to do through the type system
|
||||
*/
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
struct Alternative<T: Eq> {
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct Alternative<T: Eq> {
|
||||
name: T
|
||||
}
|
||||
|
||||
struct Edge<T: Eq, U: Float> {
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct Edge<T: Eq, U: Float> {
|
||||
weight: U,
|
||||
destination: Tree<T, U>
|
||||
}
|
||||
|
||||
enum Vertex<T: Eq, U: Float> {
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub enum Vertex<T: Eq, U: Float> {
|
||||
NonTerminal(Box<Edge<T, U>>, Box<Edge<T, U>>),
|
||||
Terminal(Alternative<T>),
|
||||
}
|
||||
|
||||
struct Tree<T: Eq, U: Float> {
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct Tree<T: Eq, U: Float> {
|
||||
root: Vertex<T, U>
|
||||
}
|
||||
|
||||
|
|
@ -84,11 +89,13 @@ mod tests {
|
|||
use super::*;
|
||||
use assert_approx_eq::assert_approx_eq;
|
||||
|
||||
// A test for the simplest symmetric case
|
||||
#[test]
|
||||
fn choice_probability_test_1() {
|
||||
let a = Alternative {name: "A"};
|
||||
let b = Alternative {name: "B"};
|
||||
fn simple_symmetric_tree() -> Tree<String, f64> {
|
||||
let a = Alternative {
|
||||
name: "A".to_owned()
|
||||
};
|
||||
let b = Alternative {
|
||||
name: "B".to_owned()
|
||||
};
|
||||
let edge_a = Edge {
|
||||
weight: 1.0,
|
||||
destination: Tree {root: Vertex::Terminal(a)}
|
||||
|
|
@ -98,28 +105,38 @@ mod tests {
|
|||
destination: Tree {root: Vertex::Terminal(b)}
|
||||
};
|
||||
let root = Vertex::NonTerminal(Box::new(edge_a), Box::new(edge_b));
|
||||
let tree = Tree {root: root};
|
||||
assert_eq!(tree.choice_probability(&"A"), 0.5);
|
||||
Tree {root: root}
|
||||
}
|
||||
|
||||
// A test for the simplest asymmetric case
|
||||
#[test]
|
||||
fn choice_probability_test_2() {
|
||||
let a = Alternative {name: "A"};
|
||||
let b = Alternative {name: "B"};
|
||||
let edge_a = Edge {weight: 3.0, destination: Tree {root: Vertex::Terminal(a)}};
|
||||
let edge_b = Edge {weight: 1.0, destination: Tree {root: Vertex::Terminal(b)}};
|
||||
fn simple_asymmetric_tree() -> Tree<String, f64> {
|
||||
let a = Alternative {
|
||||
name: "A".to_owned()
|
||||
};
|
||||
let b = Alternative {
|
||||
name: "B".to_owned()
|
||||
};
|
||||
let edge_a = Edge {
|
||||
weight: 3.0,
|
||||
destination: Tree {root: Vertex::Terminal(a)}
|
||||
};
|
||||
let edge_b = Edge {
|
||||
weight: 1.0,
|
||||
destination: Tree {root: Vertex::Terminal(b)}
|
||||
};
|
||||
let root = Vertex::NonTerminal(Box::new(edge_a), Box::new(edge_b));
|
||||
let tree = Tree {root: root};
|
||||
assert_eq!(tree.choice_probability(&"A"), 0.75);
|
||||
Tree {root: root}
|
||||
}
|
||||
|
||||
// A test for depth higher than 1
|
||||
#[test]
|
||||
fn choice_probability_test_3() {
|
||||
let a = Alternative {name: "A"};
|
||||
let b = Alternative {name: "B"};
|
||||
let c = Alternative {name: "C"};
|
||||
fn complex_tree() -> Tree<String, f64> {
|
||||
let a = Alternative {
|
||||
name: "A".to_owned()
|
||||
};
|
||||
let b = Alternative {
|
||||
name: "B".to_owned()
|
||||
};
|
||||
let c = Alternative {
|
||||
name: "C".to_owned()
|
||||
};
|
||||
let edge_a = Edge {
|
||||
weight: 2.5,
|
||||
destination: Tree {root: Vertex::Terminal(a)}
|
||||
|
|
@ -139,10 +156,62 @@ mod tests {
|
|||
destination: Tree {root: Vertex::Terminal(c)}
|
||||
};
|
||||
let root = Vertex::NonTerminal(Box::new(edge_ab), Box::new(edge_c));
|
||||
let tree = Tree {root: root};
|
||||
Tree {root: root}
|
||||
}
|
||||
|
||||
|
||||
// A test for the simplest symmetric case
|
||||
#[test]
|
||||
fn choice_probability_test_1() {
|
||||
assert_eq!(simple_symmetric_tree().choice_probability(&("A".to_owned())), 0.5);
|
||||
}
|
||||
|
||||
// A test for the simplest asymmetric case
|
||||
#[test]
|
||||
fn choice_probability_test_2() {
|
||||
assert_eq!(simple_asymmetric_tree().choice_probability(&("A".to_owned())), 0.75);
|
||||
}
|
||||
|
||||
// A test for depth higher than 1
|
||||
#[test]
|
||||
fn choice_probability_test_3() {
|
||||
assert_approx_eq!(
|
||||
tree.choice_probability(&"A"),
|
||||
complex_tree().choice_probability(&("A".to_owned())),
|
||||
(2.5/(2.5+1.0))*((2.5+1.0+0.5)/(2.5+1.0+0.5+1.0))
|
||||
);
|
||||
}
|
||||
|
||||
// A test for parsing the simplest symmetric case
|
||||
#[test]
|
||||
fn parser_test_1() {
|
||||
assert_eq!(
|
||||
{
|
||||
let (_, b) = parser::subtree("([1.0]A[1.0]B)").unwrap();
|
||||
b
|
||||
},
|
||||
simple_symmetric_tree()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parser_test_2() {
|
||||
assert_eq!(
|
||||
{
|
||||
let (_, b) = parser::subtree("([3.0]A[1.0]B)").unwrap();
|
||||
b
|
||||
},
|
||||
simple_asymmetric_tree()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parser_test_3() {
|
||||
assert_eq!(
|
||||
{
|
||||
let (_, b) = parser::subtree("([0.5]([2.5]A[1.0]B)[1.0]C)").unwrap();
|
||||
b
|
||||
},
|
||||
complex_tree()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
39
src/parser.rs
Normal file
39
src/parser.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* WHAT WE ARE PARSING:
|
||||
*
|
||||
*/
|
||||
|
||||
use nom::{branch::alt, bytes::complete::take_while1, character::char, combinator::map, error::Error, number::complete::double, sequence::{delimited, pair}, AsChar, IResult, Parser};
|
||||
|
||||
use crate::{Alternative, Edge, Tree, Vertex};
|
||||
|
||||
fn weighted_edge(input: &str) -> IResult<&str, Edge<String, f64>> {
|
||||
let weight = delimited(
|
||||
char::<&str, Error<&str>>('['),
|
||||
double,
|
||||
char(']')
|
||||
);
|
||||
let alternative = map(
|
||||
take_while1::<_, &str, Error<&str>>(AsChar::is_alpha),
|
||||
|a| Tree::<String, f64> {
|
||||
root: Vertex::Terminal(Alternative{name: a.to_string()})
|
||||
}
|
||||
);
|
||||
map(
|
||||
pair(weight, alt((alternative, subtree))),
|
||||
|(a, b)| Edge{
|
||||
weight: a,
|
||||
destination: b
|
||||
}
|
||||
).parse(input)
|
||||
}
|
||||
|
||||
pub fn subtree(input: &str) -> IResult<&str, Tree<String, f64>> {
|
||||
let inner = map(
|
||||
pair(weighted_edge, weighted_edge),
|
||||
|(a, b)| Tree{
|
||||
root: Vertex::NonTerminal(Box::new(a), Box::new(b))
|
||||
}
|
||||
);
|
||||
delimited(char('('), inner, char(')')).parse(input)
|
||||
}
|
||||
Loading…
Reference in a new issue