VMS to pseudo - latin ( not a solution !)
#15
You could also insert a threshold for successful word recognition in the Python script from the outset. For example, a candidate word may only deviate from the form of the Voynich word to such an extent that no more than about one third of the letters are different (MAX_RATIO = 0.35). However, this would result in very few contiguous hits. Our solution finder is unlikely to look at the text with such "precision". Where is the leeway in that ? Wink

Code:
#!/usr/bin/env python3 # coding: utf-8 """ DoubleMetaphone + Levenshtein Hybrid Decoder (with MAX_RATIO-threshold) ----------------------------------------------------------------------- 1. Load Latin words from wordlist.txt 2. Build a DoubleMetaphone index 3. For each Voynich word:   - Compute its DoubleMetaphone codes   - Find matching or phonetically similar Latin words   - Rank candidates using Levenshtein distance   - Output the best candidate or <no match> """ import sys from metaphone import doublemetaphone from collections import defaultdict from tqdm import tqdm # --------------------------------------------------------- # Parameter: maximum allowed relative distance # --------------------------------------------------------- MAX_RATIO = 0.35  # e.g., 35% of word length # --------------------------------------------------------- # Levenshtein distance # --------------------------------------------------------- def levenshtein(a, b):     if not a:         return len(b)     if not b:         return len(a)     dp = range(len(b) + 1)     for i, ca in enumerate(a, 1):         new_dp = [i]         for j, cb in enumerate(b, 1):             if ca == cb:                 new_dp.append(dp[j-1])             else:                 new_dp.append(1 + min(dp[j-1], dp[j], new_dp[-1]))         dp = new_dp     return dp[-1] # --------------------------------------------------------- # Load Latin words # --------------------------------------------------------- def load_wordlist(path="wordlist.txt"):     words = []     with open(path, "r", encoding="utf8") as f:         for line in f:             word = line.strip().lower()             if word:                 words.append(word)     return words # --------------------------------------------------------- # Normalize a Voynich word # --------------------------------------------------------- def normalize_voynich_word(w):     return "".join(c.lower() for c in w if c.isalpha()) # --------------------------------------------------------- # Build Metaphone index # --------------------------------------------------------- def build_metaphone_index(words):     index = defaultdict(list)     for w in words:         m1, m2 = doublemetaphone(w)         if m1:             index[m1].append(w)         if m2 and m2 != m1:             index[m2].append(w)     return index # --------------------------------------------------------- # Combined DoubleMetaphone + Levenshtein matching # --------------------------------------------------------- def hybrid_match(v_word, index, top_n=1):     if not v_word:         return ["<no match>"]     m1, m2 = doublemetaphone(v_word)     candidates = []     # 1) Exact metaphone matches     if m1 in index:         candidates.extend(index[m1])     if m2 in index:         candidates.extend(index[m2])     # 2) If too few candidates, expand by prefix similarity     if len(candidates) < 5 and m1:         prefix = m1[:2]         for key in index:             if key.startswith(prefix):                 candidates.extend(index[key])     if not candidates:         return ["<no match>"]     # 3) Compute Levenshtein distances     scored = []     for cand in candidates:         # compare only equal-length segment         score = levenshtein(v_word, cand[:len(v_word)])         scored.append((score, cand))     scored.sort(key=lambda x: x[0])     best_score = scored[0][0]     word_len = len(v_word)     # 4) Check acceptance threshold     max_allowed = max(1, int(word_len * MAX_RATIO))     if best_score > max_allowed:         return ["<no match>"]     # 5) All candidates with the same best score distance     best_candidates = [w for s, w in scored if s == best_score]     return best_candidates[:top_n] # --------------------------------------------------------- # MAIN # --------------------------------------------------------- def main():     if len(sys.argv) < 3:         print("Usage: python3 phonetic_levenshtein.py voyn2latin.txt output.txt")         sys.exit(1)     infile = sys.argv[1]     outfile = sys.argv[2]     print("Loading Latin wordlist …")     latin_words = load_wordlist("wordlist.txt")     print(f"{len(latin_words)} Latin words loaded.")     print("Building DoubleMetaphone index …")     index = build_metaphone_index(latin_words)     print(f"Index contains {len(index)} metaphone keys.")     print("Decoding …")     with open(infile, "r", encoding="utf8") as f:         lines = f.readlines()     output_lines = []     for line in tqdm(lines, desc="Lines"):         if not line.strip():             output_lines.append("")             continue         words = line.split()         decoded_line = []         for w in words:             normalized = normalize_voynich_word(w)             matches = hybrid_match(normalized, index, top_n=1)             decoded_line.append(matches[0])         output_lines.append(" ".join(decoded_line))     with open(outfile, "w", encoding="utf8") as f:         for row in output_lines:             f.write(row + "\n")     print("Done →", outfile) if __name__ == "__main__":     main()

Total words: 38529
Matches: 11348 (29.45%)
No matches: 27181 (70.55%)
Reply


Messages In This Thread
RE: VMS to pseudo - latin ( not a solution !) - by bi3mw - 24-11-2025, 05:16 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)