Convert FOPL expression to prolog - prolog

Given premises and corresponding FOPL. Goal: Prove that Marcus hated Caesar.
Marcus was a Pompeian
pompeian(Marcus)
All Pompeians were Romans.
Vx: pompeian(x) --> roman(x)
All Romans were either loyal to Caesar or hated him.
Vx: roman(x) --> loyalto(x, Caesar) v hate(x, Caesar)
Everyone is loyal to someone.
Vx Ey: loyalto(x, y)
People only try to assassinate rulers they aren't loyal to.
Vx Vy: ~loyalto(x, y) --> trytoassassinate(x, y)
Marcus tried to assassinate Caesar.
trytoassassinate(Marcus, Caesar)
How can I implement each of the premises one by one using SWI Prolog?
I tried to write prolog and code and attached my implementation below, I am having difficulty implementing rule number 4.
roman(X):-
pompeians(X).
hates(X, caesar):-
roman(X), not_loyal(X, caesar).
not_loyal(X,Y):-
not(loyal(X,Y)).
% How to implement "Everyone is loyal to someone."?
not_loyal(X,Y):-
assassinate(X,Y).
assassinate(marcus, caesar).
pompeians(marcus).

How about:
% 1
pompeian(marcus).
% 2
roman(P) :-
pompeian(P).
% 3
roman_loyalty_ceasar_feeling(caesar, loyal).
roman_loyalty_ceasar_feeling(notcaesar, hatred).
roman_loyalty_feeling(P, LoyalTo, CaesarFeel) :-
roman(P),
roman_loyalty_ceasar_feeling(LoyalTo, CaesarFeel). % 3
person(P, LoyalTo, CaesarFeel) :-
roman_loyalty_feeling(P, LoyalTo, CaesarFeel), % 4
\+ try_assassinate(P, LoyalTo). % 5
% 6
try_assassinate(marcus, caesar).
Result in swi-prolog:
?- person(Person, LoyalTo, CaesarFeel).
Person = marcus,
LoyalTo = notcaesar,
CaesarFeel = hatred.

Related

Tool to solve propositional logic / boolean expressions (SAT Solver?)

I am new to the topic of propositional logic and boolean expressions. So this is why I need help. Here is my problem:
In the car industry you have thousand of different variants of components available to choose from when you buy a car. Not every component is combinable, so for each car there exist a lot of rules that are expressed in propositional logic. In my case each car has between 2000 and 4000 rules.
They look like this:
A → B ∨ C ∨ D
C → ¬F
F ∧ G → D
...
where "∧" = "and" / "∨" = "or" / "¬" = "not" / "→" = "implication".
The variables A, B, C, ... are linked to the components in the bill of material. The data I have consists of pairs of components with their linked variables.
Example:
Component_1, Component_2: (A) ∧ (B)
Component_1, Component_3: (A) ∧ (C ∨ F)
Component_3, Component_5: (B ∨ G)
...
Now, my question is how to solve this problem. Specifically, I would like to know if each combination of the components is possible according to rules above.
Which tool, software and algorithm can solve these type of problems?
Is there a illustrative example?
How can I automate it, so I can check each combination in my list?
Generally, what should I search for in Google to deepen my knowledge in this topic?
Thank you very much for your help!
Olaf
You might want to try a Prolog system with a SAT Solver, such as SWI-Prolog, Jekejeke Minlog, etc... you can readily play with it in the REPL of the Prolog system. To load the SAT solver just type (you don't need to type the ?- itself):
/* in SWI-Prolog */
?- use_module(library(clpb)).
/* in Jekejeke Minlog */
?- use_module(library(finite/clpb)).
You can then use the top-level to search for solutions of a boolean formula, like this example here an xor:
?- sat(X#Y), labeling([X,Y]).
X = 0,
Y = 1 ;
X = 1,
Y = 0.
Here is an example of a kitchen planner code. The kitchen has 3 places,
and we assign a freezer and a stove. The freezer is not allowed to be near to the stove.
The freezer has code 0,1 and the stove has code 1,0. We make use of the card constraint to place the freezer and the stove.
:- use_module(library(finite/clpb)).
freezer([X,Y|L],[(~X)*Y|R]) :-
freezer(L, R).
freezer([], []).
stove([X,Y|L],[X*(~Y)|R]) :-
stove(L, R).
stove([], []).
free([X,Y|L],[(~X)*(~Y)|R]) :-
free(L, R).
free([], []).
allowed([X,Y,Z,T|L]) :-
sat(~((~X)*Y*Z*(~T))),
sat(~(X*(~Y)*(~Z)*T)),
allowed([Z,T|L]).
allowed([_,_]).
allowed([]).
kitchen(L) :-
freezer(L, F), card(1, F),
stove(L, G), card(1, G),
free(L, H), card(1, H),
allowed(L).
What I want to demonstrate with the Prolog code is the benefit, that problem encoding towards a SAT formulation can be done via Prolog code itself. When the above code is run I get the following result as expected:
?- L=[_,_,_,_,_,_], kitchen(L), labeling(L).
L = [0,1,0,0,1,0] ;
L = [1,0,0,0,0,1] ;
No

Prolog: simulate disjunctive facts

I've got a logic problem that I'd like to solve, so I thought, "I know, I'll try Prolog!"
Unfortunately, I'm running into a brick wall almost immediately. One of the assumptions involved is a disjunctive fact; either A, B or C is true (or more than one), but I do not know which. I've since learned that this is something Prolog does not support.
There's a lot of documentation out there that seems to address the subject, but most of it seems to immediately involve more intricate concepts and solves more advanced problems. What I'm looking for is an isolated way to simulate defining the above fact (as defining it straight away is, by limitations of Prolog, not possible).
How could I address this? Can I wrap it in a rule somehow?
EDIT: I realise I have not been very clear. Given my lack of familiarity with Prolog, I did not want to get caught up in a syntax error when trying to convey the problem, and instead went with natural language. I guess that did not work out, so I'll give it a shot in pseudo-Prolog anyway.
Intuitively, what I would want to do would be something like this, to declare that either foo(a), foo(b) or foo(c) holds, but I do not know which:
foo(a); foo(b); foo(c).
Then I would expect the following result:
?- foo(a); foo(b); foo(c).
true
Unfortunately, the fact I'm trying to declare (namely foo(x) holds for at least one x \in {a, b, c}) cannot be defined as such. Specifically, it results in No permission to modify static procedure '(;)/2'.
Side-note: after declaring the disjunctive fact, the result of ?- foo(a). would be a bit unclear to me from a logical perspective; it is clearly not true, but false does not cover it either -- Prolog simply does not have sufficient information to answer that query in this case.
EDIT 2: Here's more context to make it more of a real-world scenario, as I might have over-simplified and lost details in translation.
Say there are three people involved. Alice, Bob and Charlie. Bob holds two cards out of the set {1, 2, 3, 4}. Alice asks him questions, in response to which he shows her one card that Charlie does not see, or shows no cards. In case more cards are applicable, Bob shows just one of them. Charlie's task is to learn what cards Bob is holding. As one might expect, Charlie is an automated system.
Alice asks Bob "Do you have a 1 or a 2?", in response to which Bob shows Alice a card. Charlie now learns that Bob owns a 1 or a 2.
Alice then asks "Do you have a 2 or a 3", to which Bob has no cards to show. Clearly, Bob had a 1, which he showed Alice previously. Charlie should now be able to derive this, based on these two facts.
What I'm trying to model is the knowledge that Bob owns a 1 or a 2 (own(Bob, 1) \/ own(Bob, 2)), and that Bob does not own a 2 or a 3 (not (own(Bob, 2) \/ own(Bob, 3))). Querying if Bob owns a 1 should now be true; Charlie can derive this.
The straight-forward answer to your question:
if you can model your problem with constraint logic programming over finite domains, then, an "exclusive or" can be implemented using #\ as follows:
Of the three variables X, Y, Z, exactly one can be in the domain 1..3.
D = 1..3, X in D #\ Y in D #\ Z in D
To generalize this, you can write:
disj(D, V, V in D #\ Rest, Rest).
vars_domain_disj([V|Vs], D, Disj) :-
foldl(disj(D), Vs, Disj, V in D #\ Disj).
and use it as:
?- vars_domain_disj([X,Y,Z], 2 \/ 4 \/ 42, D).
D = (Y in 2\/4\/42#\ (Z in 2\/4\/42#\ (X in 2\/4\/42#\D))).
If you don't use CLP(FD), for example you can't find a nice mapping between your problem and integers, you can do something else. Say your variables are in a list List, and any of them, but exactly one, can be foo, and the rest cannot be foo, you can say:
?- select(foo, [A,B,C], Rest), maplist(dif(foo), Rest).
A = foo,
Rest = [B, C],
dif(B, foo),
dif(C, foo) ;
B = foo,
Rest = [A, C],
dif(A, foo),
dif(C, foo) ;
C = foo,
Rest = [A, B],
dif(A, foo),
dif(B, foo) ;
false.
The query reads: in the list [A,B,C], one of the variables can be foo, then the rest must be different from foo. You can see the three possible solutions to that query.
Original answer
It is, sadly, often claimed that Prolog does not support one thing or another; usually, this is not true.
Your question is not exactly clear at the moment, but say you mean that, with this program:
foo(a).
foo(b).
foo(c).
You get the following answer to the query:
?- foo(X).
X = a ;
X = b ;
X = c.
Which you probably interpreted as:
foo(a) is true, and foo(b) is true, and foo(c) is true.
But, if I understand your question, you want a rule which says, for example:
exactly one of foo(a), foo(b), and foo(c) can be true.
However, depending on the context, that it, the rest of your program and your query, the original solution can mean exactly that!
But you really need to be more specific in your question, because the solution will depend on it.
Edit after edited question
Here is a solution to that particular problem using constraint programming over finite domains with the great library(clpfd) by Markus Triska, available in SWI-Prolog.
Here is the full code:
:- use_module(library(clpfd)).
cards(Domain, Holds, QAs) :-
all_distinct(Holds),
Holds ins Domain,
maplist(qa_constraint(Holds), QAs).
qa_constraint(Vs, D-no) :-
maplist(not_in(D), Vs).
qa_constraint([V|Vs], D-yes) :-
foldl(disj(D), Vs, Disj, V in D #\ Disj).
not_in(D, V) :- #\ V in D.
disj(D, V, V in D #\ Rest, Rest).
And two example queries:
?- cards(1..4, [X,Y], [1 \/ 2 - yes, 2 \/ 3 - no]), X #= 1.
X = 1,
Y = 4 ;
false.
If the set of cards is {1,2,3,4}, and Bob is holding two cards, and when Alice asked "do you have 1 or 2" he said "yes", and when she asked "do you have 2 or 3" he said no, then: can Charlie know if Bob is holding a 1?
To which the answer is:
Yes, and if Bob is holding a 1, the other card is 4; there are no further possible solutions.
Or:
?- cards(1..4, [X,Y], [1 \/ 2 - yes, 2 \/ 3 - no]), X #= 3.
false.
Same as above, can Charlie know if Bob is holding a 3?
Charlie knows for sure that Bob is not holding a three!
What does it all mean?
:- use_module(library(clpfd)).
Makes the library available.
cards(Domain, Holds, QAs) :-
all_distinct(Holds),
Holds ins Domain,
maplist(qa_constraint(Holds), QAs).
This defines the rule we can query from the top level. The first argument must be a valid domain: in your case, it will be 1..4 that states that cards are in the set {1,2,3,4}. The second argument is a list of variables, each representing one of the cards that Bob is holding. The last is a list of "questions" and "answers", each in the format Domain-Answer, so that 1\/2-yes means "To the question, do you hold 1 or 2, the answer is 'yes'".
Then, we say that all cards that Bob holds are distinct, and each of them is one of the set, and then we map each of the question-answer pairs to the cards.
qa_constraint(Vs, D-no) :-
maplist(not_in(D), Vs).
qa_constraint([V|Vs], D-yes) :-
foldl(disj(D), Vs, Disj, V in D #\ Disj).
The "no" answer is easy: just say that for each of the cards Bob is holding, it is not in the provided domain: #\ V in D.
not_in(D, V) :- #\ V in D.
The "yes" answer means that we need an exclusive or for all cards Bob is holding; 2\/3-yes should result in "Either the first card is 2 or 3, or the second card is 2 or 3, but not both!"
disj(D, V, V in D #\ Rest, Rest).
To understand the last one, try:
?- foldl(disj(2\/3), [A,B], Rest, C in 2\/3 #\ Rest).
Rest = (A in 2\/3#\ (B in 2\/3#\ (C in 2\/3#\Rest))).
A generate-and-test solution in vanilla Prolog:
card(1). card(2). card(3). card(4).
owns(bob, oneof, [1,2]). % i.e., at least one of
owns(bob, not, 2).
owns(bob, not, 3).
hand(bob, Hand) :-
% bob has two distinct cards:
card(X), card(Y), X < Y, Hand = [X, Y],
% if there is a "oneof" constraint, check it:
(owns(bob, oneof, S) -> (member(A,S), member(A, Hand)) ; true),
% check all the "not" constraints:
((owns(bob, not, Card), member(Card,Hand)) -> false; true).
Transcript using the above:
$ swipl
['disjunctions.pl'].
% disjunctions.pl compiled 0.00 sec, 9 clauses
true.
?- hand(bob,Hand).
Hand = [1, 4] ;
;
false.
Note that Prolog is Turing complete, so generally speaking, when someone says "it can't be done in Prolog" they usually mean something like "it involves some extra work".
Just for the sake of it, here is a small program:
card(1). card(2). card(3). card(4). % and so on
holds_some_of([1,2]). % and so on
holds_none_of([2,3]). % and so on
holds_card(C) :-
card(C),
holds_none_of(Ns),
\+ member(C, Ns).
I have omitted who owns what and such. I have not normalized holds_some_of/1 and holds_none_of/1 on purpose.
This is actually enough for the following queries:
?- holds_card(X).
X = 1 ;
X = 4.
?- holds_card(1).
true.
?- holds_card(2).
false.
?- holds_card(3).
false.
?- holds_card(4).
true.
which comes to show that you don't even need the knowledge that Bob is holding 1 or 2. By the way, while trying to code this, I noticed the following ambiguity, from the original problem statement:
Alice asks Bob "Do you have a 1 or a 2?", in response to which Bob shows Alice a card. Charlie now learns that Bob owns a 1 or a 2.
Does that now mean that Bob has exactly one of 1 and 2, or that he could be holding either one or both of the cards?
PS
The small program above can actually be reduced to the following query:
?- member(C, [1,2,3,4]), \+ member(C, [2,3]).
C = 1 ;
C = 4.
(Eep, I just realized this is 6 years old, but maybe it's interesting to introduce logic-programming languages with probabilistic choices for the next stumbler )
I would say the accepted answer is the most correct, but if one is interested in probabilities, a PLP language such as problog might be interesting:
This example assumes we don't know how many cards bob holds. It can be modified for a fixed number of cards without much difficulty.
card(C):- between(1,5,C). % wlog: A world with 5 cards
% Assumption: We don't know how many cards bob owns. Adapting to a fixed number of cards isn't hard either
0.5::own(bob, C):-
card(C).
pos :- (own(bob,1); own(bob,2)).
neg :- (own(bob,2); own(bob,3)).
evidence(pos). % tells problog pos is true.
evidence(\+neg). % tells problog neg is not true.
query(own(bob,Z)).
Try it online: https://dtai.cs.kuleuven.be/problog/editor.html#task=prob&hash=5f28ffe6d59cae0421bb58bc892a5eb1
Although the semantics of problog are a bit harder to pick-up than prolog, I find this approach an interesting way of expressing the problem. The computation is also harder, but that's not necessarily something the user has to worry about.

Strange warning and computation result in constraint logic program

First, sorry for posting the whole program, but as I don't know were the problem is I don't know which parts are irrelevant. These are two slightly different implementations of the same logic puzzle in SWI-Prolog, the first one succeeds the second one fails and I can't find the reason for the failure.
The puzzle:
4 persons are having a diner:
Donna, Doreen, David, Danny
the woman (Donna,Doreen) are sitting vis-a-vis.
the men (David,Danny) are sitting vis-a-vis.
Each of them picked a unique meal and beverage.
1) Doreen sits next to the person that ordered risotto.
2) the salad came with a coke.
3) the person with the lasagna sits vis-a-vis the person with the milk.
4) david never drinks coffee.
5) donna only drinks water.
6) danny had no appetite for risotto.
who ordered the pizza?
I choose the following approach
table with positions:
1
4 O 2
3
domain: positions{1,2,3,4}
variables: persons, meals, beverages
First the inefficient succeeding implementation:
solution(Pizza, Doreen, Donna, David, Danny) :-
% assignment of unique positions to the variables
unique(Doreen,Donna,David,Danny),
unique(Lasagna,Pizza,Risotto,Salad),
unique(Water,Coke,Coffee,Milk),
% general setting
vis_a_vis(Donna,Doreen),
vis_a_vis(David,Danny),
% the six constraints
next_to(Doreen,Risotto),
Salad = Coke,
vis_a_vis(Lasagna,Milk),
\+ David = Coffee,
Donna = Water,
\+ Danny = Risotto.
unique(X1,X2,X3,X4) :-
pos(X1),
pos(X2),
\+ X1 = X2,
pos(X3),
\+ X1 = X3, \+ X2 = X3,
pos(X4),
\+ X1 = X4, \+ X2 = X4, \+ X3 = X4.
right(1,2).
right(2,3).
right(3,4).
right(4,1).
vis_a_vis(1,3).
vis_a_vis(3,1).
vis_a_vis(2,4).
vis_a_vis(4,2).
next_to(X,Y) :- right(X,Y).
next_to(X,Y) :- right(Y,X).
pos(1).
pos(2).
pos(3).
pos(4).
This works and gives the right result. But when I try to reorder the clauses of the solution procedure to be more efficient (this is the second implementation)
solution(Pizza, Doreen, Donna, David, Danny) :-
% general setting
vis_a_vis(Donna,Doreen),
vis_a_vis(David,Danny),
% the six constraints
Salad = Coke,
vis_a_vis(Lasagna,Milk),
\+ David = Coffee,
Donna = Water,
\+ Danny = Risotto,
% assignment of unique positions to the variables
unique(Doreen,Donna,David,Danny),
unique(Lasagna,Pizza,Risotto,Salad),
unique(Water,Coke,Coffee,Milk).
%% all other predicates are like the ones in the first implementation
I get a unassigned variable warning when trying to load the file:
Warning: /home/pizza.pl:28:
Singleton variable in \+: Coffee
and the computation returns false. But shouldn't it return the same result?
I see no reason for the difference...
the warning is due to the fact that Coffe and Risotto are unbound when the negation is executed. If you replace \+ David = Coffee, by David \= Coffee, you will avoid the warning, but the solution cannot will not be computed. Should be clear indeed that since Coffee is unbound, David \= Coffee will always fail. You can use dif/2, the solution will work and will be more efficient. I've named solution1/2 your first snippet, and solution2/5 this one (using dif/2):
solution2(Pizza, Doreen, Donna, David, Danny) :-
% general setting
vis_a_vis(Donna,Doreen),
vis_a_vis(David,Danny),
% the six constraints
next_to(Doreen,Risotto), % note: you forgot this one
Salad = Coke,
vis_a_vis(Lasagna,Milk),
dif(David, Coffee),
Donna = Water,
dif(Danny, Risotto),
% assignment of unique positions to the variables
unique(Doreen,Donna,David,Danny),
unique(Lasagna,Pizza,Risotto,Salad),
unique(Water,Coke,Coffee,Milk).
a small test:
?- time(aggregate_all(count,solution1(P,A,B,C,D),N)).
% 380,475 inferences, 0.058 CPU in 0.058 seconds (100% CPU, 6564298 Lips)
N = 8.
?- time(aggregate_all(count,solution2(P,A,B,C,D),N)).
% 10,626 inferences, 0.002 CPU in 0.002 seconds (100% CPU, 4738996 Lips)
N = 8.

How to remove duplicate facts in Prolog

I am writing a rule in Prolog to create a fact, pit(x,y). This rule below is called three times from my main function, and it is inserting three pits in which none of them is at (1,1) or (1,2) or (2,1) but the problem is that sometimes 2 pits have the same x and y where x and y can be from 1 to 4 only. (4x4 grid)
placePit(_) :- Px is random(4)+1,
Py is random(4)+1,
write(Px),
write(' '),
writeln(Py),
(Px =\= 1;
Py =\= 1),
(Px =\= 1;
Py =\= 2),
(Px =\= 2;
Py =\= 1)
->
pit(Px,Py);
placePit(4).
I don't want this to happen, so I write another rule to check whether 2 pits are the same first and will extend later to REMOVE EITHER ONE from the database. From what I have tested, it doesn't get fired at all even though 2 pits appear to be the same. What am I doing wrong? How to remove duplicate facts?
pit(A,B) :- pit(C,D),
A = C,
B = D,
write('Duplicate').
PS. I am very new at Prolog. Any suggestion is appreciated.
maybe this could help, in assumption you're actually required to generate facts:
:- dynamic(pit/2).
pit(1,1).
pit(1,2).
pit(2,1).
placePit(N) :-
N > 0,
Px is random(4)+1,
Py is random(4)+1,
( \+ pit(Px, Py) % if not exist
-> assertz(pit(Px, Py)), % store
M is N-1 % generate another
; M = N % nothing changed, retry
),
placePit(M). % recursion is the proper Prolog way to do cycles
placePit(0). % end of recursion (we call it 'base case')
you should call as
?- placePit(3).
It shows a bit of syntactic detail, like the 'if/then/else', that in Prolog has a peculiar form.
edit When done, you could remove unwanted pit/2, to get your db 'clean'.
?- maplist(retract, [pit(1,1),pit(1,2),pit(2,1)]).
(note that I assumed - based on your description - that a DB stored pit/2 was of value for further processing).

Convert peano number s(N) to integer in Prolog

I came across this natural number evaluation of logical numbers in a tutorial and it's been giving me some headache:
natural_number(0).
natural_number(s(N)) :- natural_number(N).
The rule roughly states that: if N is 0 it's natural, if not we try to send the contents of s/1 back recursively to the rule until the content is 0, then it's a natural number if not then it's not.
So I tested the above logic implementation, thought to myself, well this works if I want to represent s(0) as 1 and s(s(0)) as 2, but I´d like to be able to convert s(0) to 1 instead.
I´ve thought of the base rule:
sToInt(0,0). %sToInt(X,Y) Where X=s(N) and Y=integer of X
So here is my question: How can I convert s(0) to 1 and s(s(0)) to 2?
Has been answered
Edit: I modified the base rule in the implementation which the answer I accepted pointed me towards:
decode(0,0). %was orignally decode(z,0).
decode(s(N),D):- decode(N,E), D is E +1.
encode(0,0). %was orignally encode(0,z).
encode(D,s(N)):- D > 0, E is D-1, encode(E,N).
So I can now use it like I wanted to, thanks everyone!
Here is another solution that works "both ways" using library(clpfd) of SWI, YAP, or SICStus
:- use_module(library(clpfd)).
natsx_int(0, 0).
natsx_int(s(N), I1) :-
I1 #> 0,
I2 #= I1 - 1,
natsx_int(N, I2).
No problemo with meta-predicate nest_right/4 in tandem with
Prolog lambdas!
:- use_module(library(lambda)).
:- use_module(library(clpfd)).
:- meta_predicate nest_right(2,?,?,?).
nest_right(P_2,N,X0,X) :-
zcompare(Op,N,0),
ord_nest_right_(Op,P_2,N,X0,X).
:- meta_predicate ord_nest_right_(?,2,?,?,?).
ord_nest_right_(=,_,_,X,X).
ord_nest_right_(>,P_2,N,X0,X2) :-
N0 #= N-1,
call(P_2,X1,X2),
nest_right(P_2,N0,X0,X1).
Sample queries:
?- nest_right(\X^s(X)^true,3,0,N).
N = s(s(s(0))). % succeeds deterministically
?- nest_right(\X^s(X)^true,N,0,s(s(0))).
N = 2 ; % succeeds, but leaves behind choicepoint
false. % terminates universally
Here is mine:
Peano numbers that are actually better adapted to Prolog, in the form of lists.
Why lists?
There is an isomorphism between
a list of length N containing only s and terminating in the empty list
a recursive linear structure of depth N with function symbols s
terminating in the symbol zero
... so these are the same things (at least in this context).
There is no particular reason to hang onto what 19th century mathematicians
(i.e Giuseppe Peano )
considered "good structure structure to reason with" (born from function
application I imagine).
It's been done before: Does anyone actually use Gödelization to encode
strings? No! People use arrays of characters. Fancy that.
Let's get going, and in the middle there is a little riddle I don't know how to
solve (use annotated variables, maybe?)
% ===
% Something to replace (frankly badly named and ugly) "var(X)" and "nonvar(X)"
% ===
ff(X) :- var(X). % is X a variable referencing a fresh/unbound/uninstantiated term? (is X a "freshvar"?)
bb(X) :- nonvar(X). % is X a variable referencing an nonfresh/bound/instantiated term? (is X a "boundvar"?)
% ===
% This works if:
% Xn is boundvar and Xp is freshvar:
% Map Xn from the domain of integers >=0 to Xp from the domain of lists-of-only-s.
% Xp is boundvar and Xn is freshvar:
% Map from the domain of lists-of-only-s to the domain of integers >=0
% Xp is boundvar and Xp is boundvar:
% Make sure the two representations are isomorphic to each other (map either
% way and fail if the mapping gives something else than passed)
% Xp is freshvar and Xp is freshvar:
% WE DON'T HANDLE THAT!
% If you have a freshvar in one domain and the other (these cannot be the same!)
% you need to set up a constraint between the freshvars (via coroutining?) so that
% if any of the variables is bound with a value from its respective domain, the
% other is bound auotmatically with the corresponding value from ITS domain. How to
% do that? I did it awkwardly using a lookup structure that is passed as 3rd/4th
% argument, but that's not a solution I would like to see.
% ===
peanoify(Xn,Xp) :-
(bb(Xn) -> integer(Xn),Xn>=0 ; true), % make sure Xn is a good value if bound
(bb(Xp) -> is_list(Xp),maplist(==(s),Xp) ; true), % make sure Xp is a good value if bound
((ff(Xn),ff(Xp)) -> throw("Not implemented!") ; true), % TODO
length(Xp,Xn),maplist(=(s),Xp).
% ===
% Testing is rewarding!
% Run with: ?- rt(_).
% ===
:- begin_tests(peano).
test(left0,true(Xp=[])) :- peanoify(0,Xp).
test(right0,true(Xn=0)) :- peanoify(Xn,[]).
test(left1,true(Xp=[s])) :- peanoify(1,Xp).
test(right1,true(Xn=1)) :- peanoify(Xn,[s]).
test(left2,true(Xp=[s,s])) :- peanoify(2,Xp).
test(right2,true(Xn=2)) :- peanoify(Xn,[s,s]).
test(left3,true(Xp=[s,s,s])) :- peanoify(3,Xp).
test(right3,true(Xn=3)) :- peanoify(Xn,[s,s,s]).
test(f1,fail) :- peanoify(-1,_).
test(f2,fail) :- peanoify(_,[k]).
test(f3,fail) :- peanoify(a,_).
test(f4,fail) :- peanoify(_,a).
test(f5,fail) :- peanoify([s],_).
test(f6,fail) :- peanoify(_,1).
test(bi0) :- peanoify(0,[]).
test(bi1) :- peanoify(1,[s]).
test(bi2) :- peanoify(2,[s,s]).
:- end_tests(peano).
rt(peano) :- run_tests(peano).

Resources