Skip to content
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

Support hint function #220

Merged
merged 21 commits into from
Nov 13, 2024
Merged

Support hint function #220

merged 21 commits into from
Nov 13, 2024

Conversation

katat
Copy link
Collaborator

@katat katat commented Nov 4, 2024

This is a POC to support native hint function based on circ's IR and API.

The integration is quite straightforward. The major changes are listed below. Most of the code in circuit_writer/ir.rs remain the same as the circuit synthesizer:

  1. circ IR can be composed using its term API, which can be used independently. (doesn't have to couple with other parts in circ)

  2. creates a IR translator based on the template of synthesizer, to translate the mast to IR if it is a hint function.

  3. one major change is to replace the ConstOrCell in the Var with the circ IR term, as a term can represent either constant field or a variable.
    eg:

pub struct Var {
    pub cvars: Vec<Term>,
    pub span: Span,
}

So we may want to consider refactor the var::Var to cover both ConstOrCell and Term, in order to avoid the code duplication in the ir.rs.

  1. another major change is to translate the binary operations or conditional branch.
    eg:
Op2::Division => {
    let t: Term = term![
        Op::PfNaryOp(PfNaryOp::Mul); lhs.cvars[0].clone(), 
        term![Op::PfUnOp(PfUnOp::Recip); rhs.cvars[0].clone()]
    ];
    Var::new_cvar(t, expr.span)
}
  1. atm, everything is defined as field type at low level using the circ IR

  2. create a Value::HintIR to represent an IR generated from a hint function, and compute it via compute_val using circ's PreComp API.

TODOs:

  • support accepting other noname argument types and return types for hint functions.

Copy link
Contributor

@StefanosChaliasos StefanosChaliasos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR lgtm, I have a few questions wrt the type transformations.

src/backends/mod.rs Outdated Show resolved Hide resolved
src/circuit_writer/ir.rs Outdated Show resolved Hide resolved
}
}

pub fn new_constant_typ<F: BackendField>(cst_info: &ConstInfo<F>, span: Span) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is the coding style :D

src/circuit_writer/mod.rs Show resolved Hide resolved
src/parser/types.rs Show resolved Hide resolved
src/backends/mod.rs Outdated Show resolved Hide resolved
src/backends/mod.rs Outdated Show resolved Hide resolved
src/circuit_writer/ir.rs Outdated Show resolved Hide resolved
@katat katat changed the title POC for supporting hint function Support hint function Nov 12, 2024
ErrorKind::InvalidFnCall("only hint functions allowed"),
expr.span,
));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw what is the rationale here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, solves my previous comment :D. I guess here we check that a hint can call only another hint which makes sense

@katat
Copy link
Collaborator Author

katat commented Nov 12, 2024 via email

@mimoo
Copy link
Contributor

mimoo commented Nov 12, 2024

you could think that constraint functions wouldn't produce constraints if called by hints, but yeah I don't think it's a big deal :o was just wondering

@mimoo
Copy link
Contributor

mimoo commented Nov 12, 2024

LGTM but @StefanosChaliasos I'll let you approve

@@ -0,0 +1,1119 @@
use ark_ff::Zero;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw as most of this file will be useful in the future to have an IR pass, I think it should be moved to an circ_ir folder or something, and moved out of the circuit_writer

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

Copy link
Contributor

@mimoo mimoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approving for the sake of getting this moved, let's add a disclaimer that we're still experimental in the README

@mimoo mimoo merged commit 8c0ca9d into main Nov 13, 2024
1 check passed
@@ -199,6 +202,48 @@ pub trait Backend: Clone {

Ok(res)
}
Value::HintIR(t, named_vars) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lgtm, but I would suggest adding some unit tests here to check corner cases (0, p, p-1, p+1).

@@ -0,0 +1,1119 @@
use ark_ff::Zero;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree

if idx < self.cvars.len() {
Some(&self.cvars[idx])
} else {
None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you handle that? Should throw an error here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be cleaner instead of an option to return an error here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it is currently handled by callers.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should add an issue to change that? Or do you think it's better to handle that in the callers? In any case even if you return a result it will be basically handled by callers but it would be more explicit and it will probably print a nicer error message?

.as_value_opt()
.map(|v| BigUint::from(v.as_pf().i().to_u64_wrapping()))
} else {
None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly here

body,
} => {
match argument {
ForLoopArgument::Range(range) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the range supposed to be known at compile time? No, right?

Copy link
Collaborator Author

@katat katat Nov 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it should be known at compiler time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is that? I guess when we are in a hint, there isn't any reason why this should be known at compile time as we are out-of-circuit, right? Do I miss something?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh yeah, this is a good point. I created an issue for that: #234

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh wait, I misunderstood your question.
so yeah, this is an interesting insight. It should allow variables as the range.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess lets keep this way for now, and will revisit this when someone raises the scenarios they need the range to be variables.

Copy link
Contributor

@mimoo mimoo Nov 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't do TDD here, we do user-complaint driven development

.expect("expected constant")
.into();
let start: u32 = start_bg.try_into().map_err(|_| {
self.error(ErrorKind::InvalidRangeSize, range.start.span)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have this error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

atm, the size of the number is restricted to u32, so it throws error if it is bigger.

function: &FunctionDef,
args: Vec<crate::circuit_writer::fn_env::VarInfo<B::Field, B::Var>>,
) -> Result<Vec<crate::var::Value<B>>> {
assert!(!function.is_main());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we actually check that we are calling a hint function and not a non hint one?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, a hint should only be able to call hint function, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, only hint functions are allowed.

ErrorKind::InvalidFnCall("only hint functions allowed"),
expr.span,
));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, solves my previous comment :D. I guess here we check that a hint can call only another hint which makes sense

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants