Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
007ee3a
Added PyRegistry.
mira-vg Feb 20, 2026
be8a84b
Added PyRegistry.
mira-vg Feb 20, 2026
7f85457
Fetching newest changes from development
Hiddentale Feb 20, 2026
41552b2
Merge branch 'bindings' of https://github.com/GiPHouse/Conditional-In…
Hiddentale Feb 20, 2026
6f44c29
Added constructor for PyRegistry.
mira-vg Feb 20, 2026
cb38caf
Finished preliminary version of Python bindings:
mira-vg Feb 25, 2026
00d7b82
Implemented basic functionality of CITestRegistry on the Python side.…
mira-vg Mar 5, 2026
0bb6ac0
Merged pearsonr. Added arguments and returned results to PyCITest on …
mira-vg Mar 5, 2026
ea4b45f
Implemented first attempt at Python-side wrapper for CI tests.
mira-vg Mar 6, 2026
b4301e6
Added unit tests in Python from pgmpy.
mira-vg Mar 9, 2026
7fc2cbb
Fixed import in CITests.py.
mira-vg Apr 6, 2026
e39a5b8
Merge pull request #130 from GiPHouse/development
Hiddentale Apr 10, 2026
085281a
Merge branch 'development' into bindings
mira-vg Apr 15, 2026
d943b5e
(ci-python) Properly set up building, unit tests, etc. Registry is st…
mira-vg Apr 15, 2026
232bb05
Reverted Registry and bindings to old Registry that is currently on t…
mira-vg Apr 15, 2026
511a684
Merge branch 'development' into bindings
mira-vg Apr 15, 2026
ff8840d
Sorted imports of ci-python/test/test_Registry.py.
mira-vg Apr 15, 2026
7478502
(ci-python) Adjusted docstring of test/__init__.py.
mira-vg Apr 15, 2026
3ed7f36
(ci-python) Changed quote-style in pyproject.toml to "double".
mira-vg Apr 17, 2026
8132f60
(ci-python) Configured automatic stub and __init__.py generation.
mira-vg Apr 17, 2026
00d13c3
Extended .gitignore: Added VS Code specific files and wild card for .…
mira-vg Apr 24, 2026
bbc84ca
Merge branch 'extend-gitignore' into bindings
mira-vg Apr 24, 2026
73ac190
(ci-python) Added .vscode/settings.json.
mira-vg Apr 24, 2026
2a053b3
(ci-python) Got basic build.rs file without real functionality to work.
mira-vg May 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ celerybeat.pid

# Environments
.env
.venv
env/
venv/
ENV/
.venv*/
env*/
venv*/
ENV*/
env.bak/
venv.bak/

Expand Down Expand Up @@ -188,4 +188,30 @@ pyrightconfig.json

.DS_Store
Thumbs.db
todo.txt
todo.txt


# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode

### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix

### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide

# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode

*.code-workspace
7 changes: 7 additions & 0 deletions crates/ci-python/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"python.testing.pytestArgs": [
"test"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
9 changes: 6 additions & 3 deletions crates/ci-python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ repository = ""

[dependencies]
ci_core = { path = "../ci-core" }
pyo3 = { version = "0.28", features = ["extension-module"] }
numpy = { version = "0.28" }
ndarray = { workspace = true }
numpy = { version = "0.28" }
pyo3 = { version = "0.28", features = ["extension-module"] }
pyo3-stub-gen = "0.22.1"
pyo3-stub-gen-derive = "0.22.1"

[lints]
workspace = true

[lib]
crate-type = ["cdylib"]
name = "ci_python"
crate-type = ["cdylib", "rlib"]
4 changes: 2 additions & 2 deletions crates/ci-python/benchmarks/power_divergence.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from pgmpy.estimators.CITests import pearsonr, power_divergence
import numpy as np
from ci_python import PyRegistry
from ci_python import Registry
import time
import pandas as pd


registry = PyRegistry()
registry = Registry()
test = registry.get_test("pearson_correlation")

N_ITER = 50
Expand Down
83 changes: 83 additions & 0 deletions crates/ci-python/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use std::env;
use std::fs;
use std::path::Path;

fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();

// Struct and impl definitions
let defs_path = Path::new(&out_dir).join("ci_tests.rs");
fs::write(
&defs_path,
r#"
#[gen_stub_pyclass]
#[pyclass(frozen, name = "ChiSquared", module = "ci_python._ci_python")]
pub struct PyChiSquared {
registry: Arc<Registry>,
test_name: String,
}

#[gen_stub_pymethods]
#[pymethods]
impl PyChiSquared {
#[new]
#[must_use]
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self { registry: Arc::new(Registry::new()), test_name: "chi_squared".to_string() }
}

#[allow(clippy::needless_pass_by_value)]
#[pyo3(signature = (z, x, y))]
pub fn __call__(
&self,
py: Python<'_>,
z: PyReadonlyArray2<'_, f64>,
x: PyReadonlyArray1<'_, f64>,
y: PyReadonlyArray1<'_, f64>,
) -> PyResult<Py<PyAny>> {
let z: Array2<f64> = z.as_array().to_owned();
let x: Array1<f64> = x.as_array().to_owned();
let y: Array1<f64> = y.as_array().to_owned();

let test = self
.registry
.get_test(&self.test_name)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;

let result = test
.run_test(x, y, z)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(e.to_string()))?;

match result {
TestResult::Boolean(b) => Ok(b.into_pyobject(py)?.to_owned().into_any().unbind()),
TestResult::PValue(p_value, coefficient) => Ok((p_value, coefficient)
.into_pyobject(py)?
.into_any()
.unbind()),
TestResult::Statistic(p_value, statistic, dof) => Ok((p_value, statistic, dof)
.into_pyobject(py)?
.into_any()
.unbind()),
}
}
}
"#,
)
.unwrap();

// Module registration calls – included inside the #[pymodule_init] fn
let init_path = Path::new(&out_dir).join("ci_tests_init.rs");
fs::write(
&init_path,
r#"
use pyo3::prelude::*;

pub fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<super::_ci_python::PyChiSquared>()?;
Ok(())
}
"#,
)
.unwrap();
}
9 changes: 9 additions & 0 deletions crates/ci-python/ci_python/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""PyO3 bindings for conditional independence testing."""

from ci_python._ci_python import ChiSquared, CITest, Registry

__all__ = [
"CITest",
"ChiSquared",
"Registry",
]
35 changes: 35 additions & 0 deletions crates/ci-python/ci_python/_ci_python/__init__.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# This file is automatically generated by pyo3_stub_gen
# ruff: noqa: E501, F401, F403, F405

import builtins
import numpy
import numpy.typing
import typing
__all__ = [
"CITest",
"ChiSquared",
"Registry",
]

@typing.final
class CITest:
def __call__(self, z: numpy.typing.NDArray[numpy.float64], x: numpy.typing.NDArray[numpy.float64], y: numpy.typing.NDArray[numpy.float64]) -> typing.Any:
r"""
Run the conditional independence test on the given data.

# Errors

Returns `PyRuntimeError` if the test lookup fails or the test itself returns an error.
"""

@typing.final
class ChiSquared:
def __new__(cls) -> ChiSquared: ...
def __call__(self, z: numpy.typing.NDArray[numpy.float64], x: numpy.typing.NDArray[numpy.float64], y: numpy.typing.NDArray[numpy.float64]) -> typing.Any: ...

@typing.final
class Registry:
def __new__(cls) -> Registry: ...
def list_all_tests(self) -> builtins.list[builtins.str]: ...
def get_test(self, test_name: builtins.str) -> CITest: ...

69 changes: 69 additions & 0 deletions crates/ci-python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
[project]
name = "ci-python"
version = "0.1.0"
requires-python = ">=3.10,<3.15"
dependencies = []
classifiers = [
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Operating System :: Unix",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS",
]

[build-system]
requires = ["maturin>=1.11,<2.0"]
build-backend = "maturin"

[tool.maturin]
module-name = "ci_python._ci_python"
python-source = "."

[tool.pyo3-stub-gen]
generate-init-py = false

[project.optional-dependencies]
test = [
"pytest",
]

[tool.ruff]
line-length = 120

# Exclude generated files.
extend-exclude = [
"ci_python/_ci_python",
]

[tool.ruff.lint]
select = ["ALL"]
ignore = [
"ANN002", # missing-type-args: Checks that function *args arguments have type annotations.
"ANN003", # missing-type-kwargs: Checks that function **kwargs arguments have type annotations.
"COM812", # missing-trailing-comma: Checks for the absence of trailing commas.
"E741", # ambiguous-variable-name: Checks for the use of the characters 'l', 'O', or 'I' as variable names.
"EM101", # raw-string-in-exception: Checks for the use of string literals in exception constructors.
"EM102", # f-string-in-exception: Checks for the use of f-strings in exception constructors.
"F403", # undefined-local-with-import-star: Checks for the use of wildcard imports.
"FBT001", # boolean-type-hint-positional-argument: Checks for the use of boolean positional arguments in function definitions, as determined by the presence of a type hint containing bool as an evident subtype - e.g. bool, bool | int, typing.Optional[bool], etc.
"FBT002", # boolean-default-value-positional-argument: Checks for the use of boolean positional arguments in function definitions, as determined by the presence of a boolean default value.
"FBT003", # boolean-positional-value-in-call: Checks for boolean positional arguments in function calls.
"N812", # lowercase-imported-as-non-lowercase: Checks for lowercase imports that are aliased to non-lowercase names.
"PLR0913", # too-many-arguments: Checks for function definitions that include too many arguments.
"PLR1714", # repeated-equality-comparison: Checks for repeated equality comparisons that can be rewritten using the in operator.
"PLR2004", # magic-value-comparison: Checks for the use of unnamed numerical constants ("magic") values in comparisons.
"S101", # assert: Checks for uses of the assert keyword.
"SIM108", # if-else-block-instead-of-if-exp: Check for if-else-blocks that can be replaced with a ternary or binary operator.
"TD002", # missing-todo-author: Checks that a TODO comment includes an author.
"TD003", # missing-todo-link: Checks that a TODO comment is associated with a link to a relevant issue or ticket.
"TRY003", # raise-vanilla-args: Checks for long exception messages that are not defined in the exception class itself.
]

[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.ruff.format]
quote-style = "double"
8 changes: 8 additions & 0 deletions crates/ci-python/src/bin/stub_gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use ci_python::stub_info;
use pyo3_stub_gen::Result;

fn main() -> Result<()> {
let stub = stub_info()?;
stub.generate()?;
Ok(())
}
1 change: 1 addition & 0 deletions crates/ci-python/src/ci_tests_init.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include!(concat!(env!("OUT_DIR"), "/ci_tests_init.rs"));
Loading