Suppose that you have given a formal context in a file called test.csv
that is
specified in CSV like so:
cat test.csv
(the space before the A is not important). The task you want to solve is to
compute the canonical base of this formal context in conexp-clj
.
Now, conexp-clj
does not support this input format for formal contexts directly (maybe
it should?), but it is very easy to get the job done using many-valued contexts as an
intermediate step. For this we first read the context as many-value context and define
mv-ctx
to be the result, like so
(def mv-ctx (read-mv-context "doc/tutorials/test.csv"))
Then mv-ctx
looks like this
mv-ctx
|A B C D
--+--------
1 |1 0 0 1
2 |1 0 1 1
3 |1 0 1 1
4 |1 0 0 0
Looks almost like a formal context, doesn’t it? The only thing that has to be
done now is to turn the 1s into crosses, and the 0s into non-crosses. For this
recall that the incidence relation of mv-ctx
is a hash-map that maps pairs of
objects and attributes from mv-ctx
to their corresponding values. In other
words,
(incidence mv-ctx)
{[2 D] 1,
[4 A] 1,
[3 B] 0,
[3 A] 1,
[4 D] 0,
[1 D] 1,
[4 C] 0,
[2 C] 1,
[3 C] 1,
[2 A] 1,
[1 A] 1,
[3 D] 1,
[4 B] 0,
[1 C] 0,
[2 B] 0,
[1 B] 0}
Thus, to convert mv-ctx
into a formal context we make a cross at [g m]
(where g
is
an object, and m
an attribute from mv-ctx
) whenever the value of this pair in
(incidence mv-ctx)
is one. This can be done like so
(def ctx (make-context (objects mv-ctx)
(attributes mv-ctx)
(fn [g m]
(= 1 ((incidence mv-ctx) [g m])))))
Let’s check whether this worked as expected
ctx
|A B C D
--+--------
1 |x . . x
2 |x . x x
3 |x . x x
4 |x . . .
Great! So to get the canonical base of ctx
you just call canonical-base
on ctx
and
you are done.
(canonical-base ctx)
((#{} ⟶ #{A}) (#{A C} ⟶ #{D}) (#{A B} ⟶ #{D C}))
In case you only want to compute the implications of the canonical base which have positive support, you can either filter out those implications which have support 0
(filter #(pos? (support % ctx))
(canonical-base ctx))
((#{} ⟶ #{A}) (#{A C} ⟶ #{D}))
or you can instruct canonical-base
to not even compute implications with
support 0, by using the optional third argument of canonical-base
, which is a
predicate on the premises of the implications which should be computed:
(canonical-base ctx #{} #(pos? (support % ctx)))
((#{} ⟶ #{A}) (#{A C} ⟶ #{D}))