andrey.core.ci_cmiknn.CMIknn#
- class andrey.core.ci_cmiknn.CMIknn(data, *, k_cmi=0.1, k_perm=5, n_permutations=500, seed=None)[source]#
Conditional mutual information test with a nearest-neighbor estimator (CMIknn).
Each query estimates the conditional mutual information of
xandygiven the conditioning set from nearest-neighbor distances, then compares it with estimates on copies of the data wherexis shuffled only among rows close in the conditioning set. That keeps the dependence ofxon the conditioning set and removes any other.- Parameters:
data (
ndarray) – Observed data, one row per sample. Each column is replaced by its ranks.k_cmi (
float) – Nearest neighbors of the estimator: a fraction of the rows in(0, 1), or a count, an int from 1 to one less than the rows. More neighbors lower the estimate’s variance and raise its bias toward zero, which costs power as the conditioning set grows.k_perm (
int) – Nearest neighbors in the conditioning set among whichxis shuffled.n_permutations (
int) – Shuffled copies that form the null distribution.seed (
int|None) – Seed for the tie-breaking and the shuffles.NoneusesANDREY_SEED, or0when it is unset.
- Raises:
ValueError – If
datais not 2-D, has fewer than 2 rows, or contains NaN or infinity,k_cmiis neither a fraction in(0, 1)nor an int from 1 to one less than the rows, ork_permorn_permutationsis below 1.
Notes
How it works. Each column is replaced by its ranks, ties ordered at random, and distances are in the maximum norm. With
kthe neighbor count (round(k_cmi n)for a fraction),eps_ithe distance from rowito itsk-th nearest row in the space ofx,y, and the conditioning setz, andk_xz,k_yz,k_zthe rows strictly closer thaneps_iin each subspace (rowicounted), the estimate isI = psi(k) + mean_i [psi(k_z) - psi(k_xz) - psi(k_yz)],psithe digamma function [1]. Each null copy maps every row to one of itsk_permnearest rows inz, avoiding a row already taken where it can. Withbof theBcopies estimating at leastI, the p-value is(b + 1) / (B + 1): the observed data count as one more draw, so the p-value is never zero [3]. In the skeleton searches of PC, FCI, GFCI, and CD-NOD, a query’s shuffles stop oncep > alphaorp <= alphais certain whatever the remaining shuffles give, so the search decides as with every shuffle; where the search orients colliders by p-value (collider_order="p_value"), onlyp <= alphastops them, so every p-value it ranks is the full one. An empty conditioning set shufflesxover all rows, and the estimate becomes the mutual information estimate ofxandy[2]. The defaultk_cmi, a tenth of the rows, is the low end of the range [1] recommends; a larger share smooths the estimate, which costs power as the conditioning set grows.statistic()returns the estimateIitself, in nats, for use as a dependence measure. It is biased on finite samples, so compare estimates at the same rows andk: on Gaussian pairs with 1,000 rows andk = 5it reads 0.01 to 0.05 above the true value, independent pairs included (0.03 to 0.09 with 200 rows), and withka tenth of the rows it reads near zero for independent pairs and low for strong dependence, 0.06 below at a correlation of 0.9 and 0.26 below given one more variable. The p-value comparesIwith shuffled copies, which share the bias.Of
xandy, the one with the lowertie_ranks()rank, a rank computed from each column’s own data, is shuffled. The tie-breaking is drawn once, fromseed, when the test is built, and each column takes its keys by that rank. The shuffles come from a generator seeded byseedand the ranks of the conditioning set, so every query with the same conditioning set uses the same shuffles, and the test object keeps them for later queries. A p-value does not depend on the order of the queries, of the arguments, or of the columns.The distances and neighbor counts run on a CUDA GPU under the
cudabackend, or underautofromANDREY_CMIKNN_GPU_THRESHOLDrows squared, a batch of shuffles at a time. The counts are integers, so the p-value equals the CPU’s.Limitations. Measured in PC on simulated data from sparse random graphs (mean degree 2), each variable a weighted sum of
z + sin(z), or ofcos(z), over its parents plus Gaussian noise, with 20 datasets per size on 4 cores. At 5 variables with 500 to 2,000 rows, 10 with 500 and 1,000, and 20 with 500, its median errors (SHD) stayed below the empty graph’s at every size. The median fit took 47 seconds at 5 variables and 500 rows, and 12 to 14 minutes at 5 variables with 2,000 rows, 10 with 1,000, and 20 with 500. Each query computesn_permutations + 1estimates, each overn x ndistance matrices, so its time grows with the square of the number of rows times the permutations, and its memory with the square of the rows: hundreds of rows take seconds per query, and tens of thousands do not fit. The p-value moves withseed, is a multiple of1 / (n_permutations + 1), and is at least that.References
- statistic(x, y, condition_set=())[source]#
Return the estimate of the conditional mutual information of
xandygivencondition_set, in nats.The estimate the p-value compares with its shuffles, from the test’s ranks (see Notes); with an empty
condition_set, the mutual information estimate. It does not depend on the order of the arguments.- Return type: