.he `SEARCH1``Page %`
.fo `Aaron Sloman` - % -` Dec 1977`
.ce2
COMBINATORIAL SEARCH
====================
.sp2
The aim of this is to provide an introduction to some concepts and
techniques which crop up in a number of different areas of AI - problem-solving,
planning, parsing, matching structures, playing games, etc.

The basic idea is that sometimes being intelligent involves combining
elements from a "kit" of resources.
Sometimes the resources are objects, sometimes operations.
The snag is that you can be more
or less intelligent about trying to find the right combinations of elements.
This demo is about some of the simpler more general, and alas,
less intelligent techniques.  The point is that you need to understand
their limitations in order to appreciate more complex approaches.
Before reading this you should think about a good strategy for
finding your way out of a maze. Please write it down.

Prerequisites
.br
-------------
.pg
It will help if you have worked through the SETS1 and SETS2 demos. In any
case, you should be familiar with list-manipulation using recursive procedures.
See the QUESTIONS 1,2 and 3 demos for revision.
The SEARCH2 demo is closely related to this one.
For reading see PS.R.

.ce2
An Example
----------

Suppose you have a pile of blocks of various heights and you
need to build a pillar of a given height, how could you select
suitable blocks? Lets make a list of the sizes of available of blocks.

 	: VARS SIZES;
 	: [2 8 3 5 5 4 7 2] -> SIZES

Note that two of the blocks are 2 units high and two are 5 units
high.
.pg
Suppose you need a pillar 6 units high. Which blocks should you use?
try to write down in English, a description of your stategy in selecting a
combination, before reading on. Save your description to show your tutor.
.pg
We`d like to design a function called TOWER, which is given a
SIZES list, and a HEIGHT (an integer) and should produce a list of
CHOSENSIZES.

 	: FUNCTION TOWER (SIZES, HEIGHT) =>CHOSENSIZES;
 	: ...
 	: ...
 	: END;
.br
So that TOWER ([2 8 3 5 2 7]), 9)
 	returns the list [2 7]
 	or possibly the list [2 5 2].
.br
What should the following do:

 	: TOWER ([2 3 5], 3) =>
 	: TOWER ([2 3 5], 99) =>
 	: TOWER ([], 3) =>
 	: TOWER ([1 2 3 4 5 ], 10) =>

See  if the strategy you wrote down would work on these cases.
.bb
.ce2
Decomposing the problem
-----------------------
.pg
Clearly
there are several special cases you should be able to deal with.
.in+3
.ti-3
1. The SIZES list is empty. No solution possible. Tower
should return FALSE.

.ti-3
2. The SIZES list contains a size which is the same as the desired HEIGHT. 
The solution is then to return a list containing...?
.in-3

Suppose neither of 
these situations obtains You could run down the SIZES list  looking
for a block-size less than the largest height. If you find one,
suppose that you can use it as the bottom block of the tower. You`ve still got a
list of unused sizes, and a remaining height: the deficit to be made up. So
you can use TOWER recursively to try to get a CHOSENSIZES list
for the remaining height. If it succeeds, i.e.  does not return FALSE, then
add the first SIZE onto the list produced, and return that as the result.
.br
Will that work for TOWER ([2 3 6],5)?
.br
Try writing out the program, and testing it.
.br
There`s a bug! What happens to TOWER ([2 3 6 ], 9)?
.br
The problem is that having chosen a size less than HEIGHT, you may find
that the recursive call fails (ie returns FALSE).
So you need to see if there`s 
.ul
another
SIZE less than HEIGHT that might do for the bottom brick, and if that
fails you need to try again, until there`s nothing left to try.
You need a loop something like:

 	: UNTIL SIZES = []
 	: THEN
 	:	IF	HD (SIZES) < HEIGHT
 	:	AND	(TOWER(TL(SIZES), HEIGHT - HD(SIZES))
 	:			->> CHOSENLIST)
 	:	THEN	<extend CHOSENLIST and return>
 	:	ELSE	['TRIED UNSUCCESSFULLY WITH` %HD(LIST)%]=>
 	:		TL(SIZES) -> SIZES
 	:	CLOSE
 	: CLOSE;

Can you see what this does? Try putting it into a definition of TOWER.
Remember that you can use the word RETURN to mean "leave this function"
(see the SYNTAX demo).

If the loop ever gets to the stage where SIZES=[]
then the search has failed, so make sure the result is FALSE in that case.

Can you see any similarity between the TOWER problem and the problem
of trying to find your way out of the MAZE? Try writing down
similarities.
.bb
.ce2
REFINEMENTS
-----------
.pg
This strategy is a rather blind "depth first" search. To see how blind
it is, try to work out what will happen if you do

 	TRACE TOWER;
 	TOWER ([2 3 2 13 9 5], 6)=>

It will try starting with 2 as the bottom block SIZE then after failing it will
try starting with 3, then it will try starting with 2 again and fail.
.br
What about the following:

 	TOWER ([2 3 4 99 195 266 300], 10)=>

it will look at all those big numbers over and over
again, even though we can see that since the numbers are in
increasing order there is no point going beyond the first one that`s too
big.
.br
What about:

 	TOWER ([1 1 1 2 2 3], 99) =>

We can see that there is no hope of finding a combination that will work
because the sizes available add up to much less than the desired
height. All this suggests that instead of blindly searching through
all the different combinations it pays to try to get some sort
of overview of the problem.
.pg
So the function TOWER might be designed in two parts: the
main function which prepares the way, then a function SUBTOWER
which tries out combinations, if appropriate. Here`s  an outline
of a possible structure. 

 	FUNCTION TOWER (SIZES, HEIGHT)=>CHOSENLIST
 		IF sum of all sizes is too small THEN fail
 		ELSE sort SIZES into increasing order 
 		remove bodies bigger than HEIGHT 
 		then use SUBTOWER to try various combinations of SIZES.
 		CLOSE
 	END

The function SUBTOWER can be defined to work like our earlier definition
of TOWER, except that it can 
.ul
use
the fact that SIZES is now in increasing order, to stop
searching down the list as soon as HD(SIZES)
becomes too big. You can do this by changing the exit condition in the UNTIL
loop. ie.

 	UNTIL first element of SIZES is too big
 	THEN
 		etc.

.br
Try this. Then TRACE TOWER SUBTOWER; and try the new TOWER on all the
old examples.
.br
Can you think of ways of improving TOWER or SUBTOWER even further?
E.g. what about preventing repeated attempts to build a tower
starting with a brick of size 2 in this case:

 	TOWER([2 2 2 2 5],3)=>

What about this case:

 	TOWER([1 1 1 1 1 1 1 6],7)=>

Suppose you wanted to modify TOWER so that it chose blocks of sizes 1 and 6
instead of seven 1-unit blocks?
You`d need to change SUBTOWER so that it first looked to see if SIZES already contained the wanted number. What about making it use the
.ul
largest
size less than or equal to the desired height?
Would this guarantee that in the end it will have a tower with the smallest
possible number of bricks? You can think of this strategy
as "always take the biggest step towards the goal".
This is an example of a heuristic which is designed to move you as
quickly as possible in a good direction. Are such heuristics always useful?
Can moving in a "good" direction lead you down a blind-alley?
.pg
Try sketching a function called STABLETOWER which could do that. You`ll
need a new version of SUBTOWER which uses the largest possible
block as its bottom block. But then instead of recursing on TL(SIZES)
it will have to remove the selected number from SIZES.
Could this be avoided by reversing the order of SIZES?
.bb
.ce2
An alternative approach: breadth-first search
---------------------------------------------

The strategy outlined above is basically 
.ul
depth first
in the sense that it goes as deep as it can along a path
to a complete tower before it "backs up" and tries some alternative.
.br
For example

 	TOWER ([1 2 2 2 2 3], 11)

Will choose the 1-unit block and see how far it can get with that i.e. [1 2 2 2 2]
before it discovers it`s in a blind alley. STABLETOWER, because it
starts from the biggest block will not have this problem: i.e.
it goes down the right path without ever failing -- on that example.
But even STABLETOWER wastes a lot of time here:

 	STABLETOWER ([1 1 1 1 7 8 9 10 11 12], 16)=>

The trouble with depth first search is that you can spend a 
long time exploring long paths which lead nowhere.
Even if you choose paths which are going in the right direction:
they may be blind alleys

An alternative is breadth-first search: instead
of building only one combination at a time, until it fails, try
growing many combinations of moves at once, and stop as soon as one of them
works. This way you are guaranteed to find
the quickest path to a solution i.e. the shortest successful combination. (Why?)
But a breadth first search requires more storage space because you have to
maintain records of all the different combinations
currently being explored.
E.g. suppose the list of SIZES is

 	[3 4 4 8 9 10]

and the target HEIGHT is 11.
For each possible incomplete tower you might
keep track of (a) the tower so far (b) the remaining SIZES, (c) the deficit
to be filled. So we`d have a list of three element lists like:

 	[ [4] [3 4 8 9 10] 7]
 	[ [3] [4 4 8 9 10] 8]
 	[ [8] [3 4 4 9 10] 3]
.br
etc.

Each triple contains a CHOSENLIST, a remaining SIZES list, and a deficit.
You would probably be able to cut out duplicates.
At the next step you might get triples like:

 	[ [4 3] [4 8 9 10] 4]
 	[ [4 4] [3 8 9 10] 3]
 	...
 	[ [3 8] [4 4 9 10] 0]
.br
etc.

The deficit 0 in a triple indicates that an acceptable combination
has been found - the first element of the triple, e.g. the chosenlist [3 8].
In other words, a "goal state" has been reached.
.pg
Try to design a program which does this kind of breadth first search.
Try doing it yourself. Maybe you can invent methods of avoiding
the explicit construction of such elaborate lists. E.g. several
combinations may share a common subcombination, as in

 	[3 4 8]
 	[3 4 9]
 	[3 4 10]

"Structure-sharing" might enable you to save space.
Alternatively, don`t explicitly build the chosen list. Keep only the
remainder of SIZES, and the deficit, explicit.
.br
e.g

 	[[4 8 9 20] 7]
 	[[3 8 9 10] 3]
 	[[4 4 9 10] 0]

The chosen list can
always be re-constructed from this. For more on different sorts of search
strategies see the SEARCH2 demo.
.bb
.ce2
The idea of a state
-------------------

The triples (or whatever alternative you`ve adopted) in the previous
section can be thought of as representing intermediate states on paths
towards success or failure.
.br
Initially the state is:

 	[% [], SIZES, HEIGHT %]

i.e. the chosen combination is empty, all sizes remain available
for use, and the deficit is the desired final height.
A later state in the previous example would be:

 	[ [10 3] [4 4 8 9] 1 ]

There is no way of filling the deficit of 1, so this is a state on a path
to
.ul
failure.

 	[[4 4] [3 8 9 10] 3 ]

is a state one step away from
.ul
success.
The depth-first versions of TOWER and STABLETOWER had states like
these implicit in the values of the variables in the functions.
.bb
.ce2
State-changing operators
------------------------

In selecting a new block (i.e. new number) to add to the list of
chosen blocks, we move from one state to another.
E.g. by selecting a block of size 3, we move from the state

 	[[4 4] [3 8 9 10] 3]

to the
.ul
goal
state:

 	[[4 4 3] [8 9 10] 0]

So we can think of our search for an acceptable combination as a sort of
exploration of "states",
starting form and 
.ul
initial
state, e.g.

 	[[] [3 4 4 8 9 10] 11]

and trying to reach a
.ul
goal
state (defined here as a state whose deficit is 0, so there are alternative
goal states), via a series of
.ul
legal operations
(defined here as transferring a number from the SIZES list to
the chosen list, and subtracting it from the deficit).
.pg
Notice that in this example we have ruled out an illegal transfer
of a number bigger than the deficit. I.e. we do not allow a negative
deficit. What would happen if we did?
.bb
.ce2
Detecting Loops in the Search
-----------------------------

Some states are
.ul
dead ends,
i.e. there is no legal move possible, e.g. if the SIZES
list contains nothing, or contains only numbers which are too big.
If we allowed a block to be selected, then put back, that would 
make it legal to transfer a number from the chosen list to the SIZES list. Then no state would be a dead end. Would that be a useful change to the
problem?
.pg
See the analysis of the missionaries and cannibals problem
in the MANDC demo, for an example of a "search space" with no dead ends.
.pg
If there aren`t any dead ends theres a chance of going round forever
in circles, and it may therefore be necessary to keep a record of states
you`ve explored and treat something as a dead end if its a state you`ve
been in before.
.pg
How quickly you detect an impossible situation may depend on how you represent
states. E.G. if you represent the state

 	[[1 9] [4 8 10] 1]

simply as a two element list, omitting the "chosen" list.

 	[[4 8 10] 1]

Then you`ll have 
.ul
the same
representation for:

 	[[9 1] [4 8 10] 1]

I.e. with the more compact representation, after discovering and recording that the first one is a dead
end  you will not need to find out that the second one is also, since
it will look like the same state.
.pg
You could get the same effect in matching three element states by matching only the
middle element - the remaining sizes list. 
.pg
This shows that how you represent things, and how you match things,
can make a lot of difference to how much searching you have to
do: often  a change of representation can turn a difficult problem
into an easy one. Often this is much more effective than merely
using heuristics to guide your search.
.bb
.ce2
Exercises
---------
.in +3
.ti-3
1. Make a list of activities that involve an initial state, a set of legal
moves, and a goal state. Describe strategies for reaching the goal.

.ti-3
2. Can you treat going to London as an example? What would
be the set of legal moves? Are there good heuristics for selecting a move?
Is every action you can perform a legal move? Or only ones which change
your location? Or only a subset of those?

.ti-3
3. Can designing a program (e.g. the TOWER program) be thought of as a 
search through a space of intermediate states? What are the legal
operations - adding new symbols to the program? Deleting symbols?
How do you choose a move to make?
.ti-3

4. Are searches involved in 
recognising an object in a picture?
E.g. suppose you run SEEPICTURE on a picture of a triangle, and
then try to decide if it is a square or a triangle, using the
information in the database. Do you need to search? If so what is the
goal and what are the legal operations?

.ti-3
5. Imagine doing subtraction as follows:
.br
To subtract Y from X, think of some number N (e.g. using the function
RANDOM). Then see if N+Y=X. If not, try another value of N. Is
it helpful to think of ordinary subtraction as a special case of this with a good
heuristic for finding N?

.ti-3
6. Do the exercises in the SEARCH2 demo.
Then try re-defining TOWER using the functions in the SEARCH2 demo.

READING:
.br
--------
.br
See the PS.R  reading list.
