potassco / clingo

🤔 A grounder and solver for logic programs.
https://potassco.org/clingo
MIT License
589 stars 79 forks source link

Multiple conditions in the head of a condition literal #504

Closed cabralpinto closed 4 weeks ago

cabralpinto commented 1 month ago

I'm struggling to understand why the following code gives me 'F'' is unsafe.

progression(O, F, S, R) :- operation(O, F, S, R); F != F', F != S' : operation(_, F', S', R'), R' > R.

It seems that clingo is interpreting F != F' as if it is outside of the conditional literal. Is it not possible to have multiple conditions in the head of a condition literal? Indeed, the documentation seems to imply that it isn't. But then, why does running the following code not give me the same error?

neq(X, Y) :- X != Y, variable(X), variable(Y).
progression(O, F, S, R) :- operation(O, F, S, R); neq(F, F'), neq(F, S') : operation(_, F', S', R'), R' > R.
ejgroene commented 1 month ago

If you have a variable in the head, or even outside the conditional literal set expression, it takes precedence over the variables in the conditional literal. It bothered me for some time and I believe it is bothering you now.

On Tue, Jun 11, 2024 at 18:21, João Cabral Pinto @.***(mailto:On Tue, Jun 11, 2024 at 18:21, João Cabral Pinto < wrote:

I'm struggling to understand why the following code gives me 'F'' is unsafe.

progression(O, F, S, R) :- operation(O, F, S, R); F != F', F != S' : operation(_, F', S', R'), R' > R.

It seems that clingo is interpreting F != F' as if it is outside of the conditional literal. Is it not possible to have multiple conditions in the head of a condition literal? Indeed, the documentation seems to imply that it doesn't. But then, why does running the following code not give me the same error?

neq(X, Y) :- X != Y, variable(X), variable(Y). progression(O, F, S, R) :- operation(O, F, S, R); neq(F, F'), neq(F, S') : operation(_, F', S', R'), R' > R.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>

ejgroene commented 1 month ago

Remark 3.6 from the guide:

During grounding, the instantiation of global variables takes precedence over non-global ones, that is, the former are instantiated before the latter. As a consequence, variables that occur globally are substituted by terms before a condition is further evaluated. Hence, the names of variables in conditions must be chosen with care, making sure that they do not accidentally match the names of global variables.

On Tue, Jun 11, 2024 at 18:34, Mail @.***(mailto:On Tue, Jun 11, 2024 at 18:34, Mail < wrote:

If you have a variable in the head, or even outside the conditional literal set expression, it takes precedence over the variables in the conditional literal. It bothered me for some time and I believe it is bothering you now.

On Tue, Jun 11, 2024 at 18:21, João Cabral Pinto @.***(mailto:On Tue, Jun 11, 2024 at 18:21, João Cabral Pinto < wrote:

I'm struggling to understand why the following code gives me 'F'' is unsafe.

progression(O, F, S, R) :- operation(O, F, S, R); F != F', F != S' : operation(_, F', S', R'), R' > R.

It seems that clingo is interpreting F != F' as if it is outside of the conditional literal. Is it not possible to have multiple conditions in the head of a condition literal? Indeed, the documentation seems to imply that it doesn't. But then, why does running the following code not give me the same error?

neq(X, Y) :- X != Y, variable(X), variable(Y). progression(O, F, S, R) :- operation(O, F, S, R); neq(F, F'), neq(F, S') : operation(_, F', S', R'), R' > R.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>

cabralpinto commented 1 month ago

@ejgroene Thanks for your response! But I still don't understand how to fix the issue. What would you do?

ejgroene commented 1 month ago

The first thing I would do is rewrite it so it becomes less ambiguous. To me it is unclear, without looking at the guide, what belongs to the conditional literal and what not.

On Tue, Jun 11, 2024 at 18:40, João Cabral Pinto @.***(mailto:On Tue, Jun 11, 2024 at 18:40, João Cabral Pinto < wrote:

@.***(https://github.com/ejgroene) Thanks for your response! But I still don't understand how to fix the issue. What would you do?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.***>

rkaminsk commented 1 month ago

We cannot help you here without knowing what the program is supposed to mean. If you are beginning to write ASP, I would suggest to completely ignore conditional literals and write your program without.

rkaminsk commented 1 month ago

To get started, you could check out our guide or the book Answer Set Programming by Vladimir Lifschitz. There is also a free draft available.

cabralpinto commented 1 month ago

Hey @rkaminsk, I am quite new but I've already read much of the guide and I'm still unable to fix the issue without resorting to a verbose solution.

My objective with the excerpt I sent is as follows. Given a set of arithmetic operations, such as the one below, I want to create a corresponding progression predicate for every operation that has none of its operands used again in the operations that follow (tested with R' > R).

operation("-", "A", "B", "C"). % A - B = C
operation("+", "C", "D", "E"). % C + D = E
operation("+", "C", "F", "G"). % C + F = G

In this example, I'd want my code to output progression("-", "A", "B", "C"). and progression("+", "C", "F", "G"). The second operation doesn't get a progression predicate because C is used again in the following operation. The full code I wrote for this is as follows (before I had shown you a simplified version only to showcase the error):

progression(O, F, S, R) :-
  operation(O, F, S, R);
  F != F', F != S', S != F', S != S' : operation(_, F', S', R'), R' > R.

I can fix the error by splitting the conditions into multiple condition literals (seen below), but this results in a lot of code repetition. Isn't there a cleaner option?

progression(O, F, S, R) :- 
  operation(O, F, S, R);
  F != F' : operation(_, F', S', R'), R' > R;
  F != S' : operation(_, F', S', R'), R' > R;
  S != F' : operation(_, F', S', R'), R' > R;
  S != S' : operation(_, F', S', R'), R' > R.
rkaminsk commented 1 month ago

What about the following?

operation("-", "A", "B", "C"). % A - B = C
operation("+", "C", "D", "E"). % C + D = E
operation("+", "C", "F", "G"). % C + F = G

arg(X,R) :- operation(_,X,_,R).
arg(X,R) :- operation(_,_,X,R).

progression(O, F, S, R) :- operation(O, F, S, R); 
    #count { R' : arg(F;R';S;R'), R' > R } == 0
cabralpinto commented 1 month ago

Good idea! Thank you.

However, as a noobie, I can't help but wonder why clingo was designed without some of the constructs that are commonplace in most languages, like parenthesis to take precedence or the "or" operator. I feel like I spend most of my time finding ways to go around these missing features. Could you provide some insight on why it is the way it is? Is there maybe any plan to add these things in the future?

rkaminsk commented 1 month ago

However, as a noobie, I can't help but wonder why clingo was designed without some of the constructs that are commonplace in most languages, like parenthesis to take precedence or the "or" operator. I feel like I spend most of my time finding ways to go around these missing features. Could you provide some insight on why it is the way it is? Is there maybe any plan to add these things in the future?

In my experience, one does not need nested Boolean connectives for modeling most tasks. It is possible to encode a lot of problems mainly using normal rules. One design goal of the language has always been to keep the language as simple as possible but expressive (in sense of being able to write compact encodings). It is normal that it takes some time to develop a feeling for encoding problems. It might take a little longer to come up with good ASP code but in the end you get something compact and maybe even nicely readable.

You can even encode your problem just with normal rules:

arg(X,R) :- operation(_,X,_,R).
arg(X,R) :- operation(_,_,X,R).

covered(R) :- arg(X,R), arg(X,R'), R'>R.

progression(O,F,S,R) :- operation(O,F,S,R), not covered(R).
cabralpinto commented 1 month ago

I ended up managing to code it with just one rule! I just adapted your first idea a little:

#count { R' : operation(_, F', S', R'), (F; S) = (F'; S'), R' > R } = 0.

Thanks for your input. What I would argue is that adding parenthesis (to define precedence) and the or operator would result in more expressive code without adding any significant complexity or readability issues for the end user. I hope you consider it!