-
Notifications
You must be signed in to change notification settings - Fork 16
feat!: Log Directive support for traces #105
Conversation
…from printing when solving directives
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.
It looks like you need to persist some state during the solve. I also needed this for solving the new block opcode on my PR #114. The solve is no more recursive and we can instantiate data that persist throughout the solve process without adding specific parameters and return values. I suggest you branch from it.
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.
I'm happy with this PR now. @guipublic Can you remove the block as we've addressed the recursive solve function?
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.
My two comments are to remove the logging logic from ACIR and to keep the solving coherent with the other gates.
@@ -58,7 +58,10 @@ pub enum Directive { | |||
bits: Vec<Witness>, // control bits of the network which permutes the inputs into its sorted version | |||
sort_by: Vec<u32>, // specify primary index to sort by, then the secondary,... For instance, if tuple is 2 and sort_by is [1,0], then a=[(a0,b0),..] is sorted by bi and then ai. | |||
}, | |||
Log(LogInfo), | |||
Log { | |||
is_trace: bool, // This field states whether the log should be further manipulated or simply displayed to standard output |
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.
Why do we have two separate process, and not just one which basically notifies when the 'log witness' are solved? Then the caller can decide to print to standard output or to further manipulate them. This logic should not be in ACIR.
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.
This is needed as without this flag the caller does not know whether to print to standard output or to further manipulate the logs. I was trying to have the same directive for the builtins std::println
and std::trace
. For example, if solve
simply returns a finalized string to be logged, but both std::println
and std::trace
exist, the caller will not know whether to print to std out or manipulate the trace how it sees fit without more info. The flag would be set during the evaluator, and essentially passed along until solve
is called. Only the caller of solve
would implement the logic of how to handle a log according to the flag.
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.
I am not suggesting to remove the flag, sorry for the mis-understanding, the location of my comment is confusing. This is because my first reaction was to ask for a clarification on the flag meaning. But at the end, I think it is better to not handle it in the solve, and just solve the witness.
With the flag (from the directive), the caller should be able to handle it correctly, shouldn't he?
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.
Yes that is the case. We only pass the flag along with the solved witness info so that the caller can determine how they should handle the solved log directive. The process in solve_directives
for Directive::Log
remains the same for both traces and printlns.
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.
The flag is also used during the solve, that's why I thought we were implementing its logic.
The solve is handling the flag and also behaves differently when there is only one witness. Could you explain why we have these 2 special cases?
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.
So currently we only support logs of individual fields, arrays, or strings (formatting and handling of complex types can come in a separate PR). In the case of one witness we don't have to construct a list of hex elements. We can simply format the respective witness value and return that the log has been solved. In the case of multiple witnesses this means we have an array, where we have to fetch a list of hex elements, and format them into one finalized string output. Both cases will form a LogOutputInfo::FinalizedOutput(String)
, which the caller of solve
will then handle. In the case of a string this is formatted during acir gen and should already be a LogOutputInfo::FinalizedOutput
. If Directive::Log
already has finalized output we can just return the string stored in the enum.
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.
Thank you for the explanation. For me you are processing the logs here and you should not. The Log directive should just hold the parameters (for now just the flag), and the witness to log.
When the gate is solved, i.e when all the witness are known, you can send the parameters and witness values to the caller. That's it. How/where to print, which language to use, etc... all this should be handled by the caller.
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.
Got it, I see now. Ok I will refactor this
@@ -15,13 +15,13 @@ use super::{get_value, insert_value, sorting::route, witness_to_value}; | |||
pub fn solve_directives( | |||
initial_witness: &mut BTreeMap<Witness, FieldElement>, | |||
directive: &Directive, | |||
) -> Result<(), OpcodeResolutionError> { | |||
) -> Result<Option<Directive>, OpcodeResolutionError> { |
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.
I don't think the solve should return an optional directive. The directive is either solved or not. If it is solved, then you can get the witness values from the witness map.
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.
Agreed - If a directive is not solved, then it means we did not find a witness that it needed, this will return an Error
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.
I was unsure on whether I should do this but I went with returning a directive mainly to remove duplication of code as the logging information needed by whomever is calling solve is very similar to the Log directive itself. Whomever is calling solve must check that we have a finalized string output and whether the log is a trace or a println. This can be seen in this draft PR: noir-lang/noir#872. I will look into changing this as I agree it confuses the idea of whether the directive has been solved or not.
This should not have been a blocking change to this PR. In general, we should not branch off of or copy unrelated PRs. If we want to remove recursion, so that two separate PRs can benefit from it, we should have a PR that does this, merge it into master and then have both PRs rebase. This is beneficial for two reasons:
|
Closing in favor of PR #137 |
…from printing when solving directives
Related issue(s)
Resolves #95
Description
Directive::Log
no longer prints the evaluated log while it is being evaluating during partial witness generation. This enables us to have both trace functionality and move logic for displaying logs to the code that calls the PWG rather than keeping it with solver itself.Summary of changes
solve
under the PWG now takes in a vector of directives. These directives are limited to beDirective::Log
as it does not make sense to be returning any other directive. The log directive that is returned must have have finalized output or else that means there is a critical bug.Several more changes showing how to handle logs correctly will be supplied in
nargo
.Dependency additions / changes
(If applicable.)
Test additions / changes
(If applicable.)
Checklist
cargo fmt
with default settings.Additional context
(If applicable.)