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 (CbKST).

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.structure_from_skill_multimap(skill_multimap, skill_prerequisites=None)[source]

Delineate a knowledge structure from a skill multimap.

A skill multimap assigns to each item one or more competencies — alternative sets of skills, each sufficient on its own to solve the item (Doignon & Falmagne 1999, Chapter 4): conjunctive within a competency, disjunctive across competencies. With exactly one competency per item this coincides with structure_from_skill_map().

Parameters:
skill_multimapdict[str, list[list[str]]]

For each item, the list of its competencies, each a list of skills. Example: {"q1": [["s1"], ["s2", "s3"]]} — q1 is solvable with s1 alone, or with s2 and s3 together.

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 and the delineation runs over all subsets of skills.

Returns:
KnowledgeStructure

The delineated knowledge structure (not necessarily a space).

Parameters:
Return type:

KnowledgeStructure

Examples

>>> ks = structure_from_skill_multimap(
...     {"q1": [["s1"]], "q2": [["s1"], ["s2"]]},
... )
knowledgespaces.api.space_from_surmise_function(clauses)[source]

Build a knowledge space from a surmise function (multiple clauses).

Each item maps to one or more clauses — alternative sets of prerequisites. This generalises space_from_prerequisites, which only allows one prerequisite set per item (ordinal case).

Parameters:
clausesdict[str, list[list[str]]]

For each item, a list of clauses. Each clause is a list of items (must include the item itself).

Example:

{
    "a": [["a"]],
    "b": [["b", "d"], ["a", "b", "c"]],
    "d": [["b", "d"]],
    ...
}
Returns:
KnowledgeStructure

The derived knowledge space (union-closed).

Parameters:

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

Return type:

KnowledgeStructure

Examples

>>> ks = space_from_surmise_function({
...     "a": [["a"]],
...     "b": [["a", "b"], ["b", "c"]],
...     "c": [["c"]],
... })
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, selection='eig', seed=None)[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.

selection{“eig”, “half_split”, “informative”}

Question selection rule: "eig" (default) picks the item with maximal expected information gain, "half_split" the item whose posterior mastery probability is closest to 1/2 (Falmagne & Doignon 1988), or "informative" for Equation (13.14) of Learning Spaces. All support item and instance modes.

seedint or None

Seed for the random tie-breaking and instance draw of the instance-aware mode, and exact score ties in item mode. Without a seed, item mode is deterministic; instance EIG uses random draws.

Returns:
dict

Keys: ‘state’, ‘probability’, ‘mastery’, ‘inner_fringe’, ‘outer_fringe’, ‘questions_asked’ (int), ‘history’ (list of (instance_or_item, item, response) tuples), plus ‘converged’, ‘stop_reason’ and the final ‘posterior’.

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, selection='eig', seed=None)[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, selection, seed

Passed through to adaptive_assess().

Parameters:
Return type:

dict

knowledgespaces.api.fit_blim(structure, items, responses, counts=None, *, method='ML', constraints=None, max_iter=500, tol=1e-06)[source]

Estimate BLIM parameters from response data.

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.

method{“ML”, “MD”, “MDML”}

Estimation method (Heller & Wickelmaier 2013; same options as pks::blim()): maximum likelihood via EM (default), minimum discrepancy (non-iterative, deterministic), or maximum likelihood restricted to minimum-discrepancy assignments.

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), ‘method’ (str), ‘gof’ (dict with G2, df, p_value, npar, AIC, BIC).

Parameters:
Return type:

dict

Warning

Emits knowledgespaces.estimation.ConvergenceWarning if EM exhausts max_iter without meeting the convergence tolerance. The returned estimate is still usable but may be a local optimum or require more iterations / multiple restarts.

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 on item domains.

A surmise relation is a quasi-order (reflexive and 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. On a discriminative item domain — the usual case when QUERY is run on items (rather than instances or skills) — no two distinct items are equivalent, so the relation is additionally antisymmetric, i.e. a partial order. Mutually prerequisite (equivalent) elements may occur on either an item or a skill domain. This class supports them; item-level QUERY assumes a discriminative domain.

Storage and views:

The class stores supplied non-reflexive pairs (a != b), which may include transitive pairs and need not form a cover or a closed relation; reflexivity is implicit. Membership (in) and to_matrix() expose the reflexive relation — (x, x) is always a member and the matrix diagonal is always 1. The prerequisite/successor accessors (prerequisites_of(), successors_of(), to_adjacency_dict()) return the direct, strict relation (no self, no transitive closure); call transitive_closure() for all transitive prerequisites. Antisymmetry is not enforced at construction — verify with is_antisymmetric().

References:

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

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

Prerequisite pairs representing a surmise relation or its generators.

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, so membership and to_matrix() are reflexive). On a discriminative item domain the relation is a partial order (antisymmetric); antisymmetry is 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) on the domain are omitted from storage. Call transitive_closure() to obtain the mathematical quasi-order; the constructor preserves the supplied generating pairs.

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.

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

Uses an adjacency-set approach: for each item, the set of successors is iteratively expanded until a fixed point is reached.

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.

Raises:
ValueError

If the relation is not antisymmetric (its closure contains a cycle of mutually-prerequisite items). The transitive reduction is only well defined for partial orders; on a cyclic relation it would silently delete real edges. Merge equivalent items first, or check with is_antisymmetric().

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]

Minimal elements of the generated quasi-order.

Equivalent items may be mutually prerequisite; they are minimal when there is no prerequisite outside their equivalence class.

Return type:

frozenset[str]

maximal_items()[source]

Maximal elements of the generated quasi-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 (i.e. encodes a partial order).

Antisymmetry is evaluated on the transitive closure: the relation is antisymmetric iff its closure contains no symmetric pair (b, a) for a closure pair (a, b) with a != b. This correctly detects cycles of any length (e.g. a -> b -> c -> a), not only directly-stored 2-cycles.

Return type:

bool

item_equivalence_classes()[source]

Classes of mutual prerequisites in the generated quasi-order.

Uses the equivalence R intersect inverse(R) after transitive closure, including singleton classes. No knowledge states are enumerated. This is reduction of a quasi-order (Learning Spaces, 2011, §1.6.6).

Return type:

frozenset[frozenset[str]]

quotient()[source]

Reduce the generated quasi-order to a partial order on its classes.

The returned relation is transitively closed. Each class is labelled by its lexicographically first original item, and the second result maps that label to all members (including singleton classes). As with SetFamily.quotient, these representatives are identifiers, not lost or arbitrarily discarded items. The input is unchanged.

Return type:

tuple[SurmiseRelation, dict[str, frozenset[str]]]

to_adjacency_dict()[source]

Return {item: set of its direct prerequisites}.

An item is never listed as its own prerequisite. All stored pairs are retained, including any transitive pairs already present. Call transitive_closure() first for all prerequisites. For the reflexive boolean form of the relation, use to_matrix().

Return type:

dict[str, set[str]]

to_matrix()[source]

Return (sorted items, reflexive adjacency matrix).

matrix[i][j] == 1 iff items[i] is a prerequisite of items[j] or i == j. The surmise relation is reflexive, so the diagonal is always 1 — consistent with membership testing ((x, x) in relation is always True). Off-diagonal entries reflect the stored cover only; call transitive_closure() first for the full transitive matrix.

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]. Requires a square binary matrix and unique item labels.

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. Must be non-empty: the knowledge-structure axioms (Falmagne & Doignon 2011, Def. 2.1.2) require a nonempty domain Q, and every downstream algorithm in this package (BLIM EM, QUERY, assessment) is undefined for |Q| = 0. An empty domain raises ValueError.

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_discriminative: bool

True if no two items occur in exactly the same states (FD2011, 2.1).

item_equivalence_classes()[source]

Partition items by indistinguishability across knowledge states.

Return type:

frozenset[frozenset[str]]

property is_quasi_ordinal: bool

True if closed under union and intersection (FD2011, Def. 3.8.1).

property is_ordinal: bool

True if quasi-ordinal and discriminative (partial-order case).

property is_well_graded: bool

True if every two states admit a tight path.

The path changes one item per step and has length equal to the symmetric-difference distance (Falmagne & Doignon 2011, Definition 2.2.2), including for non-union-closed structures.

For every ordered pair K, L, check that K has a neighbour one step closer to L. Necessity follows from the first step of a tight path. Sufficiency follows by repeatedly taking such a step: the nonnegative integer distance decreases to zero. Cost is O(|K|**2 * |Q|) set-membership checks in the worst case. For the weaker predecessor condition use is_accessible.

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 (union-closed) + well-graded. Note that the absence of hanging states (accessibility) alone does NOT characterize a learning space: union closure is also required (Falmagne & Doignon 2011).

inner_fringe(state)[source]

Items whose removal yields another valid state.

A structural boundary, not a record of the individual’s learning history. Raises ValueError if the input is not a knowledge 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. Raises ValueError if the input is not a knowledge 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, Section 3.4.

property item_heights: dict[str, int]

Minimum cardinality of a state containing each item, minus one.

Equivalently, minimize over the atoms at that item. For knowledge spaces this agrees with KnowledgeBase.item_heights and kstMatrix::kmheights. It is not a Hasse level or the number of individually necessary prerequisites. The presence of Q ensures every item has a finite height, even outside union-closed spaces.

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]

Return at most max_paths gradations from ∅ to target.

Historical convenience name: this enumerates single-item additions. A general learning path is a maximal chain and may have larger jumps (Falmagne & Doignon 2011, §4.1). Use iter_learning_paths() for those chains, or iter_gradations() for uncapped iteration.

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]]]

neighbourhood(state, distance=1, *, include=False)[source]

States at symmetric-difference distance 1 through distance.

The centre is excluded, as in kst::knneighbourhood. This is a set-distance neighbourhood, not a graph-distance ball: an intermediate state need not exist. include=True also returns the centre, including when the radius is zero.

Parameters:
Return type:

frozenset[frozenset[str]]

iter_gradations(start=None, target=None, *, through=None)[source]

Iterate all chains adding one item at a time between two states.

Defaults are ∅ and Q (gradations in Definition 4.1.3 of Learning Spaces). Other endpoints give single-item chains in that interval; they need not extend to a full gradation from ∅ to Q. Endpoints must be states with start <= target; disconnected intervals yield no paths. Equal endpoints yield one singleton path. through restricts paths to those containing that state, including an endpoint. A valid state outside the interval gives no paths. Order is deterministic. Output can be factorial in the number of items; use itertools.islice to limit consumption explicitly.

Parameters:
Return type:

Iterator[tuple[frozenset[str], …]]

iter_learning_paths(start=None, target=None, *, through=None)[source]

Iterate maximal inclusion chains in the specified interval.

With the default endpoints ∅ and Q these are learning paths (§4.1). Consecutive states are inclusion covers, which may add several items. Ordering, through filtering, endpoint validation and output-size caveats are as for iter_gradations(). No well-gradedness assumption is made.

Parameters:
Return type:

Iterator[tuple[frozenset[str], …]]

compatible_with(other)[source]

Whether both structures have the same traces on their overlap.

This is compatibility for meshing (2011, §7.3), including disjoint domains. It does not assert compatibility of a skill map with a competence space, which is a different concept.

Parameters:

other (KnowledgeStructure)

Return type:

bool

maximal_mesh(other, *, max_states=100000)[source]

Maximal mesh of compatible structures (2011, Definition 7.4.1).

States are exactly the unions of states agreeing on the overlap. Thus projection onto either original domain recovers that structure. Incompatible structures raise ValueError. The exact output size is checked against max_states before materializing the unions. Even two learning spaces can have a mesh that is not well-graded.

Parameters:
Return type:

KnowledgeStructure

classmethod from_surmise_relation(relation, *, max_items=None)[source]

Build the quasi-ordinal knowledge space 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).

The states are generated as the span (union closure) of the principal downsets of the quasi order, so the cost scales with the number of states rather than with 2^|Q|. The domain-size preflight is unchanged because the number of states itself can be exponential in |Q|; max_items overrides the default hard limit for advanced callers.

Parameters:
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

dual()[source]

The complementary family {Q - K : K in the structure} (FD2011, 2.2.2).

Return type:

KnowledgeStructure

union_closure(*, max_items=None)[source]

Smallest knowledge space containing this structure.

Exact finite union closure. The result may have 2**|Q| states; the standard domain-size guard applies before enumeration.

Parameters:

max_items (int | None)

Return type:

KnowledgeStructure

intersection_closure(*, max_items=None)[source]

Smallest intersection-closed structure containing this structure.

Computed through complementation and union closure (De Morgan).

Parameters:

max_items (int | None)

Return type:

KnowledgeStructure

intersection_with(other)[source]

Intersection of two structures: states present in both.

Parameters:

other (KnowledgeStructure)

Return type:

KnowledgeStructure

as_family()[source]

Expose the states as a family for operations without automatic axioms.

Return type:

SetFamily

union_with(other)[source]

Union of two state families on the same domain, without union closure.

Parameters:

other (KnowledgeStructure)

Return type:

KnowledgeStructure

quotient()[source]

Collapse equivalent items and retain the label-to-class mapping.

Return type:

tuple[KnowledgeStructure, dict[str, frozenset[str]]]

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_function()[source]

Derive the surmise function from this knowledge space.

For each item q, σ(q) is the set of atoms at q (minimal states containing q). Requires a knowledge space (union-closed) where every item has at least one atom (granularity).

Returns:
SurmiseFunction
Raises:
ValueError

If the structure is not a granular knowledge space.

Return type:

SurmiseFunction

References

Falmagne & Doignon (2011), Definition 5.2.1, Theorem 5.2.5.

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

Surmise functions (generalized surmise relations with multiple clauses).

A surmise function σ on a domain Q maps each item q to a family of subsets of Q called clauses. Each clause represents a possible minimal foundation for the mastery of q. When every item has exactly one clause, the surmise function reduces to a surmise relation (a quasiorder, not necessarily a partial order).

The four axioms of a surmise function (Definition 5.1.2):

  1. σ(q) ≠ ∅ for all q — at least one clause per item

  2. q ∈ C for all C ∈ σ(q) — each clause contains its item

  3. if q’ ∈ C ∈ σ(q), then ∃ C’ ∈ σ(q’) with C’ ⊆ C — refinement (transitivity analogue)

  4. clauses for q are incomparable — no clause is a subset of another

References:

Falmagne, J.-C., & Doignon, J.-P. (2011). Learning Spaces, Chapter 5 — Surmise Systems. Springer-Verlag.

class knowledgespaces.structures.surmise_function.SurmiseFunction(domain, clauses)[source]

A surmise function on a domain of items.

Maps each item q to a family of clauses σ(q), where each clause is a frozenset of items representing an alternative foundation for mastering q.

Parameters:
domainCollection[str]

The set of all items.

clausesMapping[str, Collection[Collection[str]]]

For each item, the collection of its clauses. Each clause is a collection of item names.

Raises:
ValueError

If any of the four surmise function axioms is violated, or if clauses reference items outside the domain.

Parameters:
  • domain (Collection[str])

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

Examples

>>> sf = SurmiseFunction(
...     {"a", "b", "c", "d", "e"},
...     {
...         "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"}],
...     },
... )
property domain: frozenset[str]

The item domain Q.

property n_items: int

Number of items in the domain.

property is_ordinal: bool

True if the corresponding space is ordinal (a partial order).

Requires one clause per item AND discriminativity. See Falmagne & Doignon (2011), Definition 3.8.1 and Theorem 3.8.3. Before 0.2.0 this name tested only is_quasi_ordinal.

property is_quasi_ordinal: bool

True if every item has exactly one clause (quasi-order case).

property is_discriminative: bool

True if distinct items have distinct clause families.

σ(q) = σ(q’) implies q = q’ (Definition 5.1.2).

property is_acyclic: bool

True if the relation Rσ is acyclic (Definition 5.6.12).

Rσ is defined by: q Rσ q’ ⟺ ∃ C ∈ σ(q’) : q ∈ C. Acyclicity means no cycle q1 Rσ q2 Rσ … Rσ q1 with q1 ≠ qk.

clauses_for(item)[source]

Return the clause family σ(item).

Parameters:
itemstr

An item in the domain.

Returns:
frozenset[frozenset[str]]

The set of clauses for item.

Parameters:

item (str)

Return type:

frozenset[frozenset[str]]

precedence_relation()[source]

The precedence relation: r ≺ q ⟺ r ∈ ∩σ(q).

An item r precedes q iff r belongs to every clause for q. This is always a quasi order and generalizes the surmise relation (Definition 3.7.1 / §5.6.9).

Return type:

set[tuple[str, str]]

to_knowledge_space(*, max_items=None)[source]

Derive the knowledge space from this surmise function.

A set K ⊆ Q is a state iff for every q ∈ K, there exists a clause C ∈ σ(q) with C ⊆ K (Eq. 5.2, Definition 5.2.3).

The result is always a knowledge space (closed under union). When the surmise function satisfies all four axioms, each clause is an atom and the space is granular.

Parameters:
max_itemsint or None

Hard limit override for the domain-size preflight check. The states are generated as the span (union closure) of the clauses, so the cost scales with the number of states rather than with 2^|Q|; the preflight is kept because the number of states itself can be exponential in |Q|. By default the check raises DomainTooLargeError for |Q| >= 25. Pass a larger value to opt out (at your own risk).

Returns:
KnowledgeStructure

The derived knowledge space.

Parameters:

max_items (int | None)

Return type:

KnowledgeStructure

References

Falmagne & Doignon (2011), Theorem 5.2.5.

to_surmise_relation()[source]

Convert to a SurmiseRelation (quasi-ordinal case).

When each item has exactly one clause, the surmise function is equivalent to a quasi order (Definition 5.1.4).

Returns:
SurmiseRelation

The equivalent surmise relation.

Raises:
ValueError

If the surmise function is not quasi-ordinal.

Return type:

SurmiseRelation

to_knowledge_base()[source]

Base of the generated space, from the clauses without state enumeration.

Return type:

KnowledgeBase

join(other)[source]

Surmise function of the smallest space containing both spaces.

The operation is named for the knowledge-space inclusion order. kstMatrix calls the corresponding operation on surmise functions kmintersection; those representation-dependent names are not used here. Domains must match.

Parameters:

other (SurmiseFunction)

Return type:

SurmiseFunction

intersection_with(other, *, max_candidates=100000)[source]

Canonical function of the intersection of the two knowledge spaces.

An included item must satisfy a clause from each input, so attribution clauses are pairwise unions of its original clauses. Canonicalization completes these requirements without enumerating all states. kstMatrix calls this operation on functions kmunion.

Parameters:
Return type:

SurmiseFunction

expand(domain)[source]

Add unrestricted items, preserving all original clauses and states.

Parameters:

domain (Collection[str])

Return type:

SurmiseFunction

projection(domain)[source]

Canonical function for a nonempty projection, without generating states.

Parameters:

domain (Collection[str])

Return type:

SurmiseFunction

quotient()[source]

Discriminative quotient and a map from representatives to original items.

Return type:

tuple[SurmiseFunction, dict[str, frozenset[str]]]

classmethod from_knowledge_space(ks)[source]

Derive the surmise function from a granular knowledge space.

For each item q, σ(q) = {atoms at q in K} (Definition 5.2.1).

Parameters:
ksKnowledgeStructure

Must be a knowledge space (union-closed). The space must be granular (every item has at least one atom).

Returns:
SurmiseFunction
Raises:
ValueError

If the structure is not a knowledge space, or if some item has no atom (non-granular).

Parameters:

ks (KnowledgeStructure)

Return type:

SurmiseFunction

References

Falmagne & Doignon (2011), Definition 5.2.1, Theorem 5.2.5.

classmethod from_surmise_relation(relation)[source]

Cast a surmise relation as a surmise function.

Each item q gets a single clause: {q} ∪ prerequisites(q) in the transitive closure (Definition 5.1.4).

Parameters:
relationSurmiseRelation

A surmise relation (partial order on items).

Returns:
SurmiseFunction

An ordinal surmise function (one clause per item).

Parameters:

relation (SurmiseRelation)

Return type:

SurmiseFunction

A knowledge space represented by its base, without expanding all states.

Uses the finite union-span characterization of a base in Falmagne & Doignon (2011), Learning Spaces, Chapter 3. Membership and fringes are computed directly from this characterization, not by enumeration.

class knowledgespaces.structures.knowledge_base.KnowledgeBase(domain, generators)[source]

Minimal union generators of a knowledge space on a nonempty domain.

generators need not already be irredundant: duplicate/empty sets and sets expressible as unions of smaller generators are removed. Their union must equal the declared domain. Construction does not enumerate the span; its subset checks are quadratic in the number of input generators, with finite-set operation costs.

This representation describes union-closed knowledge spaces only. A general knowledge structure must not be silently replaced by its union closure. Use from_structure to check that hypothesis.

Parameters:
  • domain (Collection[str])

  • generators (Collection[Collection[str]])

property base: frozenset[frozenset[str]]

The irredundant generating sets, excluding the empty set.

outer_fringe(state)[source]

Items whose addition produces a state, computed from the base.

For a state K, q belongs to its outer fringe exactly when some base set B satisfies B minus K = {q}. No span is materialized.

Parameters:

state (Collection[str])

Return type:

frozenset[str]

inner_fringe(state)[source]

Items whose removal leaves a union of contained base sets.

This is a structural boundary, not an individual’s learning history.

Parameters:

state (Collection[str])

Return type:

frozenset[str]

neighbourhood(state, *, include=False)[source]

States one item away, computed without expanding the union span.

include=True also returns the centre. The centre must belong to the generated space. For larger symmetric-difference radii use an explicitly materialized structure and its neighbourhood method.

Parameters:
Return type:

frozenset[frozenset[str]]

property item_heights: dict[str, int]

Minimum cardinality of a state containing each item, minus one.

Equivalent to kstMatrix::kmheights: a minimum containing state is a base set, so no span enumeration is needed. This is neither a Hasse level nor the number of individually necessary prerequisites.

surmise_function()[source]

Recover clauses (atoms at each item) directly from the base.

Return type:

SurmiseFunction

to_knowledge_space(*, max_items=None)[source]

Materialize the union span; may require exponentially many states.

Parameters:

max_items (int | None)

Return type:

KnowledgeStructure

as_family()[source]

The base rows as an arbitrary family, without generating states.

Return type:

SetFamily

join(other)[source]

Base of the smallest knowledge space containing both spaces.

The domains must match. Merge and reduce generators; no state span is materialized. The result need not be the raw union of the bases.

Parameters:

other (KnowledgeBase)

Return type:

KnowledgeBase

intersection_with(other, *, max_candidates=100000)[source]

Base of the intersection of the generated knowledge spaces.

This is not the intersection of the two base families. Canonical clauses encode both requirements, without enumerating either span.

Parameters:
Return type:

KnowledgeBase

projection(domain)[source]

Base of the projected knowledge space, from projected generators.

Parameters:

domain (Collection[str])

Return type:

KnowledgeBase

refine(base, *, max_sets=100000)[source]

Refine equivalent items in the generators and take their union span.

base.domain must lie within a single equivalence class of this base. The result is a base; call as_family().refine(base) instead when only the unreduced replacement rows are wanted.

Parameters:
Return type:

KnowledgeBase

quotient()[source]

Base on representative items, plus the original equivalence classes.

Return type:

tuple[KnowledgeBase, dict[str, frozenset[str]]]

classmethod from_structure(structure)[source]

Construct from an explicit knowledge space; reject non-union-closed input.

Parameters:

structure (KnowledgeStructure)

Return type:

KnowledgeBase

General attribution functions (Falmagne & Doignon 2011, §5.1–5.2).

Unlike a surmise function, an attribution need not contain its item in each clause, satisfy refinement, or have incomparable clauses. Every item must still have a nonempty family of clauses; an empty clause is allowed.

class knowledgespaces.structures.attribution.Attribution(domain, clauses)[source]

An item-indexed family of alternative prerequisite sets.

clauses must cover the nonempty domain exactly. Duplicates are removed, but redundant clauses are retained in this representation. An absent prerequisite is expressed by the family [[]], not [].

Parameters:
  • domain (Collection[str])

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

clauses_for(item)[source]

The original clause family, including any redundant clauses.

Parameters:

item (str)

Return type:

frozenset[frozenset[str]]

property is_surmise_function: bool

Whether the original clauses already satisfy all surmise axioms.

to_surmise_function(*, max_candidates=100000)[source]

Canonical surmise function defining exactly the same knowledge space.

Search for all inclusion-minimal states containing each item. Start from its clauses with the item added; whenever an included item lacks a satisfied clause, branch over that item’s alternatives. Sets grow monotonically. A valid state prunes its supersets from further work.

This works with cycles and does not mistake a clause union for a state before every included item’s requirements are satisfied. max_candidates bounds distinct sets visited in each item-rooted search. Exceeding it raises instead of returning a partial function. The worst case is exponential, but a powerset of states is not materialized as an intermediate representation.

Parameters:

max_candidates (int)

Return type:

SurmiseFunction

to_knowledge_space(*, max_candidates=100000, max_items=None)[source]

Canonicalize and enumerate the union span, with both resource guards.

Parameters:
  • max_candidates (int)

  • max_items (int | None)

Return type:

KnowledgeStructure

expand(domain)[source]

Extend to a larger domain with no restrictions on the added items.

Original clauses are unchanged; new items have the empty prerequisite clause. Call to_surmise_function for the canonical expansion.

Parameters:

domain (Collection[str])

Return type:

Attribution

classmethod from_relation(domain, pairs)[source]

Cast any binary relation as an attribution (Definition 5.1.4).

Pair (a,b) means a is required for b. No reflexive or transitive closure is added. An item with no incoming pairs has an empty clause.

Parameters:
Return type:

Attribution

classmethod from_surmise_function(function)[source]

Preserve every clause of an already canonical surmise function.

Parameters:

function (SurmiseFunction)

Return type:

Attribution

Finite families of subsets without silently inserting structure axioms.

class knowledgespaces.structures.set_family.SetFamily(domain, sets)[source]

Immutable family of subsets of an explicit finite domain.

The family and the domain may be empty. Duplicates are removed; neither the empty set nor the full domain is inserted. This representation is useful for bases, reductions and intermediate operations that need not themselves be knowledge structures.

Parameters:
  • domain (Collection[str])

  • sets (Collection[Collection[str]])

property minimal_sets: frozenset[frozenset[str]]

Inclusion-minimal members (not necessarily smallest cardinality).

union_with(other)[source]

Union of the two families, without closing under item-set unions.

Parameters:

other (SetFamily)

Return type:

SetFamily

intersection_with(other)[source]

Members occurring in both families, not pairwise set intersections.

Parameters:

other (SetFamily)

Return type:

SetFamily

dual()[source]

Complement each member relative to the declared domain.

Return type:

SetFamily

union_closure(*, max_sets=100000)[source]

All unions of subfamilies, including the empty union ∅.

The full declared domain is not inserted unless it is generated. The guard bounds output cardinality and never truncates a result.

Parameters:

max_sets (int)

Return type:

SetFamily

intersection_closure(*, max_sets=100000)[source]

All intersections of subfamilies, including the empty intersection Q.

Unlike kstMatrix’s kmclosure(..., closure='intersection'), this closes only under intersection, not under both union and intersection.

Parameters:

max_sets (int)

Return type:

SetFamily

union_reduction()[source]

Remove members expressible as unions of other nonempty subfamilies.

This is the sets/kst reduction convention: an existing empty member is retained, unlike a knowledge-space base. No closure is enumerated, no endpoint is inserted, and the domain is preserved. The result generates the same unions of nonempty subfamilies.

Return type:

SetFamily

intersection_reduction()[source]

Remove intersections of other nonempty subfamilies.

Dual to union_reduction; an existing Q is retained. This is not a knowledge-space base and does not require intersection closure.

Return type:

SetFamily

item_equivalence_classes()[source]

Items with identical membership profiles across this family.

Return type:

frozenset[frozenset[str]]

quotient()[source]

Collapse equivalent items, returning the family and label-to-class map.

The lexicographically first original item labels each class; the second return value makes the reduction reversible by expansion.

Return type:

tuple[SetFamily, dict[str, frozenset[str]]]

refine(base, *, max_sets=100000)[source]

Replace co-occurring items by the rows of a refining base.

The base domain must be a nonempty subset of a single item-equivalence class. For each member containing that domain D, replace D in turn by every base set; members disjoint from D stay unchanged. This is the family-level operation described by kstMatrix kmrefine. It need not yield a knowledge structure or contain Q. To interpret the rows as generators, explicitly call to_knowledge_base afterwards.

Parameters:
Return type:

SetFamily

to_knowledge_structure()[source]

Convert only if the family already contains ∅ and its nonempty domain.

Return type:

KnowledgeStructure

to_knowledge_base()[source]

Treat this family as union generators, removing redundant rows.

The union must equal the nonempty declared domain; no state span is enumerated. This is an explicit change of interpretation.

Return type:

KnowledgeBase

Path validation and multi-item differences, distinct from canonical fringes.

knowledgespaces.structures.reports.is_gradation(structure, path, *, start=None, target=None)[source]

Whether a supplied path adds one item per step between its endpoints.

Defaults are ∅ and Q, giving a full gradation. With explicit endpoints this validates a single-item chain in that interval. All members must belong to structure; repetition, deletion, or exchanging items is invalid even if successive cardinalities differ by one. Invalid expected endpoints raise ValueError; an empty or invalid path returns false. Equal expected endpoints admit only their singleton path.

Parameters:
Return type:

bool

class knowledgespaces.structures.reports.StateChanges(centre, distance, all_changes, removals, additions)[source]

Families of changed-item sets within a symmetric-difference radius.

all_changes corresponds to the generalized kstpy.fringe output. removals reach proper subsets, additions proper supersets, and mixed incomparable states. These contain sets of items, whereas a canonical fringe contains individual items. The centre is excluded.

Parameters:
property mixed: frozenset[frozenset[str]]

Changes that remove some items and add others.

knowledgespaces.structures.reports.state_changes(structure, state, distance=1)[source]

Report multi-item changes to states at distance 1 through distance.

The radius uses set distance, not graph distance; intermediate states need not exist. distance=1 recovers singleton sets for the canonical inner/outer fringes. No states, including endpoints, are inserted when the input is a SetFamily. This enumerates only the supplied family.

Parameters:
Return type:

StateChanges

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.

The input pairs are closed transitively, so the expert answers according to the quasi order they generate, not according to which generating pairs the caller happened to pass. A pair query (a -> q) is positive iff (a, q) is in the transitive closure. 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 and whose answers satisfy the entailment axioms. 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])

exception knowledgespaces.query.expert.QueryNeeded(query)[source]

Raised by ReplayExpert when no cached answer exists.

Attributes:
queryQuery

The unanswered query that needs to be posed to the expert.

Parameters:

query (Query)

Return type:

None

class knowledgespaces.query.expert.ReplayExpert(answers)[source]

Expert that replays cached answers, raises QueryNeeded on cache miss.

Useful for web applications and session resumption: replay all prior answers instantly, then pause at the next unanswered query.

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

Mapping from (antecedent, consequent) to answer.

Parameters:

answers (dict[tuple[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. 15) 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.

Antisymmetry assumption (item-level QUERY).

Block 1 assumes the target surmise relation is a partial order (antisymmetric): once a prerequisite (q, r) is established, the reverse (r, q) is inferred to be False without re-querying the expert. This is the standard, valid assumption when QUERY is run on a discriminative item domain, where no two distinct items are equivalent. The inference is fully traceable, not silent: each inferred pair is logged with InferenceSource.ANTISYMMETRY, counted in Block1Stats.skipped_by_antisymmetry, and listed by Block1Result.assumed_antisymmetric_pairs. Equivalent (mutually prerequisite) items belong to competence-based KST (skills), not item-level QUERY: if your domain may contain equivalent items, collapse them first or model skills via knowledgespaces.derivation.

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 15. 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:
property assumed_antisymmetric_pairs: list[tuple[str, str]]

Reverse pairs (r, q) inferred False by the antisymmetry assumption.

These pairs were not put to the expert: each was inferred False because (q, r) is a prerequisite and the surmise relation is assumed antisymmetric (a partial order). Exposed for full transparency — see the module docstring’s “Antisymmetry assumption” note.

knowledgespaces.query.block1.run_block1(items, expert, *, use_qmax=True, max_items=None)[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.

max_itemsint | None

Hard limit override for the |Q| preflight check. Block 1 is O(|Q|^5) worst case; above ~25 items it is not practical.

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.

Two 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.

Applying a positive answer removes the states contradicting it (Definition 16.1.5), which is admissible only when the removal leaves a learning space (Theorem 16.1.6). Two treatments of an inadmissible (“blocked”) positive are implemented, selectable via algorithm:

"pending" (default)

The pending-table mechanism of Falmagne & Doignon (2011), Section 16.1 (Example 16.1.13), applied at the state level: a blocked positive is buffered with pending status and re-tested after every successful removal, within and across blocks, until the collection of states stabilizes. By Theorem 16.1.16 the procedure cannot get jammed: with an error-free expert whose latent structure is a learning space, and queries of every antecedent size, the stabilized output is the latent space. Section 16.2 of the book reformulates the same mechanism on the surmise function (Algorithm 16.2.11) to avoid storing the state collection — a memory concern (Drawback 16.1.12(b)) that does not bind at this package’s documented domain ceiling, so the state-level formulation is used here.

"one_pass"

The naive one-pass scheme of Algorithm 16.1.10 (cf. 15.1.1): a positive whose removal is blocked is recorded as negative on structural grounds, without consulting the expert, and never revisited. The book documents the defect (Example 16.1.12(a)): a removal blocked at one point can become admissible after later removals, so a truthful positive can be suppressed permanently and the result can strictly contain the latent space and depend on the item order. Retained for comparison and reproducibility.

References:

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

knowledgespaces.query.block2.Algorithm

Treatment of blocked positive answers; see the module docstring.

alias of Literal[‘pending’, ‘one_pass’]

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

Statistics from a Block N execution.

Parameters:
  • expert_queries (int)

  • inferred_negative_monotonicity (int)

  • inferred_positive_monotonicity (int)

  • inferred_structural (int)

  • pending_added (int)

  • pending_resolved (int)

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

Result of a Block N query phase.

pending holds the positive answers (from this block or carried in from earlier ones) whose state removal is still blocked when the block ends; it is empty under algorithm="one_pass".

Parameters:
knowledgespaces.query.block2.run_block_n(items, structure, prior_positive, expert, *, antecedent_size=2, minimal_global=frozenset({}), max_items=None, algorithm='pending', pending_in=None)[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.

max_itemsint | None

Hard limit override for the |Q| preflight check. Block N scales as O(|Q| * C(|Q|-1, k)); values above ~25 are not practical.

algorithm{“pending”, “one_pass”}

Treatment of positive answers whose removal is blocked; see the module docstring. The default "pending" buffers them and re-tests after every removal (Section 16.1, Theorem 16.1.16); "one_pass" records them as structural negatives without consulting the expert (Algorithm 16.1.10).

pending_inCollection[PendingQuery] | None

Blocked positives carried over from earlier blocks ("pending" only). They are re-tested against the current structure before and during this block.

Returns:
BlockNResult

Refined structure, discovered relations, stats, and (under "pending") the positives still blocked at the end.

Parameters:
Return type:

BlockNResult

knowledgespaces.query.block2.PendingQuery: tuple[frozenset[str], str]

Type alias for a pending antecedent/consequent implication.

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

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

exception knowledgespaces.query.pipeline.TruncatedQueryWarning[source]

The elicitation stopped before antecedents of size |Q| - 1.

Latent learning spaces whose refinement requires larger antecedents are returned as their depth-max_antecedent_size approximation (a superset of the latent space); the recovery guarantee of Falmagne & Doignon (2011, Theorem 16.1.16) needs queries of every antecedent size. Silence with warnings.filterwarnings when the truncation depth is a deliberate part of the elicitation design.

class knowledgespaces.query.pipeline.QueryPipelineResult(structure, block1, block_n_results=<factory>, algorithm='pending', truncated=False, pending_unapplied=())[source]

Result of the full QUERY pipeline.

pending_unapplied lists the positive answers whose state removal was still inadmissible when the procedure ended ("pending" algorithm only): the returned structure is then a learning space consistent with all applied answers, but not with these. With an error-free expert whose latent structure is a learning space this can only happen under depth truncation.

Parameters:
property recovery_guarantee_applies: bool

Whether the algorithmic conditions of Theorem 16.1.16 hold.

True when the pending-table algorithm ran at full depth (antecedents up to |Q| - 1) and no positive answer was left unapplied. Under these conditions, assuming an error-free expert whose latent structure is a learning space, the returned structure equals the latent space (Falmagne & Doignon, 2011, Theorem 16.1.16). The expert-side assumptions cannot be checked by the package.

knowledgespaces.query.pipeline.run_query(items, expert, *, use_qmax=True, max_antecedent_size=2, max_items=None, algorithm='pending')[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 len(items) - 1 for the full procedure; smaller values truncate the elicitation and emit a TruncatedQueryWarning.

max_itemsint | None

Hard limit override for the |Q| preflight check. When None (default), DEFAULT_MAX_N_ITEMS is used. QUERY is exponential in |Q|; values above ~25 are not practical in a browser worker.

algorithm{“pending”, “one_pass”}

Treatment of positive answers whose state removal is momentarily inadmissible in Blocks 2+. The default "pending" buffers and systematically re-tests them (Falmagne & Doignon 2011, Section 16.1; recovery guarantee of Theorem 16.1.16 at full depth); "one_pass" is the naive scheme of Algorithm 16.1.10, which records them as structural negatives without consulting the expert and can suppress truthful answers permanently.

Returns:
QueryPipelineResult

The derived learning space with full traceability.

Raises:
ValueError

If items is empty. QUERY is defined only for a nonempty domain (Koppen 1993, §2; Falmagne & Doignon 2011, §15).

Warns:
TruncatedQueryWarning

When max_antecedent_size < len(items) - 1.

Parameters:
Return type:

QueryPipelineResult

knowledgespaces.derivation

Skill maps and skill multimaps: mappings from items to skills.

A (conjunctive) 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. A skill multimap assigns to each item a nonempty collection of competencies — alternative skill sets, each sufficient on its own — and the problem function becomes p(C) = {q ∈ Q | some competency of q is ⊆ C} (conjunctive within a competency, disjunctive across competencies). The conjunctive skill map is the one-competency special case.

References:

Doignon, J.-P., & Falmagne, J.-C. (1999). Knowledge Spaces, Chapter 4. Springer-Verlag. Falmagne, J.-C., & Doignon, J.-P. (2011). Learning Spaces, Chapter 6. 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]]]

atomic_items()[source]

Items atomic for each skill (Stefanutti & de Chiusole 2017, Def. 3).

q is atomic for s if s belongs to μ(q) and no strictly smaller item requirement contains s. Equal requirements remain distinct items. Cost O(|S| |Q|**2), without enumerating competence states.

Return type:

dict[str, frozenset[str]]

property is_exclusive: bool

No item is atomic for two different skills (2017, Definition 4).

Equivalence with well-gradedness of the floor family requires a compatible competence space (Proposition 10); this property alone makes no assertion about an arbitrary competence structure.

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

Create from a binary matrix.

matrix[i][j] = 1 means items[i] requires skills[j]. Item and skill labels must each be unique. Item order is retained; skill columns are interpreted in the supplied order.

Raises:
ValueError

If item or skill labels repeat, matrix dimensions don’t match items/skills, or values are not 0 or 1.

Parameters:
Return type:

SkillMap

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

Mapping from items to alternative competencies (skill multimap).

A skill multimap assigns to each item q a nonempty collection μ(q) of competencies: alternative sets of skills, each sufficient on its own to solve q (Doignon & Falmagne 1999, Chapter 4). The model is conjunctive within a competency and disjunctive across competencies; the conjunctive SkillMap is the special case with exactly one competency per item.

The class exposes the same items / skills / problem_function protocol as SkillMap, so it plugs into knowledgespaces.derivation.derive_knowledge_structure() unchanged.

Parameters:
itemsCollection[str]

The domain of items.

skillsCollection[str]

The set of all skills.

mappingMapping[str, Collection[Collection[str]]]

For each item, the nonempty collection of its competencies. Duplicate competencies are dropped; a competency that is a superset of another is redundant but harmless. An empty competency means the item is solvable without any skill.

Raises:
ValueError

If an item is missing from the mapping, has an empty collection of competencies, or references a skill not in the skills set.

Parameters:
  • items (Collection[str])

  • skills (Collection[str])

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

competencies_for(item)[source]

Return μ(q): the alternative competencies of item q.

Parameters:

item (str)

Return type:

tuple[frozenset[str], …]

problem_function(competence)[source]

Compute p(C) = {q ∈ Q | some competency of q is ⊆ C}.

An item is solvable iff AT LEAST ONE of its competencies is fully contained in the competence state.

Parameters:

competence (frozenset[str])

Return type:

frozenset[str]

classmethod from_skill_map(skill_map)[source]

Lift a conjunctive skill map to the one-competency multimap.

Parameters:

skill_map (SkillMap)

Return type:

SkillMultiMap

to_matrix()[source]

Return repeated item labels, sorted skills and binary competency rows.

There is one row per alternative competency, as in CbKST tables. Unused skills remain columns. Empty competencies are all-zero rows.

Return type:

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

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

Construct from one binary row per competency (items may repeat).

Repeated item rows encode alternatives, not additional conjunctive requirements. First occurrence determines item order. Skill labels must be unique; duplicate competencies are deduplicated by the class.

Parameters:
Return type:

SkillMultiMap

Competence-Based Knowledge Space Theory (CbKST) 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:

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

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

Result of a CbKST derivation.

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

Derive a knowledge structure from a competence model.

Parameters:
skill_mapSkillMap or SkillMultiMap

The mapping from items to required skills: conjunctive (SkillMap) or with alternative competencies per item (SkillMultiMap; delineation in the sense of Doignon & Falmagne 1999, Chapter 4). Both expose the problem_function used below.

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

Inductive item tree analysis (IITA).

Derives a quasi order (surmise relation) from binary response data. The three classical sample-level variants are implemented: original (Schrepp, 1999, 2003), corrected, and minimized corrected (Sargin & Ünlü, 2009). All three share the same inductive generation of candidate quasi orders from the counterexample counts and differ in how the expected counterexamples, and hence the diff fit measure, are computed.

The implementation mirrors the sample-level algorithms of the R package DAKS (Ünlü & Sargin, 2010) and is numerically cross-validated against it (see tests/test_iita.py). Population quantities and delta-method inference are available in derivation.iita_inference, with documented corrections and regularity restrictions.

References:

Schrepp, M. (2003). A method for the analysis of hierarchical dependencies between items of a questionnaire. Methods of Psychological Research Online, 8(1), 43-79.

Sargin, A., & Ünlü, A. (2009). Inductive item tree analysis: Corrections, improvements, and comparisons. Mathematical Social Sciences, 58(3), 376-392.

Ünlü, A., & Sargin, A. (2010). DAKS: An R package for data analysis methods in knowledge space theory. Journal of Statistical Software, 37(2), 1-31.

class knowledgespaces.derivation.iita.IITAResult(implications, relation, diff, error_rate, selected_index, selection_set, version, error_rates=())[source]

Result of an inductive item tree analysis.

Attributes:
implicationsfrozenset[tuple[str, str]]

The selected quasi order as pairs (q, q') read as q is a prerequisite of q'.

relationSurmiseRelation

The selected quasi order as a SurmiseRelation. Convert to a knowledge space with KnowledgeStructure.from_surmise_relation(relation.transitive_closure()).

difftuple[float, …]

The diff fit measure of every candidate in the selection set; the selected candidate minimizes it.

error_ratefloat

IITA error rate gamma of the selected candidate, not a BLIM slip estimate.

error_ratestuple[float, …]

IITA error rates for every candidate, in selection_set order.

selected_indexint

Index of the selected candidate in selection_set.

selection_settuple[frozenset[tuple[str, str]], …]

The generated or explicitly supplied candidate quasi orders.

versionstr

The variant used: "original", "corrected", or "minimized".

Parameters:
knowledgespaces.derivation.iita.counterexamples(data)[source]

Counterexample counts b[i, j] for each ordered item pair.

b[i, j] counts the respondents who solve item j and fail item i. A small b[i, j] supports the implication that mastery of j entails mastery of i, that is, the pair (i, j) of the derived surmise relation. Pattern frequencies in data.counts are respected. The diagonal is zero.

Parameters:

data (ResponseMatrix)

Return type:

ndarray

knowledgespaces.derivation.iita.iita(data, *, version='minimized', selection_set=None)[source]

Derive a surmise relation from response data by IITA.

Inductive item tree analysis generates a nested family of candidate quasi orders from the pairwise counterexample counts of the data and selects the candidate that minimizes the diff measure, the mean squared deviation between observed and expected counterexample counts (Schrepp, 2003; Sargin & Ünlü, 2009).

Parameters:
dataResponseMatrix

Observed binary response patterns; pattern frequencies in data.counts are respected. Every item must be solved at least once and the domain must contain at least two items.

version{“original”, “corrected”, “minimized”}

The variant of the expected counterexample counts. The corrected variant treats pairs whose reverse belongs to the candidate consistently; the minimized corrected variant (default) additionally chooses the error rate that minimizes diff in closed form.

selection_setSequence[SurmiseRelation] or None

Optional prespecified candidates, corresponding to DAKS orig_iita, corr_iita and mini_iita’s A argument. Each must have the data’s domain, already be transitive, and contain a nonreflexive pair. The supplied order and duplicates are retained; first wins ties. None generates candidates inductively. Evaluating or selecting candidates is descriptive; no post-selection inference is implied.

Returns:
IITAResult

The selected quasi order with the full selection set and fit measures.

Raises:
ValueError

If version is unknown, the domain has fewer than two items, or some item is never solved.

Parameters:
Return type:

IITAResult

Examples

>>> import numpy as np
>>> from knowledgespaces.estimation import ResponseMatrix
>>> data = ResponseMatrix(
...     items=["a", "b"],
...     patterns=np.array([[1, 1], [1, 0], [0, 0]]),
...     counts=np.array([50.0, 30.0, 20.0]),
... )
>>> sorted(iita(data).implications)
[('a', 'b')]
knowledgespaces.derivation.iita.inductive_generation(counterexample_counts, *, items)[source]

Generate IITA candidates from a counterexample matrix (DAKS ind_gen).

b[i,j] counts failures on i paired with success on j; items follow the rows and columns. Input must be finite, nonnegative, square with zero diagonal and at least two distinct string labels. Zero-counterexample implications must already be transitive, as they are for actual data. These necessary conditions do not certify realizability of all counts by a response matrix. Use counterexamples(data) for observed counts.

Candidates are nested, transitively closed, nonempty in strict pairs, and deduplicated. No model fitting or state enumeration is performed.

Parameters:
Return type:

tuple[SurmiseRelation, …]

Threshold item tree analysis, distinct from inductive IITA.

The pks ITA criterion combines respondent-weighted distance to the implied structure with uniform-state distance back to observed responses. References: van Leeuwe (1974); Schrepp (1999), doi:10.1016/S0165-4896(99)00025-6.

class knowledgespaces.derivation.ita.ITAThreshold(threshold, fit, complexity, n_states)[source]

One evaluated threshold: empirical fit + uniform-state complexity.

Parameters:
property total: float

Sum of the two mean Hamming distances, in items.

class knowledgespaces.derivation.ita.ITAResult(items, relation, threshold, transitive_thresholds, counterexamples, search, evaluated, structure, discrepancy)[source]

ITA relation and transparent threshold search results.

counterexamples uses input item order, with (a,b) counting a=0,b=1. transitive_thresholds lists distinct observed violation levels whose threshold relation is already transitive. No nontransitive threshold is repaired by closure. evaluated follows descending search order. search is None for an explicitly supplied threshold. Structure and discrepancy are None when explicit threshold and make_structure=False. Arrays are read-only; threshold ties choose the largest threshold.

Parameters:
knowledgespaces.derivation.ita.ita(data, *, threshold=None, search='local', make_structure=True, max_states=1048576, max_memory_bytes=512000000)[source]

Construct a transitive threshold relation or search its discrepancy.

Pair (a,b) enters when its counterexample count is <= threshold. Search visits distinct transitive thresholds from largest to smallest; local stops at the first increase in total discrepancy, global evaluates every such threshold. A fixed threshold must be nonnegative and produce a transitive relation. Fractional weights are permitted: thresholds then refer to weighted counts, not literal numbers of respondents.

Fit is mean response-to-structure distance weighted by counts; complexity is mean state-to-observed-response distance over states, excluding zero-frequency responses. The criterion is descriptive, not a statistical test. State enumeration is bounded by max_states. Memory is preflighted conservatively for array calculations, not guaranteed for process RSS.

Parameters:
Return type:

ITAResult

Population IITA and multinomial delta-method inference.

Independent moment/gradient implementation of Ünlü & Sargin (2010), JSS 37(2), Section 3.2, footnote 4. The normalized criterion is diff/N**2. No GPL derivative code is ported. Same-sample comparisons use covariance; normal-tail conventions follow the stated alternative hypothesis.

exception knowledgespaces.derivation.iita_inference.IITAInferenceWarning[source]

First-order normal calibration is undefined or the null is nonregular.

class knowledgespaces.derivation.iita_inference.IITAVariance(normalized_diff, error_rate, asymptotic_variance, standard_error, n_respondents, influence)[source]

Normalized diff and first-order multinomial uncertainty.

asymptotic_variance is V for sqrt(N)*(diff_hat-diff), not V/N. standard_error is sqrt(V/N) for a sample and None for a population. influence is the mean-centered derivative in input response-row order; its probability-weighted squared mean is V. It is read-only. Zero V invalidates the first-order normal approximation (e.g. exact fit). Relations are treated as fixed; selection on the same data is not accounted for. No claim of valid inference after exploratory selection.

Parameters:
knowledgespaces.derivation.iita_inference.iita_variance(data, relation, *, version='minimized')[source]

Sample plug-in delta variance; counts represent integer respondents.

Only observed rows are needed: zero-probability multinomial cells contribute nothing. Duplicate rows/aggregated frequencies give the same result. Analytic differentiation includes the estimated gamma rate; it is not treated as fixed. The original variant is supported too, beyond the corrected/minimized scope of DAKS 2.1-3 variances.

Parameters:
Return type:

IITAVariance

knowledgespaces.derivation.iita_inference.population_iita_variance(patterns, probabilities, relation, *, items=None, version='minimized')[source]

Delta variance at a specified population distribution on responses.

Rows may describe only the positive support; probabilities must be finite, nonnegative, and sum to one. Columns follow items (sorted relation domain by default). Gamma is recomputed from the distribution, so an inconsistent independently supplied error rate cannot enter.

Parameters:
Return type:

IITAVariance

class knowledgespaces.derivation.iita_inference.PopulationIITAResult(items, patterns, probabilities, selection_set, diff, error_rates, version)[source]

Population response probabilities and all candidate diff/error rates.

diff uses probabilities (sample diff divided by N squared), and arrays use input items and lexicographic binary response order. Candidate generation uses exact computed counterexample levels; near ties can change the selection set under rounding. Supply a fixed selection_set for controlled comparisons. No Monte Carlo sample is generated.

Parameters:
property selected_index: int

First minimum, with candidate order retained.

property relation: SurmiseRelation

Selected relation.

knowledgespaces.derivation.iita_inference.population_iita(structure, *, beta=0.1, eta=0.1, pi=None, version='minimized', selection_set=None, data=None, max_patterns=1048576, max_memory_bytes=512000000)[source]

Evaluate IITA under a BLIM population, with optional sample candidates.

A relation is expanded to its compatible states. Defaults are uniform state masses and homogeneous errors, as in DAKS::pop_iita. General structures, item-specific errors and nonuniform pi are also supported. Candidate relations come from population moments, supplied selection_set, or data (mutually exclusive). All evaluated relations must be transitive and have at least one nonreflexive pair. Enumeration is exponential.

Parameters:
Return type:

PopulationIITAResult

class knowledgespaces.derivation.iita_inference.IITAZTest(estimate, standard_error, z, p_value, confidence_interval, diff_values, alternative, null_difference, confidence, dependence, regular_null)[source]

Normal test of a fixed relation’s diff or a difference of two diffs.

estimate is diff_1 (one relation) or diff_1-diff_2. dependence is ‘single’, ‘paired’ (same response data, including covariance), or ‘independent’. A zero standard error returns NaN z/p/interval with IITAInferenceWarning. A single-relation null diff=0 has zero gradient in the population: p/interval are NaN even if the plug-in variance is positive (z is then descriptive). regular_null only flags these detected failures; True does not establish the remaining asymptotic/model assumptions. Confidence intervals are untruncated normal intervals, potentially outside the parameter range. No post-selection correction is applied.

Parameters:
knowledgespaces.derivation.iita_inference.iita_z_test(data, relation, *, other_relation=None, other_data=None, version='minimized', alternative='two-sided', null_difference=0, confidence=0.95)[source]

Delta-method Z test, with correct pairing and normal tails.

With other_relation only, both criteria use the same respondents and covariance is included. With other_data too, samples are assumed independent. ‘greater’ tests estimate > null_difference using the upper normal tail; ‘less’ uses the lower tail. Applicable to fixed relations, not automatically to winners selected on these data. At exact fit the first-order variance can vanish; normal calibration is then undefined. In particular, a single relation tested against zero has a zero-gradient null because diff is a sum of squares. This returns a warning and NaN p-value/interval even with positive sample variance. A prespecified positive single-relation null, or a difference of two fixed relations, may admit the ordinary approximation under regularity assumptions.

Parameters:
Return type:

IITAZTest

Observable learning for conjunctive competence models.

Definitions and direct finite-set algorithms from Stefanutti & de Chiusole (2017), On the assessment of learning in competence based knowledge space theory, Journal of Mathematical Psychology 80, 22-32, https://doi.org/10.1016/j.jmp.2017.08.003, Sections 3-6.

class knowledgespaces.derivation.competence.CompetenceModel(structure, skill_map)[source]

A conjunctive skill function on an explicit competence structure.

Unlike derivation from skill prerequisites, structure may be any finite competence structure, including a non-ordinal one. Skills and items remain distinct domains. All item requirements must be nonempty (the skill-function assumptions in Section 2 of the reference).

Equivalence classes enumerate the supplied competence states, so their cost scales with the explicit structure, not an additional powerset. No stochastic learning or ability to observe latent skills is assumed.

Parameters:
property structure: KnowledgeStructure

The supplied competence structure (skills as its domain).

property skill_map: SkillMap

The supplied conjunctive skill function.

property knowledge_structure: KnowledgeStructure

The exact image p(C), without adding unattained performance states.

compatible_states(performance)[source]

All competence states that delineate the given performance state.

An unattainable performance state raises ValueError; an empty response pattern is a performance state here, not missing data.

Parameters:

performance (Collection[str])

Return type:

frozenset[frozenset[str]]

equivalence_class(state)[source]

The class [C]_C of states with the same p(C) (Section 3).

Parameters:

state (Collection[str])

Return type:

frozenset[frozenset[str]]

effective_outer_fringe(state)[source]

C+ = {s in C° : p(C) is a proper subset of p(C + s)} (4.1).

The returned elements are skills. A single effective skill may unlock several items, even when the item-level outer fringe is empty.

Parameters:

state (Collection[str])

Return type:

frozenset[str]

collective_outer_fringe(performance)[source]

Intersection of outer fringes across p^{-1}(performance).

Definition 2 and Proposition 5: each returned skill is available and effective for every compatible competence state. This need not recover every effective skill of an individual latent state.

Parameters:

performance (Collection[str])

Return type:

frozenset[str]

floor(state)[source]

Intersection of [C]_C (Definition 1).

A floor need not belong to the competence structure. Check is_floor_inclusive before treating all floors as admissible states.

Parameters:

state (Collection[str])

Return type:

frozenset[str]

property floors: frozenset[frozenset[str]]

The family C_p of floors, preserving its exact domain and members.

This is a family of sets, not automatically a KnowledgeStructure on all declared skills: unused skills need not occur in any floor.

property is_floor_inclusive: bool

True if every class contains its floor (Definition 1).

floor_fringe(state)[source]

Outer fringe of the floor in the family C_p, computed directly.

Requires floor-inclusiveness. This is distinct from the effective fringe of the floor in the original competence structure, and from the collective fringe of its equivalence class. Floor-inclusiveness and union closure alone do not make all three quantities equal. is_compatible is a sufficient condition for equality; see the derivation guide for the hypotheses and argument.

Parameters:

state (Collection[str])

Return type:

frozenset[str]

property is_compatible: bool

Compatibility of a competence space with the skill function (Def. 5).

On a union-closed family the atom condition is equivalent to every item’s required skill set being a state. Returns False if the competence structure is not a knowledge space.

Inverse problem-function computations for conjunctive maps and multimaps.

These are finite set computations using p(C) = {q: some A in mu(q) is a subset of C}. No fringe or floor theorem for conjunctive models is extended to multimaps. See Falmagne & Doignon (2011), Chapter 6, and the distinction between exact inversion and positive-item coverage in the user guide.

knowledgespaces.derivation.inverse.compatible_competencies(performance, skill_map, competence_structure, *, exact=True)[source]

Return admissible C with p(C) = performance (default).

With exact=False, return C with performance <= p(C): only the specified mastered items constrain the result. The domain of the supplied competence structure or set family must equal the skill domain. A SetFamily need not contain either endpoint. An unattainable exact performance yields the empty family, not an invented competence state.

Parameters:
Return type:

frozenset[frozenset[str]]

knowledgespaces.derivation.inverse.minimal_competencies(performance, skill_map, *, competence_structure=None, exact=True, max_candidates=100000)[source]

All inclusion-minimal compatible competence states.

By default compatibility means exact performance, including the absence of mastery of every other item. exact=False asks instead for minimal skill sets sufficient to solve the specified items, as in the positive coverage interpretation of CbKST::cbkst_perf2comp.

If a competence structure or set family is given, minimize within its admissible states; a minimal unrestricted solution may not itself be admissible. Otherwise every subset of the skill domain is admissible. The unrestricted algorithm builds minimal unions of alternative competencies incrementally, without enumerating the entire skill powerset. Its intermediate antichains can nevertheless be exponential. max_candidates bounds each intermediate family (before minimization), raising rather than returning partial output.

Minimal means set inclusion, not smallest cardinality. The empty family means infeasibility; frozenset({frozenset()}) is a valid empty-skill solution. No probability or inference about noisy responses is involved.

Parameters:
Return type:

frozenset[frozenset[str]]

Descriptive structure construction by observed pattern frequency.

knowledgespaces.derivation.empirical.empirical_structure(data, *, threshold=None, items=None, counts=None, max_memory_bytes=512000000)[source]

Select observed binary patterns with total weight >= threshold.

The empty and full states are always included. None or zero uses ceil(N / 2**n_items), where N is the sum of observation weights. This implements the frequency-selection question of kstMatrix 3.0-0 kmgenerate. Its manual says “above”; the reference implementation and boundary probes use the inclusive comparison implemented here.

Positive finite fractional thresholds and weights are supported. The default still takes the ceiling; it is not a proportion. Zero-weight rows cannot introduce states. Raw input columns default to item_1, item_2, …; a ResponseMatrix supplies its own items and counts.

Only observed patterns are counted: no power set or dense response table is allocated. This is a descriptive construction, not a latent BLIM estimate or a guarantee of union closure, learning-space axioms or statistical consistency. No response-error correction is applied.

Parameters:
Return type:

KnowledgeStructure

Exact finite problem functions on arbitrary competence families.

The algorithms evaluate the defining subset relation, not fringe theorems: p(C) = {q: there exists A in mu(q) with A <= C}. They support the family mapping operations of CbKST 0.1-1 and MATLAB KST-toolbox skillmap while keeping row identities and exact images separate from structure axioms.

class knowledgespaces.derivation.multimap_family.PerformanceClass(performance, row_indices, competence_ids, competence_states)[source]

One image state and the supplied competence rows that delineate it.

Row indices are zero based; repeated competence states retain separate indices and IDs. Classes appear in order of first image occurrence.

Parameters:
class knowledgespaces.derivation.multimap_family.InverseCompetenceRow(performance_id, performance, row_indices, competence_ids, competence_states, minimal_states)[source]

Exact preimages or positive-covering rows for one requested performance.

minimal_states contains distinct inclusion-minimal states among these rows, in first occurrence order. It is not a smallest-cardinality filter. Empty tuples denote infeasibility, unlike a tuple containing the empty set.

Parameters:
class knowledgespaces.derivation.multimap_family.CompetenceInverseReport(skills, items, exact, rows)[source]

Inverse results with an explicit exact/positive-coverage interpretation.

Families aggregate the rows without adding endpoints. Neither family automatically satisfies the axioms of a competence structure.

Parameters:
property compatible_family: SetFamily

Union of all compatible competence states across requested rows.

property minimal_family: SetFamily

Union of each row’s minima, not minima of the aggregate union.

class knowledgespaces.derivation.multimap_family.ProblemFunction(skill_map, competence_states=None, *, skills=None, competence_ids=None, max_states=100000)[source]

A skill map or multimap evaluated on explicit competence rows.

Parameters:
skill_map

Item labels and their order are retained, including numeric IDs represented as strings. Alternatives are OR; skills within each alternative are AND.

competence_states

Sequence of skill subsets, SetFamily, or KnowledgeStructure. A sequence retains its order and duplicates. Unordered families use cardinality then lexicographic order. None enumerates the powerset in cardinality then skills order, subject to max_states. An empty sequence is an empty family, distinct from None.

skills

Optional column order containing each declared skill exactly once; defaults to sorted skill labels. Skill and item labels may overlap: they always belong to separate column blocks in to_matrix.

competence_ids

Unique string IDs for the competence rows; defaults to C1, C2, … .

max_states

Positive integer guard on the number of competence rows. It raises rather than silently truncating the family. Explicit enumeration can be exponential in the number of skills.

Parameters:

Notes

The exact image is a SetFamily: p(empty) can be nonempty, and an arbitrary supplied family can omit either endpoint. Use image.to_knowledge_structure() to check the structure axioms before using methods requiring a knowledge structure. This class makes no claim about effective/collective fringes for general multimaps.

property competence_family: SetFamily

Distinct supplied competence states, without adding endpoints.

property image: SetFamily

Exact set image p(C), dropping duplicate images but adding nothing.

property equivalence_classes: tuple[PerformanceClass, ...]

Partition competence rows by their identical performance images.

to_matrix()[source]

Binary table: skill columns followed by item columns, one row per ID.

Labels/order are skills + items; the two blocks are distinct even if a skill and an item share a label. Returns an independent array of shape (number of competence rows, number of skills + number of items). Slice at len(skills) for the row-preserving performance matrix.

Return type:

NDArray[int8]

inverse(performance_states=None, *, performance_ids=None, exact=True, max_states=100000)[source]

Report compatible rows and their minima for each requested performance.

exact=True means p(C) = K, including the absence of other items. exact=False means K <= p(C): only positive mastery is required, as in CbKST cbkst_perf2comp. Requested order, duplicates and IDs are retained; unattainable rows remain visible with empty results. None enumerates all item subsets, guarded by max_states. Results always refer to this problem function’s supplied competence rows, so minima are taken within the admissible family, not unrestricted competencies subsequently filtered for admissibility.

Parameters:
Return type:

CompetenceInverseReport

knowledgespaces.curriculum

Course-dependent skill and learning-object structures.

The CDSS workflow (Hockemeyer, CDSS 0.3-1, 2026) starts from taught/required skill assignments. This independent implementation uses the attribution semantics of Falmagne & Doignon (2011), Eq. 5.2, with explicit alternatives, canonical surmise functions and preserved learning-object identifiers.

class knowledgespaces.curriculum.LearningObject(identifier, taught, required=frozenset({}))[source]

One learning-object requirement alternative.

taught skills are acquired together; required skills are jointly required. Several records with the same identifier express alternative requirements, but must agree on the taught skills. Inputs are copied to frozensets; diagnostic violations are allowed until derivation is requested.

Parameters:
identifier: str
taught: frozenset[str]
required: frozenset[str] = frozenset({})
class knowledgespaces.curriculum.AssignmentDiagnostics(untaught_skills, nonteaching_objects, self_requirements)[source]

The three CDSS compliance conditions, with offending labels retained.

These are not a test of acyclicity. A cycle involving several objects can pass all three conditions; a completion conflict can also arise from co-teaching/redundancy without a directed prerequisite cycle.

Parameters:
untaught_skills: frozenset[str]
nonteaching_objects: frozenset[str]
self_requirements: tuple[tuple[str, frozenset[str]], ...]
property compliant: bool
class knowledgespaces.curriculum.CurriculumReachability(sequence, mastered_skills, blocked_objects)[source]

One feasible object sequence and the maximal skills reachable from a seed.

Required skills are tested before each object’s taught skills are added. This is a deterministic feasibility computation, not a stochastic learning model or an optimal curriculum. Objects in blocked_objects cannot be scheduled from the given initial skills under these monotone requirements.

Parameters:
sequence: tuple[str, ...]
mastered_skills: frozenset[str]
blocked_objects: frozenset[str]
exception knowledgespaces.curriculum.CurriculumCompletionError[source]

Strict prerequisite completion includes skills taught by the same object.

class knowledgespaces.curriculum.CurriculumResult(original, completed, skill_function, object_function, skill_relation, object_relation, allow_cycles)[source]

Completed assignments and canonical structures on both labelled domains.

Relations are provided exactly when the respective function is quasi-ordinal, including equivalent items. No state span is materialized. allow_cycles records whether joint-state completion was requested.

Parameters:
original: SkillAssignment
completed: SkillAssignment
skill_function: SurmiseFunction
object_function: SurmiseFunction
skill_relation: SurmiseRelation | None
object_relation: SurmiseRelation | None
allow_cycles: bool
class knowledgespaces.curriculum.SkillAssignment(objects, *, skills=None)[source]

Taught skills and alternative required skill sets for learning objects.

Each LearningObject record is one alternative. Distinct records for the same object must have identical taught skills. Duplicates are removed, while alternative order and first-occurrence object order are retained. A single assignment has one requirement set per object; a multi-assignment has at least one object with alternatives.

The optional explicit skills domain preserves unused skills for diagnostics. Syntax is checked at construction; semantic noncompliance can be inspected with diagnostics() before derivation, which rejects it.

Parameters:
property rows: tuple[LearningObject, ...]
property learning_objects: tuple[str, ...]
property skills: frozenset[str]
property is_single_assignment: bool
taught_by(learning_object)[source]
Parameters:

learning_object (str)

Return type:

frozenset[str]

requirements_for(learning_object)[source]

Alternative jointly required skill sets; the empty set means no prerequisites.

Parameters:

learning_object (str)

Return type:

tuple[frozenset[str], …]

diagnostics()[source]
Return type:

AssignmentDiagnostics

property has_unique_teachers: bool

Exactly one distinct teaching object per skill, including full coverage.

For a single assignment this implies quasi-ordinal derived structures. For a multi-assignment, alternative requirements may still produce a non-quasi-ordinal structure despite unique teaching objects.

skill_attribution()[source]

Each taught skill receives its object’s taught-plus-required sets as clauses.

Return type:

Attribution

object_attribution(*, max_candidates=100000)[source]

Alternative prerequisite object sets, with one consistent identifier per object.

For each required skill choose a teaching object; combine and minimize their unions, then include the target object. This is the attribution specified by CDSS::cdss_lo_sa2af, extended to requirement alternatives.

Parameters:

max_candidates (int)

Return type:

Attribution

is_complete(*, allow_cycles=False)[source]

Whether every required skill has an included teaching/requirement alternative.

In strict completion the witness is included in the required skill set alone. With allow_cycles=True it may also use skills taught by the current object. This tests closure, independently of compliance.

Parameters:

allow_cycles (bool)

Return type:

bool

complete(*, allow_cycles=False, max_candidates=100000)[source]

Complete all prerequisite alternatives, retaining inclusion-minimal results.

Strict completion requires complete prerequisite skill states disjoint from the object’s taught skills. An overlap raises CurriculumCompletionError; it can reflect cycles or redundant co-teaching, not necessarily a directed graph cycle.

With allow_cycles=True, complete taught-plus-required sets jointly, then remove the object’s taught skills from the recorded prerequisites. This gives a defined static skill-space semantics for cycles; it is not a claim that these objects can be scheduled from an empty skill set. Use reachability() for that separate question. Redundant original requirement alternatives are minimized before the overlap check.

Parameters:
  • allow_cycles (bool)

  • max_candidates (int)

Return type:

SkillAssignment

derive(*, allow_cycles=False, max_candidates=100000)[source]

Complete the course and derive canonical functions on skills and objects.

The object function closes the original labelled object attribution, preserving alternatives without treating their duplicate rows as new objects. No state enumeration or empirical validation is implied.

Parameters:
  • allow_cycles (bool)

  • max_candidates (int)

Return type:

CurriculumResult

reachability(initial_skills=())[source]

Compute a feasible object order and all reachable skills by forward chaining.

Unknown initial skills are rejected. Noncompliant assignments can be inspected here; for example an untaught prerequisite remains blocked unless it is explicitly supplied among the initial skills.

Parameters:

initial_skills (Collection[str])

Return type:

CurriculumReachability

classmethod from_pairs(taught, required=(), *, learning_objects=None, skills=None)[source]

Construct a single assignment from (object, skill) tables, as in CDSS.

Optional explicit domains retain empty objects/unused skills for diagnostics. Repeated pairs are harmless. Labels are never stripped, truncated or normalized; whitespace can be part of an identifier.

Parameters:
Return type:

SkillAssignment

classmethod from_matrices(learning_objects, skills, taught, required)[source]

Construct a single assignment from aligned binary object-by-skill matrices.

Parameters:
Return type:

SkillAssignment

to_matrices()[source]

Return object labels, skill labels, taught matrix and required matrix.

This two-matrix layout requires one requirement set per object; use rows to preserve alternatives of a multi-assignment.

Return type:

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

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 7. Springer-Verlag.

Falmagne, J.-C., & Doignon, J.-P. (2011). Learning Spaces, Chapter 11. 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) is the
*informative item* condition: a mastered respondent must be more likely
to give a correct response than an unmastered one. It is necessary for
the item to be informative, but does not by itself guarantee model
identifiability (which depends on the knowledge structure).
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 exactly the structure’s states (no missing, no extra) and sum to 1.

Parameters:
Return type:

StatePosterior

property structure: KnowledgeStructure

Knowledge structure supporting this distribution.

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

Multiplicative assessment rule: Learning Spaces (2011), §13.4.4.

An agreeing state receives a factor zeta[q, response] > 1; disagreeing states receive 1. Normalization gives the next state distribution. This implements Equations (13.9)–(13.10), independently of the kstMatrix code.

class knowledgespaces.assessment.multiplicative.MultiplicativePosterior(structure, probabilities, *, zeta0, zeta1)[source]

State distribution updated by the classical multiplicative rule.

probabilities follow states: cardinality, then sorted labels. zeta0 and zeta1 are finite scalars or complete item mappings, all strictly greater than one. They reward agreement with an incorrect and correct response, respectively. The input arrays and mappings are copied.

Use select_item_half_split() for questioning and is_converged() for a probability threshold. The weights alone are not supplied as a response model to select_item_eig(). To use Bayesian EIG, specify a BLIM. Remark 13.4.5 gives the equivalence for positive BLIM errors: zeta1=(1-beta)/eta and zeta0=(1-eta)/beta.

This immutable object returns a new distribution on update. Repeated evidence is allowed; with fixed parameters the update is permutable. Zero prior masses remain zero and cannot be recovered by assessment.

Parameters:
classmethod uniform(structure, *, zeta0, zeta1)[source]

Start assessment with equal probability on every state.

Parameters:
Return type:

MultiplicativePosterior

property states: list[frozenset[str]]

State order corresponding to the probability vector.

property probabilities: ndarray

Read-only probability vector.

update(item, response)[source]

Reward states agreeing with the response, then normalize.

Logarithms avoid overflow even for very large finite update factors.

Parameters:
Return type:

MultiplicativePosterior

property entropy: float

Shannon entropy in bits, with zero masses contributing zero.

marginal_mastery()[source]

Current probability of mastery of each item.

Return type:

dict[str, float]

Adaptive assessment: item selection policies and termination criteria.

Provides three item-selection policies for adaptive assessment:

  • Expected Information Gain (EIG): pick the item whose response is expected to reduce the entropy of the state posterior the most.

  • Half-split: the classic KST questioning rule — pick the item whose current mastery probability is closest to 1/2, i.e., the item that best bisects the plausible states (Falmagne & Doignon 1988; Falmagne & Doignon 2011, ch. 13).

  • Informative: Equation (13.14), with mastery masses weighting the entropies after each hypothetical update. This differs from Bayesian EIG.

References:

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

Falmagne, J.-C., & Doignon, J.-P. (1988). A class of stochastic procedures for the assessment of knowledge. British Journal of Mathematical and Statistical Psychology, 41(1), 1-23.

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

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, *, rng=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.

rngrandom.Random or None

Choose uniformly among exact computed score ties when supplied. Otherwise choose the first candidate (sorted domain by default).

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

Notes

The EIG for an item \(q\) is derived from the definition of mutual information between the latent state \(K\) and the response \(R_q\):

\[\begin{split}\mathrm{EIG}(q) &= I(K; R_q) \\ &= H(K) - E_{R_q}\bigl[H(K \mid R_q)\bigr] \\ &= H(K) - \bigl[\,P(R_q{=}1)\,H(K \mid R_q{=}1) + P(R_q{=}0)\,H(K \mid R_q{=}0)\bigr].\end{split}\]

The marginal \(P(R_q{=}1) = \sum_K P(R_q{=}1\mid K)\,P(K)\) comes from the law of total probability; the conditional posteriors \(P(K \mid R_q)\) are obtained by Bayes’ rule and renormalized to sum to 1.

References

Cover & Thomas (2006), Elements of Information Theory, §2.

knowledgespaces.assessment.adaptive.select_item_half_split(posterior, candidates=None, exclude=None, *, rng=None)[source]

Select an item by the classic half-split questioning rule.

Picks the item whose current mastery probability \(p_q = P(q \in K) = \sum_{K \ni q} P(K)\) is closest to 1/2 — the item that best bisects the probability mass over plausible states. This is the canonical questioning rule of knowledge space theory (Falmagne & Doignon 1988; Falmagne & Doignon 2011, ch. 13), provided here as the standard baseline against which entropy-based selection (select_item_eig()) is compared.

Parameters:
posteriorStatePosterior or MultiplicativePosterior

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.

rngrandom.Random or None

Uniform choice among exact computed ties, as in Definition 13.4.7. If omitted, ties use candidate order for backward compatibility.

Returns:
ItemScore

The selected item; score = min(p_q, 1 - p_q) in [0, 0.5], higher meaning closer to a perfect bisection. Ties are broken by candidate order (sorted domain order when candidates is None), matching the deterministic tie-breaking of select_item_eig().

Raises:
ValueError

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

Parameters:
Return type:

ItemScore

Notes

Unlike EIG, the half-split criterion does not use the error parameters β and η at selection time: it depends only on the current state distribution. Under a noise-free local independence model the two rules coincide; with noise they can diverge, which is precisely what makes half-split the informative baseline.

knowledgespaces.assessment.adaptive.select_item_informative(posterior, candidates=None, exclude=None, *, rng=None)[source]

Select by Learning Spaces (2011), Definition 13.4.8 / Eq. (13.14).

Minimize p_q H(update(q, 1)) + (1-p_q) H(update(q, 0)), where p_q is the current mastery mass. The returned score is current entropy minus this criterion, in bits; it can be negative. It is not Bayesian expected information gain, which weights by predictive response probabilities under the BLIM instead of mastery masses.

Supports Bayesian and multiplicative updates. Zero-weight outcomes contribute zero without trying to condition on impossible evidence. Candidates and exclusions follow select_item_half_split(). With rng, draw uniformly among exact computed ties, as in Eq. (13.15). Otherwise choose the first candidate (sorted domain by default). No almost-sure convergence theorem for arbitrary combinations is implied.

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 or MultiplicativePosterior

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, rng=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 an item with the highest EIG (ties broken at random), 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).

rngrandom.Random or None

Source for the random tie-break and instance draw. Defaults to the module-level generator; pass a seeded random.Random for reproducible selections.

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

Bounded assessment sessions, observed-pattern replay and latent-state simulation.

These workflows compose the documented questioning and update rules; a stopping threshold is a decision rule, not a calibration or convergence proof.

class knowledgespaces.assessment.workflows.AssessmentStep(question, item, response, score, selection_seconds, update_seconds)[source]

One observation; times are wall-clock seconds, score uses the chosen policy.

Parameters:
class knowledgespaces.assessment.workflows.AssessmentResult(posterior, steps, stop_reason, probability_history)[source]

Final distribution and every administered question, including unfinished runs.

probability_history is empty unless requested; otherwise it includes the initial distribution and every update, ordered as posterior.states. MAP ties select the first state in cardinality/label order. converged means only that the requested probability threshold was reached.

Parameters:
knowledgespaces.assessment.workflows.run_assessment(posterior, ask_fn, *, selection='half_split', threshold=0.85, max_questions=25, repeat_items=False, instances=None, rng=None, track_probabilities=False, max_memory_bytes=512000000)[source]

Run a session from any Bayesian or multiplicative starting distribution.

ask_fn receives an item label, or an instance ID when instances is supplied. Responses must be booleans or numeric 0/1; other values and callback exceptions propagate. No state is changed in the input.

EIG requires a Bayesian posterior. Half-split and informative support both update rules. In item mode ties use candidate order unless rng is supplied. Instance EIG retains select_instance_eig()’s random near-tie selection; other instance policies use exact score ties. Pass random.Random(seed) to reproduce every random selection.

By default each item is asked at most once. repeat_items=True calls ask_fn anew each time; Bayesian updates then assume independent fresh responses conditional on a fixed state. With an instance pool, each instance is used at most once and repeat_items must be false.

Stop at max(P) >= threshold, exhaustion, or max_questions. The threshold has priority, then exhaustion, then the question limit. A zero question limit is permitted. All options are validated even if the initial distribution already reaches the threshold. Requested probability histories have an estimated memory guard, not an exact process-memory bound. Timing excludes the user’s response callback.

Parameters:
Return type:

AssessmentResult

class knowledgespaces.assessment.workflows.AssessmentEvaluation(results, weights, response_distances=None, structure_distances=None, true_states=None, state_distances=None)[source]

Per-row results with weights and explicitly distinguished distance metrics.

Replay reports Hamming distance of the diagnosed state to the observed response pattern, the pattern’s minimum distance to the structure, and their difference. These are not latent-state accuracy measurements. Simulation instead reports distance to the supplied true latent state. No failed-threshold session is dropped. Weighted means use all rows.

Parameters:
knowledgespaces.assessment.workflows.evaluate_assessment(posterior, data, *, selection='half_split', threshold=0.85, max_questions=25, repeat_items=False, rng=None, track_probabilities=False, max_memory_bytes=512000000)[source]

Replay one assessment per stored complete binary response row.

Labels are aligned by name; supplied row weights are retained. Aggregated rows are assessed once, so randomized ties are not independent draws for each individual represented by their weight. Expand rows when those independent draws are wanted. Zero-weight rows remain in the result.

repeat_items=True reproduces fixed-pattern repeated querying as in kstMatrix’s assessment simulation. Reusing the same observed answer is not fresh independent evidence: report it as a replay experiment, not calibrated Bayesian assessment or true-state accuracy. Default false prevents that reuse. Every row starts from the same supplied prior.

Parameters:
Return type:

AssessmentEvaluation

knowledgespaces.assessment.workflows.simulate_assessment(posterior, true_states, *, response_model, selection='half_split', threshold=0.85, max_questions=25, repeat_items=False, seed=None, track_probabilities=False, max_memory_bytes=512000000)[source]

Generate fresh conditionally independent responses during each session.

Each supplied state defines one simulated person and remains fixed throughout that assessment. Draw a Bernoulli response only when asked, using response_model slip/guess parameters. Repeated questions get new draws. Generating and assessing models may differ but must share item labels; true states must belong to the generating model, and need not belong to the assessed structure. This permits misspecification studies.

seed initializes separate local NumPy response and Python tie-breaking generators; it does not reproduce R seeds. Results describe this sample, without an automatic calibration or uncertainty claim.

Parameters:
Return type:

AssessmentEvaluation

knowledgespaces.estimation

EM algorithm for BLIM parameter estimation.

Estimates slip (β) and guess (η) parameters from observed response patterns using Expectation-Maximization. Per-item parameters are the default; BLIMConstraints supplies fixed values and homogeneous or grouped parameterizations.

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 7. Springer-Verlag.

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

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

exception knowledgespaces.estimation.blim_em.ConvergenceWarning[source]

Emitted when an iterative estimator fails to meet its convergence criterion.

exception knowledgespaces.estimation.blim_em.SparseGOFWarning[source]

The response table is sparse; assess the chi-squared approximation.

Emitted when the sample size is smaller than the number of possible response patterns minus one (N < 2^|Q| - 1), the regime in which the pks degrees-of-freedom convention caps the saturated dimension at N. This is a screening rule, not a theorem that every sparse-table approximation fails. Expected cell counts and model regularity matter (Koehler & Larntz, 1980). Treat p_value as descriptive and consider bootstrap_gof() for calibration.

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_npatterns)[source]

Goodness-of-fit statistics for a BLIM estimate.

Follows the approach of Heller & Wickelmaier (2013, Electronic Notes in Discrete Mathematics) and the conventions of the R pks package (Wickelmaier, Heller & Mollenhauer). 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: it is a software convention, not the dimension of the saturated multinomial model, which remains 2^Q - 1. Sparse observations do not change population identifiability.

p_valuefloat

P-value from the chi-squared reference for G2. Asymptotic and descriptive: when the pattern table is sparse relative to the sample size (N < 2^Q - 1), the reference distribution is unreliable and a SparseGOFWarning is emitted; bootstrap_gof() provides a simulated reference under the fitted model and refit policy, without a general calibration guarantee for singular models.

nparint

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

AICfloat

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

BICfloat

Bayesian Information Criterion using the total sample size: -2*LL + ln(N)*npar. This is the standard BIC definition of Schwarz (1978, p. 461), where N is the number of independent observations contributing to the likelihood. For BLIM each of the N respondents supplies one i.i.d. draw from the pattern distribution, so the Laplace-approximation derivation of the log(N)·npar penalty applies in regular models. BLIM selection problems are often nonregular — estimates on the boundary of the parameter space, rank-deficient structures — and the standard consistency argument then needs qualification, so read AIC and BIC as conventional summaries rather than oracles; predictive comparisons (e.g., cross-validated log-scores) are the more defensible primary criterion.

BIC_npatternsfloat

Variant Bayesian Information Criterion using the number of distinct observed response patterns: -2*LL + ln(n_patterns)*npar. This matches what R pks::blim() returns: pks does not define an explicit BIC method, instead overriding nobs.blim to return the count of distinct patterns and delegating to stats::BIC (see cran/pks/R/blim.R, logLik.blim / nobs.blim). Provided for cross-package replication; not recommended as a primary selection criterion because the count of distinct patterns is bounded above by 2^Q and therefore does not satisfy the asymptotic-consistency conditions of Schwarz (1978).

Parameters:
class knowledgespaces.estimation.blim_em.BLIMEstimate(beta, eta, pi, log_likelihood, n_iterations, converged, items, states, gof, degenerate_items, method='ML', constraints=None, discrepancy=None, data_signature=None)[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).

degenerate_itemstuple[str, …]

Items whose final beta[q] + eta[q] >= 1 - 1e-3. Such items are flagged near, at, or beyond the non-discriminating boundary. Equality beta + eta = 1 means independence of mastery; larger sums reverse discrimination. The tolerance also flags small positive discrimination. Inspect coding and fit stability before changing the structure or removing items.

methodstr

Estimation method that produced this estimate: "ML" (maximum likelihood via EM), "MD" (minimum discrepancy), or "MDML" (maximum likelihood restricted to minimum-discrepancy state assignments); see Heller & Wickelmaier (2013).

Parameters:
data_signature: str | None = None

Fingerprint of the training response measure; None for older/manual fits.

residuals(data, *, chunk_size=1024, max_patterns=1048576, max_memory_bytes=512000000)[source]

Full-table Pearson/deviance residuals on training or held-out data.

Parameters:
Return type:

BLIMResiduals

predict(patterns, *, items=None, method='ML', discrepancy=None, inclusion=None, max_memory_bytes=512000000)[source]

Predict responses and states; columns default to training item order.

Prediction defaults to ML (Bayes), independently of the estimation method. MD/MDML inherit fitted discrepancy options unless overridden or an explicit inclusion mask is supplied. Hyperbolic MD fits require a minimum-rule override for MDML prediction. Fixed-zero constraints are preserved. See predict_blim() for assignment semantics.

Parameters:
Return type:

BLIMPrediction

reliability(*, chunk_size=1024, max_patterns=1048576, max_memory_bytes=512000000, tie_tolerance=1e-12)[source]

Full-test reliability at this fit; parameter uncertainty is omitted.

Parameters:
  • chunk_size (int)

  • max_patterns (int)

  • max_memory_bytes (int)

  • tie_tolerance (float)

Return type:

BLIMReliability

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, *, method='ML', max_iter=500, tol=1e-06, beta_init=0.1, eta_init=0.1, pi_init=None, constraints=None, discrepancy=None, max_memory_bytes=8000000000)[source]

Estimate BLIM parameters by ML (EM), MD, or MDML.

Parameters:
structureKnowledgeStructure

The knowledge structure defining valid states.

dataResponseMatrix

Observed response patterns.

method{“ML”, “MD”, “MDML”}

Estimation method (Heller & Wickelmaier 2013; same options as pks::blim()). "ML" (default) maximizes the likelihood via EM. "MD" is the non-iterative minimum-discrepancy estimator: each observed pattern is assigned (uniformly) to the knowledge states requiring the fewest response errors to explain it, and the parameters are read off that assignment in a single M-step — deterministic given the data, independent of beta_init/ eta_init/tol. "MDML" runs EM with the posterior restricted to the minimum-discrepancy states of each pattern (maximum likelihood among minimum-discrepancy solutions).

max_iterint

Maximum number of EM iterations (ML/MDML). Default 500.

tolfloat

Convergence tolerance. For "ML" this is the absolute change in log-likelihood between iterations; for "MDML" it is the maximum absolute change in the parameters (pi, beta, eta), mirroring the stopping rule of pks::blim(). Ignored for "MD". Default 1e-6.

beta_initfloat or np.ndarray

Initial slip values. A scalar initializes every item equally; it does not impose equality during estimation. Free initial errors are clipped to [1e-6, 1 - 1e-6], as in the subsequent M-steps. Values must be finite and in [0, 1); beta_init + eta_init may equal or exceed 1, consistently with the fitted parameter space.

eta_initfloat or np.ndarray

Initial guess values, finite and in [0, 1). Equality during fitting requires constraints. No joint informative-item inequality is imposed.

pi_initMapping or np.ndarray or None

Initial state distribution (canonical order); None uses uniform. Ignored by MD. Zeros are absorbing in EM; use a positive start to allow every state positive fitted mass.

constraintsBLIMConstraints or None

Fixed and shared error parameters, optionally a fully fixed pi. Grouped updates pool sufficient statistics. The free parameter count excludes fixed classes and accounts for equality classes. A fixed pi is supported by ML/MDML, not the MD assignment rule. Zero fixed error rates exclude incompatible assignments exactly.

discrepancyMDOptions or None

Optional radius for MD/MDML, or a hyperbolic inclusion rule for MD. None uses ordinary minimum discrepancy. Not applicable to ML.

max_memory_bytesint

Preflight limit on estimated peak array allocation, including response-by-state and response-by-item intermediates. This is an allocation estimate, not an OS-level memory cap. Default 8 GB.

Returns:
BLIMEstimate

Estimated parameters, log-likelihood, and convergence info. log_likelihood is always the unrestricted BLIM log-likelihood evaluated at the returned parameters (also for MD/MDML, matching pks).

Raises:
ValueError

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

MemoryError

If the estimated posterior allocation would exceed max_memory_bytes.

Parameters:
Return type:

BLIMEstimate

Notes

The M-step independently clips beta[q] and eta[q] into [1e-6, 1 - 1e-6], mirroring R pks::blim(). The canonical BLIM parameter space is the open box per item; the joint condition beta[q] + eta[q] < 1 is the informative item condition of the identifiability literature (Stefanutti, Heller, Anselmi & Robusto 2012; Spoto, Stefanutti & Vidotto 2013), not part of the parameter space, and is therefore not enforced inside the loop. This is a design choice, not a necessity: the order-restricted M-step retains a closed form (the two success probabilities of an offending item pool into their common success rate) and, being an exact constrained maximization, preserves the EM ascent property. The pooled update, however, parks the item exactly on the uninformative boundary beta + eta = 1, and enforcing the strict inequality would require an arbitrary margin, so the violation is left visible.

Items whose final beta[q] + eta[q] >= 1 - 1e-3 are surfaced via BLIMEstimate.degenerate_items and a ConvergenceWarning is emitted. This is a diagnostic of weak, absent or reversed discrimination, not a sufficient reason to remove an item.

References

Dempster, A. P., Laird, N. M., & Rubin, D. B. (1977). Maximum likelihood from incomplete data via the EM algorithm. J. R. Stat. Soc. B, 39(1), 1-38.

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

Stefanutti, L., Heller, J., Anselmi, P., & Robusto, E. (2012). Assessing the local identifiability of probabilistic knowledge structures. Behavior Research Methods, 44(4), 1197-1211.

Spoto, A., Stefanutti, L., & Vidotto, G. (2013). Considerations about the identification of forward- and backward-graded knowledge structures. Journal of Mathematical Psychology, 57(5), 249-254.

knowledgespaces.estimation.blim_em.estimate_blim_restarts(structure, data, *, method='ML', n_restarts=10, max_iter=500, tol=1e-06, seed=None, max_memory_bytes=8000000000, init_range=(0.01, 0.4), init_strategy='uniform', constraints=None, discrepancy=None)[source]

Estimate BLIM parameters with multiple random restarts.

Runs estimate_blim() n_restarts times with random initial values for beta, eta (and pi for init_strategy="pks"), 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.

method{“ML”, “MDML”}

Estimation method forwarded to estimate_blim(). "MD" is rejected here: the minimum-discrepancy estimator is deterministic given the data, so restarting it is pointless — call estimate_blim() with method="MD" directly instead.

n_restartsint

Number of random restarts. Default 10.

max_iterint

Maximum EM iterations per restart.

tolfloat

Convergence tolerance per restart.

seedint, numpy.random.Generator or None

Random seed for reproducibility.

max_memory_bytesint

Forwarded to estimate_blim(). Default 8 GB.

discrepancyMDOptions or None

Forwarded unchanged to every fit. MDML supports a minimum-distance radius; options are not applicable to ML.

init_rangetuple[float, float]

Lower/upper bounds for the random U(low, high) draw of beta_init and eta_init when init_strategy="uniform". Default (0.01, 0.4). Ignored when init_strategy="pks".

init_strategy{“uniform”, “pks”}

Random initialization strategy. "uniform" (default) draws both parameters from U(*init_range) and rescales in-place until beta[q] + eta[q] < 0.95 on each item. "pks" mirrors pks::blim(..., randinit=TRUE) (R source: cran/pks/R/blim.R): each parameter is drawn from U(0, 1), then reflected as 1 - x sequentially — beta first on items where beta[q] + eta[q] >= 1, then eta on items where the constraint still fails with the updated beta — to restore the informative-item condition. The state prior uses Dirichlet(1, …, 1), uniform on the simplex, independently of the errors. This has the same law as pks’s uniform spacings. NumPy and R random streams differ. The default "uniform" strategy keeps the prior at equal state masses. A fixed prior overrides the draw.

Returns:
BLIMEstimate

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

Parameters:
Return type:

BLIMEstimate

Notes

The default init_range=(0.01, 0.4) is a narrowed basin that avoids near-boundary draws at the uninformative frontier beta + eta = 1, where EM can stall in a degenerate-item attractor (cf. Stefanutti, Heller, Anselmi & Robusto 2012). The "pks" strategy is provided for reproducibility with the R pks package: note that pks uses runif(nitems) = U(0, 1) with reflection, not U(0, 0.5) as sometimes reported.

References

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

Common-data model comparisons and explicitly screened likelihood-ratio references.

Nominal parameter counts are not automatically regular model dimensions. See Self & Liang (1987), JASA 82, 605–610, and Drton (2009), Annals of Statistics 37, 979–1012, for boundary and singular likelihood-ratio limits.

class knowledgespaces.estimation.comparison.ModelScore(name, family, method, n_parameters, log_likelihood, deviance, AIC, BIC, BIC_npatterns, nominal_residual_df, pks_residual_df, converged, training_data_matches, stored_likelihood_matches)[source]

Likelihood evaluated on the supplied data; IC penalties are conventional.

n_parameters is the declared free parameter count, not a proven identifiable dimension. BIC uses respondent weight; BIC_npatterns reproduces pks’s distinct-observed-pattern convention. Training equality uses a recorded empirical-measure fingerprint, not only the sample size. None means an older or manually constructed fit has no fingerprint.

Parameters:
class knowledgespaces.estimation.comparison.ModelContrast(null, alternative, statistic, df, nested, p_value, issues, null_rank=None, alternative_rank_at_null=None)[source]

Adjacent ordered-model contrast; raw signed LR and nominal df difference.

A requested chi-square p-value is NaN when checks fail, with reasons in issues. No reference requested gives None. Numerical ranks at the null estimate screen local regularity; they do not prove generic/global identifiability, correct specification, or global likelihood maximization.

Parameters:
  • null (str)

  • alternative (str)

  • statistic (float)

  • df (int)

  • nested (bool | None)

  • p_value (float | None)

  • issues (tuple[str, ...])

  • null_rank (int | None)

  • alternative_rank_at_null (int | None)

class knowledgespaces.estimation.comparison.ModelComparison(scores, contrasts, reference, n_respondents, n_patterns, data_signature)[source]

Scores in caller order and contrasts between consecutive models.

Parameters:
knowledgespaces.estimation.comparison.compare_models(data, models, *, reference='none', boundary_tolerance=1e-05, likelihood_tolerance=1e-07, max_memory_bytes=512000000)[source]

Compare BLIM/SLM fits on exactly the supplied complete response data.

Supply at least two distinct names in the desired null-to-alternative order. Scores are recomputed from parameters and include any held-out or differently trained model, with training-match flags. MD and MDML fits may be scored descriptively; their full likelihood is not an ML optimum. Item columns and state vectors are aligned by labels, not position.

reference='chi2' requests a conditional Wilks reference, screened for same training measure, converged ML fits, consistent stored likelihoods, established model nesting, positive nominal dimension difference, nonnegative LR (up to absolute likelihood_tolerance), interior free coordinates at the null estimate and full-column-rank response Jacobians. Free state-support extensions are boundary comparisons and fail this screen. A failed screen yields NaN plus issues, never a fabricated test. An accepted screen still needs the scientific assumptions of Wilks’s theorem and adequate optimization/sample size; it is not a proof of them.

Nesting recognizes error equalities/fixed values, fixed/free BLIM state masses, state-family inclusions and SLM-to-free-prior BLIM restrictions. General BLIM-to-SLM inclusion is left undetermined. Numerical rank is evaluated at the null fit in both models, using full response tables; enumeration/memory guards propagate. MD/MDML and held-out comparisons remain descriptive even if their numerical scores happen to coincide.

Parameters:
Return type:

ModelComparison

class knowledgespaces.estimation.comparison.BootstrapModelComparison(null_fit, alternative_fit, statistic, statistics, converged, p_value, n_extreme, seed, n_restarts, n_invalid)[source]

Plug-in null simulation of the specified ML fitting/search procedure.

Observed fits are recomputed with the same settings as every replicate. Arrays retain all replicates; no unconverged/invalid run is discarded. p_value is NaN if any selected fit failed convergence or a materially negative/nonfinite LR occurred. Otherwise it uses (1 + extremes)/(B + 1). This is a fitted-null Monte Carlo calibration, not an exact finite-sample test, a proof of bootstrap consistency at singularities, or a guarantee of global maxima. Both observed and replicate convergence flags are exposed.

Parameters:
knowledgespaces.estimation.comparison.bootstrap_model_comparison(data, null_model, alternative_model, *, n_replicates=200, seed=None, n_restarts=1, max_iter=5000, tol=1e-07, max_memory_bytes=512000000)[source]

Refit two nested model specifications and simulate under the fitted null.

Input fits supply structure, family and constraints; their parameter values and optimization history are not reused. Both observed models are fitted anew by ML, then each simulated sample is fitted by the same policy. One restart uses deterministic standard starts; more use the documented BLIM/SLM uniform random-start policies. For every alternative fit, an additional ML run starts at the embedded fitted null, selecting the higher likelihood (first wins ties). This helps preserve the nesting inequality without claiming global maximization. Free coordinates can be clipped by the estimators’ documented numerical box at initialization. Starts with beta + eta >= 1 are allowed, as in the fitted parameter space: an embedded null is not rejected or reflected to enforce positive discrimination.

Requires established nesting, complete binary data and integer frequency weights. Every replicate draws N independent respondents from the fitted null, using its BLIM response probabilities even when it is an SLM. This can be used for state-support boundary comparisons where ordinary chi2 is unavailable. It does not settle general nonregular bootstrap validity. Failed selected fits are retained; an aggregate warning and NaN p-value require further optimization rather than silently conditioning on success.

Parameters:
Return type:

BootstrapModelComparison

Simple learning model: Falmagne & Doignon (2011), Eq. 11.18, Thm. 11.5.4.

The state mass is the product of g on the state and (1-g) on its outer fringe. On a learning space these masses sum to one without normalization. Independent EM implementation using Bernoulli sufficient statistics.

knowledgespaces.estimation.slm.slm_state_probabilities(structure, g=0.1, *, items=None)[source]

SLM state distribution in canonical state order, including g=0 or 1.

Arrays of g follow items (sorted domain by default); mappings align by label. The learning-space hypothesis is checked, not bypassed by renormalizing arbitrary families. The returned array is read-only. Solvability g is not generally the marginal probability of mastering q.

Parameters:
Return type:

ndarray

class knowledgespaces.estimation.slm.SLMRestart(beta_init, eta_init, g_init, log_likelihood, objective, n_iterations, converged)[source]

One search run, with effective starts in the estimate’s item order.

Starts include fixed/equality projection and the numerical box. The objective is full log likelihood for ML and the restricted joint sum for MDML; log_likelihood always evaluates the full response model.

Parameters:
class knowledgespaces.estimation.slm.SLMEstimate(beta, eta, pi, log_likelihood, n_iterations, converged, items, states, gof, degenerate_items, method='ML', constraints=None, discrepancy=None, data_signature=None, g=<factory>, objective_history=(), restarts=(), selected_restart=None, restart_selection=None)[source]

SLM fit, with BLIM response prediction/residuals and an SLM prior.

g follows items. gof.npar counts free error groups plus |Q| solvability parameters; state masses are derived, not freely estimated. objective_history records the EM criterion before and after updates: ordinary log likelihood for ML, restricted joint sum for MDML, empty for single-step MD. Reported log_likelihood/G2 always use the full model. Use slm_jacobian (or .jacobian()) for this parameterization. A BLIM rank report with free state masses does not test SLM identifiability.

Parameters:
g_dict()[source]

Solvability estimates by item label.

Return type:

dict[str, float]

jacobian(*, max_memory_bytes=512000000)[source]

Response-map Jacobian for the fitted SLM, in sorted item order.

Parameters:

max_memory_bytes (int)

Return type:

ndarray

knowledgespaces.estimation.slm.estimate_slm(structure, data, *, method='ML', g_init=0.1, beta_init=0.1, eta_init=0.1, constraints=None, discrepancy=None, max_iter=5000, tol=1e-07, max_memory_bytes=512000000)[source]

Fit the simple learning model by ML, MD or MDML.

The E-step uses BLIM errors and the SLM state distribution. The g update is expected membership / (expected membership + expected outer-fringe membership). Error updates pool the same sufficient statistics as BLIM. ML/MDML stop on max absolute change in beta, eta and g, as pks::slm. Convergence does not guarantee a global optimum or identifiability.

MD uses one discrepancy assignment and M-step, independent of starts. Optional MDOptions adds a radius (MD/MDML) or hyperbolic weights (MD). Fixed/equal beta/eta are supported; pi_fixed is rejected because pi is determined by g. All g and free errors use the numerical box [1e-6,1-1e-6]; only fixed errors can be exactly zero. No g constraints are imposed and no latent-state distribution is silently normalized.

Parameters:
Return type:

SLMEstimate

knowledgespaces.estimation.slm.estimate_slm_restarts(structure, data, *, method='ML', n_restarts=10, seed=None, init_range=(0.01, 0.4), init_strategy='uniform', selection='objective', constraints=None, discrepancy=None, max_iter=5000, tol=1e-07, max_memory_bytes=512000000)[source]

Search SLM fits from random error and solvability starts.

n_restarts=1, init_strategy="pks" supplies the initialization law of pks::slm(randinit=TRUE): two U(0,1) error draws with sequential reflection, then independent U(0,1) solvabilities. NumPy and R seeds do not produce identical draws. uniform uses BLIM’s narrowed error starts (U(low, high), halved together until their sum is below .95); g still uses U(0,1). init_range is validated but unused for pks.

Fixed/equal errors are projected before the first E-step, as in estimate_slm; pks does not initially project its equality groups. Constraints can override the informative starting condition, which is not imposed on subsequent estimates. Free starts are clipped to [1e-6,1-1e-6]. All fit settings are forwarded unchanged to each run.

Selection maximizes the optimized criterion by default: likelihood for ML, restricted joint sum for MDML. selection="likelihood" instead ranks full response likelihoods (the BLIM restart convention). First run wins exact ties; unconverged runs are retained and eligible. The result records every start’s diagnostics and the zero-based chosen index. A selected unconverged fit warns. Restarts offer no global-optimum or identifiability guarantee. MD is deterministic and is rejected.

Parameters:
Return type:

SLMEstimate

knowledgespaces.estimation.slm.slm_jacobian(structure, *, g=0.1, beta=0.1, eta=0.1, constraints=None, max_items=None, max_memory_bytes=512000000)[source]

Analytic response Jacobian: free beta groups, eta groups, then all g.

Items are sorted; groups follow blim_jacobian’s constraint convention. The chain rule uses exact polynomial derivatives of Eq. 11.18, including boundary g values. Rank is a pointwise numerical diagnostic, not a generic/global proof. Enumeration and memory guards match blim_jacobian.

Parameters:
Return type:

ndarray

class knowledgespaces.estimation.slm.SLMBootstrap(p_value, g2_observed, g2_replicates, n_replicates, n_extreme, n_capped, method, seed, estimate, beta_replicates=<factory>, eta_replicates=<factory>, pi_replicates=<factory>, converged_replicates=<factory>, iterations_replicates=<factory>, g_replicates=<factory>, *, statistic='G2', x2_observed=None, x2_replicates=<factory>, n_failed=0, replicate_errors=(), n_restarts=None, init_strategy='uniform', init_range=(0.01, 0.4))[source]

SLM bootstrap with g replicates in addition to beta/eta/pi and G2.

g_replicates follows estimate.items. The inherited parameter_summary describes beta/eta/derived pi; use g_replicates.std(axis=0, ddof=1) for solvability dispersion. All refits are retained, including unconverged.

Parameters:
knowledgespaces.estimation.slm.bootstrap_slm(structure, data, *, method='ML', n_replicates=1000, seed=None, n_restarts=None, init_range=(0.01, 0.4), init_strategy='uniform', selection='objective', constraints=None, discrepancy=None, max_iter=5000, tol=1e-07, max_memory_bytes=512000000)[source]

Fit, simulate and refit the same SLM, preserving its prior restriction.

Integer respondent counts are required. Same settings are used for observed and bootstrap fits; beta/eta/g starts are the default 0.1. With n_restarts supplied, each fit instead uses estimate_slm_restarts with identical search settings and fresh draws from the seeded generator. Random restarts are unavailable for deterministic MD. Replicate arrays retain the selected fit of each search, including unconverged selections. The add-one Monte Carlo p-value and parameter dispersions have the same model/convergence qualifications as bootstrap_gof. No BLIM with free state masses is substituted for the SLM during refitting. max_memory_bytes guards each fit’s working arrays; retained replicate arrays additionally require O(n_replicates * (|Q| + |K|)) storage.

Parameters:
Return type:

SLMBootstrap

State inclusion rules for discrepancy-based estimation, as in pks.

Independent implementations of the distance rules, with exact feasibility under fixed-zero error parameters and log-scaled hyperbolic weights.

class knowledgespaces.estimation.discrepancy.MDOptions(rule='minimum', radius=0, exponent=1.0)[source]

Discrepancy assignment options shared by fitting and refitting.

minimum includes feasible states with d(R,K) <= d_min + radius. hypblc1 weights all feasible states by (1+d-d_min)**(-exponent); hypblc2 uses (1+d)**(-exponent). Hyperbolic rules are available for non-iterative MD only, with radius=0. The exponent must be positive. Radius is an excess distance from the response, not a distance between a state and the nearest state. Defaults recover ordinary MD/MDML. These are the pks incradius and blimMD inclusion conventions.

Parameters:
  • rule (Literal['minimum', 'hypblc1', 'hypblc2'])

  • radius (int)

  • exponent (float)

Observed-data BLIM likelihood for incomplete binary responses.

Independent marginal-likelihood EM. Ignorability requires MAR and distinct response/missingness parameters (Rubin, 1976); a NaN input is not evidence for these assumptions. No missing-not-at-random mechanism is fitted here.

exception knowledgespaces.estimation.incomplete.UnobservedItemWarning[source]

At least one item has no observed responses; individual errors lack information.

class knowledgespaces.estimation.incomplete.IncompleteResponseMatrix(items, patterns, counts=None)[source]

Immutable 0/1/NaN responses with optional nonnegative frequency weights.

Rows may be individual or aggregated. NaN is the only missing marker; zero remains an observed incorrect response. Aggregation includes the missingness mask. Arrays and labels are independent snapshots. Complete conversion rejects NaN instead of silently deleting or imputing rows.

Parameters:
property observed: ndarray

Boolean mask: True means an observed response.

property n_informative: float

Total weight of rows with at least one observed item.

aggregate()[source]

Combine identical values AND masks; remove zero-frequency rows.

Return type:

IncompleteResponseMatrix

to_complete()[source]

Return an independent complete matrix, only when every cell is observed.

Return type:

ResponseMatrix

knowledgespaces.estimation.incomplete.predict_blim_incomplete(structure, patterns, *, items=None, beta=0.1, eta=0.1, pi=None, max_memory_bytes=512000000)[source]

Marginalize missing cells and condition states on the observed cells.

Probabilities are cylinder-event probabilities, not joint probabilities of response AND missingness. Rows with different masks can overlap and must not be summed as disjoint cells. All-missing rows have probability one and the prior posterior. Impossible events have NaN posteriors. Parameter vectors follow items, state vectors follow canonical order.

Parameters:
Return type:

BLIMPrediction

class knowledgespaces.estimation.incomplete.IncompleteBLIMEstimate(items, states, beta, eta, pi, log_likelihood, npar, n_respondents, n_informative, n_iterations, converged, objective_history, constraints, unobserved_items, degenerate_items, restarts=(), selected_restart=None)[source]

ML fit to observed cells, without an automatic chi-squared GOF.

npar is the nominal free-coordinate count, not an identified dimension. n_informative excludes wholly missing rows, which contribute LL=0. AIC is conventional; BIC uses n_informative and is descriptive under nonidentifiability/unequal observation designs. For a likelihood-ratio comparison use incomplete_gof; do not use complete-table residuals.

Parameters:
knowledgespaces.estimation.incomplete.estimate_blim_incomplete(structure, data, *, beta_init=0.1, eta_init=0.1, pi_init=None, constraints=None, max_iter=5000, tol=1e-07, max_memory_bytes=512000000)[source]

Observed-data ML with fixed/shared errors and optional fixed state prior.

Only observed responses enter error numerators AND denominators. Rows entirely missing do not alter the fit. Never-observed items are warned about; unconstrained errors without information retain their starts. Stopping uses maximum absolute parameter change; a converged EM need not find a unique or global optimum. Free pi_init must be strictly positive to avoid locking out states; fixed priors may contain zeros. No MD/MDML or MNAR model is implied.

Parameters:
Return type:

IncompleteBLIMEstimate

Reproducible random-start search for observed-data BLIM likelihoods.

class knowledgespaces.estimation.incomplete_restarts.IncompleteBLIMRestart(beta_init, eta_init, pi_init, log_likelihood, objective, n_iterations, converged, error=None)[source]

One attempted fit, including failures and runs reaching the iteration cap.

Effective starts include fixed/equality projection, in the returned fit’s item and canonical state orders. objective is the observed-data log likelihood, equal to log_likelihood. An errored run has NaN scores, zero completed iterations and a nonempty error. Nonconvergence is separate: a capped fit has finite scores and is eligible for selection.

Parameters:
exception knowledgespaces.estimation.incomplete_restarts.IncompleteBLIMRestartError(restarts)[source]

Every start failed; attempted starts and errors remain in restarts.

Parameters:

restarts (tuple[IncompleteBLIMRestart, ...])

Return type:

None

knowledgespaces.estimation.incomplete_restarts.estimate_blim_incomplete_restarts(structure, data, *, n_restarts=10, seed=None, init_range=(0.01, 0.4), init_strategy='uniform', constraints=None, max_iter=5000, tol=1e-07, max_memory_bytes=512000000)[source]

Maximize observed-data BLIM likelihood across random initializations.

uniform draws independent error parameters in init_range, halves paired values until beta+eta<.95, and starts pi at equal masses. pks draws errors from U(0,1) with sequential beta/eta reflection and pi from Dirichlet(1, …, 1), the uniform-simplex law of pks 0.7-0. Constraints override starts; the fit does not impose beta+eta<1 on subsequent iterates. The name describes initialization only, not an incomplete-data pks fit.

Every run retains effective starts, likelihood, objective, convergence, iterations and numerical failure. Select the largest finite likelihood; first run wins exact ties, even if unfinished. A selected unfinished fit warns; all-error searches raise IncompleteBLIMRestartError with records. Invalid arguments and resource limits raise directly, not as fit failures. Seed/Generator controls a local stream, never NumPy’s global RNG.

This changes optimization, not missingness assumptions, identifiability, or the absence of a global-optimum guarantee. See estimate_blim_incomplete.

Parameters:
Return type:

IncompleteBLIMEstimate

Saturated observed-data likelihood and BLIM likelihood-ratio comparison.

The saturated model is a common distribution over COMPLETE responses, observed through each row’s mask. No independent mask-distribution factor or complete-table chi-squared degrees of freedom are assumed.

class knowledgespaces.estimation.incomplete_gof.SaturatedIncomplete(items, complete_patterns, probabilities, log_likelihood, n_iterations, converged, optimality_gap, objective_history)[source]

Nonparametric ML on a finite response simplex, possibly nonidentified.

complete_patterns is lexicographic in items; probabilities sum to one. optimality_gap is max_j(dLL/dpi_j)/N - 1 (clamped at zero). N*gap bounds the likelihood improvement over the current point by concavity. Converged means gap <= tol, not unique complete probabilities. Entirely missing rows do not affect the fit. Arrays are read-only.

Parameters:
knowledgespaces.estimation.incomplete_gof.saturated_incomplete(data, *, max_iter=10000, tol=1e-07, max_patterns=1048576, max_memory_bytes=512000000)[source]

Fit a common saturated distribution by multinomial incomplete-data EM.

Uniform positive initialization includes all 2**q completions. Tolerance controls the concave directional gap, rather than only parameter change. Enumeration and compatibility arrays are guarded before allocation. At the iteration cap, return the current fit with a convergence warning.

Parameters:
Return type:

SaturatedIncomplete

class knowledgespaces.estimation.incomplete_gof.IncompleteGOF(G2, log_likelihood, saturated)[source]

Observed-data likelihood ratio; no automatic chi-squared reference.

G2 is twice saturated LL minus fitted-model LL on the supplied data. It is NaN if the approximate saturated solution is worse than the BLIM. Saturated convergence is retained; its N*optimality_gap bounds the remaining saturated LL improvement. Use a justified missingness model when bootstrapping. No mask-frequency Pearson table is constructed.

Parameters:
knowledgespaces.estimation.incomplete_gof.incomplete_gof(estimate, data, *, max_iter=10000, tol=1e-07, max_patterns=1048576, max_memory_bytes=512000000)[source]

Compare a supplied fit on these data to their saturated observed model.

Item labels align explicitly; held-out comparisons are descriptive and are not a training-sample likelihood-ratio test. A nonconverged saturated fit is retained with a warning, rather than supplying a false exact G2.

Parameters:
Return type:

IncompleteGOF

Simulation and bootstrap with an explicit observation-mask model.

knowledgespaces.estimation.incomplete_bootstrap.simulate_blim_incomplete(structure, observed_mask, *, items=None, beta=0.1, eta=0.1, pi=None, seed=None, max_memory_bytes=512000000)[source]

Generate complete BLIM responses, then apply a fixed exogenous mask.

True indicates an observed cell. Rows remain individual, in mask order; columns follow items (sorted by default). This is a fixed-design/MCAR simulation, not a generic MAR missingness model. No values are imputed.

Parameters:
Return type:

IncompleteResponseMatrix

class knowledgespaces.estimation.incomplete_bootstrap.IncompleteBootstrap(estimate, observed_gof, g2_replicates, beta_replicates, eta_replicates, pi_replicates, converged_replicates, saturated_converged, iterations_replicates, saturated_iterations, n_replicates, n_failed, n_capped, p_value, seed, mask_model, n_restarts=None, init_strategy='uniform', init_range=(0.01, 0.4), restart_replicates=(), replicate_errors=(), statistic='G2', x2_observed=None, x2_replicates=None)[source]

All parameter/LR replicates, retaining failed and capped refits.

p_value uses add-one Monte Carlo counting only when the observed fits and EVERY replicate converge and yield finite LR. Otherwise it is NaN: failures are not silently dropped. A wholly missing generated sample is failed (NaN parameter rows); a cap is nonconverged. Arrays follow the observed estimate’s items/states. Standard deviations of parameter samples are descriptive, not automatically valid confidence intervals. With statistic=”X2”, the explicitly independent-mask Pearson statistic replaces G2 for calibration; saturated fitting is unnecessary and is skipped (observed_gof=None, G2 NaN, saturated iterations zero). Generic mask callbacks are disallowed for X2. descriptive_tail_fraction retains finite counting results separately from unresolved calibrated p-values.

Parameters:
property statistic_observed: float

Observed value of the statistic selected for calibration.

property statistic_replicates: ndarray

All values of the selected statistic, retaining failed rows.

property descriptive_tail_fraction: float

Finite add-one tail fraction, not calibrated inference for capped fits.

knowledgespaces.estimation.incomplete_bootstrap.bootstrap_blim_incomplete(structure, data, *, mask_model, n_replicates=1000, seed=None, constraints=None, n_restarts=None, init_strategy='uniform', init_range=(0.01, 0.4), statistic='G2', max_iter=5000, tol=1e-07, saturated_max_iter=10000, saturated_tol=1e-07, max_patterns=1048576, max_memory_bytes=512000000)[source]

Fit/simulate/refit BLIM and saturated model with a stated mask model.

‘fixed’ expands the original masks by integer respondent frequencies; masks must be exogenous (fixed design/MCAR), not generic MAR. A callback takes complete individual responses, item labels and the seeded generator and returns a boolean observed mask. Its statistical assumptions are the caller’s model; the function checks shape/type, not ignorability. The callback must not alter responses. Sample failures remain visible.

n_restarts=None uses one deterministic standard start for the observed sample and each replicate. A positive n_restarts instead uses estimate_blim_incomplete_restarts with the same initialization law, constraints, cap and tolerance for every fit, drawing from the single seeded generator. Restart records are retained for the observed fit and every replicate; all-start failures retain their errors and NaN rows. Changing this policy also changes the subsequent seeded simulations.

‘empirical_independent’ resamples whole masks with probabilities from their weighted empirical frequencies, independently of responses (MCAR). ‘fixed’ retains each stratum size; the empirical option resamples sizes. statistic=’X2’ calibrates independent_mask_pearson for these two explicit designs only. Generic mask callbacks may use G2 but cannot imply the independent-mask factorization needed by Pearson. X2 skips saturated fitting, so observed_gof is None and G2 arrays remain NaN.

Parameters:
Return type:

IncompleteBootstrap

Empirical independent observation masks and their explicitly scoped Pearson statistic.

knowledgespaces.estimation.observation_masks.sample_observation_masks(data, n_respondents, *, seed=None, max_memory_bytes=512000000)[source]

Sample entire observed masks independently from their weighted empirical law.

True means observed. Columns follow data.items; rows are newly sampled individuals. Counts weight masks even for aggregated response patterns; repeated masks pool their weights, zero-weight masks are never sampled. Fractional nonnegative source weights are allowed: these specify a discrete sampling distribution, not a fractional simulated sample size. Joint item missingness is retained; masks are not sampled cell by cell.

Applying these draws independently of complete responses models MCAR, not generic MAR. This function does not infer a missingness mechanism.

Parameters:
Return type:

ndarray

knowledgespaces.estimation.observation_masks.independent_mask_pearson(estimate, data, *, max_memory_bytes=512000000)[source]

Pearson X2 for an explicitly independent empirical-mask response model.

For each observed mask O, fit its mass as N_O/N. Joint cell probabilities are P(O) P_theta(R_O), giving X2 = sum_r n_r^2 / [N_O(r) P_theta(R_O)] - N. Sum over unique value-and-mask patterns; unseen cells are included by the normalization identity. This also equals the sum of conditional Pearson statistics across fixed exogenous mask strata, with sizes N_O.

This factorization requires independent masks (MCAR) or an exogenous fixed design. Ignorable MAR alone does NOT justify it. No automatic degrees of freedom or chi-squared reference distribution is supplied. All-missing strata contribute zero. Fractional weights produce only a descriptive statistic; respondent resampling requires integer counts. Impossible observed cells yield infinity. Columns align by item label.

Parameters:
Return type:

float

Simulation of response patterns from a BLIM.

Generates synthetic response data given a knowledge structure and BLIM parameters (state prior π, careless-error β, lucky-guess η). This is the counterpart of pks::simulate.blim() in R and the building block for parameter-recovery studies.

References:

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

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

knowledgespaces.estimation.simulate.simulate_blim(structure, n_respondents, *, beta=0.1, eta=0.1, pi=None, seed=None, return_states=False)[source]

Simulate response patterns from a BLIM.

Each simulated respondent is assigned a knowledge state drawn from pi, then answers every item independently: an item in the state is answered correctly with probability 1 - beta[q] (careless error beta[q]), an item outside the state is answered correctly with probability eta[q] (lucky guess). This is the data-generating process of the basic local independence model (Falmagne & Doignon 2011, ch. 11) and mirrors pks::simulate.blim().

Parameters:
structureKnowledgeStructure

The knowledge structure whose states generate the data.

n_respondentsint

Number of respondents to simulate.

betafloat or Mapping[str, float]

Careless-error probability, scalar (homogeneous) or per-item. Values must be in [0, 1). Default 0.1.

etafloat or Mapping[str, float]

Lucky-guess probability, scalar or per-item, in [0, 1). Default 0.1. The informative-item condition beta + eta < 1 is not enforced: simulating degenerate items is a legitimate use (e.g., to exercise degenerate-item diagnostics).

piMapping[frozenset[str], float] or np.ndarray or None

State prior. Either a mapping from state to probability, or an array in the canonical state order (states sorted by size, then lexicographically — the same order used by estimate_blim()), or None for the uniform prior. Must be non-negative and sum to 1 (tolerance 1e-8).

seedint, np.random.Generator, or None

Seed or generator for reproducibility.

return_statesbool

If False (default), return an aggregated ResponseMatrix (unique patterns with counts). If True, return the tuple (data, states) where data has one non-aggregated row per respondent and states[r] is respondent r’s true knowledge state — the form needed for recovery and assessment simulations.

Returns:
ResponseMatrix or (ResponseMatrix, list[frozenset[str]])

Simulated response data; with return_states=True, also the true state of each respondent (row-aligned).

Raises:
ValueError

If n_respondents < 1, parameters are out of range, or pi does not match the structure’s states.

Parameters:
Return type:

ResponseMatrix | tuple[ResponseMatrix, list[frozenset[str]]]

Examples

>>> from knowledgespaces import space_from_prerequisites
>>> s = space_from_prerequisites(["a", "b"], [("a", "b")])
>>> data = simulate_blim(s, 500, beta=0.1, eta=0.05, seed=42)
>>> int(data.effective_counts.sum())
500

Random quasi orders and canonical BLIM simulations on their downsets.

The Bernoulli-pair/closure scheme is described by DAKS::simu (2.1-3). It does not sample uniformly from quasi orders or control final density.

knowledgespaces.estimation.quasiordinal.random_surmise_relation(items, delta, *, seed=None, max_pairs=1000000)[source]

Draw nonreflexive directed pairs independently, then close transitively.

Every ordered pair of distinct items has inclusion probability delta before closure; reflexivity is implicit. Draws follow lexicographic item order, row by row, omitting the diagonal. (a,b) means a is required for b. Cycles produce equivalent items and are retained. delta must be finite in [0,1]; 0 gives the identity, 1 the universal relation.

This is the DAKS documented sampling scheme, not a uniform law on quasi orders. Transitive closure changes marginal edge probabilities. max_pairs bounds the number of candidate nonreflexive pairs before allocation; closure also has potentially substantial time cost.

Parameters:
Return type:

SurmiseRelation

class knowledgespaces.estimation.quasiordinal.QuasiOrdinalSimulation(data, relation, structure, true_states)[source]

Generated relation, all compatible states, and respondent-aligned data.

data has one row per respondent, with no aggregation; true_states[r] identifies respondent r’s latent state. The structure is quasi-ordinal but need not be a learning space when items are equivalent.

Parameters:
knowledgespaces.estimation.quasiordinal.simulate_quasiordinal(items, n_respondents, *, relation=None, delta=None, beta=0.1, eta=0.1, pi=None, seed=None, max_pairs=1000000, max_states=100000, max_memory_bytes=512000000)[source]

Simulate a BLIM on a supplied or randomly generated quasi order.

Supply exactly one of relation (already transitive, matching items) or delta (passed to random_surmise_relation). All downsets are enumerated with a max_states bound. State masses are uniform by default; pi and beta/eta follow simulate_blim, including heterogeneous error rates. Output items are sorted; pi arrays follow canonical structure order.

Response probabilities are P(1|mastered)=1-beta and P(1|unmastered)=eta. DAKS 2.1-3’s sequential response mutation instead makes its effective slip ce*(1-lg); that implementation discrepancy is not reproduced. A shared local generator drives relation, latent-state and response draws. The memory preflight estimates numerical working arrays; Python set/object overhead is additionally controlled by max_pairs/max_states.

Parameters:
Return type:

QuasiOrdinalSimulation

Local identifiability diagnostics for the BLIM.

The BLIM is not identifiable in general: structurally different parameter vectors (β, η, π) can induce the same distribution over response patterns. Stefanutti, Heller, Anselmi and Robusto (2012) characterize local identifiability through the rank of the Jacobian of the prediction map θ ↦ (P(R))_R. Full column rank is a sufficient regular local criterion; a deficient rank at an isolated singular point is not its converse. The unrestricted parameter count is 2|Q| + |K| - 1. Forward- and backward-gradedness of the structure (Spoto, Stefanutti & Vidotto 2013) are simple structural conditions that already imply trade-off dimensions (η_q ↔ π for forward-graded items, β_q ↔ π for backward-graded items).

These diagnostics are the counterpart of pks::jacobian(), pks::is.forward.graded() and pks::is.backward.graded() in R. Because the rank at a single point only lower-bounds the generic rank of the model, check_identifiability() evaluates the Jacobian at several interior points (a user-suppliable point plus deterministic random draws) and reports the maximum rank; see its docstring for the one-sided logic of the resulting verdict.

References:

Stefanutti, L., Heller, J., Anselmi, P., & Robusto, E. (2012). Assessing the local identifiability of probabilistic knowledge structures. Behavior Research Methods, 44(4), 1197-1211.

Spoto, A., Stefanutti, L., & Vidotto, G. (2013). Considerations about the identification of forward- and backward-graded knowledge structures. Journal of Mathematical Psychology, 57(5), 249-254.

Heller, J. (2017). Identifiability in probabilistic knowledge structures. Journal of Mathematical Psychology, 77, 46-57.

knowledgespaces.estimation.identifiability.is_forward_graded(structure, item)[source]

Whether the structure is forward-graded in item.

A knowledge structure 𝒦 is forward-graded in item q iff {K ∪ {q} : K ∈ 𝒦} ⊆ 𝒦 (Spoto, Stefanutti & Vidotto 2013). Forward-gradedness in q implies a trade-off between eta[q] and the state probabilities, hence local non-identifiability of the BLIM.

Mirrors pks::is.forward.graded().

Parameters:
Return type:

bool

knowledgespaces.estimation.identifiability.is_backward_graded(structure, item)[source]

Whether the structure is backward-graded in item.

A knowledge structure 𝒦 is backward-graded in item q iff {K \ {q} : K ∈ 𝒦} ⊆ 𝒦 (Spoto, Stefanutti & Vidotto 2013). Backward-gradedness in q implies a trade-off between beta[q] and the state probabilities, hence local non-identifiability of the BLIM.

Mirrors pks::is.backward.graded().

Parameters:
Return type:

bool

knowledgespaces.estimation.identifiability.blim_jacobian(structure, *, beta=0.1, eta=0.1, pi=None, constraints=None, max_items=None, max_memory_bytes=8000000000)[source]

Analytic Jacobian of the BLIM prediction map at (β, η, π).

The prediction map sends the parameter vector θ = (β_1..β_Q, η_1..η_Q, π_2..π_|K|) (the probability of the first canonical state is 1 - Σ of the others) to the vector of response pattern probabilities (P(R))_R over all 2^Q patterns. The BLIM has regular local identifiability at θ when this Jacobian has full column rank npar = 2|Q| + |K| - 1 (Stefanutti, Heller, Anselmi & Robusto 2012). A deficient rank at a singular point alone does not establish generic non-identifiability.

Parameters:
structureKnowledgeStructure

The knowledge structure.

beta, etafloat, Mapping[str, float], or np.ndarray

Evaluation point, scalar, per-item mapping, or array aligned with the sorted domain. Without constraints, values must lie in (0, 1). With constraints, boundary values in [0, 1] are accepted for evaluating the restricted polynomial map. Default 0.1 for both.

piMapping, np.ndarray, or None

State probabilities at the evaluation point (canonical state order: by size, then lexicographically). None (default) uses the uniform prior.

constraintsBLIMConstraints or None

Restrict the evaluation point and Jacobian to the declared model. Fixed values override inputs; free equality groups use their mean. Columns are free beta groups, free eta groups (ordered by first item in the sorted domain), then free pi coordinates. Fixed pi removes all pi columns. Boundary derivatives use products excluding the differentiated item, avoiding division by zero.

max_itemsint or None

Override for the 2^Q pattern-enumeration guard.

max_memory_bytesint

Hard cap on the estimated peak allocation, which scales with 2^Q × |K| (several concurrent float64 arrays of that shape). A MemoryError is raised before any large array is allocated; a ResourceWarning is emitted above 1 GB. Default 8 GB.

Returns:
np.ndarray

Jacobian of shape (2^Q, npar), rows in lexicographic pattern order, columns ordered (beta_q)_q, (eta_q)_q, (pi_k)_{k>=2}. Its rank equals the rank of the (2^Q - 1)-row version used by pks::jacobian(): every column sums to zero across patterns (Σ_R P(R) = 1), so dropping one row does not change the rank. Note that the rank at a single point only lower-bounds the generic rank of the model (matrix rank is lower semicontinuous); check_identifiability() therefore evaluates several points.

Raises:
ValueError

On invalid parameters.

DomainTooLargeError

If |Q| exceeds the enumeration guard.

MemoryError

If the estimated peak allocation exceeds max_memory_bytes.

Parameters:
Return type:

ndarray

class knowledgespaces.estimation.identifiability.IdentifiabilityReport(rank, npar, locally_identifiable, rank_deficiency, forward_graded, backward_graded, smallest_singular_value=0.0, rank_tolerance=0.0)[source]

Result of a BLIM local-identifiability check.

Attributes:
rankint

Numerical rank of the Jacobian of the prediction map, maximized over the evaluated interior points (the user-supplied point plus the random points drawn by check_identifiability()). Since the rank at any point lower-bounds the generic rank, this is the best available lower bound on the generic rank.

nparint

Number of free parameters after fixed/equality constraints; 2|Q| + |K| - 1 for the unrestricted model.

locally_identifiablebool

True iff rank == npar. Full rank at any interior point establishes generic local identifiability, up to the numerical rank decision (see smallest_singular_value and rank_tolerance for its margin); generic statements are silent on null sets such as the boundary, so for boundary solutions re-evaluate at the fitted point. When False, no evaluated point had full rank — strong evidence (not proof) that npar - rank independent trade-off dimensions exist generically: parameter estimates are not unique and only likelihood-based quantities (log-likelihood, G², AIC, BIC, predicted pattern probabilities) are comparable across implementations or fits.

rank_deficiencyint

npar - rank.

forward_gradedtuple[str, …]

Items in which the structure is forward-graded (η ↔ π trade-off).

backward_gradedtuple[str, …]

Items in which the structure is backward-graded (β ↔ π trade-off).

smallest_singular_valuefloat

The npar-th singular value of the Jacobian at the point that achieved the reported rank. When the rank is full, this is the smallest counted singular value (compare with rank_tolerance); when deficient, it is the smallest column-direction singular value, not necessarily the first discarded one. It is zero if there are fewer singular values than parameters.

rank_tolerancefloat

The singular-value cutoff used for the rank decision at that point (NumPy convention: max(J.shape) * eps * sigma_max). The numerical rank is the number of singular values above it.

Parameters:
  • rank (int)

  • npar (int)

  • locally_identifiable (bool)

  • rank_deficiency (int)

  • forward_graded (tuple[str, ...])

  • backward_graded (tuple[str, ...])

  • smallest_singular_value (float)

  • rank_tolerance (float)

knowledgespaces.estimation.identifiability.check_identifiability(structure, *, beta=0.1, eta=0.1, pi=None, constraints=None, n_points=5, seed=0, max_items=None, max_memory_bytes=8000000000)[source]

Check local identifiability of the BLIM on a knowledge structure.

Computes the rank of the analytic Jacobian of the prediction map at the given evaluation point (default: β = η = 0.1, uniform π) and, if that rank is deficient, at up to n_points additional random interior points (β_q, η_q ~ U(0.05, 0.3) independently per item, π ~ Dirichlet(1, …, 1)) drawn from a fixed-seed generator so results are deterministic. The reported rank is the maximum over the evaluated points, and the model is declared locally identifiable iff that rank equals npar = 2|Q| + |K| - 1 (Stefanutti, Heller, Anselmi & Robusto 2012).

Parameters:
structureKnowledgeStructure

The knowledge structure.

beta, etafloat, Mapping[str, float], or np.ndarray

User-suppliable evaluation point, e.g. fitted estimates: check_identifiability(s, beta=est.beta_dict(), eta=est.eta_dict(), pi=est.pi_dict(), n_points=0). Dictionaries preserve item alignment even if estimation used an unsorted column order; n_points=0 evaluates the fitted point alone. Default 0.1 for both.

piMapping, np.ndarray, or None

State probabilities at the evaluation point. None (default) uses the uniform prior.

n_pointsint

Number of additional random interior points at which the Jacobian is evaluated when the user-supplied point is rank deficient (evaluation stops early once full rank is found). 0 restores the single-point check. Default 5.

seedint

Seed of the internal random generator that draws the extra points; fixed by default so that repeated calls give identical reports. Default 0.

constraintsBLIMConstraints or None

Restrict the evaluation point and Jacobian to the declared model. Fixed values override inputs; free equality groups use their mean. Columns are free beta groups, free eta groups (ordered by first item in the sorted domain), then free pi coordinates. Fixed pi removes all pi columns. Boundary derivatives use products excluding the differentiated item, avoiding division by zero.

max_itemsint or None

Override for the 2^Q pattern-enumeration guard.

max_memory_bytesint

Hard cap on the estimated peak allocation per Jacobian; see blim_jacobian(). Default 8 GB.

Parameters:
Return type:

IdentifiabilityReport

Notes

The rank of the Jacobian is constant — equal to the generic rank — on an open dense subset of the parameter space, and can only drop on the complementary null set (matrix rank is lower semicontinuous). The mathematical criterion is one-sided: exact full rank at any interior point establishes generic local identifiability. This implementation uses a floating-point singular-value threshold, so inspect the reported margin and tolerance. A deficient rank at all evaluated points is strong evidence, not proof, of generic rank deficiency. Evaluating only one point is not enough: symmetric structures can place the symmetric default point β = η = 0.1 with uniform π inside the exceptional null set (e.g. the knowledge space {∅, {a, b}, {c, d}, Q} has generic rank 11 = npar but rank 7 at the default point), which is why the random points are evaluated.

Examples

>>> from knowledgespaces import space_from_prerequisites
>>> s = space_from_prerequisites(["a", "b"], [("a", "b")])
>>> report = check_identifiability(s)
>>> report.npar
6

Parametric bootstrap goodness-of-fit for the BLIM.

The chi-squared reference for the likelihood-ratio statistic G2 is asymptotic and can be unreliable on sparse response tables (Koehler & Larntz, 1980); sparsity alone is not a universal failure criterion. The parametric bootstrap calibrates the test on the estimated model instead: simulate n_replicates data sets of the observed size from the fitted parameters, refit each with the same estimator settings, and locate the observed G2 within the bootstrap distribution of replicated G2 values (Efron & Tibshirani, 1993). The Monte Carlo p-value uses the add-one convention (1 + #{G2* >= G2_obs}) / (n_replicates + 1) (Davison & Hinkley, 1997, Eq. 4.12), so it is never exactly zero.

The computation is sequential by design — one process, one core; on the data sets of the accompanying article a n_replicates=2000 run takes minutes. Refits that stop at the iteration cap are counted in n_capped and reported with a ConvergenceWarning: their log-likelihood may be below the attainable maximum, potentially inflating replicated G2. Convergence also does not guarantee a global maximum. Parameter replicates retain their item/state alignment and convergence status. Their dispersion describes the fitted resampling procedure; it does not resolve non-identifiability or guarantee confidence coverage.

References:

Davison, A. C., & Hinkley, D. V. (1997). Bootstrap Methods and their Application. Cambridge University Press. Efron, B., & Tibshirani, R. J. (1993). An Introduction to the Bootstrap. Chapman & Hall. Koehler, K. J., & Larntz, K. (1980). An empirical investigation of goodness-of-fit statistics for sparse multinomials. JASA, 75.

class knowledgespaces.estimation.bootstrap.BootstrapParameterSummary(items, states, n_used, n_capped, beta_mean, beta_sd, eta_mean, eta_sd, pi_mean, pi_sd)[source]

Means and sample standard deviations of parameter replicates.

Arrays follow items and states and are read-only. n_used records the selected replicates, with n_capped unconverged refits among them. Standard deviations use ddof=1 and are NaN with fewer than two selected replicates; means are NaN with none. These are empirical bootstrap dispersions, not automatic confidence intervals or evidence of identifiable parameters. Selection of converged fits can itself change the resampling distribution.

Parameters:
class knowledgespaces.estimation.bootstrap.BootstrapGOF(p_value, g2_observed, g2_replicates, n_replicates, n_extreme, n_capped, method, seed, estimate, beta_replicates=<factory>, eta_replicates=<factory>, pi_replicates=<factory>, converged_replicates=<factory>, iterations_replicates=<factory>, *, statistic='G2', x2_observed=None, x2_replicates=<factory>, n_failed=0, replicate_errors=(), n_restarts=None, init_strategy='uniform', init_range=(0.01, 0.4))[source]

Result of a parametric bootstrap of G2 or complete-table Pearson X2.

Attributes:
p_valuefloat

Monte Carlo p-value (1 + n_extreme) / (n_replicates + 1) when observed/replicate fits converge and all selected statistics are finite; NaN otherwise. descriptive_tail_fraction retains the finite counting result for inspection even with capped fits.

g2_observedfloat

G2 of the fit to the observed data.

g2_replicatesnp.ndarray

The n_replicates bootstrap G2 values.

n_replicatesint

Number of bootstrap replicates.

n_extremeint

Replicates at least as extreme as the selected observed statistic.

n_cappedint

Replicate refits stopped by the iteration cap (not converged); nonzero counts are also reported via ConvergenceWarning.

methodstr

Estimation method used for the observed fit and every refit.

seedint | None

Seed of the replicate generator (None = nondeterministic).

estimateBLIMEstimate

The fit to the observed data that generated the replicates.

beta_replicates, eta_replicates, pi_replicatesnp.ndarray

Rows correspond to g2_replicates; columns match estimate.items and estimate.states. No unconverged refit is silently removed.

converged_replicates, iterations_replicatesnp.ndarray

Convergence status and iteration counts of each refit. All replicate arrays returned by bootstrap_gof() are read-only snapshots.

statistic{“G2”, “X2”}

The selected calibration statistic. statistic_observed and statistic_replicates expose it consistently. G2 fields remain G2 even when Pearson X2 determines the p-value. X2 fields are only populated when requested. n_failed/replicate_errors retain failed refits with NaN parameter/statistic rows, distinct from capped runs.

Parameters:
property statistic_observed: float

Observed value of the statistic selected for calibration.

property statistic_replicates: ndarray

All replicate values of the selected statistic, including NaN failures.

property descriptive_tail_fraction: float

Add-one tail fraction for all finite statistics, even capped fits.

This is descriptive when p_value is NaN; it is not calibrated inference. Missing/nonfinite statistics give NaN, never deletion.

parameter_summary(*, converged_only=False)[source]

Summarize all refits, or explicitly select only converged refits.

Parameters:

converged_only (bool)

Return type:

BootstrapParameterSummary

knowledgespaces.estimation.bootstrap.bootstrap_gof(structure, data, *, n_replicates=1000, method='ML', max_iter=500, tol=1e-06, seed=None, estimate=None, constraints=None, discrepancy=None, statistic='G2', n_restarts=None, init_strategy='uniform', init_range=(0.01, 0.4), max_patterns=1048576, max_memory_bytes=512000000)[source]

Parametric bootstrap of G2 or Pearson X2 and BLIM parameter estimates.

Parameters:
structureKnowledgeStructure

The knowledge structure under test.

dataResponseMatrix

Observed response patterns with integer frequencies. Fractional analysis weights cannot be resampled as counts of respondents.

n_replicatesint

Number of bootstrap replicates. Default 1000; the p-value resolution is 1 / (n_replicates + 1).

method{“ML”, “MD”, “MDML”}

Estimator for the observed fit and every replicate refit.

max_iter, tol

Estimator settings, applied identically to the observed fit (unless estimate is supplied) and to every refit.

seedint | None

Seed for the replicate generator. Fixing it makes the entire bootstrap reproducible.

estimateBLIMEstimate | None

A previously computed fit of structure to data. When given, the observed fit is not recomputed; its method must match method, since the refits must use the same estimator as the fit that produced the observed G2. State and item alignment, probabilities, and likelihood/G2 on the supplied observations are checked before any bootstrap simulation. SLM estimates require bootstrap_slm(), which preserves their restricted state distribution during refitting.

constraintsBLIMConstraints or None

Restrictions for the observed fit and every refit. If an estimate is supplied, None inherits its constraints; an explicit value must match. Simulating from a constrained fit and refitting an unrestricted model would calibrate a different procedure.

discrepancyMDOptions or None

Radius or inclusion weights for the observed fit and every refit. None inherits a supplied estimate’s options; explicit options must match. Applies to MD/MDML, with hyperbolic weights limited to MD.

statistic{“G2”, “X2”}

Default G2. X2 sums squared Pearson residuals over the complete response table, including unobserved cells. This does not assert an asymptotic chi-squared reference distribution.

n_restarts, init_strategy, init_range

None retains deterministic standard starts. A positive integer uses estimate_blim_restarts with the same settings for observed/refit samples, from one seeded stream. Only ML/MDML support this search. Omit a supplied estimate to ensure an identical observed policy.

max_patterns, max_memory_bytes

Bounds for Pearson full-table diagnostics and retained replicates; max_memory_bytes is also forwarded to estimation. Estimates of array allocations, not a hard process-memory cap.

Returns:
BootstrapGOF
Warns:
ConvergenceWarning

When calibration is unresolved: capped/failed fits or nonfinite selected statistics. All rows remain present and p_value is NaN.

Parameters:
Return type:

BootstrapGOF

Fixed and equality-constrained BLIM parameterizations.

The constrained M-step pools Bernoulli sufficient statistics within each free equality class. Fixed classes do not contribute free parameters. This is a direct maximization of the EM auxiliary function; see also pks::blim’s betafix/etafix and betaequal/etaequal interfaces.

class knowledgespaces.estimation.constraints.BLIMConstraints(beta_fixed=<factory>, eta_fixed=<factory>, beta_equal=(), eta_equal=(), pi_fixed=None)[source]

Named restrictions shared by estimation, bootstrap and Jacobian.

beta_fixed and eta_fixed map item labels to fixed probabilities in [0, 1). beta_equal and eta_equal contain groups of item labels constrained to share a probability. Overlapping groups merge transitively. Fixing any member fixes its entire equality class; conflicting fixed values in that class are rejected.

pi_fixed, when supplied, fixes the complete state distribution; its keys must match all states, including zero-probability ones. Unspecified item parameters and, by default, pi remain free. The beta + eta informative-item inequality is a separate restriction and is not imposed. Inputs are immutable snapshots and pickleable.

Parameters:

BLIM prediction on specified response patterns, including exact zeros.

class knowledgespaces.estimation.prediction.BLIMPrediction(items, states, probabilities, log_probabilities, posteriors, method='ML', discrepancy_assignments=None)[source]

Predictions in input row order and canonical state order.

probabilities and log_probabilities describe P(R). posteriors[r, k] is P(K_k|R_r). A model-impossible pattern has probability zero, log probability -inf, and an all-NaN posterior: conditioning on a zero-probability event is undefined. These Bayesian quantities are unchanged by the assignment method. state_probabilities gives the selected ML/MD/MDML assignments; only ML is the ordinary BLIM posterior. MD ignores priors and error magnitudes, apart from declared fixed-zero error exclusions. MDML renormalizes the joint probability on the included states. Arrays are read-only snapshots. Use classify explicitly to select one state per row; undefined assignments produce None, not the empty set.

Parameters:
property state_probabilities: ndarray

Row-normalized assignments for the requested prediction method.

classify(*, ties='min', seed=None)[source]

Select modal states, with exact computed-probability ties.

min/max select smallest/largest cardinality among modal states, then the first in states order (canonical for the prediction functions). random samples uniformly among all modal states, using a local NumPy generator; no R/NumPy random stream identity is implied. Singleton maxima do not consume draws. Each input row receives one selection, irrespective of frequency. An all-NaN assignment row returns None.

Parameters:
Return type:

tuple[frozenset[str] | None, …]

knowledgespaces.estimation.prediction.predict_blim(structure, patterns, *, items=None, beta=0.1, eta=0.1, pi=None, method='ML', discrepancy=None, inclusion=None, constraints=None, max_memory_bytes=512000000)[source]

Predict BLIM response probabilities and state posteriors.

patterns is a binary row-by-item matrix. items defaults to the sorted domain and determines the column order, including that of array-valued beta/eta. Mappings align by labels. State arrays use canonical order (size, then lexicographic). Responses are conditionally independent given a fixed knowledge state.

Accepts the closed probability box [0, 1], including deterministic limits and uninformative items. The informative-item inequality beta + eta < 1 is not imposed by this probability calculation. No clipping of zero probabilities or imputation of missing values. The memory preflight is an allocation estimate, not an OS-level cap.

method selects state assignments (default ML, independently of how parameters were fitted). MD normalizes discrepancy weights; MDML normalizes the joint BLIM probabilities restricted to included states. discrepancy supplies the excess Hamming radius, or hyperbolic weights for MD only. Alternatively, inclusion is an explicit binary row-by-canonical-state mask (pks i.RK), mutually exclusive with discrepancy. An empty selection has undefined (all-NaN) assignments. Bayesian P(R) and posteriors always retain their original full-model meaning.

Declared constraints must match supplied parameters. Fixed-zero beta/eta (including equality groups) exclude incompatible state-response pairs from computed discrepancy assignments, as in pks. Undeclared zero values do not change the MD distance rule. A supplied inclusion mask is used as-is, independently of the distance rule and those exclusions; MDML still respects zero joint probabilities. Zero prior mass does not exclude a state from MD. No data-dependent fitting is performed here.

Parameters:
  • structure (KnowledgeStructure)

  • patterns (np.ndarray)

  • items (list[str] | None)

  • beta (ItemParameter)

  • eta (ItemParameter)

  • pi (StateParameter)

  • method (Literal['ML', 'MD', 'MDML'])

  • discrepancy (MDOptions | None)

  • inclusion (np.ndarray | None)

  • constraints (BLIMConstraints | None)

  • max_memory_bytes (int)

Return type:

BLIMPrediction

BLIM cell diagnostics over the complete multinomial response table.

class knowledgespaces.estimation.diagnostics.BLIMResiduals(items, patterns, observed, expected, log_probabilities, pearson, deviance)[source]

Complete response table and signed cell residuals.

Patterns use lexicographic binary order with columns in items (the supplied data’s order). Duplicate rows are pooled and all absent patterns receive observed count zero. Arrays are read-only snapshots. Pearson residuals are (O-E)/sqrt(E); deviance residuals are sign(O-E)*sqrt(2*(O*log(O/E)-O+E)), taking 0*log(0/E)=0. A cell with O=E=0 contributes zero to both diagnostics; O>0 with model probability exactly zero contributes +inf. Log probabilities distinguish impossible cells from floating-point underflow.

The sums of squares give G2 and X2 on the full table, independent of degrees-of-freedom conventions. They do not establish a chi-square reference distribution, identify causes of misfit, or account for testing many individual cells. Residuals can also assess held-out data.

Parameters:
property G2: float

Sum of squared deviance residuals.

property X2: float

Sum of squared Pearson residuals.

knowledgespaces.estimation.diagnostics.blim_residuals(estimate, data, *, chunk_size=1024, max_patterns=1048576, max_memory_bytes=512000000)[source]

Evaluate Pearson and deviance residuals, including unobserved cells.

No refit is performed; parameters align by labels, allowing held-out data and reordered columns. Counts may be fractional analysis weights; such weights do not automatically admit multinomial inference. Enumeration is exponential in the number of items. Chunking limits prediction temporaries but the returned complete table remains in memory. The memory preflight estimates allocations, not process RSS.

Parameters:
Return type:

BLIMResiduals

Scientific BLIM/SLM summaries with explicit data and sample conventions.

class knowledgespaces.estimation.reporting.MinimumDiscrepancy(items, distances, counts, total_weight, infeasible_weight, mean, training_data)[source]

Hamming minima over states feasible under declared fixed-zero errors.

distances retains input row order; infeasible rows have +inf. counts[d] is the supplied weight at finite distance d (0..|Q|). infeasible_weight is separate, so no observations silently vanish. mean is +inf if any positive-weight row is infeasible. This is a distance distribution, not a BLIM likelihood or state-posterior summary. training_data compares labeled empirical measures, not participant IDs; None means the fit has no training fingerprint. Arrays are read-only.

Parameters:
class knowledgespaces.estimation.reporting.Coefficient(family, value, items=(), state=None, fixed=False, derived=False)[source]

One labeled reported coefficient; equality groups appear only once.

Pi rows contain actual state masses (not free simplex coordinates). In SLM they are derived=True from g, rather than freely estimated. Fixed error groups use the merged restrictions, including propagation of a fixed value through overlapping equality groups.

Parameters:
class knowledgespaces.estimation.reporting.BLIMReport(model, method, items, states, converged, n_iterations, n_parameters, coefficients, constraints, mastery, expected_slips, expected_guesses, training_log_likelihood, training_gof, AIC, BIC, BIC_npatterns, AICc, aicc_sample_size, aicc_sample_convention, aicc_unavailable_reason, discrepancy, tradeoffs)[source]

Immutable numerical report with separate training and supplied-data roles.

Mastery is sum_K pi[K] 1(q in K). Expected slips and guesses are, respectively, beta[q]*mastery[q] and eta[q]*(1-mastery[q]): marginal probabilities of error for a new respondent taking the entire test. Their sums are expected error counts per complete test, not fitted errors conditional on observed responses or missingness masks.

AIC/BIC and training_gof belong to the stored fit, irrespective of the data supplied for discrepancy. The nominal parameter count is not an identified dimension. AICc is a conventional heuristic, with no general validity guarantee for singular/boundary BLIMs or for MD fits. For missing data, no automatic GOF or discrepancy is supplied.

Parameters:
property expected_total_errors: float

Expected number of slips plus guesses per new complete test.

to_dict()[source]

Return a detached JSON-ready export, with labels and interpretation.

Nonfinite numbers become None (JSON null), with infeasible weight and AICc’s unavailable reason retained. state is a sorted label list, never a lossy string representation. No file is written.

Return type:

dict

knowledgespaces.estimation.reporting.aicc(log_likelihood, n_parameters, n_observations)[source]

Conventional AIC + 2*k*(k+1)/(N-k-1), requiring an explicit N.

Returns NaN when N <= k+1: the correction is undefined there, rather than a negative reward. N must be finite and positive, k a nonnegative integer, and log likelihood finite. The MATLAB KST-toolbox uses total respondent count for N and negative log likelihood internally. This function takes the ordinary log likelihood. It is an algebraic criterion, not a general small-sample bias correction for singular BLIM models. Fractional N is accepted as an explicitly chosen descriptive convention; it does not turn survey/analysis weights into independent observations.

Parameters:
  • log_likelihood (float)

  • n_parameters (int)

  • n_observations (float)

Return type:

float

knowledgespaces.estimation.reporting.minimum_discrepancy(estimate, data, *, chunk_size=1024, max_memory_bytes=512000000)[source]

Weighted minimum Hamming distances on explicitly complete responses.

Matches the defining distance in pks getMD: declared fixed-zero slip or guess groups exclude impossible state/response pairs before minimizing. An estimated numerical zero or zero prior mass does not exclude a state. The excess inclusion radius and hyperbolic MD weights do not change the minimum itself. No powerset of responses is enumerated; inputs may be compressed frequencies or uncompressed observations, including zero rows.

Parameters:
Return type:

MinimumDiscrepancy

knowledgespaces.estimation.reporting.blim_report(estimate, data=None, *, aicc_sample_size=None, include_tradeoffs=False, max_memory_bytes=512000000)[source]

Summarize fitted BLIM/SLM coefficients, errors, mastery and diagnostics.

Optional complete data supplies only the distance distribution. Stored GOF/criteria remain training quantities. AICc automatically uses N only for fingerprint-matched training data with integer frequencies. For an incomplete fit, the report cannot verify whether stored total weights represent respondent counts, so supply aicc_sample_size explicitly. Held-out N is never substituted. The explicit override is a user-selected training N convention.

Trade-offs are opt-in because they enumerate all complete patterns. They describe the full-response BLIM derivative, also when the estimate used missing data. For SLM use estimate.jacobian() instead: its state masses are derived from g, so requesting BLIM trade-offs raises.

Parameters:
Return type:

BLIMReport

Numerical BLIM trade-offs by parameter family (Stefanutti et al., 2012).

Independent linear-algebra implementation: singular-value decompositions, not a port of the R/MATLAB row-reduction code. Null-space bases are not unique, so compare their spans, ranks and prediction derivatives rather than coefficients of a particular software’s basis.

class knowledgespaces.estimation.tradeoffs.BLIMParameter(family, items=(), state=None)[source]

One free Jacobian coordinate, including equality groups.

family is beta, eta, or pi. items labels an error parameter’s equality class. For pi, state labels the coordinate; the mass of the first canonical state is one minus the other masses. Fixed parameters have no coordinate. Equality groups appear once.

Parameters:
class knowledgespaces.estimation.tradeoffs.JacobianBlock(families, columns, rank, rank_tolerance, singular_values, null_space)[source]

Rank and orthonormal null-space basis for selected families.

columns indexes the complete report’s parameter list and Jacobian. null_space has one row per selected free coordinate and one column per null direction. It includes dependencies within a family as well as dependencies across families. Arrays are read-only. Multiplying the selected Jacobian by this basis gives numerical zero, within the reported absolute singular-value cutoff rank_tolerance.

Parameters:
property nullity: int

Number of numerically null parameter directions.

class knowledgespaces.estimation.tradeoffs.BLIMTradeoffs(items, states, beta, eta, pi, parameters, jacobian, blocks)[source]

Pointwise diagnostics for all seven beta/eta/pi block combinations.

beta, eta and pi record the actual evaluation point, after constraints, in sorted item and canonical state order. jacobian uses lexicographic binary response order. Arrays are read-only. Deficiency at one singular point is not proof of generic or global non-identifiability. At boundaries, null directions describe the polynomial derivative and need not be feasible probability directions. Use check_identifiability for multiple-point rank screening.

Parameters:
block(*families)[source]

Select a nonempty subset of beta, eta, pi, in any argument order.

Parameters:

families (str)

Return type:

JacobianBlock

knowledgespaces.estimation.tradeoffs.blim_tradeoffs(structure, *, beta=0.1, eta=0.1, pi=None, constraints=None, rank_tolerance=None, max_items=None, max_memory_bytes=512000000)[source]

Inspect ranks and null directions at one specified BLIM parameter point.

Uses the same constraints and coordinate convention as blim_jacobian. rank_tolerance is an optional nonnegative absolute SVD cutoff, shared by all blocks. By default each block uses NumPy’s convention max(shape)*eps*largest_singular_value. Reported bases describe linear dependencies, not unique parameter remedies or confidence intervals.

Every block is evaluated even if the full Jacobian has full rank. The complete beta/eta/pi block handles dependencies involving three families without assuming that pairwise ranks characterize them. Extra memory for seven bases and SVD work is guarded by a conservative allocation estimate; BLAS workspace and process RSS are not bounded.

Parameters:
Return type:

BLIMTradeoffs

knowledgespaces.datasets

Classic Knowledge Space Theory datasets.

Ships the standard examples used across the KST software literature so that analyses are directly comparable with published results (e.g., the R packages pks and DAKS). Structure/frequency loaders return a KSTDataset; the individual probability loader retains source case identifiers, paired responses, metadata and provenance.

Currently included:

  • load_pisa() — 340 original DAKS PISA response rows, or aggregated frequencies, without an assumed ground-truth knowledge structure.

  • load_probability_individual() — all 504 retained source cases, with paired waves, raw answers, original scores and codebook.

  • load_doignon_falmagne_7() — the canonical five-item fictitious example of Doignon & Falmagne (1999, chapter 7).

  • load_probability() — aggregated pre/post responses of the 345 completers in Anselmi & Wickelmaier’s probability study, with the original GPL-2.0-or-later data terms preserved separately from the code.

class knowledgespaces.datasets.KSTDataset(name, description, source, structure, data)[source]

A packaged KST dataset with provenance.

Attributes:
namestr

Short identifier (matches the loader name).

descriptionstr

What the data are and how they were collected/constructed.

sourcestr

Full bibliographic source of the data.

structureKnowledgeStructure

The knowledge structure associated with the data in the source.

dataResponseMatrix

Observed response-pattern frequencies, items in sorted order.

Parameters:
class knowledgespaces.datasets.ProbabilityDataset(name, description, source, structure, data, skill_map, wave, model, source_license='GPL-2.0-or-later')[source]

Aggregated probability data with a delineating skill function.

wave identifies pre/post instruction; model selects K1 (sf1, conjunctive) or K2 (sf2, alternative competencies). skill_map exposes the original item-to-skill mapping. Both waves describe the same 345 completers out of 504 source cases, but aggregate frequencies do not retain respondent pairing, treatment groups, or missing cases. They cannot support longitudinal individual-level or causal analyses. Data and skill-function CSVs retain GPL-2.0-or-later terms from pks; see the packaged data/probability/README.md and COPYING files.

Parameters:
class knowledgespaces.datasets.ProbabilityIndividualDataset(case_ids, pre, post, records, sample, mode, condition, source='Anselmi & Wickelmaier, Tuebingen 2010; probability in pks 0.7-0.', source_license='GPL-2.0-or-later')[source]

Row-aligned pre/post source scores, metadata and raw numeric responses.

case_ids retain the original pseudonymous case key; no pairing is inferred from frequency tables. records contains all 68 source columns as numeric values, strings/factor labels, ISO UTC timestamps or None. Source p values are numeric answers; source b values are the original correctness coding. schema() and source_documentation() expose types, levels and the original item wording/codebook. The source dataset and resources are GPL >= 2.

Parameters:
raw_answers(wave)[source]

Numeric p responses, including original NaN; not dichotomous scores.

Parameters:

wave (Literal['pre', 'post'])

Return type:

ndarray

static schema()[source]

Fresh JSON schema with source column types, factor levels and transformations.

Return type:

dict

static source_documentation()[source]

Original pks probability Rd: item wordings, scoring and design notes.

Return type:

str

class knowledgespaces.datasets.ResponseDataset(name, description, source, source_license, data)[source]

Observed data with source and terms, without a ground-truth structure.

data contains responses or frequency rows, as specified by the loader. Fitting a structure to these responses is an analysis step, not evidence that the fitted states or implications were observed directly.

Parameters:
knowledgespaces.datasets.load_doignon_falmagne_7()[source]

Load the Doignon & Falmagne (1999, ch. 7) five-item example.

The canonical toy example of knowledge space theory: five items (a-e), a knowledge structure with nine states that is a learning space, and the response patterns of 1000 fictitious respondents. Known as DoignonFalmagne7 in the R package pks — the “7” refers to the book chapter.

Returns:
KSTDataset

structure has 9 states over items a..e; data holds all 32 response patterns with their frequencies (N = 1000).

Return type:

KSTDataset

Examples

>>> ds = load_doignon_falmagne_7()
>>> ds.structure.n_states
9
>>> int(ds.data.effective_counts.sum())
1000
knowledgespaces.datasets.load_pisa(*, aggregate=False)[source]

Load all 340 German students and five items from DAKS::pisa.

Data are dichotomized PISA 2003 mathematical-literacy responses, in original a..e column order. By default each row is a student in the R data-frame order. aggregate=True returns lexicographically ordered distinct patterns with frequencies. No rows are filtered or imputed.

The source does not provide item wording, original PISA item identifiers, original polytomous scores or dichotomization rules. Source row numbers are export positions, not recovered OECD participant identifiers. No true latent-state structure or relation is supplied. GPL-2.0-or-later terms and the original codebook are packaged in data/pisa, separately from the MIT implementation. Loading requires neither R nor a network.

Parameters:

aggregate (bool)

Return type:

ResponseDataset

knowledgespaces.datasets.load_probability(*, wave='pre', model='K2')[source]

Load the probability study’s completer sample, without R or downloads.

Data collected by Pasquale Anselmi and Florian Wickelmaier, University of Tuebingen, February-March 2010; distributed in pks 0.7-0 as probability. Selection follows the source example: !is.na(probability$b201) (N=345). Binary items b101..b112 (pre) or b201..b212 (post) are aligned as i01..i12. Frequencies describe 110 (pre) or 84 (post) distinct response patterns.

K1 and K2 are derived from the source skill functions, with 16 and 13 states respectively; neither is a knowledge space. They are candidate models, not observed ground-truth knowledge states.

Parameters:
Return type:

ProbabilityDataset

knowledgespaces.datasets.load_probability_individual(*, sample='all', mode='all', condition='all')[source]

Load all 504 source participants, with explicit optional selections.

Completers follow the source !is.na(b201) selection (345 cases); masks in b201..b212 are identical. Pre b101..b112 is complete for all 504. Source p omissions already coded b=0 remain zeros: no rescoring/imputation. 26 lab cases all have ‘enhan’; do not infer randomized lab treatment balance from the study’s general description. Empty selections raise. The 504 retained source cases are not the whole recruited population.

Parameters:
  • sample (Literal['all', 'completers'])

  • mode (Literal['all', 'lab', 'online'])

  • condition (Literal['all', 'basic', 'enhan'])

Return type:

ProbabilityIndividualDataset

Individual probability data with source identifiers, coding and pairing.

class knowledgespaces.datasets.individual.ProbabilityIndividualDataset(case_ids, pre, post, records, sample, mode, condition, source='Anselmi & Wickelmaier, Tuebingen 2010; probability in pks 0.7-0.', source_license='GPL-2.0-or-later')[source]

Row-aligned pre/post source scores, metadata and raw numeric responses.

case_ids retain the original pseudonymous case key; no pairing is inferred from frequency tables. records contains all 68 source columns as numeric values, strings/factor labels, ISO UTC timestamps or None. Source p values are numeric answers; source b values are the original correctness coding. schema() and source_documentation() expose types, levels and the original item wording/codebook. The source dataset and resources are GPL >= 2.

Parameters:
raw_answers(wave)[source]

Numeric p responses, including original NaN; not dichotomous scores.

Parameters:

wave (Literal['pre', 'post'])

Return type:

ndarray

static schema()[source]

Fresh JSON schema with source column types, factor levels and transformations.

Return type:

dict

static source_documentation()[source]

Original pks probability Rd: item wordings, scoring and design notes.

Return type:

str

knowledgespaces.datasets.individual.load_probability_individual(*, sample='all', mode='all', condition='all')[source]

Load all 504 source participants, with explicit optional selections.

Completers follow the source !is.na(b201) selection (345 cases); masks in b201..b212 are identical. Pre b101..b112 is complete for all 504. Source p omissions already coded b=0 remain zeros: no rescoring/imputation. 26 lab cases all have ‘enhan’; do not infer randomized lab treatment balance from the study’s general description. Empty selections raise. The 504 retained source cases are not the whole recruited population.

Parameters:
  • sample (Literal['all', 'completers'])

  • mode (Literal['all', 'lab', 'online'])

  • condition (Literal['all', 'basic', 'enhan'])

Return type:

ProbabilityIndividualDataset

Published response data without imposing a latent knowledge structure.

class knowledgespaces.datasets.observed.ResponseDataset(name, description, source, source_license, data)[source]

Observed data with source and terms, without a ground-truth structure.

data contains responses or frequency rows, as specified by the loader. Fitting a structure to these responses is an analysis step, not evidence that the fitted states or implications were observed directly.

Parameters:
knowledgespaces.datasets.observed.load_pisa(*, aggregate=False)[source]

Load all 340 German students and five items from DAKS::pisa.

Data are dichotomized PISA 2003 mathematical-literacy responses, in original a..e column order. By default each row is a student in the R data-frame order. aggregate=True returns lexicographically ordered distinct patterns with frequencies. No rows are filtered or imputed.

The source does not provide item wording, original PISA item identifiers, original polytomous scores or dichotomization rules. Source row numbers are export positions, not recovered OECD participant identifiers. No true latent-state structure or relation is supplied. GPL-2.0-or-later terms and the original codebook are packaged in data/pisa, separately from the MIT implementation. Loading requires neither R nor a network.

Parameters:

aggregate (bool)

Return type:

ResponseDataset

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_attribution(path)[source]

Read item/clause CSV rows with one binary column per domain item.

Repeated row items represent alternatives. Every column item must have at least one clause row. No reflexivity/refinement/antichain repair is performed; use to_surmise_function explicitly for canonicalization.

Parameters:

path (str | Path)

Return type:

Attribution

knowledgespaces.io.csv.write_attribution(attribution, path)[source]

Write the original clauses as repeated-item binary CSV rows.

Parameters:
Return type:

None

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

Read a clause CSV and require the surmise axioms without modifying it.

Parameters:

path (str | Path)

Return type:

SurmiseFunction

knowledgespaces.io.csv.write_surmise_function(function, path)[source]

Write a surmise function as a repeated-item clause CSV.

Parameters:
Return type:

None

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

Read competency rows from CSV: item label, then binary skill columns.

Item labels may repeat; each row is an alternative sufficient skill set. The header has an item-column label followed by unique skill labels. Quoted commas, newlines and Unicode labels use standard CSV escaping.

Parameters:

path (str | Path)

Return type:

SkillMultiMap

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

Write a CSV row per alternative competency, retaining unused skills.

Parameters:
Return type:

None

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, SkillMap, and SurmiseFunction.

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.io.json.surmise_function_to_dict(sf)[source]

Serialize a SurmiseFunction to a JSON-compatible dict.

Format:

{
    "domain": ["a", "b", ...],
    "clauses": {
        "a": [["a"]],
        "b": [["b", "d"], ["a", "b", "c"]],
        ...
    },
    "properties": {
        "n_items": 5,
        "is_ordinal": false,
        "is_discriminative": true,
        "is_acyclic": false
    }
}
Parameters:

sf (SurmiseFunction)

Return type:

dict[str, Any]

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

Deserialize a SurmiseFunction from a dict.

Raises:
ValueError

If required keys are missing or have wrong types.

Parameters:

data (dict[str, Any])

Return type:

SurmiseFunction

knowledgespaces.io.json.write_surmise_function_json(sf, path)[source]

Write a SurmiseFunction to a JSON file.

Parameters:
Return type:

None

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

Read a SurmiseFunction from a JSON file.

Parameters:

path (str | Path)

Return type:

SurmiseFunction

knowledgespaces.io.json.skill_multimap_to_dict(mapping)[source]

Serialize items, all skills and alternative competency families.

Parameters:

mapping (SkillMultiMap)

Return type:

dict[str, Any]

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

Deserialize alternative competencies, retaining item/alternative order.

Parameters:

data (dict[str, Any])

Return type:

SkillMultiMap

knowledgespaces.io.json.write_skill_multimap_json(mapping, path)[source]

Write a skill multimap with labels and empty/unused skills preserved.

Parameters:
Return type:

None

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

Read a labelled skill multimap JSON document.

Parameters:

path (str | Path)

Return type:

SkillMultiMap

knowledgespaces.io.json.attribution_to_dict(attribution)[source]

Serialize an attribution without replacing its clauses by canonical ones.

Parameters:

attribution (Attribution)

Return type:

dict[str, Any]

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

Deserialize general clauses; missing clause families are errors.

Parameters:

data (dict[str, Any])

Return type:

Attribution

knowledgespaces.io.json.write_attribution_json(attribution, path)[source]

Write general clauses and their domain to JSON.

Parameters:
Return type:

None

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

Read general clauses from JSON, without canonicalizing them.

Parameters:

path (str | Path)

Return type:

Attribution

knowledgespaces.io.json.family_to_dict(family)[source]

Serialize exactly the given family, without inserting endpoints.

Parameters:

family (SetFamily)

Return type:

dict[str, Any]

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

Deserialize a family that need not be a knowledge structure.

Parameters:

data (dict[str, Any])

Return type:

SetFamily

knowledgespaces.io.json.write_family_json(family, path)[source]

Write an arbitrary family; bases can be exported via as_family().

Parameters:
Return type:

None

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

Read a family; an explicit to_knowledge_base() interprets generators.

Parameters:

path (str | Path)

Return type:

SetFamily

Label-preserving interchange of taught/required course assignments.

The two CSV tables follow the CDSS column order (learning object, skill). JSON additionally retains requirement alternatives and unused domain labels.

knowledgespaces.io.curriculum.assignment_to_dict(assignment)[source]

Serialize all alternatives and the explicit skill domain.

Parameters:

assignment (SkillAssignment)

Return type:

dict[str, Any]

knowledgespaces.io.curriculum.dict_to_assignment(data)[source]

Read assignment JSON, validating nested records before construction.

Parameters:

data (dict[str, Any])

Return type:

SkillAssignment

knowledgespaces.io.curriculum.write_assignment_json(assignment, path)[source]

Write a UTF-8 JSON assignment, including alternative requirements.

Parameters:
Return type:

None

knowledgespaces.io.curriculum.read_assignment_json(path)[source]

Read a UTF-8 JSON assignment.

Parameters:

path (str | Path)

Return type:

SkillAssignment

knowledgespaces.io.curriculum.read_assignment_csv(taught_path, required_path, *, delimiter=',', header=True, learning_objects=None, skills=None)[source]

Read two CDSS-style (object, skill) tables as a single assignment.

Column order is positional; an optional header must have two columns. Empty records are ignored. Labels, including surrounding whitespace, are preserved exactly. Explicit domains retain absent objects/skills. Diagnostics are available on the result; semantic noncompliance does not prevent importing a course that needs correction.

Parameters:
Return type:

SkillAssignment

knowledgespaces.io.curriculum.write_assignment_csv(assignment, taught_path, required_path, *, delimiter=',', header=True)[source]

Write two CDSS-style tables without silently losing information.

This representation cannot retain alternative requirements or domain labels absent from both tables; such inputs are rejected. Use JSON for those cases. A reader infers object order from the taught table first; pass learning_objects when the precise order of a nonteaching object matters. Column headers are LO,Skill when requested.

Parameters:
Return type:

None

knowledgespaces.io.curriculum.read_assignment(path, required_path=None, *, taught_sheet='Taught', required_sheet='Required', header=True, delimiter=',', learning_objects=None, skills=None, formula_policy='reject', max_cells=1000000)[source]

Read a course from JSON, two CSV files, or a two-sheet XLSX/ODS workbook.

Format is detected by extension. Workbook sheets use the CDSS pair-table layout and default names Taught/Required; sheet indices are zero-based. Noncompliant courses remain available for diagnostics. Call .derive() on the returned assignment to run completion and both structural workflows. JSON carries its own domains; external domain/header/sheet options apply only to tabular inputs. No workbook formula is evaluated.

Parameters:
Return type:

SkillAssignment

knowledgespaces.io.curriculum.write_assignment(assignment, path, required_path=None, *, taught_sheet='Taught', required_sheet='Required', header=True, delimiter=',', max_cells=1000000)[source]

Export JSON, two CSV files, or a new CDSS-style XLSX/ODS workbook.

Pair-table formats reject alternatives and unrepresented domain labels. Use JSON to preserve those cases. Workbook exports contain exactly the requested taught/required sheets, with literal identifiers.

Parameters:
Return type:

None

Small typed tables in CSV, XLSX and ODS, without importing engines eagerly.

Writers create new export workbooks; they are not editors of existing workbook styles, charts or formulas. Empty trailing worksheet padding is discarded, while interior blank cells/rows remain explicit. Formulas are never evaluated.

knowledgespaces.io.tables.read_table(path, *, sheet=0, format='auto', delimiter=',', formula_policy='reject', max_cells=1000000)[source]

Read one rectangular table, including its header if present.

Only the selected sheet is interpreted. formula_policy='cached' uses stored formula results explicitly; their freshness is the caller’s responsibility. Missing caches raise. The default rejects formulas. CSV has no formula type and its fields are always read as literal text. CSV sheet selection must be 0. max_cells bounds the returned rectangular area, including interior blanks; trailing empty padding is ignored. XLSX/ODS require the optional spreadsheets extra.

Parameters:
Return type:

list[list[str | int | float | bool | None]]

knowledgespaces.io.tables.write_tables(tables, path, *, format='auto', delimiter=',', max_cells=1000000)[source]

Create an export file from named tables (headers are ordinary first rows).

CSV accepts exactly one table. XLSX and ODS preserve literal string labels, including strings starting with ‘=’; they are never written as formulas. Interior blanks remain empty cells. All input cells and dimensions are checked before opening the output. No existing workbook content is retained.

Parameters:
Return type:

None

Strict KST conversions for the tabular layouts of kstIO and CbKST.

Explicit kind selects the mathematical interpretation; no closure, endpoint insertion, base reduction, frequency aggregation or imputation is performed by a reader. Use the raw table API to inspect nonconforming files.

knowledgespaces.io.interchange.table_to_kst(table, *, kind, header=True, items=None, count_column=None, missing_marker='NA')[source]

Validate and convert an already loaded table to a mathematical object.

State/base/response tables have one column per item. Clause and multimap tables have an initial item-ID column and one column per item/skill. Relation rows and columns follow the same label order, with entries (prerequisite, dependent). Repeated response rows remain repeated. count_column explicitly selects a frequency column for response data; it is never guessed. Incomplete data additionally recognize empty cells and missing_marker; missingness assumptions remain the caller’s concern.

Parameters:
  • table (Sequence[Sequence[str | int | float | bool | None]])

  • kind (Literal['family', 'structure', 'space', 'basis', 'relation', 'attribution', 'surmise_function', 'skill_map', 'skill_multimap', 'data', 'incomplete_data'])

  • header (bool)

  • items (Sequence[str] | None)

  • count_column (str | None)

  • missing_marker (str)

Return type:

SetFamily | KnowledgeStructure | KnowledgeBase | SurmiseRelation | Attribution | SurmiseFunction | SkillMap | SkillMultiMap | ResponseMatrix | IncompleteResponseMatrix

knowledgespaces.io.interchange.read_kst(path, *, kind, sheet=0, format='auto', header=True, items=None, count_column=None, missing_marker='NA', delimiter=',', formula_policy='reject', max_cells=1000000)[source]

Read a CSV/XLSX/ODS table with an explicit, strictly checked KST kind.

For KST/SRBT/plain binary text use the legacy readers. Spreadsheet engines load only when needed. See table_to_kst() for layout conventions.

Parameters:
  • path (str | Path)

  • kind (Literal['family', 'structure', 'space', 'basis', 'relation', 'attribution', 'surmise_function', 'skill_map', 'skill_multimap', 'data', 'incomplete_data'])

  • sheet (str | int)

  • format (Literal['auto', 'csv', 'xlsx', 'ods'])

  • header (bool)

  • items (Sequence[str] | None)

  • count_column (str | None)

  • missing_marker (str)

  • delimiter (str)

  • formula_policy (Literal['reject', 'cached'])

  • max_cells (int)

Return type:

SetFamily | KnowledgeStructure | KnowledgeBase | SurmiseRelation | Attribution | SurmiseFunction | SkillMap | SkillMultiMap | ResponseMatrix | IncompleteResponseMatrix

knowledgespaces.io.interchange.kst_to_table(obj, *, items=None, header=True, count_column=None, missing_marker='NA')[source]

Export the exact represented family, clauses, relation, map or responses.

A base exports its base sets, not its span. A function exports canonical clauses, not all states. Frequencies require an explicit count_column; they are not discarded or expanded. Incomplete responses use an explicit text marker so entirely missing trailing respondents survive workbook I/O.

Parameters:
Return type:

list[list[str | int | float | bool | None]]

knowledgespaces.io.interchange.write_kst(obj, path, *, sheet='Data', format='auto', items=None, header=True, count_column=None, missing_marker='NA', delimiter=',', max_cells=1000000)[source]

Write one KST object as a new CSV/XLSX/ODS export.

Use write_tables with kst_to_table to put several objects into separate sheets of the same workbook.

Parameters:
Return type:

None

Strict readers/writers for the binary KST and SRBT 2.0 text formats.

The format specification is derived from the kstIO 0.5-1 public readers and writers. This is an independent implementation, not a translation of its GPL source. Neither format stores item labels: preserve the ordered labels separately and supply them on read. No missing-value code is defined here.

knowledgespaces.io.legacy.read_legacy_matrix(path, *, kind, format='auto', items=None)[source]

Read ordered labels and binary rows, preserving row order/duplicates.

kind declares the expected object and checks SRBT headers. This low level function validates syntax and dimensions, not knowledge-space axioms. With no items, labels are the strings "1" through "n". auto recognizes SRBT by its header and otherwise validates both KST and plain matrix syntax. Ambiguous files require an explicit format.

Only contiguous comment lines after SRBT dimensions are accepted. Wrong widths, nonbinary cells, missing/extra rows and unsupported headers fail. Structure reads also accept SRBT space headers, with or without ASCII.

Parameters:
  • path (str | Path)

  • kind (Literal['structure', 'space', 'basis', 'data', 'relation', 'family'])

  • format (Literal['KST', 'SRBT', 'matrix', 'auto'])

  • items (Sequence[str] | None)

Return type:

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

knowledgespaces.io.legacy.write_legacy_matrix(items, matrix, path, *, kind, format='KST')[source]

Write a binary matrix as KST, SRBT or unheaded rows.

items fixes the column order but labels are not stored in the file. SRBT supports structure, space, basis, data and relation. KST supports all these except relation, which has only SRBT/plain-matrix syntax. Matrices must have at least one row and column. No axioms are repaired. All validation occurs before opening the output file.

Parameters:
Return type:

None

knowledgespaces.io.legacy.read_legacy_structure(path, *, format='auto', items=None)[source]

Read a structure, requiring both ∅ and Q in the input rows.

Unlike the permissive structure constructor, this importer never inserts absent endpoint states. A space header is accepted as a structure without claiming that union closure has been verified; inspect is_knowledge_space.

Parameters:
Return type:

KnowledgeStructure

knowledgespaces.io.legacy.write_legacy_structure(structure, path, *, format='SRBT', items=None)[source]

Write states in cardinality/label order; columns default to sorted items.

Parameters:
Return type:

None

knowledgespaces.io.legacy.read_legacy_relation(path, *, format='auto', items=None)[source]

Read a reflexive, transitive relation; no closure is added implicitly.

Entry row a, column b equal to 1 means a is a prerequisite of b, matching kstMatrix/kstIO (columns are the minimal states for their items).

Parameters:
Return type:

SurmiseRelation

knowledgespaces.io.legacy.write_legacy_relation(relation, path, *, format='SRBT', items=None)[source]

Write a closed prerequisite relation; generators must be closed explicitly.

Parameters:
Return type:

None

Explicit, lossless conversions of binary patterns and named state rows.

These in-memory operations preserve row order and duplicates. Use SetFamily to request deduplication, and KnowledgeStructure to insert endpoint states.

knowledgespaces.io.patterns.states_to_matrix(states, *, items, dtype='int8', max_cells=1000000)[source]

Encode state rows in the supplied column order, without adding endpoints.

A path/list retains its order and duplicates; SetFamily and KnowledgeStructure iterate in their canonical order. Empty rows/columns are supported, including shape (0, len(items)). Unknown items raise. dtype=bool provides a logical matrix. No labels are inferred from observed states, so never-observed items remain represented.

Parameters:
Return type:

ndarray

knowledgespaces.io.patterns.matrix_to_states(matrix, *, items)[source]

Decode rows preserving order and duplicates, with explicit column labels.

Parameters:
Return type:

tuple[frozenset[str], …]

knowledgespaces.io.patterns.matrix_to_patterns(matrix, *, items=None, separator='', empty='{}')[source]

Encode rows as binary strings, or labelled strings when items is supplied.

The latter joins correct item labels with separator and uses empty for the empty response. Ambiguous label/separator/empty combinations raise rather than silently losing information. Duplicates and order survive. Binary strings use exactly one 0/1 per column, independent of separator.

Parameters:
Return type:

tuple[str, …]

knowledgespaces.io.patterns.patterns_to_matrix(patterns, *, items=None, separator='', empty='{}', dtype='int8', max_cells=1000000)[source]

Decode binary strings, or labelled patterns with an explicit domain.

With items=None, infer the binary width from the first row; an empty sequence has shape (0,0). With items supplied, parse labelled strings using the same conventions as matrix_to_patterns. Repeated labels, unknown labels and ragged/nonbinary rows are rejected.

Parameters:
Return type:

ndarray

knowledgespaces.io.patterns.expand_response_patterns(data, *, max_rows=1000000, max_cells=1000000, dtype='int8')[source]

Expand integer response frequencies, retaining original row/column order.

Fractional weights cannot represent individual respondents and raise. Output rows/cells are checked before repetition; zero-count rows vanish. For analysis without expansion keep the weighted ResponseMatrix instead.

Parameters:
Return type:

ndarray

knowledgespaces.io.patterns.subset_matrix(states, other=None, *, proper=False, max_cells=1000000)[source]

Boolean incidence I[i,j] = states[i] <= other[j] (pks is.subset).

other=None compares states with themselves; proper=True uses strict inclusion. Rows/columns follow input iteration, including duplicates. This is inclusion incidence, not the adjacency of a Hasse diagram.

Parameters:
Return type:

ndarray

knowledgespaces.io.patterns.boolean_matrix_product(left, right, *, max_cells=1000000)[source]

Boolean relational composition: any intermediate link, without integer overflow.

Parameters:
Return type:

ndarray

knowledgespaces.io.patterns.fringe_matrix(structure, states, *, items=None, kind='both', max_cells=1000000)[source]

Binary fringe rows for explicit states, using a structure or compact base.

Both means the union of inner and outer fringes. Each input must be a state of the represented structure; no span is enumerated for a base. Columns default to sorted domain and rows preserve input order.

Parameters:
Return type:

ndarray

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

Notes

Returns the symmetric Hausdorff distance \(\max(\vec{d}(K_1 \to K_2),\ \vec{d}(K_2 \to K_1))\). Argument order is irrelevant: hausdorff(A, B) == hausdorff(B, A). For the directional (asymmetric) components, use directional_distances() instead.

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. Returns nan if the domain has fewer than 2 items, or if both relations classify every off-diagonal pair in the same single category (p_expected=1, making the coefficient 0/0 rather than an estimable agreement).

Raises:
ValueError

If the relations have different domains.

Parameters:
Return type:

float

Notes

Both relations are first reduced to their transitive closure, so two representations of the same partial order agree. Agreement is then counted over the ordered pairs (a, b) with a != b — the strict, irreflexive relation. The diagonal is excluded because reflexivity is shared by every surmise relation and would only inflate agreement, giving the pair-based denominator \(n(n-1)\). This is Cohen’s (1960) kappa applied to the off-diagonal cells of the relation matrix.

Full-test BLIM reliability from de Chiusole et al. (2024, 2025).

2024: doi:10.3758/s13428-024-02468-3, Equations 10-11. 2025: doi:10.1111/bmsp.70013, Section 3, Equations 6-9. Direct finite-sum implementations, with response enumeration in chunks.

class knowledgespaces.metrics.reliability.BLIMReliability(rp_reliability, ks_reliability, mutual_information, entropy_responses, entropy_states, conditional_entropy_responses, accuracy, chance_accuracy, adjusted_accuracy, expected_discrepancy, discrepancy_distribution, states, state_accuracy, n_response_patterns, tie_tolerance)[source]

Population quantities for a fixed BLIM and administration of all items.

rp_reliability = I(K;R)/H(R); ks_reliability = I(K;R)/H(K). Entropies and mutual information use bits. Zero entropy in a denominator produces NaN. These coefficients concern the model, not a particular respondent or an adaptive stopping rule.

accuracy is P(MAP state = true state), with uniform random choice among tied posterior modes. chance_accuracy is max(pi), and adjusted_accuracy is (accuracy - chance)/(1 - chance), the 2025 kappa-prime index; it is NaN if chance is one. discrepancy_distribution[d] is P(|K xor MAP| = d), including d=0; expected_discrepancy is its unconditional mean in items. state_accuracy[k] is conditional accuracy given state k; it is NaN for states with zero prior mass. Arrays are read-only snapshots.

Parameters:
knowledgespaces.metrics.reliability.blim_reliability(structure, *, beta=0.1, eta=0.1, pi=None, chunk_size=1024, max_patterns=1048576, max_memory_bytes=512000000, tie_tolerance=1e-12)[source]

Compute the 2024 entropy and 2025 MAP reliability indices.

Parameters align with the sorted item domain and canonical state order; mappings are recommended for fitted parameters. Closed-box probabilities [0, 1] permit deterministic and independence limits. No sample is simulated: all 2**|Q| patterns are summed in bounded-size chunks. This reduces memory use, not the exponential enumeration time.

tie_tolerance groups log joint probabilities within this absolute distance of their maximum as tied modes. Set zero for equality of the computed values. Tied modes receive equal classification probability, as specified in the 2025 paper; no random seed is needed.

max_patterns limits work before enumeration; max_memory_bytes bounds a conservative allocation estimate, not actual process memory. These are full-test, fixed-model quantities. Plugging in a fit does not account for parameter uncertainty or establish validity for adaptive assessment or unobserved responses.

Parameters:
Return type:

BLIMReliability

Empirical validation of structures and prerequisite relations.

Direct implementations of the definitions documented by kst::kvalidate (Schrepp, 1999; Schrepp, Held & Albert, 1999). These are descriptive data–structure indices, distinct from model-based BLIM reliability.

class knowledgespaces.metrics.validation.StructureValidation(items, pattern_distances, distance_frequencies, di, da, uniform_distance, uniform_distance_frequencies)[source]

Empirical distance summary, with read-only arrays.

pattern_distances follows the input rows, including zero-weight rows. distance_frequencies[d] is their total weight at distance d, including d=0. di is their weighted mean distance in items. uniform_distance is the mean over the complete response powerset; da = di / uniform_distance. Both it and its frequency table are None when compute_da=False. DA is NaN for a powerset structure (zero denominator), and can exceed one. Smaller DI/DA means closer responses; neither is a significance test or a probability of validity.

Parameters:
knowledgespaces.metrics.validation.validate_structure(structure, data, *, compute_da=True, chunk_size=1024, max_patterns=1048576, max_memory_bytes=512000000)[source]

Nearest Hamming distances, DI, and optionally exact DA.

The supplied family need not be union closed and is not enlarged. Binary responses align by data.items. Integer counts and fractional nonnegative analysis weights are supported. DA compares DI with the uniform mean distance over all 2**|Q| responses, not a fitted BLIM. Chunking bounds temporary storage, not exponential enumeration time. max_patterns guards DA enumeration only; max_memory_bytes is a conservative allocation estimate, not a process memory guarantee.

Parameters:
Return type:

StructureValidation

class knowledgespaces.metrics.validation.RelationValidation(items, pairs, n_concordant, n_discordant, gamma, vc, solution_rates)[source]

Descriptive oriented-pair counts and coefficients.

pairs lists every nonreflexive pair (a, b): b implies a. Each respondent contributes once per pair, so counts may exceed sample size. A=1,B=0 is concordant; A=0,B=1 is discordant; ties contribute neither. gamma = (n_concordant - n_discordant)/(n_concordant + n_discordant); it is NaN without untied responses. vc divides discordances by total response weight times the number of pairs; it is NaN without nonreflexive pairs. solution_rates are fractions in input item order, not percentages. The array is read-only.

Parameters:
knowledgespaces.metrics.validation.validate_relation(relation, data)[source]

Compute KST gamma, VC and item solution rates.

Relation generators are transitively closed before counting; equivalent items contribute both directed pairs. A structure is replaced by its implied surmise relation. For non-quasi-ordinal structures this tests only the pairwise prerequisites, not all restrictions of the family; use validate_structure() to assess the family itself.

Parameters:
Return type:

RelationValidation

Observed response-pattern frequencies, independent of latent-state fitting.

class knowledgespaces.metrics.frequencies.PatternFrequencies(items, patterns, counts, probabilities, total_weight, n_observed_patterns, reference_patterns, reference_counts)[source]

Frequency report with read-only arrays and explicit item order.

patterns/counts contain the selected most frequent observed rows. probabilities divides these counts by total_weight, including rows omitted by n. n_observed_patterns counts all distinct positive-weight rows. reference_patterns/reference_counts retain the requested row order and duplicates, with zero for rows absent from the observations. Matching a knowledge state here is an observed response frequency, not an estimate of that state’s latent probability under a BLIM.

Parameters:
property n: int

Number of returned observed patterns, after truncation.

knowledgespaces.metrics.frequencies.pattern_frequencies(data, *, n=5, reference_patterns=None, items=None, counts=None, max_memory_bytes=512000000)[source]

Count observed patterns and exact matches to optional reference rows.

This is the descriptive workflow of DAKS::pattern(dataset,n,P), with weights and unambiguous numeric row keys. ResponseMatrix supplies its labels and frequencies; items/counts must then be omitted. Raw numeric matrices also support polytomous scores (this does not extend BLIM or IITA to such scores). Raw labels default to item_1, item_2, etc.

n=None returns every distinct positive-weight row. Otherwise n is a positive integer, capped at that number. Counts sort descending, ties by ascending numeric row order. Fractions use the total input weight, not the truncated sum. Nonnegative fractional weights are allowed; zero-weight rows are excluded from the observed-pattern report.

reference_patterns is a matrix in data’s column order, or a labelled KnowledgeStructure (aligned by label, in canonical state order). Every reference row is retained, including duplicates and absent rows. Missing/infinite scores are rejected rather than imputed or omitted. Inputs are copied into read-only output arrays; integral scores are not converted to float, and patterns are never concatenated into strings. max_memory_bytes preflights estimated array and row-lookup storage; it is not an exact peak-memory guarantee of NumPy’s sorting backend.

Parameters:
Return type:

PatternFrequencies

knowledgespaces.viz

Requires the optional viz extra (pip install "knowledgespaces[viz]").

Hasse diagram visualization for knowledge structures and surmise relations.

Requires the viz extra: pip install knowledgespaces[viz]

The Hasse diagram of a knowledge structure plots states as nodes and draws the covering relation of state inclusion: no intermediate state lies strictly between the endpoints. For surmise relations, it shows the transitive reduction.

References:

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

class knowledgespaces.viz.hasse.HasseData(domain, states, edges, positions)[source]

Exact plotted members, cover edges and coordinates, without matplotlib.

Edges use integer indices into states. as_dict exports JSON-ready lists; item identities are preserved independently of display labels.

Parameters:
knowledgespaces.viz.hasse.hasse_data(structure, *, orientation='vertical')[source]

Compute inclusion covers of the supplied family, preserving its members.

For KnowledgeBase this plots the base rows, never its union span. Empty families are allowed. Coordinates use cardinality layers, swapped for horizontal orientation. This layout does not promise to avoid every edge crossing. No optional plotting dependency is imported.

Parameters:
Return type:

HasseData

knowledgespaces.viz.hasse.plot_hasse(structure, *, ax=None, figsize=(10, 8), node_color='#4A90D9', edge_color='#888888', highlight_states=None, highlight_color='#E74C3C', title=None, show_labels=True, font_size=8, orientation='vertical', node_labels=None, node_colors=None, node_values=None, cmap='viridis', value_label='Node value', highlight_paths=None, centre=None)[source]

Plot the Hasse diagram of a knowledge structure.

Nodes are knowledge states, arranged in layers by cardinality. Edges connect states with no intermediate state in the inclusion order. They differ by one item on a well-graded family, but may differ by multiple items on a general structure or with equivalent items.

Parameters:
structureKnowledgeStructure, SetFamily or KnowledgeBase

Exact members to visualize. A base is not expanded into its span.

axmatplotlib Axes or None

Axes to draw on. If None, a new figure is created.

figsizetuple

Figure size (width, height) in inches.

node_colorstr

Color for state nodes.

edge_colorstr

Color for edges.

highlight_statescollection of frozensets or None

States to highlight (e.g., atoms, base, current state).

highlight_colorstr

Color for highlighted states.

titlestr or None

Plot title. None (the default) uses a standard title; pass an empty string to suppress the title entirely.

show_labelsbool

Whether to show state labels on nodes.

font_sizeint

Font size for labels.

orientation{‘vertical’, ‘horizontal’}

Direction of increasing cardinality.

node_labels, node_colorsmapping or None

Optional display labels or colors, keyed by exact member states.

node_valuesmapping or None

Finite values for every node, mapped to cmap with a colorbar. Useful for state masses; not normalized or treated as probabilities. Mutually exclusive with node_colors. Highlights then use outlines.

cmap, value_labelstr

Matplotlib colormap name and colorbar label.

highlight_pathscollection of paths or None

Highlight states and edges of supplied chains of inclusion covers.

centrecollection or None

A member to mark with a diamond, for example a neighbourhood centre.

Returns:
matplotlib.figure.Figure

The figure containing the Hasse diagram.

Parameters:
Return type:

Figure

knowledgespaces.viz.hasse.plot_relation(relation, *, ax=None, figsize=(8, 6), node_color='#2ECC71', edge_color='#555555', title=None, font_size=10, collapse_equivalent=False, orientation='vertical', node_labels=None, node_colors=None)[source]

Plot the Hasse diagram of a surmise relation.

Nodes are items, arranged by topological level. Edges show the transitive reduction (direct prerequisites only).

Parameters:
relationSurmiseRelation

The surmise relation (will be transitively reduced).

axmatplotlib Axes or None

Axes to draw on. If None, a new figure is created.

figsizetuple

Figure size in inches.

node_colorstr

Node color.

edge_colorstr

Edge color.

titlestr or None

Plot title. None (the default) uses a standard title; pass an empty string to suppress the title entirely.

font_sizeint

Font size for item labels.

collapse_equivalentbool

If True, draw the partial-order quotient by mutual prerequisites. Each node displays every item in its class. Class representatives and members are separately available via relation.quotient(). Default False preserves rejection of cycles. Closure and reduction operate directly on the relation, without enumerating knowledge states.

orientation{‘vertical’, ‘horizontal’}

Direction of increasing prerequisite level; arrows keep their meaning.

node_labels, node_colorsmapping or None

Optional labels or colors keyed by items (quotient representatives when collapse_equivalent=True). Identities and order are unchanged.

Returns:
matplotlib.figure.Figure
Parameters:
  • relation (SurmiseRelation)

  • ax (Axes | None)

  • figsize (tuple[float, float])

  • node_color (str)

  • edge_color (str)

  • title (str | None)

  • font_size (int)

  • collapse_equivalent (bool)

  • orientation (Literal['vertical', 'horizontal'])

  • node_labels (Mapping[str, str] | None)

  • node_colors (Mapping[str, str] | None)

Return type:

Figure

Optional matplotlib diagnostics for complete BLIM response tables.

knowledgespaces.viz.diagnostics.plot_blim_residuals(residuals, *, kind='deviance', ax=None, figsize=(8, 5))[source]

Plot signed residuals against predicted response probabilities.

Uses all cells, including zero observed counts. The horizontal zero line is a visual reference, not a significance threshold. A table with infinite residuals is rejected explicitly; inspect impossible observed cells in the numerical result before plotting. Requires the viz extra.

Parameters:
Return type:

Figure

Static plots of clauses, coefficients and retained bootstrap outcomes.

knowledgespaces.viz.reports.plot_surmise_function(function, *, space=False, max_items=None, ax=None, figsize=(10, 8), orientation='vertical', node_color='#4A90D9', font_size=8, title=None)[source]

Draw clauses as base nodes, labelled with the items at which they are atoms.

The same clause can be atomic at several items, all shown in its label. space=True explicitly expands the union span and labels atomic states there; max_items is forwarded to the guarded expansion. The default plots the compact base. Display text does not alter state identities.

Parameters:
Return type:

Figure

knowledgespaces.viz.reports.plot_coefficients(coefficients, *, standard_deviations=None, replicates=None, converged=None, kind='point', ax=None, figsize=(8, 5), color='#4A90D9', title='Coefficients', ylabel='Parameter value')[source]

Plot named estimates with SD bars or bootstrap parameter boxplots.

Column order of replicates must match insertion order of the mapping. SD bars are descriptive ±1 SD, not confidence intervals, and are never clipped to [0,1]. NaN SDs are annotated as unavailable. kind='box' requires a replicate matrix and displays every finite outlier. Nonfinite cells cannot be drawn and their count is annotated; convergence flags annotate, never filter, replicates. The original inputs are not modified. Use the estimate/bootstrap arrays directly for numerical export.

Parameters:
  • coefficients (Mapping[str, float])

  • standard_deviations (Mapping[str, float] | None)

  • replicates (ArrayLike | None)

  • converged (ArrayLike | None)

  • kind (Literal['point', 'box'])

  • ax (Axes | None)

  • figsize (tuple[float, float])

  • color (str)

  • title (str)

  • ylabel (str)

Return type:

Figure

knowledgespaces.viz.reports.plot_bootstrap_distribution(replicates, *, observed=None, converged=None, bins='auto', kind='histogram', ax=None, figsize=(8, 5), color='#4A90D9', title='Bootstrap distribution', xlabel='Statistic')[source]

Plot finite bootstrap statistics with an optional observed reference line.

No outlier trimming or convergence selection is performed. Nonfinite values and nonconverged fits are counted visibly. An empty or wholly nonfinite array produces a labelled empty plot. This plot does not compute a p-value or repair failed refits; inspect the underlying bootstrap result. kind='survival' draws the empirical fraction of finite values >= each unique value, preserving ties. It is descriptive, conditional on finite values when failures occur, and is not an add-one Monte Carlo p-value.

Parameters:
  • replicates (ArrayLike)

  • observed (float | None)

  • converged (ArrayLike | None)

  • bins (int | str)

  • kind (Literal['histogram', 'survival'])

  • ax (Axes | None)

  • figsize (tuple[float, float])

  • color (str)

  • title (str)

  • xlabel (str)

Return type:

Figure