Pyke, like Prolog, does not provide built-in support for certainty factors.
You can associate certainty values with facts as you show for son_of. Then you can combine certainty factors in rules, such as:
grandson_of
foreach
son_of($son, $father, $certainty1)
son_of($father, $grandfather, $certainty2)
$final_certainty = combine($certainty1, $certainty2)
assert
grandson_of($son, $grandfather, $final_certainty)
where "combine" is a Python function that combines certainty values in the way that makes sense for your application.
To answer your question about nesting facts. You can not nest facts directly as shown because a "fact" is not a standard Python object. But you can nest tuples. It is easy to rewrite facts as tuples, so rather than:
son_of(bruce, or(thomas, mike))
you can say:
son_of(bruce, (or, thomas, mike))
These could be combined for grandson_of as follows:
grandson_of
foreach
son_of($child, (or, *$fathers))
python grandfathers = []
forall
$father in $fathers
son_of($father, (or, *$grandfathers))
python grandfathers.extend($grandfathers)
$grandfathers = tuple(grandfathers)
assert
grandson_of($child, (or, *$grandfathers))
So, while there are fewer facts this way, the rules are more complicated.
Also, if you use separate son_of facts for each father, then Pyke can index the facts by father and very quickly find the sons of any given father. But using tuples of fathers, Pyke would have to search all of the son_of facts to find which ones include any given father in this tuple.
So it is generally better to represent individual facts separately, even though that means more facts.