devansh152 commited on
Commit
ef1decc
·
verified ·
1 Parent(s): 5c96b2b

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +193 -0
app.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import pipeline
2
+ import gradio as gr
3
+ import networkx as nx
4
+ import sympy as sp
5
+ from collections import defaultdict
6
+ import re
7
+ from dotenv import load_dotenv
8
+ import google.generativeai as genai
9
+ import os
10
+ from gradio.themes import Ocean
11
+
12
+ load_dotenv()
13
+ API_KEY = os.getenv("GEMINI_API")
14
+ genai.configure(api_key=API_KEY)
15
+
16
+ # Initialize the Gemini Flash Model
17
+ model = genai.GenerativeModel('gemini-2.5-flash-lite-preview-06-17')
18
+
19
+
20
+ # Gradio App with Support for Multi-Reactant Networks (e.g. A + B -> AB)
21
+
22
+ # --- Parsing Functions ---
23
+ def parse_species(expr):
24
+ # e.g., "A + B" -> ["A", "B"]
25
+ return [s.strip() for s in re.split(r'\s*[\+\-]\s*', expr)]
26
+
27
+ def parse_network(input_string):
28
+ edges = []
29
+ reversible_edges = []
30
+
31
+ for part in input_string.split(','):
32
+ part = part.strip()
33
+ if '<->' in part:
34
+ lhs, rhs = part.split('<->')
35
+ lhs_species = parse_species(lhs)
36
+ rhs_species = parse_species(rhs)
37
+ reversible_edges.append((lhs_species, rhs_species))
38
+ elif '->' in part:
39
+ lhs, rhs = part.split('->')
40
+ lhs_species = parse_species(lhs)
41
+ rhs_species = parse_species(rhs)
42
+ edges.append((lhs_species, rhs_species))
43
+
44
+ return edges, reversible_edges
45
+
46
+ def build_graph(edges, reversible_edges):
47
+ G = nx.DiGraph()
48
+ for a, b in edges:
49
+ lhs = " + ".join(a)
50
+ rhs = " + ".join(b)
51
+ G.add_edge(lhs, rhs)
52
+ for a, b in reversible_edges:
53
+ lhs = " + ".join(a)
54
+ rhs = " + ".join(b)
55
+ G.add_edge(lhs, rhs)
56
+ G.add_edge(rhs, lhs)
57
+ return G
58
+
59
+ def analyze_graph(G):
60
+ return {
61
+ "nodes": list(G.nodes),
62
+ "edges": list(G.edges),
63
+ "num_nodes": G.number_of_nodes(),
64
+ "num_edges": G.number_of_edges(),
65
+ "is_cyclic": not nx.is_directed_acyclic_graph(G)
66
+ }
67
+
68
+ # --- ODE Generator for Complex Reactions ---
69
+ def mass_action_odes(edges, reversible_edges):
70
+ species = set()
71
+ odes = defaultdict(lambda: 0)
72
+ rate_counter = 1
73
+
74
+ def term(species_list):
75
+ term_expr = 1
76
+ for s in species_list:
77
+ sym = sp.symbols(s)
78
+ species.add(sym)
79
+ term_expr *= sym
80
+ return term_expr
81
+
82
+ for lhs_species, rhs_species in edges:
83
+ k = sp.symbols(f'k{rate_counter}')
84
+ rate_counter += 1
85
+
86
+ flux = k * term(lhs_species)
87
+ for s in lhs_species:
88
+ sym = sp.symbols(s)
89
+ odes[sym] -= flux
90
+ for s in rhs_species:
91
+ sym = sp.symbols(s)
92
+ odes[sym] += flux
93
+
94
+ for lhs_species, rhs_species in reversible_edges:
95
+
96
+ kf = sp.symbols(f'k{rate_counter}')
97
+ rate_counter += 1
98
+ kr = sp.symbols(f'k{rate_counter}')
99
+ rate_counter += 1
100
+
101
+ forward_flux = kf * term(lhs_species)
102
+ reverse_flux = kr * term(rhs_species)
103
+
104
+ for s in lhs_species:
105
+ sym = sp.symbols(s)
106
+ odes[sym] -= forward_flux
107
+ odes[sym] += reverse_flux
108
+ for s in rhs_species:
109
+ sym = sp.symbols(s)
110
+ odes[sym] += forward_flux
111
+ odes[sym] -= reverse_flux
112
+
113
+ return dict(odes)
114
+
115
+ def format_odes(odes):
116
+ return "\n".join([f"d{var}/dt = {sp.simplify(expr)}" for var, expr in odes.items()])
117
+
118
+ def compute_jacobian(odes):
119
+ variables = list(odes.keys())
120
+ F = sp.Matrix([odes[var] for var in variables])
121
+ J = F.jacobian(variables)
122
+ return sp.pretty(J)
123
+
124
+ def extract_network_from_image(image):
125
+ prompt = (
126
+ "Analyze this network diagram and list the network only. "
127
+ "Use reaction format like 'A + B -> C' or 'X <-> Y'. "
128
+ "List multiple reactions separated by commas."
129
+ )
130
+ gemini_response = model.generate_content([prompt, image])
131
+ return gemini_response.text.strip()
132
+
133
+
134
+ def full_process(image, text_input, query):
135
+ if text_input.strip(): # If text is given, use it
136
+ network_description = text_input.strip()
137
+ elif image is not None: # Else if image is given, extract network from image
138
+ network_description = extract_network_from_image(image)
139
+ else:
140
+ return "❌ Please provide either a network image or a textual description."
141
+
142
+ # Step 2: Process extracted/generated network
143
+ return process_network(network_description, query)
144
+
145
+
146
+ qa = pipeline("text2text-generation", model="google/flan-t5-base")
147
+ def process_network(input_string, query):
148
+ edges, reversible_edges = parse_network(input_string)
149
+ G = build_graph(edges, reversible_edges)
150
+ info = analyze_graph(G)
151
+
152
+ if 'ode' in query.lower():
153
+ ode_sys = mass_action_odes(edges, reversible_edges)
154
+ return format_odes(ode_sys)
155
+
156
+ elif 'jacobian' in query.lower():
157
+ ode_sys = mass_action_odes(edges, reversible_edges)
158
+ return f"Jacobian Matrix:\n{compute_jacobian(ode_sys)}"
159
+
160
+ elif 'variables' in query.lower():
161
+ return f"There are {info['num_nodes']} variables: {info['nodes']}"
162
+
163
+ elif 'edges' in query.lower():
164
+ return f"Edges: {info['edges']}"
165
+
166
+ elif 'cyclic' or 'cycle' in query.lower():
167
+ cycles = list(nx.simple_cycles(G))
168
+ if cycles:
169
+ cycles_str = "\n".join([" -> ".join(cycle + [cycle[0]]) for cycle in cycles])
170
+ return f"Cycles found:\n{cycles_str}"
171
+ else:
172
+ return "No cycles found."
173
+
174
+ else:
175
+ prompt = f"Given the network with nodes: {info['nodes']} and edges: {info['edges']}, answer the query: {query}"
176
+ answer = qa(prompt, max_length=128)[0]['generated_text']
177
+ return answer
178
+
179
+
180
+ iface = gr.Interface(
181
+ fn=full_process,
182
+ inputs=[
183
+ gr.Image(type="pil", label="Upload Network Image (optional)"),
184
+ gr.Textbox(label="Text Input (optional)", placeholder="Or paste network: A + B -> C, X <-> Y"),
185
+ gr.Textbox(label="Query", placeholder="Ask about ODEs, Jacobian, edges, etc.")
186
+ ],
187
+ outputs="text",
188
+ title="Biological Network Analyzer",
189
+ description="Upload an image or enter network text. Then ask a query like 'Give ODEs' or 'Is it cyclic?'.",
190
+ theme=Ocean()
191
+ )
192
+
193
+ iface.launch(share=True)