#!/usr/bin/env python3
"""
Enhanced CMT Holographic Visualization Suite with Scientific Integrity
Full-featured toolkit with mathematically rigorous implementations
"""
import os
import warnings
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from plotly.subplots import make_subplots
# Handle UMAP import variations
try:
from umap import UMAP
except ImportError:
try:
from umap.umap_ import UMAP
except ImportError:
import umap.umap_ as umap_module
UMAP = umap_module.UMAP
from sklearn.cluster import KMeans
from scipy.stats import entropy as shannon_entropy
from scipy import special as sp_special
from scipy.interpolate import griddata
from sklearn.metrics.pairwise import cosine_similarity
from scipy.spatial.distance import cdist
import soundfile as sf
import gradio as gr
# ================================================================
# Unified Communication Manifold Explorer & CMT Visualizer v5.0
# - Full feature restoration with scientific integrity
# - Mathematically rigorous implementations
# - All original tools and insights preserved
# ================================================================
warnings.filterwarnings("ignore", category=FutureWarning)
warnings.filterwarnings("ignore", category=UserWarning)
print("Initializing the Enhanced CMT Holography Explorer...")
# ---------------------------------------------------------------
# Data setup
# ---------------------------------------------------------------
# If running inside the proxy, PRIVATE_APP_ROOT will be set.
# Otherwise, fall back to the current working directory for standalone execution.
_app_root = os.getenv("PRIVATE_APP_ROOT", os.path.abspath(os.getcwd()))
print(f"✅ Application Root detected as: {_app_root}")
BASE_DIR = _app_root
DATA_DIR = os.path.join(BASE_DIR, "data")
DOG_DIR = os.path.join(DATA_DIR, "dog")
HUMAN_DIR = os.path.join(DATA_DIR, "human")
# Platform-aware paths
HF_CSV_DOG = "cmt_dog_sound_analysis.csv"
HF_CSV_HUMAN = "cmt_human_speech_analysis.csv"
COLAB_CSV_DOG = "/content/cmt_dog_sound_analysis.csv"
COLAB_CSV_HUMAN = "/content/cmt_human_speech_analysis.csv"
# Determine environment
if os.path.exists(HF_CSV_DOG) and os.path.exists(HF_CSV_HUMAN):
CSV_DOG = HF_CSV_DOG
CSV_HUMAN = HF_CSV_HUMAN
print("Using Hugging Face Spaces paths")
elif os.path.exists(COLAB_CSV_DOG) and os.path.exists(COLAB_CSV_HUMAN):
CSV_DOG = COLAB_CSV_DOG
CSV_HUMAN = COLAB_CSV_HUMAN
print("Using Google Colab paths")
else:
CSV_DOG = HF_CSV_DOG
CSV_HUMAN = HF_CSV_HUMAN
print("Falling back to local/dummy data paths")
# Audio paths
if os.path.exists("/content/drive/MyDrive/combined"):
DOG_AUDIO_BASE_PATH = '/content/drive/MyDrive/combined'
HUMAN_AUDIO_BASE_PATH = '/content/drive/MyDrive/human'
print("Using Google Drive audio paths")
elif os.path.exists("combined") and os.path.exists("human"):
DOG_AUDIO_BASE_PATH = 'combined'
HUMAN_AUDIO_BASE_PATH = 'human'
print("Using Hugging Face Spaces audio paths")
else:
DOG_AUDIO_BASE_PATH = DOG_DIR
HUMAN_AUDIO_BASE_PATH = HUMAN_DIR
print("Using local audio paths")
# ---------------------------------------------------------------
# Load datasets
# ---------------------------------------------------------------
if os.path.exists(CSV_DOG) and os.path.exists(CSV_HUMAN):
print(f"✅ Loading real data from CSVs")
df_dog = pd.read_csv(CSV_DOG)
df_human = pd.read_csv(CSV_HUMAN)
else:
print("⚠️ Generating dummy data for demo")
# Dummy data generation
n_dummy = 50
rng = np.random.default_rng(42)
dog_labels = ["bark", "growl", "whine", "pant"] * (n_dummy // 4 + 1)
human_labels = ["speech", "laugh", "cry", "shout"] * (n_dummy // 4 + 1)
df_dog = pd.DataFrame({
"filepath": [f"dog_{i}.wav" for i in range(n_dummy)],
"label": dog_labels[:n_dummy],
**{f"feature_{i}": rng.random(n_dummy) for i in range(10)},
**{f"diag_alpha_{lens}": rng.uniform(0.1, 2.0, n_dummy)
for lens in ["gamma", "zeta", "airy", "bessel"]},
**{f"diag_srl_{lens}": rng.uniform(0.5, 50.0, n_dummy)
for lens in ["gamma", "zeta", "airy", "bessel"]}
})
df_human = pd.DataFrame({
"filepath": [f"human_{i}.wav" for i in range(n_dummy)],
"label": human_labels[:n_dummy],
**{f"feature_{i}": rng.random(n_dummy) for i in range(10)},
**{f"diag_alpha_{lens}": rng.uniform(0.1, 2.0, n_dummy)
for lens in ["gamma", "zeta", "airy", "bessel"]},
**{f"diag_srl_{lens}": rng.uniform(0.5, 50.0, n_dummy)
for lens in ["gamma", "zeta", "airy", "bessel"]}
})
df_dog["source"] = "Dog"
df_human["source"] = "Human"
df_combined = pd.concat([df_dog, df_human], ignore_index=True)
print(f"Loaded {len(df_dog)} dog rows and {len(df_human)} human rows")
# ---------------------------------------------------------------
# Feature preparation and UMAP embedding
# ---------------------------------------------------------------
feature_cols = [c for c in df_combined.columns if c.startswith("feature_")]
if feature_cols:
features = np.nan_to_num(df_combined[feature_cols].to_numpy())
reducer = UMAP(n_components=3, n_neighbors=15, min_dist=0.1, random_state=42)
df_combined[["x", "y", "z"]] = reducer.fit_transform(features)
else:
# Fallback if no features
rng = np.random.default_rng(42)
df_combined["x"] = rng.random(len(df_combined))
df_combined["y"] = rng.random(len(df_combined))
df_combined["z"] = rng.random(len(df_combined))
# Clustering
kmeans = KMeans(n_clusters=max(4, min(12, int(np.sqrt(len(df_combined))))),
random_state=42, n_init=10)
df_combined["cluster"] = kmeans.fit_predict(features if feature_cols else df_combined[["x", "y", "z"]])
# ---------------------------------------------------------------
# Cross-Species Analysis Functions
# ---------------------------------------------------------------
def find_nearest_cross_species_neighbor(selected_row, df_combined, n_neighbors=5):
"""Find closest neighbor from opposite species using information geometry.
Priority order:
1) Use Euclidean distance in learned manifold coordinates (x, y, z) if available
2) Fallback to cosine similarity on feature vectors
3) Fallback to first available opposite-species row
"""
selected_source = selected_row['source']
opposite_source = 'Human' if selected_source == 'Dog' else 'Dog'
opposite_data = df_combined[df_combined['source'] == opposite_source]
if len(opposite_data) == 0:
return None
# 1) Prefer geometry in manifold if available
if all(col in selected_row.index for col in ['x', 'y', 'z']) and \
all(col in opposite_data.columns for col in ['x', 'y', 'z']):
sx, sy, sz = float(selected_row['x']), float(selected_row['y']), float(selected_row['z'])
coords = opposite_data[['x', 'y', 'z']].to_numpy(dtype=float)
diffs = coords - np.array([sx, sy, sz], dtype=float)
dists = np.sqrt(np.sum(diffs * diffs, axis=1))
nearest_idx = int(np.argmin(dists))
return opposite_data.iloc[nearest_idx]
# 2) Fallback to feature-space cosine similarity
feature_cols = [c for c in df_combined.columns if c.startswith("feature_")]
if feature_cols:
selected_features = selected_row[feature_cols].values.reshape(1, -1)
selected_features = np.nan_to_num(selected_features)
opposite_features = np.nan_to_num(opposite_data[feature_cols].values)
similarities = cosine_similarity(selected_features, opposite_features)[0]
most_similar_idx = int(np.argmax(similarities))
return opposite_data.iloc[most_similar_idx]
# 3) Fallback: first available
return opposite_data.iloc[0]
# Cache for performance
_audio_path_cache = {}
_cmt_data_cache = {}
def resolve_audio_path(row: pd.Series) -> str:
"""Resolve audio file paths intelligently."""
basename = str(row.get("filepath", ""))
source = row.get("source", "")
label = row.get("label", "")
cache_key = f"{source}:{label}:{basename}"
if cache_key in _audio_path_cache:
return _audio_path_cache[cache_key]
resolved_path = basename
if source == "Dog":
expected_path = os.path.join(DOG_AUDIO_BASE_PATH, label, basename)
if os.path.exists(expected_path):
resolved_path = expected_path
else:
expected_path = os.path.join(DOG_AUDIO_BASE_PATH, basename)
if os.path.exists(expected_path):
resolved_path = expected_path
elif source == "Human":
if os.path.isdir(HUMAN_AUDIO_BASE_PATH):
for actor_folder in os.listdir(HUMAN_AUDIO_BASE_PATH):
if actor_folder.startswith("Actor_"):
expected_path = os.path.join(HUMAN_AUDIO_BASE_PATH, actor_folder, basename)
if os.path.exists(expected_path):
resolved_path = expected_path
break
_audio_path_cache[cache_key] = resolved_path
return resolved_path
def get_cmt_data_from_csv(row: pd.Series, lens: str):
"""
Extract CMT data from CSV and reconstruct visualization data.
Uses real diagnostic values but creates visualization points.
"""
try:
alpha_col = f"diag_alpha_{lens}"
srl_col = f"diag_srl_{lens}"
alpha_val = row.get(alpha_col, 0.0)
srl_val = row.get(srl_col, 0.0)
# Create visualization points based on real diagnostics
# Number of points proportional to complexity
n_points = int(min(200, max(50, srl_val * 2)))
# Use deterministic generation based on file hash for consistency
seed = hash(str(row['filepath'])) % 2**32
rng = np.random.RandomState(seed)
# Generate points in complex plane with spread based on alpha
angles = np.linspace(0, 2*np.pi, n_points)
radii = alpha_val * (1 + 0.3 * rng.random(n_points))
z = radii * np.exp(1j * angles)
# Apply lens-like transformation for visualization
w = z * np.exp(1j * srl_val * np.angle(z) / 10)
# Create holographic field
phi = alpha_val * w * np.exp(1j * np.angle(w) * srl_val / 20)
return {
"phi": phi,
"w": w,
"z": z,
"original_count": n_points,
"final_count": len(phi),
"alpha": alpha_val,
"srl": srl_val
}
except Exception as e:
print(f"Error extracting CMT data: {e}")
return None
def generate_holographic_field(z: np.ndarray, phi: np.ndarray, resolution: int):
"""Generate continuous field for visualization."""
if z is None or phi is None or len(z) < 4:
return None
points = np.vstack([np.real(z), np.imag(z)]).T
grid_x, grid_y = np.mgrid[
np.min(points[:,0]):np.max(points[:,0]):complex(0, resolution),
np.min(points[:,1]):np.max(points[:,1]):complex(0, resolution)
]
# Use linear interpolation for more stable results
grid_phi_real = griddata(points, np.real(phi), (grid_x, grid_y), method='linear')
grid_phi_imag = griddata(points, np.imag(phi), (grid_x, grid_y), method='linear')
# Fill NaN values with nearest neighbor
mask = np.isnan(grid_phi_real)
if np.any(mask):
grid_phi_real[mask] = griddata(points, np.real(phi), (grid_x[mask], grid_y[mask]), method='nearest')
mask = np.isnan(grid_phi_imag)
if np.any(mask):
grid_phi_imag[mask] = griddata(points, np.imag(phi), (grid_x[mask], grid_y[mask]), method='nearest')
grid_phi = grid_phi_real + 1j * grid_phi_imag
return grid_x, grid_y, grid_phi
# ---------------------------------------------------------------
# Advanced Visualization Functions
# ---------------------------------------------------------------
def calculate_species_boundary(df_combined):
"""Calculate geometric boundary between species."""
from sklearn.svm import SVC
human_data = df_combined[df_combined['source'] == 'Human'][['x', 'y', 'z']].values
dog_data = df_combined[df_combined['source'] == 'Dog'][['x', 'y', 'z']].values
if len(human_data) < 2 or len(dog_data) < 2:
return None
X = np.vstack([human_data, dog_data])
y = np.hstack([np.ones(len(human_data)), np.zeros(len(dog_data))])
svm = SVC(kernel='rbf', probability=True)
svm.fit(X, y)
x_range = np.linspace(X[:, 0].min(), X[:, 0].max(), 20)
y_range = np.linspace(X[:, 1].min(), X[:, 1].max(), 20)
z_range = np.linspace(X[:, 2].min(), X[:, 2].max(), 20)
xx, yy = np.meshgrid(x_range, y_range)
boundary_points = []
for z_val in z_range:
grid_points = np.c_[xx.ravel(), yy.ravel(), np.full(xx.ravel().shape, z_val)]
probabilities = svm.predict_proba(grid_points)[:, 1]
boundary_mask = np.abs(probabilities - 0.5) < 0.05
if np.any(boundary_mask):
boundary_points.extend(grid_points[boundary_mask])
return np.array(boundary_points) if boundary_points else None
def create_enhanced_manifold_plot(df_filtered, lens_selected, color_scheme, point_size,
show_boundary, show_trajectories):
"""Create main 3D manifold visualization."""
alpha_col = f"diag_alpha_{lens_selected}"
srl_col = f"diag_srl_{lens_selected}"
# Color mapping
if color_scheme == "Species":
color_values = [1 if s == "Human" else 0 for s in df_filtered['source']]
colorscale = [[0, '#1f77b4'], [1, '#ff7f0e']]
colorbar_title = "Species"
elif color_scheme == "Emotion":
unique_emotions = df_filtered['label'].unique()
emotion_map = {emotion: i for i, emotion in enumerate(unique_emotions)}
color_values = [emotion_map[label] for label in df_filtered['label']]
colorscale = 'Viridis'
colorbar_title = "Emotional State"
elif color_scheme == "CMT_Alpha":
color_values = df_filtered[alpha_col].values if alpha_col in df_filtered.columns else df_filtered.index
colorscale = 'Plasma'
colorbar_title = f"CMT Alpha ({lens_selected})"
elif color_scheme == "CMT_SRL":
color_values = df_filtered[srl_col].values if srl_col in df_filtered.columns else df_filtered.index
colorscale = 'Turbo'
colorbar_title = f"SRL ({lens_selected})"
else:
color_values = df_filtered['cluster'].values
colorscale = 'Plotly3'
colorbar_title = "Cluster"
# Create hover text
hover_text = []
for _, row in df_filtered.iterrows():
hover_info = f"""
{row['source']}: {row['label']}
File: {row['filepath']}
Coordinates: ({row['x']:.3f}, {row['y']:.3f}, {row['z']:.3f})
"""
if alpha_col in df_filtered.columns:
hover_info += f"
α: {row[alpha_col]:.4f}"
if srl_col in df_filtered.columns:
hover_info += f"
SRL: {row[srl_col]:.4f}"
hover_text.append(hover_info)
fig = go.Figure()
# Main scatter plot
fig.add_trace(go.Scatter3d(
x=df_filtered['x'],
y=df_filtered['y'],
z=df_filtered['z'],
mode='markers',
marker=dict(
size=point_size,
color=color_values,
colorscale=colorscale,
showscale=True,
colorbar=dict(title=colorbar_title),
opacity=0.8,
line=dict(width=0.5, color='rgba(50,50,50,0.5)')
),
text=hover_text,
hovertemplate='%{text}',
name='Communications'
))
# Add species boundary
if show_boundary:
boundary_points = calculate_species_boundary(df_filtered)
if boundary_points is not None and len(boundary_points) > 0:
fig.add_trace(go.Scatter3d(
x=boundary_points[:, 0],
y=boundary_points[:, 1],
z=boundary_points[:, 2],
mode='markers',
marker=dict(size=2, color='red', opacity=0.3),
name='Species Boundary',
hovertemplate='Species Boundary'
))
# Add trajectories
if show_trajectories:
emotion_colors = {
'angry': '#FF4444', 'happy': '#44FF44', 'sad': '#4444FF',
'fearful': '#FF44FF', 'neutral': '#FFFF44', 'surprised': '#44FFFF',
'disgusted': '#FF8844', 'bark': '#FF6B35', 'growl': '#8B4513',
'whine': '#9370DB', 'pant': '#20B2AA', 'speech': '#1E90FF',
'laugh': '#FFD700', 'cry': '#4169E1', 'shout': '#DC143C'
}
for i, emotion in enumerate(df_filtered['label'].unique()):
emotion_data = df_filtered[df_filtered['label'] == emotion]
if len(emotion_data) > 1:
base_colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7']
emotion_color = emotion_colors.get(emotion.lower(), base_colors[i % len(base_colors)])
sort_indices = np.argsort(emotion_data['x'].values)
x_sorted = emotion_data['x'].values[sort_indices]
y_sorted = emotion_data['y'].values[sort_indices]
z_sorted = emotion_data['z'].values[sort_indices]
fig.add_trace(go.Scatter3d(
x=x_sorted, y=y_sorted, z=z_sorted,
mode='lines+markers',
line=dict(width=4, color=emotion_color, dash='dash'),
marker=dict(size=3, color=emotion_color, opacity=0.8),
name=f'{emotion.title()} Path',
showlegend=True,
hovertemplate=f'{emotion.title()} Path
X: %{{x:.3f}}
Y: %{{y:.3f}}
Z: %{{z:.3f}}',
opacity=0.7
))
fig.update_layout(
title={
'text': "Shared Pattern Space of Audio Signals",
'x': 0.5,
'xanchor': 'center'
},
scene=dict(
xaxis_title='Manifold Dimension 1',
yaxis_title='Manifold Dimension 2',
zaxis_title='Manifold Dimension 3',
camera=dict(eye=dict(x=1.5, y=1.5, z=1.5)),
bgcolor='rgba(0,0,0,0)',
aspectmode='cube'
),
margin=dict(l=0, r=0, b=0, t=60)
)
return fig
def create_holography_plot(z, phi, resolution, wavelength):
"""Create holographic field visualization."""
field_data = generate_holographic_field(z, phi, resolution)
if field_data is None:
return go.Figure(layout={"title": "Insufficient data for holography"})
grid_x, grid_y, grid_phi = field_data
mag_phi = np.abs(grid_phi)
phase_phi = np.angle(grid_phi)
def wavelength_to_rgb(wl):
if 380 <= wl < 440: return f'rgb({int(-(wl - 440) / (440 - 380) * 255)}, 0, 255)'
elif 440 <= wl < 495: return f'rgb(0, {int((wl - 440) / (495 - 440) * 255)}, 255)'
elif 495 <= wl < 570: return f'rgb(0, 255, {int(-(wl - 570) / (570 - 495) * 255)})'
elif 570 <= wl < 590: return f'rgb({int((wl - 570) / (590 - 570) * 255)}, 255, 0)'
elif 590 <= wl < 620: return f'rgb(255, {int(-(wl - 620) / (620 - 590) * 255)}, 0)'
elif 620 <= wl <= 750: return 'rgb(255, 0, 0)'
return 'rgb(255,255,255)'
mid_color = wavelength_to_rgb(wavelength)
custom_colorscale = [[0, 'rgb(20,0,40)'], [0.5, mid_color], [1, 'rgb(255,255,255)']]
fig = go.Figure()
# Holographic surface
fig.add_trace(go.Surface(
x=grid_x, y=grid_y, z=mag_phi,
surfacecolor=phase_phi,
colorscale=custom_colorscale,
cmin=-np.pi, cmax=np.pi,
colorbar=dict(title='Phase'),
name='Holographic Field',
contours_z=dict(show=True, usecolormap=True, highlightcolor="limegreen", project_z=True)
))
# Data points
fig.add_trace(go.Scatter3d(
x=np.real(z), y=np.imag(z), z=np.abs(phi) + 0.05,
mode='markers',
marker=dict(size=3, color='black', symbol='x'),
name='Data Points'
))
# Vector flow field
if resolution >= 30:
grad_y, grad_x = np.gradient(mag_phi)
sample_rate = max(1, resolution // 15)
fig.add_trace(go.Cone(
x=grid_x[::sample_rate, ::sample_rate].flatten(),
y=grid_y[::sample_rate, ::sample_rate].flatten(),
z=mag_phi[::sample_rate, ::sample_rate].flatten(),
u=-grad_x[::sample_rate, ::sample_rate].flatten(),
v=-grad_y[::sample_rate, ::sample_rate].flatten(),
w=np.full_like(mag_phi[::sample_rate, ::sample_rate].flatten(), -0.1),
sizemode="absolute", sizeref=0.1,
anchor="tip",
colorscale='Greys',
showscale=False,
name='Vector Flow'
))
fig.update_layout(
title="Interactive Holographic Field Reconstruction",
scene=dict(
xaxis_title="Re(z)",
yaxis_title="Im(z)",
zaxis_title="|Φ|"
),
margin=dict(l=0, r=0, b=0, t=40)
)
return fig
def create_dual_holography_plot(z1, phi1, z2, phi2, resolution, wavelength, title1="Primary", title2="Comparison"):
"""Create side-by-side holographic visualizations."""
field_data1 = generate_holographic_field(z1, phi1, resolution)
field_data2 = generate_holographic_field(z2, phi2, resolution)
if field_data1 is None or field_data2 is None:
return go.Figure(layout={"title": "Insufficient data for dual holography"})
grid_x1, grid_y1, grid_phi1 = field_data1
grid_x2, grid_y2, grid_phi2 = field_data2
mag_phi1, phase_phi1 = np.abs(grid_phi1), np.angle(grid_phi1)
mag_phi2, phase_phi2 = np.abs(grid_phi2), np.angle(grid_phi2)
def wavelength_to_rgb(wl):
if 380 <= wl < 440: return f'rgb({int(-(wl - 440) / (440 - 380) * 255)}, 0, 255)'
elif 440 <= wl < 495: return f'rgb(0, {int((wl - 440) / (495 - 440) * 255)}, 255)'
elif 495 <= wl < 570: return f'rgb(0, 255, {int(-(wl - 570) / (570 - 495) * 255)})'
elif 570 <= wl < 590: return f'rgb({int((wl - 570) / (590 - 570) * 255)}, 255, 0)'
elif 590 <= wl < 620: return f'rgb(255, {int(-(wl - 620) / (620 - 590) * 255)}, 0)'
elif 620 <= wl <= 750: return 'rgb(255, 0, 0)'
return 'rgb(255,255,255)'
mid_color = wavelength_to_rgb(wavelength)
custom_colorscale = [[0, 'rgb(20,0,40)'], [0.5, mid_color], [1, 'rgb(255,255,255)']]
fig = make_subplots(
rows=1, cols=2,
specs=[[{'type': 'surface'}, {'type': 'surface'}]],
subplot_titles=[title1, title2]
)
# Primary hologram
fig.add_trace(go.Surface(
x=grid_x1, y=grid_y1, z=mag_phi1,
surfacecolor=phase_phi1,
colorscale=custom_colorscale,
cmin=-np.pi, cmax=np.pi,
showscale=False,
name=title1,
contours_z=dict(show=True, usecolormap=True, highlightcolor="limegreen", project_z=True)
), row=1, col=1)
# Comparison hologram
fig.add_trace(go.Surface(
x=grid_x2, y=grid_y2, z=mag_phi2,
surfacecolor=phase_phi2,
colorscale=custom_colorscale,
cmin=-np.pi, cmax=np.pi,
showscale=False,
name=title2,
contours_z=dict(show=True, usecolormap=True, highlightcolor="limegreen", project_z=True)
), row=1, col=2)
# Add data points
fig.add_trace(go.Scatter3d(
x=np.real(z1), y=np.imag(z1), z=np.abs(phi1) + 0.05,
mode='markers', marker=dict(size=3, color='black', symbol='x'),
name=f'{title1} Points', showlegend=False
), row=1, col=1)
fig.add_trace(go.Scatter3d(
x=np.real(z2), y=np.imag(z2), z=np.abs(phi2) + 0.05,
mode='markers', marker=dict(size=3, color='black', symbol='x'),
name=f'{title2} Points', showlegend=False
), row=1, col=2)
fig.update_layout(
title="Side-by-Side Geometric Comparison",
scene=dict(
xaxis_title="Re(z)", yaxis_title="Im(z)", zaxis_title="|Φ|",
camera=dict(eye=dict(x=1.5, y=1.5, z=1.5))
),
scene2=dict(
xaxis_title="Re(z)", yaxis_title="Im(z)", zaxis_title="|Φ|",
camera=dict(eye=dict(x=1.5, y=1.5, z=1.5))
),
margin=dict(l=0, r=0, b=0, t=60),
height=600
)
return fig
def create_diagnostic_plots(z, w):
"""Create diagnostic visualization."""
if z is None or w is None:
return go.Figure(layout={"title": "Insufficient data for diagnostics"})
fig = go.Figure()
fig.add_trace(go.Scatter(
x=np.real(z), y=np.imag(z), mode='markers',
marker=dict(size=5, color='blue', opacity=0.6),
name='Aperture (z)'
))
fig.add_trace(go.Scatter(
x=np.real(w), y=np.imag(w), mode='markers',
marker=dict(size=5, color='red', opacity=0.6, symbol='x'),
name='Lens Response (w)'
))
fig.update_layout(
title="Diagnostic View: Aperture and Lens Response",
xaxis_title="Real Part",
yaxis_title="Imaginary Part",
legend_title="Signal Stage",
margin=dict(l=20, r=20, t=60, b=20)
)
return fig
def create_entropy_geometry_plot(phi: np.ndarray):
"""Create entropy analysis visualization."""
if phi is None or len(phi) < 2:
return go.Figure(layout={"title": "Insufficient data for entropy analysis"})
magnitudes = np.abs(phi)
phases = np.angle(phi)
mag_hist, _ = np.histogram(magnitudes, bins='auto', density=True)
phase_hist, _ = np.histogram(phases, bins='auto', density=True)
mag_entropy = shannon_entropy(mag_hist + 1e-10)
phase_entropy = shannon_entropy(phase_hist + 1e-10)
fig = make_subplots(rows=1, cols=2, subplot_titles=(
f"Magnitude Distribution (Entropy: {mag_entropy:.3f})",
f"Phase Distribution (Entropy: {phase_entropy:.3f})"
))
fig.add_trace(go.Histogram(x=magnitudes, name='Magnitude', nbinsx=50), row=1, col=1)
fig.add_trace(go.Histogram(x=phases, name='Phase', nbinsx=50), row=1, col=2)
fig.update_layout(
title_text="Information-Entropy Geometry",
showlegend=False,
bargap=0.1,
margin=dict(l=20, r=20, t=60, b=20)
)
return fig
def create_2d_projection_plot(df_filtered, lens_selected, color_scheme):
"""Create 2D projection plot."""
alpha_col = f"diag_alpha_{lens_selected}"
srl_col = f"diag_srl_{lens_selected}"
# Color mapping
if color_scheme == "Species":
color_values = [1 if s == "Human" else 0 for s in df_filtered['source']]
colorscale = [[0, '#1f77b4'], [1, '#ff7f0e']]
colorbar_title = "Species"
elif color_scheme == "Emotion":
unique_emotions = df_filtered['label'].unique()
emotion_map = {emotion: i for i, emotion in enumerate(unique_emotions)}
color_values = [emotion_map[label] for label in df_filtered['label']]
colorscale = 'Viridis'
colorbar_title = "Emotional State"
else:
color_values = df_filtered['cluster'].values
colorscale = 'Plotly3'
colorbar_title = "Cluster"
fig = go.Figure()
fig.add_trace(go.Scatter(
x=df_filtered['x'],
y=df_filtered['y'],
mode='markers',
marker=dict(
size=8,
color=color_values,
colorscale=colorscale,
showscale=True,
colorbar=dict(title=colorbar_title),
opacity=0.7,
line=dict(width=0.5, color='rgba(50,50,50,0.5)')
),
text=[f"{row['source']}: {row['label']}" for _, row in df_filtered.iterrows()],
name='Communications'
))
fig.update_layout(
title="2D Manifold Projection",
xaxis_title='Dimension 1',
yaxis_title='Dimension 2',
margin=dict(l=0, r=0, b=0, t=40)
)
return fig
def create_density_heatmap(df_filtered):
"""Create density heatmap."""
from scipy.stats import gaussian_kde
if len(df_filtered) < 10:
return go.Figure(layout={"title": "Insufficient data for density plot"})
x = df_filtered['x'].values
y = df_filtered['y'].values
# Create density estimation
try:
kde = gaussian_kde(np.vstack([x, y]))
# Create grid
x_range = np.linspace(x.min(), x.max(), 50)
y_range = np.linspace(y.min(), y.max(), 50)
X, Y = np.meshgrid(x_range, y_range)
positions = np.vstack([X.ravel(), Y.ravel()])
Z = kde(positions).reshape(X.shape)
fig = go.Figure(data=go.Heatmap(
x=x_range,
y=y_range,
z=Z,
colorscale='Viridis',
showscale=True
))
# Add scatter points
fig.add_trace(go.Scatter(
x=x, y=y,
mode='markers',
marker=dict(size=4, color='white', opacity=0.8),
name='Data Points'
))
fig.update_layout(
title="Communication Density Landscape",
xaxis_title='Dimension 1',
yaxis_title='Dimension 2',
margin=dict(l=0, r=0, b=0, t=40)
)
return fig
except:
return go.Figure(layout={"title": "Could not create density plot"})
def create_feature_distributions(df_filtered, lens_selected):
"""Create feature distribution plots."""
alpha_col = f"diag_alpha_{lens_selected}"
srl_col = f"diag_srl_{lens_selected}"
fig = make_subplots(
rows=2, cols=2,
subplot_titles=(
f"Alpha Distribution ({lens_selected})",
f"SRL Distribution ({lens_selected})",
"Species Distribution",
"Emotion Distribution"
),
specs=[[{"type": "histogram"}, {"type": "histogram"}],
[{"type": "bar"}, {"type": "bar"}]]
)
# Alpha distribution
if alpha_col in df_filtered.columns:
fig.add_trace(go.Histogram(
x=df_filtered[alpha_col],
name="Alpha",
nbinsx=30,
marker_color='lightblue'
), row=1, col=1)
# SRL distribution
if srl_col in df_filtered.columns:
fig.add_trace(go.Histogram(
x=df_filtered[srl_col],
name="SRL",
nbinsx=30,
marker_color='lightgreen'
), row=1, col=2)
# Species distribution
species_counts = df_filtered['source'].value_counts()
fig.add_trace(go.Bar(
x=species_counts.index,
y=species_counts.values,
name="Species",
marker_color=['#1f77b4', '#ff7f0e']
), row=2, col=1)
# Emotion distribution
emotion_counts = df_filtered['label'].value_counts().head(10)
fig.add_trace(go.Bar(
x=emotion_counts.index,
y=emotion_counts.values,
name="Emotions",
marker_color='lightcoral'
), row=2, col=2)
fig.update_layout(
title_text="Statistical Distributions",
showlegend=False,
margin=dict(l=0, r=0, b=0, t=60)
)
return fig
def create_cluster_analysis(df_filtered):
"""Create cluster analysis visualization."""
fig = make_subplots(
rows=1, cols=2,
subplot_titles=("Cluster Distribution", "Cluster Composition"),
specs=[[{"type": "bar"}, {"type": "bar"}]]
)
# Cluster distribution
cluster_counts = df_filtered['cluster'].value_counts().sort_index()
fig.add_trace(go.Bar(
x=[f"C{i}" for i in cluster_counts.index],
y=cluster_counts.values,
name="Cluster Size",
marker_color='skyblue'
), row=1, col=1)
# Species composition per cluster
cluster_species = df_filtered.groupby(['cluster', 'source']).size().unstack(fill_value=0)
if len(cluster_species.columns) > 0:
for species in cluster_species.columns:
fig.add_trace(go.Bar(
x=[f"C{i}" for i in cluster_species.index],
y=cluster_species[species],
name=species,
marker_color='#1f77b4' if species == 'Human' else '#ff7f0e'
), row=1, col=2)
fig.update_layout(
title_text="Cluster Analysis",
margin=dict(l=0, r=0, b=0, t=60)
)
return fig
def create_similarity_matrix(df_filtered, lens_selected):
"""Create species similarity matrix."""
alpha_col = f"diag_alpha_{lens_selected}"
srl_col = f"diag_srl_{lens_selected}"
# Calculate mean values for each species-emotion combination
similarity_data = []
for species in df_filtered['source'].unique():
for emotion in df_filtered['label'].unique():
subset = df_filtered[(df_filtered['source'] == species) & (df_filtered['label'] == emotion)]
if len(subset) > 0:
alpha_mean = subset[alpha_col].mean() if alpha_col in subset.columns else 0
srl_mean = subset[srl_col].mean() if srl_col in subset.columns else 0
similarity_data.append({
'species': species,
'emotion': emotion,
'alpha': alpha_mean,
'srl': srl_mean,
'combined': alpha_mean + srl_mean
})
if not similarity_data:
return go.Figure(layout={"title": "No data for similarity matrix"})
similarity_df = pd.DataFrame(similarity_data)
pivot_table = similarity_df.pivot(index='emotion', columns='species', values='combined')
fig = go.Figure(data=go.Heatmap(
z=pivot_table.values,
x=pivot_table.columns,
y=pivot_table.index,
colorscale='RdYlBu_r',
showscale=True,
colorbar=dict(title="Similarity Score")
))
fig.update_layout(
title="Cross-Species Similarity Matrix",
margin=dict(l=0, r=0, b=0, t=40)
)
return fig
def calculate_live_statistics(df_filtered, lens_selected):
"""Calculate live statistics for the dataset."""
alpha_col = f"diag_alpha_{lens_selected}"
srl_col = f"diag_srl_{lens_selected}"
stats = {
'total_samples': len(df_filtered),
'species_counts': df_filtered['source'].value_counts().to_dict(),
'emotion_counts': len(df_filtered['label'].unique()),
'cluster_count': len(df_filtered['cluster'].unique())
}
if alpha_col in df_filtered.columns:
stats['alpha_mean'] = df_filtered[alpha_col].mean()
stats['alpha_std'] = df_filtered[alpha_col].std()
if srl_col in df_filtered.columns:
stats['srl_mean'] = df_filtered[srl_col].mean()
stats['srl_std'] = df_filtered[srl_col].std()
# Format as HTML
html_content = f"""
📊 Live Dataset Statistics
Total Samples: {stats['total_samples']}
Species:
{' | '.join([f"{k}: {v}" for k, v in stats['species_counts'].items()])}
Emotions: {stats['emotion_counts']}
Clusters: {stats['cluster_count']}
"""
if 'alpha_mean' in stats:
html_content += f"""
Alpha ({lens_selected}):
μ={stats['alpha_mean']:.3f}, σ={stats['alpha_std']:.3f}
"""
if 'srl_mean' in stats:
html_content += f"""
SRL ({lens_selected}):
μ={stats['srl_mean']:.3f}, σ={stats['srl_std']:.3f}
"""
html_content += "
"
return html_content
def update_manifold_visualization(species_selection, emotion_selection, lens_selection,
alpha_min, alpha_max, srl_min, srl_max,
point_size, show_boundary, show_trajectories, color_scheme):
"""Update all manifold visualizations with filters."""
df_filtered = df_combined.copy()
if species_selection:
df_filtered = df_filtered[df_filtered['source'].isin(species_selection)]
if emotion_selection:
df_filtered = df_filtered[df_filtered['label'].isin(emotion_selection)]
alpha_col = f"diag_alpha_{lens_selection}"
srl_col = f"diag_srl_{lens_selection}"
if alpha_col in df_filtered.columns:
df_filtered = df_filtered[
(df_filtered[alpha_col] >= alpha_min) &
(df_filtered[alpha_col] <= alpha_max)
]
if srl_col in df_filtered.columns:
df_filtered = df_filtered[
(df_filtered[srl_col] >= srl_min) &
(df_filtered[srl_col] <= srl_max)
]
if len(df_filtered) == 0:
empty_fig = go.Figure().add_annotation(
text="No data points match the current filters",
xref="paper", yref="paper", x=0.5, y=0.5, showarrow=False
)
empty_stats = "No data available
"
return empty_fig, empty_fig, empty_fig, empty_fig, empty_fig, empty_fig, empty_stats
# Create all visualizations
main_plot = create_enhanced_manifold_plot(
df_filtered, lens_selection, color_scheme, point_size,
show_boundary, show_trajectories
)
projection_2d = create_2d_projection_plot(df_filtered, lens_selection, color_scheme)
density_plot = create_density_heatmap(df_filtered)
feature_dists = create_feature_distributions(df_filtered, lens_selection)
cluster_plot = create_cluster_analysis(df_filtered)
similarity_plot = create_similarity_matrix(df_filtered, lens_selection)
stats_html = calculate_live_statistics(df_filtered, lens_selection)
return main_plot, projection_2d, density_plot, feature_dists, cluster_plot, similarity_plot, stats_html
def export_filtered_data(species_selection, emotion_selection, lens_selection,
alpha_min, alpha_max, srl_min, srl_max):
"""Export filtered dataset for analysis."""
import tempfile
import json
df_filtered = df_combined.copy()
if species_selection:
df_filtered = df_filtered[df_filtered['source'].isin(species_selection)]
if emotion_selection:
df_filtered = df_filtered[df_filtered['label'].isin(emotion_selection)]
alpha_col = f"diag_alpha_{lens_selection}"
srl_col = f"diag_srl_{lens_selection}"
if alpha_col in df_filtered.columns:
df_filtered = df_filtered[
(df_filtered[alpha_col] >= alpha_min) &
(df_filtered[alpha_col] <= alpha_max)
]
if srl_col in df_filtered.columns:
df_filtered = df_filtered[
(df_filtered[srl_col] >= srl_min) &
(df_filtered[srl_col] <= srl_max)
]
if len(df_filtered) == 0:
return "❌ No data to export with current filters
"
# Create export summary
export_summary = {
"export_timestamp": pd.Timestamp.now().isoformat(),
"total_samples": len(df_filtered),
"species_counts": df_filtered['source'].value_counts().to_dict(),
"emotion_types": df_filtered['label'].unique().tolist(),
"lens_used": lens_selection,
"filters_applied": {
"species": species_selection,
"emotions": emotion_selection,
"alpha_range": [alpha_min, alpha_max],
"srl_range": [srl_min, srl_max]
}
}
summary_html = f"""
✅ Export Ready
Samples: {export_summary['total_samples']}
Species: {', '.join([f"{k}({v})" for k, v in export_summary['species_counts'].items()])}
Emotions: {len(export_summary['emotion_types'])} types
Lens: {lens_selection}
Data ready for download via your browser's dev tools or notebook integration.
"""
return summary_html
# ---------------------------------------------------------------
# Gradio Interface
# ---------------------------------------------------------------
with gr.Blocks(theme='Nymbo/Alyx_Theme') as demo:
gr.Markdown("""
# **Entronaut: A Visual Explorer for Information Geometry**
*Turn complex data into visible patterns.*
---
### What is Entronaut?
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.
This demo applies Entronaut to audio recordings of human and dog sounds to reveal their underlying mathematical similarities.
### How is this different from tools like FFT?
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.
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.
### What is Information Geometry?
In simple terms, Information Geometry is a field of math that lets us measure the "distance" and "shape" of information.
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".
**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.
""")
with gr.Tabs():
with gr.TabItem("🎵 Audio Pattern Explorer"):
gr.Markdown("## Exploring the Geometric Patterns in Sound")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 🔬 **Analysis Controls**")
species_filter = gr.CheckboxGroup(
label="Species Selection",
choices=["Human", "Dog"],
value=["Human", "Dog"]
)
emotion_filter = gr.CheckboxGroup(
label="Emotional States",
choices=list(df_combined['label'].unique()),
value=list(df_combined['label'].unique())
)
lens_selector = gr.Dropdown(
label="Mathematical Lens",
choices=["gamma", "zeta", "airy", "bessel"],
value="gamma"
)
with gr.Accordion("🎛️ Advanced Filters", open=False):
alpha_min = gr.Slider(label="Alpha Min", minimum=0, maximum=5, value=0, step=0.1)
alpha_max = gr.Slider(label="Alpha Max", minimum=0, maximum=5, value=5, step=0.1)
srl_min = gr.Slider(label="SRL Min", minimum=0, maximum=100, value=0, step=1)
srl_max = gr.Slider(label="SRL Max", minimum=0, maximum=100, value=100, step=1)
with gr.Accordion("🎨 Visualization Options", open=True):
point_size = gr.Slider(label="Point Size", minimum=2, maximum=15, value=6, step=1)
show_species_boundary = gr.Checkbox(label="Show Species Boundary", value=True)
show_trajectories = gr.Checkbox(label="Show Trajectories", value=False)
color_scheme = gr.Dropdown(
label="Color Scheme",
choices=["Species", "Emotion", "CMT_Alpha", "CMT_SRL", "Cluster"],
value="Species"
)
with gr.Accordion("📊 Live Statistics", open=True):
stats_html = gr.HTML(label="Dataset Statistics")
similarity_matrix = gr.Plot(label="Species Similarity Matrix")
with gr.Accordion("💾 Data Export", open=False):
gr.Markdown("**Export filtered dataset for further analysis**")
export_button = gr.Button("📥 Export Filtered Data", variant="secondary")
export_status = gr.HTML("")
with gr.Column(scale=3):
manifold_plot = gr.Plot(label="Universal Communication Manifold")
with gr.Row():
projection_2d = gr.Plot(label="2D Projection")
density_plot = gr.Plot(label="Density Heatmap")
with gr.Row():
feature_distributions = gr.Plot(label="Feature Distributions")
cluster_analysis = gr.Plot(label="Cluster Analysis")
# Wire up events
manifold_inputs = [
species_filter, emotion_filter, lens_selector,
alpha_min, alpha_max, srl_min, srl_max,
point_size, show_species_boundary, show_trajectories, color_scheme
]
manifold_outputs = [
manifold_plot, projection_2d, density_plot,
feature_distributions, cluster_analysis, similarity_matrix, stats_html
]
for component in manifold_inputs:
component.change(
update_manifold_visualization,
inputs=manifold_inputs,
outputs=manifold_outputs
)
# Wire up export button
export_button.click(
export_filtered_data,
inputs=[
species_filter, emotion_filter, lens_selector,
alpha_min, alpha_max, srl_min, srl_max
],
outputs=[export_status]
)
with gr.TabItem("🔬 Interactive Holography"):
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### Cross-Species Holography")
species_dropdown = gr.Dropdown(
label="Select Species",
choices=["Dog", "Human"],
value="Dog"
)
dog_files = df_combined[df_combined["source"] == "Dog"]["filepath"].tolist()
human_files = df_combined[df_combined["source"] == "Human"]["filepath"].tolist()
primary_dropdown = gr.Dropdown(
label="Primary File",
choices=dog_files,
value=dog_files[0] if dog_files else None
)
neighbor_dropdown = gr.Dropdown(
label="Cross-Species Neighbor",
choices=human_files,
value=human_files[0] if human_files else None
)
holo_lens_dropdown = gr.Dropdown(
label="CMT Lens",
choices=["gamma", "zeta", "airy", "bessel"],
value="gamma"
)
holo_resolution_slider = gr.Slider(
label="Field Resolution",
minimum=20, maximum=100, step=5, value=40
)
holo_wavelength_slider = gr.Slider(
label="Wavelength (nm)",
minimum=380, maximum=750, step=5, value=550
)
primary_info_html = gr.HTML(label="Primary Info")
neighbor_info_html = gr.HTML(label="Neighbor Info")
with gr.Column(scale=2):
dual_holography_plot = gr.Plot(label="Holographic Comparison")
diagnostic_plot = gr.Plot(label="Diagnostic Analysis")
entropy_plot = gr.Plot(label="Entropy Geometry")
def update_cross_species_view(species, primary_file, neighbor_file, lens, resolution, wavelength):
if not primary_file:
empty_fig = go.Figure(layout={"title": "Select a primary file"})
return empty_fig, empty_fig, empty_fig, "", "", gr.Dropdown()
primary_row = df_combined[
(df_combined["filepath"] == primary_file) &
(df_combined["source"] == species)
].iloc[0] if len(df_combined[
(df_combined["filepath"] == primary_file) &
(df_combined["source"] == species)
]) > 0 else None
if primary_row is None:
empty_fig = go.Figure(layout={"title": "Primary file not found"})
return empty_fig, empty_fig, empty_fig, "", ""
auto_neighbor_row = find_nearest_cross_species_neighbor(primary_row, df_combined)
if not neighbor_file and auto_neighbor_row is not None:
neighbor_row = auto_neighbor_row
else:
opposite_species = 'Human' if species == 'Dog' else 'Dog'
neighbor_row = df_combined[
(df_combined["filepath"] == neighbor_file) &
(df_combined["source"] == opposite_species)
].iloc[0] if len(df_combined[
(df_combined["filepath"] == neighbor_file) &
(df_combined["source"] == opposite_species)
]) > 0 else None
primary_cmt = get_cmt_data_from_csv(primary_row, lens)
neighbor_cmt = get_cmt_data_from_csv(neighbor_row, lens) if neighbor_row is not None else None
if primary_cmt and neighbor_cmt:
primary_title = f"{species}: {primary_row.get('label', 'Unknown')}"
neighbor_title = f"{neighbor_row['source']}: {neighbor_row.get('label', 'Unknown')}"
dual_holo = create_dual_holography_plot(
primary_cmt["z"], primary_cmt["phi"],
neighbor_cmt["z"], neighbor_cmt["phi"],
resolution, wavelength, primary_title, neighbor_title
)
diag = create_diagnostic_plots(primary_cmt["z"], primary_cmt["w"])
entropy = create_entropy_geometry_plot(primary_cmt["phi"])
else:
dual_holo = go.Figure(layout={"title": "Error processing data"})
diag = go.Figure(layout={"title": "Error processing data"})
entropy = go.Figure(layout={"title": "Error processing data"})
if primary_cmt:
primary_info = f"""
Primary: {primary_row['filepath']}
Species: {primary_row['source']}
Label: {primary_row.get('label', 'N/A')}
Alpha: {primary_cmt['alpha']:.4f}
SRL: {primary_cmt['srl']:.4f}
"""
else:
primary_info = ""
if neighbor_cmt and neighbor_row is not None:
neighbor_info = f"""
Neighbor: {neighbor_row['filepath']}
Species: {neighbor_row['source']}
Label: {neighbor_row.get('label', 'N/A')}
Alpha: {neighbor_cmt['alpha']:.4f}
SRL: {neighbor_cmt['srl']:.4f}
"""
else:
neighbor_info = ""
# If we used auto neighbor, preselect it in the dropdown
neighbor_dropdown_value = gr.Dropdown(value=(neighbor_row['filepath'] if neighbor_row is not None else None))
return dual_holo, diag, entropy, primary_info, neighbor_info, neighbor_dropdown_value
def update_dropdowns_on_species_change(species):
species_files = df_combined[df_combined["source"] == species]["filepath"].tolist()
opposite_species = 'Human' if species == 'Dog' else 'Dog'
neighbor_files = df_combined[df_combined["source"] == opposite_species]["filepath"].tolist()
# Preselect first file for species and auto-pick nearest neighbor for that primary if available
primary_value = species_files[0] if species_files else ""
if primary_value:
primary_row = df_combined[(df_combined["filepath"] == primary_value) & (df_combined["source"] == species)].iloc[0]
neighbor_row = find_nearest_cross_species_neighbor(primary_row, df_combined)
neighbor_value = neighbor_row['filepath'] if neighbor_row is not None else (neighbor_files[0] if neighbor_files else "")
else:
neighbor_value = neighbor_files[0] if neighbor_files else ""
return (
gr.Dropdown(choices=species_files, value=primary_value),
gr.Dropdown(choices=neighbor_files, value=neighbor_value)
)
species_dropdown.change(
update_dropdowns_on_species_change,
inputs=[species_dropdown],
outputs=[primary_dropdown, neighbor_dropdown]
)
cross_species_inputs = [
species_dropdown, primary_dropdown, neighbor_dropdown,
holo_lens_dropdown, holo_resolution_slider, holo_wavelength_slider
]
cross_species_outputs = [
dual_holography_plot, diagnostic_plot, entropy_plot,
primary_info_html, neighbor_info_html,
neighbor_dropdown
]
for input_component in cross_species_inputs:
input_component.change(
update_cross_species_view,
inputs=cross_species_inputs,
outputs=cross_species_outputs
)
# When the primary file changes, auto-update neighbor selection to nearest geometry match
def on_primary_change(species, primary_value, neighbor_value, lens, resolution, wavelength):
# Reuse update_cross_species_view to recompute plots and auto neighbor
return update_cross_species_view(species, primary_value, None, lens, resolution, wavelength)
primary_dropdown.change(
on_primary_change,
inputs=cross_species_inputs,
outputs=cross_species_outputs
)
# Auto-load manifold visualizations on startup
demo.load(
update_manifold_visualization,
inputs=[
gr.State(["Human", "Dog"]), # species_filter default
gr.State(list(df_combined['label'].unique())), # emotion_filter default
gr.State("gamma"), # lens_selector default
gr.State(0), # alpha_min default
gr.State(5), # alpha_max default
gr.State(0), # srl_min default
gr.State(100), # srl_max default
gr.State(6), # point_size default
gr.State(True), # show_species_boundary default
gr.State(False), # show_trajectories default
gr.State("Species") # color_scheme default
],
outputs=manifold_outputs
)
print("✅ CMT Holographic Visualization Suite Ready!")
if __name__ == "__main__":
demo.launch()