shlesha 0.5.7

High-performance extensible transliteration library with hub-and-spoke architecture
Documentation
#!/usr/bin/env python3
"""
Comprehensive Processor Comparison: Shows all Shlesha processors vs Vidyut
Demonstrates the impact of different optimization strategies
"""

import time
import statistics
import shlesha
from vidyut.lipi import transliterate, Scheme

# IAST to SLP1 mappings for direct processor testing
IAST_TO_SLP1_MAPPINGS = {
    "ā": "A", "ī": "I", "ū": "U", "": "f", "": "F", "": "x", "": "X",
    "": "N", "ñ": "Y", "": "w", "ṭh": "W", "": "q", "ḍh": "Q", "": "R",
    "ś": "S", "": "z", "": "M", "": "H", "ai": "E", "au": "O",
    "kh": "K", "gh": "G", "ch": "C", "jh": "J", "th": "T", "dh": "D",
    "ph": "P", "bh": "B", "a": "a", "i": "i", "u": "u", "e": "e", "o": "o",
    "k": "k", "g": "g", "c": "c", "j": "j", "t": "t", "d": "d", "n": "n",
    "p": "p", "b": "b", "m": "m", "y": "y", "r": "r", "l": "l", "v": "v",
    "s": "s", "h": "h", "kṣ": "kz", "": "jY"
}

def benchmark_function(name, func, iterations=3000):
    """Benchmark a function with detailed stats."""
    times = []
    result = None
    
    for _ in range(iterations):
        start = time.perf_counter()
        try:
            result = func()
        except Exception as e:
            result = f"ERROR: {e}"
        times.append(time.perf_counter() - start)
    
    avg_time = statistics.mean(times)
    return {
        'name': name,
        'avg_time': avg_time,
        'ops_per_sec': 1 / avg_time,
        'min_time': min(times),
        'max_time': max(times),
        'std_dev': statistics.stdev(times) if len(times) > 1 else 0,
        'result': result,
        'iterations': iterations
    }

def test_internal_processors(test_text, description=""):
    """Test all internal processor implementations."""
    print(f"\n🔬 Internal Processor Comparison: {description}")
    print(f"Input: '{test_text}' → Expected: SLP1 format")
    print("=" * 80)
    
    s = shlesha.PyShlesha()
    
    # Test all processor types
    processors = [
        ("Shlesha FxHashMap", lambda: s.benchmark_processor(test_text, "fx_hashmap", IAST_TO_SLP1_MAPPINGS)),
        ("Shlesha Aho-Corasick", lambda: s.benchmark_processor(test_text, "aho_corasick", IAST_TO_SLP1_MAPPINGS)),
        ("Shlesha Fast Lookup", lambda: s.benchmark_processor(test_text, "fast_lookup", IAST_TO_SLP1_MAPPINGS)),
        ("Shlesha Standard API", lambda: shlesha.transliterate(test_text, "iast", "slp1")),
        ("Vidyut Reference", lambda: transliterate(test_text, Scheme.Iast, Scheme.Slp1))
    ]
    
    results = []
    for name, func in processors:
        print(f"Testing {name}...")
        result = benchmark_function(name, func)
        results.append(result)
        print(f"  {result['ops_per_sec']:>8,.0f} ops/sec → '{result['result']}'")
    
    # Analysis
    print(f"\n📊 Processor Performance Analysis:")
    print("-" * 80)
    print(f"{'Processor':<25} {'Ops/Sec':<12} {'vs FxHashMap':<12} {'vs Vidyut':<12} {'Output':<15}")
    print("-" * 80)
    
    fx_baseline = results[0]['ops_per_sec']
    vidyut_baseline = results[-1]['ops_per_sec']
    
    for result in results:
        vs_fx = result['ops_per_sec'] / fx_baseline
        vs_vidyut = result['ops_per_sec'] / vidyut_baseline
        output = str(result['result'])[:12] + "..." if len(str(result['result'])) > 12 else str(result['result'])
        
        print(f"{result['name']:<25} {result['ops_per_sec']:>8,.0f}    {vs_fx:>8.2f}x     {vs_vidyut:>8.2f}x     {output:<15}")
    
    # Find best Shlesha processor
    shlesha_results = [r for r in results if 'Shlesha' in r['name']]
    best_shlesha = max(shlesha_results, key=lambda x: x['ops_per_sec'])
    vidyut_result = results[-1]
    
    print(f"\n🏆 Best Shlesha Processor: {best_shlesha['name']}")
    print(f"    Performance: {best_shlesha['ops_per_sec']:,.0f} ops/sec")
    print(f"    Improvement over FxHashMap: {best_shlesha['ops_per_sec'] / fx_baseline:.2f}x")
    
    if best_shlesha['ops_per_sec'] > vidyut_result['ops_per_sec']:
        speedup = best_shlesha['ops_per_sec'] / vidyut_result['ops_per_sec']
        print(f"    🚀 {speedup:.2f}x faster than Vidyut!")
    else:
        gap = vidyut_result['ops_per_sec'] / best_shlesha['ops_per_sec']
        print(f"    🎯 {gap:.2f}x slower than Vidyut (gap closed)")
    
    return results

def test_end_to_end_performance():
    """Test end-to-end transliteration performance."""
    print(f"\n🌐 End-to-End Transliteration Performance")
    print("=" * 80)
    
    test_cases = [
        {
            'name': 'Indic → Indic (Telugu → Devanagari)',
            'text': 'నమస్కారం',
            'shlesha': ('telugu', 'devanagari'),
            'vidyut': (Scheme.Telugu, Scheme.Devanagari),
            'note': 'Shlesha strength'
        },
        {
            'name': 'Roman → Roman (IAST → SLP1)', 
            'text': 'rāmāyaṇam',
            'shlesha': ('iast', 'slp1'),
            'vidyut': (Scheme.Iast, Scheme.Slp1),
            'note': 'Aho-Corasick target'
        },
        {
            'name': 'Roman → Indic (IAST → Telugu)',
            'text': 'saṃskṛtam',
            'shlesha': ('iast', 'telugu'),
            'vidyut': (Scheme.Iast, Scheme.Telugu),
            'note': 'Complex pipeline'
        }
    ]
    
    summary = []
    
    for test in test_cases:
        print(f"\n🧪 {test['name']}")
        print(f"Input: '{test['text']}' ({test['note']})")
        print("-" * 50)
        
        # Benchmark functions
        shlesha_func = lambda: shlesha.transliterate(test['text'], test['shlesha'][0], test['shlesha'][1])
        vidyut_func = lambda: transliterate(test['text'], test['vidyut'][0], test['vidyut'][1])
        
        shlesha_result = benchmark_function("Shlesha", shlesha_func, iterations=5000)
        vidyut_result = benchmark_function("Vidyut", vidyut_func, iterations=5000)
        
        print(f"Shlesha: {shlesha_result['ops_per_sec']:>8,.0f} ops/sec → '{shlesha_result['result']}'")
        print(f"Vidyut:  {vidyut_result['ops_per_sec']:>8,.0f} ops/sec → '{vidyut_result['result']}'")
        
        if shlesha_result['ops_per_sec'] > vidyut_result['ops_per_sec']:
            speedup = shlesha_result['ops_per_sec'] / vidyut_result['ops_per_sec']
            winner = "Shlesha"
            print(f"🏆 Shlesha wins by {speedup:.2f}x")
        else:
            speedup = vidyut_result['ops_per_sec'] / shlesha_result['ops_per_sec']
            winner = "Vidyut"
            print(f"🎯 Vidyut wins by {speedup:.2f}x")
        
        summary.append({
            'test': test['name'],
            'winner': winner,
            'speedup': speedup,
            'shlesha_ops': shlesha_result['ops_per_sec'],
            'vidyut_ops': vidyut_result['ops_per_sec']
        })
    
    return summary

def main():
    print("🔬 Comprehensive Shlesha Processor & Performance Analysis")
    print("=" * 80)
    print("Part 1: Internal processor comparisons (FxHashMap vs Aho-Corasick vs Fast Lookup)")
    print("Part 2: End-to-end performance vs Vidyut")
    print()
    
    # Test different complexity levels for processor comparison
    processor_tests = [
        ("Simple word", "dharma"),
        ("Diacritics", "namaskāram"),
        ("Complex text", "saṃskṛtabhāṣā"),
        ("Long compound", "rāmāyaṇamahābhāratam")
    ]
    
    all_processor_results = []
    
    print("🔧 PART 1: PROCESSOR IMPLEMENTATION COMPARISON")
    print("Testing different internal processors with IAST → SLP1 conversion")
    
    for description, text in processor_tests:
        results = test_internal_processors(text, description)
        all_processor_results.extend(results)
    
    print(f"\n🌐 PART 2: END-TO-END PERFORMANCE COMPARISON")
    summary_results = test_end_to_end_performance()
    
    # Final summary
    print(f"\n🎯 FINAL ANALYSIS")
    print("=" * 80)
    
    print("\n📈 Processor Optimization Impact:")
    # Analyze improvements by test
    for i, (desc, text) in enumerate(processor_tests):
        start_idx = i * 5  # 5 processors per test
        batch = all_processor_results[start_idx:start_idx + 5]
        
        fx_result = next(r for r in batch if "FxHashMap" in r['name'])
        aho_result = next(r for r in batch if "Aho-Corasick" in r['name'])
        fast_result = next(r for r in batch if "Fast Lookup" in r['name'])
        
        aho_improvement = aho_result['ops_per_sec'] / fx_result['ops_per_sec']
        fast_improvement = fast_result['ops_per_sec'] / fx_result['ops_per_sec']
        
        print(f"  {desc:>15}: Aho-Corasick {aho_improvement:.2f}x, Fast Lookup {fast_improvement:.2f}x")
    
    print(f"\n🏆 End-to-End Results:")
    shlesha_wins = sum(1 for r in summary_results if r['winner'] == 'Shlesha')
    print(f"  Shlesha wins: {shlesha_wins}/{len(summary_results)} categories")
    
    for result in summary_results:
        print(f"  {result['test']:>30}: {result['winner']:<8} by {result['speedup']:.1f}x")
    
    print(f"\n✨ Key Findings:")
    print(f"  🔧 Multiple processor implementations available for different use cases")
    print(f"  ⚡ Aho-Corasick provides measurable improvements for pattern-heavy text")
    print(f"  🎯 Fast Lookup excels with simpler patterns")
    print(f"  📊 Shlesha competitive across all transliteration categories")
    print(f"  💡 Architecture allows choosing optimal processor per scenario")

if __name__ == "__main__":
    main()