/** * Author: R.Morelli, CPSC 352 * vss.pl -- an implementation of the Version Space Search as described in Luger and Stubblefield. For this implementation, training instaces and concepts are represented as simple lists. For example, an instance of a large, white, ball would be [large,white,ball]. A minimally more general concept would be [X,white,ball]. The concept ball would be [X,Y,ball]. The current implementation works, more or less, in the specific to general direction and in the general to specific direction, although these only work separately. The vss/4 predicate is a partial implementation of the bi-directional search. It is lacking an adequate definition of delete_G_Specs/3, a predicate that will delete from G any hypothesis more specific than some hypothesis in S and delete_S_Gens/3, a predicate that will delete from S any hypothesis not more specific than some hypothesis in G. Many of the individual predicates have been tested independently and appear to be working correctly. */ /* * training_set(Positives,Negatives). */ %% training_set([[small,red,ball],[small,white,ball],[large,blue,ball]],[]). %% training_set([[large,white,ball],[small,blue,ball]],[[small,red,brick],[large,blue,cube]]). %%training_set([[small,red,ball],[large,red,ball]],[[small,blue,brick],[large,red,cube]]). training_set([[small,red,ball],[large,blue,ball],[small,white,ball]],[[small,red,cube],[large,white,brick],[small,white,brick]]). /* * get_alt_properties/2 provides a list of the alternate values for a property of a given * value. In this example, the color property has three possible values: red, white, blue. */ get_alt_properties(small,[large]). get_alt_properties(large,[small]). get_alt_properties(white,[blue,red]). get_alt_properties(red,[white,red]). get_alt_properties(blue,[white,red]). get_alt_properties(ball,[brick,cube]). get_alt_properties(brick,[ball,cube]). get_alt_properties(cube,[ball,brick]). /* * do_vss is supposed to do the full version space search. It is not quite complete, * as noted. */ do_vss :- training_set([P|Pos],Negs), vss([X,Y,Z],[P],Pos,Negs). vss([],[],_,_) :- write('Sorry no concept convers all positives and none of negatives'),nl. vss(G,S,[],[]) :- G==[C], H==[C],printlist(C),nl. vss(G,S,[],[Neg|Negs]) :- /* For each negative instance */ delete_S(S,Neg,NewS), specialize(G,Neg,[],NewG), delete_G_Specs(NewG,NewNewG), /* This predicate is incomplete */ delete_G_Pos(NewNewG,S,NewNewNewG), vss(NewNewNewG,NewS,[],Negs). vss(G,S,[P1|Pos],Negs) :- /* For each positive instance */ delete_G(G,P1,NewG), generalize(S,P1,[],NewS), delete_S_Gens(NewS, NewNewS), /* This predicate is incomplete */ delete_S_Negs(NewNewS, G, NewNewNewS), vss(NewG,NewNewNewS,Pos,Negs). /* * do_spec_to_gen performs the candidate elimination algorithm in the specific to * general direction. It starts with the first positive training instance and * eliminates candidates that match negative instances and generalizes candidates * that don't match positive instances. */ do_spec_to_gen :- training_set([P|Pos],Negs), spec_to_gen([P],[],Pos,Negs). spec_to_gen(S, N, [], []) :- printlist(S),nl. %% S is the learned concept spec_to_gen(S, N, [], [Neg|Negs]) :- /* For each negative instance */ delete_S(S,Neg, NewS), spec_to_gen(NewS, [Neg|N], [], Negs). spec_to_gen(S, N, [P|Pos], Negs) :- /* For each positive instance */ generalize(S, P, [], NewS), delete_S_Gens(NewS, NewNewS), delete_S_Negs(NewNewS, N, NewNewNewS), spec_to_gen(NewNewNewS,N, Pos, Negs). /* * generalize(S,P,NewS). * For every element of S, if it does not match P, replace it with its * most specific generalization that matches P. */ generalize([],P,NewS,NewS). generalize([S1|Ss],P, OldSs, NewSs ) :- match(S1,P), generalize(Ss, P, [S1|OldSs], NewSs). generalize([S1|Ss],P, OldSs, NewSs) :- more_general(S1,P,NewS1), generalize(Ss, P, [NewS1|OldSs], NewSs). /* * more_general(S,P,NewS). * Replace S with a more general NewS that matches P. */ more_general([],[],[]). more_general([H1|T1],[H2|T2],[H1|T3]) :- H1==H2, more_general(T1,T2,T3). more_general([H1|T1],[H2|T2],[H1|T3]) :- var(H1), more_general(T1,T2,T3). more_general([H1|T1],[H2|T2],[H3|T3]) :- atom(H1), more_general(T1,T2,T3). /* * do_gen_to_spec performs the candidate elimination algorithm in the general to * specific direction. It starts with the most general hypothesis and * eliminates candidates that fail to match positive instances and specializes candidates * that match negative instances. */ do_gen_to_spec :- training_set(Pos,Negs), gen_to_spec([[X,Y,Z]],[],Pos,Negs). gen_to_spec(G, P, [], []) :- printlist(G). %% G is the learned concept gen_to_spec(G, P, [P1|Pos], []) :- delete_G(G,P1,NewG), gen_to_spec(NewG, [P1|P], Pos, []). gen_to_spec(G, P, Pos, [Neg|Negs]) :- specialize(G,Neg,[],NewG), delete_G_Specs(NewG,NewNewG), delete_G_Pos(NewNewG,P,NewNewNewG), gen_to_spec(NewNewNewG,P,Pos,Negs). /* * specialize(G,N,NewG). * For every element of G, if it matches N, replace it with its * most general specializations that don't match N. */ specialize([],N,NewG,NewG). specialize([G1|Gs],N, OldGs, NewGs ) :- match(G1,N), more_special(G1,N,NewG1s), append(NewG1s,OldGs,NewG1s), specialize(Gs, N, NewG1s, NewGs). specialize([G1|Gs],N, OldGs, NewGs) :- specialize(Gs, N, [G1|OldGs], NewGs). /* * more_special(G,N,NewGs). * Replace G with a more special NewG that don't match N. */ more_special(G,N,NewGs) :- generate_alt_prop_lists(G,N,[],Props), apply_prop_list(Props,[],G,[],NewGs). /* * generate_alt_prop_lists/4 generates a list of alternative properties * for a given concept. it is used in the Gen_to_specific search. For example, * in order to specialize the concept [large,Y,Z] relative to the negative instance * [large,blue,cube], the algorithm needs the following list: [[],[white,red],[brick,ball]]. * The first element are the properties alternative to _large_ -- there are none because * it cannot be specialized because it is already a constant. But the alternatives to * _blue_ in the training instance are [white,red]. These are used to specialize the Y * in the hypothesis. */ generate_alt_prop_lists([],[],Props,Props). generate_alt_prop_lists([H1|T1],[H2|T2],Props,NewProps) :- atom(H1),H1==H2, append(Props,[[]],Props2), generate_alt_prop_lists(T1,T2,Props2,NewProps). generate_alt_prop_lists([H1|T1],[H2|T2],Props,NewProps) :- var(H1), get_alt_properties(H2,AltProps), append(Props,[AltProps],Props2), generate_alt_prop_lists(T1,T2,Props2,NewProps). /* * apply_prop_list/5 applies a property list of the form [[],[red,white],[brick,ball]] * to the concept [large,Y,Z] resulting in a set of more specialized concepts that * don't match a negative instance. The negative instance is represented by a Prefix * and a Tail. So initially the instance [large,blue,cube] would be represented by * [],[large,blue,cube], and as each value is processed, the prefix grows and the * tail shrinks. Thus, [large],[blue,cube] would be the next case, and so on. */ apply_prop_list([],_,_,Gs,Gs). apply_prop_list([[]|Ps],Pre,[H|T],GsIn,GsOut) :- append(Pre,[H],NewPre), apply_prop_list(Ps,NewPre,T,GsIn,GsOut). apply_prop_list([PList|Ps],Pre,[H|T],GsIn,GsOut) :- apply(PList,Pre,H,T,[],G1s), append(Pre,[H],NewPre), append(GsIn,G1s,NewG1s), apply_prop_list(Ps,NewPre,T,NewG1s,GsOut). apply([],_,_,_,Gs,Gs). apply([H|T],Pre,Prop,Tail,GsIn,GsOut) :- append(Pre,[H],NewPre), append(NewPre,Tail,Gs), append(GsIn,[Gs],G1s), apply(T,Pre,Prop,Tail,G1s,GsOut). /* * delete_S(S,Neg,NewS). * Delete all members of S that match Neg giving NewS. */ delete_S([],_,[]). delete_S([S1|Ss],Neg,NewS) :- match(S1,Neg), delete_S(Ss,Neg,NewS). delete_S([S1|Ss],Neg,[S1|NewS]) :- delete_S(Ss,Neg,NewS). /* * delete_G(G,Pos,NewG). * Delete all members of G that fail to match Pos giving NewG. */ delete_G([],_,[]). delete_G([G1|Gs],Pos,NewG) :- not(match(G1,Pos)), delete_G(Gs,Pos,NewG). delete_G([G1|Gs],Pos,[G1|NewG]) :- delete_G(Gs,Pos,NewG). /* * delete_S_Gens(S, NewS). * Delete all members of S more general than some other hypothesis giving NewS. */ delete_S_Gens(S,S). /* * delete_G_Specs(G, NewG). * Delete all members of G more specific than some other hypothesis in S giving NewG. */ delete_G_Specs(G,G). /* * Delete all members of S that match a negative instance in N giving NewS. * delete_S_Negs(S, N, NewS). */ delete_S_Negs(S,[],S) :- !. delete_S_Negs(S,[N|Negs],NewS) :- delete_S(S,N,S1), delete_S_Negs(S1,Negs,NewS). /* * Delete all members of G that fail to match a positive instance in P giving NewG. * delete_S_Negs(S, N, NewS). */ delete_G_Pos(G,[],G) :- !. delete_G_Pos(G,[P|Pos],NewG) :- delete_G(G,P,G1), delete_G_Pos(G1,Pos,NewG). match([],[]). match([H1|T1], [H2|T2]) :- H1==H2, match(T1,T2). match([H1|T1], [H2|T2]) :- var(H1),atom(H2), match(T1,T2). match([H1|T1], [H2|T2]) :- atom(H1),var(H2), match(T1,T2). match([H1|T1], [H2|T2]) :- var(H1),var(H2), match(T1,T2). not(P) :- call(P),!,fail. not(P). append([],Ys,Ys). append([X|Xs],Ys,[X|Zs]) :- append(Xs,Ys,Zs). printlist([]) :- nl. printlist([H|T]) :- write(H), write(' '), printlist(T).