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

Add helper traits for constructing Value::known constants #699

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

str4d
Copy link
Contributor

@str4d str4d commented Dec 1, 2022

Part of #698.

Value::known(Assigned::Zero)
FieldValue::ZERO
Copy link
Contributor Author

Choose a reason for hiding this comment

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

In cases like this, the helper constant is a clear win.

sign: Value::known(pallas::Base::one()),
sign: FieldValue::ONE,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similarly here (though the length difference could be smaller as pallas::Base::ONE is now also valid).

magnitude_error: Value::known(pallas::Base::from(1 << 1)),
},
// -2^64
MyCircuit {
magnitude: Value::known(pallas::Base::from_u128(1 << 64)),
sign: Value::known(-pallas::Base::one()),
sign: -Value::<pallas::Base>::ONE,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

However, situations like this require type annotations (to know which impl Neg to use), so you save a bit of length overall but it's still roughly as complex (if not moreso).

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 adding the need of the explicit type requirement makes it worse TBH. Although -1 is also one of the "magic numbers" usually. But I kinda agree that should not have it's own constant..

Copy link
Contributor

Choose a reason for hiding this comment

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

Why not (have its own constant)? IIRC, -1 was the most common constant in the Orchard circuit.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes I was remembering correctly: #698 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why not (have its own constant)?

For -1 specifically it would help:

let a = -Value::<pallas::Base>::ONE;
let a = FieldValue::MINUS_ONE;

But the point still applies generally.

@@ -979,7 +978,7 @@ mod tests {
config.q.enable(&mut region, 1)?;

// Assign a = 0.
region.assign_advice(|| "a", config.a, 0, || Value::known(Fp::ZERO))?;
region.assign_advice(|| "a", config.a, 0, || Value::<Fp>::ZERO)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Similarly here we cannot avoid the type annotation because of how flexibly the Region::assign_advice API is (intentionally) set up, so we only save a few characters.

let mut digest = [BlockWord(Value::known(0)); DIGEST_SIZE];
let mut digest = [BlockWord(IntegerValue::ZERO); DIGEST_SIZE];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here the result is actually longer, as for primitive integers the type can often be elided.

@CPerezz
Copy link
Contributor

CPerezz commented Dec 1, 2022

Will continue the discussion of #698 here.

Thanks @str4d for taking initiative on this. The issues are clear. And they obviously prevent us to go on the direction this PR is IMO..

For some reason I was thinking that maybe GATs would allow us to do something like:

pub trait Value<T> {
    type Inner: Option<T>;
}

EDIT: It's Associated Type Defaults what would solve the issue of constraining more the inner type.

Then we can define the same exact implementations for Value generically and add a few extra if needed.
I understand this would bring issues anyway as then it's up to the implementor to implement Value on a correct way. But at least was giving a lot of expressiveness. And you are also "enforcing the implementor" to consider the inner item of Value as something that might be unknown or None etc...

There are times where we need to decompose a Value into bytes. And it's simply a nightmare. When instead we could have a From implementation or some similar thing that returned the [Value<F>; 32]. Same thing for bit representations. That also happens for some XOR/Bitwise ops etc... SHR, SHL....

Traitify the Value is the only thing that looks like would give enough freedom to add things like the ones I suggested on the issue or in this comment. But it's concerning to delegate the implementation of things like these and, also, to provide a set of associated methods that always makes sense.

I think anyway, that right now, Value is really tied to Field/FieldExt struct implementors. But if we traitify it, we could extend it to elliptic curve points for example or other stuff.

WDYT? Does the trait approach sound like a possibility to you?

Base automatically changed from fieldext-finale to main December 5, 2022 19:27
@str4d
Copy link
Contributor Author

str4d commented Dec 17, 2022

We should keep discussion in this PR around the API that is proposed here. Proposals for new alternatives should be in the main issue, otherwise keeping track of the discussion will get unwieldy and slow down (as people may only be tracking that issue, not the PRs).

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