Knowledge Structures and Relations¶
Surmise relations¶
A surmise relation (or prerequisite relation) is a partial order on items. If \((a, b)\) is in the relation, then mastering \(a\) is a prerequisite for mastering \(b\).
from knowledgespaces import SurmiseRelation
rel = SurmiseRelation(
items=["a", "b", "c", "d"],
relations=[("a", "b"), ("a", "c"), ("b", "d"), ("c", "d")],
)
Transitive closure and Hasse diagram¶
closure = rel.transitive_closure() # all implied relations
hasse = closure.transitive_reduction() # minimal representation
print(closure.levels()) # {'a': 0, 'b': 1, 'c': 1, 'd': 2}
print(hasse.minimal_items()) # {'a'}
The transitive closure adds all transitively implied edges. The transitive reduction (Hasse diagram) keeps only the direct edges.
Knowledge structures¶
A knowledge structure \(\mathcal{K}\) on a domain \(Q\) is a family of subsets of \(Q\) (called knowledge states) that contains \(\emptyset\) and \(Q\).
from knowledgespaces import KnowledgeStructure
ks = KnowledgeStructure.from_surmise_relation(closure)
print(ks.n_states) # number of valid states
Structural properties¶
Property |
Meaning |
Check |
|---|---|---|
Knowledge space |
Closed under \(\cup\) |
|
Closure space |
Closed under \(\cap\) |
|
Well-graded |
Every state has a predecessor |
|
Learning space |
Knowledge space + well-graded |
|
Fringes¶
For a state \(K\):
Inner fringe: items \(q \in K\) such that \(K \setminus \{q\} \in \mathcal{K}\) — the most recently consolidated items.
Outer fringe: items \(q \notin K\) such that \(K \cup \{q\} \in \mathcal{K}\) — what the student is ready to learn.
state = frozenset({"a", "b"})
print(ks.outer_fringe(state)) # next learnable items
print(ks.inner_fringe(state)) # most recently consolidated
A hanging state is a non-empty state with empty inner fringe — it
cannot be reached by adding items one at a time, and usually signals a
problem in the structure. Use ks.hanging_states() to list them.
Base of the space¶
The base is the minimal generating family under union. Every state in the space can be expressed as a union of base elements.
print(ks.base())
Learning paths¶
A learning path is a sequence of states from \(\emptyset\) to \(Q\) where each step adds exactly one item:
for path in ks.learning_paths():
print([set(s) for s in path])
Surmise functions¶
A surmise function \(\sigma\) generalises the surmise relation by allowing multiple alternative clauses per item. Each clause represents a possible minimal foundation for mastering an item.
When each item has exactly one clause, the surmise function reduces to a surmise relation (the ordinal case).
from knowledgespaces import SurmiseFunction
sf = SurmiseFunction(
domain={"a", "b", "c", "d", "e"},
clauses={
"a": [{"a"}],
"b": [{"b", "d"}, {"a", "b", "c"}, {"b", "c", "e"}],
"c": [{"a", "b", "c"}, {"b", "c", "e"}],
"d": [{"b", "d"}],
"e": [{"b", "c", "e"}],
},
)
Here item b has three alternative foundations: you can master b by
first mastering d, or by mastering both a and c, or by mastering
both c and e.
Properties¶
Property |
Meaning |
Check |
|---|---|---|
Ordinal |
One clause per item (equivalent to surmise relation) |
|
Discriminative |
Distinct items have distinct clause families |
|
Acyclic |
No cycles in the prerequisite graph \(R_\sigma\) |
|
Deriving a knowledge space¶
A surmise function uniquely defines a (granular) knowledge space (Theorem 5.2.5, Learning Spaces). A set \(K\) is a state iff every item in \(K\) has at least one clause contained in \(K\):
ks = sf.to_knowledge_space()
print(ks.n_states) # 10
print(ks.is_knowledge_space) # True
Extracting a surmise function from a knowledge space¶
Conversely, the surmise function of a knowledge space is derived from its atoms — the clause for each item is the set of atoms at that item:
sf2 = ks.surmise_function()
assert sf2 == sf # roundtrip identity
High-level API¶
import knowledgespaces as ks
structure = ks.space_from_surmise_function({
"a": [["a"]],
"b": [["b", "d"], ["a", "b", "c"], ["b", "c", "e"]],
"c": [["a", "b", "c"], ["b", "c", "e"]],
"d": [["b", "d"]],
"e": [["b", "c", "e"]],
})
Converting to/from surmise relations¶
from knowledgespaces import SurmiseRelation
# Surmise relation → ordinal surmise function
rel = SurmiseRelation(["a", "b", "c"], [("a", "b"), ("b", "c")])
sf_ordinal = SurmiseFunction.from_surmise_relation(rel)
assert sf_ordinal.is_ordinal
# Back to surmise relation (only if ordinal)
rel2 = sf_ordinal.to_surmise_relation()