0

I am attempting to solve some mutually recursive constraints with SWI-Prolog. These constraints are relatively simple, but querying any of these predicates leads to infinite recursion:

%If X is an animal, then X is a bird or a mammal, and vice-versa.
animal(X) :- 
    (mammal(X);bird(X)),
    (male(X);female(X)).

male(X) :- animal(X).
female(X) :- animal(X).

bird(X) :- (X='parrot';X='pigeon'),animal(X).

mammal(X) :- (X='cat';X='dog'),animal(X).

Would it be possible to solve these constraints in Prolog without making them non-recursive?

I wrote a similar program with several base cases, but the query mammal(X),bird(X) still leads to infinite recursion instead of returning false:

%If X is an animal, then X is a bird or a mammal, and vice-versa.
animal(X) :- 
    (mammal(X);bird(X)).

bird('parrot').
bird('pigeon').
bird(X) :- (X='parrot';X='pigeon'),animal(X).

mammal('cat').
mammal('dog').
mammal(X) :- (X='cat';X='dog'),animal(X).
Anderson Green
  • 30,230
  • 67
  • 195
  • 328
  • 2
    You realize that prolog predicates don't return values like functions do, right? So `dif(mammal(X), bird(X))` doesn't do what you probably think it does. In fact, it will *always* succeed since the terms `mammal(X)` and `bird(X)` are always necessarily different for any `X`. As Scott points out in his "answer", you don't have any facts or base cases. – lurker Jul 22 '16 at 20:35
  • @lurker Yes, the the `dif/2` predicates were redundant in this case. I edited the program to correct this problem. – Anderson Green Jul 23 '16 at 19:52
  • They weren't just redundant. They were erroneously used. :p Your existing logic, besides missing base case(s) as Scott points out, is circular. `animal/1` is defined in terms lf `male/1`, `female/1` and `mammal/1`. And `male/1`, `female/1`, and `mammal/1` are defined in terms of `animal/1`. – lurker Jul 23 '16 at 20:03
  • @lurker I updated the question again, and I encountered the same problem even after I added several base cases. – Anderson Green Jul 23 '16 at 20:08
  • 1
    The new logic is *still* circular. `mammal` defined in terms of `animal`, and `animal` defined in terms of `mammal`. – lurker Jul 23 '16 at 20:17

4 Answers4

3

Solving a recursive constraint requires one or more base cases; you have not provided any. The problem isn't with Prolog; its with the problem definition.

Scott Hunter
  • 48,888
  • 12
  • 60
  • 101
  • Of course, it would be possible to re-write these mutually recursive constraints as non-recursive constraints, but my goal is to solve mutually recursive constraints without manually re-writing them. There is an [SMT solver](https://www.cs.kent.ac.uk/pubs/2012/3136/content.pdf) for Prolog that might make this easier. – Anderson Green Jul 22 '16 at 22:01
  • 1
    There is *no* solver of any kind that can "solve" a recursive problem that does not have any base cases. Adding base cases does not make it non-recursive; base cases are *part* of a recursive definition. – Scott Hunter Jul 23 '16 at 00:52
  • Q: "Would it be possible to solve these constraints in Prolog without making them non-recursive?"; I explained why the problem is ill-formed & thus cannot be solved. – Scott Hunter Jul 23 '16 at 16:56
  • @ScottHunter I updated the question again, but adding base cases did not completely solve this problem. – Anderson Green Jul 23 '16 at 20:14
3

It is possible to find solutions for mutually recursive constraints using constraint handling rules.

This is a set of mutually recursive constraints:

%If X is an animal, then X is a bird or a mammal, and vice-versa.
:- use_module(library(chr)).

:- chr_constraint mammal/2,bird/2,animal/1,male/1,female/1,species/2.

animal(X) <=> 
    (mammal(X,Species);bird(X,Species)),
    (male(X);female(X)).

male(X),female(X) ==> false.

bird(X,Species) <=> member(Species,[parrot,pigeon,crow]),species(X,Species).
bird(X,Species) ==> animal(X).

mammal(X,Species) <=> member(Species,[cat,dog,bull]),species(X,Species).
mammal(X,Species) ==> animal(X).

species(X,bull) ==> male(X).

...and this is the output of a query for this program:

?- male(X),mammal(X,Species).
male(_G67406)
species(_G67406,cat)
Species = cat
Anderson Green
  • 30,230
  • 67
  • 195
  • 328
2

I think what you're trying to get at is that you have birds and you have mammals. And you are further trying to establish that a creature is an animal if it is either a bird or a mammal.

The code currently over-specifies, and has circular logic.

Walking through the code...

animal(X) :- 
    (mammal(X); bird(X)).

This says that X is an animal if X is a mammal or X is a bird. So far, so good.

The description of bird reads:

bird('parrot').
bird('pigeon').

These are facts that indicate that a parrot is a bird and a pigeon is a bird. But then there's this rule:

bird(X) :- (X='parrot';X='pigeon'),animal(X).

Which says that X is a bird if X is either a parrot or pigeon, AND if X is an animal. The prior two facts already establish that parrot and pigeon are birds. Why is this rule necessary? And it further adds the condition that X is an animal, which is, in turn, defined in terms of bird and mammal, so is circular.

Similar holds true for the mammal definition. It has the needed facts for mammals:

mammal('cat').
mammal('dog').

And then overspecifies with circular logic:

mammal(X) :- (X='cat';X='dog'), animal(X).

The essence of what you need is simply:

bird('parrot').
bird('pigeon').

mammal('cat').
mammal('dog').

animal(X) :- 
    mammal(X); bird(X).

This logic defines what creatures are birds or mammals using facts, then provides a rule that says if a creature is known to be a bird or a mammal, then it's an animal.

lurker
  • 56,987
  • 9
  • 69
  • 103
  • This solution works in this case, but my goal is to solve mutually recursive constraints without manually rewriting them in a non-recursive form. In order to do this, I might need to use a [forward-chaining](https://en.wikipedia.org/wiki/Forward_chaining) inference engine like [CHR](https://en.wikipedia.org/wiki/Constraint_Handling_Rules). – Anderson Green Jul 24 '16 at 05:12
2

One solution to such problems is to simply enable your Prolog system's tabling mechanism.

For example, in SWI-Prolog (latest development version), if I simply add the following directives at the start of your program:

:- use_module(library(tabling)).

:- table animal/1.

Then I get for example:

?- animal(X).
false.

?- male(X).
false.

?- bird(X).
false.

So, in these cases, we still do not find any solution, but at least we get answers.

mat
  • 40,498
  • 3
  • 51
  • 78