Skip to content

Commit

Permalink
Distinguish between integer and field element.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Jan 24, 2024
1 parent f2be0cb commit aac525c
Show file tree
Hide file tree
Showing 25 changed files with 374 additions and 140 deletions.
6 changes: 4 additions & 2 deletions asm-to-pil/src/romgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ fn substitute_name_in_statement_expressions<T>(
) {
fn substitute<T>(e: &mut Expression<T>, substitution: &HashMap<String, String>) {
if let Expression::Reference(r) = e {
if let Some(v) = substitution.get(r.try_to_identifier().unwrap()).cloned() {
*r = NamespacedPolynomialReference::from_identifier(v);
if let Some(n) = r.try_to_identifier() {
if let Some(v) = substitution.get(n).cloned() {
*r = NamespacedPolynomialReference::from_identifier(v);
}
}
};
}
Expand Down
1 change: 1 addition & 0 deletions ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ powdr-number = { path = "../number" }

itertools = "0.11.0"
num-bigint = "0.4.3"
num-traits = "0.2.15"
diff = "0.1"
log = "0.4.18"
derive_more = "0.99.17"
Expand Down
34 changes: 0 additions & 34 deletions ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

use itertools::Itertools;
use log::log_enabled;
use parsed::{BinaryOperator, UnaryOperator};
use powdr_number::FieldElement;
use std::fmt::{Display, Result, Write};
use std::sync::Arc;

Expand Down Expand Up @@ -60,38 +58,6 @@ impl DiffMonitor {
}
}

pub fn evaluate_binary_operation<T: FieldElement>(left: T, op: BinaryOperator, right: T) -> T {
match op {
BinaryOperator::Add => left + right,
BinaryOperator::Sub => left - right,
BinaryOperator::Mul => left * right,
BinaryOperator::Div => left.integer_div(right),
BinaryOperator::Pow => left.pow(right.to_integer()),
BinaryOperator::Mod => (left.to_arbitrary_integer() % right.to_arbitrary_integer()).into(),
BinaryOperator::BinaryAnd => (left.to_integer() & right.to_integer()).into(),
BinaryOperator::BinaryXor => (left.to_integer() ^ right.to_integer()).into(),
BinaryOperator::BinaryOr => (left.to_integer() | right.to_integer()).into(),
BinaryOperator::ShiftLeft => (left.to_integer() << right.to_degree()).into(),
BinaryOperator::ShiftRight => (left.to_integer() >> right.to_degree()).into(),
BinaryOperator::LogicalOr => (!left.is_zero() || !right.is_zero()).into(),
BinaryOperator::LogicalAnd => (!left.is_zero() && !right.is_zero()).into(),
BinaryOperator::Less => (left.to_integer() < right.to_integer()).into(),
BinaryOperator::LessEqual => (left.to_integer() <= right.to_integer()).into(),
BinaryOperator::Equal => (left == right).into(),
BinaryOperator::NotEqual => (left != right).into(),
BinaryOperator::GreaterEqual => (left.to_integer() >= right.to_integer()).into(),
BinaryOperator::Greater => (left.to_integer() > right.to_integer()).into(),
}
}

pub fn evaluate_unary_operation<T: FieldElement>(op: UnaryOperator, v: T) -> T {
match op {
UnaryOperator::Minus => -v,
UnaryOperator::LogicalNot => v.is_zero().into(),
UnaryOperator::Next => panic!("Cannot evaluate \"'\"."),
}
}

/// quick and dirty String to String indentation
pub fn indent<S: ToString>(s: S, indentation: usize) -> String {
s.to_string()
Expand Down
1 change: 1 addition & 0 deletions executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ log = { version = "0.4.17" }
rayon = "1.7.0"
bit-vec = "0.6.3"
num-traits = "0.2.15"
num-bigint = "0.4.3"
lazy_static = "1.4.0"
indicatif = "0.17.7"

Expand Down
60 changes: 46 additions & 14 deletions executor/src/constant_evaluator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,13 @@ fn generate_values<T: FieldElement>(
// We could try to avoid the first evaluation to be run for each iteration,
// but the data is not thread-safe.
let fun = evaluator::evaluate(e, &symbols).unwrap();
evaluator::evaluate_function_call(fun, vec![Rc::new(T::from(i).into())], &symbols)
.and_then(|v| v.try_to_number())
evaluator::evaluate_function_call(
fun,
vec![Rc::new(Value::Integer(num_bigint::BigInt::from(i)))],
&symbols,
)
// TODO should this be non-sloppy?
.and_then(|v| v.try_to_field_element_sloppy())
})
.collect::<Result<Vec<_>, _>>(),
FunctionValueDefinition::Array(values) => values
Expand All @@ -59,7 +64,11 @@ fn generate_values<T: FieldElement>(
let items = elements
.pattern()
.iter()
.map(|v| evaluator::evaluate(v, &symbols).and_then(|v| v.try_to_number()))
.map(|v| {
evaluator::evaluate(v, &symbols)
// TODO should this be non-sloppy?
.and_then(|v| v.try_to_field_element_sloppy())
})
.collect::<Result<Vec<_>, _>>()?;

Ok(items
Expand Down Expand Up @@ -126,14 +135,16 @@ impl<'a, T: FieldElement> SymbolLookup<'a, T, FixedColumnRef<'a>> for Symbols<'a
arguments.len()
)))?
};
let Value::Number(row) = arguments[0].as_ref() else {
let Value::Integer(row) = arguments[0].as_ref() else {
return Err(EvalError::TypeError(format!(
"Expected number but got {}",
"Expected integer but got {}",
arguments[0]
)));
};
let data = &self.computed_columns[function.name];
Ok(Value::Number(data[row.to_degree() as usize % data.len()]))
Ok(Value::FieldElement(
data[usize::try_from(row).unwrap() % data.len()],
))
}
}

Expand Down Expand Up @@ -276,9 +287,11 @@ mod test {
pub fn test_poly_call() {
let src = r#"
constant %N = 10;
namespace std::conv(%N);
let int = [];
namespace F(%N);
col fixed seq(i) { i };
col fixed doub(i) { seq((2 * i) % %N) + 1 };
col fixed doub(i) { std::conv::int(seq((2 * i) % %N)) + 1 };
col fixed half_nibble(i) { i & 0x7 };
col fixed doubled_half_nibble(i) { half_nibble(i / 2) };
"#;
Expand Down Expand Up @@ -361,20 +374,23 @@ mod test {
pub fn comparisons() {
let src = r#"
constant %N = 6;
namespace std::conv(%N);
let int = 9;
let fe = 8;
namespace F(%N);
col fixed id(i) { i };
col fixed inv(i) { %N - i };
col fixed a = [0, 1, 0, 1, 2, 1];
col fixed b = [0, 0, 1, 1, 0, 5];
col fixed or(i) { a(i) || b(i) };
col fixed and(i) { a(i) && b(i) };
col fixed not(i) { !a(i) };
col fixed less(i) { id(i) < inv(i) };
col fixed less_eq(i) { id(i) <= inv(i) };
col fixed or(i) { (a(i) != std::conv::fe(0)) || (b(i) != std::conv::fe(0)) };
col fixed and(i) { (a(i) != std::conv::fe(0)) && (b(i) != std::conv::fe(0)) };
col fixed not(i) { !(a(i) != std::conv::fe(0)) };
col fixed less(i) { std::conv::int(id(i)) < std::conv::int(inv(i)) };
col fixed less_eq(i) { std::conv::int(id(i)) <= std::conv::int(inv(i)) };
col fixed eq(i) { id(i) == inv(i) };
col fixed not_eq(i) { id(i) != inv(i) };
col fixed greater(i) { id(i) > inv(i) };
col fixed greater_eq(i) { id(i) >= inv(i) };
col fixed greater(i) { std::conv::int(id(i)) > std::conv::int(inv(i)) };
col fixed greater_eq(i) { std::conv::int(id(i)) >= std::conv::int(inv(i)) };
"#;
let analyzed = analyze_string(src);
assert_eq!(analyzed.degree(), 6);
Expand Down Expand Up @@ -471,4 +487,20 @@ mod test {
assert_eq!(constants[0], ("F.x", convert([21, 22, 23, 24].to_vec())));
assert_eq!(constants[1], ("F.y", convert([20, 21, 22, 23].to_vec())));
}

#[test]
pub fn bigint_arith() {
let src = r#"
constant %N = 4;
namespace std::conv(%N);
let int = [];
let fe = [];
namespace F(%N);
let x = |i| std::conv::fe((std::conv::int(1) << (2000 + i)) >> 2000);
"#;
let analyzed = analyze_string::<GoldilocksField>(src);
assert_eq!(analyzed.degree(), 4);
let constants = generate(&analyzed);
assert_eq!(constants[0], ("F.x", convert([1, 2, 4, 8].to_vec())));
}
}
24 changes: 14 additions & 10 deletions executor/src/witgen/query_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{fmt::Display, rc::Rc};

use num_traits::ToPrimitive;
use powdr_ast::analyzed::{AlgebraicReference, Expression, PolyID, PolynomialType};
use powdr_number::FieldElement;
use powdr_number::{DegreeType, FieldElement};
use powdr_pil_analyzer::evaluator::{self, Custom, EvalError, SymbolLookup, Value};

use super::{rows::RowPair, Constraint, EvalResult, EvalValue, FixedData, IncompleteCause};
Expand Down Expand Up @@ -77,7 +77,9 @@ impl<'a, 'b, T: FieldElement, QueryCallback: super::QueryCallback<T>>
query: &'a Expression<T>,
rows: &RowPair<T>,
) -> Result<String, EvalError> {
let arguments = vec![Rc::new(T::from(rows.current_row_index).into())];
let arguments = vec![Rc::new(Value::Integer(num_bigint::BigInt::from(
rows.current_row_index,
)))];
let symbols = Symbols {
fixed_data: self.fixed_data,
rows,
Expand Down Expand Up @@ -114,17 +116,20 @@ impl<'a, T: FieldElement> SymbolLookup<'a, T, Reference<'a>> for Symbols<'a, T>
arguments.len()
)))?
};
let Value::Number(row) = arguments[0].as_ref() else {
let Value::Integer(row) = arguments[0].as_ref() else {
return Err(EvalError::TypeError(format!(
"Expected number but got {}",
"Expected integer but got {}",
arguments[0]
)));
};
Ok(Value::Number(match function.poly_id.ptype {
Ok(Value::FieldElement(match function.poly_id.ptype {
PolynomialType::Committed | PolynomialType::Intermediate => {
let next = self.rows.is_row_number_next(row.to_degree()).map_err(|_| {
EvalError::OutOfBounds(format!("Referenced row outside of window: {row}"))
})?;
let next = self
.rows
.is_row_number_next(DegreeType::try_from(row).unwrap())
.map_err(|_| {
EvalError::OutOfBounds(format!("Referenced row outside of window: {row}"))
})?;
let poly_ref = AlgebraicReference {
name: function.name.to_string(),
poly_id: function.poly_id,
Expand All @@ -137,8 +142,7 @@ impl<'a, T: FieldElement> SymbolLookup<'a, T, Reference<'a>> for Symbols<'a, T>
}
PolynomialType::Constant => {
let values = self.fixed_data.fixed_cols[&function.poly_id].values;
// TODO can we avoid bigint?
values[(row.to_arbitrary_integer() % values.len())
values[(usize::try_from(row).unwrap() % values.len())
.to_u64()
.unwrap() as usize]
}
Expand Down
2 changes: 1 addition & 1 deletion linker/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ namespace main_sub(16);
pol pc_update = ((((((instr_jmpz * (instr_jmpz_pc_update + instr_jmpz_pc_update_1)) + (instr_jmp * instr_jmp_param_l)) + (instr__jump_to_operation * _operation_id)) + (instr__loop * pc)) + (instr_return * 0)) + ((1 - ((((instr_jmpz + instr_jmp) + instr__jump_to_operation) + instr__loop) + instr_return)) * (pc + 1)));
pc' = ((1 - first_step') * pc_update);
pol constant p_line = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + [10]*;
pol commit X_free_value(i) query match pc(i) { 2 => ("input", 1), 4 => ("input", (CNT(i) + 1)), 7 => ("input", 0), };
pol commit X_free_value(i) query match pc(i) { 2 => ("input", 1), 4 => ("input", (std::conv::int(CNT(i)) + 1)), 7 => ("input", 0), };
pol constant p_X_const = [0]*;
pol constant p_X_read_free = [0, 0, 1, 0, 1, 0, 0, -1, 0, 0, 0] + [0]*;
pol constant p_instr__jump_to_operation = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] + [0]*;
Expand Down
2 changes: 2 additions & 0 deletions pil-analyzer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ powdr-parser = { path = "../parser" }
powdr-parser-util = { path = "../parser-util" }

itertools = "^0.10"
num-bigint = "0.4.3"
num-traits = "0.2.15"

[dev-dependencies]
test-log = "0.2.12"
Expand Down
15 changes: 7 additions & 8 deletions pil-analyzer/src/condenser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,7 @@ impl<T: FieldElement> Condenser<T> {
evaluator::evaluate(e, &self)
.and_then(|result| match result {
Value::Custom(Condensate::Expression(expr)) => Ok(expr),
Value::Number(n) => Ok(n.into()),
_ => Err(EvalError::TypeError(format!(
"Expected expression, but got {result}"
))),
x => Ok(x.try_to_field_element()?.into()),
})
.unwrap_or_else(|err| {
panic!("Error reducing expression to constraint:\nExpression: {e}\nError: {err:?}")
Expand Down Expand Up @@ -269,13 +266,13 @@ impl<'a, T: FieldElement> SymbolLookup<'a, T, Condensate<T>> for &'a Condenser<T
}) if poly_id.ptype == PolynomialType::Constant => {
let arguments = if next {
assert_eq!(arguments.len(), 1);
let Value::Number(arg) = *arguments[0] else {
let Value::Integer(ref arg) = *arguments[0] else {
return Err(EvalError::TypeError(
"Expected numeric argument when evaluating function with next ref."
"Expected integer argument when evaluating function with next ref."
.to_string(),
));
};
vec![Rc::new(Value::Number(arg + 1.into()))]
vec![Rc::new(Value::Integer(arg + num_bigint::BigInt::from(1)))]
} else {
arguments.to_vec()
};
Expand Down Expand Up @@ -403,7 +400,9 @@ impl<'a, T: FieldElement> TryFrom<Value<'a, T, Self>> for Condensate<T> {

fn try_from(value: Value<'a, T, Self>) -> Result<Self, Self::Error> {
match value {
Value::Number(n) => Ok(Condensate::Expression(n.into())),
Value::FieldElement(_) | Value::Integer(_) => {
Ok(Condensate::Expression(value.try_to_field_element()?.into()))
}
Value::Custom(v) => Ok(v),
value => Err(EvalError::TypeError(format!(
"Expected algebraic expression, got {value}"
Expand Down
Loading

0 comments on commit aac525c

Please sign in to comment.