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]).