Getting started
This guide walks through installing the Python package and running a first session against an existing theory file.
Installation
Install from PyPI with pip:
pip install theoryforge
Sample theories
The examples below read a sample theory bundled with the project repository. The fixture files live under fixtures/ in the repository at github.com/pablobernabeu/theoryforge, for example fixtures/panic-network.theory.yaml. Download a fixture, or point the path at one of your own theory files. Here fixtures names that directory, and every result shown on this page is produced by running the code when the page is built.
A first session
Import the package and read a theory from a YAML file. Adjust the path to the file on your own machine.
import theoryforge as tf
t = tf.read(fixtures / "panic-network.theory.yaml")
Validate the theory. With no arguments this checks the required fields and enum membership, returning True on success and raising ValueError with a list of problems otherwise. Pass full=True to additionally check referential integrity: that ids are unique within each collection, that every cross-reference between constructs, propositions, predictions and alternatives points to a declared id, and that assumption, test-outcome and evidence entries reference declared predictions.
t.validate()
t.validate(full=True) # also checks referential integrity
Produce the rigour report. The "json" format returns the 12-item rigour checklist together with the overall gate. t.check() returns the same information as a plain Python dictionary; t.report(format=...) renders it as a string, in "json" or "html".
print(t.report("json"))
{
"theory_id": "panic-network-2026",
"schema_version": "1.0",
"maturity": "developing",
"aggregate_score": 84.8,
"gate": "pass",
"n_blockers_failed": 0,
"items": [
{
"id": "falsifiability",
"status": "pass",
"score": 1.0,
"weight": 0.15,
"severity_if_fail": "blocker",
"citation": "Popper (1959); Bacharach (1989)"
},
{
"id": "precision",
"status": "pass",
"score": 0.667,
"weight": 0.1,
"severity_if_fail": "warning",
"citation": "Meehl (1967, 1990)"
},
{
"id": "risk_severity",
"status": "pass",
"score": 0.567,
"weight": 0.1,
"severity_if_fail": "warning",
"citation": "Mayo (2018); Meehl (1990)"
},
{
"id": "parsimony",
"status": "pass",
"score": 0.667,
"weight": 0.08,
"severity_if_fail": "warning",
"citation": "Forster & Sober (1994); Lakatos (1970)"
},
{
"id": "non_redundancy",
"status": "pass",
"score": 0.909,
"weight": 0.1,
"severity_if_fail": "warning",
"citation": "Kelley (1927); Le et al. (2010); Lawson & Robins (2021)"
},
{
"id": "construct_clarity",
"status": "pass",
"score": 1.0,
"weight": 0.08,
"severity_if_fail": "warning",
"citation": "Suddaby (2010); Cronbach & Meehl (1955); Flake & Fried (2020)"
},
{
"id": "scope",
"status": "pass",
"score": 1.0,
"weight": 0.06,
"severity_if_fail": "warning",
"citation": "Whetten (1989); Bacharach (1989)"
},
{
"id": "logical_why",
"status": "pass",
"score": 1.0,
"weight": 0.08,
"severity_if_fail": "warning",
"citation": "Sutton & Staw (1995); Whetten (1989)"
},
{
"id": "causal_testability",
"status": "pass",
"score": 1.0,
"weight": 0.06,
"severity_if_fail": "warning",
"citation": "Textor et al. (2016); Eronen & Bringmann (2021)"
},
{
"id": "diagnosticity",
"status": "pass",
"score": 0.333,
"weight": 0.06,
"severity_if_fail": "warning",
"citation": "Platt (1964); Fiedler (2017)"
},
{
"id": "formalisation",
"status": "pass",
"score": 1.0,
"weight": 0.05,
"severity_if_fail": "warning",
"citation": "Robinaugh et al. (2021); Guest & Martin (2021)"
},
{
"id": "derivation_chain",
"status": "pass",
"score": 1.0,
"weight": 0.08,
"severity_if_fail": "blocker",
"citation": "Scheel et al. (2021); Szollosi et al. (2020)"
}
]
}
Working from the dictionary that t.check() returns, individual results are easy to read off, including each checklist item in order.
report = t.check()
print(report["aggregate_score"])
print(report["gate"])
print(report["n_blockers_failed"])
print(report["items"][0]["id"])
print(report["items"][0]["status"])
84.8
pass
0
falsifiability
pass
Export a diagram. The digraph types, such as nomological_net, return Graphviz DOT, which you can render with Graphviz or inspect directly.
print(t.diagram("nomological_net"))
digraph nomological_net {
graph [rankdir=LR, bgcolor="transparent", fontname="Helvetica", fontsize=11, pad="0.2", nodesep="0.3", ranksep="0.45"];
node [fontname="Helvetica", fontsize=11, shape=box, style="rounded,filled", color="#33567A", fillcolor="#F2F6F9", fontcolor="#12283A", penwidth=1.1, margin="0.16,0.1"];
edge [fontname="Helvetica", fontsize=10, color="#7B909F", fontcolor="#0F6E6E", arrowsize=0.7];
"c_arousal" [label="Physiological\narousal", fillcolor="#E4F1F1", color="#1E7B7B"];
"c_perceived_threat" [label="Perceived threat", fillcolor="#E4F1F1", color="#1E7B7B"];
"c_avoidance" [label="Avoidance\nbehaviour", fillcolor="#E4F1F1", color="#1E7B7B"];
"c_arousal" -> "c_perceived_threat" [label="increases"];
"c_perceived_threat" -> "c_avoidance" [label="increases"];
"c_perceived_threat" -> "c_arousal" [label="causes"];
}
The causal_dag type returns dagitty syntax instead, for causal-inference tooling. It carries the same edges as the DOT above, without the presentation attributes.
print(t.diagram("causal_dag"))
dag {
c_arousal -> c_perceived_threat
c_perceived_threat -> c_avoidance
c_perceived_threat -> c_arousal
}
With the optional render extra (pip install theoryforge[render]), render_diagram(t, "nomological_net") wraps the DOT in a graphviz.Source that displays inline in a notebook; the causal_dag view is the one exception, since dagitty syntax renders in a dagitty tool rather than Graphviz. Rendered, the nomological-net DOT printed above reads as the figure below.
Run the lexical redundancy screen. This returns every construct pair with a lexical similarity of their definitions and an ok/review flag, marking review the pairs whose definitions overlap enough to suggest jingle-jangle redundancy.
for pair in t.redundancy_check():
print(pair["a"], pair["b"], pair["similarity"], pair["flag"])
c_perceived_threat c_avoidance 0.091 ok
c_arousal c_avoidance 0.0 ok
c_arousal c_perceived_threat 0.0 ok
A theory can be written back to disk with t.write(). The format follows the file extension, .json for JSON and otherwise YAML.
t.write("panic-network.theory.yaml") # YAML; use a .json path for JSON
reloaded = tf.read("panic-network.theory.yaml")
reloaded.id == t.id # True
Next steps
The same Theory object supports the building, development and testing modes (severity(), appraise_amendment(), preregister()) and the literature layer (read_corpus, litmap, landscape). See the API reference for the full function list.