


The technique used by Bernhard Pfahringer was exactly the right one for
Competitions 1 and 3.  His program used a search he describes as essentially
brute-force, but with some necessary sophistication.  The rest of this file
has the remainder of his description (with an added reference and
acknowledgement).

                               ...               In principle I did just
generate all possible clauses of size 3,4,... and so on in a depth-first
iterative deepening manner [2]. Every completed clause was checked for 
completeness and coverage. The final version of the program solved Competition
1 and 3 simultanously (generation of clauses did not depend on the
examples) using about one day realtime on Sparc 10.

History: Originally I started with a version of Foil [3] that I've
implemented in Prolog some time ago. After realizing that an optimal
solution was required, I did a few experiments which proved depth-first
iterative deepening to be feasible.  So I just kept the
literal-generator of my original Foil clone. This generator is efficient,
because it uses types for variables, ensures that only one 'canonical' form
of a literal is generated if there are a few symmetric ones possibly, and
it makes a distinction between literals that introduce new variables
(generators) and literals that don't (tests).

At some point I made the decision that introducing 'sub-predicates' is
probably too costly. So I concentrated on the expressiveness of the 
generated prolog clauses. Incrementally I first added 'or', then 'findall'
and then tried to add recursion. At that point I was still using the
examples for checking while generating - immediately dropping clauses
that would not cover all positive examples. But then I really could not
figure it out for the interaction of or and recursion. That was when
I finally dropped using the examples for pruning and started to just
generate all syntactically possible clauses. 

The first attempts were too inefficent - way too many duplicate clauses
were generated. But then I realized that because I would generate all
clauses, I could impose some ordering without losing completeness. So
for every 'and-branch' (the toplevel originally is an and-branch, every
or introduces two independent and-branches, findall yields another one)
first all possible generators are produced, then with a now fixed set
of variables all possible tests are tried (of course always staying
within the given size-bounds). Introduction of 'or' is only allowed
at the last position in an 'and-branch'.

Testing of the clauses needed indirection through a check against
run-away recursion: the worst case was the a combination of findall
and recursion were the list grew quadratically at every recursive call
quickly overflowing the prolog-stack.

After trying to add cut and negation, I found so many possible sources of
incompleteness that I just dropped all smarts. I just defined and
implemented a simple grammar to produce valid clauses:
 s -> any_literal
 s -> s,s
 s -> ( s ; s)
 s -> \+ s
 s -> ( s, ! ; s)
 s -> ( s, !, s ; s)
 s -> findall(1,s,[1])    ;; test iff exactly one occurence
 ...
 s -> findall(1,s,[1,1,1,1])
 s -> findall(Var,s,TrainVar)
Sizes are checked immediately to enable early pruning, e.g. for
's -> \+ s' the allocated size must be > 2 because '\+' costs one and
the subsequent 's' will cost at least two. Every call must produce
exactly the size called for. For symmetric productions (e.g. s -> s,s)
always the first subpart will be greater or equal in size to the second
subpart.

This purely size-oriented approximation to a normalform yielded sufficient
efficiency.

Final re-expression: I dropped the run-away recursion checks, substituted
'or' for '\+' (I am using Sicstus [1]), and reordered literals were ordering
seemed 'unnatural' to me, like if an 'or' contained a branch with recursive
call and one without, I prefer have the recursive call in the second
branch, just like recursive predicate definitions where you first state
the base case and then the recursion case. 



I am indepted to the Austrian Research Institute for Artificial Intelligence
for providing the necessary computing resources.



Reference

[1] Carlsson M., Widen J.: Sicstus Prolog Users Manual, Swedish Institute of
    Computer Science, SICS/R-88/88007C, 1990.

[2] Korf R.E.: Depth-First Iterative-Deeping: An Optimal Admissible Tree Search,
    Artificial Intelligence, 27(1), 97-110, 1985.

[3] Quinlan J.R., Cameron-Jones R.M.: FOIL: A Midterm Report, in Brazdil
    P.B.(ed.), Machine Learning: ECML-93, Springer, Berlin, pp.3-20, 1993.
