mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
Recommit pipeline orchestration and e2e tests
This commit is contained in:
@@ -34,10 +34,19 @@ License: MIT
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import numpy as np
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
try:
|
||||
import numpy as np
|
||||
except ImportError:
|
||||
np = None
|
||||
|
||||
try:
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
except ImportError:
|
||||
px = None
|
||||
go = None
|
||||
make_subplots = None
|
||||
|
||||
from ..utils.exceptions import ProcessingError
|
||||
from ..utils.logging import get_logger
|
||||
@@ -68,6 +77,23 @@ class AnalyticsVisualizer:
|
||||
except (KeyError, AttributeError):
|
||||
self.color_scheme = ColorScheme.DEFAULT
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if dependencies are available."""
|
||||
if px is None or go is None:
|
||||
raise ProcessingError(
|
||||
"Plotly is required for analytics visualization. "
|
||||
"Install with: pip install plotly"
|
||||
)
|
||||
if np is None:
|
||||
raise ProcessingError(
|
||||
"NumPy is required for analytics visualization. "
|
||||
"Install with: pip install numpy"
|
||||
)
|
||||
|
||||
def visualize_centrality(self, *args, **kwargs):
|
||||
"""Alias for visualize_centrality_rankings."""
|
||||
return self.visualize_centrality_rankings(*args, **kwargs)
|
||||
|
||||
def visualize_centrality_rankings(
|
||||
self,
|
||||
centrality: Dict[str, Any],
|
||||
@@ -91,6 +117,7 @@ class AnalyticsVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="AnalyticsVisualizer",
|
||||
@@ -188,6 +215,7 @@ class AnalyticsVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing community structure")
|
||||
|
||||
# Use KG visualizer for community visualization
|
||||
@@ -217,6 +245,7 @@ class AnalyticsVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing connectivity analysis")
|
||||
|
||||
# Extract metrics
|
||||
@@ -291,6 +320,7 @@ class AnalyticsVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing degree distribution")
|
||||
|
||||
# Calculate degrees
|
||||
@@ -360,6 +390,7 @@ class AnalyticsVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing graph metrics dashboard")
|
||||
|
||||
# Extract key metrics
|
||||
@@ -498,6 +529,7 @@ class AnalyticsVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing centrality comparison")
|
||||
|
||||
# Extract top nodes for each centrality type
|
||||
|
||||
@@ -35,10 +35,16 @@ from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
import seaborn as sns
|
||||
from plotly.subplots import make_subplots
|
||||
|
||||
try:
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
except ImportError:
|
||||
px = None
|
||||
go = None
|
||||
make_subplots = None
|
||||
|
||||
from sklearn.decomposition import PCA
|
||||
from sklearn.manifold import TSNE
|
||||
|
||||
@@ -85,6 +91,14 @@ class EmbeddingVisualizer:
|
||||
self.color_scheme = ColorScheme.DEFAULT
|
||||
self.point_size = config.get("point_size", 5)
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if dependencies are available."""
|
||||
if px is None or go is None:
|
||||
raise ProcessingError(
|
||||
"Plotly is required for embedding visualization. "
|
||||
"Install with: pip install plotly"
|
||||
)
|
||||
|
||||
def visualize_2d_projection(
|
||||
self,
|
||||
embeddings: np.ndarray,
|
||||
@@ -92,17 +106,30 @@ class EmbeddingVisualizer:
|
||||
method: str = "umap",
|
||||
output: str = "interactive",
|
||||
file_path: Optional[Union[str, Path]] = None,
|
||||
color_by: Optional[List[Any]] = None,
|
||||
size_by: Optional[List[float]] = None,
|
||||
hover_data: Optional[List[Dict[str, Any]]] = None,
|
||||
**options,
|
||||
) -> Optional[Any]:
|
||||
"""
|
||||
Visualize embeddings in 2D using dimensionality reduction.
|
||||
|
||||
Implements the 5-step visualization process:
|
||||
1. Problem setting: Dimensionality reduction choice
|
||||
2. Data analysis: Logs embedding statistics
|
||||
3. Layout: 2D Projection (UMAP/t-SNE/PCA)
|
||||
4. Styling: Configurable color and size mapping
|
||||
5. Interaction: Rich hover data
|
||||
|
||||
Args:
|
||||
embeddings: Embedding matrix (n_samples, n_features)
|
||||
labels: Optional labels for coloring points
|
||||
labels: Optional labels for points (used as default color_by if provided)
|
||||
method: Reduction method ("umap", "tsne", "pca")
|
||||
output: Output type ("interactive", "html", "png", "svg")
|
||||
file_path: Output file path
|
||||
color_by: List of values to map to color (overrides labels)
|
||||
size_by: List of values to map to point size
|
||||
hover_data: List of dictionaries containing metadata for each point
|
||||
**options: Additional options:
|
||||
- n_components: Number of components (default: 2)
|
||||
- perplexity: Perplexity for t-SNE
|
||||
@@ -111,6 +138,7 @@ class EmbeddingVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="EmbeddingVisualizer",
|
||||
@@ -119,6 +147,10 @@ class EmbeddingVisualizer:
|
||||
|
||||
try:
|
||||
self.logger.info(f"Visualizing 2D projection using {method}")
|
||||
|
||||
# Step 2: Data Analysis
|
||||
n_samples, n_features = embeddings.shape
|
||||
self.logger.info(f"Embedding Analysis: {n_samples} samples, {n_features} dimensions")
|
||||
|
||||
if embeddings.shape[1] <= 2:
|
||||
# Already 2D or less, use directly
|
||||
@@ -138,7 +170,14 @@ class EmbeddingVisualizer:
|
||||
tracking_id, message="Generating visualization..."
|
||||
)
|
||||
result = self._visualize_2d_plotly(
|
||||
projected, labels, output, file_path, **options
|
||||
projected,
|
||||
labels,
|
||||
output,
|
||||
file_path,
|
||||
color_by=color_by,
|
||||
size_by=size_by,
|
||||
hover_data=hover_data,
|
||||
**options
|
||||
)
|
||||
|
||||
self.progress_tracker.stop_tracking(
|
||||
@@ -176,6 +215,7 @@ class EmbeddingVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="EmbeddingVisualizer",
|
||||
@@ -238,6 +278,7 @@ class EmbeddingVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="EmbeddingVisualizer",
|
||||
@@ -340,6 +381,7 @@ class EmbeddingVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="EmbeddingVisualizer",
|
||||
@@ -444,6 +486,7 @@ class EmbeddingVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="EmbeddingVisualizer",
|
||||
|
||||
@@ -33,12 +33,23 @@ License: MIT
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
import matplotlib.patches as mpatches
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
|
||||
try:
|
||||
import matplotlib.patches as mpatches
|
||||
import matplotlib.pyplot as plt
|
||||
except ImportError:
|
||||
mpatches = None
|
||||
plt = None
|
||||
|
||||
try:
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
except ImportError:
|
||||
px = None
|
||||
go = None
|
||||
make_subplots = None
|
||||
|
||||
from ..utils.exceptions import ProcessingError
|
||||
from ..utils.logging import get_logger
|
||||
@@ -96,25 +107,47 @@ class KGVisualizer:
|
||||
self.hierarchical_layout = HierarchicalLayout(**config)
|
||||
self.circular_layout = CircularLayout(**config)
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if dependencies are available."""
|
||||
if px is None or go is None:
|
||||
raise ProcessingError(
|
||||
"Plotly is required for KG visualization. "
|
||||
"Install with: pip install plotly"
|
||||
)
|
||||
|
||||
def visualize_network(
|
||||
self,
|
||||
graph: Dict[str, Any],
|
||||
output: str = "interactive",
|
||||
file_path: Optional[Union[str, Path]] = None,
|
||||
node_color_by: str = "type",
|
||||
node_size_by: Optional[str] = None,
|
||||
hover_data: Optional[List[str]] = None,
|
||||
**options,
|
||||
) -> Optional[Any]:
|
||||
"""
|
||||
Visualize knowledge graph as interactive network.
|
||||
|
||||
Implements the 5-step visualization process:
|
||||
1. Problem setting: implicit in graph selection
|
||||
2. Data analysis: logs graph statistics
|
||||
3. Layout: configurable via options
|
||||
4. Styling: configurable node color/size mappings
|
||||
5. Interaction: rich hover data and zoom capabilities
|
||||
|
||||
Args:
|
||||
graph: Knowledge graph dictionary with entities and relationships
|
||||
output: Output type ("interactive", "html", "png", "svg")
|
||||
file_path: Output file path (required for non-interactive)
|
||||
node_color_by: Property to map to node color (default: "type")
|
||||
node_size_by: Property to map to node size (default: fixed)
|
||||
hover_data: List of properties to show in hover tooltip
|
||||
**options: Additional visualization options
|
||||
|
||||
Returns:
|
||||
Plotly figure (if interactive) or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="KGVisualizer",
|
||||
@@ -137,18 +170,34 @@ class KGVisualizer:
|
||||
)
|
||||
raise ProcessingError("No entities found in graph")
|
||||
|
||||
# Step 2: Data Analysis - Understand data structure
|
||||
nodes = self._extract_nodes(entities)
|
||||
edges = self._extract_edges(relationships, entities)
|
||||
|
||||
num_nodes = len(nodes)
|
||||
num_edges = len(edges)
|
||||
entity_types = set(n.get("type", "unknown") for n in nodes)
|
||||
|
||||
self.logger.info(f"Graph Structure Analysis: {num_nodes} nodes, {num_edges} edges")
|
||||
self.logger.info(f"Entity Types: {', '.join(sorted(entity_types))}")
|
||||
|
||||
# Build node and edge lists
|
||||
self.progress_tracker.update_tracking(
|
||||
tracking_id, message="Building node and edge lists..."
|
||||
)
|
||||
nodes = self._extract_nodes(entities)
|
||||
edges = self._extract_edges(relationships, entities)
|
||||
|
||||
self.progress_tracker.update_tracking(
|
||||
tracking_id, message="Generating visualization..."
|
||||
)
|
||||
result = self._visualize_network_plotly(
|
||||
nodes, edges, output, file_path, **options
|
||||
nodes,
|
||||
edges,
|
||||
output,
|
||||
file_path,
|
||||
node_color_by=node_color_by,
|
||||
node_size_by=node_size_by,
|
||||
hover_data=hover_data,
|
||||
**options
|
||||
)
|
||||
|
||||
self.progress_tracker.stop_tracking(
|
||||
@@ -184,6 +233,7 @@ class KGVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing knowledge graph communities")
|
||||
|
||||
entities = graph.get("entities", [])
|
||||
@@ -242,6 +292,7 @@ class KGVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info(
|
||||
f"Visualizing knowledge graph with {centrality_type} centrality"
|
||||
)
|
||||
@@ -295,6 +346,7 @@ class KGVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing entity type distribution")
|
||||
|
||||
entities = graph.get("entities", [])
|
||||
@@ -339,6 +391,7 @@ class KGVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing relationship matrix")
|
||||
|
||||
entities = graph.get("entities", [])
|
||||
@@ -442,6 +495,9 @@ class KGVisualizer:
|
||||
edges: List[Dict[str, Any]],
|
||||
output: str,
|
||||
file_path: Optional[Path],
|
||||
node_color_by: str = "type",
|
||||
node_size_by: Optional[str] = None,
|
||||
hover_data: Optional[List[str]] = None,
|
||||
**options,
|
||||
) -> Optional[Any]:
|
||||
"""Create Plotly network visualization."""
|
||||
@@ -458,11 +514,91 @@ class KGVisualizer:
|
||||
else:
|
||||
pos = self.force_layout.compute_layout(node_ids, edge_tuples, **options)
|
||||
|
||||
# Get entity type colors
|
||||
entity_types = list(set(n.get("type", "entity") for n in nodes))
|
||||
type_colors = ColorPalette.get_entity_type_colors(
|
||||
entity_types, self.color_scheme
|
||||
)
|
||||
# Step 4: Styling - Node Colors
|
||||
# Priority 1: Explicit color set in node (e.g. from visualize_communities)
|
||||
# Priority 2: Mapped property via node_color_by
|
||||
|
||||
node_colors = []
|
||||
if any("color" in n for n in nodes):
|
||||
node_colors = [n.get("color", "#888") for n in nodes if n["id"] in pos]
|
||||
else:
|
||||
if node_color_by == "type":
|
||||
entity_types = list(set(n.get("type", "entity") for n in nodes))
|
||||
type_colors = ColorPalette.get_entity_type_colors(
|
||||
entity_types, self.color_scheme
|
||||
)
|
||||
node_colors = [
|
||||
type_colors.get(n.get("type", "entity"), "#888")
|
||||
for n in nodes
|
||||
if n["id"] in pos
|
||||
]
|
||||
else:
|
||||
# Custom property mapping
|
||||
values = []
|
||||
for n in nodes:
|
||||
if n["id"] not in pos: continue
|
||||
val = n.get(node_color_by) or n.get("metadata", {}).get(node_color_by, "Unknown")
|
||||
values.append(str(val))
|
||||
|
||||
unique_vals = sorted(list(set(values)))
|
||||
colors = ColorPalette.get_colors(self.color_scheme, len(unique_vals))
|
||||
val_map = dict(zip(unique_vals, colors))
|
||||
|
||||
node_colors = []
|
||||
for n in nodes:
|
||||
if n["id"] not in pos: continue
|
||||
val = str(n.get(node_color_by) or n.get("metadata", {}).get(node_color_by, "Unknown"))
|
||||
node_colors.append(val_map.get(val, "#888"))
|
||||
|
||||
# Step 4: Styling - Node Sizes
|
||||
# Priority 1: Explicit size set in node (e.g. from visualize_centrality)
|
||||
# Priority 2: Mapped property via node_size_by
|
||||
|
||||
node_sizes = []
|
||||
if any("size" in n for n in nodes) and not node_size_by:
|
||||
node_sizes = [n.get("size", self.node_size) for n in nodes if n["id"] in pos]
|
||||
elif node_size_by:
|
||||
raw_sizes = []
|
||||
valid_indices = []
|
||||
for i, n in enumerate(nodes):
|
||||
if n["id"] not in pos: continue
|
||||
val = n.get(node_size_by) or n.get("metadata", {}).get(node_size_by, 0)
|
||||
try:
|
||||
s = float(val)
|
||||
except (ValueError, TypeError):
|
||||
s = 0
|
||||
raw_sizes.append(s)
|
||||
valid_indices.append(i)
|
||||
|
||||
# Normalize to range [10, 50]
|
||||
if raw_sizes and max(raw_sizes) > min(raw_sizes):
|
||||
min_s, max_s = min(raw_sizes), max(raw_sizes)
|
||||
node_sizes = [10 + 40 * ((s - min_s) / (max_s - min_s)) for s in raw_sizes]
|
||||
else:
|
||||
node_sizes = [self.node_size] * len(raw_sizes)
|
||||
else:
|
||||
node_sizes = [self.node_size for n in nodes if n["id"] in pos]
|
||||
|
||||
# Step 5: Interaction - Rich Hover
|
||||
node_text = []
|
||||
for n in nodes:
|
||||
if n["id"] not in pos: continue
|
||||
|
||||
# Basic info
|
||||
text = f"<b>{n['label']}</b><br>Type: {n.get('type', 'entity')}"
|
||||
|
||||
# Additional hover data
|
||||
if hover_data:
|
||||
for field in hover_data:
|
||||
val = n.get(field) or n.get("metadata", {}).get(field, "N/A")
|
||||
text += f"<br>{field}: {val}"
|
||||
|
||||
# Add dynamic styling info if relevant
|
||||
if node_size_by:
|
||||
val = n.get(node_size_by) or n.get("metadata", {}).get(node_size_by, "N/A")
|
||||
text += f"<br>{node_size_by}: {val}"
|
||||
|
||||
node_text.append(text)
|
||||
|
||||
# Prepare edge traces
|
||||
edge_x = []
|
||||
@@ -485,13 +621,6 @@ class KGVisualizer:
|
||||
# Prepare node traces
|
||||
node_x = [pos[n["id"]][0] for n in nodes if n["id"] in pos]
|
||||
node_y = [pos[n["id"]][1] for n in nodes if n["id"] in pos]
|
||||
node_text = [n["label"] for n in nodes if n["id"] in pos]
|
||||
node_colors = [
|
||||
type_colors.get(n.get("type", "entity"), "#888")
|
||||
for n in nodes
|
||||
if n["id"] in pos
|
||||
]
|
||||
node_sizes = [n.get("size", self.node_size) for n in nodes if n["id"] in pos]
|
||||
|
||||
node_trace = go.Scatter(
|
||||
x=node_x,
|
||||
@@ -499,9 +628,12 @@ class KGVisualizer:
|
||||
mode="markers+text",
|
||||
hoverinfo="text",
|
||||
text=node_text,
|
||||
textposition="middle center",
|
||||
textposition="top center",
|
||||
marker=dict(
|
||||
size=node_sizes, color=node_colors, line=dict(width=2, color="white")
|
||||
size=node_sizes,
|
||||
color=node_colors,
|
||||
line=dict(width=2, color="white"),
|
||||
opacity=0.9
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -37,10 +37,17 @@ from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import matplotlib.patches as mpatches
|
||||
import matplotlib.pyplot as plt
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
|
||||
try:
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
except ImportError:
|
||||
px = None
|
||||
go = None
|
||||
make_subplots = None
|
||||
|
||||
from matplotlib.patches import FancyBboxPatch
|
||||
from plotly.subplots import make_subplots
|
||||
|
||||
try:
|
||||
import graphviz
|
||||
@@ -90,26 +97,60 @@ class OntologyVisualizer:
|
||||
self.color_scheme = ColorScheme.DEFAULT
|
||||
self.node_size = config.get("node_size", 15)
|
||||
|
||||
def _check_dependencies(self, require_graphviz: bool = False):
|
||||
"""Check if dependencies are available."""
|
||||
if require_graphviz:
|
||||
if graphviz is None:
|
||||
raise ProcessingError(
|
||||
"Graphviz is required for DOT export. "
|
||||
"Install with: pip install graphviz"
|
||||
)
|
||||
else:
|
||||
if px is None or go is None:
|
||||
raise ProcessingError(
|
||||
"Plotly is required for ontology visualization. "
|
||||
"Install with: pip install plotly"
|
||||
)
|
||||
|
||||
def visualize_hierarchy(
|
||||
self,
|
||||
ontology: Dict[str, Any],
|
||||
output: str = "interactive",
|
||||
file_path: Optional[Union[str, Path]] = None,
|
||||
node_color_by: str = "level",
|
||||
node_size_by: str = "instances",
|
||||
hover_data: Optional[List[str]] = None,
|
||||
**options,
|
||||
) -> Optional[Any]:
|
||||
"""
|
||||
Visualize class hierarchy as tree.
|
||||
|
||||
Implements the 5-step visualization process:
|
||||
1. Problem setting: Implicit in ontology selection
|
||||
2. Data analysis: Logs ontology statistics
|
||||
3. Layout: Hierarchical tree layout
|
||||
4. Styling: Configurable node color (e.g. by level) and size (e.g. by instances)
|
||||
5. Interaction: Rich hover data
|
||||
|
||||
Args:
|
||||
ontology: Ontology dictionary with classes, or SemanticNetwork object,
|
||||
or ontology generator result
|
||||
output: Output type ("interactive", "html", "png", "svg", "dot")
|
||||
file_path: Output file path
|
||||
node_color_by: Property to map to node color (default: "level")
|
||||
node_size_by: Property to map to node size (default: "instances")
|
||||
hover_data: List of properties to show in hover tooltip
|
||||
**options: Additional options
|
||||
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
# Check dependencies
|
||||
if output == "dot":
|
||||
self._check_dependencies(require_graphviz=True)
|
||||
else:
|
||||
self._check_dependencies()
|
||||
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="OntologyVisualizer",
|
||||
@@ -150,6 +191,15 @@ class OntologyVisualizer:
|
||||
"No classes found in ontology. Please provide classes or a semantic network."
|
||||
)
|
||||
|
||||
# Step 2: Data Analysis
|
||||
num_classes = len(classes)
|
||||
max_depth = 0
|
||||
for cls in classes:
|
||||
depth = self._calculate_class_depth(cls, classes)
|
||||
max_depth = max(max_depth, depth)
|
||||
|
||||
self.logger.info(f"Ontology Analysis: {num_classes} classes, max depth {max_depth}")
|
||||
|
||||
# If output is dot and graphviz is available, use it
|
||||
if output == "dot" and graphviz is not None and file_path:
|
||||
self.progress_tracker.update_tracking(
|
||||
@@ -175,7 +225,14 @@ class OntologyVisualizer:
|
||||
tracking_id, message="Generating visualization..."
|
||||
)
|
||||
result = self._visualize_hierarchy_plotly(
|
||||
hierarchy, classes, output, file_path, **options
|
||||
hierarchy,
|
||||
classes,
|
||||
output,
|
||||
file_path,
|
||||
node_color_by=node_color_by,
|
||||
node_size_by=node_size_by,
|
||||
hover_data=hover_data,
|
||||
**options
|
||||
)
|
||||
|
||||
self.progress_tracker.stop_tracking(
|
||||
@@ -209,6 +266,7 @@ class OntologyVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing ontology properties")
|
||||
|
||||
# Handle different input formats
|
||||
@@ -258,6 +316,7 @@ class OntologyVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing ontology structure")
|
||||
|
||||
classes = ontology.get("classes", [])
|
||||
@@ -342,6 +401,7 @@ class OntologyVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing class-property matrix")
|
||||
|
||||
classes = ontology.get("classes", [])
|
||||
@@ -411,6 +471,7 @@ class OntologyVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing ontology metrics")
|
||||
|
||||
classes = ontology.get("classes", [])
|
||||
@@ -603,6 +664,7 @@ class OntologyVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing semantic model")
|
||||
|
||||
# Handle OntologyGenerator result
|
||||
@@ -651,6 +713,9 @@ class OntologyVisualizer:
|
||||
classes: List[Dict[str, Any]],
|
||||
output: str,
|
||||
file_path: Optional[Path],
|
||||
node_color_by: str = "level",
|
||||
node_size_by: str = "instances",
|
||||
hover_data: Optional[List[str]] = None,
|
||||
**options,
|
||||
) -> Optional[Any]:
|
||||
"""Create Plotly hierarchy visualization."""
|
||||
@@ -676,7 +741,16 @@ class OntologyVisualizer:
|
||||
edges = []
|
||||
|
||||
def add_node_and_children(cls_name, level=0, x_offset=0):
|
||||
nodes.append({"name": cls_name, "level": level, "x": x_offset, "y": -level})
|
||||
# Find class data
|
||||
cls_data = all_class_names.get(cls_name, {})
|
||||
|
||||
nodes.append({
|
||||
"name": cls_name,
|
||||
"level": level,
|
||||
"x": x_offset,
|
||||
"y": -level,
|
||||
"data": cls_data
|
||||
})
|
||||
|
||||
children = hierarchy.get(cls_name, [])
|
||||
child_width = 1.0 / max(len(children), 1)
|
||||
@@ -692,6 +766,63 @@ class OntologyVisualizer:
|
||||
root_x = (i + 0.5) * root_width
|
||||
add_node_and_children(root, 0, root_x)
|
||||
|
||||
# Step 4: Styling - Node Colors
|
||||
# Default to coloring by level
|
||||
node_colors = []
|
||||
if node_color_by == "level":
|
||||
node_colors = [n["level"] for n in nodes]
|
||||
else:
|
||||
# Map custom property
|
||||
values = []
|
||||
for n in nodes:
|
||||
val = str(n["data"].get(node_color_by, "Unknown"))
|
||||
values.append(val)
|
||||
|
||||
unique_vals = sorted(list(set(values)))
|
||||
colors = ColorPalette.get_colors(self.color_scheme, len(unique_vals))
|
||||
val_map = dict(zip(unique_vals, colors))
|
||||
node_colors = [val_map.get(str(n["data"].get(node_color_by, "Unknown")), "#888") for n in nodes]
|
||||
|
||||
# Step 4: Styling - Node Sizes
|
||||
# Default to sizing by instances (if available) or fixed size
|
||||
node_sizes = []
|
||||
if node_size_by:
|
||||
raw_sizes = []
|
||||
for n in nodes:
|
||||
val = n["data"].get(node_size_by, 0)
|
||||
try:
|
||||
s = float(val)
|
||||
except (ValueError, TypeError):
|
||||
s = 0
|
||||
raw_sizes.append(s)
|
||||
|
||||
if raw_sizes and max(raw_sizes) > min(raw_sizes):
|
||||
min_s, max_s = min(raw_sizes), max(raw_sizes)
|
||||
# Scale between 10 and 40
|
||||
node_sizes = [10 + 30 * ((s - min_s) / (max_s - min_s)) for s in raw_sizes]
|
||||
else:
|
||||
node_sizes = [self.node_size] * len(nodes)
|
||||
else:
|
||||
node_sizes = [self.node_size] * len(nodes)
|
||||
|
||||
# Step 5: Interaction - Rich Hover
|
||||
node_text = []
|
||||
for n in nodes:
|
||||
cls_data = n["data"]
|
||||
text = f"<b>{n['name']}</b><br>Level: {n['level']}"
|
||||
|
||||
# Add instances if available
|
||||
if "instances" in cls_data:
|
||||
text += f"<br>Instances: {cls_data['instances']}"
|
||||
|
||||
# Additional hover data
|
||||
if hover_data:
|
||||
for field in hover_data:
|
||||
val = cls_data.get(field, "N/A")
|
||||
text += f"<br>{field}: {val}"
|
||||
|
||||
node_text.append(text)
|
||||
|
||||
# Create visualization
|
||||
edge_x = []
|
||||
edge_y = []
|
||||
@@ -714,18 +845,21 @@ class OntologyVisualizer:
|
||||
|
||||
node_x = [n["x"] for n in nodes]
|
||||
node_y = [n["y"] for n in nodes]
|
||||
node_text = [n["name"] for n in nodes]
|
||||
|
||||
node_trace = go.Scatter(
|
||||
x=node_x,
|
||||
y=node_y,
|
||||
mode="markers+text",
|
||||
text=node_text,
|
||||
textposition="middle center",
|
||||
text=[n["name"] for n in nodes], # Keep label on node simple
|
||||
hovertext=node_text, # Rich hover text
|
||||
hoverinfo="text",
|
||||
textposition="top center",
|
||||
marker=dict(
|
||||
size=self.node_size * 10,
|
||||
color="lightblue",
|
||||
line=dict(width=2, color="darkblue"),
|
||||
size=node_sizes,
|
||||
color=node_colors,
|
||||
colorscale="Viridis" if node_color_by == "level" else None,
|
||||
line=dict(width=2, color="white"),
|
||||
showscale=True if node_color_by == "level" else False
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -33,9 +33,19 @@ License: MIT
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
try:
|
||||
import numpy as np
|
||||
except ImportError:
|
||||
np = None
|
||||
|
||||
try:
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
except ImportError:
|
||||
px = None
|
||||
go = None
|
||||
make_subplots = None
|
||||
|
||||
from ..utils.exceptions import ProcessingError
|
||||
from ..utils.logging import get_logger
|
||||
@@ -66,6 +76,14 @@ class QualityVisualizer:
|
||||
except (KeyError, AttributeError):
|
||||
self.color_scheme = ColorScheme.DEFAULT
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if dependencies are available."""
|
||||
if px is None or go is None:
|
||||
raise ProcessingError(
|
||||
"Plotly is required for quality visualization. "
|
||||
"Install with: pip install plotly"
|
||||
)
|
||||
|
||||
def visualize_dashboard(
|
||||
self,
|
||||
quality_report: Any,
|
||||
@@ -85,6 +103,7 @@ class QualityVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="QualityVisualizer",
|
||||
@@ -266,6 +285,7 @@ class QualityVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing quality score distribution")
|
||||
|
||||
fig = go.Figure(
|
||||
@@ -315,6 +335,7 @@ class QualityVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing quality issues")
|
||||
|
||||
# Extract issues
|
||||
@@ -405,6 +426,7 @@ class QualityVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing completeness metrics")
|
||||
|
||||
# Extract metrics
|
||||
@@ -468,6 +490,13 @@ class QualityVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
if np is None:
|
||||
raise ProcessingError(
|
||||
"NumPy is required for consistency heatmap visualization. "
|
||||
"Install with: pip install numpy"
|
||||
)
|
||||
|
||||
self.logger.info("Visualizing consistency heatmap")
|
||||
|
||||
# Extract consistency matrix
|
||||
@@ -477,8 +506,6 @@ class QualityVisualizer:
|
||||
if not matrix:
|
||||
raise ProcessingError("No consistency matrix found")
|
||||
|
||||
import numpy as np
|
||||
|
||||
matrix = np.array(matrix)
|
||||
|
||||
fig = go.Figure(
|
||||
|
||||
@@ -33,8 +33,12 @@ License: MIT
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
try:
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
except ImportError:
|
||||
px = None
|
||||
go = None
|
||||
|
||||
from ..utils.exceptions import ProcessingError
|
||||
from ..utils.logging import get_logger
|
||||
@@ -62,6 +66,14 @@ class SemanticNetworkVisualizer:
|
||||
except (KeyError, AttributeError):
|
||||
self.color_scheme = ColorScheme.DEFAULT
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if dependencies are available."""
|
||||
if px is None or go is None:
|
||||
raise ProcessingError(
|
||||
"Plotly is required for semantic network visualization. "
|
||||
"Install with: pip install plotly"
|
||||
)
|
||||
|
||||
def visualize_network(
|
||||
self,
|
||||
semantic_network: Any,
|
||||
@@ -87,6 +99,7 @@ class SemanticNetworkVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="SemanticNetworkVisualizer",
|
||||
@@ -274,6 +287,7 @@ class SemanticNetworkVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing semantic network node types")
|
||||
|
||||
# Extract nodes
|
||||
@@ -325,6 +339,7 @@ class SemanticNetworkVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing semantic network edge types")
|
||||
|
||||
# Extract edges
|
||||
|
||||
@@ -33,9 +33,14 @@ License: MIT
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
try:
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
except ImportError:
|
||||
px = None
|
||||
go = None
|
||||
make_subplots = None
|
||||
|
||||
from ..utils.exceptions import ProcessingError
|
||||
from ..utils.logging import get_logger
|
||||
@@ -66,6 +71,14 @@ class TemporalVisualizer:
|
||||
except (KeyError, AttributeError):
|
||||
self.color_scheme = ColorScheme.DEFAULT
|
||||
|
||||
def _check_dependencies(self):
|
||||
"""Check if dependencies are available."""
|
||||
if px is None or go is None:
|
||||
raise ProcessingError(
|
||||
"Plotly is required for temporal visualization. "
|
||||
"Install with: pip install plotly"
|
||||
)
|
||||
|
||||
def visualize_timeline(
|
||||
self,
|
||||
temporal_data: Dict[str, Any],
|
||||
@@ -85,6 +98,7 @@ class TemporalVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
tracking_id = self.progress_tracker.start_tracking(
|
||||
module="visualization",
|
||||
submodule="TemporalVisualizer",
|
||||
@@ -208,6 +222,7 @@ class TemporalVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing temporal patterns")
|
||||
|
||||
if not patterns:
|
||||
@@ -279,6 +294,7 @@ class TemporalVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing snapshot comparison")
|
||||
|
||||
timestamps = sorted(snapshots.keys())
|
||||
@@ -371,6 +387,7 @@ class TemporalVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing version history")
|
||||
|
||||
# Build tree structure
|
||||
@@ -438,6 +455,7 @@ class TemporalVisualizer:
|
||||
Returns:
|
||||
Visualization figure or None
|
||||
"""
|
||||
self._check_dependencies()
|
||||
self.logger.info("Visualizing metrics evolution")
|
||||
|
||||
fig = go.Figure()
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
import numpy as np
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
# Add project root to path
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')))
|
||||
|
||||
from semantica.visualization import (
|
||||
KGVisualizer,
|
||||
OntologyVisualizer,
|
||||
EmbeddingVisualizer,
|
||||
SemanticNetworkVisualizer,
|
||||
QualityVisualizer,
|
||||
AnalyticsVisualizer,
|
||||
TemporalVisualizer
|
||||
)
|
||||
from semantica.kg import GraphBuilder, GraphAnalyzer, TemporalVersionManager
|
||||
from semantica.ontology import OntologyGenerator
|
||||
from semantica.embeddings import EmbeddingGenerator
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger("reproduce_notebooks")
|
||||
|
||||
def run_introduction_notebook():
|
||||
logger.info("Running Introduction Notebook steps...")
|
||||
|
||||
# Step 1: Knowledge Graph Visualization
|
||||
logger.info("Step 1: Knowledge Graph Visualization")
|
||||
kg_visualizer = KGVisualizer()
|
||||
builder = GraphBuilder()
|
||||
|
||||
entities = [
|
||||
{"id": "e1", "type": "Organization", "name": "Apple Inc.", "properties": {}},
|
||||
{"id": "e2", "type": "Person", "name": "Tim Cook", "properties": {}}
|
||||
]
|
||||
|
||||
relationships = [
|
||||
{"source": "e2", "target": "e1", "type": "CEO_of", "properties": {}}
|
||||
]
|
||||
|
||||
kg = builder.build([{"entities": entities, "relationships": relationships}])
|
||||
viz = kg_visualizer.visualize_network(kg, output="interactive")
|
||||
assert viz is not None, "KG visualization failed"
|
||||
logger.info("KG Visualization successful")
|
||||
|
||||
# Step 2: Ontology Visualization
|
||||
logger.info("Step 2: Ontology Visualization")
|
||||
ontology_visualizer = OntologyVisualizer()
|
||||
generator = OntologyGenerator(min_occurrences=1)
|
||||
|
||||
ontology = generator.generate_ontology({"entities": entities, "relationships": relationships})
|
||||
viz = ontology_visualizer.visualize_hierarchy(ontology, output="interactive")
|
||||
# Note: verify if None is expected if ontology is simple or empty, but here it should be fine
|
||||
if viz is None:
|
||||
logger.warning("Ontology visualization returned None (might be due to empty hierarchy)")
|
||||
else:
|
||||
logger.info("Ontology Visualization successful")
|
||||
|
||||
# Step 3: Embedding Visualization
|
||||
logger.info("Step 3: Embedding Visualization")
|
||||
embedding_visualizer = EmbeddingVisualizer()
|
||||
# Mocking EmbeddingGenerator to avoid heavy model loading if possible,
|
||||
# but let's try to use the real one if it falls back gracefully.
|
||||
# If it fails, we will catch and use random embeddings.
|
||||
try:
|
||||
emb_generator = EmbeddingGenerator()
|
||||
texts = ["Apple Inc.", "Microsoft Corporation", "Amazon"]
|
||||
embeddings = emb_generator.generate_embeddings(texts, data_type="text")
|
||||
except Exception as e:
|
||||
logger.warning(f"Embedding generation failed: {e}. Using random embeddings.")
|
||||
embeddings = np.random.rand(3, 384)
|
||||
|
||||
labels = ["Apple", "Microsoft", "Amazon"]
|
||||
|
||||
# Need at least n_neighbors + 1 samples for UMAP usually, but with 3 samples it might warn.
|
||||
# Let's use PCA or just catch potential UMAP errors if samples are too few.
|
||||
try:
|
||||
viz = embedding_visualizer.visualize_2d_projection(embeddings, labels, method="umap")
|
||||
if viz is None:
|
||||
# Fallback to pca if umap fails silently or returns None
|
||||
viz = embedding_visualizer.visualize_2d_projection(embeddings, labels, method="pca")
|
||||
except Exception as e:
|
||||
logger.warning(f"UMAP visualization failed: {e}. Trying PCA.")
|
||||
viz = embedding_visualizer.visualize_2d_projection(embeddings, labels, method="pca")
|
||||
|
||||
assert viz is not None, "Embedding visualization failed"
|
||||
logger.info("Embedding Visualization successful")
|
||||
|
||||
# Step 4: Semantic Network Visualization
|
||||
logger.info("Step 4: Semantic Network Visualization")
|
||||
semantic_network = {
|
||||
"nodes": [
|
||||
{"id": "n1", "label": "Node 1", "type": "Entity"},
|
||||
{"id": "n2", "label": "Node 2", "type": "Entity"}
|
||||
],
|
||||
"edges": [
|
||||
{"source": "n1", "target": "n2", "label": "related_to"}
|
||||
]
|
||||
}
|
||||
|
||||
sem_viz = SemanticNetworkVisualizer()
|
||||
viz1 = sem_viz.visualize_network(semantic_network, output="interactive")
|
||||
viz2 = sem_viz.visualize_node_types(semantic_network, output="interactive")
|
||||
viz3 = sem_viz.visualize_edge_types(semantic_network, output="interactive")
|
||||
|
||||
assert viz1 is not None, "Semantic Network visualization failed"
|
||||
assert viz2 is not None, "Node Types visualization failed"
|
||||
assert viz3 is not None, "Edge Types visualization failed"
|
||||
logger.info("Semantic Network Visualization successful")
|
||||
|
||||
# Step 5: Advanced Embedding Visualization
|
||||
logger.info("Step 5: Advanced Embedding Visualization")
|
||||
text_emb = np.random.rand(50, 128)
|
||||
image_emb = np.random.rand(50, 128)
|
||||
audio_emb = np.random.rand(50, 128)
|
||||
|
||||
emb_viz = EmbeddingVisualizer()
|
||||
viz1 = emb_viz.visualize_multimodal_comparison(text_emb, image_emb, audio_emb, output="interactive")
|
||||
viz2 = emb_viz.visualize_quality_metrics(text_emb, output="interactive")
|
||||
|
||||
assert viz1 is not None, "Multimodal comparison failed"
|
||||
assert viz2 is not None, "Quality metrics visualization failed"
|
||||
logger.info("Advanced Embedding Visualization successful")
|
||||
|
||||
|
||||
def run_advanced_notebook():
|
||||
logger.info("Running Advanced Notebook steps...")
|
||||
|
||||
# Step 1: Create Sample Knowledge Graph
|
||||
logger.info("Step 1: Create Sample Knowledge Graph")
|
||||
builder = GraphBuilder()
|
||||
|
||||
entities = [
|
||||
{"id": "e1", "type": "Person", "name": "Alice", "properties": {"age": 30}},
|
||||
{"id": "e2", "type": "Person", "name": "Bob", "properties": {"age": 35}},
|
||||
{"id": "e3", "type": "Organization", "name": "Tech Corp", "properties": {"founded": 2010}},
|
||||
{"id": "e4", "type": "Location", "name": "San Francisco", "properties": {"country": "USA"}},
|
||||
]
|
||||
|
||||
relationships = [
|
||||
{"source": "e1", "target": "e2", "type": "knows", "properties": {"since": 2020}},
|
||||
{"source": "e1", "target": "e3", "type": "works_for", "properties": {"role": "Engineer"}},
|
||||
{"source": "e3", "target": "e4", "type": "located_in", "properties": {}},
|
||||
]
|
||||
|
||||
knowledge_graph = builder.build([{"entities": entities, "relationships": relationships}])
|
||||
|
||||
# Step 2: Knowledge Graph Visualization
|
||||
logger.info("Step 2: Knowledge Graph Visualization")
|
||||
kg_visualizer = KGVisualizer(layout="force", color_scheme="vibrant")
|
||||
viz = kg_visualizer.visualize_network(knowledge_graph, output="interactive")
|
||||
assert viz is not None, "KG visualization failed"
|
||||
logger.info("KG Visualization successful")
|
||||
|
||||
# Step 3: Generate Embeddings and Visualize
|
||||
logger.info("Step 3: Generate Embeddings and Visualize")
|
||||
# Use random embeddings to ensure stability
|
||||
embeddings = np.random.rand(len(entities), 128)
|
||||
labels = [entity.get("type", "Unknown") for entity in entities]
|
||||
|
||||
embedding_visualizer = EmbeddingVisualizer()
|
||||
# t-SNE requires more samples typically, use PCA if it fails
|
||||
try:
|
||||
viz = embedding_visualizer.visualize_2d_projection(embeddings, labels, method="tsne", output="interactive", file_path=None)
|
||||
except Exception as e:
|
||||
logger.warning(f"t-SNE failed (likely too few samples): {e}. Using PCA.")
|
||||
viz = embedding_visualizer.visualize_2d_projection(embeddings, labels, method="pca", output="interactive", file_path=None)
|
||||
|
||||
assert viz is not None, "Embedding visualization failed"
|
||||
logger.info("Embedding Visualization successful")
|
||||
|
||||
# Step 4: Quality Metrics Visualization
|
||||
logger.info("Step 4: Quality Metrics Visualization")
|
||||
quality_visualizer = QualityVisualizer()
|
||||
quality_report = {
|
||||
"overall_score": 0.85,
|
||||
"consistency_score": 0.90,
|
||||
"completeness_score": 0.80
|
||||
}
|
||||
viz = quality_visualizer.visualize_dashboard(quality_report, output="interactive")
|
||||
assert viz is not None, "Quality dashboard visualization failed"
|
||||
logger.info("Quality Visualization successful")
|
||||
|
||||
# Step 5: Graph Analytics Visualization
|
||||
logger.info("Step 5: Graph Analytics Visualization")
|
||||
# Mocking GraphAnalyzer results
|
||||
centrality_scores = {"e1": 0.5, "e2": 0.3, "e3": 0.8, "e4": 0.4}
|
||||
# Wrap in expected format
|
||||
centrality_data = {"centrality": centrality_scores}
|
||||
|
||||
community_dict = {"e1": 0, "e2": 0, "e3": 1, "e4": 1}
|
||||
# Wrap in expected format
|
||||
communities_data = {"node_assignments": community_dict}
|
||||
|
||||
analytics_visualizer = AnalyticsVisualizer()
|
||||
viz1 = analytics_visualizer.visualize_centrality_rankings(centrality_data, title="Node Centrality Scores")
|
||||
viz2 = analytics_visualizer.visualize_community_structure(
|
||||
knowledge_graph,
|
||||
communities_data,
|
||||
title="Community Detection"
|
||||
)
|
||||
|
||||
assert viz1 is not None, "Centrality visualization failed"
|
||||
assert viz2 is not None, "Communities visualization failed"
|
||||
logger.info("Analytics Visualization successful")
|
||||
|
||||
# Step 6: Temporal Data Visualization
|
||||
logger.info("Step 6: Temporal Data Visualization")
|
||||
temporal_kg = {
|
||||
"entities": entities,
|
||||
"relationships": relationships,
|
||||
"timestamps": {
|
||||
"e1": [2020, 2021, 2022],
|
||||
"e2": [2020, 2021],
|
||||
"e3": [2010, 2015, 2020, 2022],
|
||||
}
|
||||
}
|
||||
|
||||
# Generate events from timestamps
|
||||
events = []
|
||||
for entity_id, times in temporal_kg["timestamps"].items():
|
||||
for t in times:
|
||||
events.append({
|
||||
"timestamp": t,
|
||||
"type": "update",
|
||||
"entity": entity_id,
|
||||
"label": f"Update {entity_id}"
|
||||
})
|
||||
temporal_kg["events"] = events
|
||||
|
||||
entity_history = {
|
||||
"e1": [
|
||||
{"timestamp": 2020, "properties": {"age": 28}},
|
||||
{"timestamp": 2021, "properties": {"age": 29}},
|
||||
{"timestamp": 2022, "properties": {"age": 30}},
|
||||
]
|
||||
}
|
||||
|
||||
temporal_visualizer = TemporalVisualizer()
|
||||
viz1 = temporal_visualizer.visualize_timeline(temporal_kg, output="interactive")
|
||||
|
||||
timestamps = [str(item["timestamp"]) for item in entity_history["e1"]]
|
||||
age_values = [item["properties"]["age"] for item in entity_history["e1"]]
|
||||
metrics_history = {"age": age_values}
|
||||
viz2 = temporal_visualizer.visualize_metrics_evolution(metrics_history, timestamps, output="interactive")
|
||||
|
||||
assert viz1 is not None, "Timeline visualization failed"
|
||||
assert viz2 is not None, "Metrics evolution visualization failed"
|
||||
|
||||
# Version Manager part
|
||||
try:
|
||||
version_manager = TemporalVersionManager()
|
||||
v1 = version_manager.create_version(temporal_kg, timestamp="2020-01-01", version_label="v2020")
|
||||
temporal_kg_v2 = {
|
||||
"entities": temporal_kg.get("entities", []),
|
||||
"relationships": temporal_kg.get("relationships", []) + [
|
||||
{"source": "e1", "target": "e2", "type": "collaborated_with", "valid_from": "2023-01-01"}
|
||||
]
|
||||
}
|
||||
v2 = version_manager.create_version(temporal_kg_v2, timestamp="2023-01-01", version_label="v2023")
|
||||
snapshots = {v1["timestamp"]: v1, v2["timestamp"]: v2}
|
||||
|
||||
viz3 = temporal_visualizer.visualize_snapshot_comparison(snapshots, output="interactive")
|
||||
|
||||
version_history = [
|
||||
{"version": v1.get("label"), "timestamp": v1.get("timestamp")},
|
||||
{"version": v2.get("label"), "timestamp": v2.get("timestamp")}
|
||||
]
|
||||
viz4 = temporal_visualizer.visualize_version_history(version_history, output="interactive")
|
||||
|
||||
assert viz3 is not None, "Snapshot comparison failed"
|
||||
assert viz4 is not None, "Version history visualization failed"
|
||||
except Exception as e:
|
||||
logger.warning(f"Temporal Version Manager part failed: {e}")
|
||||
|
||||
logger.info("Temporal Visualization successful")
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
run_introduction_notebook()
|
||||
print("-" * 50)
|
||||
run_advanced_notebook()
|
||||
print("ALL NOTEBOOK REPRODUCTIONS SUCCESSFUL")
|
||||
except Exception as e:
|
||||
logger.error(f"Reproduction failed: {e}")
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,149 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
import sys
|
||||
import numpy as np
|
||||
|
||||
# Helper to mock modules
|
||||
def mock_module(name):
|
||||
m = MagicMock()
|
||||
sys.modules[name] = m
|
||||
return m
|
||||
|
||||
class TestOptionalDependencies(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# Mock heavy/problematic dependencies globally to prevent environment crashes
|
||||
# We use a dict to save original modules if they exist, but for this test file
|
||||
# we generally want to run in a controlled "clean" environment.
|
||||
cls.modules_to_patch = [
|
||||
'sklearn', 'sklearn.decomposition', 'sklearn.manifold',
|
||||
'scipy', 'scipy.optimize',
|
||||
'matplotlib', 'matplotlib.pyplot', 'matplotlib.patches',
|
||||
'plotly', 'plotly.express', 'plotly.graph_objects', 'plotly.subplots',
|
||||
'networkx', 'seaborn'
|
||||
]
|
||||
|
||||
cls.original_modules = {}
|
||||
for mod in cls.modules_to_patch:
|
||||
if mod in sys.modules:
|
||||
cls.original_modules[mod] = sys.modules[mod]
|
||||
sys.modules[mod] = MagicMock()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
# Restore original modules
|
||||
for mod in cls.modules_to_patch:
|
||||
if mod in cls.original_modules:
|
||||
sys.modules[mod] = cls.original_modules[mod]
|
||||
else:
|
||||
del sys.modules[mod]
|
||||
|
||||
def setUp(self):
|
||||
# Clear cached visualization modules to ensure fresh imports
|
||||
self.viz_modules = [
|
||||
'semantica.visualization.embedding_visualizer',
|
||||
'semantica.visualization.ontology_visualizer',
|
||||
'semantica.visualization.kg_visualizer',
|
||||
'semantica.visualization.utils.export_formats'
|
||||
]
|
||||
for mod in self.viz_modules:
|
||||
if mod in sys.modules:
|
||||
del sys.modules[mod]
|
||||
|
||||
def test_embedding_visualizer_without_umap(self):
|
||||
"""Test EmbeddingVisualizer behavior when umap is missing."""
|
||||
# Ensure umap is missing
|
||||
with patch.dict(sys.modules, {'umap': None}):
|
||||
from semantica.visualization.embedding_visualizer import EmbeddingVisualizer
|
||||
|
||||
# Setup PCA mock to verify fallback
|
||||
mock_pca_class = sys.modules['sklearn.decomposition'].PCA
|
||||
mock_pca_instance = mock_pca_class.return_value
|
||||
# Configure fit_transform to return correct shape (n_samples, 2)
|
||||
mock_pca_instance.fit_transform.return_value = np.zeros((4, 2))
|
||||
|
||||
viz = EmbeddingVisualizer()
|
||||
# Use numpy array!
|
||||
embeddings = np.array([[0, 1, 2], [1, 0, 3], [0, 0, 0], [1, 1, 1]])
|
||||
|
||||
# Should fallback to PCA when method="umap" is used but umap is None
|
||||
# The code logs a warning and uses PCA
|
||||
viz.visualize_2d_projection(embeddings, method="umap")
|
||||
|
||||
# Verify PCA was called
|
||||
mock_pca_class.assert_called()
|
||||
|
||||
def test_ontology_visualizer_without_graphviz(self):
|
||||
"""Test OntologyVisualizer behavior when graphviz is missing."""
|
||||
# Ensure graphviz is missing
|
||||
with patch.dict(sys.modules, {'graphviz': None}):
|
||||
from semantica.visualization.ontology_visualizer import OntologyVisualizer, ProcessingError
|
||||
|
||||
viz = OntologyVisualizer()
|
||||
ontology = {
|
||||
"classes": [
|
||||
{"name": "A", "label": "A"},
|
||||
{"name": "B", "label": "B", "parent": "A"}
|
||||
]
|
||||
}
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
viz.visualize_hierarchy(ontology, output="dot", file_path="test.dot")
|
||||
|
||||
self.assertIn("Graphviz is required for DOT export", str(cm.exception))
|
||||
|
||||
def test_analytics_visualizer_without_plotly(self):
|
||||
"""Test AnalyticsVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer, ProcessingError
|
||||
|
||||
# Need to ensure numpy is available for init (it's imported at top level)
|
||||
# But we are testing plotly missing.
|
||||
|
||||
viz = AnalyticsVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
viz.visualize_centrality_rankings({"node1": 1.0})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
def test_quality_visualizer_without_plotly(self):
|
||||
"""Test QualityVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.quality_visualizer import QualityVisualizer, ProcessingError
|
||||
|
||||
viz = QualityVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
viz.visualize_dashboard({})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
def test_semantic_network_visualizer_without_plotly(self):
|
||||
"""Test SemanticNetworkVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.semantic_network_visualizer import SemanticNetworkVisualizer, ProcessingError
|
||||
|
||||
viz = SemanticNetworkVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
viz.visualize_network({})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
def test_temporal_visualizer_without_plotly(self):
|
||||
"""Test TemporalVisualizer behavior when plotly is missing."""
|
||||
with patch.dict(sys.modules, {'plotly': None, 'plotly.express': None, 'plotly.graph_objects': None}):
|
||||
from semantica.visualization.temporal_visualizer import TemporalVisualizer, ProcessingError
|
||||
|
||||
viz = TemporalVisualizer()
|
||||
|
||||
with self.assertRaises(ProcessingError) as cm:
|
||||
viz.visualize_timeline({"events": []})
|
||||
|
||||
self.assertIn("Plotly is required", str(cm.exception))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,252 @@
|
||||
import unittest
|
||||
from unittest.mock import MagicMock, patch
|
||||
import sys
|
||||
import numpy as np
|
||||
from pathlib import Path
|
||||
|
||||
# Mock heavy libraries before importing visualization modules
|
||||
sys.modules['matplotlib'] = MagicMock()
|
||||
sys.modules['matplotlib.pyplot'] = MagicMock()
|
||||
sys.modules['matplotlib.colors'] = MagicMock()
|
||||
sys.modules['matplotlib.patches'] = MagicMock()
|
||||
sys.modules['plotly'] = MagicMock()
|
||||
sys.modules['plotly.express'] = MagicMock()
|
||||
sys.modules['plotly.graph_objects'] = MagicMock()
|
||||
sys.modules['plotly.subplots'] = MagicMock()
|
||||
sys.modules['seaborn'] = MagicMock()
|
||||
sys.modules['umap'] = MagicMock()
|
||||
sys.modules['sklearn'] = MagicMock()
|
||||
sys.modules['sklearn.decomposition'] = MagicMock()
|
||||
sys.modules['sklearn.manifold'] = MagicMock()
|
||||
sys.modules['networkx'] = MagicMock()
|
||||
sys.modules['graphviz'] = MagicMock()
|
||||
|
||||
# Import visualizers
|
||||
from semantica.visualization.kg_visualizer import KGVisualizer
|
||||
from semantica.visualization.ontology_visualizer import OntologyVisualizer
|
||||
from semantica.visualization.embedding_visualizer import EmbeddingVisualizer
|
||||
from semantica.visualization.semantic_network_visualizer import SemanticNetworkVisualizer
|
||||
from semantica.visualization.quality_visualizer import QualityVisualizer
|
||||
from semantica.visualization.analytics_visualizer import AnalyticsVisualizer
|
||||
from semantica.visualization.temporal_visualizer import TemporalVisualizer
|
||||
from semantica.visualization.utils.color_schemes import ColorScheme
|
||||
|
||||
class TestVisualizationComprehensive(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.mock_logger = MagicMock()
|
||||
self.mock_tracker = MagicMock()
|
||||
|
||||
# Patch dependencies for all visualizers
|
||||
self.patchers = [
|
||||
patch('semantica.visualization.kg_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.kg_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.visualization.ontology_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.ontology_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.visualization.embedding_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.embedding_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.visualization.semantic_network_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.semantic_network_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.visualization.quality_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.quality_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.visualization.analytics_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.analytics_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
patch('semantica.visualization.temporal_visualizer.get_logger', return_value=self.mock_logger),
|
||||
patch('semantica.visualization.temporal_visualizer.get_progress_tracker', return_value=self.mock_tracker),
|
||||
# Mock Layouts
|
||||
patch('semantica.visualization.kg_visualizer.ForceDirectedLayout', MagicMock()),
|
||||
patch('semantica.visualization.kg_visualizer.HierarchicalLayout', MagicMock()),
|
||||
patch('semantica.visualization.kg_visualizer.CircularLayout', MagicMock()),
|
||||
patch('semantica.visualization.ontology_visualizer.HierarchicalLayout', MagicMock()),
|
||||
patch('semantica.visualization.semantic_network_visualizer.ForceDirectedLayout', MagicMock()),
|
||||
]
|
||||
|
||||
for p in self.patchers:
|
||||
p.start()
|
||||
|
||||
# Reset plotly mocks
|
||||
import plotly.graph_objects as go
|
||||
import plotly.express as px
|
||||
go.Figure.reset_mock()
|
||||
px.bar.reset_mock()
|
||||
px.scatter.reset_mock()
|
||||
|
||||
def tearDown(self):
|
||||
for p in self.patchers:
|
||||
p.stop()
|
||||
|
||||
# --- KGVisualizer Tests ---
|
||||
def test_kg_visualizer(self):
|
||||
viz = KGVisualizer()
|
||||
graph = {
|
||||
"entities": [{"id": "e1", "label": "E1", "type": "T1"}, {"id": "e2", "label": "E2", "type": "T2"}],
|
||||
"relationships": [{"source": "e1", "target": "e2", "type": "R1"}]
|
||||
}
|
||||
|
||||
# Test visualize_network
|
||||
viz.visualize_network(graph)
|
||||
|
||||
# Test visualize_communities
|
||||
communities = {"node_assignments": {"e1": 0, "e2": 1}, "num_communities": 2}
|
||||
viz.visualize_communities(graph, communities)
|
||||
|
||||
# Test visualize_centrality
|
||||
centrality = {"centrality": {"e1": 0.5, "e2": 0.3}}
|
||||
viz.visualize_centrality(graph, centrality)
|
||||
|
||||
# Test visualize_entity_types
|
||||
viz.visualize_entity_types(graph)
|
||||
|
||||
# Test visualize_relationship_matrix
|
||||
viz.visualize_relationship_matrix(graph)
|
||||
|
||||
# --- OntologyVisualizer Tests ---
|
||||
def test_ontology_visualizer(self):
|
||||
viz = OntologyVisualizer()
|
||||
ontology = {
|
||||
"classes": [
|
||||
{"name": "C1", "label": "Class 1", "parent": None},
|
||||
{"name": "C2", "label": "Class 2", "parent": "C1"}
|
||||
],
|
||||
"properties": [
|
||||
{"name": "P1", "label": "Prop 1", "domain": "C1", "range": "C2"}
|
||||
]
|
||||
}
|
||||
|
||||
# Test visualize_hierarchy
|
||||
viz.visualize_hierarchy(ontology)
|
||||
|
||||
# Test visualize_properties
|
||||
viz.visualize_properties(ontology)
|
||||
|
||||
# Test visualize_structure
|
||||
viz.visualize_structure(ontology)
|
||||
|
||||
# Test visualize_class_property_matrix
|
||||
viz.visualize_class_property_matrix(ontology)
|
||||
|
||||
# Test visualize_metrics
|
||||
viz.visualize_metrics(ontology)
|
||||
|
||||
# Test visualize_semantic_model (mocking extract classes)
|
||||
semantic_model = {"nodes": [{"id": "n1", "type": "T1"}], "edges": []}
|
||||
viz.visualize_semantic_model(semantic_model)
|
||||
|
||||
# --- SemanticNetworkVisualizer Tests ---
|
||||
def test_semantic_network_visualizer(self):
|
||||
viz = SemanticNetworkVisualizer()
|
||||
semantic_network = {
|
||||
"nodes": [{"id": "n1", "label": "N1", "type": "T1"}],
|
||||
"edges": [{"source": "n1", "target": "n1", "label": "R1"}]
|
||||
}
|
||||
|
||||
# Test visualize_network
|
||||
with patch('semantica.visualization.kg_visualizer.KGVisualizer') as MockKG:
|
||||
viz.visualize_network(semantic_network)
|
||||
MockKG.return_value.visualize_network.assert_called()
|
||||
|
||||
# Test visualize_node_types
|
||||
viz.visualize_node_types(semantic_network)
|
||||
|
||||
# Test visualize_edge_types
|
||||
viz.visualize_edge_types(semantic_network)
|
||||
|
||||
# --- QualityVisualizer Tests ---
|
||||
def test_quality_visualizer(self):
|
||||
viz = QualityVisualizer()
|
||||
|
||||
# Test visualize_dashboard
|
||||
report = {"overall_score": 0.8, "consistency_score": 0.9, "completeness_score": 0.7}
|
||||
viz.visualize_dashboard(report)
|
||||
|
||||
# Test visualize_score_distribution
|
||||
scores = [0.1, 0.5, 0.9]
|
||||
viz.visualize_score_distribution(scores)
|
||||
|
||||
# Test visualize_issues
|
||||
report_issues = {"issues": [{"type": "error", "severity": "high"}]}
|
||||
viz.visualize_issues(report_issues)
|
||||
|
||||
# Test visualize_completeness_metrics
|
||||
metrics = {"entity_completeness": 0.8}
|
||||
viz.visualize_completeness_metrics(metrics)
|
||||
|
||||
# Test visualize_consistency_heatmap
|
||||
consistency = {"consistency_matrix": [[1.0]], "labels": ["C1"]}
|
||||
viz.visualize_consistency_heatmap(consistency)
|
||||
|
||||
# --- AnalyticsVisualizer Tests ---
|
||||
def test_analytics_visualizer(self):
|
||||
viz = AnalyticsVisualizer()
|
||||
graph = {"entities": [], "relationships": []}
|
||||
|
||||
# Test visualize_centrality_rankings
|
||||
centrality = {"rankings": [{"node": "n1", "score": 0.9}]}
|
||||
viz.visualize_centrality_rankings(centrality)
|
||||
|
||||
# Test visualize_community_structure
|
||||
communities = {"node_assignments": {}}
|
||||
with patch('semantica.visualization.kg_visualizer.KGVisualizer') as MockKG:
|
||||
viz.visualize_community_structure(graph, communities)
|
||||
|
||||
# Test visualize_connectivity
|
||||
connectivity = {"is_connected": True, "num_components": 1, "component_sizes": [10]}
|
||||
viz.visualize_connectivity(connectivity)
|
||||
|
||||
# Test visualize_degree_distribution
|
||||
viz.visualize_degree_distribution(graph)
|
||||
|
||||
# Test visualize_metrics_dashboard
|
||||
metrics = {"num_nodes": 10, "num_edges": 20, "density": 0.1}
|
||||
viz.visualize_metrics_dashboard(metrics)
|
||||
|
||||
# Test visualize_centrality_comparison
|
||||
results = {"degree": {"rankings": [{"node": "n1", "score": 0.9}]}}
|
||||
viz.visualize_centrality_comparison(results)
|
||||
|
||||
# --- TemporalVisualizer Tests ---
|
||||
def test_temporal_visualizer(self):
|
||||
viz = TemporalVisualizer()
|
||||
|
||||
# Test visualize_timeline
|
||||
temporal_data = {"events": [{"timestamp": "2023-01-01", "type": "create", "label": "E1"}], "timestamps": ["2023-01-01"]}
|
||||
viz.visualize_timeline(temporal_data)
|
||||
|
||||
# Test visualize_temporal_patterns
|
||||
patterns = [{"pattern_type": "trend", "start_time": "2023", "end_time": "2024", "entities": ["e1"]}]
|
||||
viz.visualize_temporal_patterns(patterns)
|
||||
|
||||
# Test visualize_snapshot_comparison
|
||||
snapshots = {"2023": {"entities": ["e1"], "relationships": []}}
|
||||
viz.visualize_snapshot_comparison(snapshots)
|
||||
|
||||
# Test visualize_version_history
|
||||
history = [{"version": "v1", "date": "2023-01-01"}]
|
||||
viz.visualize_version_history(history)
|
||||
|
||||
# Test visualize_metrics_evolution
|
||||
metrics_history = {"nodes": [10, 20]}
|
||||
timestamps = ["2023", "2024"]
|
||||
viz.visualize_metrics_evolution(metrics_history, timestamps)
|
||||
|
||||
# --- EmbeddingVisualizer Tests ---
|
||||
def test_embedding_visualizer(self):
|
||||
viz = EmbeddingVisualizer()
|
||||
embeddings = np.random.rand(10, 10)
|
||||
|
||||
# Test visualize_2d_projection (mock UMAP/PCA)
|
||||
with patch('semantica.visualization.embedding_visualizer.umap.UMAP') as MockUMAP:
|
||||
MockUMAP.return_value.fit_transform.return_value = np.random.rand(10, 2)
|
||||
viz.visualize_2d_projection(embeddings)
|
||||
|
||||
# Test visualize_similarity_heatmap
|
||||
viz.visualize_similarity_heatmap(embeddings[:5]) # smaller for heatmap
|
||||
|
||||
# Test visualize_clustering
|
||||
clusters = [0, 1, 0, 1, 0, 1, 0, 1, 0, 1]
|
||||
with patch('semantica.visualization.embedding_visualizer.umap.UMAP') as MockUMAP:
|
||||
MockUMAP.return_value.fit_transform.return_value = np.random.rand(10, 2)
|
||||
viz.visualize_clustering(embeddings, clusters)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user