API Reference

Complete reference generated from source code docstrings.

High-level API

High-level convenience API for common KST operations.

Designed for users who want simple function calls without needing to construct intermediate objects. Accepts plain Python types (lists, dicts, tuples) instead of library-specific classes.

This module re-exports its functions at the package level, so you can write:

import knowledgespaces
structure = ks.space_from_prerequisites(...)

instead of dealing with SurmiseRelation, KnowledgeStructure, etc.

knowledgespaces.api.space_from_prerequisites(items, prerequisites)[source]

Build a knowledge structure from prerequisite pairs.

Parameters:
itemslist[str]

All item names.

prerequisiteslist[tuple[str, str]]

Pairs (a, b) meaning ‘a is a prerequisite of b’.

Returns:
KnowledgeStructure

The ordinal knowledge structure (closed under union and intersection).

Parameters:
Return type:

KnowledgeStructure

Examples

>>> ks = space_from_prerequisites(
...     ["add", "sub", "mul"],
...     [("add", "sub"), ("sub", "mul")],
... )
>>> ks.n_states
4
knowledgespaces.api.structure_from_skill_map(skill_map, skill_prerequisites=None)[source]

Derive a knowledge structure from a skill map (CB-KST).

Note: with conjunctive skill maps (items requiring multiple skills) the result is not necessarily union-closed and therefore may not be a knowledge space. Use is_knowledge_space to check.

Parameters:
skill_mapdict[str, list[str]]

For each item, the list of skills it requires. Example: {“q1”: [“s_add”], “q2”: [“s_add”, “s_carry”]}

skill_prerequisiteslist[tuple[str, str]] or None

Pairs (a, b) meaning ‘skill a is a prerequisite of skill b’. If None, skills are treated as independent.

Returns:
KnowledgeStructure

The derived knowledge structure (may or may not be a space).

Parameters:
Return type:

KnowledgeStructure

Examples

>>> ks = structure_from_skill_map(
...     {"q1": ["s1"], "q2": ["s1", "s2"]},
...     [("s1", "s2")],
... )
knowledgespaces.api.space_from_skill_map(skill_map, skill_prerequisites=None)[source]

Deprecated: use structure_from_skill_map() instead.

This function was renamed because with conjunctive skill maps the result is not necessarily a knowledge space.

Parameters:
Return type:

KnowledgeStructure

knowledgespaces.api.assess(structure, responses, beta=0.1, eta=0.2, prior=None)[source]

Assess a student’s knowledge state from their responses.

Parameters:
structureKnowledgeStructure

The knowledge structure.

responsesdict[str, bool] or list[tuple[str, bool]]

Observed responses. Two formats accepted:

  • dict: one observation per item, e.g. {"add": True, "sub": False}

  • list of tuples: multiple observations allowed per item (from different instances), e.g. [("add", True), ("add", True), ("sub", False)]

In the list format, the same item can appear multiple times. Each observation updates the posterior independently (local independence assumption).

betafloat or dict[str, float]

Slip parameter (scalar or per-item).

etafloat or dict[str, float]

Guess parameter (scalar or per-item).

priordict[frozenset[str], float] or None

Optional prior over states (e.g. from a previous EM fit). If None, a uniform prior is used.

Returns:
dict

Keys: ‘state’ (most likely state as a set), ‘probability’, ‘mastery’ (per-item mastery probabilities), ‘inner_fringe’, ‘outer_fringe’.

Parameters:
Return type:

dict

Examples

One observation per item:

result = assess(structure, {"add": True, "sub": True, "mul": False})

Multiple instances of the same item:

result = assess(structure, [("add", True), ("add", True), ("sub", False)])
knowledgespaces.api.assess_from_fit(structure, fit, responses)[source]

Assess using estimated parameters from fit_blim().

Equivalent to assess(structure, responses, beta=..., eta=..., prior=...) with values taken from the fit result.

Parameters:
structureKnowledgeStructure

The knowledge structure (same used for fitting).

fitdict

Output of fit_blim().

responsesdict or list

Observed responses (same format as assess()).

Parameters:
Return type:

dict

knowledgespaces.api.adaptive_assess(structure, ask_fn, *, instances=None, beta=0.1, eta=0.2, prior=None, threshold=0.85, max_questions=25)[source]

Run a complete adaptive assessment.

Parameters:
structureKnowledgeStructure

The knowledge structure.

ask_fncallable

If instances is None: takes an item name (str) and returns bool. If instances is provided: takes an instance ID (str) and returns bool.

instancesdict[str, list[str]] or None

Optional mapping {item: [instance_id, …]} for multi-instance assessment. When provided, the engine selects the best un-asked instance (not item) and passes its ID to ask_fn. Different instances of the same item are treated as equivalent by the BLIM.

beta, etafloat or dict[str, float]

BLIM parameters (scalar or per-item).

priordict[frozenset[str], float] or None

Optional prior over states. If None, uniform.

thresholdfloat

Stop when most likely state reaches this probability.

max_questionsint

Maximum number of questions to ask.

Returns:
dict

Keys: ‘state’, ‘probability’, ‘mastery’, ‘inner_fringe’, ‘outer_fringe’, ‘questions_asked’ (int), ‘history’ (list of (instance_or_item, item, response) tuples).

Parameters:
Return type:

dict

Examples

Simple (one instance per item):

result = adaptive_assess(structure, lambda item: item in {"add", "sub"})

With multiple instances:

result = adaptive_assess(
    structure,
    lambda inst_id: ask_student(inst_id),
    instances={
        "addition": ["3+2", "7+5", "12+9"],
        "subtraction": ["8-3", "15-7"],
    },
)
knowledgespaces.api.adaptive_assess_from_fit(structure, fit, ask_fn, *, instances=None, threshold=0.85, max_questions=25)[source]

Run adaptive assessment using parameters from fit_blim().

Equivalent to adaptive_assess(structure, ask_fn, beta=..., eta=..., prior=...) with values taken from the fit result.

Parameters:
structureKnowledgeStructure

The knowledge structure (same used for fitting).

fitdict

Output of fit_blim().

ask_fncallable

Question function (same as adaptive_assess()).

instances, threshold, max_questions

Passed through to adaptive_assess().

Parameters:
Return type:

dict

knowledgespaces.api.fit_blim(structure, items, responses, counts=None)[source]

Estimate BLIM parameters from response data via EM.

Parameters:
structureKnowledgeStructure

The knowledge structure.

itemslist[str]

Item names (column labels for the response matrix).

responsesarray-like

Binary response matrix, shape (n_patterns, n_items).

countsarray-like or None

Optional frequency of each pattern.

Returns:
dict

Keys: ‘beta’ (dict item→float), ‘eta’ (dict item→float), ‘pi’ (dict frozenset→float, state prior probabilities), ‘states’ (list of frozensets, ordered as in pi), ‘log_likelihood’ (float), ‘converged’ (bool), ‘n_iterations’ (int), ‘gof’ (dict with G2, df, p_value, npar, AIC, BIC).

Parameters:
Return type:

dict

Examples

>>> result = fit_blim(structure, ["a","b","c"], [[1,1,1],[1,1,0],[1,0,0],[0,0,0]])
>>> result["converged"]
True

knowledgespaces.structures

Surmise relations (partial orders) on item domains.

A surmise relation is a partial order (reflexive, antisymmetric, transitive) on a set of items that encodes prerequisite dependencies. If (a, b) is in the relation, then mastering item a is a prerequisite for mastering item b.

Note: the class stores only strict pairs (a != b) and treats reflexivity as implicit. The QUERY algorithm (Block 1) assumes antisymmetry during pruning, consistent with the standard KST formulation where the surmise relation is a partial order (Learning Spaces, Falmagne & Doignon, 2011).

References:

Falmagne, J.-C., & Doignon, J.-P. (2011). Learning Spaces. Springer-Verlag.

class knowledgespaces.structures.relations.SurmiseRelation(items, relations)[source]

A partial order on items representing prerequisite dependencies.

The relation is stored as a set of directed pairs (a, b) meaning ‘a is a prerequisite of b’. Self-loops are excluded from storage (reflexivity is implicit). Antisymmetry is assumed by the QUERY algorithm but not enforced at construction time; use is_antisymmetric() to verify.

Parameters:
itemsCollection[str]

The domain of items.

relationsCollection[tuple[str, str]]

Pairs (a, b) where a is a prerequisite of b. Self-loops (a, a) are silently ignored.

Parameters:
property items: frozenset[str]
property relations: frozenset[tuple[str, str]]
property size: int

Number of items in the domain.

transitive_closure()[source]

Compute the transitive closure (Floyd-Warshall).

Returns a new SurmiseRelation containing all transitively implied pairs. If (a, b) and (b, c) are in the relation, (a, c) is added.

Return type:

SurmiseRelation

transitive_reduction()[source]

Compute the transitive reduction (Hasse diagram).

Returns a new SurmiseRelation containing only the direct prerequisite edges — removes any edge (a, c) when there exists an intermediate item b such that (a, b) and (b, c) are both in the transitive closure.

Return type:

SurmiseRelation

prerequisites_of(item)[source]

Return the prerequisites of an item in the stored relation.

Returns only items directly related in this instance’s edges. To get all transitive prerequisites, call transitive_closure() first, then query the result.

Parameters:

item (str)

Return type:

frozenset[str]

successors_of(item)[source]

Return the successors of an item in the stored relation.

Returns only items directly related in this instance’s edges. To get all transitive successors, call transitive_closure() first, then query the result.

Parameters:

item (str)

Return type:

frozenset[str]

minimal_items()[source]

Items with no prerequisites (bottom of the order).

Return type:

frozenset[str]

maximal_items()[source]

Items that are not prerequisite of anything (top of the order).

Return type:

frozenset[str]

levels()[source]

Compute topological levels.

Level 0 items have no prerequisites. Level n items have max predecessor level n-1.

Returns:
dict[str, int]

Mapping from item to its level. Items in cycles are omitted.

Return type:

dict[str, int]

is_antisymmetric()[source]

Check if the relation is antisymmetric (a partial order).

Return type:

bool

to_adjacency_dict()[source]

Return {item: set of direct prerequisites}.

Return type:

dict[str, set[str]]

to_matrix()[source]

Return (sorted items, adjacency matrix).

matrix[i][j] = 1 iff items[i] is a prerequisite of items[j].

Return type:

tuple[list[str], list[list[int]]]

classmethod from_adjacency_matrix(items, matrix)[source]

Create from an adjacency matrix.

matrix[i][j] = 1 means items[i] is a prerequisite of items[j].

Parameters:
Return type:

SurmiseRelation

classmethod from_prerequisites_dict(prereqs)[source]

Create from {item: [its prerequisites]}.

Parameters:
prereqsMapping[str, Collection[str]]

For each item, the collection of its prerequisites.

Parameters:

prereqs (Mapping[str, Collection[str]])

Return type:

SurmiseRelation

Knowledge structures, spaces, and learning spaces.

A knowledge structure on a domain Q is a family K of subsets of Q (called knowledge states) that contains at least the empty set and Q itself.

Special cases: - Knowledge space: closed under set union. - Closure space: closed under set intersection. - Learning space: a well-graded knowledge space (equivalently, an antimatroid).

References:

Doignon, J.-P., & Falmagne, J.-C. (1999). Knowledge Spaces. Springer-Verlag.

Falmagne, J.-C., & Doignon, J.-P. (2011). Learning Spaces. Springer-Verlag.

class knowledgespaces.structures.knowledge_structure.KnowledgeStructure(domain, states)[source]

A family of knowledge states over a domain of items.

Parameters:
domainCollection[str]

The set of all items.

statesCollection[Collection[str]]

The knowledge states (subsets of domain). The empty set and the full domain are added automatically if missing.

Parameters:
  • domain (Collection[str])

  • states (Collection[Collection[str]])

property domain: frozenset[str]
property states: frozenset[frozenset[str]]
property n_items: int
property n_states: int
property is_knowledge_space: bool

True if closed under union.

property is_closure_space: bool

True if closed under intersection.

property is_well_graded: bool

True if every non-empty state has an immediate predecessor.

A state K has an immediate predecessor if there exists q in K such that K {q} is also a state.

Note

This checks a local condition (every state has at least one single-item predecessor), not the full well-gradedness definition which requires a tight path between every pair of comparable states (Falmagne & Doignon, 2011, Def. 1.44). For knowledge spaces (union-closed families) the two conditions are equivalent (Theorem 1.49, ibid.), so is_learning_space (which combines is_knowledge_space and is_well_graded) is correct. On a bare KnowledgeStructure that is not union-closed, this property may return True even when full well-gradedness does not hold.

property is_accessible: bool

True if every state is reachable from the empty set.

Reachability means there exists a chain from ∅ to the state where each step adds exactly one item.

property is_learning_space: bool

True if this is a well-graded knowledge space.

Equivalent to: knowledge space + well-graded. Equivalent to: no hanging states + ∅ and Q present.

inner_fringe(state)[source]

Items whose removal yields another valid state.

These represent the ‘most recently consolidated’ items in the state.

Parameters:

state (frozenset[str])

Return type:

frozenset[str]

outer_fringe(state)[source]

Items whose addition yields another valid state.

These represent the ‘next learnable’ items from this state.

Parameters:

state (frozenset[str])

Return type:

frozenset[str]

hanging_states()[source]

Non-empty states with empty inner fringe.

A hanging state cannot be reached incrementally — it indicates a structural problem in the learning space.

Return type:

list[frozenset[str]]

atoms()[source]

Atoms of the knowledge structure.

An atom at item q is a minimal state containing q — i.e. a state K such that q ∈ K and no proper subset of K that is also a state contains q. An item can have more than one atom.

In a closure space (closed under intersection) each item has exactly one atom (the intersection of all states containing it). In a knowledge space (closed under union) the collection of all atoms equals the base.

Returns:
dict[str, list[frozenset[str]]]

Mapping from each item to the list of its atoms (minimal states containing that item), sorted by size then content.

Return type:

dict[str, list[frozenset[str]]]

References

Doignon & Falmagne (1999), Knowledge Spaces, Def. 1.23. Falmagne & Doignon (2011), Learning Spaces, Chapter 1.

base()[source]

Minimal generating family under union.

The base B of a knowledge space K is the smallest family such that K equals the closure of B under union (plus ∅). A state K is in the base iff it cannot be expressed as the union of states in K that are strictly contained in K.

Only meaningful for knowledge spaces (closed under union).

Return type:

list[frozenset[str]]

states_by_size()[source]

Distribution of states by cardinality.

Return type:

dict[int, int]

learning_paths(target=None, max_paths=100)[source]

Find learning paths from ∅ to target.

A learning path is a maximal chain where each step adds one item.

Parameters:
targetfrozenset[str] or None

Target state. Defaults to the full domain.

max_pathsint

Maximum number of paths to return (to avoid combinatorial explosion).

Parameters:
Return type:

list[list[frozenset[str]]]

classmethod from_surmise_relation(relation)[source]

Build the ordinal knowledge structure from a surmise relation.

A state is valid iff for every item in the state, all its prerequisites are also in the state (downward closure).

The resulting structure is always closed under both union and intersection (it is a distributive lattice).

Parameters:

relation (SurmiseRelation)

Return type:

KnowledgeStructure

classmethod from_states(states)[source]

Build from an explicit collection of states.

The domain is inferred as the union of all states.

Parameters:

states (Collection[Collection[str]])

Return type:

KnowledgeStructure

intersection_with(other)[source]

Intersection of two structures: states present in both.

Parameters:

other (KnowledgeStructure)

Return type:

KnowledgeStructure

projection(sub_domain)[source]

Project (restrict) the structure to a subset of items.

For each state K, the projected state is K ∩ sub_domain. Only items present in the original domain are retained.

Raises:
ValueError

If sub_domain contains items not in the original domain.

Parameters:

sub_domain (Collection[str])

Return type:

KnowledgeStructure

surmise_relation()[source]

Extract the surmise relation implied by this structure.

Item a is a prerequisite of b iff every state containing b also contains a.

Return type:

SurmiseRelation

knowledgespaces.query

Core types for the QUERY algorithm.

Defines the Query object and the result containers used across all blocks of the algorithm.

class knowledgespaces.query.types.InferenceSource(*values)[source]

How a query answer was determined.

class knowledgespaces.query.types.Query(antecedent, consequent)[source]

A single query: ‘Does failing all items in A imply failing q?’

For pair queries (Block 1): antecedent has 1 item. For group queries (Block 2+): antecedent has 2+ items. For Qmax minimality test: antecedent = Q without {q}.

Parameters:
antecedentfrozenset[str]

The set A of items.

consequentstr

The target item q.

Parameters:
classmethod pair(a, q)[source]

Create a pair query: a -> q.

Parameters:
Return type:

Query

classmethod group(A, q)[source]

Create a group query: A -> q.

Parameters:
Return type:

Query

class knowledgespaces.query.types.QueryAnswer(query, answer, source)[source]

A resolved query with its answer and source.

Parameters:
property was_asked: bool

True if this answer came from an expert, not inference.

Expert protocols for the QUERY algorithm.

An expert is any callable that answers prerequisite queries. This module defines the protocol and provides built-in implementations for testing and interactive use.

References:

Koppen, M., & Doignon, J.-P. (1990). How to build a knowledge space by querying an expert. Journal of Mathematical Psychology, 34, 311-331.

class knowledgespaces.query.expert.Expert(*args, **kwargs)[source]

Protocol for expert query functions.

An expert receives a Query and returns True (positive) or False (negative).

The semantic of a positive answer to Query(A, q) is: ‘If a student fails all items in A, they will also fail q.’ Equivalently: mastering q requires mastering at least one item in A.

class knowledgespaces.query.expert.PresetExpert(answers=None, default=False)[source]

Expert with predetermined answers, for testing and replay.

Parameters:
answersdict[tuple[frozenset[str], str], bool]

Mapping from (antecedent, consequent) to answer.

defaultbool

Answer for queries not in the mapping.

Parameters:
classmethod from_relation(relations)[source]

Create an expert that answers based on a known surmise relation.

A pair query (a -> q) is positive iff (a, q) is in the relation. A group query (A -> q) is positive iff some a in A has (a, q).

This simulates an expert who knows the true prerequisite structure. Suitable for testing the query algorithm against a known ground truth.

Parameters:

relations (Collection[tuple[str, str]])

Return type:

PresetExpert

class knowledgespaces.query.expert.CallbackExpert(fn)[source]

Expert that delegates to a user-supplied function.

Parameters:
fnCallable[[frozenset[str], str], bool]

A function that takes (antecedent_set, consequent) and returns bool.

Parameters:

fn (Callable[[frozenset[str], str], bool])

Block 1 of the QUERY algorithm: surmise relation discovery.

Extracts prerequisite relations between items by querying an expert with pair queries (a -> q) and optional Qmax minimality tests.

Implementation note — multi-phase optimization.

The standard QUERY Block 1 (Koppen & Doignon, 1990; Learning Spaces, Falmagne & Doignon, 2011, Ch. 4) iterates over all item pairs and uses transitivity + antisymmetry to prune redundant queries.

This implementation splits Block 1 into four phases as an optimization that maximises early inference opportunities:

  • Phase 0 (optional): Qmax test — group query Q{q} → q for each item, identifies globally minimal items that need no pair queries.

  • Phase 1: Forward pass — pair queries with transitivity/antisymmetry pruning, processing items in input order.

  • Phase 2: Bottom-up pass — re-explores from minimal items outward, exploiting newly discovered transitivity.

  • Phase 3: Final verification sweep — covers any pair not yet resolved by Phases 1–2, guaranteeing completeness.

The result is identical to the standard single-pass algorithm: the same surmise relation is discovered, but typically with fewer expert queries thanks to the ordering strategy. Completeness is guaranteed by Phase 3.

References:

Koppen, M., & Doignon, J.-P. (1990). How to build a knowledge space by querying an expert. Journal of Mathematical Psychology, 34, 311-331.

Falmagne, J.-C., & Doignon, J.-P. (2011). Learning Spaces, Chapter 4. Springer-Verlag.

class knowledgespaces.query.block1.Block1Stats(pair_queries=0, group_queries=0, deduced_by_transitivity=0, skipped_by_antisymmetry=0)[source]

Statistics from Block 1 execution.

Parameters:
  • pair_queries (int)

  • group_queries (int)

  • deduced_by_transitivity (int)

  • skipped_by_antisymmetry (int)

class knowledgespaces.query.block1.Block1Result(relation, closure, minimal_global, minimal_local, stats, log=<factory>)[source]

Result of Block 1 query phase.

Parameters:
knowledgespaces.query.block1.run_block1(items, expert, *, use_qmax=True)[source]

Execute Block 1 of the QUERY algorithm.

Parameters:
itemslist[str]

The domain of items.

expertExpert

The expert to query.

use_qmaxbool

If True, run Phase 0 (Qmax minimality test). Default True.

Returns:
Block1Result

Discovered surmise relation, closure, minimal items, and stats.

Parameters:
Return type:

Block1Result

Block 2+ of the QUERY algorithm: learning space refinement.

Refines an ordinal space (from Block 1) into a learning space by querying group prerequisites. Block N tests antecedent sets of size N.

Block 2 handles antecedent size 2, Block 3 size 3, etc. The algorithm is identical at every level — only the antecedent cardinality changes.

Three inference mechanisms reduce expert queries:

  • Negative monotonicity: if q is globally minimal (Qmax), then (A,q)=NO.

  • Positive monotonicity: if any subset of A already implies q, then (A,q)=YES.

  • Structural test (Theorem 43): if answering YES would create hanging states, then the answer must be NO.

References:

Doignon, J.-P., & Falmagne, J.-C. (1999). Knowledge Spaces, Chapter 4. Springer-Verlag.

class knowledgespaces.query.block2.BlockNStats(expert_queries=0, inferred_negative_monotonicity=0, inferred_positive_monotonicity=0, inferred_structural=0)[source]

Statistics from a Block N execution.

Parameters:
  • expert_queries (int)

  • inferred_negative_monotonicity (int)

  • inferred_positive_monotonicity (int)

  • inferred_structural (int)

class knowledgespaces.query.block2.BlockNResult(structure, positive, negative, stats, log=<factory>)[source]

Result of a Block N query phase.

Parameters:
knowledgespaces.query.block2.run_block_n(items, structure, prior_positive, expert, *, antecedent_size=2, minimal_global=frozenset({}))[source]

Execute Block N of the QUERY algorithm.

Parameters:
itemslist[str]

The domain of items.

structureKnowledgeStructure

The current knowledge structure to refine.

prior_positiveset[tuple[frozenset[str], str]]

Positive relations from all previous blocks. Each entry is (antecedent_set, consequent). Single-item antecedents come from Block 1’s closure: {(frozenset({a}), b) for (a,b) in closure}.

expertExpert

The expert to query.

antecedent_sizeint

Size of antecedent sets to test. Default 2 (standard Block 2).

minimal_globalfrozenset[str]

Items certified globally minimal by Qmax in Block 1.

Returns:
BlockNResult

Refined structure, discovered relations, and stats.

Parameters:
Return type:

BlockNResult

Full QUERY pipeline: Block 1 → L1 → Block 2 → … → Block N.

Orchestrates the complete derivation of a learning space from expert queries.

class knowledgespaces.query.pipeline.QueryPipelineResult(structure, block1, block_n_results=<factory>)[source]

Result of the full QUERY pipeline.

Parameters:
knowledgespaces.query.pipeline.run_query(items, expert, *, use_qmax=True, max_antecedent_size=2)[source]

Run the full QUERY algorithm.

Parameters:
itemslist[str]

The domain of items.

expertExpert

The expert to query.

use_qmaxbool

If True, use Qmax minimality test in Block 1.

max_antecedent_sizeint

Maximum antecedent size for group queries. Default 2 (Block 2 only). Set to 3 for Block 3, etc.

Returns:
QueryPipelineResult

The derived learning space with full traceability.

Parameters:
Return type:

QueryPipelineResult

knowledgespaces.derivation

Skill maps: the mapping from items to required skills.

A skill map μ assigns to each item q the set of skills μ(q) needed to solve it. The problem function p(C) = {q ∈ Q | μ(q) ⊆ C} maps a competence state to the set of solvable items.

References:

Doignon, J.-P., & Falmagne, J.-C. (1999). Knowledge Spaces, Chapter 7. Springer-Verlag.

class knowledgespaces.derivation.skill_map.SkillMap(items, skills, mapping)[source]

Mapping from items to the skills required to solve them.

Parameters:
itemsCollection[str]

The domain of items.

skillsCollection[str]

The set of all skills.

mappingMapping[str, Collection[str]]

For each item, the skills required to solve it: μ(q).

Raises:
ValueError

If an item references a skill not in the skills set, or if mapping keys don’t match items.

Parameters:
  • items (Collection[str])

  • skills (Collection[str])

  • mapping (Mapping[str, Collection[str]])

skills_for(item)[source]

Return μ(q): skills required by item q.

Parameters:

item (str)

Return type:

frozenset[str]

problem_function(competence)[source]

Compute p(C) = {q ∈ Q | μ(q) ⊆ C}.

An item is solvable iff ALL its required skills are present in the competence state.

Parameters:

competence (frozenset[str])

Return type:

frozenset[str]

to_matrix()[source]

Return (items, skills, binary matrix).

matrix[i][j] = 1 iff skill skills[j] is required by items[i].

Return type:

tuple[list[str], list[str], list[list[int]]]

classmethod from_matrix(items, skills, matrix)[source]

Create from a binary matrix.

matrix[i][j] = 1 means items[i] requires skills[j].

Raises:
ValueError

If matrix dimensions don’t match items/skills, or if values are not 0 or 1.

Parameters:
Return type:

SkillMap

Competence-Based Knowledge Space Theory (CB-KST) derivation.

Given a skill map μ (items → skills) and a surmise relation on skills, derives the knowledge structure on items through the problem function.

The pipeline: 1. Build competence structure C from skill prerequisites. 2. Apply problem function p(C) to each competence state. 3. Collect unique knowledge states → knowledge structure K.

Additionally provides conversion from skill prerequisites to item prerequisites (surmise relation on items).

References:

Doignon, J.-P., & Falmagne, J.-C. (1999). Knowledge Spaces, Chapter 7. Springer-Verlag.

class knowledgespaces.derivation.cbkst.CBKSTResult(competence_structure, knowledge_structure, mapping, skill_map, skill_relation)[source]

Result of a CB-KST derivation.

Parameters:
knowledgespaces.derivation.cbkst.derive_knowledge_structure(skill_map, skill_relation)[source]

Derive a knowledge structure from a competence model.

Parameters:
skill_mapSkillMap

The mapping μ: items → required skills.

skill_relationSurmiseRelation

Prerequisite relation on skills (will be transitively closed). Must cover all skills referenced by the skill map.

Returns:
CBKSTResult

Contains the competence structure C, knowledge structure K, and the mapping from competence states to knowledge states.

Raises:
ValueError

If skill_map references skills not in skill_relation’s domain.

Parameters:
Return type:

CBKSTResult

knowledgespaces.derivation.cbkst.skill_to_item_relation(skill_map, skill_relation)[source]

Derive an item surmise relation from skills.

Item p is a prerequisite of item q iff every skill required by p is “covered” by q — meaning the skill is either directly required by q or is a prerequisite of a skill required by q.

Formally:

covers(q) = μ(q) ∪ {s : ∃t ∈ μ(q), (s,t) ∈ closure} p ≺ q iff μ(p) ⊆ covers(q)

Note: the result is already transitively closed.

Parameters:
skill_mapSkillMap

The mapping μ: items → required skills.

skill_relationSurmiseRelation

Prerequisite relation on skills.

Returns:
SurmiseRelation

Surmise relation on items.

Raises:
ValueError

If skill_map references skills not in skill_relation’s domain.

Parameters:
Return type:

SurmiseRelation

knowledgespaces.assessment

Basic Local Independence Model (BLIM) and Bayesian state inference.

The BLIM defines the probability of a response pattern given a knowledge state, using two parameters: - beta (slip): P(incorrect | item mastered) - eta (guess): P(correct | item not mastered)

The model assumes local independence: responses to different items are conditionally independent given the knowledge state.

StatePosterior maintains the Bayesian probability distribution over knowledge states and supports sequential updating.

References:

Doignon, J.-P., & Falmagne, J.-C. (1999). Knowledge Spaces, Chapter 12. Springer-Verlag.

Falmagne, J.-C., & Doignon, J.-P. (2011). Learning Spaces, Chapter 12. Springer-Verlag.

class knowledgespaces.assessment.blim.BLIMParams(beta, eta)[source]

Parameters for the Basic Local Independence Model.

Parameters:
betafloat or dict[str, float]

Slip probability: P(incorrect response | item mastered). A scalar applies the same value to every item; a dict maps each item to its own slip probability. Values must be in [0, 1).

etafloat or dict[str, float]

Guess probability: P(correct response | item not mastered). A scalar applies the same value to every item; a dict maps each item to its own guess probability. Values must be in [0, 1).

The constraint beta + eta < 1 (per item, when dicts are used)
ensures model identifiability: a mastered item must be more likely
to get a correct response than an unmastered one.
Parameters:
class knowledgespaces.assessment.blim.BLIM(structure, params)[source]

Basic Local Independence Model on a knowledge structure.

Parameters:
structureKnowledgeStructure

The knowledge structure defining valid states.

paramsBLIMParams

Slip and guess parameters (scalar or per-item dict).

Parameters:
likelihood(item, response, state)[source]

Compute P(response | state) for a single item and state.

Parameters:
itemstr

The item being assessed.

responsebool

True for correct, False for incorrect.

statefrozenset[str]

The knowledge state. Must be a state in the structure.

Raises:
ValueError

If item is not in the domain or state is not in the structure.

Parameters:
Return type:

float

likelihood_vector(item, response)[source]

Compute P(response | state) for all states.

Returns a 1D array of length n_states, where element i is P(response | states[i]).

Raises:
ValueError

If item is not in the domain.

Parameters:
Return type:

ndarray

class knowledgespaces.assessment.blim.StatePosterior(blim, probabilities)[source]

Bayesian probability distribution over knowledge states.

Immutable: update() returns a new StatePosterior.

Parameters:
blimBLIM

The BLIM model defining states and likelihoods.

probabilitiesnp.ndarray

Probability for each state. Must sum to 1.

Parameters:
  • blim (BLIM)

  • probabilities (np.ndarray)

classmethod uniform(blim)[source]

Create a uniform prior (maximum uncertainty).

Parameters:

blim (BLIM)

Return type:

StatePosterior

classmethod from_prior(blim, prior)[source]

Create from an explicit prior mapping states to probabilities.

Parameters:
blimBLIM

The BLIM model.

priordict[frozenset[str], float]

Mapping from each state to its prior probability. Must cover all states and sum to 1.

Parameters:
Return type:

StatePosterior

update(item, response)[source]

Return a new posterior after observing a response.

Applies Bayes’ theorem:

P(state | response) ∝ P(response | state) × P(state)

Parameters:
itemstr

The item that was assessed. Must be in the structure’s domain.

responsebool

True for correct, False for incorrect.

Raises:
ValueError

If item is not in the domain, or if the observation has zero probability under all states with nonzero prior (impossible evidence).

Parameters:
Return type:

StatePosterior

property entropy: float

Shannon entropy (bits) of the current distribution.

property most_likely_state: tuple[frozenset[str], float]

Return (state, probability) of the most probable state.

marginal_mastery()[source]

Marginal probability of mastering each item.

For each item q: P(q mastered) = sum of P(state) for all states containing q.

Return type:

dict[str, float]

knowledgespaces.assessment.blim.shannon_entropy(probs)[source]

Shannon entropy in bits, handling zero probabilities.

Parameters:

probs (ndarray)

Return type:

float

Adaptive assessment: item selection policies and termination criteria.

Provides the Expected Information Gain (EIG) policy for selecting the most informative item at each step of an adaptive assessment.

References:

Cover, T. M., & Thomas, J. A. (2006). Elements of Information Theory, 2nd ed. Wiley.

class knowledgespaces.assessment.adaptive.ItemScore(item, score)[source]

Score of an item under a selection policy.

Parameters:
knowledgespaces.assessment.adaptive.select_item_eig(posterior, candidates=None, exclude=None)[source]

Select the item maximizing Expected Information Gain.

EIG(q) = H(current) - E[H(posterior after observing q)]

where the expectation is over both possible responses (correct/incorrect), weighted by their marginal probability.

Parameters:
posteriorStatePosterior

Current state distribution.

candidateslist[str] or None

Items to consider. If None, uses all items in the domain.

excludeset[str] or None

Items to exclude (e.g. already asked). Applied after candidates.

Returns:
ItemScore

The best item and its EIG score.

Raises:
ValueError

If no candidates are available, or if candidates contains items not in the domain.

Parameters:
Return type:

ItemScore

knowledgespaces.assessment.adaptive.is_converged(posterior, threshold=0.85)[source]

Check if the assessment has converged.

Convergence occurs when the most likely state has probability above the threshold.

Parameters:
posteriorStatePosterior

Current state distribution.

thresholdfloat

Probability threshold for convergence. Must be in (0, 1]. Default 0.85.

Raises:
ValueError

If threshold is not in (0, 1].

Parameters:
Return type:

bool

Instance pool: multiple questions per item.

In KST assessment, each item (competency) can be tested through multiple instances (concrete questions). The adaptive engine selects the best instance, maps it to its parent item for the BLIM update, and excludes only that specific instance from future selection.

This module provides the InstancePool data structure and an instance-aware version of select_item_eig.

class knowledgespaces.assessment.instances.Instance(id, item)[source]

A concrete question that tests a specific item.

Parameters:
idstr

Unique identifier for this instance.

itemstr

The item (competency) this instance tests.

Parameters:
class knowledgespaces.assessment.instances.InstancePool(instances)[source]

A collection of instances mapped to items.

Parameters:
instanceslist[Instance]

All available instances.

Raises:
ValueError

If instance IDs are not unique, or if an instance references an item not in the provided domain.

Parameters:

instances (list[Instance])

property items: set[str]

All unique items covered by this pool.

item_of(instance_id)[source]

Get the parent item of an instance.

Parameters:

instance_id (str)

Return type:

str

instances_for(item)[source]

Get all instance IDs for a given item.

Parameters:

item (str)

Return type:

list[str]

validate_domain(domain)[source]

Verify that pool items match the structure’s domain.

Raises:
ValueError

If pool contains items not in domain, or domain has items with no instances.

Parameters:

domain (frozenset[str])

Return type:

None

classmethod from_dict(mapping)[source]

Create from {item: [instance_id, …]}.

Example:

pool = InstancePool.from_dict({
    "addition": ["add_q1", "add_q2", "add_q3"],
    "subtraction": ["sub_q1", "sub_q2"],
})
Parameters:

mapping (dict[str, list[str]])

Return type:

InstancePool

class knowledgespaces.assessment.instances.InstanceScore(instance_id, item, score)[source]

Score of an instance under the EIG policy.

Parameters:
knowledgespaces.assessment.instances.select_instance_eig(posterior, pool, asked=None)[source]

Select the instance maximizing Expected Information Gain.

This is the instance-aware version of select_item_eig. It computes EIG per item, picks the item with highest EIG, then selects a random un-asked instance of that item (since instances of the same item are equivalent from the BLIM perspective).

Parameters:
posteriorStatePosterior

Current state distribution.

poolInstancePool

Available instances.

askedset[str] or None

Instance IDs already asked (excluded from selection).

Returns:
InstanceScore

The best instance, its parent item, and EIG score.

Raises:
ValueError

If no un-asked instances remain, or if pool items don’t match the structure’s domain.

Parameters:
Return type:

InstanceScore

knowledgespaces.estimation

EM algorithm for BLIM parameter estimation.

Estimates slip (β) and guess (η) parameters from observed response patterns using Expectation-Maximization. Supports both global (homogeneous) and per-item (heterogeneous) parameterization.

The algorithm iterates between: - E-step: compute posterior P(state | response pattern) for each pattern. - M-step: re-estimate β, η, and state prior π from sufficient statistics.

References:

Doignon, J.-P., & Falmagne, J.-C. (1999). Knowledge Spaces, Chapter 12. Springer-Verlag.

Heller, J., & Wickelmaier, F. (2013). Minimum discrepancy estimation in probabilistic knowledge structures. Electronic Notes in Discrete Mathematics, 42, 49-56.

class knowledgespaces.estimation.blim_em.ResponseMatrix(items, patterns, counts=None)[source]

Observed response patterns from a group of respondents.

Parameters:
itemslist[str]

Item labels (columns), must match the structure’s domain.

patternsnp.ndarray

Binary matrix of shape (n_respondents, n_items). patterns[r, q] = 1 if respondent r answered item q correctly.

countsnp.ndarray or None

Optional frequency for each unique pattern. If None, each row in patterns is one respondent (count=1 each).

Parameters:
property effective_counts: ndarray

Counts for each pattern (ones if not provided).

class knowledgespaces.estimation.blim_em.GoodnessOfFit(G2, df, p_value, npar, AIC, BIC, BIC_N)[source]

Goodness-of-fit statistics for a BLIM estimate.

Follows the approach of Heller & Wickelmaier (2013) and the R pks package (Heller & Wickelmaier, 2013, J. Stat. Softw.). The primary statistic is the likelihood ratio G2 (deviance), tested against a chi-squared distribution.

Attributes:
G2float

Likelihood ratio statistic: 2 * sum_r N_r ln(N_r / E_r).

dfint

Degrees of freedom: max(min(2^Q - 1, N) - npar, 0). The min(2^Q - 1, N) cap follows the pks convention: when the total sample size N is smaller than the number of possible response patterns (2^Q), the saturated model cannot be fully identified, so N replaces 2^Q - 1.

p_valuefloat

P-value from chi-squared test on G2.

nparint

Number of free parameters: |K| - 1 + 2 * Q.

AICfloat

Akaike Information Criterion: -2*LL + 2*npar.

BICfloat

Bayesian Information Criterion using the number of unique response patterns: -2*LL + ln(n_patterns)*npar. This follows the pks convention (Heller & Wickelmaier, 2013). See BIC_N for the standard textbook variant.

BIC_Nfloat

Bayesian Information Criterion using the total sample size: -2*LL + ln(N)*npar. This is the standard BIC definition (Schwarz, 1978).

Parameters:
class knowledgespaces.estimation.blim_em.BLIMEstimate(beta, eta, pi, log_likelihood, n_iterations, converged, items, states, gof)[source]

Result of BLIM parameter estimation via EM.

Attributes:
betanp.ndarray

Slip parameters, shape (n_items,). beta[q] = P(incorrect | q mastered).

etanp.ndarray

Guess parameters, shape (n_items,). eta[q] = P(correct | q not mastered).

pinp.ndarray

State prior probabilities, shape (n_states,).

log_likelihoodfloat

Final log-likelihood of the data.

n_iterationsint

Number of EM iterations until convergence.

convergedbool

True if converged within max_iter.

itemslist[str]

Item labels corresponding to beta/eta indices.

stateslist[frozenset[str]]

Knowledge states corresponding to pi indices (same order).

gofGoodnessOfFit

Goodness-of-fit statistics (G2, df, p-value, AIC, BIC).

Parameters:
beta_for(item)[source]

Get beta (slip) for a specific item.

Parameters:

item (str)

Return type:

float

eta_for(item)[source]

Get eta (guess) for a specific item.

Parameters:

item (str)

Return type:

float

beta_dict()[source]

Return beta as {item: value} dict.

Return type:

dict[str, float]

eta_dict()[source]

Return eta as {item: value} dict.

Return type:

dict[str, float]

pi_dict()[source]

Return pi as {state: probability} dict.

Return type:

dict[frozenset[str], float]

knowledgespaces.estimation.blim_em.estimate_blim(structure, data, *, max_iter=500, tol=1e-06, beta_init=0.1, eta_init=0.1)[source]

Estimate BLIM parameters via Expectation-Maximization.

Parameters:
structureKnowledgeStructure

The knowledge structure defining valid states.

dataResponseMatrix

Observed response patterns.

max_iterint

Maximum number of EM iterations. Default 500.

tolfloat

Convergence tolerance on log-likelihood change. Default 1e-6.

beta_initfloat or np.ndarray

Initial slip values. Scalar for homogeneous, array for per-item.

eta_initfloat or np.ndarray

Initial guess values. Scalar for homogeneous, array for per-item.

Returns:
BLIMEstimate

Estimated parameters, log-likelihood, and convergence info.

Raises:
ValueError

If data items don’t match structure domain, or if init parameters are out of range.

Parameters:
Return type:

BLIMEstimate

knowledgespaces.estimation.blim_em.estimate_blim_restarts(structure, data, *, n_restarts=10, max_iter=500, tol=1e-06, seed=None)[source]

Estimate BLIM parameters with multiple random restarts.

Runs estimate_blim() n_restarts times with random initial values for beta, eta, and selects the result with the highest log-likelihood. This helps avoid local optima.

The R pks package does not provide this natively — users must loop manually with randinit=TRUE.

Parameters:
structureKnowledgeStructure

The knowledge structure defining valid states.

dataResponseMatrix

Observed response patterns.

n_restartsint

Number of random restarts. Default 10.

max_iterint

Maximum EM iterations per restart.

tolfloat

Convergence tolerance per restart.

seedint or None

Random seed for reproducibility.

Returns:
BLIMEstimate

The best result (highest log-likelihood) across all restarts.

Parameters:
Return type:

BLIMEstimate

knowledgespaces.io

CSV import/export for KST objects.

Supports three standard CSV formats: - Skill map matrix: rows=items, cols=skills, binary (μ: items→skills). - Prerequisite matrix: rows=labels, cols=labels, binary (surmise relation). - Knowledge structure: state_size, state_id, then binary columns per item.

All CSV files use the first column as row index and the first row as header.

knowledgespaces.io.csv.read_skill_map(path)[source]

Read a skill map from CSV.

Expected format:

,skill1,skill2,...
item1,0,1,...
item2,1,0,...
Raises:
ValueError

If rows have wrong column count or non-binary values.

Parameters:

path (str | Path)

Return type:

SkillMap

knowledgespaces.io.csv.write_skill_map(skill_map, path)[source]

Write a skill map to CSV.

Parameters:
Return type:

None

knowledgespaces.io.csv.read_relation(path)[source]

Read a surmise relation from a CSV prerequisite matrix.

Expected format:

,label1,label2,...
label1,0,1,...
label2,0,0,...

Row labels must match column labels exactly.

Raises:
ValueError

If rows have wrong column count, non-binary values, or row labels don’t match header labels.

Parameters:

path (str | Path)

Return type:

SurmiseRelation

knowledgespaces.io.csv.write_relation(relation, path)[source]

Write a surmise relation to a CSV prerequisite matrix.

Parameters:
Return type:

None

knowledgespaces.io.csv.read_structure(path)[source]

Read a knowledge structure from CSV.

Expected format:

state_size,state_id,item1,item2,...
0,0,0,0,...
1,1,1,0,...
Raises:
ValueError

If rows have wrong column count or non-binary item values.

Parameters:

path (str | Path)

Return type:

KnowledgeStructure

knowledgespaces.io.csv.write_structure(structure, path)[source]

Write a knowledge structure to CSV.

Parameters:
Return type:

None

JSON serialization for KST objects.

Provides roundtrip-safe serialization for the core KST types: KnowledgeStructure, SurmiseRelation, and SkillMap.

knowledgespaces.io.json.structure_to_dict(structure)[source]

Serialize a KnowledgeStructure to a JSON-compatible dict.

Parameters:

structure (KnowledgeStructure)

Return type:

dict[str, Any]

knowledgespaces.io.json.dict_to_structure(data)[source]

Deserialize a KnowledgeStructure from a dict.

Raises:
ValueError

If required keys are missing or have wrong types.

Parameters:

data (dict[str, Any])

Return type:

KnowledgeStructure

knowledgespaces.io.json.write_structure_json(structure, path)[source]

Write a KnowledgeStructure to a JSON file.

Parameters:
Return type:

None

knowledgespaces.io.json.read_structure_json(path)[source]

Read a KnowledgeStructure from a JSON file.

Parameters:

path (str | Path)

Return type:

KnowledgeStructure

knowledgespaces.io.json.relation_to_dict(relation)[source]

Serialize a SurmiseRelation to a JSON-compatible dict.

Parameters:

relation (SurmiseRelation)

Return type:

dict[str, Any]

knowledgespaces.io.json.dict_to_relation(data)[source]

Deserialize a SurmiseRelation from a dict.

Raises:
ValueError

If required keys are missing or have wrong types.

Parameters:

data (dict[str, Any])

Return type:

SurmiseRelation

knowledgespaces.io.json.write_relation_json(relation, path)[source]

Write a SurmiseRelation to a JSON file.

Parameters:
Return type:

None

knowledgespaces.io.json.read_relation_json(path)[source]

Read a SurmiseRelation from a JSON file.

Parameters:

path (str | Path)

Return type:

SurmiseRelation

knowledgespaces.io.json.skill_map_to_dict(skill_map)[source]

Serialize a SkillMap to a JSON-compatible dict.

Parameters:

skill_map (SkillMap)

Return type:

dict[str, Any]

knowledgespaces.io.json.dict_to_skill_map(data)[source]

Deserialize a SkillMap from a dict.

Raises:
ValueError

If required keys are missing or have wrong types.

Parameters:

data (dict[str, Any])

Return type:

SkillMap

knowledgespaces.io.json.write_skill_map_json(skill_map, path)[source]

Write a SkillMap to a JSON file.

Parameters:
Return type:

None

knowledgespaces.io.json.read_skill_map_json(path)[source]

Read a SkillMap from a JSON file.

Parameters:

path (str | Path)

Return type:

SkillMap

knowledgespaces.metrics

Distance measures between knowledge structures.

Provides formal metrics for comparing two knowledge structures: - Symmetric difference distance - Hausdorff distance (on states) - Directional Hamming distances (H→A and A→H) - Graph Edit Distance on prerequisite relations

All functions require matching domains.

knowledgespaces.metrics.distances.symmetric_difference(ks1, ks2)[source]

Number of states in exactly one of the two structures.

Card(K1 △ K2) = Card(K1 K2) + Card(K2 K1)

Raises:
ValueError

If structures have different domains.

Parameters:
Return type:

int

knowledgespaces.metrics.distances.hausdorff(ks1, ks2)[source]

Hausdorff distance between two structures.

max(max over K in K1 of min over L in K2 of d(K,L),

max over L in K2 of min over K in K1 of d(K,L))

where d is the Hamming distance between states. Returns 0 if the structures are identical.

Raises:
ValueError

If structures have different domains.

Parameters:
Return type:

int

class knowledgespaces.metrics.distances.DirectionalDistances(forward_mean, backward_mean, forward_max, backward_max)[source]

Directional Hamming distances between two structures.

Attributes:
forward_meanfloat

Mean min-distance from ks1 states to nearest ks2 state.

backward_meanfloat

Mean min-distance from ks2 states to nearest ks1 state.

forward_maxint

Max min-distance from ks1 to ks2.

backward_maxint

Max min-distance from ks2 to ks1.

Parameters:
  • forward_mean (float)

  • backward_mean (float)

  • forward_max (int)

  • backward_max (int)

knowledgespaces.metrics.distances.directional_distances(ks1, ks2)[source]

Compute directional Hamming distances.

Raises:
ValueError

If structures have different domains.

Parameters:
Return type:

DirectionalDistances

knowledgespaces.metrics.distances.graph_edit_distance(rel1, rel2)[source]

Edge differences between two surmise relation graphs.

Returns:
addedint

Edges in rel2 but not in rel1.

removedint

Edges in rel1 but not in rel2.

totalint

added + removed.

Raises:
ValueError

If relations have different domains.

Parameters:
Return type:

tuple[int, int, int]

Agreement measures between knowledge structures or relations.

Provides Cohen’s kappa and related indices for comparing prerequisite matrices or state memberships.

knowledgespaces.metrics.agreement.cohens_kappa(rel1, rel2)[source]

Cohen’s kappa for agreement on prerequisite relations.

Compares two surmise relations on the same domain. For every ordered pair (a, b) with a ≠ b, counts agreement/disagreement on whether (a, b) is a prerequisite.

Parameters:
rel1, rel2SurmiseRelation

Must have the same item domain.

Returns:
float

Cohen’s kappa in [-1, 1]. 1 = perfect agreement, 0 = chance agreement, <0 = worse than chance.

Raises:
ValueError

If the relations have different domains.

Parameters:
Return type:

float