Modelling Examples
Why did the Chicken cross the River?
One core question in modelling a problem is "what are the relevant variables?"
"Consider a traveller somewhere in the tropical rain forest of northern Australia. She wishes to paddle across a fast flowing river in an inflatable boat. It is important to cross the river as quickly as possible since it is full of crocodiles. There is a single small clearing on the other side of the river at which she must land. Where should she set off from her side of the river in order to reach the clearing in the least time?"

We simplify the problem by assuming (correctly) that the fastest way across is to head directly across the river, and allow the current to carry the boat downstream
So the relevant variables are the width W and speed S of the river, the rowing speed R, and the distance upstream P from which the boat should be launched
Finally, to relate the crossing and downstream distances, we will need a variable T to represent the time taken (but this variable need not be visible to the outside World
Thus the rule will be
river(W, S, R, P) :-
T = W / R,
P = S * T.
Assuming a river 24m wide flowing at 1m/s, and a rowing speed of 1.5m/s we would run the query
S = 1, W = 24, R = 1.5, river(W, S, R, P).
To get the answer
P = 16
R = 1.5
W = 24
S = 1
Conversely, suppose our traveller cannot set off any more than 20m upstream, and her rowing speed is between 1 and 1.3 m/s. Can she make it?
S = 1, W = 24, 1 <= R, R <= 1.3, P <= 20, river(W, S, R, P).
And the answer
W = 24
S = 1
R <= 1.3
1 <= R
P <= 20
24 = R * P
*** Maybe
Perhaps we'll have to think further about this (see exercise P5.1)
All in the Family
A second core issue in modelling is "what relationships do we need to represent?"
The following might represent the basic data about a particular family:
%father(P1, P2) if P1 is the father of P2
father(jim, edward).
father(jim, maggy).
father(edward, peter).
father(edward, helen).
father(edward, kitty).
father(bill, fi).
%mother(P1, P2) if P1 is the mother of P2
mother(maggy, fi).
mother(fi, lillian).
%age(P, Age) if Age is the age of person P
age(maggy, 63).
age(helen, 37).
age(kitty, 35).
age(fi, 43).
age(lillian, 22).
age(jim, 85).
age(edward, 60).
age(peter, 33).
age(bill, 65).
We can construct more complex relationships using rules:
%parent(P1, P2) if P1 is a parent of P2
parent(X, Y) :-
father(X, Y).
parent(X, Y) :-
mother(X, Y).
%sibling(P1, P2) if P1 is a sibling of P2
sibling(X, Y) :-
parent(Z, X),
parent(Z, Y),
not X = Y.
%cousin(P1, P2) if P1 is a cousin of P2
%note that it should probably also incorporate
%the condition "not X = Y", else incest would
%allow a person to be their own cousin
cousin(X, Y) :-
parent(Z, X),
sibling(Z, T),
parent(T, Y).
%older(P1, P2) if P1's age is greater than P2's
older(X, Y) :-
AX >= AY,
age(X, AX),
age(Y, AY).
We can check if Peter has any cousins:
cousin(peter, X).
X = fi
*** Retry? ;
*** No
or whether Fi has any older cousins:
cousin(fi, Y), older(Y, fi).
*** No
Yuppies Away
A call option gives the holder the right to buy a fixed number of shares for a fixed price, the exercise price, prior to the (fixed) expiry date
A put option gives the holder the right to sell a fixed number of shares for a fixed price, the exercise price, prior to the (fixed) expiry date
Since you have the choice of whether to exercise an option (and presumably will not exercise it if it is cheaper not to), buying options in effect limits the total exposure:

We may choose to model options trading via the parameters C (cost of option), E (exercise price), S (share price), P (total payoff) (we assume all options contracts are in units of 100 shares)
Buying a call option could be modelled by the rules:
%buy_call_payoff(S, C, E, P) if P is the payoff on buying
%a call option with an exercise price E at cost C
%when the share price is S
buy_call_payoff(S, C, E, P) :-
0 <= S,
S <= E / 100,
P = -C.
buy_call_payoff(S, C, E, P) :-
S >= E / 100,
P = 100 * S - E -C.
We would then need to write a separate set of rules for selling call options; it is simpler to introduce a parameter B to distinguish between buying and selling:
%call_option(B, S, C, E, P) if P is the payoff on buying (B=1)
%or selling (B=-1) a call option with an exercise
%price E at cost C when the share price is S
call_option(B, S, C, E, P) :-
0 <= S,
S <= E / 100,
P = -C * B.
call_option(B, S, C, E, P) :-
S >= E / 100,
P = (100 * S - E -C) * B.
%put_option(B, S, C, E, P) if P is the payoff on buying (B=1)
%or selling (B=-1) a put option with an exercise
%price E at cost C when the share price is S
call_option(B, S, C, E, P) :-
0 <= S,
S <= E / 100,
P = (E - 100 * S - C) * B.
call_option(B, S, C, E, P) :-
S >= E / 100,
P = -C * B.
The butterfly is a more complex combination of put and call options which has the effect of yielding a payoff within a narrow range, but has a guaranteed maximum loss outside that range
%butterfly(S, C1, E1, C2, E2, C3, E3, P) if
%P is the payoff from a butterfly of
%buy call options at cost C1, C3
%and exercise prices E1 and E3,
%and sell call options at cost C2
%and exercise price E2,
%at share price S
butterfly(S,C1,E1,C2,E2,C3,E3,P1+2*P2+P3) :-
Buy = 1, Sell = -1,
call_option(Buy, S, C1, E1, P1),
call_option(Sell, S, C2, E2, P2),
call_option(Buy, S, C3, E3, P3).
Making the Best of It
So far, we have just looked at finding solutions to constraints
Often, we would like to find a best (eg minimal) solution to a set of constraints
There is no standard way of doing this within CLP systems; Marriott and Stuckey use the syntax
minimize(G, E)
for a predicate which will find the solutions to G which give the smallest value of E
For example,
minimize(butterfly(S, 400, 100, 200, 300, 100, 500, P), -P)
would find the maximum payoff and corresponding share price for the butterfly described in the text (S = 3, P = 100)
minimize(butterfly(S, 400, 100, 200, 300, 100, 500, P), P)
would find the maximum loss and corresponding share price for the same butterfly (S <= 1 or S >= 5, P = -100)
Note that there is no built-in minimize construct for CLP(R), and SICStus and ECLiPSe have them only for finite domains
Data Structures
So far, our modelling problems have depended only on simply structured data
There are many problems for which this is not enough
Example: Heat Flow in a Flat Plate
The steady-state temperature at each point in a steel plate is just the mean of its orthogonal neighbouring points:
|
T11 |
T12 |
T13 |
T14 |
|
T21 |
T22 |
T23 |
T24 |
|
T31 |
T32 |
T33 |
T34 |
T22 = (T12 + T21 + T23 + T32) / 4
T23 = (T13 + T22 + T24 + T33) / 4
This approach is feasible for a small grid, but for a larger mesh would be impossibly tedious and error-prone to express
Records
The simplest data structuring approach in CLP languages is to wrap a number of items together in a term (we've already seen examples of this)
For example, we could represent the imaginary number a + ib as c(a,b)
We could then, for example, define addition and multiplication of complex numbers:
c_add(c(R1, I1), c(R2, I2), c(R3, I3)) :-
R3 = R1 + R2,
I3 = I1 + I2.
c_mult(c(R1, I1), c(R2, I2), c(R3, I3)) :-
R3 = R1 * R2 - I1 * I2,
I3 = R2 * I1 + R1 * I2.
In dealing with imaginary numbers, we will often be interested in only the real part
To save having to invent names for variables in which we are not interested, CLP languages permit us to refer to c(R, _), where the "_" is effectively a notation for "any value"
This will be more important in larger and more complex data structures, where the burden of inventing irrelevant variable names would become impossibly burdensome
Lists
Lists are probably the most widely used CLP data structure
Their importance lies in their extensibility - we don't have to know ahead of time how long a list is (in particular, we can write constraints which deal with lists of arbitrary, or even indefinite, length)
Lists are represented with square brackets
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
represents the sequence of the first 10 positive integers
There is a special notation for the empty list
[]
and another (this is the really important bit) for separating out the first element of a list from the rest:
[X|Xs]
The commonest use for lists is in recursive (ie almost circular) constraints
Example: Append
For example, what does it mean for two lists to combine together end to end to make a third?
%append(Xs, Ys, Zs) if Zs is the list created
%by joining Xs and Ys end to end
append([],Ys,Ys).
append([X|Xs], Ys, [X|Zs]) :-
append(Xs, Ys, Zs).
Like most LP constraints, this can be run in directions which may not be obvious:
append([1,2],Y,[1,2,3]).
append(X, Y, [1, 2, 3]).
Example: all_different
%all_different(Xs) if the elements of Xs %are all distinct
all_different([]).
all_different([X|Xs]) :-
not_member(X,Xs),
all_different(Xs).
%not_member(X,Ys) if X does not occur
%in Ys
not_member(_, []).
not_member(X, [Y|Ys]) :-
X <> Y,
not_member(X,Ys).
Example: Stable_temperatures
%stable_temperatures(List_list) if List_list
%is a list of lists (representing the X and Y
%coordinates of a grid) obeying the stable
%temperature condition described earlier
stable_temperature([_,_]). %if the grid has
%edges only, OK
stable_temperature([R1, R2, R3 | Rs]) :-
stable_col(R1, R2, R3) , %the first three
%rows are OK
stable_temperature([R2, R3|Rs]).
%so is the rest
%stable_col(R1, R2, R3) if R1, R2, R3 form a 3*N
%grid which satisfies the stable heat flow cond.
stable_col([_,_],[_,_],[_,_]).
%Edges are OK
stable_col( [LT,L,LB|Ls],
[MT,M,MB|Ms],
[RT,R,RB|Rs]) :-
M = (L + MT + MB + R) / 4,
stable_col( [L,LB|Ls],
[M,MB|Ms],
[R,RB|Rs]).