Recovering a causal graph from a system that isn't watching you build it Konjugate v0.7.0 introduces a causal inference feature that recovers causal graphs from CSV time-series data, demonstrated on an 8-node synthetic thermal/vibration system with a known structure. The tool proposed 11 candidate edges, correctly identified 10 true edges, and flagged one low-confidence reverse edge (componentTemperature → ambientTemperature, score 0.28, coefficient -0.008) that users can reject before committing. Recovered equation coefficients matched true values within 0.1% to 0.4% error across tested edges. Konjugate v0.7.0 pioneers the feature to perform Causal Inference by simply importing a CSV-file containing timeseries data from several nodes. In the real world, the nodes could be sensor logs, census data or even stock market valuation. It works in two stages: a statistical skeleton determines which nodes are related at all, and then a lagged regression fit determines the direction of each relationship and fits it as a linear or polynmial equation. Every proposed node and edge is reviewable before it is added to your model. To test and to demonstrate this feature, we built an 8-node synthetic thermal/vibration system with a known-in-advance causal structure, fed only its CSV output to Konjugate's causal inference feature and checked what came back; including the parts it got wrong. Eight variables in an electronics enclosure: three independent drivers with their own AR 1 noise nothing causes them , and five downstream quantities built from twelve lagged relationships — one of them curved. Every equation below is the literal generator: target t = f parent t−1 + noise . ambientTemperature root amb t = 0.5·amb t−1 + noise motorLoad root motor t = 0.5·motor t−1 + noise solarIrradiance root solar t = 0.5·solar t−1 + noise enclosureTemperature enc t = 2.0·amb t−1 + 1.5·solar t−1 + noise vibrationAmplitude vib t = 3.0·motor t−1 + noise componentTemperature comp t = 2.5·enc t−1 + 1.0·motor t−1 + 1.0·amb t−1 + noise thermalStress stress t = 3.5·comp t−1 + 0.8·comp t−1 ² + 2.0·amb t−1 + noise fatigueAccumulation fatigue t = 1.5·stress t−1 + 1.2·vib t−1 + 1.0·comp t−1 + 0.8·motor t−1 + noise Three roots, two nodes with several converging parents, one purely mediated relationship and one quadratic edge. Eight nodes with twelve edges is dense enough that getting it right is not going to be easy. Here is the true structure and Konjugate's pred The true structure is shown below first, with coefficients labeled and the two edges Konjugate did not recover marked separately. Below that, what you see is an unedited screenshot of the graph that Konjugate actually produced after importing the CSV-file, running inference in "let the tool decide" mode, and committing the accepted candidates. Konjugate proposed 11 candidates. Ten matched true edges. The eleventh, componentTemperature → ambientTemperature, a reverse edge with score 0.28 and a coefficient of -0.008, is the kind of low-confidence artifact the review-before-commit step exists for; we unchecked it before committing, the same way a real user would. Zero high-confidence false edges made it into the model. Indeed, a recovered graph is only useful if the equations are close to the true ones. For each downstream node, we evaluated it's recovered equation one step ahead: predicted t = fitted equation of true parent t−1 — against what the system actually did next. Indeed, a recovered graph is only useful if the equations are close to the true ones. For each downstream node, the true data of the parent at the previous timestep was used to evaluate the equation, and the result is compared against the true data of the node at this timestep. Note that true data means the content in the CSV-file. Every equation is always fed ground truth, never an upstream equations prior prediction. Also, this is not what you would see from actually running the committed model forward inside Konjugate. That uses a different execution path entirely, which is detailed in the Known Limitations section. | Edge | Degree | True | Recovered | Error | |---|---|---|---|---| | ambientTemperature → enclosureTemperature | linear | 2 | 2.005 | 0.2% | | solarIrradiance → enclosureTemperature | linear | 1.5 | 1.499 | 0.1% | | motorLoad → vibrationAmplitude | linear | 3 | 2.989 | 0.4% | | ambientTemperature → componentTemperature | linear | 1 | 1.003 | 0.3% | | motorLoad → componentTemperature | linear | 1 | 0.996 | 0.4% | | enclosureTemperature → componentTemperature | linear | 2.5 | 2.500 | 0.0% | | componentTemperature → thermalStress | linear | 3.5 | 3.557 | 1.6% | | componentTemperature → thermalStress | x² | 0.8 | 0.799 | 0.1% | | ambientTemperature → thermalStress not recovered | linear | 2 | — | — | | thermalStress → fatigueAccumulation | linear | 1.5 | 1.499 | 0.0% | | vibrationAmplitude → fatigueAccumulation | linear | 1.2 | 1.309 | 9.1% | | componentTemperature → fatigueAccumulation | linear | 1 | 1.007 | 0.7% | | motorLoad → fatigueAccumulation not recovered | linear | 0.8 | — | — | Konjugate's causal inference screens candidate relationships using lag-1 partial correlation. This is to isolate each edge's own fit quality instead of letting errors compound through a chain, like the butterfly effect. Indeed, when we finally run the final model on Konjugate, it will have some error compounding as it would not have access to any source of truth. As you can see in the table above, two edges were not predicted by Konjugate. These share a pattern: each is a direct effect of a variable that also has an almost-single-parent proxy elsewhere in the graph componentTemperature for the first, vibrationAmplitude for the second . This is structural, not a sample-size problem; tripling the row count and doubling the true coefficient makes no difference. Ridge regression, faced with two predictors that carry almost the same information, is free to assign the shared effect to either one, and it consistently picks the proxy. The underlying cause is a known graphical-model pitfall called collider bias. Conditioning on every other variable at once, including a target's own near-deterministic descendants, can suppress a real relationship's signal. Perhaps this may be improved in the future using an adaptive conditioning search. Ten out of twelve true edges, correct linear-vs-curved classification on every edge were recovered. No confidently-wrong structure; one flagged low-confidence candidate that a two-second review can catch. The two misses have a specific, understood cause rather than being unexplained noise. That's a result we're comfortable publishing with the failures left in.