Skip to content

Comments

Nonlin LA preds#790

Open
BritikovKI wants to merge 5 commits intomasterfrom
nonlin-la-preds
Open

Nonlin LA preds#790
BritikovKI wants to merge 5 commits intomasterfrom
nonlin-la-preds

Conversation

@BritikovKI
Copy link
Member

@BritikovKI BritikovKI commented Oct 29, 2024

Allows to create nonlin functions

Removed all of the constraints for the creation of nonlin predicates inside of the functions.
Only the assertions are checked for nonlinearity

@BritikovKI BritikovKI force-pushed the nonlin-la-preds branch 8 times, most recently from d89fbf6 to b1f40d9 Compare October 31, 2024 11:43
@BritikovKI BritikovKI marked this pull request as ready for review October 31, 2024 15:09
@aehyvari
Copy link
Member

aehyvari commented Nov 4, 2024

The code fails with this input

(set-logic QF_LIA)
(define-fun uninterp_mul ((a Int) (b Int)) Int (mod a b))
(assert (= (uninterp_mul 1 2) 0))
(check-sat)
(exit)

Here's the output:

(error "Divisor must be constant in linear logic")

(error "define-fun returns an unknown sort")

(error "Unknown symbol `uninterp_mul Int Int'")

(error "assertion returns an unknown sort")

sat

@aehyvari
Copy link
Member

aehyvari commented Nov 4, 2024

For our limited form of axioms this might actually be just enough. One thing is missing before I can put this to our production: To be sure that we're running the correct version, I'd need to have the version information from opensmt. See issue #800 .

@BritikovKI BritikovKI force-pushed the nonlin-la-preds branch 3 times, most recently from 46d12d9 to 8acd08a Compare November 4, 2024 10:25
@BritikovKI BritikovKI linked an issue Nov 4, 2024 that may be closed by this pull request
@BritikovKI BritikovKI force-pushed the nonlin-la-preds branch 11 times, most recently from 2810c10 to 32f3743 Compare November 5, 2024 13:51
@BritikovKI
Copy link
Member Author

BritikovKI commented Nov 5, 2024

Ok, I think generally PR is over

Though, we definitely need some sort of error handling, LANonLinearException produces errors with different format under different complilers rn. Should it be a different issue though?

@BritikovKI BritikovKI force-pushed the nonlin-la-preds branch 5 times, most recently from 7e441a2 to adaea42 Compare November 29, 2024 16:59
@BritikovKI BritikovKI requested a review from blishko December 2, 2024 14:44
Copy link
Member

@blishko blishko left a comment

Choose a reason for hiding this comment

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

Some more comments.

Comment on lines 418 to 420
assert(logic.isPlus(polyTerm) || logic.isTimesNonlin(polyTerm));
for (PTRef factor : logic.getPterm(polyTerm)) {
auto [var, c] = logic.splitTermToVarAndConst(factor);
auto [var, c] = logic.splitPolyTerm(factor);
Copy link
Member

Choose a reason for hiding this comment

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

Nonlinear multiplication should be handled as a factor, not as a sum, no? I think this is not correct.

Copy link
Member Author

Choose a reason for hiding this comment

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

In this case it is the same though.. Operations like
x1 * x2 * x3
and
x1 + x2 + x3
Are split in the same manner, elements of the arithmetic operation extracted one by one and analyzed separately

Copy link
Member Author

Choose a reason for hiding this comment

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

Or maybe I don't fully understand what toPoly function does, but from what I get it splits operation into terms

Copy link
Member

Choose a reason for hiding this comment

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

Can @blishko comment on this? I am not following up on the issue at the moment.

Copy link
Member

Choose a reason for hiding this comment

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

I am also confused right now. How is the term like5 * x1 * x2 gonna be represented?
One nonlinear multiplication with three children, or like 5 * (x1*x2) so the top-level multiplication is linear and the second one is nonlinear?

assert(logic.isNumVar(v) || (laVarMapper.isNegated(v) && logic.isNumVar(logic.mkNeg(v))));
auto [v,c] = logic.splitPolyTerm(expr);
x = getLAVar_single(v);
assert(logic.isNumVar(v) || (laVarMapper.isNegated(v) && logic.isNumVar(logic.mkNeg(v))));
Copy link
Member

Choose a reason for hiding this comment

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

Why has the assert moved? Just keep it in the original place, no?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was moved because getLAVar_single contains check for nonlin multiplication and will throw exception if it is detected. Otherwise assertion will fail because v is nonlin multiplication and is not in the scope of assertion...

I can either change assertion disjunction or add handling of the nonlin multiplication before the assertion, but those are both non-perfect solutions either... Tell me which one you prefer!

Copy link
Member

Choose a reason for hiding this comment

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

I do not understand. Since expr must be either NumVar or TimesLin, how can splitPolyTerm(expr) yield TimesNonLin? I would expect that getLAVar_single never throws here. I do not even consider it necessary to add assert(not isTimesNonLin(v)) because I really do not see a rationale for how it could appear here unless I am missing something.

Copy link
Member Author

Choose a reason for hiding this comment

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

Problem is that at this point expr can be potentially timesNonlin, since we don't have NonLinArith and send potentially Nonlin queries into the LASolver (we can truly understand if it is Lin or Nonlin only after simplifications).
splitPolyTerm is general call, that works both for linear and nonlinear terms...

Copy link
Member

Choose a reason for hiding this comment

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

We are talking about an if block guarded by the condition if (logic.isNumVar(expr) || logic.isTimesLin(expr)), hence why I assume that it is impossible that expr is TimesNonlin.

Copy link
Member Author

Choose a reason for hiding this comment

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

It is due to our representation of formulas

basically 2xy is (TimesLin 2 (TimesNonlin x y))
Therefore this check doesn't check that there is no nonlin multiplication in the formula

Copy link
Member

Choose a reason for hiding this comment

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

OK. Then I would add an additional assert before getLAVar_single that considers nonlin as well, to 1) make it more under control and 2) to document that this is the expected behavior.

@BritikovKI BritikovKI force-pushed the nonlin-la-preds branch 3 times, most recently from c974f2b to 469252e Compare February 27, 2025 13:25
@Tomaqa Tomaqa self-requested a review March 17, 2025 10:01
@@ -91,9 +92,7 @@ void LASolver::isProperLeq(PTRef tr)
assert(logic.isLeq(tr));
auto [cons, sum] = logic.leqToConstantAndTerm(tr);
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
auto [cons, sum] = logic.leqToConstantAndTerm(tr);
[[maybe_unused]] auto const [cons, sum] = logic.leqToConstantAndTerm(tr);

Copy link
Member

Choose a reason for hiding this comment

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

I would also change the name of the function to assertIsProperLeq with [[maybe_unused]] PTRef tr

Copy link
Member Author

Choose a reason for hiding this comment

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

Should we address it in this PR? Because it already seems to be grown out of scope significantly, and refactoring [cons,sum] is not connected directly to the NonLin support...

assert(!logic.isTimes(sum) || ((logic.isNumVar(logic.getPterm(sum)[0]) && logic.isOne(logic.mkNeg(logic.getPterm(sum)[1]))) ||
(logic.isNumVar(logic.getPterm(sum)[1]) && logic.isOne(logic.mkNeg(logic.getPterm(sum)[0])))));
assert(logic.isPlus(sum) or logic.isTimesLin(sum) or logic.isMonomial(sum));
(void) cons; (void)sum;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
(void) cons; (void)sum;

Copy link
Member Author

Choose a reason for hiding this comment

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

Same question as above, I can do it in separate PR


LVRef LASolver::getLAVar_single(PTRef expr_in) {

if (logic.isTimesNonlin(expr_in)) { throw NonLinException(logic.pp(expr_in)); }
Copy link
Member

Choose a reason for hiding this comment

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

Can we do a function for this? Like logic.checkIsNotNonlin. Then, for example, changing the error message would be necessary to update in just one place.

Copy link
Member Author

@BritikovKI BritikovKI Dec 9, 2025

Choose a reason for hiding this comment

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

Do you mean logic.checkIsNotNonlin would be the only function to throw NonLinException, right?
(Because otherwise it wouldn't bring much benefit...)
However, it would mean making it void, because if/else branch wouldn't make much sense and just calling it within a function to check if it will throw exception or not
I'm not sure how efficient this architectural change would be 🤔

@Tomaqa Tomaqa self-requested a review October 24, 2025 15:24
Comment on lines +42 to +43
SymRef SymStore::newSymbImpl(char const * fname, SRef rsort, vec<SRef> const & args, SymbolConfig const & symConfig,
bool isInternal) {
Copy link
Member

Choose a reason for hiding this comment

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

After I studied a bit Symbol.h, I believe we should integrate an internal flag into SymbolConfig (e.g. similarly to isInterpreted) instead of this isInternal parameter. This would be compatible with the existing code, allowing to use it also with declareFun etc. Connected to this, I think we should remove SymConf and instead allow graceful construction of SymbolConfig, possibly also adding some getters. For example, I see that noScoping implies isInterpreted (I dunno why, though).

Copy link
Member

Choose a reason for hiding this comment

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

That part of the code is very much historical, to put it nicely. noScoping would probably mean that the symbol is a reserved word. that could well be the reason why it's isInterpreted. I'm not convinced that the choices I made when introducing those fields were very informed. I encourage you to look at them with a critical eye.

Copy link
Member Author

Choose a reason for hiding this comment

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

@Tomaqa do you think it should be done within the scope of this PR or separately?
Bc it will require slight amount of changes in SymbolConfig

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.

Opensmt rejects non-linear integer arithmetic too eagerly

4 participants