示例#1
0
def alter_weights(gdag: GaussDAG,
                  prob_altered: float = None,
                  num_altered: int = None,
                  rand_weight_fn=unif_away_original):
    """
    Return a copy of a GaussDAG with some of its arc weights randomly altered by `rand_weight_fn`.

    Parameters
    ----------
    gdag:
        GaussDAG
    prob_altered:
        Probability each arc has its weight altered.
    num_altered:
        Number of arcs whose weights are altered.
    rand_weight_fn:
        Function to generate random weights, given the original weight.
    """
    if num_altered is None and prob_altered is None:
        raise ValueError(
            "Must specify at least one of `percent_altered` or `num_altered`.")
    num_altered = num_altered if num_altered is not None else np.random.binomial(
        gdag.num_arcs, prob_altered)
    altered_arcs = random.sample(list(gdag.arcs), num_altered)
    new_gdag = gdag.copy()
    weights = gdag.arc_weights
    for i, j in altered_arcs:
        new_gdag.set_arc_weight(i, j, rand_weight_fn(weights[(i, j)]))
    return new_gdag
示例#2
0
    def to_gauss_dag(self, perm):
        """
        Return a GaussDAG with the same mean and covariance as this GGM, and is a minimal IMAP of this GGM
        consistent with the node ordering `perm`.

        Parameters
        ----------
        perm:
            The desired permutation, or total order, of the nodes in the result.

        Returns
        -------

        Examples
        --------
        TODO
        """
        from causaldag import DAG, GaussDAG

        d = DAG(nodes=self.nodes)
        ixs = list(
            itr.chain.from_iterable(
                ((f, s) for f in range(s)) for s in range(len(perm))))
        for i, j in ixs:
            pi_i, pi_j = perm[i], perm[j]
            if not np.isclose(
                    self.partial_correlation(pi_i, pi_j,
                                             d.markov_blanket(pi_i)), 0):
                d.add_arc(pi_i, pi_j, unsafe=True)

        arcs = dict()
        means = []
        Sigma = self.covariance
        variances = []
        for i in perm:
            ps = list(d.parents_of(i))

            # === LINEAR REGRESSION TO FIND EDGE WEIGHTS
            S_xx = Sigma[np.ix_(ps, ps)]
            S_xy = Sigma[ps, i]
            coeffs = inv(S_xx) @ S_xy

            # === COMPUTE MEAN AND VARIANCE
            mean = self.means[i] - self.means[ps] @ coeffs.T
            variance = Sigma[i, i] - Sigma[i, ps] @ coeffs

            for p, coeff in zip(ps, coeffs):
                print(p, i)
                arcs[(p, i)] = coeff
            means.append(mean)
            variances.append(variance)

        return GaussDAG(list(range(self.num_nodes)),
                        arcs,
                        means=means,
                        variances=variances)
示例#3
0
def rand_weights(dag, rand_weight_fn=unif_away_zero) -> GaussDAG:
    """
    Generate a GaussDAG from a DAG, with random edge weights independently drawn from `rand_weight_fn`.

    Parameters
    ----------
    dag:
        DAG
    rand_weight_fn:
        Function to generate random weights.

    Examples
    --------
    >>> d = cd.DAG(arcs={(1, 2), (2, 3)})
    >>> g = cd.rand.rand_weights(d)
    """
    weights = rand_weight_fn(size=len(dag.arcs))
    return GaussDAG(nodes=list(range(len(dag.nodes))),
                    arcs=dict(zip(dag.arcs, weights)))
示例#4
0
def alter_weights(gdag: GaussDAG,
                  prob_altered: float = None,
                  num_altered: int = None,
                  prob_added: float = None,
                  num_added: int = None,
                  prob_removed: float = None,
                  num_removed: int = None,
                  rand_weight_fn=unif_away_zero,
                  rand_change_fn=unif_away_original):
    """
    Return a copy of a GaussDAG with some of its arc weights randomly altered by `rand_weight_fn`.

    Parameters
    ----------
    gdag:
        GaussDAG
    prob_altered:
        Probability each arc has its weight altered.
    num_altered:
        Number of arcs whose weights are altered.
    prob_added:
        Probability that each missing arc is added.
    num_added:
        Number of missing arcs added.
    prob_removed:
        Probability that each arc is removed.
    num_removed:
        Number of arcs removed.
    rand_weight_fn:
        Function that returns a random weight for each new edge.
    rand_change_fn:
        Function that takes the current weight of an edge and returns the new weight.
    """
    if num_altered is None and prob_altered is None:
        raise ValueError(
            "Must specify at least one of `prob_altered` or `num_altered`.")
    if num_added is None and prob_added is None:
        raise ValueError(
            "Must specify at least one of `prob_added` or `num_added`.")
    if num_removed is None and prob_removed is None:
        raise ValueError(
            "Must specify at least one of `prob_removed` or `num_removed`.")
    if num_altered + num_removed > gdag.num_arcs:
        raise ValueError(
            f"Tried altering {num_altered} arcs and removing {num_removed} arcs, but there are only {gdag.num_arcs} arcs in this DAG."
        )
    num_missing_arcs = comb(gdag.nnodes, 2) - gdag.num_arcs
    if num_added > num_missing_arcs:
        raise ValueError(
            f"Tried adding {num_added} arcs but there are only {num_missing_arcs} arcs missing from the DAG."
        )

    # GET NUMBER ADDED/CHANGED/REMOVED
    num_altered = num_altered if num_altered is not None else np.random.binomial(
        gdag.num_arcs, prob_altered)
    num_removed = num_removed if num_removed is not None else np.random.binomial(
        gdag.num_arcs, prob_removed)
    num_removed = min(num_removed, gdag.num_arcs - num_altered)
    num_added = num_added if num_added is not None else np.random.binomial(
        num_missing_arcs, prob_added)

    # GET ACTUAL ARCS THAT ARE ADDED/CHANGED/REMOVED
    altered_arcs = random.sample(list(gdag.arcs), num_altered)
    removed_arcs = random.sample(list(gdag.arcs - set(altered_arcs)),
                                 num_removed)
    valid_arcs_to_add = set(itr.combinations(gdag.topological_sort(),
                                             2)) - gdag.arcs
    added_arcs = random.sample(list(valid_arcs_to_add), num_added)

    # CREATE NEW DAG
    new_gdag = gdag.copy()
    weights = gdag.arc_weights
    for i, j in altered_arcs:
        new_gdag.set_arc_weight(i, j, rand_change_fn(weights[(i, j)]))
    for i, j in removed_arcs:
        new_gdag.remove_arc(i, j)
    new_weights = rand_weight_fn(size=num_added)
    for (i, j), val in zip(added_arcs, new_weights):
        new_gdag.set_arc_weight(i, j, val)

    return new_gdag
示例#5
0
            mean = self.means[i] - self.means[ps] @ coeffs.T
            variance = Sigma[i, i] - Sigma[i, ps] @ coeffs

            for p, coeff in zip(ps, coeffs):
                print(p, i)
                arcs[(p, i)] = coeff
            means.append(mean)
            variances.append(variance)

        return GaussDAG(list(range(self.num_nodes)),
                        arcs,
                        means=means,
                        variances=variances)


if __name__ == '__main__':
    P = np.eye(3)
    P[0, 1] = P[1, 0] = .1
    P[2, 1] = P[1, 2] = .1
    g = GGM(P)
    samples = g.sample(1000)
    cov_sample = np.cov(samples, rowvar=False)

    from causaldag import GaussDAG
    A = np.array([[0, .1, 0], [0, 0, .1], [0, 0, 0]])
    gdag = GaussDAG.from_amat(A)
    ggm = GGM.from_covariance(gdag.covariance)
    gdag2 = ggm.to_gauss_dag([0, 1, 2])
    print(gdag.weight_mat)
    print(gdag2.weight_mat)