Skip to content

Commit

Permalink
logging for debugging
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurpaulino committed Aug 23, 2023
1 parent e241b1a commit b46df88
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 30 deletions.
31 changes: 28 additions & 3 deletions src/cli/repl_lem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,34 @@ impl ReplLEM<F> {
}
}

#[inline]
fn eval_expr(&mut self, expr_ptr: Ptr<F>) -> Result<(Vec<Ptr<F>>, usize, Vec<Ptr<F>>)> {
evaluate_simple(expr_ptr, &mut self.store, self.limit)
let (ptrs, iterations, emitted) = evaluate_simple(expr_ptr, &mut self.store, self.limit)?;
match ptrs[2].tag() {
Tag::Cont(Terminal) => Ok((ptrs, iterations, emitted)),
t => {
let iterations_display = Self::pretty_iterations_display(iterations);
if t == &Tag::Cont(Error) {
bail!("Evaluation encountered an error after {iterations_display}")
} else {
bail!("Limit reached after {iterations_display}")
}
}
}
}

fn eval_expr_allowing_error_continuation(
&mut self,
expr_ptr: Ptr<F>,
) -> Result<(Vec<Ptr<F>>, usize, Vec<Ptr<F>>)> {
let (ptrs, iterations, emitted) = evaluate_simple(expr_ptr, &mut self.store, self.limit)?;
if matches!(ptrs[2].tag(), Tag::Cont(Terminal) | Tag::Cont(Error)) {
Ok((ptrs, iterations, emitted))
} else {
bail!(
"Limit reached after {}",
Self::pretty_iterations_display(iterations)
)
}
}

fn eval_expr_and_memoize(&mut self, expr_ptr: Ptr<F>) -> Result<(Vec<Ptr<F>>, usize)> {
Expand Down Expand Up @@ -415,7 +440,7 @@ impl ReplLEM<F> {
}
"assert-error" => {
let first = self.peek1(cmd, args)?;
let (first_io, ..) = self.eval_expr(first)?;
let (first_io, ..) = self.eval_expr_allowing_error_continuation(first)?;
if first_io[2].tag() != &Tag::Cont(Error) {
eprintln!(
"`assert-error` failed. {} doesn't result on evaluation error.",
Expand Down
20 changes: 17 additions & 3 deletions src/lem/eval.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::Result;
use once_cell::sync::OnceCell;

use crate::{field::LurkField, func, tag::ContTag::*};
use crate::{field::LurkField, func, state::initial_lurk_state, tag::ContTag::*};

use super::{interpreter::Frame, pointers::Ptr, store::Store, Func, Tag};

Expand Down Expand Up @@ -31,8 +31,20 @@ pub fn evaluate<F: LurkField>(
let stop_cond = |output: &[Ptr<F>]| {
output[2] == Ptr::null(Tag::Cont(Terminal)) || output[2] == Ptr::null(Tag::Cont(Error))
};
let state = initial_lurk_state();
let log_fmt = |i: usize, ptrs: &[Ptr<F>], store: &Store<F>| {
format!(
"Frame: {}\n\tExpr: {}\n\tEnv: {}\n\tCont: {}",
i,
ptrs[0].fmt_to_string(store, state),
ptrs[1].fmt_to_string(store, state),
ptrs[2].fmt_to_string(store, state)
)
};

let input = &[expr, store.intern_nil(), Ptr::null(Tag::Cont(Outermost))];
let (frames, iterations, _) = eval_step().call_until(input, store, stop_cond, limit)?;
let (frames, iterations, _) =
eval_step().call_until(input, store, stop_cond, limit, log_fmt)?;
Ok((frames, iterations))
}

Expand Down Expand Up @@ -1107,13 +1119,15 @@ mod tests {
// Stop condition: the continuation is either terminal or error
let stop_cond = |output: &[Ptr<Fr>]| output[2] == terminal || output[2] == error;

let log_fmt = |_: usize, _: &[Ptr<Fr>], _: &Store<Fr>| String::default();

let limit = 10000;

let mut cs_prev = None;
for (expr_in, expr_out) in pairs {
let input = [expr_in, nil, outermost];
let (frames, _, paths) = eval_step
.call_until(&input, store, stop_cond, limit)
.call_until(&input, store, stop_cond, limit, log_fmt)
.unwrap();
let last_frame = frames.last().expect("eval should add at least one frame");
assert_eq!(last_frame.output[0], expr_out);
Expand Down
56 changes: 35 additions & 21 deletions src/lem/interpreter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,12 +487,18 @@ impl Func {

/// Calls a `Func` on an input until the stop contidion is satisfied, using the output of one
/// iteration as the input of the next one.
pub fn call_until<F: LurkField, Stop: Fn(&[Ptr<F>]) -> bool>(
pub fn call_until<
F: LurkField,
StopCond: Fn(&[Ptr<F>]) -> bool,
LogFmt: Fn(usize, &[Ptr<F>], &Store<F>) -> String,
>(
&self,
args: &[Ptr<F>],
store: &mut Store<F>,
stop_cond: Stop,
stop_cond: StopCond,
limit: usize,
// TODO: make this argument optional
log_fmt: LogFmt,
) -> Result<(Vec<Frame<F>>, usize, Vec<Path>)> {
assert_eq!(self.input_params.len(), self.output_size);
assert_eq!(self.input_params.len(), args.len());
Expand All @@ -502,34 +508,39 @@ impl Func {
let mut frames = vec![];
let mut paths = vec![];

for iterations in 0..limit {
let mut iterations = 0;

log::info!("{}", &log_fmt(iterations, &input, store));

for _ in 0..limit {
let preimages = Preimages::new_from_func(self);
let (frame, path) = self.call(&input, store, preimages, &mut vec![])?;
let stop = stop_cond(&frame.output);
// Should frames take borrowed vectors instead, as to avoid cloning?
// Using AVec is a possibility, but to create a dynamic AVec, currently,
// requires 2 allocations since it must be created from a Vec and
// Vec<T> -> Arc<[T]> uses `copy_from_slice`.
input = frame.output.clone();
frames.push(frame);
paths.push(path);
if stop {
// pushing a frame that can be padded to match the rc
let preimages = Preimages::new_from_func(self);
let (frame, path) = self.call(&input, store, preimages, &mut vec![])?;
iterations += 1;
log::info!("{}", &log_fmt(iterations, &input, store));
if stop_cond(&frame.output) {
frames.push(frame);
paths.push(path);
return Ok((frames, iterations + 1, paths));
break;
}
frames.push(frame);
paths.push(path);
}
bail!("Computation exceeded the limit of steps {limit}")
if iterations < limit {
// pushing a frame that can be padded
let preimages = Preimages::new_from_func(self);
let (frame, path) = self.call(&input, store, preimages, &mut vec![])?;
frames.push(frame);
paths.push(path);
}
Ok((frames, iterations, paths))
}

pub fn call_until_simple<F: LurkField, Stop: Fn(&[Ptr<F>]) -> bool>(
pub fn call_until_simple<F: LurkField, StopCond: Fn(&[Ptr<F>]) -> bool>(
&self,
args: Vec<Ptr<F>>,
store: &mut Store<F>,
stop_cond: Stop,
stop_cond: StopCond,
limit: usize,
) -> Result<(Vec<Ptr<F>>, usize, Vec<Ptr<F>>)> {
assert_eq!(self.input_params.len(), self.output_size);
Expand All @@ -538,13 +549,16 @@ impl Func {
let mut input = args;
let mut emitted = vec![];

for iterations in 0..limit {
let mut iterations = 0;

for _ in 0..limit {
let (frame, _) = self.call(&input, store, Preimages::default(), &mut emitted)?;
input = frame.output.clone();
iterations += 1;
if stop_cond(&frame.output) {
return Ok((input, iterations + 1, emitted));
break;
}
}
bail!("Computation exceeded the limit of steps {limit}")
Ok((input, iterations, emitted))
}
}
6 changes: 5 additions & 1 deletion src/lem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -812,10 +812,14 @@ mod tests {

let computed_num_constraints = func.num_constraints::<Fr>(store);

let log_fmt = |_: usize, _: &[Ptr<Fr>], _: &Store<Fr>| String::default();

let mut cs_prev = None;
for input in inputs.into_iter() {
let input = [input, nil, outermost];
let (frames, ..) = func.call_until(&input, store, stop_cond, 10).unwrap();
let (frames, ..) = func
.call_until(&input, store, stop_cond, 10, log_fmt)
.unwrap();

let mut cs;

Expand Down
4 changes: 2 additions & 2 deletions src/lem/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ impl<F: LurkField> Ptr<F> {
_ => "<Malformed U64>".into(),
},
Fun => match self.get_index3() {
None => "<Malformed Fun>".into(),
None => "<Opaque Fun>".into(),
Some(idx) => {
if let Some((arg, bod, _)) = store.fetch_3_ptrs(idx) {
match bod.tag() {
Expand Down Expand Up @@ -735,7 +735,7 @@ impl<F: LurkField> Ptr<F> {
None => "<Malformed Comm>".into(),
},
},
Tag::Cont(_) => todo!(),
Tag::Cont(_) => "<CONTINUATION (TODO)>".into(),
Tag::Ctrl(_) => unreachable!(),
}
}
Expand Down

0 comments on commit b46df88

Please sign in to comment.