-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Topic/inverse evaluate proposer #8
base: master
Are you sure you want to change the base?
Changes from all commits
3369455
2e787dd
677d68c
64cd2c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,7 +12,7 @@ use rand::Rng; | |
use std::f64::NEG_INFINITY; | ||
use std::fmt; | ||
use term_rewriting::trace::Trace; | ||
use term_rewriting::{Rule, RuleContext, Strategy as RewriteStrategy, TRS as UntypedTRS}; | ||
use term_rewriting::{Rule, RuleContext, Strategy as RewriteStrategy, Term, TRS as UntypedTRS}; | ||
|
||
use super::{Lexicon, ModelParams, SampleError, TypeError}; | ||
|
||
|
@@ -279,13 +279,9 @@ impl TRS { | |
) -> Result<TRS, SampleError> { | ||
let mut trs = self.clone(); | ||
let context = sample_iter(rng, contexts, 1)?[0].clone(); | ||
let rule = trs.lex.sample_rule_from_context( | ||
context, | ||
&mut trs.ctx, | ||
atom_weights, | ||
true, | ||
max_size, | ||
)?; | ||
let rule = | ||
trs.lex | ||
.sample_rule_from_context(context, &mut trs.ctx, atom_weights, true, max_size)?; | ||
trs.lex | ||
.0 | ||
.write() | ||
|
@@ -304,11 +300,112 @@ impl TRS { | |
Err(SampleError::OptionsExhausted) | ||
} else { | ||
let mut trs = self.clone(); | ||
trs.utrs | ||
.remove_clauses(sample_iter(rng, deletable, 1)?[0])?; | ||
trs.utrs.remove_clauses(sample_iter(rng, deletable, 1)?[0])?; | ||
Ok(trs) | ||
} | ||
} | ||
/// Replaces one subterm with another subterm in a main Term. | ||
fn replace_term_helper(term: &Term, replace: &Term, new: Term) -> Term { | ||
if Term::alpha(replace, term) != None { | ||
let unwrapped_sub = Term::alpha(replace, term).unwrap(); | ||
return new.substitute(&unwrapped_sub); | ||
} else if term.args() != vec![] { | ||
match *term { | ||
Term::Variable(ref _var) => { | ||
return term.clone(); | ||
} | ||
Term::Application { ref op, args: _ } => { | ||
let mut args = term.args().clone(); | ||
for idx in 0..args.len() { | ||
args[idx] = TRS::replace_term_helper(&args[idx], replace, new.clone()); | ||
} | ||
let op = op.clone(); | ||
return Term::Application { op, args }; | ||
} | ||
} | ||
} | ||
return term.clone(); | ||
} | ||
/// Replaces one subterm with another subterm in a given Rule. | ||
fn replace_term_in_rule_helper(rule: &Rule, t: &Term, v: Term) -> Option<Rule> { | ||
let r = rule.clone(); | ||
let lhs = TRS::replace_term_helper(&r.lhs, t, v.clone()); | ||
let mut rhs: Vec<Term> = vec![]; | ||
for idx in 0..r.rhs.len() { | ||
rhs.push(TRS::replace_term_helper(&r.rhs[idx].clone(), t, v.clone())); | ||
} | ||
Rule::new(lhs, rhs) | ||
} | ||
// helper for routinization | ||
// TODO fix rule lhs before inserting | ||
fn inverse_evaluation_helper<R: Rng>(rule: &Rule, t: &Term, rng: &mut R) -> Term { | ||
let rhs_idx = rng.gen_range(0, rule.rhs.len()); | ||
TRS::replace_term_helper(t, &rule.rhs[rhs_idx], rule.lhs.clone()) | ||
} | ||
/// Given two rules, attempts to apply one rule inversely to the other. | ||
fn inverse_evaluate_rule_helper<R: Rng>(rule: &Rule, r: &Rule, rng: &mut R) -> Option<Rule> { | ||
let lhs = TRS::inverse_evaluation_helper(rule, &r.lhs, rng); | ||
let mut rhs: Vec<Term> = vec![]; | ||
for idx in 0..r.rhs.len() { | ||
rhs.push(TRS::inverse_evaluation_helper(rule, &r.rhs[idx], rng)); | ||
} | ||
Rule::new(lhs, rhs) | ||
} | ||
|
||
/// Extracts a sngle rule from the TRS selecting only 1 rhs | ||
fn sample_rule<R: Rng>(&self, rng: &mut R) -> Result<Rule, SampleError> { | ||
let mut trs = self.clone(); | ||
let num_rules = self.len(); | ||
let num_background = self | ||
.lex | ||
.0 | ||
.read() | ||
.expect("poisoned lexicon") | ||
.background | ||
.len(); | ||
let ref_idx: usize = rng.gen_range(0, num_rules); | ||
let mut ref_rule = trs.utrs.rules[ref_idx].clone(); | ||
let rhs_idx = rng.gen_range(0, ref_rule.rhs.len()); | ||
let temp_rule = Rule::new(ref_rule.lhs, vec![ref_rule.rhs[rhs_idx].clone()]); | ||
if temp_rule == None { | ||
return Err(SampleError::OptionsExhausted); | ||
} | ||
ref_rule = temp_rule.unwrap(); | ||
joshrule marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if ref_rule.lhs.variables().len() < ref_rule.rhs[0].variables().len() { | ||
return Err(SampleError::OptionsExhausted); | ||
} | ||
Ok(ref_rule) | ||
} | ||
/// Selects two Rules from the TRS at random and atempts to inverse evaluate one rule on the other, if it | ||
/// succeeds it takes that new rule and inserts it imediately after the background. | ||
pub fn inverse_evaluate<R: Rng>(&self, rng: &mut R) -> Result<TRS, SampleError> { | ||
let mut trs = self.clone(); | ||
let num_rules = self.len(); | ||
let num_background = self | ||
.lex | ||
.0 | ||
.read() | ||
.expect("poisoned lexicon") | ||
.background | ||
.len(); | ||
if num_background >= num_rules - 1 { | ||
return Err(SampleError::OptionsExhausted); | ||
} | ||
let mut target_idx: usize = rng.gen_range(num_background, num_rules); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of sampling a rule by index, would it work just to sample another clause? Also, we write so much boilerplate to figure out how many rules we've learned. Perhaps that could also be extracted into a helper function, say, |
||
let ref_rule = trs.sample_rule(rng)?; | ||
let new_rule = | ||
TRS::inverse_evaluate_rule_helper(&ref_rule, &trs.utrs.rules[target_idx], rng); | ||
if new_rule == None { | ||
return Err(SampleError::OptionsExhausted); | ||
} | ||
trs.utrs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you can use trs.utrs.remove_clauses(&ref_rule)?; |
||
.remove_clauses(&ref_rule) | ||
.expect("removing old rule"); | ||
trs.utrs | ||
.insert_clauses(&new_rule.unwrap()) | ||
.expect("inserting new rule"); | ||
Ok(trs) | ||
} | ||
} | ||
impl fmt::Display for TRS { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
|
@@ -331,3 +428,68 @@ impl fmt::Display for TRS { | |
write!(f, "{}", trs_str) | ||
} | ||
} | ||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
extern crate polytype; | ||
extern crate rand; | ||
extern crate term_rewriting; | ||
use rand::thread_rng; | ||
use term_rewriting::{parse_rule, parse_term, Context, RuleContext, Signature}; | ||
|
||
#[test] | ||
fn replace_term_helper_test() { | ||
let mut sig = Signature::default(); | ||
let term = parse_term(&mut sig, "A(y_ B(C) D)").expect("parse of A(y_ B(C) D)"); | ||
let t = parse_term(&mut sig, "B(C)").expect("parse of B(C)"); | ||
let v = parse_term(&mut sig, "x_").expect("parse of x_"); | ||
|
||
let new_term = TRS::replace_term_helper(&term, &t, v); | ||
assert_eq!(new_term.display(), "A(y_ x_ D)"); | ||
} | ||
|
||
#[test] | ||
fn replace_term_in_rule_helper_test() { | ||
let mut sig = Signature::default(); | ||
let rule = | ||
parse_rule(&mut sig, "A(y_ B(C) D) = B(C)").expect("parse of A(y_ B(C) D) = B(C)"); | ||
let t = parse_term(&mut sig, "B(C)").expect("parse of B(C)"); | ||
let v = parse_term(&mut sig, "x_").expect("parse of x_"); | ||
|
||
let new_rule = TRS::replace_term_in_rule_helper(&rule, &t, v); | ||
if new_rule == None { | ||
assert!(false); | ||
} else { | ||
let rule = new_rule.unwrap(); | ||
assert_eq!(rule.display(), "A(y_ x_ D) = x_"); | ||
} | ||
} | ||
|
||
#[test] | ||
fn inverse_evaluation_helper_test() { | ||
let mut sig = Signature::default(); | ||
|
||
let t = parse_term(&mut sig, "A(B(x_ x_))").expect("parse of A(B(x_ x_))"); | ||
let r = parse_rule(&mut sig, "C(y_) = B(y_ y_)").expect("parse of C(y_) = B(y_ y_)"); | ||
let mut rng = thread_rng(); | ||
|
||
let result = TRS::inverse_evaluation_helper(&r, &t, &mut rng); | ||
|
||
assert_eq!(result.display(), "A(C(x_))"); | ||
} | ||
|
||
#[test] | ||
fn inverse_evluate_rule_helper_test() { | ||
let mut sig = Signature::default(); | ||
|
||
let r = parse_rule(&mut sig, "A(B(x_ x_)) = D B(x_ x_)") | ||
.expect("parse of A(B(x_ x_)) = D B(x_ x_)"); | ||
let rule = parse_rule(&mut sig, "C(y_) = B(y_ y_)").expect("parse of C(y_) = B(y_ y_)"); | ||
let mut rng = thread_rng(); | ||
|
||
let result = TRS::inverse_evaluate_rule_helper(&rule, &r, &mut rng); | ||
|
||
assert_eq!(result.unwrap().pretty(), "A(C(x_)) = D C(x_)"); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Funny - I forgot that I'd put this in the last review and just wrote a similar function,
TRS::choose_clause
, a couple days ago. I suppose we have a strong use case for it! I'm not sure why this one is quite so long, though. Here's my implementation:Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I was reading some of the lines you'd deleted as part of the current function. They're pretty similar in length.