Small readability refactor in `opt_solver`, no behavioral change.
## Rename
- `m_models` → `m_objective_models`
- `m_last_model` → `m_model`
## Rationale
`m_models` is the per-objective vector of witnessing models, indexed by
the objective index `i` exactly like `m_objective_vars`,
`m_objective_values`, and `m_objective_terms`. It was the only member of
that parallel family not following the `m_objective_*` naming, so
`m_objective_models` makes the intent self-documenting.
`m_last_model` is the single, transient model most recently obtained
from the context and handed back to callers via `get_model_core`.
Renaming it to `m_model` reads more naturally, and the two now-distinct
names (`m_model` vs `m_objective_models`) avoid the previous one-letter
`m_model`/`m_models` clash hazard.
Both are private members of `opt_solver`, so the change is fully
self-contained within `opt_solver.{h,cpp}`. A stale TRACE label ("last
model") was updated to "current model" to match.
## Testing
- Builds clean (CMake/Ninja, Release).
- `test-z3 /a`: 92 passed, 0 failed.
- Spot-checked optimization behavior (single-objective minimize with
large `distinct`, box mode) — unchanged.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Push/pop isolation in maximize_objectives1 (added for #7677) can corrupt
LP column values between objectives. For non-linear objectives like mod,
the LP maximize call may return stale values after a preceding
objective's push/pop cycle.
Fix: save the baseline model before the push/pop loop and use it as a
floor for each objective's value. Extract two helpers:
- maximize_objective_isolated: push/pop + save/restore per objective
- update_from_baseline_model: adopt baseline model value when it is better
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add API solve_for(vars).
It takes a list of variables and returns a triangular solved form for the variables.
Currently for arithmetic. The solved form is a list with elements of the form (var, term, guard).
Variables solved in the tail of the list do not occur before in the list.
For example it can return a solution [(x, z, True), (y, x + z, True)] because first x was solved to be z,
then y was solved to be x + z which is the same as 2z.
Add congruent_explain that retuns an explanation for congruent terms.
Terms congruent in the final state after calling SimpleSolver().check() can be queried for
an explanation, i.e., a list of literals that collectively entail the equality under congruence closure.
The literals are asserted in the final state of search.
Adjust smt_context cancellation for the smt.qi.max_instantiations parameter.
It gets checked when qi-queue elements are consumed.
Prior it was checked on insertion time, which didn't allow for processing as many
instantations as there were in the queue. Moreover, it would not cancel the solver.
So it would keep adding instantations to the queue when it was full / depleted the
configuration limit.
This update includes an experimental feature to access a congruence closure data-structure after search.
It comes with several caveats as pre-processing is free to eliminate terms. It is therefore necessary to use a solver that does not eliminate the terms you want to track for congruence of. This is partially addressed by using SimpleSolver or incremental mode solving.
```python
from z3 import *
s = SimpleSolver()
x, y, z = Ints('x y z')
s.add(x == y)
s.add(y == z)
s.check()
print(s.root(x), s.root(y), s.root(z))
print(s.next(x), s.next(y), s.next(z))
```