Create app.py
Browse files
app.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import networkx as nx
|
| 2 |
+
import pyreason as pr
|
| 3 |
+
import gradio as gr
|
| 4 |
+
import pandas as pd
|
| 5 |
+
|
| 6 |
+
# ================================ CREATE GRAPH ====================================
|
| 7 |
+
# Create an Undirected Graph for Mutual Relationships
|
| 8 |
+
g = nx.Graph()
|
| 9 |
+
|
| 10 |
+
# Add workers
|
| 11 |
+
workers = ['John', 'Mary', 'Justin']
|
| 12 |
+
g.add_nodes_from(workers)
|
| 13 |
+
|
| 14 |
+
# Add equipment
|
| 15 |
+
equipment = ['Crane', 'Bulldozer']
|
| 16 |
+
g.add_nodes_from(equipment)
|
| 17 |
+
|
| 18 |
+
# Add relationships among workers (colleagues)
|
| 19 |
+
g.add_edge('John', 'Mary', colleagues=1)
|
| 20 |
+
g.add_edge('Mary', 'Justin', colleagues=1)
|
| 21 |
+
g.add_edge('John', 'Justin', colleagues=1)
|
| 22 |
+
|
| 23 |
+
# Define equipment usage (initially, all are using the Crane)
|
| 24 |
+
g.add_edge('John', 'Crane', uses=1)
|
| 25 |
+
g.add_edge('Mary', 'Crane', uses=1)
|
| 26 |
+
g.add_edge('Justin', 'Crane', uses=1)
|
| 27 |
+
|
| 28 |
+
# ================================ VERIFY GRAPH ====================================
|
| 29 |
+
def display_graph_edges(graph):
|
| 30 |
+
print("\nGraph edges with attributes:")
|
| 31 |
+
for u, v, d in graph.edges(data=True):
|
| 32 |
+
print(f" {u} -- {v}, attributes: {d}")
|
| 33 |
+
|
| 34 |
+
display_graph_edges(g)
|
| 35 |
+
|
| 36 |
+
# ================================ LOAD GRAPH INTO PYREASON ====================================
|
| 37 |
+
pr.load_graph(g)
|
| 38 |
+
pr.settings.verbose = False # Set to False for cleaner output
|
| 39 |
+
pr.settings.atom_trace = False # Disable atom tracing for clarity
|
| 40 |
+
|
| 41 |
+
# ================================ DEFINE RULES ====================================
|
| 42 |
+
# Define the 'certification' propagation rule with bounds
|
| 43 |
+
certification_rule_str = (
|
| 44 |
+
'certified(x) : [1,1] <-1 certified(y) : [1,1], '
|
| 45 |
+
'colleagues(x,y) : [1,1], uses(x,z) : [1,1], uses(y,z) : [1,1]'
|
| 46 |
+
)
|
| 47 |
+
certification_rule = pr.Rule(certification_rule_str, 'certification_rule')
|
| 48 |
+
pr.add_rule(certification_rule)
|
| 49 |
+
|
| 50 |
+
# Define the 'persistence' rule to maintain certifications over time with bounds
|
| 51 |
+
persistence_rule_str = 'certified(x) : [1,1] <-1 certified(x) : [1,1]'
|
| 52 |
+
persistence_rule = pr.Rule(persistence_rule_str, 'persistence_rule')
|
| 53 |
+
pr.add_rule(persistence_rule)
|
| 54 |
+
|
| 55 |
+
# ================================ ADD FACTS ====================================
|
| 56 |
+
# Add initial fact that Mary is certified from timestep 0 to 999 (indefinite)
|
| 57 |
+
initial_fact = pr.Fact(
|
| 58 |
+
fact_text='certified(Mary) : true',
|
| 59 |
+
name='certified_fact',
|
| 60 |
+
start_time=0,
|
| 61 |
+
end_time=999 # Use a large number to indicate indefinite duration
|
| 62 |
+
)
|
| 63 |
+
pr.add_fact(initial_fact)
|
| 64 |
+
|
| 65 |
+
# ================================ DEFINE DISPLAY FUNCTION ====================================
|
| 66 |
+
# Function to capture active 'uses' edges and certification statuses
|
| 67 |
+
def capture_state(graph, timestep):
|
| 68 |
+
# Capture active 'uses' edges
|
| 69 |
+
active_uses = [
|
| 70 |
+
{'User': u, 'Equipment': v} for u, v, d in graph.edges(data=True)
|
| 71 |
+
if d.get('uses', 0) == 1
|
| 72 |
+
]
|
| 73 |
+
|
| 74 |
+
# Capture certification statuses
|
| 75 |
+
interpretation = pr.reason(timesteps=1)
|
| 76 |
+
dataframes = pr.filter_and_sort_nodes(interpretation, ['certified'])
|
| 77 |
+
certification_status = {}
|
| 78 |
+
|
| 79 |
+
for df in dataframes:
|
| 80 |
+
for index, row in df.iterrows():
|
| 81 |
+
component = row['component']
|
| 82 |
+
status = row['certified']
|
| 83 |
+
# Assuming the bounds [1,1] imply status is binary
|
| 84 |
+
certification_status[component] = 'Yes' if status[0] >= 1 else 'No'
|
| 85 |
+
|
| 86 |
+
# Convert certification_status dictionary to a list of dictionaries
|
| 87 |
+
certifications = [
|
| 88 |
+
{'Worker': worker, 'Certified': status}
|
| 89 |
+
for worker, status in certification_status.items()
|
| 90 |
+
]
|
| 91 |
+
|
| 92 |
+
# Convert lists to DataFrames
|
| 93 |
+
uses_df = pd.DataFrame(active_uses) if active_uses else pd.DataFrame(columns=['User', 'Equipment'])
|
| 94 |
+
cert_df = pd.DataFrame(certifications) if certifications else pd.DataFrame(columns=['Worker', 'Certified'])
|
| 95 |
+
|
| 96 |
+
return uses_df, cert_df
|
| 97 |
+
|
| 98 |
+
# ================================ RUN REASONING AND CAPTURE STATES ====================================
|
| 99 |
+
timesteps = 4
|
| 100 |
+
state_data = []
|
| 101 |
+
|
| 102 |
+
for t in range(timesteps):
|
| 103 |
+
print(f"\n=== TIMESTEP - {t} ===")
|
| 104 |
+
|
| 105 |
+
# Dynamically update equipment usage based on timestep
|
| 106 |
+
if t == 1:
|
| 107 |
+
# At timestep 1, John stops using the Crane
|
| 108 |
+
if g.has_edge('John', 'Crane'):
|
| 109 |
+
g['John']['Crane']['uses'] = 0 # Set 'uses' to 0 instead of removing the edge
|
| 110 |
+
print("Timestep 1: John stops using the Crane.")
|
| 111 |
+
if t == 2:
|
| 112 |
+
# At timestep 2, Justin stops using the Crane
|
| 113 |
+
if g.has_edge('Justin', 'Crane'):
|
| 114 |
+
g['Justin']['Crane']['uses'] = 0 # Set 'uses' to 0 instead of removing the edge
|
| 115 |
+
print("Timestep 2: Justin stops using the Crane.")
|
| 116 |
+
if t == 3:
|
| 117 |
+
# At timestep 3, Mary stops using the Crane
|
| 118 |
+
if g.has_edge('Mary', 'Crane'):
|
| 119 |
+
g['Mary']['Crane']['uses'] = 0 # Set 'uses' to 0 instead of removing the edge
|
| 120 |
+
print("Timestep 3: Mary stops using the Crane.")
|
| 121 |
+
|
| 122 |
+
# Display current 'uses' edges for verification
|
| 123 |
+
current_uses = [
|
| 124 |
+
(u, v, d['uses']) for u, v, d in g.edges(data=True)
|
| 125 |
+
if 'uses' in d and d['uses'] == 1
|
| 126 |
+
]
|
| 127 |
+
print("\nCurrent 'uses' edges:")
|
| 128 |
+
if current_uses:
|
| 129 |
+
for edge in current_uses:
|
| 130 |
+
print(f" {edge[0]} uses {edge[1]}: {edge[2]}")
|
| 131 |
+
else:
|
| 132 |
+
print(" No active 'uses' edges.")
|
| 133 |
+
|
| 134 |
+
# Reload the updated graph into PyReason
|
| 135 |
+
pr.load_graph(g)
|
| 136 |
+
|
| 137 |
+
# Capture the current state
|
| 138 |
+
uses_df, cert_df = capture_state(g, t)
|
| 139 |
+
state_data.append({
|
| 140 |
+
'timestep': t,
|
| 141 |
+
'uses': uses_df,
|
| 142 |
+
'certifications': cert_df
|
| 143 |
+
})
|
| 144 |
+
|
| 145 |
+
# ================================ GRADIO INTERFACE ====================================
|
| 146 |
+
def visualize_timestep(timestep):
|
| 147 |
+
if timestep < 0 or timestep >= timesteps:
|
| 148 |
+
return "<p style='color:red;'>Invalid timestep selected.</p>", "<p style='color:red;'>Invalid timestep selected.</p>"
|
| 149 |
+
state = state_data[timestep]
|
| 150 |
+
uses_df = state['uses']
|
| 151 |
+
cert_df = state['certifications']
|
| 152 |
+
|
| 153 |
+
# Convert DataFrames to HTML tables for better display
|
| 154 |
+
uses_html = uses_df.to_html(index=False) if not uses_df.empty else "<p>No active 'uses' edges.</p>"
|
| 155 |
+
cert_html = cert_df.to_html(index=False) if not cert_df.empty else "<p>No certifications found.</p>"
|
| 156 |
+
|
| 157 |
+
return uses_html, cert_html
|
| 158 |
+
|
| 159 |
+
# Create Gradio interface
|
| 160 |
+
with gr.Blocks() as demo:
|
| 161 |
+
gr.Markdown("# ๐ง Neuro-Symbolic AI: Certification Propagation Visualization")
|
| 162 |
+
gr.Markdown(
|
| 163 |
+
"""
|
| 164 |
+
Welcome to the **Certification Propagation Visualization** tool powered by **Neuro-Symbolic AI** and **PyReason**.
|
| 165 |
+
|
| 166 |
+
## **Understanding the Technology**
|
| 167 |
+
|
| 168 |
+
### **Neuro-Symbolic AI**
|
| 169 |
+
Neuro-Symbolic AI is an advanced approach that combines the pattern recognition capabilities of neural networks with the logical reasoning strengths of symbolic AI. This fusion enables systems to learn from data and perform complex reasoning tasks, offering both flexibility and interpretability.
|
| 170 |
+
|
| 171 |
+
### **PyReason**
|
| 172 |
+
PyReason is a symbolic reasoning engine that facilitates logical inferences on graph-based knowledge representations. It allows the definition of rules and facts to simulate reasoning processes, such as how certifications propagate among workers based on their relationships and equipment usage.
|
| 173 |
+
|
| 174 |
+
## **How It Works**
|
| 175 |
+
|
| 176 |
+
1. **Graph Representation:** Workers and equipment are represented as nodes in a graph, with edges denoting relationships like colleagueship and equipment usage.
|
| 177 |
+
2. **Defining Rules:** Logical rules dictate how certifications spread based on these relationships and equipment usage.
|
| 178 |
+
3. **Reasoning Process:** PyReason applies these rules iteratively to update certification statuses over time.
|
| 179 |
+
4. **Interactive Visualization:** The Gradio interface allows you to observe these dynamics interactively across different timesteps.
|
| 180 |
+
|
| 181 |
+
## **Instructions**
|
| 182 |
+
|
| 183 |
+
- **Select Timestep:** Use the slider to choose a timestep (0 to 3).
|
| 184 |
+
- **Visualize:** Click the "Visualize" button or simply move the slider to update the displays.
|
| 185 |
+
- **Interpret Results:**
|
| 186 |
+
- **Active Equipment Usages:** See which workers are currently using which equipment.
|
| 187 |
+
- **Certification Statuses:** View the certification status (`Yes` or `No`) of each worker.
|
| 188 |
+
"""
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
with gr.Row():
|
| 192 |
+
with gr.Column():
|
| 193 |
+
timestep_slider = gr.Slider(
|
| 194 |
+
minimum=0,
|
| 195 |
+
maximum=timesteps-1,
|
| 196 |
+
step=1,
|
| 197 |
+
label="๐ Select Timestep",
|
| 198 |
+
value=0
|
| 199 |
+
)
|
| 200 |
+
submit_button = gr.Button("๐ Visualize")
|
| 201 |
+
with gr.Column():
|
| 202 |
+
uses_output = gr.HTML(label="๐ผ Active Equipment Usages")
|
| 203 |
+
cert_output = gr.HTML(label="๐
Certification Statuses")
|
| 204 |
+
|
| 205 |
+
submit_button.click(
|
| 206 |
+
visualize_timestep,
|
| 207 |
+
inputs=timestep_slider,
|
| 208 |
+
outputs=[uses_output, cert_output]
|
| 209 |
+
)
|
| 210 |
+
|
| 211 |
+
# Optionally, synchronize slider changes with visualization
|
| 212 |
+
timestep_slider.change(
|
| 213 |
+
visualize_timestep,
|
| 214 |
+
inputs=timestep_slider,
|
| 215 |
+
outputs=[uses_output, cert_output]
|
| 216 |
+
)
|
| 217 |
+
|
| 218 |
+
gr.Markdown(
|
| 219 |
+
"""
|
| 220 |
+
---
|
| 221 |
+
|
| 222 |
+
### **Behind the Scenes**
|
| 223 |
+
|
| 224 |
+
- **Initialization:** The system starts by defining workers, equipment, and their initial relationships and equipment usage.
|
| 225 |
+
- **Rule Application:** At each timestep, rules defined in PyReason determine how certifications propagate based on current equipment usage and colleague relationships.
|
| 226 |
+
- **State Capture:** The active equipment usages and certification statuses are captured and displayed in tables for each timestep.
|
| 227 |
+
|
| 228 |
+
### **Benefits of This Approach**
|
| 229 |
+
|
| 230 |
+
- **Interpretability:** The logical rules provide clear insights into how decisions (certifications) are made.
|
| 231 |
+
- **Flexibility:** Easily adjust rules or relationships to simulate different scenarios.
|
| 232 |
+
- **Scalability:** Can be extended to larger networks with more complex relationships and rules.
|
| 233 |
+
"""
|
| 234 |
+
)
|
| 235 |
+
|
| 236 |
+
# Launch the Gradio app
|
| 237 |
+
demo.launch()
|