This is a fork from the R package pcalg
that implements a C++ optimization of the
function idaFast, idaFastOpt. Using C++ together
with the Armadillo library instead of R improves the
performance already. On top of that, the new implementation
seems to have reduced the complexity of the algorithm (see
Details and Benchmarks).
- Patch file:
dataOpt/patch_pcalg.txt
Given a completed partially directed acyclic graph pdag, and
a given node x, we identify the unique parents uniq_pa and the
ambiguous parents amb_pa. From the set of amb_pa, we direct them
in all possible ways (2|amb_pa|) checking first that
they introduce no new colliders. This implementation differs with
the original implementation in how "all possible ways" are examined.
-
Original code: A loop from 1 to the number of elements in
amb_pa,|amb_pa|is executed, and the function,pa.tmp <- combn(amb_pa,i,simplify = TRUE)is called where
iis the variable in the loop. All sets of sizeiinamb_paare obtained. For every set, all parents in it are directed towardsx, the ones not in it outwards. If the configuration does not introduce a new collider, the causal effects are computed, nothing is done otherwise. -
Optimization: A loop from 0 until
2|amb_pa| - 1is executed, where binary representation ofi(loop variable,long int) is sweeping over the half posibilities. For everyiwe create two subsetspa_1andpa_2complementary to each other.pa_1(pa_2) contains thejthelement ofamb_paif thejthbit ofiis 1 (0). In that way, we have covered all possibilities if we first setpa_1to be directed towardsx,pa_2outwards, check for colliders, compute causal effects and then do the same reversing the roles ofpa_1andpa_2.
Comparison of performance of idaFast (original function of the package)
versus our implementation, idaFastOpt. We have used real data and created cpdag of
100,200,500,1000,5000 nodes. idaFast and idaFastOpt have then been run on these
graphs on a Intel(R) Xeon(R) CPU E5-4620 0 @ 2.20GHz. In the graph below, we can see
the performance of the two functions, in a log-log scale.
We see not only that the runtime is significantly reduced, but also, and
for the cpdag we were testing the functions on, idaFast seemed to scale like
O(n3) whereas idaFastOpt like O(n2). Note that
the scaling is not generalizable to all types of graphs. For different input graphs,
one can expect a different complexity. Nevertheless, it seems that the new implementation
has reduced it.
idaFastOpt(x.pos.set = 1:N, mcov = cov(subDat), graphEst = l$cpdag_graph)
-
Arguments:
x.pos.set: (integer vector) optional argument. Nodes on which the causal effects are going to be computed. If not passed, the function computes the causal effects on all nodes. Note a vector of positions of the target variables is not passed as an argument, in contrast toidaFastsince in this function all nodes are going to be targets.mcov: covariance matrix that was used to estimategraphEstgraphEst: estimated cpdag from the functionpc. If the output ofpcispc.fit, then the estimated cpdag can be obtained bypc.fit@graph.
-
Output: list of causal values; one list element (matrix) for each element of
x.pos.set
In the folder dataOpt, one can find a code example in run_idaFast.R,
where both functions, idaFast, idaFastOpt, are run for the cpdag's
contained in dataOpt/skeletonfiles, and a profiling is performed.
In the same folder, the script benchmarks.R creates the plot shown in this README.md
file.
