use super::config::RelationsAnalysisConfig;
use super::results::{
RelationStrength, RelationshipHierarchy, RelationshipNetwork, RelationshipPattern,
SemanticGraph, SemanticRelation, SemanticRelationsMetrics,
};
use crate::error::TextAnalysisError;
use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
use std::hash::{Hash, Hasher};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RelationsAnalysisError {
#[error("Invalid relations analysis configuration: {0}")]
ConfigError(String),
#[error("Relationship calculation failed: {0}")]
CalculationError(String),
#[error("Semantic relations analysis error: {0}")]
AnalysisError(String),
}
pub type RelationsResult<T> = Result<T, RelationsAnalysisError>;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SemanticRelationType {
Hierarchical,
Similarity,
Opposition,
Causal,
Temporal,
Spatial,
PartWhole,
Functional,
Associative,
Thematic,
}
#[derive(Debug, Clone)]
pub struct SemanticRelationsAnalyzer {
config: RelationsAnalysisConfig,
relation_graph: SemanticGraph,
detected_relations: Vec<SemanticRelation>,
relation_patterns: Vec<RelationshipPattern>,
hierarchies: BTreeMap<String, RelationshipHierarchy>,
relation_cache: HashMap<u64, SemanticRelationsMetrics>,
network_cache: HashMap<String, RelationshipNetwork>,
strength_tracker: HashMap<String, f64>,
}
impl SemanticRelationsAnalyzer {
pub fn new(config: RelationsAnalysisConfig) -> RelationsResult<Self> {
Self::validate_config(&config)?;
Ok(Self {
config,
relation_graph: SemanticGraph::new(),
detected_relations: Vec::new(),
relation_patterns: Vec::new(),
hierarchies: BTreeMap::new(),
relation_cache: HashMap::new(),
network_cache: HashMap::new(),
strength_tracker: HashMap::new(),
})
}
pub fn analyze_semantic_relations(
&mut self,
text: &str,
domain_knowledge: Option<&HashMap<String, Vec<String>>>,
) -> RelationsResult<SemanticRelationsMetrics> {
let cache_key = self.generate_cache_key(text, domain_knowledge);
if let Some(cached) = self.relation_cache.get(&cache_key) {
return Ok(cached.clone());
}
let sentences = self.extract_sentences(text);
let mut metrics = SemanticRelationsMetrics::default();
self.build_semantic_graph(&sentences, domain_knowledge)?;
metrics.overall_relation_score = self.calculate_overall_relation_score()?;
metrics.relation_density = self.calculate_relation_density()?;
metrics.relation_coherence = self.measure_relation_coherence()?;
metrics.semantic_connectivity = self.analyze_semantic_connectivity()?;
metrics.detected_relations = self.detect_semantic_relations(&sentences)?;
metrics.relationship_networks = self.build_relationship_networks()?;
metrics.relation_strengths = self.calculate_relation_strengths()?;
if self.config.analyze_advanced_relations {
metrics.relationship_patterns = self.identify_relationship_patterns()?;
metrics.hierarchical_structures = self.analyze_hierarchical_structures()?;
metrics.semantic_graph_metrics = self.calculate_graph_metrics()?;
metrics.relation_evolution = self.trace_relation_evolution(&sentences)?;
}
if self.config.multi_level_analysis {
metrics.local_relations = self.analyze_local_relations(&sentences)?;
metrics.global_relations = self.analyze_global_relations(&sentences)?;
metrics.cross_sentence_relations = self.analyze_cross_sentence_relations(&sentences)?;
}
if self.config.assess_relation_quality {
metrics.relation_quality_metrics = self.assess_relation_quality()?;
metrics.redundancy_analysis = self.analyze_relation_redundancy()?;
metrics.completeness_analysis = self.analyze_relation_completeness(&sentences)?;
}
self.relation_cache.insert(cache_key, metrics.clone());
Ok(metrics)
}
fn build_semantic_graph(
&mut self,
sentences: &[String],
domain_knowledge: Option<&HashMap<String, Vec<String>>>,
) -> RelationsResult<()> {
self.relation_graph = SemanticGraph::new();
let mut entities = HashMap::new();
let mut concepts = HashMap::new();
for (i, sentence) in sentences.iter().enumerate() {
let sentence_entities = self.extract_entities(sentence)?;
let sentence_concepts = self.extract_concepts(sentence)?;
for entity in sentence_entities {
entities
.entry(entity.clone())
.or_insert_with(Vec::new)
.push(i);
self.relation_graph.add_node(&entity, "entity");
}
for concept in sentence_concepts {
concepts
.entry(concept.clone())
.or_insert_with(Vec::new)
.push(i);
self.relation_graph.add_node(&concept, "concept");
}
}
self.build_intra_sentence_relations(sentences)?;
self.build_inter_sentence_relations(sentences)?;
if let Some(knowledge) = domain_knowledge {
self.incorporate_domain_knowledge(knowledge)?;
}
self.calculate_graph_relationships()?;
Ok(())
}
fn calculate_overall_relation_score(&self) -> RelationsResult<f64> {
let relation_count = self.detected_relations.len() as f64;
let potential_relations = self.estimate_potential_relations()?;
if potential_relations == 0.0 {
return Ok(0.0);
}
let coverage_score = relation_count / potential_relations;
let quality_score = self.calculate_average_relation_quality()?;
let coherence_score = self.measure_relation_coherence()?;
let overall_score = (coverage_score * self.config.coverage_weight
+ quality_score * self.config.quality_weight
+ coherence_score * self.config.coherence_weight)
/ (self.config.coverage_weight
+ self.config.quality_weight
+ self.config.coherence_weight);
Ok(overall_score.max(0.0).min(1.0))
}
fn calculate_relation_density(&self) -> RelationsResult<f64> {
let node_count = self.relation_graph.node_count() as f64;
let edge_count = self.relation_graph.edge_count() as f64;
if node_count < 2.0 {
return Ok(0.0);
}
let max_edges = (node_count * (node_count - 1.0)) / 2.0;
let density = edge_count / max_edges;
Ok(density)
}
fn measure_relation_coherence(&self) -> RelationsResult<f64> {
if self.detected_relations.is_empty() {
return Ok(0.0);
}
let mut coherence_scores = Vec::new();
let relation_groups = self.group_relations_by_type();
for (_relation_type, relations) in relation_groups {
let type_coherence = self.calculate_type_coherence(&relations)?;
coherence_scores.push(type_coherence);
}
let mean_coherence = coherence_scores.iter().sum::<f64>() / coherence_scores.len() as f64;
let interaction_coherence = self.calculate_interaction_coherence()?;
Ok((mean_coherence + interaction_coherence) / 2.0)
}
fn analyze_semantic_connectivity(&self) -> RelationsResult<f64> {
let components = self.relation_graph.connected_components();
let total_nodes = self.relation_graph.node_count();
if total_nodes == 0 {
return Ok(0.0);
}
let largest_component_size = components
.iter()
.map(|component| component.len())
.max()
.unwrap_or(0) as f64;
let connectivity_ratio = largest_component_size / total_nodes as f64;
let avg_path_length = self.calculate_average_path_length()?;
let normalized_path_length = if avg_path_length > 0.0 {
1.0 / avg_path_length.ln()
} else {
0.0
};
Ok((connectivity_ratio + normalized_path_length) / 2.0)
}
fn detect_semantic_relations(
&mut self,
sentences: &[String],
) -> RelationsResult<Vec<SemanticRelation>> {
let mut relations = Vec::new();
for relation_type in self.get_enabled_relation_types() {
let type_relations = self.detect_relations_of_type(sentences, &relation_type)?;
relations.extend(type_relations);
}
let filtered_relations = self.filter_relations(relations)?;
self.detected_relations = filtered_relations.clone();
Ok(filtered_relations)
}
fn build_relationship_networks(
&mut self,
) -> RelationsResult<HashMap<String, RelationshipNetwork>> {
let mut networks = HashMap::new();
let relation_groups = self.group_relations_by_type();
for (relation_type, relations) in relation_groups {
let network = self.build_network_for_type(&relation_type, &relations)?;
networks.insert(relation_type.to_string(), network);
}
let overall_network = self.build_overall_network()?;
networks.insert("overall".to_string(), overall_network);
self.network_cache = networks.clone();
Ok(networks)
}
fn calculate_relation_strengths(
&mut self,
) -> RelationsResult<HashMap<String, RelationStrength>> {
let mut strengths = HashMap::new();
for relation in &self.detected_relations {
let strength = self.calculate_individual_relation_strength(relation)?;
let key = format!("{}_{}", relation.source, relation.target);
strengths.insert(key.clone(), strength);
self.strength_tracker.insert(key, strength.overall_strength);
}
Ok(strengths)
}
fn identify_relationship_patterns(&mut self) -> RelationsResult<Vec<RelationshipPattern>> {
let mut patterns = Vec::new();
let sequential_patterns = self.detect_sequential_patterns()?;
patterns.extend(sequential_patterns);
let hierarchical_patterns = self.detect_hierarchical_patterns()?;
patterns.extend(hierarchical_patterns);
let clustering_patterns = self.detect_clustering_patterns()?;
patterns.extend(clustering_patterns);
self.relation_patterns = patterns.clone();
Ok(patterns)
}
fn analyze_hierarchical_structures(
&mut self,
) -> RelationsResult<BTreeMap<String, RelationshipHierarchy>> {
let mut hierarchies = BTreeMap::new();
let hierarchical_relations: Vec<_> = self
.detected_relations
.iter()
.filter(|r| r.relation_type == SemanticRelationType::Hierarchical)
.collect();
if hierarchical_relations.is_empty() {
return Ok(hierarchies);
}
let hierarchy_roots = self.identify_hierarchy_roots(&hierarchical_relations)?;
for root in hierarchy_roots {
let hierarchy = self.build_hierarchy_from_root(&root, &hierarchical_relations)?;
hierarchies.insert(root.clone(), hierarchy);
}
self.hierarchies = hierarchies.clone();
Ok(hierarchies)
}
fn validate_config(config: &RelationsAnalysisConfig) -> RelationsResult<()> {
if config.min_relation_strength < 0.0 || config.min_relation_strength > 1.0 {
return Err(RelationsAnalysisError::ConfigError(
"min_relation_strength must be between 0.0 and 1.0".to_string(),
));
}
if config.coverage_weight < 0.0
|| config.quality_weight < 0.0
|| config.coherence_weight < 0.0
{
return Err(RelationsAnalysisError::ConfigError(
"all weights must be non-negative".to_string(),
));
}
Ok(())
}
fn extract_sentences(&self, text: &str) -> Vec<String> {
text.split(&self.config.sentence_delimiters)
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect()
}
fn generate_cache_key(
&self,
text: &str,
domain_knowledge: Option<&HashMap<String, Vec<String>>>,
) -> u64 {
let mut hasher = DefaultHasher::new();
text.hash(&mut hasher);
if let Some(knowledge) = domain_knowledge {
for (k, v) in knowledge {
k.hash(&mut hasher);
for item in v {
item.hash(&mut hasher);
}
}
}
self.config.hash(&mut hasher);
hasher.finish()
}
fn extract_entities(&self, sentence: &str) -> RelationsResult<Vec<String>> {
let words: Vec<&str> = sentence.split_whitespace().collect();
let mut entities = Vec::new();
for word in words {
if self.is_potential_entity(word) {
entities.push(word.to_lowercase());
}
}
if self.config.extract_multiword_entities {
entities.extend(self.extract_multiword_entities(sentence)?);
}
Ok(entities)
}
fn extract_concepts(&self, sentence: &str) -> RelationsResult<Vec<String>> {
let words: Vec<&str> = sentence.split_whitespace().collect();
let mut concepts = Vec::new();
for word in words {
if self.is_meaningful_concept(word) {
concepts.push(word.to_lowercase());
}
}
Ok(concepts)
}
fn is_potential_entity(&self, word: &str) -> bool {
word.len() >= self.config.min_entity_length
&& word.chars().any(|c| c.is_uppercase())
&& word.chars().any(|c| c.is_alphabetic())
}
fn is_meaningful_concept(&self, word: &str) -> bool {
word.len() >= self.config.min_concept_length
&& word.chars().any(|c| c.is_alphabetic())
&& !self.is_stop_word(word)
}
fn is_stop_word(&self, word: &str) -> bool {
self.config.stop_words.contains(&word.to_lowercase())
}
fn get_enabled_relation_types(&self) -> Vec<SemanticRelationType> {
let mut types = Vec::new();
if self.config.detect_hierarchical {
types.push(SemanticRelationType::Hierarchical);
}
if self.config.detect_similarity {
types.push(SemanticRelationType::Similarity);
}
if self.config.detect_opposition {
types.push(SemanticRelationType::Opposition);
}
if self.config.detect_causal {
types.push(SemanticRelationType::Causal);
}
if self.config.detect_temporal {
types.push(SemanticRelationType::Temporal);
}
if self.config.detect_spatial {
types.push(SemanticRelationType::Spatial);
}
if self.config.detect_part_whole {
types.push(SemanticRelationType::PartWhole);
}
if self.config.detect_functional {
types.push(SemanticRelationType::Functional);
}
if self.config.detect_associative {
types.push(SemanticRelationType::Associative);
}
if self.config.detect_thematic {
types.push(SemanticRelationType::Thematic);
}
types
}
}
impl Default for SemanticRelationsAnalyzer {
fn default() -> Self {
Self::new(RelationsAnalysisConfig::default()).expect("default relations config should be valid")
}
}