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]
|
[dependencies]
|
||||||
assert_approx_eq = "1.1.0"
|
assert_approx_eq = "1.1.0"
|
||||||
|
nom = "8.0.0"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
|
|
|
||||||
127
src/main.rs
127
src/main.rs
|
|
@ -1,5 +1,7 @@
|
||||||
use num_traits::Float;
|
use num_traits::Float;
|
||||||
|
|
||||||
|
mod parser;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* NOTES
|
* NOTES
|
||||||
* - each alternative should appear only once in a tree. need to figure out a
|
* - 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
|
* - looks like this is like quite difficult to do through the type system
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
struct Alternative<T: Eq> {
|
pub struct Alternative<T: Eq> {
|
||||||
name: T
|
name: T
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Edge<T: Eq, U: Float> {
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct Edge<T: Eq, U: Float> {
|
||||||
weight: U,
|
weight: U,
|
||||||
destination: Tree<T, 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>>),
|
NonTerminal(Box<Edge<T, U>>, Box<Edge<T, U>>),
|
||||||
Terminal(Alternative<T>),
|
Terminal(Alternative<T>),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Tree<T: Eq, U: Float> {
|
#[derive(PartialEq, Eq, Debug)]
|
||||||
|
pub struct Tree<T: Eq, U: Float> {
|
||||||
root: Vertex<T, U>
|
root: Vertex<T, U>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -84,11 +89,13 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use assert_approx_eq::assert_approx_eq;
|
use assert_approx_eq::assert_approx_eq;
|
||||||
|
|
||||||
// A test for the simplest symmetric case
|
fn simple_symmetric_tree() -> Tree<String, f64> {
|
||||||
#[test]
|
let a = Alternative {
|
||||||
fn choice_probability_test_1() {
|
name: "A".to_owned()
|
||||||
let a = Alternative {name: "A"};
|
};
|
||||||
let b = Alternative {name: "B"};
|
let b = Alternative {
|
||||||
|
name: "B".to_owned()
|
||||||
|
};
|
||||||
let edge_a = Edge {
|
let edge_a = Edge {
|
||||||
weight: 1.0,
|
weight: 1.0,
|
||||||
destination: Tree {root: Vertex::Terminal(a)}
|
destination: Tree {root: Vertex::Terminal(a)}
|
||||||
|
|
@ -98,28 +105,38 @@ mod tests {
|
||||||
destination: Tree {root: Vertex::Terminal(b)}
|
destination: Tree {root: Vertex::Terminal(b)}
|
||||||
};
|
};
|
||||||
let root = Vertex::NonTerminal(Box::new(edge_a), Box::new(edge_b));
|
let root = Vertex::NonTerminal(Box::new(edge_a), Box::new(edge_b));
|
||||||
let tree = Tree {root: root};
|
Tree {root: root}
|
||||||
assert_eq!(tree.choice_probability(&"A"), 0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A test for the simplest asymmetric case
|
fn simple_asymmetric_tree() -> Tree<String, f64> {
|
||||||
#[test]
|
let a = Alternative {
|
||||||
fn choice_probability_test_2() {
|
name: "A".to_owned()
|
||||||
let a = Alternative {name: "A"};
|
};
|
||||||
let b = Alternative {name: "B"};
|
let b = Alternative {
|
||||||
let edge_a = Edge {weight: 3.0, destination: Tree {root: Vertex::Terminal(a)}};
|
name: "B".to_owned()
|
||||||
let edge_b = Edge {weight: 1.0, destination: Tree {root: Vertex::Terminal(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)}
|
||||||
|
};
|
||||||
let root = Vertex::NonTerminal(Box::new(edge_a), Box::new(edge_b));
|
let root = Vertex::NonTerminal(Box::new(edge_a), Box::new(edge_b));
|
||||||
let tree = Tree {root: root};
|
Tree {root: root}
|
||||||
assert_eq!(tree.choice_probability(&"A"), 0.75);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A test for depth higher than 1
|
fn complex_tree() -> Tree<String, f64> {
|
||||||
#[test]
|
let a = Alternative {
|
||||||
fn choice_probability_test_3() {
|
name: "A".to_owned()
|
||||||
let a = Alternative {name: "A"};
|
};
|
||||||
let b = Alternative {name: "B"};
|
let b = Alternative {
|
||||||
let c = Alternative {name: "C"};
|
name: "B".to_owned()
|
||||||
|
};
|
||||||
|
let c = Alternative {
|
||||||
|
name: "C".to_owned()
|
||||||
|
};
|
||||||
let edge_a = Edge {
|
let edge_a = Edge {
|
||||||
weight: 2.5,
|
weight: 2.5,
|
||||||
destination: Tree {root: Vertex::Terminal(a)}
|
destination: Tree {root: Vertex::Terminal(a)}
|
||||||
|
|
@ -139,10 +156,62 @@ mod tests {
|
||||||
destination: Tree {root: Vertex::Terminal(c)}
|
destination: Tree {root: Vertex::Terminal(c)}
|
||||||
};
|
};
|
||||||
let root = Vertex::NonTerminal(Box::new(edge_ab), Box::new(edge_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!(
|
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))
|
(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