2
# next line is a comment in tcl \
3
exec tclsh "$0" ${1+"$@"}
5
package require Tcl 8.3
8
###########################################################################
12
# An implementation of Inheritance Hierachies Inference (IHI)
14
# Classifies objects with varying patterns of attributes
17
# "A Simple and Efficient Algorithm for Inferring Inheritance Heirarchies"
18
# Ivan Moore, Tim Clement
19
# Department of Computer Science, University of Manchester
20
# Published in TOOLS Europe, 1996, Prentice-Hall
22
# tcl + tcldg implementation by John Ellson (ellson@lucent.com)
27
# where files contain one object per line each object described
28
# by a set of features, e.g:
36
# (contrary to this example, the features of each object do
37
# not have to be sorted, and the objects do not have to have unique
41
###########################################################################
43
set options(removefeatureedges) true
44
set options(removefeaturenodes) true
45
set options(removeobjectnodes) true
46
set options(labelclasses) true
50
set feature_set [gets $f]
51
if {![string length $feature_set]} {continue}
52
extract_features $g $feature_set
55
add_inheritance_edges $g
56
remove_unnecessary_edges $g
61
proc extract_features {g object} {
62
foreach feature $object {
63
if {![catch {$g addnode $feature type feature shape triangle}]} {
64
lappend feature_set $feature
67
if {![info exists feature_set]} {return}
69
# sort feature_set so that objects with the same features but in
70
# different order are not duplicated
71
set feature_set [lsort $feature_set]
72
if {[catch {$g addnode $feature_set type object shape box} o]} {
73
set o [$g findnode $feature_set]
75
foreach feature $feature_set {
76
catch {$g addedge $feature $o color red}
80
proc introduce_classes {g} {
83
# create the mapping graph -- introduces all the classes
84
foreach feature [$g listnodes type feature] {
86
foreach featureedge [$feature listoutedges] {
87
lappend objects [$featureedge headof]
88
if {$options(removefeatureedges)} {
92
set objects [lsort $objects]
94
# if the set of objects matches those of a previously added
95
# class node then this addnode operation returns the handle of
96
# that existing node rather than creating a new one.
97
set class [$g addnode $objects type class label { }]
98
$feature addedge $class
100
foreach class [$g listnodes type class] {
101
foreach o [$class showname] {
107
proc add_inheritance_edges {g} {
108
#sort the classes into buckets according to number of subclasses
109
# this aids the finding of subclasses which are proper subsets of
110
# superclasses since the subclass must have at least one less member
112
foreach class [$g listnodes type class] {
113
set count [$class countoutedges]
114
lappend classes($count) [$class showname]
115
if {$count > $max} {set max $count}
118
# add inheritance edges
119
for {set i $max} {$i > 1} {incr i -1} {
120
for {set j [expr $i -1]} {$j} {incr j -1} {
121
if {! [info exists classes($j)]} {continue}
122
foreach sub $classes($j) {
123
if {! [info exists classes($i)]} {continue}
124
set nsub [$g findnode $sub]
125
foreach sup $classes($i) {
126
# compare till mismatch
128
set length [llength $sup]
129
foreach object $sub {
131
for {set k $index} {$k < $length} {incr k} {
132
if {[string equal $object [lindex $sup $k]} {
137
if {! $found} {break}
141
set nsup [$g findnode $sup]
150
proc remove_unnecessary_edges {g} {
151
# remove inheritance edges that are unnecessary due to transtivity
152
foreach class [$g listnodes type class] {
154
foreach sub [$class listoutedges] {
155
lappend subs [$sub headof]
158
remove_transitive_recurse $class $sub
163
proc remove_transitive_recurse {sup sub} {
165
foreach subsub [$sub listoutedges] {
166
lappend subsubs [$subsub headof]
168
foreach subsub $subsubs {
169
remove_transitive_recurse $sup $subsub
170
foreach supsubsub [$subsub listinedges] {
171
if {[string equal [$supsubsub tailof] $sup]} {
178
proc label_classes {g} {
181
# label classes with their aggregate feature set
182
# i.e. the features they add plus the ones they inherit
183
if {$options(labelclasses)} {
184
foreach class [$g listnodes type class] {
185
foreach name [array names features] {unset features($name)}
186
foreach feature [list_features_recurse $class] {
187
set features($feature) 1
189
$class set label [lsort [array names features]]
194
proc list_features_recurse {sup} {
195
if {[string equal [$sup set type] feature]} {
196
set result [$sup showname]
199
foreach sup [$sup listinedges] {
200
set result [concat $result [list_features_recurse [$sup tailof]]]
209
# remove feature nodes
210
if {$options(removefeaturenodes)} {
211
foreach feature [$g listnodes type feature] {
216
# remove objects and mark instantiable classes
217
if {$options(removeobjectnodes)} {
218
foreach object [$g listnodes type object] {
219
set sup [$object listinedges]
220
if {[llength $sup] > 1} {
221
error "object has more than one class"
223
if {![string length $sup]} {
224
$object setattributes peripheries 2 shape {}
226
[$sup tailof] set peripheries 2
233
#######################
239
#set v [dgview dynadag]
242
#######################
244
if {[string length $argv]} {
250
#set g [dgnew digraphstrict]
251
set g [dgnew digraph]
256
set display "dot -Tps | gv -"
257
set f [open |$display w]