{-# LANGUAGE GADTs, TypeSynonymInstances, FunctionalDependencies, MultiParamTypeClasses #-}moduleAI.VersionSpaceswhereimportControl.Arrow((***))-- | Representation of a traditional version space, as described by-- Hirsh: Hirsh, H.: 1991, 'Theoretical Underpinnings of Version-- Spaces'. In: Proceedings of the Twelfth International Joint-- Conference on Artificial Intelligence. pp. 665–670.dataBSRaio=EmptyBSR|BSR{storage::a,narrow::BSRaio->i->o->BSRaio,hypos::BSRaio->[i->o]}-- | Renders a BSR to a string to show whether the BSR is empty or-- not. Additional details place undesirable restrictions on the-- state storage.showBSR::BSRaio->StringshowBSREmptyBSR="Empty"showBSR(BSR___)="non-empty"-- | Union two versionspaces, generating a third.union::VersionSpaceab->VersionSpaceab->VersionSpaceabunionEmptyy=yunionxEmpty=xunionxy=Unionxy-- | Join two versionspaces, generating a third.join::(Eqb,Eqd)=>VersionSpaceab->VersionSpacecd->VersionSpace(a,c)(b,d)joinEmpty_=Emptyjoin_Empty=Emptyjoinxy=Joinxy-- | Transform a version space to mutate the input and/or output types.-- Transforms require that three functions be specified:---- [@i -> a@] Transform the input of the resulting version space to the input of the initial versionspace.---- [@o -> b@] Transform the output of the initial versionspace into the output of the resulting versionspace.---- [@b -> o@] Transform the output of the /resulting/ versionspace-- into the output of the /initial/ versionspace. This is necessary-- to support training: the training examples will be in terms of the-- resulting versionspace, so the output must be transformed back-- into the terms of the initial versionspace.tr::(Eqb)=>(i->a)->(o->b)->(b->o)->VersionSpaceab->VersionSpaceiotr___Empty=Emptytrtintoutfoutvs=Trtintoutfoutvs-- | Version Space algebraic operators:dataVersionSpaceiowhere-- The empty, or collapsed versionspace.Empty::VersionSpaceio-- A basic leaf versionspace, this just wraps a 'BSR'VS::BSRaio->VersionSpaceio-- The Join of two versionspaces. This should not be used directly, rather, use the 'join' function.Join::(Eqd,Eqb)=>VersionSpaceab->VersionSpacecd->VersionSpace(a,c)(b,d)-- The union of two versionspaces. This should not be used directly, rather, use the 'union' function.Union::VersionSpaceab->VersionSpaceab->VersionSpaceab-- The transform of two versionspaces. This should not be used directly, rather, use the 'tr' function.Tr::(Eqb)=>(i->a)->(o->b)->(b->o)->VersionSpaceab->VersionSpaceio-- | Serializes a versionspace to a human-readable string, for certain values of 'human'.showVS::VersionSpaceio->StringshowVSEmpty="Empty"showVS(VShs)=showBSRhsshowVS(Unionvs1vs2)="["++showVSvs1++" U "++showVSvs2++"]"showVS(Joinvs1vs2)="["++showVSvs1++" |><| "++showVSvs2++"]"showVS(Tr___vs)="[TR "++showVSvs++"]"-- | Train a version space, reducing the set of valid hypotheses. We-- handle the Empty VS cases prior to the corresponding non-empty-- cases because the Empties are simplifying cases, so logic can be-- short-circuited by collapsing parts of the hierarchy before-- recursing.train::(Eqo)=>VersionSpaceio->i->o->VersionSpaceiotrainEmpty__=Emptytrain(VSb)io=case(narrowb)bioofEmptyBSR->Emptybsr->VSbsr-- | The join of an empty VS with any other VS is empty.train(JoinEmpty_)__=Emptytrain(Join_Empty)__=Emptytrain(Joinvs1vs2)(i1,i2)(o1,o2)=join(trainvs1i1o1)(trainvs2i2o2)-- | Unioning a VS with an empty VS is just the non-empty VS.train(Unionvs1Empty)__=vs1train(UnionEmptyvs2)__=vs2train(Unionvs1vs2)io=union(trainvs1io)(trainvs2io)-- | Any transform on an empty VS is just an empty VS.train(Tr___Empty)__=Emptytrain(TrtintoutfoutinnerVS)io=trtintoutfouttrainedVSwheretrainedVS=traininnerVS(tini)(touto)-- | Retrieve the valid hypotheses for a version space.hypotheses::VersionSpaceio->[(i->o)]-- could be i -> [o]hypothesesEmpty=[]hypotheses(VShs)=(hyposhs)hshypotheses(Joinvs1vs2)=zipWith(***)(hypothesesvs1)(hypothesesvs2)hypotheses(Unionvs1vs2)=hypothesesvs1++hypothesesvs2hypotheses(Trfin_foutvs)=map(\x->fout.x.fin)$hypothesesvs-- | Runs all valid hypotheses from the version space-- on the specified input.runVS::VersionSpaceab->a->[b]runVSvsinput=map(\x->x$input)$hypothesesvs{-
Notes regarding error-tolerance:
* The Similar function happens at the narrowing stage - I think this
can be done in the narrow fun. on BSR. However, this assumes that
user error on different aspects of the input is independent of the
other aspects of error in the same way that the components of the
trained hypotheses are independent.
* The Aggregate function operates on sets of hypotheses. It
converts a set of hypotheses into a new, potentially smaller, set of
hypotheses. It could probably do better if it could use the
demonstrations to determine the best aggregation. I don't think
Aggregation can happen solely at the leaves (although it may be
worthwhile to aggregate at that level). Rather, I think it may be
necessary to aggregate on higher-evel types.
This might not be a problem though, since the only reasons I can
think of to aggregate at a higher level are due to dependencies
between sibling version spaces.
-}