Skip to content

Commit 9d13ca8

Browse files
authored
Merge pull request #16 from pablormier/dev
add back old functions from graph class
2 parents 6644014 + a66bf30 commit 9d13ca8

9 files changed

Lines changed: 1109 additions & 515 deletions

File tree

corneto/graph/_graph.py

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111
Optional,
1212
Set,
1313
Tuple,
14+
Union,
1415
)
1516

16-
from corneto._types import Edge
17+
import numpy as np
18+
19+
from corneto._io import import_cobra_model
20+
from corneto._types import CobraModel, Edge, NxDiGraph, NxGraph
1721

1822
from ._base import Attr, Attributes, BaseGraph, EdgeType
1923

@@ -616,6 +620,27 @@ def from_json(cls, json_str: str) -> "Graph":
616620
data = json.loads(json_str)
617621
return cls.from_dict(data)
618622

623+
@staticmethod
624+
def from_networkx(G: Union[NxGraph, NxDiGraph]):
625+
"""Create graph from NetworkX graph.
626+
627+
Args:
628+
G: NetworkX graph instance
629+
630+
Returns:
631+
Graph instance with equivalent structure
632+
"""
633+
Gc = Graph()
634+
is_directed = G.is_directed()
635+
for edge in G.edges():
636+
e_data = G.get_edge_data(edge[0], edge[1], default=dict())
637+
if is_directed:
638+
e_data[Attr.EDGE_TYPE.value] = EdgeType.DIRECTED.value
639+
else:
640+
e_data[Attr.EDGE_TYPE.value] = EdgeType.UNDIRECTED.value
641+
Gc.add_edge(edge[0], edge[1], **e_data)
642+
return Gc
643+
619644
def save_json(
620645
self,
621646
filepath: str,
@@ -686,3 +711,139 @@ def load_json(cls, filepath: str, compression: Optional[str] = "auto") -> "Graph
686711

687712
# Create graph from JSON string
688713
return cls.from_json(json_str)
714+
715+
# DEPRECATED
716+
717+
@staticmethod
718+
def from_vertex_incidence(
719+
A: np.ndarray,
720+
vertex_ids: Union[List[str], np.ndarray],
721+
edge_ids: Union[List[str], np.ndarray],
722+
):
723+
"""Create graph from vertex incidence matrix and labels.
724+
725+
Args:
726+
A: Vertex incidence matrix. Rows are vertices, columns are edges.
727+
Non-zero entries indicate edge-vertex connections.
728+
vertex_ids: Labels for vertices corresponding to matrix rows
729+
edge_ids: Labels for edges corresponding to matrix columns
730+
731+
Returns:
732+
Graph instance constructed from incidence matrix
733+
734+
Raises:
735+
ValueError: If dimensions of inputs don't match
736+
"""
737+
g = Graph()
738+
if len(vertex_ids) != A.shape[0]:
739+
raise ValueError(
740+
"""The number of rows in A matrix is different from
741+
the number of vertex ids"""
742+
)
743+
if len(edge_ids) != A.shape[1]:
744+
raise ValueError(
745+
"""The number of columns in A matrix is different from
746+
the number of edge ids"""
747+
)
748+
for v in vertex_ids:
749+
g.add_vertex(v)
750+
for j, v in enumerate(edge_ids):
751+
values = A[:, j]
752+
idx = np.flatnonzero(values)
753+
coeffs = values[idx]
754+
v_names = [vertex_ids[i] for i in idx]
755+
s = {n: val for n, val in zip(v_names, coeffs) if val < 0}
756+
t = {n: val for n, val in zip(v_names, coeffs) if val > 0}
757+
g.add_edge(s, t, id=v)
758+
return g
759+
760+
@staticmethod
761+
def from_sif(
762+
sif_file: str,
763+
delimiter: str = "\t",
764+
has_header: bool = False,
765+
discard_self_loops: Optional[bool] = True,
766+
column_order: List[int] = [0, 1, 2],
767+
):
768+
"""Create graph from Simple Interaction Format (SIF) file.
769+
770+
Args:
771+
sif_file: Path to SIF file
772+
delimiter: Column delimiter in file
773+
has_header: Whether file has a header row
774+
discard_self_loops: Whether to ignore self-loops
775+
column_order: Order of source, interaction, target columns
776+
777+
Returns:
778+
New Graph loaded from SIF file
779+
"""
780+
from corneto._io import _read_sif_iter
781+
782+
it = _read_sif_iter(
783+
sif_file,
784+
delimiter=delimiter,
785+
has_header=has_header,
786+
discard_self_loops=discard_self_loops,
787+
column_order=column_order,
788+
)
789+
return Graph.from_sif_tuples(it)
790+
791+
@staticmethod
792+
def from_sif_tuples(tuples: Iterable[Tuple]):
793+
"""Create graph from iterable of SIF tuples.
794+
795+
Args:
796+
tuples: Iterable of (source, interaction, target) tuples
797+
798+
Returns:
799+
New Graph created from SIF data
800+
"""
801+
g = Graph()
802+
for s, v, t in tuples:
803+
g.add_edge(s, t, interaction=v)
804+
return g
805+
806+
@staticmethod
807+
def from_cobra_model(model: CobraModel):
808+
"""Create graph from COBRA metabolic model.
809+
810+
Args:
811+
model: COBRApy model instance
812+
813+
Returns:
814+
New Graph representing the metabolic network
815+
"""
816+
S, R, M = import_cobra_model(model)
817+
G = Graph.from_vertex_incidence(S, M["id"], R["id"])
818+
# Add metadata to the graph, such as default lb/ub for reactions
819+
for i in range(G.num_edges):
820+
attr = G.get_attr_edge(i)
821+
attr["default_lb"] = R["lb"][i]
822+
attr["default_ub"] = R["ub"][i]
823+
attr["GPR"] = R["gpr"][i]
824+
return G
825+
826+
@staticmethod
827+
def from_miom_model(model):
828+
"""Create graph from MIOM metabolic model.
829+
830+
Args:
831+
model: MIOM model instance or path to compressed model file
832+
833+
Returns:
834+
New Graph representing the metabolic network
835+
"""
836+
if isinstance(model, str):
837+
from corneto._io import _load_compressed_gem
838+
839+
S, R, M = _load_compressed_gem(model)
840+
else:
841+
S = model.S, M = model.M, R = model.R
842+
G = Graph.from_vertex_incidence(S, M["id"], R["id"])
843+
# Add metadata to the graph, such as default lb/ub for reactions
844+
for i in range(G.num_edges):
845+
attr = G.get_attr_edge(i)
846+
attr["default_lb"] = R["lb"][i]
847+
attr["default_ub"] = R["ub"][i]
848+
attr["GPR"] = R["gpr"][i]
849+
return G

corneto/methods/future/carnival.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,25 @@ def create_flow_based_problem(self, flow_problem, graph: BaseGraph, data: Data):
416416
return problem
417417

418418
@staticmethod
419-
def get_citations():
419+
def references():
420420
"""Returns citation keys for this method.
421421
422422
Returns:
423423
A list of citation keys that can be used to lookup BibTeX entries.
424424
"""
425425
return ["rodriguez2024unified", "liu2019expression"]
426+
427+
@staticmethod
428+
def description():
429+
"""Returns a description of the method.
430+
431+
Returns:
432+
A string describing the method.
433+
"""
434+
return (
435+
"Method extending the original CARNIVAL for intracellular network inference "
436+
"that uses integer linear programming to model signal propagation."
437+
)
426438

427439

428440
class CarnivalILP(Method):
@@ -676,7 +688,7 @@ def create_problem(self, graph: BaseGraph, data: Data):
676688
return P
677689

678690
@staticmethod
679-
def get_citations():
691+
def references():
680692
"""Returns citation keys for this method.
681693
682694
Returns:

corneto/methods/future/fba.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,5 +279,5 @@ def create_flow_based_problem(self, flow_problem, graph: BaseGraph, data: Data):
279279
return flow_problem
280280

281281
@staticmethod
282-
def get_citations():
282+
def references():
283283
return ["savinell1992network", "rodriguez2024unified"]

corneto/methods/future/imat.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,5 +318,5 @@ def create_flow_based_problem(self, flow_problem, graph: BaseGraph, data: Data):
318318
return flow_problem
319319

320320
@staticmethod
321-
def get_citations():
321+
def references():
322322
return ["shlomi2008network", "rodriguez2024unified"]

corneto/methods/future/method.py

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ class Method(ABC):
2222
backend: Optimization backend to use. Defaults to DEFAULT_BACKEND.
2323
"""
2424

25+
__show_info_on_import__ = False
26+
2527
def __init__(
2628
self,
2729
lambda_reg: float = 0.0,
@@ -43,6 +45,93 @@ def __init__(
4345
self.processed_graph = None
4446
self.disable_structured_sparsity = disable_structured_sparsity
4547

48+
def __repr__(self) -> str:
49+
"""Returns a string representation of the method.
50+
51+
Includes the method name, description, parameters, and formatted citations (if any).
52+
53+
Returns:
54+
A formatted string representation of the method.
55+
"""
56+
import inspect
57+
58+
from corneto.utils._citations import format_references_plaintext
59+
60+
name = self.name()
61+
description = self.description()
62+
citation_keys = self.references()
63+
64+
repr_str = f"{name or self.__class__.__name__}"
65+
66+
if description:
67+
repr_str += f": {description})"
68+
69+
# Add parameters information
70+
repr_str += "\n\nParameters:"
71+
# Get all instance attributes that don't start with _ (except _backend)
72+
params = {}
73+
for attr_name, attr_value in self.__dict__.items():
74+
if attr_name == "_backend":
75+
params["backend"] = attr_value.__class__.__name__
76+
elif not attr_name.startswith("_") or attr_name in [
77+
"_reg_varname",
78+
"_reg_varname_suffix",
79+
]:
80+
# For problem, processed_data, etc. that could be None initially, skip them
81+
if (
82+
attr_name in ["problem", "processed_data", "processed_graph"]
83+
and attr_value is None
84+
):
85+
continue
86+
# Skip complex objects that aren't useful for a summary
87+
if (
88+
isinstance(attr_value, (list, dict, tuple))
89+
and len(str(attr_value)) > 100
90+
):
91+
continue
92+
# Format the parameter name (remove leading underscore if present)
93+
param_name = attr_name[1:] if attr_name.startswith("_") else attr_name
94+
params[param_name] = attr_value
95+
96+
# Get signature to determine default values
97+
signature = None
98+
try:
99+
signature = inspect.signature(self.__class__.__init__)
100+
except (ValueError, TypeError):
101+
pass
102+
103+
if params:
104+
for name, value in sorted(params.items()):
105+
# Check if this is a default value
106+
is_default = False
107+
if signature and name in signature.parameters:
108+
param = signature.parameters[name]
109+
if param.default is not param.empty:
110+
if param.default == value:
111+
is_default = True
112+
113+
# Format the parameter value
114+
if isinstance(value, (int, float, bool, str)) or value is None:
115+
value_str = str(value)
116+
elif hasattr(value, "__class__"):
117+
value_str = f"<{value.__class__.__name__}>"
118+
else:
119+
value_str = str(value)
120+
121+
# Add default indicator if applicable
122+
if is_default:
123+
repr_str += f"\n {name} = {value_str} (default)"
124+
else:
125+
repr_str += f"\n {name} = {value_str}"
126+
else:
127+
repr_str += "\n No parameters"
128+
129+
if citation_keys:
130+
repr_str += "\n\nReferences:"
131+
repr_str += format_references_plaintext(citation_keys)
132+
133+
return repr_str
134+
46135
@abstractmethod
47136
def preprocess(self, graph: BaseGraph, data: Data) -> Tuple[BaseGraph, Data]:
48137
"""Preprocess the input graph and dataset before optimization.
@@ -131,7 +220,24 @@ def build(self, graph: BaseGraph, data: Data) -> ProblemDef:
131220
return self.problem
132221

133222
@staticmethod
134-
def get_citations() -> list:
223+
def name() -> str:
224+
"""Returns the name of the method.
225+
226+
Returns:
227+
The name of the optimization method.
228+
"""
229+
return ""
230+
231+
def description(self) -> str:
232+
"""Returns a description of the method.
233+
234+
Returns:
235+
A string describing the optimization method.
236+
"""
237+
return ""
238+
239+
@staticmethod
240+
def references() -> list:
135241
"""Returns citation keys for this method.
136242
137243
Returns:
@@ -140,16 +246,23 @@ def get_citations() -> list:
140246
return []
141247

142248
@classmethod
143-
def show_citations(cls):
249+
def show_references(cls):
144250
"""Display formatted citations in a Jupyter notebook."""
145-
from corneto.utils._citations import show_citations
146-
show_citations(cls.get_citations())
251+
from corneto.utils._citations import show_references
252+
253+
show_references(cls.references())
147254

148255
@classmethod
149256
def show_bibtex(cls):
150257
"""Display raw BibTeX entries in a formatted block for easy copying."""
151258
from corneto.utils._citations import show_bibtex
152-
show_bibtex(cls.get_citations())
259+
260+
show_bibtex(cls.references())
261+
262+
@classmethod
263+
def about(cls):
264+
"""Display information about the method in a Jupyter notebook."""
265+
cls.show_references()
153266

154267
@property
155268
def backend(self):

0 commit comments

Comments
 (0)