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

Permutation instructions/links #1126

Merged
merged 1 commit into from
Mar 20, 2024
Merged

Permutation instructions/links #1126

merged 1 commit into from
Mar 20, 2024

Conversation

pacheco
Copy link
Collaborator

@pacheco pacheco commented Mar 5, 2024

Allow asm machines to be linked by permutation arguments.
The syntax is the same as for lookups, but uses a ~ (tilde) instead of =.

This PR is written in a way that there's no change to existing asm/pil code and tests, and so witgen support can be implemented separately

ast/src/object/mod.rs Outdated Show resolved Hide resolved
parser/src/powdr.lalrpop Outdated Show resolved Hide resolved
@georgwiese
Copy link
Collaborator

I tried implementing memory with it: #1129

One issue that I think should be fixed in this PR is that it seems like the selectors can't actually be used. I expect this to be a common pattern:

let is_mem_op = array::sum(selectors);

This currently gives me symbol not found in '::std::memory': 'selectors' even though the selectors do appear in the correct namespace in the final pil. So I guess they are added too late for the check to pass.

@chriseth
Copy link
Member

chriseth commented Mar 7, 2024

I tried implementing memory with it: #1129

One issue that I think should be fixed in this PR is that it seems like the selectors can't actually be used. I expect this to be a common pattern:

let is_mem_op = array::sum(selectors);

This currently gives me symbol not found in '::std::memory': 'selectors' even though the selectors do appear in the correct namespace in the final pil. So I guess they are added too late for the check to pass.

I think they are missing from Machine::local_names()

ast/src/parsed/asm.rs Outdated Show resolved Hide resolved
@pacheco pacheco marked this pull request as ready for review March 12, 2024 18:10
@georgwiese georgwiese self-requested a review March 13, 2024 08:56
Copy link
Collaborator

@georgwiese georgwiese left a comment

Choose a reason for hiding this comment

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

Hmm, I think the approach of completely hiding the selectors from the user introduces some issues:

  • I re-based my memory PR onto this branch and now I have this issue. Basically, I need access to the used flag in the constraints.
  • As mentioned in the comment below, I think the current way to keep selectors constant in the block fails if the latch is in the first row.

I'm not sure what the best abstraction is. My original proposal (just adding the selectors, maybe bit-constrain them, but no other magic) wouldn't have these issues and instead require the user to figure this out for their machine. I wonder if at this point that is the better approach until we know the patterns and figured out an abstraction that is flexible enough to cover all use-cases...

#[should_panic = "Witness generation failed"]
fn permutations_to_block_to_block() {
// TODO: witgen issue
let f = "asm/permutations_block_to_block.asm";
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I understand this right, the issue here is not the block-to-block think (which would be good to test!) But that a machine of block size 1 does not get recognized as a block machine. If the first machine had a block size > 1 it should work! I think it would be good testing this, because it makes sure that the calls to the second machine are masked out in the dummy blocks of the first!

Comment on lines 36 to 41
link 1 ~> bin.or A, B -> X;
link 1 ~> bin.or C, D -> Y;
link 1 ~> bin.or X, Y -> E;

col fixed operation_id = [0]*;
col fixed latch = [1]*;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
link 1 ~> bin.or A, B -> X;
link 1 ~> bin.or C, D -> Y;
link 1 ~> bin.or X, Y -> E;
col fixed operation_id = [0]*;
col fixed latch = [1]*;
link latch ~> bin.or A, B -> X;
link latch ~> bin.or C, D -> Y;
link latch ~> bin.or X, Y -> E;
col fixed operation_id = [0]*;
// TODO: Currently, witgen fails if we set the latch to [1]*, because then the machine
// does not get detected as a block machine and connecting VMs via permutations is not supported
// yet. By setting the latch to [0, 1]*, the first row of every block is completely unconstrained.
col fixed latch = [0, 1]*;

This seems to work!

The problem with the original example is not the block-to-block construction but that this machine does not have a latch (it gets optimized away). Feel free to keep the original file as a failing test, but I think it would be good to test the block-to-block case, because it tests that calls to Binary get masked out in the dummy blocks of Binary4!

Copy link
Collaborator

Choose a reason for hiding this comment

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

Alright then we're hitting other bugs ^^

It works though if we have a strict 1-to-1 mappings (Main has 1 link to Binary4, which has 1 link to Binary). LMK if I should give it a shot to come up with a simple-enough-but not dumb example!

]);
if let Some(latch) = &obj.latch {
obj.pil.push(parse_pil_statement(&format!(
"std::array::map({LINK_SELECTOR_NAME}, |s| std::utils::unchanged_until(s, {latch}));"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, I think this would be a problem if the latch is in the first row instead of the last :/

I think in that case it should be:
std::utils::unchanged_until(s, {latch}')); (note the '!)

Can we detect this? I guess not easily because we haven't evaluated the fixed columns yet?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think Poseidon is a good test case for this. We could eventually change mos of our tests to use permutations instead of lookups.

I tried implementing this suggestion and testing with the Poseidon machine. I ran into more witgen issues which are fixed here: #1157

Copy link
Collaborator Author

@pacheco pacheco Mar 13, 2024

Choose a reason for hiding this comment

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

I think we can't, were still producing pil at this point

for (location, count) in incoming_permutations {
let obj = objects.get_mut(&location).unwrap();
if count > 0 {
// TODO: only block machines actually need a used flag to
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this true? What if a VM calls another machine via a permutation?

Copy link
Collaborator Author

@pacheco pacheco Mar 13, 2024

Choose a reason for hiding this comment

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

The used flag is only used to filter out dummy blocks in the caller, and those (AFAIU) are not an issue in VMs, no?
But in any case, we don't know that here, so we add used anyways.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ah, right, true!

@pacheco
Copy link
Collaborator Author

pacheco commented Mar 13, 2024

For the issue of accessing the selectors, is accessing the used flag directly (i.e., sum of selectors) instead of the selector array enough?
We could do one of the following:

  • a) try a builtin for that

  • b) allow machines to have an optional declaration (selector foo;?), which would be the name for the used flag in place of the default "hidden" name, and thus would be referenceable by machine code.

What do you think? I kinda like the second option

@georgwiese
Copy link
Collaborator

For the issue of accessing the selectors, is accessing the used flag directly (i.e., sum of selectors) instead of the selector array enough?

Not to fix the unchanged_until thing in Poseidon, as discussed above, right? But that would be fixed by giving the user access to the raw selectors and making the user responsible for keeping them constant if needed.

Copy link
Collaborator

@georgwiese georgwiese left a comment

Choose a reason for hiding this comment

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

I just tested this by rebasing my memory PR (#1129) onto it and also trying to change the Poseidon machine so that it can be used via a lookup and permutation (by adding the call_selectors statement).

I ran into witgen issues with the selector[0] (reserved for lookups):

  • If I have the call_selectors statement, lookups compile to a selector of LATCH * selectors[0], in which case witgen doesn't detect the machine during witgen.
  • If the machine is connected only via permutation, we have an extra column selectors[0] which is never used. This also breaks machine detection for the memory machine.

Both of these could be fixed in witgen, but I'm not sure if there is need for this extra selector. It is not needed for the lookup argument, and just to pass a bit that is guaranteed to be 1 for any "real" call, we could also add an argument to the operation, as discussed in the chat.

#[test]
#[should_panic = "has outgoing permutations but doesn't declare call_selectors"]
fn permutation_outgoing_needs_selector() {
// TODO: witgen issue
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// TODO: witgen issue

#[test]
#[should_panic = "has incoming permutations but doesn't declare call_selectors"]
fn permutation_incoming_needs_selector() {
// TODO: witgen issue
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// TODO: witgen issue

instr or_into_C X, Y = bin.or X, Y -> C;

// permutation to machine bin4
instr or4 X, Y, Z, W -> R = bin4.or4;
Copy link
Collaborator

Choose a reason for hiding this comment

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

These are lookups, no?

call_selectors sel;

// permutation links to Binary machine
link 1 ~> bin.or A, B -> X;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
link 1 ~> bin.or A, B -> X;
link array::sum(sel) ~> bin.or A, B -> X;

No? Also, the sum of selectors needs to be constrained to be binary then I think.

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've updated the example. I think the constraint force_bool(sum(sel)) could also be auto inserted by the compiler, but from our discussions I left it up to the user to add it to the asm.

Copy link
Collaborator

@georgwiese georgwiese left a comment

Choose a reason for hiding this comment

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

Great! With this and some other recently merged PRs, the memory machine is now working (#1129)! 🎉

I also tried adding call_selectors sel; to a bunch of machines in the standard library, so now we can use them both via a permutation and lookup. Probably, we should do that in a follow-up and also connect machines via permutations in the RISC-V machine!

As another follow-up, I think we need to update the book, right?

by instructions and links using `~` instead of `=`.
Block machines must declare `call_selectors`.
@georgwiese georgwiese added this pull request to the merge queue Mar 20, 2024
Merged via the queue into main with commit 9fc93a3 Mar 20, 2024
5 checks passed
@georgwiese georgwiese deleted the instr_permutation branch March 20, 2024 12:16
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.

None yet

4 participants