Course-dependent skill structures¶
SkillAssignment links learning objects (lessons, exercises or activities)
to the skills they teach and require. It implements a CDSS-style workflow
with explicit labels and alternative prerequisite sets. The source workflow
is described in Hockemeyer’s CDSS vignette
(CDSS 0.3-1, 2026). The Python algorithms are independent implementations
using the attribution semantics in Falmagne & Doignon (2011), Equation 5.2.
Build and inspect a course¶
from knowledgespaces import LearningObject, SkillAssignment
course = SkillAssignment([
LearningObject("basics", frozenset({"a"})),
LearningObject("alternative-basics", frozenset({"b"})),
LearningObject("route-a", frozenset({"c"}), frozenset({"a"})),
LearningObject("route-b", frozenset({"c"}), frozenset({"b"})),
LearningObject("application", frozenset({"d", "e"}), frozenset({"c"})),
])
assert course.diagnostics().compliant
result = course.derive()
print(result.completed.requirements_for("application"))
# Two alternatives: {a, c} or {b, c}.
skill_space = result.skill_function.to_knowledge_space()
object_space = result.object_function.to_knowledge_space()
Within one record, all taught skills are acquired together and all required
skills are jointly required. Repeated records with the same identifier express
alternative requirements for one learning object; their taught sets must
agree. This differs from having two distinct teaching activities.
from_pairs(taught, required) accepts two (object, skill) tables.
from_matrices(objects, skills, taught, required) accepts aligned binary
matrices for single assignments. Explicit domains retain unused skills and
nonteaching objects for diagnosis.
diagnostics() reports untaught skills, nonteaching objects and overlaps
between taught and required skills. These semantic checks follow the three
CDSS compliance conditions. They do not establish acyclicity or empirical
validity. Derivation requires compliance; importing and inspecting an
incomplete course does not.
What is derived¶
Write \(T_l\) for an object’s taught skills and \(\mathcal R_l\) for its alternative required sets. The skill attribution assigns to a skill \(s\) all sets \(T_l\cup R\) with \(s\in T_l\) and \(R\in\mathcal R_l\). A set \(K\) is admitted exactly when every \(s\in K\) has such a set contained in \(K\). The object attribution admits a set \(M\) exactly when each \(l\in M\) has some \(R\in\mathcal R_l\) contained in \(\bigcup_{j\in M}T_j\). Both constructions produce union-closed families; their canonical surmise functions are obtained by the attribution algorithm.
Strict complete() replaces each original requirement alternative by its
inclusion-minimal containing skill states. It removes dominated alternatives.
An overlap with the object’s own taught skills raises
CurriculumCompletionError. Such a conflict can reflect a prerequisite
cycle or redundant teaching of skills that are acquired together.
It is not, by itself, a graph-cycle diagnosis.
complete(allow_cycles=True) instead completes each \(T_l\cup R\) jointly
and records the resulting minimal states after removing \(T_l\). This option
has an explicit static semantics and preserves the original skill space.
It is not a literal reproduction of CDSS’s allowcycles output, whose
source documentation leaves problematic cyclic results undefined.
derive() returns the original and completed assignment, canonical skill
and object surmise functions, and a relation for each function that is
quasi-ordinal. Relation pairs use (prerequisite, dependent) orientation.
Exactly one teaching object per skill is sufficient in a single assignment;
requirement alternatives can invalidate that shortcut. The implementation
checks quasi-ordinality of the resulting functions directly.
The object function closes the original object attribution, retaining the
original object domain. Completion alternatives do not create extra objects.
CDSS 0.3-1’s cdss_lo_csma2sf duplicates object columns in some such cases;
that behavior is deliberately not reproduced. Skills acquired together may
be equivalent in the derived space. Use quotient() to inspect these classes
and the refinement operations to introduce a justified finer structure.
Static compatibility and executable lessons¶
progress = course.reachability()
assert not progress.blocked_objects
print(progress.sequence)
reachability(initial_skills=()) tests prerequisites before adding the
skills taught by each object. It returns one deterministic feasible sequence,
all reachable skills and blocked objects. It does not optimize lesson cost,
model forgetting or estimate success probabilities.
For example, an activity teaching a while requiring b, paired with an
activity teaching b while requiring a, admits the static state {a, b}.
Neither activity is executable from the empty skill set. The joint completion
option preserves that distinction; it cannot invent an initial learning step.
Interchange and computational limits¶
read_assignment_csv / write_assignment_csv use two CDSS-style tables
with object and skill columns. JSON read/write functions also preserve
alternative requirements and absent domain labels. read_assignment and
write_assignment also support native XLSX/ODS workbooks; see
spreadsheet interchange. Pair-table export rejects cases
it cannot represent without loss; see I/O.
Derivation searches minimal clause combinations without materializing all
states. The number of alternatives can still grow exponentially.
max_candidates bounds intermediate candidate families and raises rather
than truncating the result. Subsequent state enumeration has its own size
guards. Neither resource limits nor successful derivation establish that an
expert-specified course accurately represents observed learning.
Run python cookbook/10_curriculum.py for a complete executable example.