Multi-receptor integration with CARNIVAL#

The original CARNIVAL method supports linking multiple receptors to TFs. However, one frequent problem is that CARNIVAL can select a single receptor to connect as many TFs as possible, ignoring the other provided TFs. Even though CORNETO adds specific options to extend CARNIVAL to penalize the depth of trees, which will encourage use of other receptors, in many cases this indirect way is not enough.

Since CORNETO provides a multi-sample CARNIVAL version, we can model the problem where every receptor is a single sample instead. In that case, the joint regularization (lambda > 0) will enforce similarity across the trees that link each receptor to TFs. This provides an optimal strategy for finding the smallest union graph that contains the signalling trees from receptors to TFs.

import corneto as cn
import numpy as np

cn.info()
Installed version:v1.0.0b0
Available backends:CVXPY v1.7.1
Default backend (corneto.opt):CVXPY
Installed solvers:CLARABEL, GUROBI, HIGHS, OSQP, SCIPY, SCS
Graphviz version:v0.21
Installed path:/Users/pablorodriguezmier/Documents/work/projects/corneto/docs/tutorials/carnival/.pixi/envs/default/lib/python3.10/site-packages/corneto
Repository:https://github.com/saezlab/corneto
from corneto.graph import Graph

G = Graph.from_tuples(
    [
        ("rec1", 1, "a"),
        ("rec1", -1, "b"),
        ("rec1", 1, "f"),
        ("rec1", -1, "c"),
        ("rec2", 1, "b"),
        ("rec2", 1, "tf2"),
        ("b", 1, "g"),
        ("g", -1, "d"),
        ("rec2", -1, "d"),
        ("a", 1, "c"),
        ("a", -1, "d"),
        ("c", 1, "d"),
        ("c", -1, "e"),
        ("c", 1, "tf3"),
        ("e", 1, "a"),
        ("d", -1, "c"),
        ("e", 1, "tf1"),
        ("a", -1, "tf1"),
        ("d", 1, "tf2"),
        ("c", -1, "tf2"),
        ("tf1", 1, "tf2"),
        ("tf1", -1, "rec2"),
        ("tf2", 1, "rec1"),
        ("tf1", 1, "f"),
    ]
)
# Plot our PKN
G.plot()
../../_images/aa9477ed7f909b3615d276fde5fa6487a4bfd9166690bb5e834aa75e0a962989.svg
from corneto.methods.future.carnival import CarnivalFlow

samples = {
    "receptor1_sample": {
        "rec1": {"value": 1, "mapping": "vertex", "role": "input"},
        "tf2": {"value": 1, "mapping": "vertex", "role": "output"},
    },
    "receptor2_sample": {
        "rec2": {"value": -1, "mapping": "vertex", "role": "input"},
        "tf1": {"value": 1, "mapping": "vertex", "role": "output"},
        "tf2": {"value": 1, "mapping": "vertex", "role": "output"},
    },
}

data = cn.Data.from_cdict(samples)
data
Data(n_samples=2, n_feats=[2 3])
c = CarnivalFlow(lambda_reg=1e-3)
P = c.build(G, data)
P.solve(verbosity=0, solver="scipy")

for o in P.objectives:
    print(o.value)
Unreachable vertices for sample: 0
Unreachable vertices for sample: 0
0.0
0.0
8.0
P.expr
{'edge_inhibits': edge_inhibits: Variable((25, 2), edge_inhibits, boolean=True),
 '_flow': _flow: Variable((25,), _flow),
 'edge_activates': edge_activates: Variable((25, 2), edge_activates, boolean=True),
 'const0x13b0fa60bb3aa529': const0x13b0fa60bb3aa529: Constant(CONSTANT, NONNEGATIVE, (10, 25)),
 '_dag_layer': _dag_layer: Variable((10, 2), _dag_layer),
 'const0x147754b80d2a59f6': const0x147754b80d2a59f6: Constant(CONSTANT, NONNEGATIVE, (10, 25)),
 'edge_has_signal_OR': edge_has_signal_OR: Variable((25,), edge_has_signal_OR, boolean=True),
 'flow': _flow: Variable((25,), _flow),
 'vertex_value': Expression(AFFINE, UNKNOWN, (10, 2)),
 'vertex_activated': Expression(AFFINE, NONNEGATIVE, (10, 2)),
 'vertex_inhibited': Expression(AFFINE, NONNEGATIVE, (10, 2)),
 'edge_value': Expression(AFFINE, UNKNOWN, (25, 2)),
 'edge_has_signal': Expression(AFFINE, NONNEGATIVE, (25, 2)),
 'vertex_max_depth': _dag_layer: Variable((10, 2), _dag_layer)}
c.processed_graph.plot_values(vertex_values=P.expr.vertex_value.value[:, 0], edge_values=P.expr.edge_value.value[:, 0])
../../_images/b93d9a4e185f524d89a565b7eb8b770da6373197e7869c9de48f63100eca4df8.svg
c.processed_graph.plot_values(vertex_values=P.expr.vertex_value.value[:, 1], edge_values=P.expr.edge_value.value[:, 1])
../../_images/c2ce46303dc01be9d27ef18faa3e2230c57c5cdd0f53d3f7257ca2103b94651d.svg
# Plot the union of the solutions
c.processed_graph.edge_subgraph(np.flatnonzero(P.expr.edge_has_signal_OR.value)).plot()
../../_images/e14a6bf7682c375a0a2973d04f7139b1e5652f4709d7230e5e25df0c0cb8c84d.svg
# Plot carnival tree for sample 1 (receptor 1)
c.processed_graph.edge_subgraph(np.flatnonzero(P.expr.edge_has_signal.value[:, 0])).plot()
../../_images/3a5b16c1e8592becf1dd9d1834ae49a1fd5f9a5e5ea6265f03872419ce9ee024.svg
# Plot carnival  tree for sample 2 (receptor 2)
c.processed_graph.edge_subgraph(np.flatnonzero(P.expr.edge_has_signal.value[:, 1])).plot()
../../_images/942dbbde4f9beea1a51d1a0a4682c40b6ec1d821d405c8385dbbc840a2f76896.svg