Making a predicate true as default and functional

I have a rule that checks whether a thing is big enough:

big_enough(X) :- size(X,Y), Y #> 2.

If the size of a thing a is not specified, I want the answer to the query ?- big_enough(a). to be a model containing size(a, Y | {Y #> 2}) for some variable Y. Therefore, I want that everything has every size until it is stated to have a specific size. Then, it should have this size and no other. Here is my attempt:

size(X,Y) :- has_size(X,Y). 
size(X,Y) :- not -size(X,Y). 
-size(X,Y1) :- has_size(X,Y2), Y1 \= Y2. 

In order to avoid ending up in a loop, the “stating a thing to have a specific size” is done indirectly through the predicate has_size.

Querying (in ciao) ?- big_enough(a). gives the model

{ big_enough(a),  size(a,Var0 | {Var0 #> 2}),  not -size(a,Var0 | {Var0 #> 2}),  not -size(Var1,Var2) }.

It contains size(a,Var0 | {Var0 #> 2}) as expected. I don’t understand why it contains not -size(Var1,Var2).

When the size of “a” is specified through the fact

size(a,3).

I expect the answer to the query ?- big_enough(a). to be exactly one model. However, I get two nearly identical models:

{ big_enough(a),  size(a,3),  has_size(a,3),  not -size(Var0 | {Var0 \= a},Var1),  not has_size(Var0 | {Var0 \= a},Var2),  not -size(a,3),  not has_size(a,Var3 | {Var3 \= 3}),  -size(a,Var4 | {Var4 \= 3}),  not size(a,Var4 | {Var4 \= 3}),  not has_size(a,Var4 | {Var4 \= 3}) }

{ big_enough(a),  size(a,3),  not -size(a,3),  not has_size(a,Var0 | {Var0 \= 3}),  has_size(a,3),  not -size(Var1 | {Var1 \= a},Var2),  not has_size(Var1 | {Var1 \= a},Var3),  not has_size(a,Var4 | {Var4 \= 3}),  -size(a,Var5 | {Var5 \= 3}),  not size(a,Var5 | {Var5 \= 3}),  not has_size(a,Var5 | {Var5 \= 3}) }

Can someone help me understand this behaviour? I would also appreciate tips on better ways to do this.

Thanks in advance!

Hi mars,
I can’t figure out really in what sort of computations you’re stuck, but…the only meaningful thing that comes to my mind is that usually (in natural languages) determining the meaning of adjectives like “big”, “tall”, “small”, etc. is not so straightforward, because it depends a lot on the comparison class you choose (or that you have in mind, more or less consciously, talking about natural languages). And I guess that also determining if something is “big enough” depends a lot on what one aims at.
Best regards

Size is a property of a thing. So just use:

big_enough(thing(_Name, Size)) :-
    freeze(Size, Size > 2).

Results:

?- big_enough(thing(elephant, Size)), Size = 3.
Size = 3.

?- big_enough(thing(mouse, Size)), Size = 1.
false.

?- big_enough(thing(alien, Unknown)).
freeze(Unknown, Unknown>2).

The background is that I’m trying to formalize a regulation that lists the requirements cars must fulfill in order to drive on different kinds of roads. I want to be able to query in all directions: Both “Is this car allowed to drive on this road?” and “What sizes of cars are allowed on this road?”. In the latter case, the idea is to make the size-predicate true for all sizes, so that we get a model with a set expression that corresponds to the requirement. But in the former case, when the size is given, the size-predicate should be false for every other size, so that we get no models when the requirements are not fulfilled.

That looks interesting, thank you! I didn’t know about freeze.

This started as s(CASP) problem. Yes, possibly normal Prolog, possibly extended with tabling and/or constraints could be an alternative. You’ll have to make up your mind though. s(CASP) implements Stable Model Semantics, while pure Prolog only has negation-as-failure or, using tabling, Well Founded Semantics. If negation is important to you, you need to consider this. If you don’t need s(CASP) for its reasoning about negation, pure Prolog is probably better.

1 Like

Thank you for this comment! I’m quite new to all of this and still pondering about the implications of using s(CASP) vs pure Prolog. According to my current understanding, s(CASP) fits my use case best. So I’m still looking for a way to solve the original problem.

It’s my fault, I admit. But a) the phrasing was somewhat ambiguous and b) one shouldn’t underestimate the positive side: there were a couple of things I wanted to ask about XPCE, but I found a way to tackle them without asking … I hope it is appreciated :slight_smile:

What makes you think so? s(CASP) is (roughly) plain Horn clauses with inequality constraints and basic numeric constraints. It handles negation using stable model semantics, which implies that it finds (possibly) multiple “models” that are consistent with the data (program). It’s combination of negation as failure and classical negation allows it to express 5 levels of certainty. It can provide an justification for its answers.

That all sounds great, but as usual, there are some "but"s. The scalability is poor. In part that is due to the current implementation, but in part it is more fundamental. It deals with negation as constructive negation, i.e., not p(X) finds all X for which we cannot proof p(X) is true. This easily blows up the computation. In my experience it can be really hard to express really simple things. Your case is an example. This is easily written down in Prolog, but I do not know the s(CASP) solution. That may of course well be due to my lack of experience :slight_smile:

FWIW that sounds like a case that can leverage abduction, e.g.:

:- module(foobar, [big_enough/1]).

:- use_module(library(scasp)).

:- abducible maybe_has_size(X, S).

big_enough(X) :- size(X, S), S #>= 2.

size(X, S) :- has_size(X, S).
size(X, S) :- not has_size(X), maybe_has_size(X, S).

has_size(X) :- has_size(X, _).

has_size(b, 1).
has_size(c, 3).

Now we can ask:

?- module(foobar).
true.

?- ? big_enough(a).  % unspecified size
% s(CASP) model
{ big_enough(a),         not has_size(a,_),     maybe_has_size(a,_A),
  not has_size(a),       maybe_has_size(_,_),   size(a,_A)
},
{_A>=2} ;
false.

?- ? big_enough(b).  % specified size, not big enough
false.

?- ? big_enough(c).  % specified size, big enough
% s(CASP) model
{ big_enough(c),        maybe_has_size(_,_),
  has_size(c,3),        size(c,3)
} ;
false.

You were initially talking about going off into infinity, i.e. anything > 2. If you’re now talking about e.g. 5 sizes (classifications), that’s far easier to manage and reason about.

Are there other relevant classifications? E.g. number of car doors, emissions level, fuel type, crash safety rating.

For reasoning, such classifications are very convenient :grinning:

Thank you, I think this is what I was looking for!

Do I understand correctly that :- abducible maybe_has_size(X, S). is a shorthand for the following even loop?

maybe_has_size(X,S) :- not other_world.
other_world :- not maybe_has_size(X,S).

So we have either maybe_has_size(X, S), hence maybe_has_size(X, S) is true for every value of X and S or not maybe_has_size(X, S), hence we do not know about any values of X and S where maybe_has_size(X, S) is true?