Native Ports

py-scm is the reference linear-Gaussian implementation, but it is not the only runtime we maintain. The same continuous reasoning surface is also available in the C#, Java, C++, and TypeScript/JavaScript Darkstar ports in the darkstar-bbn superproject.

These ports are maintained against the same shared fixtures and benchmark harness used for the Python reference. Continuous parity is checked on both the committed oracle models and generated Gaussian BBN sweeps, with deterministic size/topology runs across the five implementations.

The maintained continuous surface targets parity for:

  • pquery

  • iquery

  • equery

  • cquery

  • samples

  • continuous serde

  • seeded singly- and multi-connected Gaussian BBN generation

Current port names and namespaces:

  • C#: RocketVector.DarkStar.Continuous

  • Java: io.rocketvector.darkstar.continuous

  • C++: rocketvector::darkstar::continuous

  • TypeScript / JavaScript: package @rocketvector/darkstar under darkstar.continuous

Code Examples

Each example below builds the same small linear-Gaussian model, then runs associational, interventional, average-causal-effect, and counterfactual queries against it.

C#

using RocketVector.DarkStar.Continuous;

var graph = GraphData.Create(
    new[] { "C", "X", "Y" },
    new[]
    {
        new[] { "C", "X" },
        new[] { "C", "Y" },
        new[] { "X", "Y" },
    });

var parameters = Parameters.FromPayload(
    new[] { "C", "X", "Y" },
    new[] { 1.0017, 4.9960, 10.5033 },
    new[]
    {
        new[] { 0.9907, 2.9799, 6.9569 },
        new[] { 2.9799, 9.9734, 22.4469 },
        new[] { 6.9569, 22.4469, 52.1280 },
    });

var model = Reasoning.CreateReasoningModel(graph, parameters);

var posterior = model.Pquery(new Dictionary<string, double> { ["X"] = 4.0 });
var interventional = model.Iquery("Y", new Dictionary<string, double> { ["X"] = 4.0 });
var ace =
    model.Equery(
        "Y",
        new Dictionary<string, double> { ["X"] = 5.0 },
        new Dictionary<string, double> { ["X"] = 3.0 });
var counterfactual =
    model.Cquery(
        "Y",
        new Dictionary<string, double> { ["C"] = 1.0, ["X"] = 4.0, ["Y"] = 10.0 },
        new[]
        {
            (IReadOnlyDictionary<string, double>)new Dictionary<string, double> { ["X"] = 2.0 },
            new Dictionary<string, double> { ["X"] = 6.0 },
        });

Console.WriteLine(posterior.Mean.Get("Y"));
Console.WriteLine(interventional.Mean);
Console.WriteLine(ace.Mean);
Console.WriteLine(counterfactual.Row(0)[0]);

Java

import io.rocketvector.darkstar.continuous.GraphData;
import io.rocketvector.darkstar.continuous.Parameters;
import io.rocketvector.darkstar.continuous.Reasoning;
import java.util.List;
import java.util.Map;

var graph =
    new GraphData(
        List.of("C", "X", "Y"),
        List.of(List.of("C", "X"), List.of("C", "Y"), List.of("X", "Y")));

var parameters =
    Parameters.fromPayload(
        List.of("C", "X", "Y"),
        new double[] {1.0017, 4.9960, 10.5033},
        new double[][] {
          {0.9907, 2.9799, 6.9569},
          {2.9799, 9.9734, 22.4469},
          {6.9569, 22.4469, 52.1280}
        });

var model = Reasoning.createReasoningModel(graph, parameters);

var posterior = model.pquery(Map.of("X", 4.0));
var interventional = model.iquery("Y", Map.of("X", 4.0));
var ace = model.equery("Y", Map.of("X", 5.0), Map.of("X", 3.0));
var counterfactual =
    model.cquery(
        "Y",
        Map.of("C", 1.0, "X", 4.0, "Y", 10.0),
        List.of(Map.of("X", 2.0), Map.of("X", 6.0)));

System.out.println(posterior.mean().get("Y"));
System.out.println(interventional.mean());
System.out.println(ace.mean());
System.out.println(counterfactual.row(0)[0]);

C++

#include <iostream>
#include "continuous.h"

using rocketvector::darkstar::continuous::GraphData;
using rocketvector::darkstar::continuous::Parameters;
using rocketvector::darkstar::continuous::createReasoningModel;

GraphData graph{
    {"C", "X", "Y"},
    {{"C", "X"}, {"C", "Y"}, {"X", "Y"}},
};

Parameters parameters{
    {{"C", "X", "Y"}, {1.0017, 4.9960, 10.5033}},
    {{"C", "X", "Y"},
     {
         {0.9907, 2.9799, 6.9569},
         {2.9799, 9.9734, 22.4469},
         {6.9569, 22.4469, 52.1280},
     }},
};

auto model = createReasoningModel(graph, parameters);

auto posterior = model.pquery({{"X", 4.0}});
auto interventional = model.iquery("Y", {{"X", 4.0}});
auto ace = model.equery("Y", {{"X", 5.0}}, {{"X", 3.0}});
auto counterfactual =
    model.cquery("Y", {{"C", 1.0}, {"X", 4.0}, {"Y", 10.0}},
                 {{{"X", 2.0}}, {{"X", 6.0}}});

std::cout << posterior.mean.get("Y") << '\n';
std::cout << interventional.mean << '\n';
std::cout << ace.mean << '\n';
std::cout << counterfactual.row(0).at(0) << '\n';

TypeScript / JavaScript

The TypeScript port compiles to JavaScript for Node, so the same public API is available from either language. The example below is plain modern JavaScript.

import { darkstar } from '@rocketvector/darkstar';

const graph = {
  nodes: ['C', 'X', 'Y'],
  edges: [['C', 'X'], ['C', 'Y'], ['X', 'Y']],
};

const parameters = {
  v: ['C', 'X', 'Y'],
  m: [1.0017, 4.9960, 10.5033],
  S: [
    [0.9907, 2.9799, 6.9569],
    [2.9799, 9.9734, 22.4469],
    [6.9569, 22.4469, 52.1280],
  ],
};

const model = darkstar.continuous.createReasoningModel(graph, parameters);

const posterior = model.pquery({ X: 4.0 });
const interventional = model.iquery('Y', { X: 4.0 });
const ace = model.equery('Y', { X: 5.0 }, { X: 3.0 });
const counterfactual = model.cquery(
  'Y',
  { C: 1.0, X: 4.0, Y: 10.0 },
  [{ X: 2.0 }, { X: 6.0 }]
);

console.log(posterior.mean.get('Y'));
console.log(interventional.mean);
console.log(ace.mean);
console.log(counterfactual.row(0)[0]);

Runtime Comparison

The shared continuous benchmark harness runs deterministic Gaussian models across all five implementations and checks every non-Python query/serde/startup output against the Python reference. It also supports generated singly- and multi-connected size sweeps at 30, 50, 100, 250, 500, and 1000 nodes.

The saved measurements below reflect a generated multi Gaussian network at SIZE=1000 rerun locally on April 4, 2026 after the retained continuous hot-path optimizations. They are useful for relative comparisons, not as universal absolutes.

In the saved continuous query runs, every non-Python port matched the Python reference on the generated Gaussian workload.

1000-Node Generated Gaussian Query Sweep

Language

Cold query (ms)

Warm query (ms)

Python

16.066

0.046

TypeScript / JavaScript

73.979

0.850

Java

17.609

0.804

C#

52.427

1.088

C++

12.979

6.150

1000-Node Generated Gaussian Sampling Sweep

Language

Warm sampling (ms)

Python

43.898

TypeScript / JavaScript

90.290

Java

81.199

C#

53.493

C++

20.660

The largest improvements in these saved runs came from structural Gaussian sampling and target-only interventional evaluation, which avoid unnecessary dense covariance work on repeated continuous queries.