128 Commits

Author SHA1 Message Date
chriseth
ab3a22edaa Remove JournalingConstraintSystem. (#3305)
The file was not referenced anywhere.
2025-09-22 19:20:08 +00:00
chriseth
fad77555ad Reduce visibility. (#3295)
This PR changes `pub(crate)` visibility of the fields of
`GroupedExpression` back to `non-pub` as it was before the extraction of
the solving routines. The solving routines do not really need write
access to those fields and read-access is obtained using `components()`.
2025-09-20 17:58:29 +00:00
chriseth
e6af1b3448 Remove bit decomposition. (#3243)
Removes the bit decomposition solver inside GroupedExpression /
AlgebraicConstraint.

Its functionality is now mostly taken over by the constraint splitter.
This is not fully true: The constraint splitter only works for offsets
that are constant, but this should not be a hindrance for our current
use-cases. If we need it, it should not be too difficult to extend it,
we only need to extend the final part of the `find_solution` function,
where it checks if the solution is unique.

---------

Co-authored-by: Thibaut Schaeffer <schaeffer.thibaut@gmail.com>
2025-09-16 09:17:42 +00:00
chriseth
d7d9c7b497 Split algebraic constraint modulo constant (#2751)
Splits constraints into multiple independent ones based on the following
idea:

Suppose we have the constraint `x + k * y + c = 0` with `x` and `y`
being variables (or expressions containing variables) and `k` and `c`
are constants. Furthermore, the range constraints of `x` and `y` are
such that no wrapping occurs in the operations, i.e. the constraint is
equivalent to the same constraint on the integers (where field elements
larger than half the modulus are mapped to negative numbers).

Then the same constraint is also true modulo `k`, where we get `x % k +
c % k = 0`. If this equation has a unique solution `s` in the range
constraints for `x`, we get a new constraint `x - s = 0`. We can
subtract that constraint from the original to get `k * y + c - s = 0`
and iterate.

Closes https://github.com/powdr-labs/powdr/issues/3108

---------

Co-authored-by: Thibaut Schaeffer <schaeffer.thibaut@gmail.com>
2025-09-15 15:31:43 +00:00
chriseth
0e1d9d73dd System splitter. (#3249) 2025-09-09 15:11:29 +00:00
chriseth
7675b99e34 Adjust reachability (#3250)
Include blocking variables in the returned set of reachable variables.

This is needed in https://github.com/powdr-labs/powdr/pull/3046
2025-09-05 19:13:43 +00:00
Thibaut Schaeffer
f95c0087c4 Take variables by reference in constraints_referencing_variables (#3252)
Got curious after #3248
2025-09-05 11:03:07 +00:00
chriseth
ba882efe81 Move reachability and use IndexedConstraintSystem (#3248)
This moves the reachability analysis module from the autoprecompiles
crate to the constraint solver create. I will need it in that crate in
an upcoming PR and it also does not have any autoprecompiles-specific
code.

I also changed the code to use `IndexedConstraintSystem` instead of
`ConstraintSystem`.
2025-09-05 10:02:37 +00:00
chriseth
38b8406c78 Some refactoring of AlgebraicConstraint. (#3242)
Co-authored-by: Thibaut Schaeffer <schaeffer.thibaut@gmail.com>
2025-09-03 09:37:12 +00:00
chriseth
cfc7cf31e2 Derive clone for ConstraintRef (#3241)
Derives `Clone` (and some other traits) for `ConstraintRef`. Since it is
a reference, it should implement Clone.

Note that `Clone` for `AlgebraicConstraint` is only auto-derived if `V`
is `Clone`, which is the case if `V` is a reference.
2025-09-01 17:11:46 +00:00
Thibaut Schaeffer
396737e749 Remove one level of nested calls in the optimizer (#3218)
In order to make the optimizer clearer, this PR tries to put all
optimization passes one after another in `optimize_constraints`.

Also removes `JournalingConstraintSystem`.

TODO:
- [x] Figure out what to do about the `JournalingConstraintSystem`
2025-09-01 13:41:38 +00:00
Thibaut Schaeffer
f32fd0c81a Make the solver work over constraints not expressions (#3220)
- remove `Deref` for `AlgebraicConstraint` so we cannot call functions
that expect `&GroupedExpression`
- make constraint solver work over constraints instead of expressions
- move algebraic expression to its own module, along with said solver
- make more components like the boolean extractor work on
`AlgebraicConstraint<&E>`
2025-09-01 13:03:21 +00:00
chriseth
e65df60cfe Document is_simple_equivalence. (#3224) 2025-08-26 14:14:44 +00:00
chriseth
91bb74b192 Use RuntimeConstant instead of FieldElement. (#3225) 2025-08-26 09:52:04 +00:00
Thibaut Schaeffer
3cc2de7e0d Introduce AlgebraicConstraint type in solver (#3215)
I got confused before about what is an expression and what is a
constraint.
For example, in boolean extraction, we return an expression which I
thought was the boolean. But it turns out the expression is the new
constraint.

Similarly, there are some things that we can do for constraints which
wouldn't work for expression: for example, `2 * x == 0` can be replaced
by `x == 0`. But `2 * x` as an expression cannot be replaced by `x`.

This PR introduces a wrapper and uses it where possible.

Some caveats:
- This PR implements `Deref` and `DerefMut` for the constraint, which
means that we can call any function which takes a reference to an
expression by passing a constraint. This partially defeats the purpose
of the change, as we can by mistake call a function which would only be
meant for expressions. This is fixed in #3220
- This PR keeps the `solve` functions as before, where they run on
expressions, not constraints. This does not make sense in my opinion, as
solving should be for constraints only, however here's an example which
would break if we made this change: if we are solving the constraint `a
* b = 0` we want to solve the constraint `a = 0`. This would require
creating a constraint out of `a` on the fly, which is not implemented in
this PR. This is fixed in #3220
2025-08-25 19:08:44 +00:00
chriseth
2748ac0579 Inline only at end (#3173)
Fixes https://github.com/powdr-labs/powdr/issues/3113

---------

Co-authored-by: Georg Wiese <georgwiese@gmail.com>
Co-authored-by: schaeff <thibaut@powdrlabs.com>
2025-08-25 15:41:02 +00:00
chriseth
4adeccd914 Remove duplicate factors. (#3205)
This new optimizer step removes duplicate factors in a single algebraic
constraint.

It was detected by the assertion that is now commented, which assumed
that if all factors of one constraint also appear in another constraint
then the number of factors is also less or equal the number of factors
in the other constraint.

By running the duplicate factor removal right before that other step
this assumption gets true.
2025-08-22 13:09:22 +00:00
chriseth
1ecfbf5484 Booleanize changed constraints. (#3208)
The boolean extractor currently handles newly incoming algebraic
constraints. Substitutions that are performed during solving can lead to
constraints being boolean-extractable where we were not able to do
boolean extraction before. This PR tries to perform boolean extraction
on all constraints that potentially changed because of a substitution.

---------

Co-authored-by: schaeff <thibaut@powdrlabs.com>
2025-08-21 15:01:45 +00:00
chriseth
b5aa6759b4 Avoid running boolean extraction twice. (#3200)
In a future PR (#3208) we will try to run boolean extraction on any
newly changed constraint. This can have the risk that we perform boolean
extraction on a constraint where we already have run boolean extraction.
This PR stores a map of substitutions we performed to avoid introducing
a new boolean variable that then just turns out to be equivalent to an
already existing one.

Closes 3202

---------

Co-authored-by: schaeff <thibaut@powdrlabs.com>
2025-08-21 13:56:15 +00:00
chriseth
8701951b47 Collapse solvers (#3199)
The layered trait-based solver structure does provide a clean separation
but if we want to implement #3106 we have to call the boolean extractor
component from the innermost component which is quite difficult to
achieve. Also, most of the layers were just a simple pass-through for
most of the functions.

So this PR collapses the boolean extractor, linearizer and base solver
into one solver. It still keeps the VarTransform layer because it is
convenient to keep the type generic.
2025-08-20 16:27:57 +00:00
chriseth
5e83a8a93a Do not use bus field variables. (#3171)
Removes the special introduction and removal of bus variables because
the solver can now handle this on its own through the `LinearizedSolver`
component. It also has the benefit that we do not need to run the
optimizer twice any more (once with bus interaction variables and once
without).

Previously we would introduce a new variable type outside of the solver
and replace all bus interactions `bus_interaction(1, x + y * z)` by
`bus_interaction(1, bus_var_1)` and the algebraic constraint `bus_var_1
= x + y * z`.

Now the `LinearizedSolver` is a component inside the solver that
replaces all bus interaction fields (that are not already constants or
simple variables) and also replaces all non-linear components:

`bus_interaction(1, x + y * z)`

becomes

```
bus_interaction(1, v1)
v1 = x + v2
v2 = y * z
```

The reason behind also replacing non-linear parts is because range
constraints are only stored for variables, not for all sub-expressions.
Also if we find the same non-linear sub-expression multiple times, we
can now replace it directly by the same variable.

---------

Co-authored-by: Georg Wiese <georgwiese@gmail.com>
2025-08-20 14:20:15 +00:00
chriseth
f4c99197f9 Introduce range constraint size. (#3177)
Introduce the concept of `size` for range constraint. It provides an
upper bound for the number of values in the rang econstraint and can be
different from the range width.
2025-08-18 07:07:05 +00:00
chriseth
5d0d97a269 Linearizing solver. (#3140)
Adds a solver that introduces new variables for every non-linear
component in an algebraic constraint and also for every bus interaction
field that is not a constant or a variable already.

---------

Co-authored-by: Georg Wiese <georgwiese@gmail.com>
2025-08-15 13:21:11 +00:00
Georg Wiese
bdf3a5fefa Low-degree bus interaction optimizer (#3130)
This PR adds a new optimizer: If a stateless bus interaction (a.k.a.
lookup) implements a low-degree function, it is replaced by a polynomial
constraint (and range constraints).

Some examples include:
- `xor(a, 0xff)` (logical `not` on bytes): This is a linear function
(`255 - a`)
- `xor(a, b)`, where `a` and `b` are known to not have the same bits
set: This equals `a + b`
- `xor(a, b)`, where `a` and `b` are known to be bit-valued: This is
equal to `a + b - 2 * a * b`

Doing this replacement has several advantages:
- A bus interaction is replaced with a polynomial constraint (which is
usually much cheaper)
- Because the output is equal to a low-degree expression of the inputs
(often linear), the inliner can inline it, so we don't have to commit to
it

Note that in addition to the linear function, a lookup always *also*
enforces a range constraint. These are added to the system by using the
API added with the range constraint optimizer (#3151). In many cases,
these constraints are redundant and can be removed again by the range
constraint optimizer (e.g. the inputs might come from memory, which can
be assumed to store byte-constrained words.

---------

Co-authored-by: Leo <leo@powdrlabs.com>
Co-authored-by: chriseth <chris@ethereum.org>
2025-08-15 10:47:55 +00:00
chriseth
32fc857c57 Optimize exhaustive search. (#3181)
Optimize exhaustive search by directly applying assignments and doing it
in the solver, so further exhaustive searches profit from the new
information.

- Initial time for solver optimization in `test_optimize` (release):
 4.0 seconds
- Directly apply assignments to the variable sets and constraint system
 2.6 seconds
- Directly apply assignments inside the solver (i.e. also update the
range constraints)
 2.57 seconds
2025-08-14 22:01:51 +00:00
chriseth
9e08aa9016 Introduce var transformation. (#3146)
This introduces a single solver layer that does the variable
transformations. This way the trait bounds are much easier to state.
2025-08-11 15:48:30 +00:00
chriseth
92e3a79f10 Add test for bit decomposition bug (#3157)
This is not a proper fix, but we would like to replace the routine by a
better one soon anyway.
2025-08-11 10:16:19 +00:00
Georg Wiese
71d821c427 Range constraint optimizer (#3151)
Fixes #3128

---------

Co-authored-by: Leo <leo@powdrlabs.com>
2025-08-08 14:05:05 +00:00
chriseth
356ceba690 Use solver directly for memory (#3111)
Co-authored-by: Georg Wiese <georgwiese@gmail.com>
2025-08-05 19:04:18 +00:00
chriseth
24ddce250d Prefer inlining last variable. (#3136) 2025-08-05 19:03:23 +00:00
chriseth
ea3e10d686 Move solvers into their own files. (#3145)
This just moves code without modification.
2025-08-05 17:01:58 +00:00
chriseth
648b308e9c Remove leftover code relating to bus field assignments. (#3144)
Closes #2980
2025-08-05 16:18:53 +00:00
chriseth
4544897d58 Boolean extracted solver (#3094)
Extracts boolean variables as alternative constraints for all
constraints in the solver.

The exact implementation adds a layer of the Solver trait on top of an
existing solver where we try to extract a boolean variable for all
constraints that are added to the solver.

The interface is also simplified a little in that we always start out
with an "empty" solver and use the `add_algebraic_constraints` and
`add_bus_interaction` functions to fill it with the system.
2025-08-01 08:21:20 +00:00
chriseth
5166625cf5 Do not extract variables that are already boolean. (#3112)
Fixes https://github.com/powdr-labs/powdr/issues/3095
2025-07-31 16:00:06 +00:00
chriseth
7485dbf829 Improve boolean extraction (#3107)
Co-authored-by: Georg Wiese <georgwiese@gmail.com>
2025-07-31 15:04:46 +00:00
Georg Wiese
5c8ecd2a46 Remove free variables (#3098)
This PR adds a new optimizer step:

5568270f77/autoprecompiles/src/constraint_optimizer.rs (L96-L108)

This can be useful to remove "left-over" range constraints, e.g. from
removed memory bus interactions:
- Imagine a memory bus interaction receiving a previous time stamp and
then having a range constraint like `[current_timestamp - prev_timestamp
- 1] in [BIT16]` to enforce that it is smaller than the current
timestamp.
- Then, `prev_timestamp` would only be mentioned in this stateless bus
interaction after the memory bus interaction is removed by the memory
optimizer.
- The `remove_disconnected_columns` step would not remove it, because
`current_timestamp` is also mentioned and it is connected to a stateful
bus interaction.
- The constraint is still redundant: For any value of
`current_timestamp`, the prover could pick a satisfying value for
`current_timestamp - prev_timestamp - 1` (e.g. $0$) and solve for
`prev_timestamp`

---------

Co-authored-by: Thibaut Schaeffer <schaeffer.thibaut@gmail.com>
2025-07-30 15:03:39 +00:00
chriseth
ef97650646 Use negative numbers in range constraints. (#3115) 2025-07-30 08:53:22 +00:00
chriseth
581a9916fe Group into btreeset directly (#3105)
Co-authored-by: Georg Wiese <georgwiese@gmail.com>
2025-07-29 13:58:45 +00:00
Thibaut Schaeffer
4700c1815f Rename extract_boolean to try_extract_boolean (#3110)
It can fail and returns an Option, so `try_` seems appropriate.
2025-07-29 12:26:23 +00:00
Georg Wiese
92429bf364 Variable Occurrences: Use BTreeSet (#3102)
This way, we guarantee that there are no duplicates, which can cause
performance issues.

Although, it seems like on the OpenVM Keccak test case (#3104), there
are not many duplicates and performance decreases slightly:
```
$ cargo bench --bench optimizer_benchmark
Benchmarking optimize-keccak/optimize: Warming up for 3.0000 s
Warning: Unable to complete 10 samples in 5.0s. You may wish to increase target time to 45.8s.
optimize-keccak/optimize
                        time:   [4.5420 s 4.5712 s 4.6035 s]
                        change: [+3.5320% +4.2324% +5.0959%] (p = 0.00 < 0.05)
                        Performance has regressed.
```
2025-07-29 09:33:27 +00:00
chriseth
448debf109 Solver queue (#3092)
Implements a queue over constraint system items such that items are
re-queued when their variables are updated.

---------

Co-authored-by: Georg Wiese <georgwiese@gmail.com>
2025-07-29 07:26:23 +00:00
chriseth
0d43b411b3 Turn solver into trait. (#3087)
Turns `Solver` into a trait to abstract away the bus interaction handler
and also allow other implementations in the future.

It also makes the solver persistent across optimization loops so that we
do not have to derive the same range constraints all over again.

This is also a preparatory PR to make the solver more independent from
the constraint system: The constraint system inside the solver will have
way more redundancies in the future.

---------

Co-authored-by: Georg Wiese <georgwiese@gmail.com>
2025-07-28 14:18:02 +00:00
chriseth
5aaa375a70 Move derive_more... to exhaustive search module. (#3090)
It is not really an intrinsic task of the constraint system to derive
assignments.
2025-07-26 10:55:15 +00:00
chriseth
362d3f9fdb Remove redundant constraints. (#3074)
Removes algebraic constraints that are implied by other algebraic
constraints.

In order to do that, grouped expressions are turned into products of
factors such that these factors are as much normalized as possible. Then
we try to find algebraic constraints that are divisors of other
algebraic constraints (i.e. the factors are subsets).
2025-07-26 10:54:15 +00:00
chriseth
c79e4c17f0 Substitute bus vars at start and end. (#3078) 2025-07-25 11:08:22 +00:00
chriseth
993a1ac81b Avoid quadratic terms that sum to zero. (#3084) 2025-07-25 09:25:23 +00:00
chriseth
9a97fca7c6 Take other bus interactions into account when computing xor range constraints (#3012) 2025-07-22 09:32:46 +00:00
chriseth
ad9c6c0b3e Also return determined range constraints. (#3068)
Sometimes we want to run an optimization that depends on range
constraints. Using the solver is the best way to determine range
constraints, so it should also return them.
2025-07-21 18:59:07 +00:00
chriseth
57ed5b9bba Detect unsatisfiable first. (#3047) 2025-07-15 13:26:25 +00:00
chriseth
8d11daa952 Rename effect. (#3029) 2025-07-10 08:13:34 +00:00