Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -130,7 +130,6 @@ df_human["source"] = "Human"
|
|
130 |
df_combined = pd.concat([df_dog, df_human], ignore_index=True)
|
131 |
print(f"Loaded {len(df_dog)} dog rows and {len(df_human)} human rows")
|
132 |
|
133 |
-
|
134 |
# ---------------------------------------------------------------
|
135 |
# Feature preparation and UMAP embedding
|
136 |
# ---------------------------------------------------------------
|
@@ -155,29 +154,42 @@ df_combined["cluster"] = kmeans.fit_predict(features if feature_cols else df_com
|
|
155 |
# Cross-Species Analysis Functions
|
156 |
# ---------------------------------------------------------------
|
157 |
def find_nearest_cross_species_neighbor(selected_row, df_combined, n_neighbors=5):
|
158 |
-
"""Find closest neighbor from opposite species using
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
selected_source = selected_row['source']
|
160 |
opposite_source = 'Human' if selected_source == 'Dog' else 'Dog'
|
161 |
-
|
162 |
-
feature_cols = [c for c in df_combined.columns if c.startswith("feature_")]
|
163 |
-
if not feature_cols:
|
164 |
-
opposite_data = df_combined[df_combined['source'] == opposite_source]
|
165 |
-
return opposite_data.iloc[0] if len(opposite_data) > 0 else None
|
166 |
-
|
167 |
-
selected_features = selected_row[feature_cols].values.reshape(1, -1)
|
168 |
-
selected_features = np.nan_to_num(selected_features)
|
169 |
-
|
170 |
opposite_data = df_combined[df_combined['source'] == opposite_source]
|
171 |
if len(opposite_data) == 0:
|
172 |
return None
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
|
182 |
# Cache for performance
|
183 |
_audio_path_cache = {}
|
@@ -439,7 +451,7 @@ def create_enhanced_manifold_plot(df_filtered, lens_selected, color_scheme, poin
|
|
439 |
|
440 |
fig.update_layout(
|
441 |
title={
|
442 |
-
'text': "
|
443 |
'x': 0.5,
|
444 |
'xanchor': 'center'
|
445 |
},
|
@@ -598,7 +610,7 @@ def create_dual_holography_plot(z1, phi1, z2, phi2, resolution, wavelength, titl
|
|
598 |
), row=1, col=2)
|
599 |
|
600 |
fig.update_layout(
|
601 |
-
title="Side-by-Side
|
602 |
scene=dict(
|
603 |
xaxis_title="Re(z)", yaxis_title="Im(z)", zaxis_title="|Φ|",
|
604 |
camera=dict(eye=dict(x=1.5, y=1.5, z=1.5))
|
@@ -664,7 +676,7 @@ def create_entropy_geometry_plot(phi: np.ndarray):
|
|
664 |
fig.add_trace(go.Histogram(x=phases, name='Phase', nbinsx=50), row=1, col=2)
|
665 |
|
666 |
fig.update_layout(
|
667 |
-
title_text="
|
668 |
showlegend=False,
|
669 |
bargap=0.1,
|
670 |
margin=dict(l=20, r=20, t=60, b=20)
|
@@ -1074,24 +1086,35 @@ def export_filtered_data(species_selection, emotion_selection, lens_selection,
|
|
1074 |
# ---------------------------------------------------------------
|
1075 |
with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
|
1076 |
gr.Markdown("""
|
1077 |
-
# **Entronaut
|
1078 |
-
*
|
|
|
|
|
1079 |
|
1080 |
-
|
1081 |
-
- **Universal Manifold Explorer**: Multi-dimensional visualization suite with live statistics
|
1082 |
-
- **Interactive Holography**: Cross-species communication mapping with mathematical precision
|
1083 |
-
- **Real-time Analytics**: Dynamic filtering, clustering, and similarity analysis
|
1084 |
-
- **Rich Visualizations**: 2D/3D plots, density heatmaps, feature distributions
|
1085 |
-
- **Data Export**: Export filtered datasets for external analysis
|
1086 |
-
- **⚡ Auto-loading**: Manifold visualizations load automatically on startup
|
1087 |
|
1088 |
-
|
1089 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1090 |
""")
|
1091 |
|
1092 |
with gr.Tabs():
|
1093 |
-
with gr.TabItem("
|
1094 |
-
gr.Markdown("
|
1095 |
|
1096 |
with gr.Row():
|
1097 |
with gr.Column(scale=1):
|
@@ -1233,7 +1256,7 @@ with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
|
|
1233 |
def update_cross_species_view(species, primary_file, neighbor_file, lens, resolution, wavelength):
|
1234 |
if not primary_file:
|
1235 |
empty_fig = go.Figure(layout={"title": "Select a primary file"})
|
1236 |
-
return empty_fig, empty_fig, empty_fig, "", ""
|
1237 |
|
1238 |
primary_row = df_combined[
|
1239 |
(df_combined["filepath"] == primary_file) &
|
@@ -1247,8 +1270,10 @@ with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
|
|
1247 |
empty_fig = go.Figure(layout={"title": "Primary file not found"})
|
1248 |
return empty_fig, empty_fig, empty_fig, "", ""
|
1249 |
|
1250 |
-
|
1251 |
-
|
|
|
|
|
1252 |
else:
|
1253 |
opposite_species = 'Human' if species == 'Dog' else 'Dog'
|
1254 |
neighbor_row = df_combined[
|
@@ -1301,16 +1326,27 @@ with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
|
|
1301 |
else:
|
1302 |
neighbor_info = ""
|
1303 |
|
1304 |
-
|
|
|
|
|
1305 |
|
1306 |
def update_dropdowns_on_species_change(species):
|
1307 |
species_files = df_combined[df_combined["source"] == species]["filepath"].tolist()
|
1308 |
opposite_species = 'Human' if species == 'Dog' else 'Dog'
|
1309 |
neighbor_files = df_combined[df_combined["source"] == opposite_species]["filepath"].tolist()
|
1310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1311 |
return (
|
1312 |
-
gr.Dropdown(choices=species_files, value=
|
1313 |
-
gr.Dropdown(choices=neighbor_files, value=
|
1314 |
)
|
1315 |
|
1316 |
species_dropdown.change(
|
@@ -1326,7 +1362,8 @@ with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
|
|
1326 |
|
1327 |
cross_species_outputs = [
|
1328 |
dual_holography_plot, diagnostic_plot, entropy_plot,
|
1329 |
-
primary_info_html, neighbor_info_html
|
|
|
1330 |
]
|
1331 |
|
1332 |
for input_component in cross_species_inputs:
|
@@ -1336,6 +1373,17 @@ with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
|
|
1336 |
outputs=cross_species_outputs
|
1337 |
)
|
1338 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1339 |
# Auto-load manifold visualizations on startup
|
1340 |
demo.load(
|
1341 |
update_manifold_visualization,
|
|
|
130 |
df_combined = pd.concat([df_dog, df_human], ignore_index=True)
|
131 |
print(f"Loaded {len(df_dog)} dog rows and {len(df_human)} human rows")
|
132 |
|
|
|
133 |
# ---------------------------------------------------------------
|
134 |
# Feature preparation and UMAP embedding
|
135 |
# ---------------------------------------------------------------
|
|
|
154 |
# Cross-Species Analysis Functions
|
155 |
# ---------------------------------------------------------------
|
156 |
def find_nearest_cross_species_neighbor(selected_row, df_combined, n_neighbors=5):
|
157 |
+
"""Find closest neighbor from opposite species using information geometry.
|
158 |
+
|
159 |
+
Priority order:
|
160 |
+
1) Use Euclidean distance in learned manifold coordinates (x, y, z) if available
|
161 |
+
2) Fallback to cosine similarity on feature vectors
|
162 |
+
3) Fallback to first available opposite-species row
|
163 |
+
"""
|
164 |
selected_source = selected_row['source']
|
165 |
opposite_source = 'Human' if selected_source == 'Dog' else 'Dog'
|
166 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
167 |
opposite_data = df_combined[df_combined['source'] == opposite_source]
|
168 |
if len(opposite_data) == 0:
|
169 |
return None
|
170 |
+
|
171 |
+
# 1) Prefer geometry in manifold if available
|
172 |
+
if all(col in selected_row.index for col in ['x', 'y', 'z']) and \
|
173 |
+
all(col in opposite_data.columns for col in ['x', 'y', 'z']):
|
174 |
+
sx, sy, sz = float(selected_row['x']), float(selected_row['y']), float(selected_row['z'])
|
175 |
+
coords = opposite_data[['x', 'y', 'z']].to_numpy(dtype=float)
|
176 |
+
diffs = coords - np.array([sx, sy, sz], dtype=float)
|
177 |
+
dists = np.sqrt(np.sum(diffs * diffs, axis=1))
|
178 |
+
nearest_idx = int(np.argmin(dists))
|
179 |
+
return opposite_data.iloc[nearest_idx]
|
180 |
+
|
181 |
+
# 2) Fallback to feature-space cosine similarity
|
182 |
+
feature_cols = [c for c in df_combined.columns if c.startswith("feature_")]
|
183 |
+
if feature_cols:
|
184 |
+
selected_features = selected_row[feature_cols].values.reshape(1, -1)
|
185 |
+
selected_features = np.nan_to_num(selected_features)
|
186 |
+
opposite_features = np.nan_to_num(opposite_data[feature_cols].values)
|
187 |
+
similarities = cosine_similarity(selected_features, opposite_features)[0]
|
188 |
+
most_similar_idx = int(np.argmax(similarities))
|
189 |
+
return opposite_data.iloc[most_similar_idx]
|
190 |
+
|
191 |
+
# 3) Fallback: first available
|
192 |
+
return opposite_data.iloc[0]
|
193 |
|
194 |
# Cache for performance
|
195 |
_audio_path_cache = {}
|
|
|
451 |
|
452 |
fig.update_layout(
|
453 |
title={
|
454 |
+
'text': "Shared Pattern Space of Audio Signals",
|
455 |
'x': 0.5,
|
456 |
'xanchor': 'center'
|
457 |
},
|
|
|
610 |
), row=1, col=2)
|
611 |
|
612 |
fig.update_layout(
|
613 |
+
title="Side-by-Side Geometric Comparison",
|
614 |
scene=dict(
|
615 |
xaxis_title="Re(z)", yaxis_title="Im(z)", zaxis_title="|Φ|",
|
616 |
camera=dict(eye=dict(x=1.5, y=1.5, z=1.5))
|
|
|
676 |
fig.add_trace(go.Histogram(x=phases, name='Phase', nbinsx=50), row=1, col=2)
|
677 |
|
678 |
fig.update_layout(
|
679 |
+
title_text="Information-Entropy Geometry",
|
680 |
showlegend=False,
|
681 |
bargap=0.1,
|
682 |
margin=dict(l=20, r=20, t=60, b=20)
|
|
|
1086 |
# ---------------------------------------------------------------
|
1087 |
with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
|
1088 |
gr.Markdown("""
|
1089 |
+
# **Entronaut: A Visual Explorer for Information Geometry**
|
1090 |
+
*Turn complex data into visible patterns.*
|
1091 |
+
|
1092 |
+
---
|
1093 |
|
1094 |
+
### What is Entronaut?
|
|
|
|
|
|
|
|
|
|
|
|
|
1095 |
|
1096 |
+
Entronaut is a tool that transforms complex data—like audio signals, financial data, or text—into geometric shapes. Think of it like a mathematical prism: just as a prism splits a beam of light into a rainbow of colors, Entronaut splits a data stream into its fundamental patterns, making hidden structures visible and explorable.
|
1097 |
+
|
1098 |
+
This demo applies Entronaut to audio recordings of human and dog sounds to reveal their underlying mathematical similarities.
|
1099 |
+
|
1100 |
+
### How is this different from tools like FFT?
|
1101 |
+
|
1102 |
+
Traditional tools like Fast Fourier Transform (FFT) or Wavelets are excellent for breaking down a signal into its basic frequencies. They can tell you *what* notes are in a piece of music, but not how they are arranged into a melody or harmony.
|
1103 |
+
|
1104 |
+
Entronaut goes a step further. It uses **Information Geometry** to analyze the *relationships* between data points. This allows it to capture not just the components, but the intricate structure of the data itself. It reveals the 'shape' of the information, showing you how patterns are organized and connected.
|
1105 |
+
|
1106 |
+
### What is Information Geometry?
|
1107 |
+
|
1108 |
+
In simple terms, Information Geometry is a field of math that lets us measure the "distance" and "shape" of information.
|
1109 |
+
|
1110 |
+
Imagine you have two different news articles. You could count the words, but that wouldn't tell you how similar their meaning is. Information Geometry provides tools to define a meaningful distance between them based on the information they contain. Entronaut makes this concept visual, creating a 3D map where you can see how close or far different data points are in this abstract "information space".
|
1111 |
+
|
1112 |
+
**This App's Goal**: To demonstrate how Entronaut can map sounds from different sources into a shared geometric space. By exploring this space, you can see how Entronaut reveals underlying structural patterns in sound data.
|
1113 |
""")
|
1114 |
|
1115 |
with gr.Tabs():
|
1116 |
+
with gr.TabItem("🎵 Audio Pattern Explorer"):
|
1117 |
+
gr.Markdown("## Exploring the Geometric Patterns in Sound")
|
1118 |
|
1119 |
with gr.Row():
|
1120 |
with gr.Column(scale=1):
|
|
|
1256 |
def update_cross_species_view(species, primary_file, neighbor_file, lens, resolution, wavelength):
|
1257 |
if not primary_file:
|
1258 |
empty_fig = go.Figure(layout={"title": "Select a primary file"})
|
1259 |
+
return empty_fig, empty_fig, empty_fig, "", "", gr.Dropdown()
|
1260 |
|
1261 |
primary_row = df_combined[
|
1262 |
(df_combined["filepath"] == primary_file) &
|
|
|
1270 |
empty_fig = go.Figure(layout={"title": "Primary file not found"})
|
1271 |
return empty_fig, empty_fig, empty_fig, "", ""
|
1272 |
|
1273 |
+
auto_neighbor_row = find_nearest_cross_species_neighbor(primary_row, df_combined)
|
1274 |
+
|
1275 |
+
if not neighbor_file and auto_neighbor_row is not None:
|
1276 |
+
neighbor_row = auto_neighbor_row
|
1277 |
else:
|
1278 |
opposite_species = 'Human' if species == 'Dog' else 'Dog'
|
1279 |
neighbor_row = df_combined[
|
|
|
1326 |
else:
|
1327 |
neighbor_info = ""
|
1328 |
|
1329 |
+
# If we used auto neighbor, preselect it in the dropdown
|
1330 |
+
neighbor_dropdown_value = gr.Dropdown(value=(neighbor_row['filepath'] if neighbor_row is not None else None))
|
1331 |
+
return dual_holo, diag, entropy, primary_info, neighbor_info, neighbor_dropdown_value
|
1332 |
|
1333 |
def update_dropdowns_on_species_change(species):
|
1334 |
species_files = df_combined[df_combined["source"] == species]["filepath"].tolist()
|
1335 |
opposite_species = 'Human' if species == 'Dog' else 'Dog'
|
1336 |
neighbor_files = df_combined[df_combined["source"] == opposite_species]["filepath"].tolist()
|
1337 |
+
|
1338 |
+
# Preselect first file for species and auto-pick nearest neighbor for that primary if available
|
1339 |
+
primary_value = species_files[0] if species_files else ""
|
1340 |
+
if primary_value:
|
1341 |
+
primary_row = df_combined[(df_combined["filepath"] == primary_value) & (df_combined["source"] == species)].iloc[0]
|
1342 |
+
neighbor_row = find_nearest_cross_species_neighbor(primary_row, df_combined)
|
1343 |
+
neighbor_value = neighbor_row['filepath'] if neighbor_row is not None else (neighbor_files[0] if neighbor_files else "")
|
1344 |
+
else:
|
1345 |
+
neighbor_value = neighbor_files[0] if neighbor_files else ""
|
1346 |
+
|
1347 |
return (
|
1348 |
+
gr.Dropdown(choices=species_files, value=primary_value),
|
1349 |
+
gr.Dropdown(choices=neighbor_files, value=neighbor_value)
|
1350 |
)
|
1351 |
|
1352 |
species_dropdown.change(
|
|
|
1362 |
|
1363 |
cross_species_outputs = [
|
1364 |
dual_holography_plot, diagnostic_plot, entropy_plot,
|
1365 |
+
primary_info_html, neighbor_info_html,
|
1366 |
+
neighbor_dropdown
|
1367 |
]
|
1368 |
|
1369 |
for input_component in cross_species_inputs:
|
|
|
1373 |
outputs=cross_species_outputs
|
1374 |
)
|
1375 |
|
1376 |
+
# When the primary file changes, auto-update neighbor selection to nearest geometry match
|
1377 |
+
def on_primary_change(species, primary_value, neighbor_value, lens, resolution, wavelength):
|
1378 |
+
# Reuse update_cross_species_view to recompute plots and auto neighbor
|
1379 |
+
return update_cross_species_view(species, primary_value, None, lens, resolution, wavelength)
|
1380 |
+
|
1381 |
+
primary_dropdown.change(
|
1382 |
+
on_primary_change,
|
1383 |
+
inputs=cross_species_inputs,
|
1384 |
+
outputs=cross_species_outputs
|
1385 |
+
)
|
1386 |
+
|
1387 |
# Auto-load manifold visualizations on startup
|
1388 |
demo.load(
|
1389 |
update_manifold_visualization,
|