-
Notifications
You must be signed in to change notification settings - Fork 83
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
Gringo considers unnecessary instantiations of rule heads. #499
Comments
The grounder orders body literals according to some heuristic before grounding. It is not guaranteed to be the best order and it is easy to construct something where it fails. Maybe it can be improved... The grounder does not handle equivalences as well as it could. Currently, it is better to write |
Unfortunately, it gets stuck doing quadratic tests on the many variables doing other tests 🥈 |
Thank you both for your answers. Sorry for the confusion in my original post. My issue does not have to do with optimizing the order of the body literals. It is rather than gringo unnecessarily generates unreachable head atoms only to discard them immediately. Consider the following small example. I would expect an instance of the head
The ouput is
The fact that |
But it is all about the body ordering heuristic. Gringo may evaluate body literals and functions appearing in a rule in any order. There are however some ways to enforce orders. For example, introducing an auxiliary predicate:
|
Thank you. Now I understand it better. Your example interests me:
because it illustrates the pragmatics my programs rely on. I have developed in-source testing for my code and would write an
The call to I really never thought about it because it seemed obvious to me. What exactly is the problem with gringo generating "unreachable" atoms? Why do you call them unreachable? |
Erik, see below some examples where the current approach is counter-intuitive to me. The python functions are defined afterwards.
I expect to obtain
I expect to obtain
If the complexity of a slow computation is
Consider this snippet
What should this snippet be grounded to? Here is a correct (but undesirable) grounding. It is undesirable because it contains some rules that are unreachable: we know at grounding time that their body cannot ever be satisfied.
Here is an incorrect grounding. It is incorrect because there is a relevant rule missing (when
In my opinion, of all the correct groundings, the most desirable one is the one that only includes relevant rules. Incidentally, this is the one clingo returns (with
Without going too much into details, one can determine fairly desirable groudings by inductively defining sets of reachable ground atoms. An atom
For example, consider this snippet where I add a rule that cannot trigger.
Because gringo knows that positive(0) is not reachable in any number of inflation steps, the output is the same "most desirable" grounding and, in particular, we do not get a rule with head (One can improve the definition of reachability above to make the set of reachable atoms even smaller, but this is getting out of scope.) Anyway. Consider this snippet
Although 0 is in the Herbrand universe (via number(0), for example), we do not expect any division by zero to take place in the computation of Now, introduce
We still don't have any division by 0 issue, because no substitution that satisfies the body maps You can contrast
The body is the same as for the rule for p2 and there is no substitution that satisfies the body while mapping (There are workarounds, like staging as Roland offers, but they make the code more cumbersome in my applications.) |
You should return an empty list if a function is not defined for its arguments. |
Yes, I use the approach of returning an empty list when the function is not defined for some meaningful arguments, as in the |
We can also think of some way to give the user more control in which order body literals are instantiated (external functions are translated into |
Ok, I understand better why you keep talking about body literals while I refer to the head of a rule. It's because in my example programs, the @call does not occur is not a body literal (so my examples don't require any particular ordering of the body literals in the user program); however the current implementation moves the @call to the body, so after this transformation the counter-intuitive behaviou may occur or not depending on the ordering of the body literals of the intermediate program Although giving more control to users about the order in which the body literals are processed could be a nice addition, it seems that it is much more than I ask: I only ask that the head literal is instantiated after the body literals. (And at the same time, providing the user more control is less than I ask, because it would still require the user to provide redundant information that was already in the source file.) In the following example, the p2 rule does not generate a division by 0 but the q2, r2, and t2 rules might. I think it's normal that gringo does not provide a guarantee for r2 and t2 (for the reasons that you have given, it depends on the ordering of the body literals), but I don't see why q2 cannot come with the same mechanism as p2.
To me, this issue is orthogonal to a user-choice of body literal ordering because there will never be a case where instantiating a head literal before instantiating a body literal is a better order: Take any ordering, adjust it so that the head literal is last and you are guaranteed to get a non-slower (sometimes faster) process. (Assuming there is no crash, of course). As such, instantiating the head literals last should not be seen as a heuristic but as an optimal choice. |
Thank you very much Abdallah for your elaborate explanation. I believe I understand it better now, and if indeed so, I conclude that multiple issues play a role here.
At times while reading your explanation I got the impression that you seem to think that the way it works now is somehow not correct. But that isn't really the case, is it? Often pragmatics of a language are implicitly defined by the implementation and ASP seems no exception. As there are no formal specifications, changing the implementation might solve your problem and create a new one for me. For that reason I support the idea of explicit control of body literals and have it formalised.
I do believe that it is a very important issue for ASP (and any other logic language) that optimisation and heuristics are quite essential in many applications. I haven't had such a problem myself yet, but that might change and at some point it might become necessary to prune expensive calculations. After all, a correct answer, but too late, is not a correct answer. Therefore I support the idea of being able to control unnecessary calls to Python by smart ordering of the literals.
Seeing from this perspective I can't really support this case. I believe it is usually not hard to write better code that does some checking, and that Python code must rather not leave it to the caller if it crashes or not. Then again, I like the idea of having a separate step of calling the Python functions before the grounder starts optimising. @call is the bridge between logic and imperative code, and it might be useful to know a thing or two about when it is called exactly. I use it to create in-source tests/asserts and I'd rather not see it breaking. |
In your program
a small tweak of the scoring heuristic would suffice to ensure that On another note, even the head is part of the join gringo is computing. The complete join is something like
If the head is a fact, the rule is discarded. All its elements are subject to the ordering heuristic. There might be cases where it might be beneficial to do the fact check early. Admittedly not in typical programs. When writing logic programs, especially when using function symbols, arithmetic, or external functions, we have to have some knowledge about the way a program is instantiated to argue that it yields a finite instantiation. However, I would recommend to make as few assumptions as possible. For external functions, I would always check for valid inputs. |
Consider the following program.
The rules producing
value
,value1
, andvalue2
are equivalent, however in the rule forvalue
, gringo attempts to ground the head with substitutions that are inconsistent with the rule body.The output I get is:
I haven't detected any semantic inconsistency/bug, but this behaviour could potentially have a detrimental impact to grounding speed independent of the use of external functions. The running the test file below with
--const test=0
takes 1.5sec on my machine whereas any other value oftest
runs in less than 0.1secThe text was updated successfully, but these errors were encountered: