source: proiecte/Parallel-DT/R8/Src/siftrules.c @ 24

Last change on this file since 24 was 24, checked in by (none), 14 years ago

blabla

File size: 18.4 KB
Line 
1/*************************************************************************/
2/*                                                                       */
3/*      Process sets of rules                                            */
4/*      ---------------------                                            */
5/*                                                                       */
6/*************************************************************************/
7
8
9#include "defns.i"
10#include "types.i"
11#include "extern.i"
12#include "rulex.i"
13
14
15ItemNo  *ClassFreq,     /* ClassFreq[c] = no. items of class c  */
16        *Covered,       /* Covered[i]   = no. included rules that cover item i */
17        *FalsePos,      /* FalsePos[c]  = no. false positives from rules
18                                          selected for class c */
19        *NoRule,        /* NoRule[c]    = no. items covered by no selected rule */
20
21        *Right,         /* Right[r]     = no. correct rule firings */
22        *Wrong;         /* Wrong[r]     = no. incorrect rule firings */
23
24float   *Value,         /* Value[r]     = advantage attributable to rule r or
25                                          realisable if rule r included */
26        SubsetValue,    /* value of best class subset so far */
27        CodeWeight;     /* multiplying factor for rule encodings */
28
29Boolean *RuleIn,        /* RuleIn[r]    = true if rule r included */
30        *Subset,        /* best class subset so far */
31        **Match;        /* Match[r][i]  = true if rule r fires on item i */
32
33RuleNo  *ClassRules;    /* list of all rules for current target class */
34
35ClassNo FocusClass;
36
37
38
39/*************************************************************************/
40/*                                                                       */
41/*  Construct an ordered subset (indexed by RuleIndex) of the current    */
42/*  set of rules                                                         */
43/*                                                                       */
44/*************************************************************************/
45
46
47    ConstructRuleset()
48/*  ----------------  */
49{
50    RuleNo r, OldNRules = NRules;
51
52    /*  Allocate tables  */
53
54    Right = (ItemNo *) calloc(NRules+1, sizeof(ItemNo));
55    Wrong = (ItemNo *) calloc(NRules+1, sizeof(ItemNo));
56
57    Value = (float *) calloc(NRules+1, sizeof(float));
58
59    RuleIn = (Boolean *) calloc(NRules+1, sizeof(Boolean));
60    Subset = (Boolean *) malloc((NRules+1) * sizeof(Boolean));
61
62    ClassRules = (RuleNo *) malloc((NRules+1) * sizeof(RuleNo));
63
64    ClassFreq = (ItemNo *) calloc(MaxClass+1, sizeof(ItemNo));
65
66    Covered = (ItemNo *) calloc(MaxItem+1, sizeof(ItemNo));
67
68    Match = (Boolean **) calloc(NRules+1, sizeof(Boolean *));
69
70    FalsePos = (ItemNo *) calloc(MaxClass+1, sizeof(ItemNo));
71
72    NoRule = (ItemNo *) calloc(MaxClass+1, sizeof(ItemNo));
73
74    ForEach(r, 1, NRules)
75    {
76        Match[r] = (Boolean *) calloc(MaxItem+1, sizeof(Boolean));
77    }
78
79    /*  Cover each class, then order the classes to give an index of rules  */
80
81    InitialiseTables();
82
83    FindRuleCodes();
84    CodeWeight = 0.5;
85
86    ForEach(FocusClass, 0, MaxClass)
87    {
88        CoverClass();
89    }
90
91    MakeIndex();
92    FindDefault();
93
94    /*  Clear  */
95
96    cfree(Value);
97    cfree(RuleIn);
98    cfree(ClassRules);
99    cfree(Subset);
100    cfree(Covered);
101    cfree(FalsePos);
102    cfree(NoRule);
103    ForEach(r, 1, OldNRules)
104    {
105        cfree(Match[r]);
106    }
107    cfree(Match);
108}
109
110
111
112/*************************************************************************/
113/*                                                                       */
114/*              Initialise all tables used in sifting                    */
115/*                                                                       */
116/*************************************************************************/
117
118
119    InitialiseTables()
120/*  ----------------  */
121{
122    ItemNo i;
123    RuleNo r;
124    ClassNo c;
125    float Strength();
126
127    ForEach(r, 1, NRules)
128    {
129        RuleIn[r] = false;
130        Rule[r].Used = Rule[r].Incorrect = 0;
131    }
132
133    ForEach(c, 0, MaxClass)
134    {
135        ClassFreq[c] = 0;
136    }
137
138    ForEach(i, 0, MaxItem)
139    {
140        ClassFreq[Class(Item[i])]++;
141
142        ForEach(r, 1, NRules)
143        {
144            Match[r][i] = Strength(Rule[r], Item[i]) > 0.1;
145
146            if ( Match[r][i] )
147            {
148                Rule[r].Used++;
149                if ( Class(Item[i]) != Rule[r].Rhs ) Rule[r].Incorrect++;
150            }
151        }
152    }
153}
154
155
156
157/*************************************************************************/
158/*                                                                       */
159/*      Select a subset of the rules for class FocusClass                */
160/*                                                                       */
161/*************************************************************************/
162
163
164    CoverClass()
165/*  ----------  */
166{
167    RuleNo r, RuleCount=0;
168    ItemNo i;
169
170    Verbosity(1)
171        printf("\nClass %s\n-----\nAction  Change  Value",
172                ClassName[FocusClass]);
173
174    ForEach(i, 0, MaxItem)
175    {
176        Covered[i] = 0;
177    }
178
179    ForEach(r, 1, NRules)
180    {
181        if ( Rule[r].Rhs == FocusClass )
182        {
183            RuleCount++;
184            ClassRules[RuleCount] = r;
185        }
186    }
187
188    if ( ! RuleCount )
189    {
190        return;
191    }
192
193    SubsetValue = 1E10;
194
195    if ( RuleCount <= 10 )
196    {
197        AllCombinations(RuleCount);
198    }
199    else
200    if ( SIMANNEAL )
201    {
202        SimAnneal(RuleCount);
203    }
204    else
205    {
206        SpotSearch(RuleCount);
207    }
208
209    memcpy(RuleIn, Subset, NRules+1);
210    Verbosity(1) printf("\n\tBest value %.1f\n", SubsetValue);
211}
212
213
214 
215/*************************************************************************/
216/*                                                                       */
217/*    Try all combinations of rules to find best value                   */
218/*                                                                       */
219/*************************************************************************/
220
221
222    AllCombinations(NR)
223/*  ---------------  */
224    RuleNo NR;
225{
226    RuleNo r;
227
228    if ( ! NR )
229    {
230        CalculateValue();
231    }
232    else
233    {
234        r = ClassRules[NR];
235
236        AllCombinations(NR-1);
237
238        AddRule(r);
239        AllCombinations(NR-1);
240
241        DeleteRule(r);
242        Verbosity(1) printf("\n");
243    }
244}
245
246
247
248/*************************************************************************/
249/*                                                                       */
250/*  Find a good subset by simulated annealing                            */
251/*                                                                       */
252/*************************************************************************/
253
254
255    SimAnneal(RuleCount)
256/*  ---------  */
257    RuleNo RuleCount;
258{
259    RuleNo r, OutCount;
260    short ri, Tries;
261    float Temp, Delta;
262    Boolean Changed;
263
264    /*  Keep dropping and adding rules until can't improve  */
265
266    for ( Temp = 1000 ; Temp > 0.001 ; Temp *= 0.95 )
267    {
268        CalculateValue();
269
270        Verbosity(2)
271        {
272            OutCount = 0;
273
274            ForEach(ri, 1, RuleCount)
275            {
276                r = ClassRules[ri];
277
278                if ( ! RuleIn[r] )
279                {
280                    if ( ! (OutCount++ % 3) ) printf("\n\t\t");
281                    printf("%d<%d|%d=%.1f> ", r, Right[r], Wrong[r], Value[r]);
282                }
283            }
284
285            printf("\n\n");
286        }
287
288        Changed = false;
289
290        for ( Tries = 100 ; ! Changed && Tries > 0 ; Tries-- )
291        {
292            /*  Choose a rule to add or delete  */
293
294            ri = RuleCount * Random + 1;
295
296            r = ClassRules[ri];
297
298            Delta = ( RuleIn[r] ? -Value[r] : Value[r] );
299
300            if ( Delta > 0 || Random < exp(Delta / Temp) )
301            {
302                if ( RuleIn[r] )
303                {
304                    DeleteRule(r);
305                }
306                else
307                {
308                    AddRule(r);
309                }
310               
311                Changed = true;
312            }
313        }
314
315        if ( ! Changed ) break;
316    }
317
318    /*  Try to improve best subset so far by hill-climbing  */
319
320    Verbosity(1) printf("Polish: ");
321    memcpy(RuleIn, Subset, NRules+1);
322    HillClimb(RuleCount);
323}
324
325
326
327/*************************************************************************/
328/*                                                                       */
329/*  Find a good subset by repeated greedy search                         */
330/*                                                                       */
331/*************************************************************************/
332
333
334    SpotSearch(RuleCount)
335/*  ----------  */
336    RuleNo RuleCount;
337{
338    RuleNo r;
339    short ri, Trial;
340    float ProbIn;
341
342    ForEach(Trial, 0, 10)
343    {
344        Verbosity(1) printf("\n    Trial %d:", Trial);
345
346        /*  Add rules randomly to the initial subset  */
347
348        ProbIn = Trial / 10.0;
349        ForEach(ri, 1, RuleCount)
350        {
351            r = ClassRules[ri];
352            RuleIn[r] = Random < ProbIn;
353        }
354
355        HillClimb(RuleCount);
356    }
357}
358
359
360
361/*************************************************************************/
362/*                                                                       */
363/*  Improve a subset of rules by adding and deleting rules               */
364/*                                                                       */
365/*************************************************************************/
366
367
368    HillClimb(RuleCount)
369/*  ---------  */
370    RuleNo RuleCount;
371{
372    RuleNo r, Bestr;
373    short ri, OutCount;
374    ItemNo i;
375    float Delta, BestDelta;
376
377    ForEach(i, 0, MaxItem)
378    {
379        Covered[i] = 0;
380    }
381
382    ForEach(ri, 1, RuleCount)
383    {
384        r = ClassRules[ri];
385        if ( RuleIn[r] )
386        {
387            ForEach(i, 0, MaxItem)
388            {
389                if ( Match[r][i] )
390                {
391                    Covered[i]++;
392                }
393            }
394        }
395    }
396   
397    /*  Add or drop rule with greatest reduction in coding cost  */
398
399    while ( true )
400    {
401        CalculateValue();
402
403        Verbosity(2)
404        {
405            OutCount = 0;
406
407            ForEach(ri, 1, RuleCount)
408            {
409                r = ClassRules[ri];
410
411                if ( ! RuleIn[r] )
412                {
413                    if ( ! (OutCount++ % 3) ) printf("\n\t\t");
414                    printf("%d<%d|%d=%.1f> ", r, Right[r], Wrong[r], Value[r]);
415                }
416            }
417
418            printf("\n\n");
419        }
420
421        Bestr = BestDelta = 0;
422        ForEach(ri, 1, RuleCount)
423        {
424            r = ClassRules[ri];
425            Delta = ( RuleIn[r] ? -Value[r] : Value[r] );
426            if ( Delta > BestDelta )
427            {
428                Bestr = r;
429                BestDelta = Delta;
430            }
431        }
432        if ( ! Bestr ) break;
433
434        if ( RuleIn[Bestr] )
435        {
436            DeleteRule(Bestr);
437        }
438        else
439        {
440            AddRule(Bestr);
441        }
442    }
443}
444
445
446
447/*************************************************************************/
448/*                                                                       */
449/*  Find the number of correct and incorrect rule firings for rules      */
450/*  for class FocusClass and hence determine the Value of the rules.     */
451/*  If best so far, remember.                                            */
452/*                                                                       */
453/*************************************************************************/
454
455
456    CalculateValue()
457/*  --------------  */
458{
459    RuleNo r, Selected=0, InCount;
460    ItemNo i, Times, FPos=0, FNeg=0, SumCover=0;
461    float BaseBits, RuleBits=0, NewBits, ExceptionBits();
462    ClassNo ThisClass;
463    Boolean *RuleMatch;
464
465    ForEach(i, 0, MaxItem)
466    {
467        ThisClass = Class(Item[i]);
468
469        if ( Covered[i] )
470        {
471            SumCover++;
472            if( ThisClass != FocusClass ) FPos++;
473        }
474        else
475        if ( ThisClass == FocusClass )
476        {
477            FNeg++;
478        }
479    }
480
481    ForEach(r, 1, NRules)
482    {
483        if ( Rule[r].Rhs == FocusClass )
484        {
485            Right[r] = Wrong[r] = 0;
486
487            if ( RuleIn[r] )
488            {
489                RuleBits += Rule[r].Bits;
490                Selected++;
491            }
492
493            RuleMatch = Match[r];
494
495            ForEach(i, 0, MaxItem)
496            {
497                if ( RuleMatch[i] &&
498                   ( ! (Times = Covered[i]) || Times == 1 && RuleIn[r] ) )
499                {
500                    if ( Class(Item[i]) == FocusClass )
501                    {
502                        Right[r]++;
503                    }
504                    else
505                    {
506                        Wrong[r]++;
507                    }
508                }
509            }
510        }
511    }
512
513    RuleBits -= LogFact[Selected];      /* allow for reordering of rules */
514
515    BaseBits = CodeWeight * RuleBits + ExceptionBits(SumCover, FPos, FNeg);
516
517    /*  From the Right and Wrong of each rule, calculate its value  */
518
519    Verbosity(1)
520    {
521        printf("\t");
522        InCount = -1;
523    }
524
525    ForEach(r, 1, NRules)
526    {
527        if ( Rule[r].Rhs == FocusClass )
528        {
529            if ( RuleIn[r] )
530            {
531                NewBits = ExceptionBits(SumCover-Right[r]-Wrong[r],
532                                        FPos-Wrong[r], FNeg+Right[r]) +
533                          CodeWeight *
534                              (RuleBits - Rule[r].Bits + LogItemNo[Selected]);
535                Value[r] = NewBits - BaseBits;
536            }
537            else
538            {
539                NewBits = ExceptionBits(SumCover+Right[r]+Wrong[r],
540                                        FPos+Wrong[r], FNeg-Right[r]) +
541                          CodeWeight *
542                              (RuleBits + Rule[r].Bits - LogItemNo[Selected+1]);
543                Value[r] = BaseBits - NewBits;
544            }
545
546            Verbosity(1)
547            {
548                if ( RuleIn[r] )
549                {
550                    if ( ++InCount && ! (InCount % 3) ) printf("\n\t\t");
551                    printf("%d[%d|%d=%.1f]  ", r, Right[r], Wrong[r], Value[r]);
552                }
553            }
554        }
555    }
556
557    Verbosity(1)
558    {
559        printf("\n\t\t%d rules, %d firings: F+=%d, F-=%d, %.1f bits (rules=%.1f)\n",
560                Selected, SumCover, FPos, FNeg, BaseBits, RuleBits);
561    }
562
563    if ( BaseBits < SubsetValue )
564    {
565        SubsetValue = BaseBits;
566        memcpy(Subset, RuleIn, NRules+1);
567    }
568}
569
570
571
572/*************************************************************************/
573/*                                                                       */
574/*  Add rule r to the set of included rules and increase the number of   */
575/*  rules covering each of the items that fire the rule                  */
576/*                                                                       */
577/*************************************************************************/
578
579
580    AddRule(r)
581/*  -------  */
582    RuleNo r;
583{
584    ItemNo i;
585
586    RuleIn[r] = true;
587
588    ForEach(i, 0, MaxItem)
589    {
590        if ( Match[r][i] )
591        {
592            Covered[i]++;
593        }
594    }
595
596    Verbosity(1) printf("%5d+  %6.1f", r, Value[r]);
597}
598
599
600
601/*************************************************************************/
602/*                                                                       */
603/*  Delete rule r from the included rules and decrease the number of     */
604/*  rules covering each of the items covered by the rule                 */
605/*                                                                       */
606/*************************************************************************/
607
608
609    DeleteRule(r)
610/*  ----------  */
611    RuleNo r;
612{
613    ItemNo i;
614
615    RuleIn[r] = false;
616
617    ForEach(i, 0, MaxItem)
618    {
619        if ( Match[r][i] )
620        {
621            Covered[i]--;
622        }
623    }
624
625    Verbosity(1) printf("%5d-  %6.1f", r, -Value[r]);
626}
627
628
629
630/*************************************************************************/
631/*                                                                       */
632/*  Make an index of included rules in RuleIndex.  Select first those    */
633/*  classes whose rules have the fewest false positives.  Within a       */
634/*  class, put rules with higher accuracy ahead.                         */
635/*                                                                       */
636/*************************************************************************/
637
638
639    MakeIndex()
640/*  ---------  */
641{
642    ClassNo c, BestC, Pass;
643    RuleNo r, BestR, NewNRules = 0;
644    ItemNo i;
645    Boolean *Included;
646
647    Included = (Boolean *) calloc(MaxClass+1, sizeof(Boolean));
648    RuleIndex = (RuleNo *) calloc(NRules+1, sizeof(RuleNo));
649
650    Verbosity(1) printf("\nFalsePos  Class\n");
651
652    ForEach(i, 0, MaxItem)
653    {
654        Covered[i] = 0;
655    }
656
657    /*  Select the best class to put next  */
658
659    ForEach(Pass, 0, MaxClass)
660    {
661        ForEach(c, 0, MaxClass)
662        {
663            if ( Included[c] ) continue;
664
665            FalsePos[c] = 0;
666
667            ForEach(i, 0, MaxItem)
668            {
669                if ( Covered[i] || Class(Item[i]) == c ) continue;
670
671                ForEach(r, 1, NRules)
672                {
673                    if ( Rule[r].Rhs == c && RuleIn[r] && Match[r][i] )
674                    {
675                        FalsePos[c]++;
676                        break;
677                    }
678                }
679            }
680        }
681
682        BestC = -1;
683        ForEach(c, 0, MaxClass)
684        {
685            if ( ! Included[c] &&
686                 ( BestC < 0 || FalsePos[c] < FalsePos[BestC] ) )
687            {
688                BestC = c;
689            }
690        }
691        Included[BestC] = true;
692
693        Verbosity(1)
694            printf("%5d     %s\n", FalsePos[BestC], ClassName[BestC]);
695
696        /*  Now grab the rules for this class  */
697
698        do
699        {
700            BestR = 0;
701
702            /*  Find the best rule to put next  */
703
704            ForEach(r, 1, NRules)
705            {
706                if ( RuleIn[r] && Rule[r].Rhs == BestC &&
707                     ( ! BestR || Rule[r].Error < Rule[BestR].Error ) )
708                {
709                    BestR = r;
710                }
711            }
712
713            if ( BestR )
714            {
715                RuleIndex[++NewNRules] = BestR;
716                RuleIn[BestR] = false;
717
718                ForEach(i, 0, MaxItem)
719                {
720                    Covered[i] |= Match[BestR][i];
721                }
722            }
723        } while ( BestR );
724    }
725
726    NRules = NewNRules;
727    cfree(Included);
728}
729
730
731
732/*************************************************************************/
733/*                                                                       */
734/*  Find the default class as the one with most items not covered by     */
735/*  any rule.  Resolve ties in favour of more frequent classes.          */
736/*  (Note: Covered has been set by MakeIndex.)                           */
737/*                                                                       */
738/*************************************************************************/
739
740
741    FindDefault()
742/*  -----------  */
743{
744    ClassNo c;
745    ItemNo i;
746
747    /*  Determine uncovered items  */
748
749    ForEach(c, 0, MaxClass)
750    {
751        NoRule[c] = 0;
752    }
753
754    ForEach(i, 0, MaxItem)
755    {
756        if ( ! Covered[i] )
757        {
758            NoRule[Class(Item[i])]++;
759        }
760    }
761
762    Verbosity(1)
763    {
764        printf("\nItems: Uncovered   Class\n");
765        ForEach(c, 0, MaxClass)
766        {
767            printf("%5d %7d      %s\n", ClassFreq[c], NoRule[c], ClassName[c]);
768        }
769        printf("\n");
770    }
771
772    DefaultClass = 0;
773    ForEach(c, 1, MaxClass)
774    {
775        if ( NoRule[c] > NoRule[DefaultClass] ||
776             NoRule[c] == NoRule[DefaultClass] &&
777             ClassFreq[c] > ClassFreq[DefaultClass] )
778        {
779            DefaultClass = c;
780        }
781    }
782}
783
784
785
786/*************************************************************************/
787/*                                                                       */
788/*  Given a rule and a case, determine the strength with which we can    */
789/*  conclude that the case belongs to the class specified by the rule's  */
790/*  right-hand side.                                                     */
791/*                                                                       */
792/*  If the case doesn't satisfy all the conditions of the rule,          */
793/*  then this is 0.                                                      */
794/*                                                                       */
795/*************************************************************************/
796
797
798float Strength(ThisRule, Case)
799/*    --------  */
800    PR ThisRule;
801    Description Case;
802{
803    short d;
804    Boolean Satisfies();
805
806    if ( ThisRule.Error > 0.7 ) return 0.0;
807
808    ForEach(d, 1, ThisRule.Size)
809    {
810        if ( ! Satisfies(Case, ThisRule.Lhs[d]) )
811        {
812            return 0.0;
813        }
814    }
815
816    return ( 1 - ThisRule.Error );
817}
818
819
820
821/*************************************************************************/
822/*                                                                       */
823/*  Determine the number of bits to encode exceptions.  Unlike the       */
824/*  version in the book, this uses an approximate encoding that          */
825/*  penalizes unbalanced numbers of false positives and false negatives  */
826/*  as described in my paper at 1995 International Machine Learning      */
827/*  Conference (published by Morgan Kaufmann).                           */
828/*                                                                       */
829/*************************************************************************/
830
831
832float Biased(N, E, ExpE)
833/*    ------  */
834    int N, E;
835    float ExpE;
836{
837    float Rate;
838
839    if ( ExpE <= 1E-6 )
840    {
841        return ( E == 0 ? 0.0 : 1E6 );
842    }
843    else
844    if ( ExpE >= N-1E-6 )
845    {
846        return ( E == N ? 0.0 : 1E6 );
847    }
848
849    Rate = ExpE / N;
850    return -E * Log(Rate) - (N-E) * Log(1-Rate);
851}
852
853
854
855float ExceptionBits(Fires, FP, FN)
856/*    -------------  */
857    int Fires, FP, FN;
858{
859    if ( Fires > 0.5 * (MaxItem+1) )
860    {
861        return Log(MaxItem+1)
862                + Biased(Fires, FP, 0.5 * (FP+FN))
863                + Biased(MaxItem+1-Fires, FN, (float) FN);
864    }
865    else
866    {
867        return Log(MaxItem+1)
868                + Biased(Fires, FP, (float) FP)
869                + Biased(MaxItem+1-Fires, FN, 0.5 * (FP+FN));
870    }
871}
872
873
874
875/*************************************************************************/
876/*                                                                       */
877/*  Find encoding lengths for all rules                                  */
878/*                                                                       */
879/*************************************************************************/
880
881
882    FindRuleCodes()
883/*  -------------  */
884{
885    RuleNo r;
886    short d, NCond;
887    float Bits, CondBits();
888
889    ForEach(r, 1, NRules)
890    {
891        NCond = Rule[r].Size;
892        Bits = 0;
893
894        ForEach(d, 1, NCond)
895        {
896            Bits += CondBits(Rule[r].Lhs[d]);
897        }
898
899        /*  Must encode the number of conditions, but credit the total
900            encoding by the ways conditions can be reordered  */
901
902        Rule[r].Bits = Bits + LogItemNo[NCond] - LogFact[NCond];
903    }
904}
905
906
907
908/*************************************************************************/
909/*                                                                       */
910/*  Determine the number of bits required to encode a condition          */
911/*                                                                       */
912/*************************************************************************/
913
914
915float CondBits(C)
916/*    --------  */
917    Condition C;
918{
919    Test t;
920    Attribute a;
921
922    t = C->CondTest;
923    a = t->Tested;
924
925    switch ( t->NodeType )
926    {
927        case BrDiscr:           /* test of discrete attribute */
928        case ThreshContin:      /* test of continuous attribute */
929
930            return AttTestBits/REDUNDANCY + BranchBits[a];
931
932        case BrSubset:          /* subset test on discrete attribute  */
933
934            return AttTestBits/REDUNDANCY + MaxAttVal[a];
935    } 
936}
Note: See TracBrowser for help on using the repository browser.