====== Hyper hypothesis refiner ====== {{tag>learning hypothesis}} ===== Description ===== Program HYPER (Hypothesis Refiner) for learning in logicn **Source**: PROLOG programming for artificial intelligence, 3rd Edition, Harlow, 2001, ISBN 0-201-40375-7. ===== Download ===== Program source code: {{hyper_hypothesis_refiner.pl}} ===== Listing ===== % Figure 19.7 The HYPER program. % Program HYPER (Hypothesis Refiner) for learning in logic :- op( 500, xfx, :). :- op( 900, fy, not). % not Goal): negation as failure; % Note: This is often available as a built-in predicate, % often written as prefix operator "\+", e.g. \+ likes(mary,snakes) not Goal :- Goal, !, fail ; true. % induce( Hyp): % induce a consistent and complete hypothesis Hyp by gradually % refining start hypotheses induce( Hyp) :- init_counts, !, % Initialise counters of hypotheses start_hyps( Hyps), % Get starting hypotheses best_search( Hyps, _:Hyp). % Specialised best-first search % best_search( CandidateHyps, FinalHypothesis) best_search( [Hyp | Hyps], Hyp) :- show_counts, % Display counters of hypotheses Hyp = 0:H, % cost = 0: H doesn't cover any neg. examples complete(H). % H covers all positive examples best_search( [C0:H0 | Hyps0], H) :- write('Refining hypo with cost '), write( C0), write(:), nl, show_hyp(H0), nl, all_refinements( H0, NewHs), % All refinements of H0 add_hyps( NewHs, Hyps0, Hyps), !, add1( refined), % Count refined hypos best_search( Hyps, H). all_refinements( H0, Hyps) :- findall( C:H, ( refine_hyp(H0,H), % H new hypothesis once(( add1( generated), % Count generated hypos complete(H), % H covers all pos. exampl. add1( complete), % Count complete hypos eval(H,C) % C is cost of H )) ), Hyps). % add_hyps( Hyps1, Hyps2, Hyps): % merge Hyps1 and Hyps2 in order of costs, giving Hyps add_hyps( Hyps1, Hyps2, Hyps) :- mergesort( Hyps1, OrderedHyps1), merge( Hyps2, OrderedHyps1, Hyps). complete( Hyp) :- % Hyp covers all positive examples not ( ex( P), % A positive example once( prove( P, Hyp, Answ)), % Prove it with Hyp Answ \== yes). % Possibly not proved % eval( Hypothesis, Cost): % Cost of Hypothesis = Size + 10 * # covered negative examples eval( Hyp, Cost) :- size( Hyp, S), % Size of hypothesis covers_neg( Hyp, N), % Number of covered neg. examples ( N = 0, !, Cost is 0; % No covered neg. examples Cost is S + 10*N). % size( Hyp, Size): % Size = k1*#literals + k2*#variables in hypothesis; % Settings of parameters: k1=10, k2=1 size( [], 0). size( [Cs0/Vs0 | RestHyp], Size) :- length(Cs0, L0), length( Vs0, N0), size( RestHyp, SizeRest), Size is 10*L0 + N0 + SizeRest. % covers_neg( H, N): % N is number of neg. examples possibly covered by H % Example possibly covered if prove/3 returns 'yes' or 'maybe' covers_neg( Hyp, N) :- % Hyp covers N negative examples findall( 1, (nex(E), once(prove(E,Hyp,Answ)), Answ \== no), L), length( L, N). % unsatisfiable( Clause, Hyp): % Clause can never be used in any proof, that is: % Clause's body cannot be proved from Hyp unsatisfiable( [Head | Body], Hyp) :- once( prove( Body, Hyp, Answ)), Answ = no. start_hyps( Hyps) :- % Set of starting hypotheses max_clauses( M), setof( C:H, (start_hyp(H,M), add1(generated), complete(H), add1(complete), eval(H,C)), Hyps). % start_hyp( Hyp, MaxClauses): % A starting hypothesis with no more than MaxClauses start_hyp( [], _). start_hyp( [C | Cs], M) :- M > 0, M1 is M-1, start_clause( C), % A user-defined start clause start_hyp( Cs, M1). % refine_hyp( Hyp0, Hyp): % refine hypothesis Hyp0 into Hyp refine_hyp( Hyp0, Hyp) :- choose_clause( Hyp0, Clause0/Vars0, Clauses1, Clauses2), % Choose a clause conc( Clauses1, [Clause/Vars | Clauses2], Hyp), % New hypothesis refine( Clause0, Vars0, Clause, Vars), % Refine chosen clause non_redundant( Clause), % No redundancy in Clause not unsatisfiable( Clause, Hyp). % Clause not unsatisfiable choose_clause( Hyp, Clause, Clauses1, Clauses2) :- % Choose Clause from Hyp conc( Clauses1, [Clause | Clauses2], Hyp), % Choose a clause nex(E), % A negative example E prove( E, [Clause], yes), % Clause itself covers E ! % Clause must be refined ; conc( Clauses1, [Clause | Clauses2], Hyp). % Otherwise choose any clause % refine( Clause, Args, NewClause, NewArgs): % refine Clause with variables Args giving NewClause with NewArgs % Refine by unifying arguments refine( Clause, Args, Clause, NewArgs) :- conc( Args1, [A | Args2], Args), % Select a variable A member( A, Args2), % Match it with another one conc( Args1, Args2, NewArgs). % Refine a variable to a term refine( Clause, Args0, Clause, Args) :- del( Var:Type, Args0, Args1), % Delete Var:Type from Args0 term( Type, Var, Vars), % Var becomes term of type Type conc( Args1, Vars, Args). % Add variables in the new term % Refine by adding a literal refine( Clause, Args, NewClause, NewArgs) :- length( Clause, L), max_clause_length( MaxL), L < MaxL, backliteral( Lit, InArgs, RestArgs), % Background knowledge literal conc( Clause, [Lit], NewClause), % Add literal to body of clause connect_inputs( Args, InArgs), % Connect literal's inputs to other args. conc( Args, RestArgs, NewArgs). % Add rest of literal's arguments % non_redundant( Clause): Clause has no obviously redundant literals non_redundant( [_]). % Single literal clause non_redundant( [Lit1 | Lits]) :- not literal_member( Lit1, Lits), non_redundant( Lits). literal_member( X, [X1 | Xs]) :- % X literally equal to member of list X == X1, ! ; literal_member( X, Xs). % show_hyp( Hypothesis): % Write out Hypothesis in readable form with variables names A, B, ... show_hyp( []) :- nl. show_hyp( [C/Vars | Cs]) :- nl, copy_term( C/Vars, C1/Vars1), name_vars( Vars1, ['A','B','C','D','E','F','G','H','I','J','K','L','M','N']), show_clause( C1), show_hyp( Cs), !. show_clause( [Head | Body]) :- write( Head), ( Body = []; write( ':-' ), nl ), write_body( Body). write_body( []) :- write('.'), !. write_body( [G | Gs]) :- !, tab( 2), write( G), ( Gs = [], !, write('.'), nl ; write(','), nl, write_body( Gs) ). name_vars( [], _). name_vars( [Name:Type | Xs], [Name | Names]) :- name_vars( Xs, Names). % connect_inputs( Vars, Inputs): % Match each variable in list Outputs with a variable in list Vars connect_inputs( _, []). connect_inputs( S, [X | Xs]) :- member( X, S), connect_inputs( S, Xs). % merge( L1, L2, L3), all lists sorted merge( [], L, L) :- !. merge( L, [], L) :- !. merge( [X1|L1], [X2|L2], [X1|L3]) :- X1 @=< X2, !, % X1 "lexicographically precedes" X2 (built-in predicate) merge( L1, [X2|L2], L3). merge( L1, [X2|L2], [X2|L3]) :- merge( L1, L2, L3). % mergesort( L1, L2): sort L1 giving L2 mergesort( [], []) :- !. mergesort( [X], [X]) :- !. mergesort( L, S) :- split( L, L1, L2), mergesort( L1, S1), mergesort( L2, S2), merge( S1, S2, S). % split( L, L1, L2): split L into lists of approx. equal length split( [], [], []). split( [X], [X], []). split( [X1,X2 | L], [X1|L1], [X2|L2]) :- split( L, L1, L2). % Counters of generated, complete and refined hypotheses init_counts :- retract( counter(_,_)), fail % Delete old counters ; assert( counter( generated, 0)), % Init. counter 'generated' assert( counter( complete, 0)), % Init. counter 'complete' assert( counter( refined, 0)). % Init. counter 'refined' add1( Counter) :- retract( counter( Counter, N)), !, N1 is N+1, assert( counter( Counter, N1)). show_counts :- counter(generated, NG), counter( refined, NR), counter( complete, NC), nl, write( 'Hypotheses generated: '), write(NG), nl, write( 'Hypotheses refined: '), write(NR), ToBeRefined is NC - NR, nl, write( 'To be refined: '), write( ToBeRefined), nl. % Parameter settings max_proof_length( 6). % Max. proof length, counting calls to preds. in hypothesis max_clauses( 4). % Max. number of clauses in hypothesis max_clause_length( 5). % Max. number of literals in a clause prove( Goal, Hypo, Answer) :- max_proof_length( D), prove( Goal, Hypo, D, RestD), (RestD >= 0, Answer = yes % Proved ; RestD < 0, !, Answer = maybe % Maybe, but it looks like inf. loop ). prove( Goal, _, no). % Otherwise Goal definitely cannot be proved % prove( Goal, Hyp, MaxD, RestD): % MaxD allowed proof length, RestD 'remaining length' after proof; % Count only proof steps using Hyp prove( G, H, D, D) :- D < 0, !. % Proof length overstepped prove( [], _, D, D) :- !. prove( [G1 | Gs], Hypo, D0, D) :- !, prove( G1, Hypo, D0, D1), prove( Gs, Hypo, D1, D). prove( G, _, D, D) :- prolog_predicate( G), % Background predicate in Prolog? call( G). % Call of background predicate prove( G, Hyp, D0, D) :- D0 =< 0, !, D is D0-1 % Proof too long ; D1 is D0-1, % Remaining proof length member( Clause/Vars, Hyp), % A clause in Hyp copy_term( Clause, [Head | Body] ), % Rename variables in clause G = Head, % Match clause's head with goal prove( Body, Hyp, D1, D). % Prove G using Clause ===== Comments =====