see5-installer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +11 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +10 -0
- data/README.md +29 -0
- data/Rakefile +12 -0
- data/ext/c5.0/Makefile +86 -0
- data/ext/c5.0/attwinnow.c +394 -0
- data/ext/c5.0/c50.c +330 -0
- data/ext/c5.0/classify.c +700 -0
- data/ext/c5.0/confmat.c +195 -0
- data/ext/c5.0/construct.c +853 -0
- data/ext/c5.0/contin.c +613 -0
- data/ext/c5.0/defns.i +788 -0
- data/ext/c5.0/discr.c +307 -0
- data/ext/c5.0/extern.i +170 -0
- data/ext/c5.0/formrules.c +720 -0
- data/ext/c5.0/formtree.c +1158 -0
- data/ext/c5.0/getdata.c +521 -0
- data/ext/c5.0/getnames.c +733 -0
- data/ext/c5.0/global.c +211 -0
- data/ext/c5.0/gpl.txt +674 -0
- data/ext/c5.0/implicitatt.c +1112 -0
- data/ext/c5.0/info.c +146 -0
- data/ext/c5.0/mcost.c +138 -0
- data/ext/c5.0/modelfiles.c +952 -0
- data/ext/c5.0/p-thresh.c +313 -0
- data/ext/c5.0/prune.c +1069 -0
- data/ext/c5.0/report.c +345 -0
- data/ext/c5.0/rules.c +579 -0
- data/ext/c5.0/ruletree.c +398 -0
- data/ext/c5.0/siftrules.c +1285 -0
- data/ext/c5.0/sort.c +156 -0
- data/ext/c5.0/subset.c +599 -0
- data/ext/c5.0/text.i +223 -0
- data/ext/c5.0/trees.c +740 -0
- data/ext/c5.0/update.c +129 -0
- data/ext/c5.0/utility.c +1146 -0
- data/ext/c5.0/xval +150 -0
- data/ext/c5.0/xval.c +402 -0
- data/ext/gritbot/Makefile +98 -0
- data/ext/gritbot/check.c +1110 -0
- data/ext/gritbot/cluster.c +342 -0
- data/ext/gritbot/common.c +1269 -0
- data/ext/gritbot/continatt.c +412 -0
- data/ext/gritbot/defns.i +623 -0
- data/ext/gritbot/discratt.c +459 -0
- data/ext/gritbot/extern.i +101 -0
- data/ext/gritbot/getdata.c +329 -0
- data/ext/gritbot/getnames.c +573 -0
- data/ext/gritbot/global.c +104 -0
- data/ext/gritbot/gpl.txt +674 -0
- data/ext/gritbot/gritbot.c +295 -0
- data/ext/gritbot/implicitatt.c +1108 -0
- data/ext/gritbot/inspect.c +794 -0
- data/ext/gritbot/modelfiles.c +687 -0
- data/ext/gritbot/outlier.c +415 -0
- data/ext/gritbot/sort.c +130 -0
- data/ext/gritbot/text.i +159 -0
- data/ext/gritbot/update.c +126 -0
- data/ext/gritbot/utility.c +1029 -0
- data/ext/see5-installer/extconf.rb +25 -0
- data/lib/see5/installer.rb +10 -0
- data/lib/see5/installer/version.rb +7 -0
- data/see5-installer.gemspec +30 -0
- metadata +115 -0
data/ext/c5.0/ruletree.c
ADDED
@@ -0,0 +1,398 @@
|
|
1
|
+
/*************************************************************************/
|
2
|
+
/* */
|
3
|
+
/* Copyright 2010 Rulequest Research Pty Ltd. */
|
4
|
+
/* */
|
5
|
+
/* This file is part of C5.0 GPL Edition, a single-threaded version */
|
6
|
+
/* of C5.0 release 2.07. */
|
7
|
+
/* */
|
8
|
+
/* C5.0 GPL Edition is free software: you can redistribute it and/or */
|
9
|
+
/* modify it under the terms of the GNU General Public License as */
|
10
|
+
/* published by the Free Software Foundation, either version 3 of the */
|
11
|
+
/* License, or (at your option) any later version. */
|
12
|
+
/* */
|
13
|
+
/* C5.0 GPL Edition is distributed in the hope that it will be useful, */
|
14
|
+
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
15
|
+
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
|
16
|
+
/* General Public License for more details. */
|
17
|
+
/* */
|
18
|
+
/* You should have received a copy of the GNU General Public License */
|
19
|
+
/* (gpl.txt) along with C5.0 GPL Edition. If not, see */
|
20
|
+
/* */
|
21
|
+
/* <http://www.gnu.org/licenses/>. */
|
22
|
+
/* */
|
23
|
+
/*************************************************************************/
|
24
|
+
|
25
|
+
|
26
|
+
|
27
|
+
/*************************************************************************/
|
28
|
+
/* */
|
29
|
+
/* Routines for building a rule tree for faster classification. */
|
30
|
+
/* A ruletree node consists of */
|
31
|
+
/* * a list of rules satisfied at this node, terminated by 0 */
|
32
|
+
/* * a new test */
|
33
|
+
/* * subtrees for each outcome (with branch 0 dealing with those */
|
34
|
+
/* rules that do not contain the new test) */
|
35
|
+
/* */
|
36
|
+
/*************************************************************************/
|
37
|
+
|
38
|
+
|
39
|
+
#include "defns.i"
|
40
|
+
#include "extern.i"
|
41
|
+
|
42
|
+
Condition *Test=Nil; /* tests that appear in ruleset */
|
43
|
+
int NTest, /* number of distinct tests */
|
44
|
+
TestSpace, /* space allocated for tests */
|
45
|
+
*TestOccur, /* frequency of test occurrence in rules */
|
46
|
+
*RuleCondOK; /* conditions satisfied by rule */
|
47
|
+
|
48
|
+
Boolean *TestUsed; /* used in parent nodes */
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
/*************************************************************************/
|
53
|
+
/* */
|
54
|
+
/* Construct ruletree for ruleset RS */
|
55
|
+
/* */
|
56
|
+
/*************************************************************************/
|
57
|
+
|
58
|
+
|
59
|
+
void ConstructRuleTree(CRuleSet RS)
|
60
|
+
/* ----------------- */
|
61
|
+
{
|
62
|
+
int r, c;
|
63
|
+
RuleNo *All;
|
64
|
+
|
65
|
+
Test = Alloc((TestSpace = 1000), Condition);
|
66
|
+
NTest = 0;
|
67
|
+
|
68
|
+
All = Alloc(RS->SNRules, RuleNo);
|
69
|
+
ForEach(r, 1, RS->SNRules)
|
70
|
+
{
|
71
|
+
All[r-1] = r;
|
72
|
+
|
73
|
+
ForEach(c, 1, RS->SRule[r]->Size)
|
74
|
+
{
|
75
|
+
SetTestIndex(RS->SRule[r]->Lhs[c]);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
TestOccur = Alloc(NTest, int);
|
80
|
+
TestUsed = AllocZero(NTest, Boolean);
|
81
|
+
|
82
|
+
RuleCondOK = AllocZero(RS->SNRules+1, int);
|
83
|
+
|
84
|
+
RS->RT = GrowRT(All, RS->SNRules, RS->SRule);
|
85
|
+
|
86
|
+
Free(All);
|
87
|
+
Free(Test);
|
88
|
+
Free(TestUsed);
|
89
|
+
Free(TestOccur);
|
90
|
+
Free(RuleCondOK);
|
91
|
+
}
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
/*************************************************************************/
|
96
|
+
/* */
|
97
|
+
/* Set test number for a condition. If no existing test matches, */
|
98
|
+
/* add new test to Test[] */
|
99
|
+
/* */
|
100
|
+
/*************************************************************************/
|
101
|
+
|
102
|
+
|
103
|
+
void SetTestIndex(Condition C)
|
104
|
+
/* ------------ */
|
105
|
+
{
|
106
|
+
int t;
|
107
|
+
Condition CC;
|
108
|
+
Attribute Att;
|
109
|
+
|
110
|
+
Att = C->Tested;
|
111
|
+
|
112
|
+
ForEach(t, 0, NTest-1)
|
113
|
+
{
|
114
|
+
CC = Test[t];
|
115
|
+
if ( CC->Tested != Att || CC->NodeType != C->NodeType ) continue;
|
116
|
+
|
117
|
+
switch ( C->NodeType )
|
118
|
+
{
|
119
|
+
case BrDiscr:
|
120
|
+
C->TestI = t;
|
121
|
+
return;
|
122
|
+
|
123
|
+
case BrSubset:
|
124
|
+
if ( ! memcmp(C->Subset, CC->Subset, (MaxAttVal[Att]>>3)+1) )
|
125
|
+
{
|
126
|
+
C->TestI = t;
|
127
|
+
return;
|
128
|
+
}
|
129
|
+
break;
|
130
|
+
|
131
|
+
case BrThresh:
|
132
|
+
if ( C->TestValue == 1 && CC->TestValue == 1 ||
|
133
|
+
( C->TestValue != 1 && CC->TestValue != 1 &&
|
134
|
+
C->Cut == CC->Cut ) )
|
135
|
+
{
|
136
|
+
C->TestI = t;
|
137
|
+
return;
|
138
|
+
}
|
139
|
+
break;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
/* New test -- make sure have enough space */
|
144
|
+
|
145
|
+
if ( NTest >= TestSpace )
|
146
|
+
{
|
147
|
+
Realloc(Test, (TestSpace += 1000), Condition);
|
148
|
+
}
|
149
|
+
|
150
|
+
Test[NTest] = C;
|
151
|
+
C->TestI = NTest++;
|
152
|
+
}
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
/*************************************************************************/
|
157
|
+
/* */
|
158
|
+
/* Construct ruletree for rules RR */
|
159
|
+
/* */
|
160
|
+
/*************************************************************************/
|
161
|
+
|
162
|
+
|
163
|
+
RuleTree GrowRT(RuleNo *RR, int RRN, CRule *Rule)
|
164
|
+
/* ------ */
|
165
|
+
{
|
166
|
+
RuleTree Node;
|
167
|
+
RuleNo r, *LR;
|
168
|
+
int FP=0, ri, TI, *Expect, LRN;
|
169
|
+
DiscrValue v;
|
170
|
+
|
171
|
+
if ( ! RRN ) return Nil;
|
172
|
+
|
173
|
+
Node = AllocZero(1, RuleTreeRec);
|
174
|
+
|
175
|
+
/* Record and swap to front any rules that are satisfied */
|
176
|
+
|
177
|
+
ForEach(ri, 0, RRN-1)
|
178
|
+
{
|
179
|
+
r = RR[ri];
|
180
|
+
|
181
|
+
if ( RuleCondOK[r] == Rule[r]->Size )
|
182
|
+
{
|
183
|
+
RR[ri] = RR[FP];
|
184
|
+
RR[FP] = r;
|
185
|
+
FP++;
|
186
|
+
}
|
187
|
+
}
|
188
|
+
|
189
|
+
if ( FP )
|
190
|
+
{
|
191
|
+
Node->Fire = Alloc(FP+1, RuleNo);
|
192
|
+
memcpy(Node->Fire, RR, FP * sizeof(RuleNo));
|
193
|
+
Node->Fire[FP] = 0;
|
194
|
+
RR += FP;
|
195
|
+
RRN -= FP;
|
196
|
+
}
|
197
|
+
else
|
198
|
+
{
|
199
|
+
Node->Fire = Nil;
|
200
|
+
}
|
201
|
+
|
202
|
+
if ( ! RRN ) return Node;
|
203
|
+
|
204
|
+
/* Choose test for this node */
|
205
|
+
|
206
|
+
TI = SelectTest(RR, RRN, Rule);
|
207
|
+
TestUsed[TI] = true;
|
208
|
+
|
209
|
+
Node->CondTest = Test[TI];
|
210
|
+
|
211
|
+
/* Find the desired outcome for each rule */
|
212
|
+
|
213
|
+
Expect = Alloc(RRN, int);
|
214
|
+
ForEach(ri, 0, RRN-1)
|
215
|
+
{
|
216
|
+
Expect[ri] = DesiredOutcome(Rule[RR[ri]], TI);
|
217
|
+
}
|
218
|
+
|
219
|
+
/* Now construct individual branches. Rules that do not reference
|
220
|
+
the selected test go down branch 0; at classification time,
|
221
|
+
any case with an unknown outcome for the selected test also
|
222
|
+
goes to branch 0. */
|
223
|
+
|
224
|
+
Node->Forks =
|
225
|
+
( Test[TI]->NodeType == BrDiscr ? MaxAttVal[Test[TI]->Tested] :
|
226
|
+
Test[TI]->NodeType == BrSubset ? 1 : 3 );
|
227
|
+
|
228
|
+
Node->Branch = Alloc(Node->Forks+1, RuleTree);
|
229
|
+
|
230
|
+
LR = Alloc(RRN, RuleNo);
|
231
|
+
ForEach(v, 0, Node->Forks)
|
232
|
+
{
|
233
|
+
/* Extract rules with outcome v and increment conditions satisfied,
|
234
|
+
if relevant */
|
235
|
+
|
236
|
+
LRN = 0;
|
237
|
+
ForEach(ri, 0, RRN-1)
|
238
|
+
{
|
239
|
+
if ( abs(Expect[ri]) == v )
|
240
|
+
{
|
241
|
+
LR[LRN++] = RR[ri];
|
242
|
+
|
243
|
+
if ( Expect[ri] > 0 ) RuleCondOK[RR[ri]]++;
|
244
|
+
}
|
245
|
+
}
|
246
|
+
|
247
|
+
/* LR now contains rules with outcome v */
|
248
|
+
|
249
|
+
Node->Branch[v] = GrowRT(LR, LRN, Rule);
|
250
|
+
|
251
|
+
if ( v )
|
252
|
+
{
|
253
|
+
/* Restore conditions satisfied */
|
254
|
+
|
255
|
+
ForEach(ri, 0, LRN-1)
|
256
|
+
{
|
257
|
+
RuleCondOK[LR[ri]]--;
|
258
|
+
}
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
TestUsed[TI] = false;
|
263
|
+
|
264
|
+
/* Free local storage */
|
265
|
+
|
266
|
+
Free(LR);
|
267
|
+
Free(Expect);
|
268
|
+
|
269
|
+
return Node;
|
270
|
+
}
|
271
|
+
|
272
|
+
|
273
|
+
|
274
|
+
/*************************************************************************/
|
275
|
+
/* */
|
276
|
+
/* Check whether rule uses Test[TI]. */
|
277
|
+
/* Return 0 (no) or test outcome required for rule */
|
278
|
+
/* */
|
279
|
+
/*************************************************************************/
|
280
|
+
|
281
|
+
|
282
|
+
int DesiredOutcome(CRule R, int TI)
|
283
|
+
/* -------------- */
|
284
|
+
{
|
285
|
+
int c;
|
286
|
+
Boolean ContinTest;
|
287
|
+
|
288
|
+
ContinTest = Continuous(Test[TI]->Tested); /* test of continuous att */
|
289
|
+
|
290
|
+
ForEach(c, 1, R->Size)
|
291
|
+
{
|
292
|
+
if ( R->Lhs[c]->TestI == TI )
|
293
|
+
{
|
294
|
+
return R->Lhs[c]->TestValue;
|
295
|
+
}
|
296
|
+
|
297
|
+
/* If this test references the same continuous attribute but
|
298
|
+
with a different threshold, may be able to exploit outcome:
|
299
|
+
-2 means "rule can only be matched down branch 2"
|
300
|
+
-3 means "rule can only be matched down branch 3" */
|
301
|
+
|
302
|
+
if ( ContinTest && Test[TI]->Tested == R->Lhs[c]->Tested )
|
303
|
+
{
|
304
|
+
switch ( R->Lhs[c]->TestValue )
|
305
|
+
{
|
306
|
+
case 1:
|
307
|
+
return 1;
|
308
|
+
|
309
|
+
case 2:
|
310
|
+
if ( R->Lhs[c]->Cut < Test[TI]->Cut ) return -2;
|
311
|
+
break;
|
312
|
+
|
313
|
+
case 3:
|
314
|
+
if ( R->Lhs[c]->Cut > Test[TI]->Cut ) return -3;
|
315
|
+
}
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
return 0;
|
320
|
+
}
|
321
|
+
|
322
|
+
|
323
|
+
|
324
|
+
/*************************************************************************/
|
325
|
+
/* */
|
326
|
+
/* Select most frequently-occurring test to partition rules in RR */
|
327
|
+
/* */
|
328
|
+
/*************************************************************************/
|
329
|
+
|
330
|
+
|
331
|
+
int SelectTest(RuleNo *RR, int RRN, CRule *Rule)
|
332
|
+
/* ---------- */
|
333
|
+
{
|
334
|
+
int c, cc, ri;
|
335
|
+
RuleNo r;
|
336
|
+
|
337
|
+
/* Count test occurrences */
|
338
|
+
|
339
|
+
ForEach(c, 0, NTest-1)
|
340
|
+
{
|
341
|
+
TestOccur[c] = 0;
|
342
|
+
}
|
343
|
+
|
344
|
+
ForEach(ri, 0, RRN-1)
|
345
|
+
{
|
346
|
+
r = RR[ri];
|
347
|
+
|
348
|
+
ForEach(c, 1, Rule[r]->Size)
|
349
|
+
{
|
350
|
+
TestOccur[Rule[r]->Lhs[c]->TestI]++;
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
354
|
+
/* Find most frequently-occurring test */
|
355
|
+
|
356
|
+
cc = -1;
|
357
|
+
ForEach(c, 0, NTest-1)
|
358
|
+
{
|
359
|
+
if ( ! TestUsed[c] && ( cc < 0 || TestOccur[c] > TestOccur[cc] ) )
|
360
|
+
{
|
361
|
+
cc = c;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
|
365
|
+
return cc;
|
366
|
+
}
|
367
|
+
|
368
|
+
|
369
|
+
|
370
|
+
/*************************************************************************/
|
371
|
+
/* */
|
372
|
+
/* Free ruletree */
|
373
|
+
/* */
|
374
|
+
/*************************************************************************/
|
375
|
+
|
376
|
+
|
377
|
+
void FreeRuleTree(RuleTree RT)
|
378
|
+
/* ------------ */
|
379
|
+
{
|
380
|
+
int b;
|
381
|
+
|
382
|
+
if ( ! RT ) return;
|
383
|
+
|
384
|
+
if ( RT->Branch )
|
385
|
+
{
|
386
|
+
ForEach(b, 0, RT->Forks )
|
387
|
+
{
|
388
|
+
FreeRuleTree(RT->Branch[b]);
|
389
|
+
}
|
390
|
+
Free(RT->Branch);
|
391
|
+
}
|
392
|
+
|
393
|
+
/* Don't free RT->Cond since this is just a pointer to a condition
|
394
|
+
in one of the rules */
|
395
|
+
|
396
|
+
FreeUnlessNil(RT->Fire);
|
397
|
+
Free(RT);
|
398
|
+
}
|
@@ -0,0 +1,1285 @@
|
|
1
|
+
/*************************************************************************/
|
2
|
+
/* */
|
3
|
+
/* Copyright 2010 Rulequest Research Pty Ltd. */
|
4
|
+
/* Author: Ross Quinlan (quinlan@rulequest.com) [Rev Jan 2016] */
|
5
|
+
/* */
|
6
|
+
/* This file is part of C5.0 GPL Edition, a single-threaded version */
|
7
|
+
/* of C5.0 release 2.07. */
|
8
|
+
/* */
|
9
|
+
/* C5.0 GPL Edition is free software: you can redistribute it and/or */
|
10
|
+
/* modify it under the terms of the GNU General Public License as */
|
11
|
+
/* published by the Free Software Foundation, either version 3 of the */
|
12
|
+
/* License, or (at your option) any later version. */
|
13
|
+
/* */
|
14
|
+
/* C5.0 GPL Edition is distributed in the hope that it will be useful, */
|
15
|
+
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
16
|
+
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
|
17
|
+
/* General Public License for more details. */
|
18
|
+
/* */
|
19
|
+
/* You should have received a copy of the GNU General Public License */
|
20
|
+
/* (gpl.txt) along with C5.0 GPL Edition. If not, see */
|
21
|
+
/* */
|
22
|
+
/* <http://www.gnu.org/licenses/>. */
|
23
|
+
/* */
|
24
|
+
/*************************************************************************/
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
/*************************************************************************/
|
29
|
+
/* */
|
30
|
+
/* Find a good subset of a set of rules */
|
31
|
+
/* ------------------------------------ */
|
32
|
+
/* */
|
33
|
+
/*************************************************************************/
|
34
|
+
|
35
|
+
|
36
|
+
#include "defns.i"
|
37
|
+
#include "extern.i"
|
38
|
+
|
39
|
+
|
40
|
+
float *DeltaErrs=Nil, /* DeltaErrs[r] = change attributable to rule r or
|
41
|
+
realisable if rule r included */
|
42
|
+
*Bits=Nil, /* Bits[r] = bits to encode rule r */
|
43
|
+
BitsErr, /* BitsErr = bits to label prediction as error */
|
44
|
+
BitsOK; /* BitsOK = bits to label prediction as ok */
|
45
|
+
|
46
|
+
int **TotVote=Nil; /* TotVote[i][c] = case i's votes for class c */
|
47
|
+
|
48
|
+
ClassNo *TopClass=Nil, /* TopClass[i] = class with highest vote */
|
49
|
+
*AltClass=Nil; /* AltClass[i] = class with second highest vote */
|
50
|
+
|
51
|
+
Boolean *RuleIn=Nil, /* RuleIn[r] = rule r included */
|
52
|
+
*Covered=Nil; /* Covered[i] = case i covered by rule(s) */
|
53
|
+
|
54
|
+
Byte *CovByBlock=Nil,/* holds entries for inverse of Fires */
|
55
|
+
**CovByPtr=Nil; /* next entry for CovBy[i] */
|
56
|
+
|
57
|
+
RuleNo *LastCovBy=Nil; /* Last rule covering case i */
|
58
|
+
|
59
|
+
|
60
|
+
/*************************************************************************/
|
61
|
+
/* */
|
62
|
+
/* Main rule selection routine. */
|
63
|
+
/* 1. Form initial theory */
|
64
|
+
/* 2. Hillclimb in MDL space */
|
65
|
+
/* */
|
66
|
+
/*************************************************************************/
|
67
|
+
|
68
|
+
|
69
|
+
void SiftRules(float EstErrRate)
|
70
|
+
/* --------- */
|
71
|
+
{
|
72
|
+
RuleNo r;
|
73
|
+
int d, *bp;
|
74
|
+
CRule R;
|
75
|
+
float CodeLength;
|
76
|
+
CaseNo i;
|
77
|
+
|
78
|
+
NotifyStage(SIFTRULES);
|
79
|
+
Progress(-(float) NRules);
|
80
|
+
|
81
|
+
/* Determine inverse of Fires in CovBy, CovByPtr, CovByBlock */
|
82
|
+
|
83
|
+
InvertFires();
|
84
|
+
|
85
|
+
/* Clean up any subsets in conditions by removing values that do
|
86
|
+
not appear in the covered cases */
|
87
|
+
|
88
|
+
if ( SUBSET )
|
89
|
+
{
|
90
|
+
PruneSubsets();
|
91
|
+
}
|
92
|
+
|
93
|
+
Covered = Alloc(MaxCase+1, Boolean);
|
94
|
+
RuleIn = AllocZero(NRules+1, Boolean);
|
95
|
+
|
96
|
+
/* Set initial theory */
|
97
|
+
|
98
|
+
SetInitialTheory();
|
99
|
+
|
100
|
+
Bits = Alloc(NRules+1, float);
|
101
|
+
|
102
|
+
/* Calculate the number of bits associated with attribute tests;
|
103
|
+
this is not repeated in boosting, composite rulesets etc */
|
104
|
+
|
105
|
+
if ( ! BranchBits || NRules > MaxCase )
|
106
|
+
{
|
107
|
+
GenerateLogs(Max(MaxCase+1, Max(MaxAtt, Max(MaxClass,
|
108
|
+
Max(MaxDiscrVal, NRules)))));
|
109
|
+
}
|
110
|
+
|
111
|
+
if ( ! BranchBits )
|
112
|
+
{
|
113
|
+
FindTestCodes();
|
114
|
+
}
|
115
|
+
|
116
|
+
/* Determine rule codelengths */
|
117
|
+
|
118
|
+
if ( NRules >= MaxCase+1 )
|
119
|
+
{
|
120
|
+
Realloc(List, NRules+1, CaseNo);
|
121
|
+
}
|
122
|
+
|
123
|
+
ForEach(r, 1, NRules)
|
124
|
+
{
|
125
|
+
R = Rule[r];
|
126
|
+
|
127
|
+
CodeLength = 0;
|
128
|
+
ForEach(d, 1, R->Size)
|
129
|
+
{
|
130
|
+
CodeLength += CondBits(R->Lhs[d]);
|
131
|
+
}
|
132
|
+
Bits[r] = CodeLength + LogCaseNo[R->Size] - LogFact[R->Size];
|
133
|
+
}
|
134
|
+
|
135
|
+
/* Use estimated error rate to determine the bits required to
|
136
|
+
label a theory's prediction for a case as an error or correct */
|
137
|
+
|
138
|
+
if ( EstErrRate > 0.5 ) EstErrRate = 0.45;
|
139
|
+
|
140
|
+
BitsErr = - Log(EstErrRate);
|
141
|
+
BitsOK = - Log(1.0 - EstErrRate);
|
142
|
+
|
143
|
+
|
144
|
+
/* Allocate tables used in hillclimbing */
|
145
|
+
|
146
|
+
DeltaErrs = Alloc(NRules+1, float);
|
147
|
+
TopClass = Alloc(MaxCase+1, ClassNo);
|
148
|
+
|
149
|
+
AltClass = Alloc(MaxCase+1, ClassNo);
|
150
|
+
TotVote = Alloc(MaxCase+1, int *);
|
151
|
+
|
152
|
+
bp = AllocZero((MaxCase+1) * (MaxClass+1), int);
|
153
|
+
ForEach(i, 0, MaxCase)
|
154
|
+
{
|
155
|
+
TotVote[i] = bp;
|
156
|
+
bp += MaxClass + 1;
|
157
|
+
}
|
158
|
+
|
159
|
+
/* Now find best subset of rules */
|
160
|
+
|
161
|
+
HillClimb();
|
162
|
+
|
163
|
+
/* Determine default class and reorder rules */
|
164
|
+
|
165
|
+
SetDefaultClass();
|
166
|
+
OrderRules();
|
167
|
+
|
168
|
+
/* Deallocate storage */
|
169
|
+
|
170
|
+
FreeSiftRuleData();
|
171
|
+
}
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
/*************************************************************************/
|
176
|
+
/* */
|
177
|
+
/* Find inverse of Fires[][] in CovBy, CovByPtr, and CovByBlock. */
|
178
|
+
/* */
|
179
|
+
/* CovBy[i] = number of rules covering case i (set by NewRule) */
|
180
|
+
/* */
|
181
|
+
/* Set up CovByPtr as pointers into CovByBlock so that */
|
182
|
+
/* CovByPtr[i] is the start of the compressed entry for case i */
|
183
|
+
/* */
|
184
|
+
/*************************************************************************/
|
185
|
+
|
186
|
+
|
187
|
+
void InvertFires()
|
188
|
+
/* ----------- */
|
189
|
+
{
|
190
|
+
RuleNo r, Entry;
|
191
|
+
int j, Blocks, Extra;
|
192
|
+
CaseNo i;
|
193
|
+
Byte *p, *From, *To, *Next;
|
194
|
+
|
195
|
+
CovByPtr = Alloc(MaxCase+2, Byte *);
|
196
|
+
Extra = NRules / 128; /* max number of filler entries */
|
197
|
+
CovByPtr[0] = 0;
|
198
|
+
ForEach(i, 1, MaxCase+1)
|
199
|
+
{
|
200
|
+
CovByPtr[i] = CovByPtr[i-1] + CovBy[i-1] + Extra;
|
201
|
+
}
|
202
|
+
|
203
|
+
CovByBlock = Alloc((size_t) CovByPtr[MaxCase+1], Byte);
|
204
|
+
ForEach(i, 0, MaxCase)
|
205
|
+
{
|
206
|
+
CovByPtr[i] += (size_t) CovByBlock;
|
207
|
+
}
|
208
|
+
|
209
|
+
LastCovBy = AllocZero(MaxCase+1, RuleNo);
|
210
|
+
|
211
|
+
/* Add entries for each rule */
|
212
|
+
|
213
|
+
ForEach(r, 1, NRules)
|
214
|
+
{
|
215
|
+
Uncompress(Fires[r], List);
|
216
|
+
ForEach(j, 1, List[0])
|
217
|
+
{
|
218
|
+
i = List[j];
|
219
|
+
|
220
|
+
/* Add compressed entry for this rule */
|
221
|
+
|
222
|
+
p = CovByPtr[i];
|
223
|
+
Entry = r - LastCovBy[i];
|
224
|
+
LastCovBy[i] = r;
|
225
|
+
|
226
|
+
while ( Entry > 127 )
|
227
|
+
{
|
228
|
+
Blocks = (Entry >> 7);
|
229
|
+
if ( Blocks > 127 ) Blocks = 127;
|
230
|
+
Entry -= Blocks * 128;
|
231
|
+
*p++ = Blocks + 128;
|
232
|
+
}
|
233
|
+
|
234
|
+
*p++ = Entry;
|
235
|
+
CovByPtr[i] = p;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
|
239
|
+
Free(LastCovBy); LastCovBy = Nil;
|
240
|
+
|
241
|
+
/* Reset CovByPtr entries and compact */
|
242
|
+
|
243
|
+
To = CovByPtr[0];
|
244
|
+
From = CovByPtr[0] = CovByBlock;
|
245
|
+
|
246
|
+
ForEach(i, 1, MaxCase)
|
247
|
+
{
|
248
|
+
From += CovBy[i-1] + Extra;
|
249
|
+
Next = CovByPtr[i];
|
250
|
+
CovByPtr[i] = To;
|
251
|
+
|
252
|
+
for ( p = From ; p < Next ; )
|
253
|
+
{
|
254
|
+
*To++ = *p++;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
|
258
|
+
/* Reduce CovByBlock to size actually used */
|
259
|
+
|
260
|
+
From = CovByBlock; /* current address */
|
261
|
+
|
262
|
+
Realloc(CovByBlock, To - CovByBlock, Byte);
|
263
|
+
|
264
|
+
if ( CovByBlock != From )
|
265
|
+
{
|
266
|
+
/* CovByBlock has been moved */
|
267
|
+
|
268
|
+
ForEach(i, 0, MaxCase)
|
269
|
+
{
|
270
|
+
CovByPtr[i] += CovByBlock - From;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
}
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
/*************************************************************************/
|
278
|
+
/* */
|
279
|
+
/* Determine code lengths for attributes and branches */
|
280
|
+
/* */
|
281
|
+
/*************************************************************************/
|
282
|
+
|
283
|
+
|
284
|
+
void FindTestCodes()
|
285
|
+
/* ------------- */
|
286
|
+
{
|
287
|
+
Attribute Att;
|
288
|
+
DiscrValue v, V;
|
289
|
+
CaseNo i, *ValFreq;
|
290
|
+
int PossibleAtts=0;
|
291
|
+
float Sum;
|
292
|
+
|
293
|
+
BranchBits = AllocZero(MaxAtt+1, float);
|
294
|
+
AttValues = AllocZero(MaxAtt+1, int);
|
295
|
+
|
296
|
+
ForEach(Att, 1, MaxAtt)
|
297
|
+
{
|
298
|
+
if ( Skip(Att) || Att == ClassAtt ) continue;
|
299
|
+
|
300
|
+
PossibleAtts++;
|
301
|
+
|
302
|
+
if ( Ordered(Att) )
|
303
|
+
{
|
304
|
+
BranchBits[Att] = 1 + 0.5 * LogCaseNo[MaxAttVal[Att] - 1];
|
305
|
+
}
|
306
|
+
else
|
307
|
+
if ( (V = MaxAttVal[Att]) )
|
308
|
+
{
|
309
|
+
/* Discrete attribute */
|
310
|
+
|
311
|
+
ValFreq = AllocZero(V+1, CaseNo);
|
312
|
+
|
313
|
+
ForEach(i, 0, MaxCase)
|
314
|
+
{
|
315
|
+
assert(XDVal(Case[i],Att) >= 0 && XDVal(Case[i],Att) <= V);
|
316
|
+
ValFreq[ XDVal(Case[i],Att) ]++;
|
317
|
+
}
|
318
|
+
|
319
|
+
Sum = 0;
|
320
|
+
ForEach(v, 1, V)
|
321
|
+
{
|
322
|
+
if ( ValFreq[v] )
|
323
|
+
{
|
324
|
+
Sum += (ValFreq[v] / (MaxCase+1.0)) *
|
325
|
+
(LogCaseNo[MaxCase+1] - LogCaseNo[ValFreq[v]]);
|
326
|
+
AttValues[Att]++;
|
327
|
+
}
|
328
|
+
}
|
329
|
+
Free(ValFreq);
|
330
|
+
|
331
|
+
BranchBits[Att] = Sum;
|
332
|
+
}
|
333
|
+
else
|
334
|
+
{
|
335
|
+
/* Continuous attribute */
|
336
|
+
|
337
|
+
BranchBits[Att] = PossibleCuts[Att] > 1 ?
|
338
|
+
1 + 0.5 * LogCaseNo[PossibleCuts[Att]] : 0 ;
|
339
|
+
}
|
340
|
+
}
|
341
|
+
|
342
|
+
AttTestBits = LogCaseNo[PossibleAtts];
|
343
|
+
}
|
344
|
+
|
345
|
+
|
346
|
+
|
347
|
+
/*************************************************************************/
|
348
|
+
/* */
|
349
|
+
/* Determine the number of bits required to encode a condition */
|
350
|
+
/* */
|
351
|
+
/*************************************************************************/
|
352
|
+
|
353
|
+
|
354
|
+
float CondBits(Condition C)
|
355
|
+
/* -------- */
|
356
|
+
{
|
357
|
+
Attribute Att;
|
358
|
+
float Code=0;
|
359
|
+
int Elts=0;
|
360
|
+
DiscrValue v;
|
361
|
+
|
362
|
+
Att = C->Tested;
|
363
|
+
switch ( C->NodeType )
|
364
|
+
{
|
365
|
+
case BrDiscr: /* test of discrete attribute */
|
366
|
+
case BrThresh: /* test of continuous attribute */
|
367
|
+
|
368
|
+
return AttTestBits + BranchBits[Att];
|
369
|
+
|
370
|
+
case BrSubset: /* subset test on discrete attribute */
|
371
|
+
|
372
|
+
/* Ignore subset test form for ordered attributes */
|
373
|
+
|
374
|
+
if ( Ordered(Att) )
|
375
|
+
{
|
376
|
+
return AttTestBits + BranchBits[Att];
|
377
|
+
}
|
378
|
+
|
379
|
+
ForEach(v, 1, MaxAttVal[Att])
|
380
|
+
{
|
381
|
+
if ( In(v, C->Subset) )
|
382
|
+
{
|
383
|
+
Elts++;
|
384
|
+
}
|
385
|
+
}
|
386
|
+
Elts = Min(Elts, AttValues[Att] - 1); /* if values not present */
|
387
|
+
Code = LogFact[AttValues[Att]] -
|
388
|
+
(LogFact[Elts] + LogFact[AttValues[Att] - Elts]);
|
389
|
+
|
390
|
+
return AttTestBits + Code;
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
|
395
|
+
|
396
|
+
/*************************************************************************/
|
397
|
+
/* */
|
398
|
+
/* Select initial theory. This is important, since the greedy */
|
399
|
+
/* optimization procedure is very sensitive to starting with */
|
400
|
+
/* a reasonable theory. */
|
401
|
+
/* */
|
402
|
+
/* The theory is constructed class by class. For each class, */
|
403
|
+
/* rules are added in confidence order until all of the cases of */
|
404
|
+
/* that class are covered. Rules that do not improve coverage */
|
405
|
+
/* are skipped. */
|
406
|
+
/* */
|
407
|
+
/*************************************************************************/
|
408
|
+
|
409
|
+
|
410
|
+
void SetInitialTheory()
|
411
|
+
/* ---------------- */
|
412
|
+
{
|
413
|
+
ClassNo c;
|
414
|
+
RuleNo r, Active=0;
|
415
|
+
|
416
|
+
ForEach(c, 1, MaxClass)
|
417
|
+
{
|
418
|
+
CoverClass(c);
|
419
|
+
}
|
420
|
+
|
421
|
+
/* Remove rules that don't help coverage */
|
422
|
+
|
423
|
+
ForEach(r, 1, NRules)
|
424
|
+
{
|
425
|
+
if ( (RuleIn[r] &= 1) ) Active++;
|
426
|
+
}
|
427
|
+
}
|
428
|
+
|
429
|
+
|
430
|
+
|
431
|
+
void CoverClass(ClassNo Target)
|
432
|
+
/* ---------- */
|
433
|
+
{
|
434
|
+
CaseNo i;
|
435
|
+
double Remaining, FalsePos=0, NewFalsePos, NewTruePos;
|
436
|
+
RuleNo r, Best;
|
437
|
+
int j;
|
438
|
+
|
439
|
+
memset(Covered, false, MaxCase+1);
|
440
|
+
|
441
|
+
Remaining = ClassFreq[Target];
|
442
|
+
|
443
|
+
while ( Remaining > FalsePos )
|
444
|
+
{
|
445
|
+
/* Find most accurate unused rule from a leaf */
|
446
|
+
|
447
|
+
Best = 0;
|
448
|
+
ForEach(r, 1, NRules)
|
449
|
+
{
|
450
|
+
if ( Rule[r]->Rhs == Target && ! RuleIn[r] &&
|
451
|
+
Rule[r]->Correct >= MINITEMS )
|
452
|
+
{
|
453
|
+
if ( ! Best || Rule[r]->Vote > Rule[Best]->Vote ) Best = r;
|
454
|
+
}
|
455
|
+
}
|
456
|
+
|
457
|
+
if ( ! Best ) return;
|
458
|
+
|
459
|
+
/* Check increased coverage */
|
460
|
+
|
461
|
+
NewFalsePos = NewTruePos = 0;
|
462
|
+
|
463
|
+
Uncompress(Fires[Best], List);
|
464
|
+
for( j = List[0] ; j ; j-- )
|
465
|
+
{
|
466
|
+
i = List[j];
|
467
|
+
if ( ! Covered[i] )
|
468
|
+
{
|
469
|
+
if ( Class(Case[i]) == Target )
|
470
|
+
{
|
471
|
+
NewTruePos += Weight(Case[i]);
|
472
|
+
}
|
473
|
+
else
|
474
|
+
{
|
475
|
+
NewFalsePos += Weight(Case[i]);
|
476
|
+
}
|
477
|
+
}
|
478
|
+
}
|
479
|
+
|
480
|
+
/* If coverage is not increased, set RuleIn to 2 so that
|
481
|
+
the rule can be removed later */
|
482
|
+
|
483
|
+
if ( NewTruePos - NewFalsePos <= MINITEMS + Epsilon )
|
484
|
+
{
|
485
|
+
RuleIn[Best] = 2;
|
486
|
+
}
|
487
|
+
else
|
488
|
+
{
|
489
|
+
Remaining -= NewTruePos;
|
490
|
+
FalsePos += NewFalsePos;
|
491
|
+
|
492
|
+
RuleIn[Best] = true;
|
493
|
+
|
494
|
+
Uncompress(Fires[Best], List);
|
495
|
+
for( j = List[0] ; j ; j-- )
|
496
|
+
{
|
497
|
+
i = List[j];
|
498
|
+
if ( ! Covered[i] )
|
499
|
+
{
|
500
|
+
Covered[i] = true;
|
501
|
+
}
|
502
|
+
}
|
503
|
+
}
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
|
508
|
+
|
509
|
+
/*************************************************************************/
|
510
|
+
/* */
|
511
|
+
/* Calculate total message length as */
|
512
|
+
/* THEORYFRAC * cost of transmitting theory */
|
513
|
+
/* + cost of identifying and correcting errors */
|
514
|
+
/* */
|
515
|
+
/* The cost of identifying errors assumes that the final theory */
|
516
|
+
/* will have about the same error rate as the pruned tree, so */
|
517
|
+
/* is approx. the sum of the corresponding messages. */
|
518
|
+
/* */
|
519
|
+
/*************************************************************************/
|
520
|
+
|
521
|
+
|
522
|
+
double MessageLength(RuleNo NR, double RuleBits, float Errs)
|
523
|
+
/* ------------- */
|
524
|
+
{
|
525
|
+
return
|
526
|
+
(THEORYFRAC * Max(0, RuleBits - LogFact[NR]) +
|
527
|
+
Errs * BitsErr + (MaxCase+1 - Errs) * BitsOK +
|
528
|
+
Errs * LogCaseNo[MaxClass-1]);
|
529
|
+
}
|
530
|
+
|
531
|
+
|
532
|
+
|
533
|
+
/*************************************************************************/
|
534
|
+
/* */
|
535
|
+
/* Improve a subset of rules by adding and deleting rules. */
|
536
|
+
/* MDL costs are rounded to nearest 0.01 bit */
|
537
|
+
/* */
|
538
|
+
/*************************************************************************/
|
539
|
+
|
540
|
+
|
541
|
+
void HillClimb()
|
542
|
+
/* --------- */
|
543
|
+
{
|
544
|
+
RuleNo r, RuleCount=0, OriginalCount, Toggle, LastToggle=0;
|
545
|
+
int OutCount;
|
546
|
+
CaseNo i;
|
547
|
+
int j;
|
548
|
+
CaseCount Errs;
|
549
|
+
double RuleBits=0;
|
550
|
+
double LastCost=1E99, CurrentCost, AltCost, NewCost;
|
551
|
+
Boolean DeleteOnly=false;
|
552
|
+
|
553
|
+
ForEach(r, 1, NRules)
|
554
|
+
{
|
555
|
+
if ( RuleIn[r] )
|
556
|
+
{
|
557
|
+
RuleBits += Bits[r];
|
558
|
+
RuleCount++;
|
559
|
+
}
|
560
|
+
}
|
561
|
+
OriginalCount = RuleCount;
|
562
|
+
|
563
|
+
InitialiseVotes();
|
564
|
+
Verbosity(1, fprintf(Of, "\n"))
|
565
|
+
|
566
|
+
/* Initialise DeltaErrs[] */
|
567
|
+
|
568
|
+
Errs = CalculateDeltaErrs();
|
569
|
+
|
570
|
+
/* Add or drop rule with greatest reduction in coding cost */
|
571
|
+
|
572
|
+
while ( true )
|
573
|
+
{
|
574
|
+
CurrentCost = NewCost = MessageLength(RuleCount, RuleBits, Errs);
|
575
|
+
|
576
|
+
Verbosity(1,
|
577
|
+
fprintf(Of, "\t%d rules, %.1f errs, cost=%.1f bits\n",
|
578
|
+
RuleCount, Errs, CurrentCost/100.0);
|
579
|
+
|
580
|
+
if ( ! DeleteOnly && CurrentCost > LastCost )
|
581
|
+
{
|
582
|
+
fprintf(Of, "ERROR %g %g\n",
|
583
|
+
CurrentCost/1000.0, LastCost/100.0);
|
584
|
+
break;
|
585
|
+
})
|
586
|
+
|
587
|
+
Toggle = OutCount = 0;
|
588
|
+
|
589
|
+
ForEach(r, 1, NRules)
|
590
|
+
{
|
591
|
+
if ( r == LastToggle ) continue;
|
592
|
+
|
593
|
+
if ( RuleIn[r] )
|
594
|
+
{
|
595
|
+
AltCost = MessageLength(RuleCount - 1,
|
596
|
+
RuleBits - Bits[r],
|
597
|
+
Errs + DeltaErrs[r]);
|
598
|
+
}
|
599
|
+
else
|
600
|
+
{
|
601
|
+
if ( Errs < 1E-3 || DeleteOnly ) continue;
|
602
|
+
|
603
|
+
AltCost = MessageLength(RuleCount + 1,
|
604
|
+
RuleBits + Bits[r],
|
605
|
+
Errs + DeltaErrs[r]);
|
606
|
+
}
|
607
|
+
|
608
|
+
Verbosity(2,
|
609
|
+
if ( ! (OutCount++ % 5) ) fprintf(Of, "\n\t\t");
|
610
|
+
fprintf(Of, "%d<%g=%.1f> ",
|
611
|
+
r, DeltaErrs[r], (AltCost - CurrentCost)/100.0))
|
612
|
+
|
613
|
+
if ( AltCost < NewCost ||
|
614
|
+
AltCost == NewCost && RuleIn[r] )
|
615
|
+
{
|
616
|
+
Toggle = r;
|
617
|
+
NewCost = AltCost;
|
618
|
+
}
|
619
|
+
}
|
620
|
+
|
621
|
+
if ( ! DeleteOnly && NewCost > CurrentCost )
|
622
|
+
{
|
623
|
+
DeleteOnly = true;
|
624
|
+
Verbosity(1, fprintf(Of, "(start delete mode)\n"))
|
625
|
+
}
|
626
|
+
|
627
|
+
Verbosity(2, fprintf(Of, "\n"))
|
628
|
+
|
629
|
+
if ( ! Toggle || DeleteOnly && RuleCount <= OriginalCount ) break;
|
630
|
+
|
631
|
+
Verbosity(1,
|
632
|
+
fprintf(Of, "\t%s rule %d/%d (errs=%.1f, cost=%.1f bits)\n",
|
633
|
+
( RuleIn[Toggle] ? "Delete" : "Add" ),
|
634
|
+
Rule[Toggle]->TNo, Rule[Toggle]->RNo,
|
635
|
+
Errs + DeltaErrs[Toggle], NewCost/100.0))
|
636
|
+
|
637
|
+
/* Adjust vote information */
|
638
|
+
|
639
|
+
Uncompress(Fires[Toggle], List);
|
640
|
+
for ( j = List[0] ; j ; j-- )
|
641
|
+
{
|
642
|
+
i = List[j];
|
643
|
+
|
644
|
+
/* Downdate DeltaErrs for all rules except Toggle that cover i */
|
645
|
+
|
646
|
+
UpdateDeltaErrs(i, -Weight(Case[i]), Toggle);
|
647
|
+
|
648
|
+
if ( RuleIn[Toggle] )
|
649
|
+
{
|
650
|
+
TotVote[i][Rule[Toggle]->Rhs] -= Rule[Toggle]->Vote;
|
651
|
+
}
|
652
|
+
else
|
653
|
+
{
|
654
|
+
TotVote[i][Rule[Toggle]->Rhs] += Rule[Toggle]->Vote;
|
655
|
+
}
|
656
|
+
|
657
|
+
CountVotes(i);
|
658
|
+
|
659
|
+
/* Update DeltaErrs for all rules except Toggle that cover i */
|
660
|
+
|
661
|
+
UpdateDeltaErrs(i, Weight(Case[i]), Toggle);
|
662
|
+
}
|
663
|
+
|
664
|
+
/* Update information about rules selected and current errors */
|
665
|
+
|
666
|
+
if ( RuleIn[Toggle] )
|
667
|
+
{
|
668
|
+
RuleIn[Toggle] = false;
|
669
|
+
RuleBits -= Bits[Toggle];
|
670
|
+
RuleCount--;
|
671
|
+
}
|
672
|
+
else
|
673
|
+
{
|
674
|
+
RuleIn[Toggle] = true;
|
675
|
+
RuleBits += Bits[Toggle];
|
676
|
+
RuleCount++;
|
677
|
+
}
|
678
|
+
|
679
|
+
Errs += DeltaErrs[Toggle];
|
680
|
+
DeltaErrs[Toggle] = - DeltaErrs[Toggle];
|
681
|
+
|
682
|
+
LastToggle = Toggle;
|
683
|
+
LastCost = CurrentCost;
|
684
|
+
|
685
|
+
Progress(1.0);
|
686
|
+
}
|
687
|
+
}
|
688
|
+
|
689
|
+
|
690
|
+
|
691
|
+
/*************************************************************************/
|
692
|
+
/* */
|
693
|
+
/* Determine votes for each case from initial rules */
|
694
|
+
/* Note: no vote for default class */
|
695
|
+
/* */
|
696
|
+
/*************************************************************************/
|
697
|
+
|
698
|
+
|
699
|
+
void InitialiseVotes()
|
700
|
+
/* --------------- */
|
701
|
+
{
|
702
|
+
CaseNo i;
|
703
|
+
int j, Vote;
|
704
|
+
ClassNo Rhs;
|
705
|
+
RuleNo r;
|
706
|
+
|
707
|
+
/* Adjust vote for each case covered by rule */
|
708
|
+
|
709
|
+
ForEach(r, 1, NRules)
|
710
|
+
{
|
711
|
+
if ( ! RuleIn[r] ) continue;
|
712
|
+
|
713
|
+
Rhs = Rule[r]->Rhs;
|
714
|
+
Vote = Rule[r]->Vote;
|
715
|
+
|
716
|
+
Uncompress(Fires[r], List);
|
717
|
+
for ( j = List[0] ; j ; j-- )
|
718
|
+
{
|
719
|
+
TotVote[List[j]][Rhs] += Vote;
|
720
|
+
}
|
721
|
+
}
|
722
|
+
|
723
|
+
/* Find the best and alternate class for each case */
|
724
|
+
|
725
|
+
ForEach(i, 0, MaxCase)
|
726
|
+
{
|
727
|
+
CountVotes(i);
|
728
|
+
}
|
729
|
+
}
|
730
|
+
|
731
|
+
|
732
|
+
|
733
|
+
/*************************************************************************/
|
734
|
+
/* */
|
735
|
+
/* Find the best and second-best class for each case using the */
|
736
|
+
/* current values of TotVote */
|
737
|
+
/* */
|
738
|
+
/*************************************************************************/
|
739
|
+
|
740
|
+
|
741
|
+
void CountVotes(CaseNo i)
|
742
|
+
/* ---------- */
|
743
|
+
{
|
744
|
+
ClassNo c, First=0, Second=0;
|
745
|
+
int V;
|
746
|
+
|
747
|
+
ForEach(c, 1, MaxClass)
|
748
|
+
{
|
749
|
+
if ( (V = TotVote[i][c]) )
|
750
|
+
{
|
751
|
+
if ( ! First || V > TotVote[i][First] )
|
752
|
+
{
|
753
|
+
Second = First;
|
754
|
+
First = c;
|
755
|
+
}
|
756
|
+
else
|
757
|
+
if ( ! Second || V > TotVote[i][Second] )
|
758
|
+
{
|
759
|
+
Second = c;
|
760
|
+
}
|
761
|
+
}
|
762
|
+
}
|
763
|
+
|
764
|
+
TopClass[i] = First;
|
765
|
+
AltClass[i] = Second;
|
766
|
+
}
|
767
|
+
|
768
|
+
|
769
|
+
|
770
|
+
/*************************************************************************/
|
771
|
+
/* */
|
772
|
+
/* Adjust DeltaErrors for all rules except Toggle that cover case i */
|
773
|
+
/* */
|
774
|
+
/*************************************************************************/
|
775
|
+
|
776
|
+
|
777
|
+
#define Prefer(d,c1,c2) ((d) > 0 || (d) == 0 && c1 < c2)
|
778
|
+
|
779
|
+
void UpdateDeltaErrs(CaseNo i, double Delta, RuleNo Toggle)
|
780
|
+
/* --------------- */
|
781
|
+
{
|
782
|
+
ClassNo RealClass, Top, Alt, Rhs;
|
783
|
+
RuleNo r;
|
784
|
+
Byte *p;
|
785
|
+
int k;
|
786
|
+
|
787
|
+
RealClass = Class(Case[i]);
|
788
|
+
Top = TopClass[i];
|
789
|
+
Alt = AltClass[i];
|
790
|
+
|
791
|
+
r = 0;
|
792
|
+
p = CovByPtr[i];
|
793
|
+
ForEach(k, 1, CovBy[i])
|
794
|
+
{
|
795
|
+
/* Update r to next rule covering case i */
|
796
|
+
|
797
|
+
while ( (*p) & 128 )
|
798
|
+
{
|
799
|
+
r += ((*p++) & 127) * 128;
|
800
|
+
}
|
801
|
+
r += *p++;
|
802
|
+
|
803
|
+
if ( r != Toggle )
|
804
|
+
{
|
805
|
+
/* Examine effect of adding or deleting rule */
|
806
|
+
|
807
|
+
Rhs = Rule[r]->Rhs;
|
808
|
+
|
809
|
+
if ( RuleIn[r] )
|
810
|
+
{
|
811
|
+
if ( Rhs == Top &&
|
812
|
+
Prefer(TotVote[i][Alt] - (TotVote[i][Top] - Rule[r]->Vote),
|
813
|
+
Alt, Top) )
|
814
|
+
{
|
815
|
+
DeltaErrs[r] +=
|
816
|
+
(NCost[Alt][RealClass] - NCost[Top][RealClass]) * Delta;
|
817
|
+
}
|
818
|
+
}
|
819
|
+
else
|
820
|
+
{
|
821
|
+
if ( Rhs != Top &&
|
822
|
+
Prefer(TotVote[i][Rhs] + Rule[r]->Vote - TotVote[i][Top],
|
823
|
+
Rhs, Top) )
|
824
|
+
{
|
825
|
+
DeltaErrs[r] +=
|
826
|
+
(NCost[Rhs][RealClass] - NCost[Top][RealClass]) * Delta;
|
827
|
+
}
|
828
|
+
}
|
829
|
+
}
|
830
|
+
}
|
831
|
+
}
|
832
|
+
|
833
|
+
|
834
|
+
|
835
|
+
/*************************************************************************/
|
836
|
+
/* */
|
837
|
+
/* Calculate initial value of DeltaErrs and total errors */
|
838
|
+
/* */
|
839
|
+
/*************************************************************************/
|
840
|
+
|
841
|
+
|
842
|
+
CaseCount CalculateDeltaErrs()
|
843
|
+
/* ------------------ */
|
844
|
+
{
|
845
|
+
RuleNo r;
|
846
|
+
CaseNo i;
|
847
|
+
double Errs=0;
|
848
|
+
|
849
|
+
ForEach(i, 0, MaxCase)
|
850
|
+
{
|
851
|
+
Errs += Weight(Case[i]) * NCost[TopClass[i]][Class(Case[i])];
|
852
|
+
}
|
853
|
+
|
854
|
+
ForEach(r, 1, NRules)
|
855
|
+
{
|
856
|
+
DeltaErrs[r] = 0;
|
857
|
+
}
|
858
|
+
|
859
|
+
ForEach(i, 0, MaxCase)
|
860
|
+
{
|
861
|
+
UpdateDeltaErrs(i, Weight(Case[i]), 0);
|
862
|
+
}
|
863
|
+
|
864
|
+
return Errs;
|
865
|
+
}
|
866
|
+
|
867
|
+
|
868
|
+
|
869
|
+
/*************************************************************************/
|
870
|
+
/* */
|
871
|
+
/* Remove unrepresented values from subsets */
|
872
|
+
/* */
|
873
|
+
/*************************************************************************/
|
874
|
+
|
875
|
+
|
876
|
+
void PruneSubsets()
|
877
|
+
/* ------------ */
|
878
|
+
{
|
879
|
+
Set *PossibleValues;
|
880
|
+
Attribute Att, *Atts, Last;
|
881
|
+
int *Bytes, d, NAtts, j, b;
|
882
|
+
CaseNo i;
|
883
|
+
CRule R;
|
884
|
+
RuleNo r;
|
885
|
+
|
886
|
+
/* Allocate subsets for possible values */
|
887
|
+
|
888
|
+
Atts = Alloc(MaxAtt+1, Attribute);
|
889
|
+
Bytes = Alloc(MaxAtt+1, int);
|
890
|
+
|
891
|
+
PossibleValues = AllocZero(MaxAtt+1, Set);
|
892
|
+
ForEach(Att, 1, MaxAtt)
|
893
|
+
{
|
894
|
+
if ( MaxAttVal[Att] > 3 )
|
895
|
+
{
|
896
|
+
Bytes[Att] = (MaxAttVal[Att]>>3)+1;
|
897
|
+
PossibleValues[Att] = AllocZero(Bytes[Att], Byte);
|
898
|
+
}
|
899
|
+
}
|
900
|
+
|
901
|
+
/* Check each rule in turn */
|
902
|
+
|
903
|
+
ForEach(r, 1, NRules)
|
904
|
+
{
|
905
|
+
R = Rule[r];
|
906
|
+
NAtts = 0;
|
907
|
+
|
908
|
+
/* Find all subset conditions */
|
909
|
+
|
910
|
+
ForEach(d, 1, R->Size)
|
911
|
+
{
|
912
|
+
if ( R->Lhs[d]->NodeType != BrSubset ) continue;
|
913
|
+
|
914
|
+
Atts[++NAtts] = Att = R->Lhs[d]->Tested;
|
915
|
+
ClearBits(Bytes[Att], PossibleValues[Att]);
|
916
|
+
}
|
917
|
+
|
918
|
+
if ( ! NAtts ) continue; /* no subset conditions */
|
919
|
+
|
920
|
+
/* Scan cases covered by this rule */
|
921
|
+
|
922
|
+
Uncompress(Fires[r], List);
|
923
|
+
for ( j = List[0] ; j ; j-- )
|
924
|
+
{
|
925
|
+
i = List[j];
|
926
|
+
|
927
|
+
/* Record values of listed attributes */
|
928
|
+
|
929
|
+
ForEach(d, 1, NAtts)
|
930
|
+
{
|
931
|
+
Att = Atts[d];
|
932
|
+
SetBit(DVal(Case[i], Att), PossibleValues[Att]);
|
933
|
+
}
|
934
|
+
}
|
935
|
+
|
936
|
+
/* Delete unrepresented values */
|
937
|
+
|
938
|
+
ForEach(d, 1, R->Size)
|
939
|
+
{
|
940
|
+
if ( R->Lhs[d]->NodeType != BrSubset ) continue;
|
941
|
+
|
942
|
+
Att = R->Lhs[d]->Tested;
|
943
|
+
ForEach(b, 0, Bytes[Att]-1)
|
944
|
+
{
|
945
|
+
R->Lhs[d]->Subset[b] &= PossibleValues[Att][b];
|
946
|
+
}
|
947
|
+
|
948
|
+
if ( Elements(Att, R->Lhs[d]->Subset, &Last) == 1 )
|
949
|
+
{
|
950
|
+
R->Lhs[d]->NodeType = BrDiscr;
|
951
|
+
R->Lhs[d]->TestValue = Last;
|
952
|
+
Free(R->Lhs[d]->Subset);
|
953
|
+
}
|
954
|
+
}
|
955
|
+
}
|
956
|
+
|
957
|
+
FreeVector((void **) PossibleValues, 1, MaxAtt);
|
958
|
+
Free(Bytes);
|
959
|
+
Free(Atts);
|
960
|
+
}
|
961
|
+
|
962
|
+
|
963
|
+
|
964
|
+
/*************************************************************************/
|
965
|
+
/* */
|
966
|
+
/* Choose the default class as the one with the maximum */
|
967
|
+
/* weight of uncovered cases */
|
968
|
+
/* */
|
969
|
+
/*************************************************************************/
|
970
|
+
|
971
|
+
|
972
|
+
void SetDefaultClass()
|
973
|
+
/* --------------- */
|
974
|
+
{
|
975
|
+
RuleNo r;
|
976
|
+
ClassNo c;
|
977
|
+
double *UncoveredWeight, TotUncovered=1E-3;
|
978
|
+
CaseNo i, j;
|
979
|
+
|
980
|
+
memset(Covered, false, MaxCase+1);
|
981
|
+
UncoveredWeight = AllocZero(MaxClass+1, double);
|
982
|
+
|
983
|
+
/* Check which cases are covered by at least one rule */
|
984
|
+
|
985
|
+
ForEach(r, 1, NRules)
|
986
|
+
{
|
987
|
+
if ( ! RuleIn[r] ) continue;
|
988
|
+
|
989
|
+
Uncompress(Fires[r], List);
|
990
|
+
for ( j = List[0] ; j ; j-- )
|
991
|
+
{
|
992
|
+
Covered[List[j]] = true;
|
993
|
+
}
|
994
|
+
}
|
995
|
+
|
996
|
+
/* Find weights by class of uncovered cases */
|
997
|
+
|
998
|
+
ForEach(i, 0, MaxCase)
|
999
|
+
{
|
1000
|
+
if ( ! Covered[i] )
|
1001
|
+
{
|
1002
|
+
UncoveredWeight[ Class(Case[i]) ] += Weight(Case[i]);
|
1003
|
+
TotUncovered += Weight(Case[i]);
|
1004
|
+
}
|
1005
|
+
}
|
1006
|
+
|
1007
|
+
/* Choose new default class using rel freq and rel uncovered */
|
1008
|
+
|
1009
|
+
Verbosity(1, fprintf(Of, "\n Weights of uncovered cases:\n"));
|
1010
|
+
|
1011
|
+
ForEach(c, 1, MaxClass)
|
1012
|
+
{
|
1013
|
+
Verbosity(1, fprintf(Of, "\t%s (%.2f): %.1f\n",
|
1014
|
+
ClassName[c], ClassFreq[c] / (MaxCase + 1.0),
|
1015
|
+
UncoveredWeight[c]));
|
1016
|
+
|
1017
|
+
ClassSum[c] = (UncoveredWeight[c] + 1) / (TotUncovered + 2.0) +
|
1018
|
+
ClassFreq[c] / (MaxCase + 1.0);
|
1019
|
+
}
|
1020
|
+
|
1021
|
+
Default = SelectClass(1, (Boolean) (MCost && ! CostWeights));
|
1022
|
+
|
1023
|
+
Free(UncoveredWeight);
|
1024
|
+
}
|
1025
|
+
|
1026
|
+
|
1027
|
+
|
1028
|
+
/*************************************************************************/
|
1029
|
+
/* */
|
1030
|
+
/* Swap two rules */
|
1031
|
+
/* */
|
1032
|
+
/*************************************************************************/
|
1033
|
+
|
1034
|
+
|
1035
|
+
void SwapRule(RuleNo A, RuleNo B)
|
1036
|
+
/* -------- */
|
1037
|
+
{
|
1038
|
+
CRule Hold;
|
1039
|
+
Boolean HoldIn;
|
1040
|
+
|
1041
|
+
Hold = Rule[A];
|
1042
|
+
Rule[A] = Rule[B];
|
1043
|
+
Rule[B] = Hold;
|
1044
|
+
|
1045
|
+
HoldIn = RuleIn[A];
|
1046
|
+
RuleIn[A] = RuleIn[B];
|
1047
|
+
RuleIn[B] = HoldIn;
|
1048
|
+
}
|
1049
|
+
|
1050
|
+
|
1051
|
+
|
1052
|
+
/*************************************************************************/
|
1053
|
+
/* */
|
1054
|
+
/* Order rules by utility, least important first */
|
1055
|
+
/* (Called after HilClimb(), so RuleIn etc already known.) */
|
1056
|
+
/* */
|
1057
|
+
/*************************************************************************/
|
1058
|
+
|
1059
|
+
|
1060
|
+
int OrderByUtility()
|
1061
|
+
/* -------------- */
|
1062
|
+
{
|
1063
|
+
RuleNo r, *Drop, NDrop=0, NewNRules=0, Toggle;
|
1064
|
+
CaseNo i;
|
1065
|
+
int j, OutCount;
|
1066
|
+
double Errs=0;
|
1067
|
+
|
1068
|
+
Verbosity(1, fprintf(Of, "\n Determining rule utility\n"))
|
1069
|
+
|
1070
|
+
Drop = Alloc(NRules, RuleNo);
|
1071
|
+
|
1072
|
+
/* Find the rule that has the least beneficial effect on accuracy */
|
1073
|
+
|
1074
|
+
while ( true )
|
1075
|
+
{
|
1076
|
+
Toggle = OutCount = 0;
|
1077
|
+
|
1078
|
+
ForEach(r, 1, NRules)
|
1079
|
+
{
|
1080
|
+
if ( ! RuleIn[r] ) continue;
|
1081
|
+
|
1082
|
+
Verbosity(2,
|
1083
|
+
if ( ! (OutCount++ %10 ) ) fprintf(Of, "\n\t\t");
|
1084
|
+
fprintf(Of, "%d<%g> ", r, DeltaErrs[r]))
|
1085
|
+
|
1086
|
+
if ( ! Toggle ||
|
1087
|
+
DeltaErrs[r] < DeltaErrs[Toggle] - 1E-3 ||
|
1088
|
+
( DeltaErrs[r] < DeltaErrs[Toggle] + 1E-3 &&
|
1089
|
+
Bits[r] > Bits[Toggle] ) )
|
1090
|
+
{
|
1091
|
+
Toggle = r;
|
1092
|
+
}
|
1093
|
+
}
|
1094
|
+
Verbosity(2, fprintf(Of, "\n"))
|
1095
|
+
|
1096
|
+
if ( ! Toggle ) break;
|
1097
|
+
|
1098
|
+
Verbosity(1,
|
1099
|
+
fprintf(Of, "\tDelete rule %d/%d (errs up %.1f)\n",
|
1100
|
+
Rule[Toggle]->TNo, Rule[Toggle]->RNo,
|
1101
|
+
Errs + DeltaErrs[Toggle]))
|
1102
|
+
|
1103
|
+
/* Adjust vote information */
|
1104
|
+
|
1105
|
+
Uncompress(Fires[Toggle], List);
|
1106
|
+
for ( j = List[0] ; j ; j-- )
|
1107
|
+
{
|
1108
|
+
i = List[j];
|
1109
|
+
|
1110
|
+
/* Downdate DeltaErrs for all rules except Toggle that cover i */
|
1111
|
+
|
1112
|
+
UpdateDeltaErrs(i, -Weight(Case[i]), Toggle);
|
1113
|
+
|
1114
|
+
TotVote[i][Rule[Toggle]->Rhs] -= Rule[Toggle]->Vote;
|
1115
|
+
|
1116
|
+
CountVotes(i);
|
1117
|
+
|
1118
|
+
/* Update DeltaErrs for all rules except Toggle that cover i */
|
1119
|
+
|
1120
|
+
UpdateDeltaErrs(i, Weight(Case[i]), Toggle);
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
Drop[NDrop++] = Toggle;
|
1124
|
+
RuleIn[Toggle] = false;
|
1125
|
+
|
1126
|
+
Errs += DeltaErrs[Toggle];
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
/* Now reverse the order */
|
1130
|
+
|
1131
|
+
while ( --NDrop >= 0 )
|
1132
|
+
{
|
1133
|
+
NewNRules++;
|
1134
|
+
RuleIn[Drop[NDrop]] = true;
|
1135
|
+
SwapRule(Drop[NDrop], NewNRules);
|
1136
|
+
|
1137
|
+
/* Have to alter rule number in Drop */
|
1138
|
+
ForEach(r, 0, NDrop-1)
|
1139
|
+
{
|
1140
|
+
if ( Drop[r] == NewNRules ) Drop[r] = Drop[NDrop];
|
1141
|
+
}
|
1142
|
+
}
|
1143
|
+
Free(Drop);
|
1144
|
+
|
1145
|
+
return NewNRules;
|
1146
|
+
}
|
1147
|
+
|
1148
|
+
|
1149
|
+
|
1150
|
+
|
1151
|
+
/*************************************************************************/
|
1152
|
+
/* */
|
1153
|
+
/* Order rules by class and then by rule CF */
|
1154
|
+
/* */
|
1155
|
+
/*************************************************************************/
|
1156
|
+
|
1157
|
+
|
1158
|
+
int OrderByClass()
|
1159
|
+
/* ------------ */
|
1160
|
+
{
|
1161
|
+
RuleNo r, nr, NewNRules=0;
|
1162
|
+
ClassNo c;
|
1163
|
+
|
1164
|
+
ForEach(c, 1, MaxClass)
|
1165
|
+
{
|
1166
|
+
while ( true )
|
1167
|
+
{
|
1168
|
+
nr = 0;
|
1169
|
+
ForEach(r, NewNRules+1, NRules)
|
1170
|
+
{
|
1171
|
+
if ( RuleIn[r] && Rule[r]->Rhs == c &&
|
1172
|
+
( ! nr || Rule[r]->Vote > Rule[nr]->Vote ) )
|
1173
|
+
{
|
1174
|
+
nr = r;
|
1175
|
+
}
|
1176
|
+
}
|
1177
|
+
|
1178
|
+
if ( ! nr ) break;
|
1179
|
+
|
1180
|
+
NewNRules++;
|
1181
|
+
if ( nr != NewNRules )
|
1182
|
+
{
|
1183
|
+
SwapRule(NewNRules, nr);
|
1184
|
+
}
|
1185
|
+
}
|
1186
|
+
}
|
1187
|
+
|
1188
|
+
return NewNRules;
|
1189
|
+
}
|
1190
|
+
|
1191
|
+
|
1192
|
+
|
1193
|
+
/*************************************************************************/
|
1194
|
+
/* */
|
1195
|
+
/* Discard deleted rules and sequence and renumber those remaining. */
|
1196
|
+
/* Sort by class and then by rule CF or by utility */
|
1197
|
+
/* */
|
1198
|
+
/*************************************************************************/
|
1199
|
+
|
1200
|
+
|
1201
|
+
void OrderRules()
|
1202
|
+
/* ---------- */
|
1203
|
+
{
|
1204
|
+
RuleNo r, NewNRules;
|
1205
|
+
|
1206
|
+
NewNRules = ( UTILITY ? OrderByUtility() : OrderByClass() );
|
1207
|
+
|
1208
|
+
ForEach(r, 1, NewNRules)
|
1209
|
+
{
|
1210
|
+
Rule[r]->RNo = r;
|
1211
|
+
}
|
1212
|
+
|
1213
|
+
/* Free discarded rules */
|
1214
|
+
|
1215
|
+
ForEach(r, NewNRules+1, NRules)
|
1216
|
+
{
|
1217
|
+
FreeRule(Rule[r]);
|
1218
|
+
}
|
1219
|
+
|
1220
|
+
NRules = NewNRules;
|
1221
|
+
}
|
1222
|
+
|
1223
|
+
|
1224
|
+
|
1225
|
+
/*************************************************************************/
|
1226
|
+
/* */
|
1227
|
+
/* Tabluate logs and log factorials (to improve speed) */
|
1228
|
+
/* */
|
1229
|
+
/*************************************************************************/
|
1230
|
+
|
1231
|
+
|
1232
|
+
void GenerateLogs(int MaxN)
|
1233
|
+
/* ------------ */
|
1234
|
+
{
|
1235
|
+
CaseNo i;
|
1236
|
+
|
1237
|
+
if ( LogCaseNo )
|
1238
|
+
{
|
1239
|
+
Realloc(LogCaseNo, MaxN+2, double);
|
1240
|
+
Realloc(LogFact, MaxN+2, double);
|
1241
|
+
}
|
1242
|
+
else
|
1243
|
+
{
|
1244
|
+
LogCaseNo = Alloc(MaxN+2, double);
|
1245
|
+
LogFact = Alloc(MaxN+2, double);
|
1246
|
+
}
|
1247
|
+
|
1248
|
+
LogCaseNo[0] = -1E38;
|
1249
|
+
LogCaseNo[1] = 0;
|
1250
|
+
|
1251
|
+
LogFact[0] = LogFact[1] = 0;
|
1252
|
+
|
1253
|
+
ForEach(i, 2, MaxN+1)
|
1254
|
+
{
|
1255
|
+
LogCaseNo[i] = Log((double) i);
|
1256
|
+
LogFact[i] = LogFact[i-1] + LogCaseNo[i];
|
1257
|
+
}
|
1258
|
+
}
|
1259
|
+
|
1260
|
+
|
1261
|
+
|
1262
|
+
void FreeSiftRuleData()
|
1263
|
+
/* ---------------- */
|
1264
|
+
{
|
1265
|
+
FreeUnlessNil(List); List = Nil;
|
1266
|
+
FreeVector((void **) Fires, 1, RuleSpace-1); Fires = Nil;
|
1267
|
+
FreeUnlessNil(CBuffer); CBuffer = Nil;
|
1268
|
+
FreeUnlessNil(Covered); Covered = Nil;
|
1269
|
+
FreeUnlessNil(RuleIn); RuleIn = Nil;
|
1270
|
+
FreeUnlessNil(CovBy); CovBy = Nil;
|
1271
|
+
FreeUnlessNil(CovByPtr); CovByPtr = Nil;
|
1272
|
+
FreeUnlessNil(BranchBits); BranchBits = Nil;
|
1273
|
+
FreeUnlessNil(AttValues); AttValues = Nil;
|
1274
|
+
|
1275
|
+
FreeUnlessNil(DeltaErrs); DeltaErrs = Nil;
|
1276
|
+
FreeUnlessNil(CovByBlock); CovByBlock = Nil;
|
1277
|
+
FreeUnlessNil(Bits); Bits = Nil;
|
1278
|
+
FreeUnlessNil(TopClass); TopClass = Nil;
|
1279
|
+
FreeUnlessNil(AltClass); AltClass = Nil;
|
1280
|
+
if ( TotVote )
|
1281
|
+
{
|
1282
|
+
FreeUnlessNil(TotVote[0]);
|
1283
|
+
FreeUnlessNil(TotVote); TotVote = Nil;
|
1284
|
+
}
|
1285
|
+
}
|