uslap-query / Code_files /archive /uslap_forest_v3_domain_aware.py
uslap's picture
Upload folder using huggingface_hub
7cc8e29 verified
Raw
History Blame Contribute Delete
43.6 kB
#!/usr/bin/env python3
"""
USLaP SELF-GROWING FOREST SYSTEM - V3 DOMAIN-AWARE ENHANCEMENT
Enhanced with missing domain variant detection for fractal ratio families.
"""
import openpyxl
from openpyxl import load_workbook
from pathlib import Path
from datetime import datetime
import os
import sys
import re
# ============================================================================
# COMPONENT 1: SELF-READER ENGINE
# ============================================================================
class SelfReader:
"""Reads the entire Excel file into memory as a living graph"""
def __init__(self, filepath):
self.filepath = Path(filepath)
self.wb = None
self.graph = {}
self.foundation = {}
self.mechanism = {}
self.application = {}
self.sheets_data = {}
self.total_entries = 0
def read_all(self):
"""Read every sheet in the workbook"""
print(f"\nπŸ“– Reading {self.filepath.name}...")
if not self.filepath.exists():
raise FileNotFoundError(f"File not found: {self.filepath}")
self.wb = load_workbook(self.filepath, data_only=True)
# Read all sheets
for sheet_name in self.wb.sheetnames:
sheet = self.wb[sheet_name]
data = []
headers = []
# Get headers from first row
for row in sheet.iter_rows(min_row=1, max_row=1, values_only=True):
headers = [str(h) if h else f"COL_{i}" for i, h in enumerate(row)]
# Get data from remaining rows
for row in sheet.iter_rows(min_row=2, values_only=True):
if any(cell for cell in row if cell is not None):
row_dict = {}
for i, value in enumerate(row):
if i < len(headers):
row_dict[headers[i]] = value
if row_dict:
data.append(row_dict)
self.sheets_data[sheet_name] = data
# Categorize by sheet prefix
if sheet_name.startswith('F'):
self.foundation[sheet_name] = data
elif sheet_name.startswith('M'):
self.mechanism[sheet_name] = data
elif sheet_name.startswith('A'):
self.application[sheet_name] = data
print(f" βœ“ {sheet_name}: {len(data)} rows")
# Build graph from A1_ENTRIES
self.build_graph()
return self
def build_graph(self):
"""Build a connected graph from A1_ENTRIES"""
entries = self.sheets_data.get('A1_ENTRIES', [])
for entry in entries:
entry_id = entry.get('ENTRY_ID') or entry.get('Π—ΠΠŸΠ˜Π‘Π¬_ID')
if not entry_id:
continue
node = {
'id': str(entry_id),
'en_term': entry.get('EN_TERM') or entry.get('Π Π£Π‘_Π’Π•Π ΠœΠ˜Π', ''),
'root': entry.get('ROOT_ID', ''),
'root_letters': entry.get('ROOT_LETTERS') or entry.get('ΠšΠžΠ Π•ΠΠ¬_ID', ''),
'network': entry.get('NETWORK_ID') or entry.get('Π‘Π•Π’Π¬_ID', ''),
'allah_name': entry.get('ALLAH_NAME_ID') or entry.get('ИМЯ_АЛЛАΠ₯А_ID', ''),
'phonetic_chain': entry.get('PHONETIC_CHAIN') or entry.get('Π€ΠžΠΠ•Π’Π˜Π§Π•Π‘ΠšΠΠ―_Π¦Π•ΠŸΠ¬', ''),
'score': entry.get('SCORE') or entry.get('БАЛЛ', 0),
'ar_word': entry.get('AR_WORD') or entry.get('АР_Π‘Π›ΠžΠ’Πž', ''),
'pattern': entry.get('PATTERN') or entry.get('ΠŸΠΠ’Π’Π•Π Π', ''),
'foundation_ref': entry.get('FOUNDATION_REF') or entry.get('ΠžΠ‘ΠΠžΠ’ΠΠΠ˜Π•', ''),
'connections': []
}
self.graph[str(entry_id)] = node
self.total_entries = len(self.graph)
print(f"\nπŸ“Š Graph built: {self.total_entries} nodes")
return self.graph
# ============================================================================
# COMPONENT 2: PATTERN DETECTOR - ENHANCED WITH DOMAIN VARIANT DETECTION
# ============================================================================
class PatternDetector:
"""Finds gaps and opportunities in the existing data - ENHANCED VERSION"""
def __init__(self, reader):
self.reader = reader
self.graph = reader.graph
self.gaps = []
self.candidates = []
self.shifts = self._load_shifts()
self.networks = self._load_networks()
self.roots = self._build_root_index()
def _load_shifts(self):
"""Load phonetic shifts from M1 sheet"""
shifts = {}
m1_data = self.reader.sheets_data.get('M1_PHONETIC_SHIFTS', [])
for row in m1_data:
shift_id = row.get('SHIFT_ID') or row.get('Π‘Π”Π’Π˜Π“_ID')
if shift_id:
shifts[shift_id] = {
'ar_letter': row.get('AR_LETTER') or row.get('АР_Π‘Π£ΠšΠ’Π', ''),
'en_outputs': row.get('EN_OUTPUTS') or row.get('Π Π£Π‘_Π’Π«Π₯ΠžΠ”Π«', ''),
'examples': row.get('EXAMPLES') or row.get('ΠŸΠ Π˜ΠœΠ•Π Π«', '')
}
return shifts
def _load_networks(self):
"""Load networks from M4 sheet"""
networks = {}
m4_data = self.reader.sheets_data.get('M4_NETWORKS', [])
for row in m4_data:
net_id = row.get('NETWORK_ID') or row.get('Π‘Π•Π’Π¬_ID')
if net_id:
networks[net_id] = {
'name': row.get('NAME') or row.get('ΠΠΠ—Π’ΠΠΠ˜Π•', ''),
'entries': []
}
return networks
def _build_root_index(self):
"""Group entries by root"""
roots = {}
for node_id, node in self.graph.items():
root = node.get('root')
if root:
if root not in roots:
roots[root] = []
roots[root].append(node_id)
return roots
def _extract_ratio_from_term(self, term):
"""Extract ratio from terms like 'musical fourth (4/3)'"""
if not term:
return None, None
term_str = str(term)
# Look for patterns like (4/3), (5/3), (7/3) etc.
import re
match = re.search(r'\((\d+)/(\d+)\)', term_str)
if match:
numerator = int(match.group(1))
denominator = int(match.group(2))
return numerator, denominator
# Also check for "4/3" without parentheses
match = re.search(r'(\d+)/(\d+)', term_str)
if match:
numerator = int(match.group(1))
denominator = int(match.group(2))
return numerator, denominator
return None, None
def _detect_domain_from_term(self, term):
"""Detect which domain a term belongs to"""
if not term:
return 'unknown'
term_lower = str(term).lower()
if 'musical' in term_lower or 'fourth' in term_lower or 'sixth' in term_lower or 'interval' in term_lower:
return 'musical'
elif 'tempo' in term_lower or 'rhythmic' in term_lower:
return 'rhythmic'
elif 'maqam' in term_lower:
return 'maqam'
elif 'sacred' in term_lower or 'proportion' in term_lower:
return 'spiritual/geometric'
elif 'harmonic' in term_lower or 'string' in term_lower:
return 'harmonic'
elif 'temporal' in term_lower or 'cycle' in term_lower:
return 'temporal'
elif 'architectural' in term_lower or 'spatial' in term_lower:
return 'spatial'
elif 'prayer' in term_lower or 'spiritual' in term_lower:
return 'spiritual'
elif 'growth' in term_lower or 'botanical' in term_lower:
return 'botanical'
elif 'orbital' in term_lower or 'celestial' in term_lower:
return 'celestial'
elif 'mathematical' in term_lower or 'constant' in term_lower:
return 'numerical'
else:
return 'unknown'
def get_existing_ratios_for_network(self, network):
"""Get all ratios already present in a network"""
existing_ratios = set()
for node_id, node in self.graph.items():
if node.get('network') == network:
term = node.get('en_term', '')
num, den = self._extract_ratio_from_term(term)
if num and den:
existing_ratios.add(f"{num}/{den}")
return existing_ratios
def detect_all(self):
"""Run all detection methods - ENHANCED with domain variant detection"""
print("\nπŸ” Detecting patterns and gaps...")
# Clear previous results
self.gaps = []
self.candidates = []
# Run detection methods in priority order
self.find_missing_domain_variants() # NEW: Highest priority
self.find_networks_without_ratios()
self.find_roots_with_multiple_entries()
self.find_entries_without_networks()
self.find_potential_ratio_entries()
print(f" Found {len(self.gaps)} gaps, {len(self.candidates)} candidates")
return self.gaps, self.candidates
def find_missing_domain_variants(self):
"""NEW: Find missing domain variants for ratio families"""
print(" πŸ” Checking for missing domain variants...")
# Group entries by ratio
ratio_groups = {}
for node_id, node in self.graph.items():
term = node.get('en_term', '')
num, den = self._extract_ratio_from_term(term)
if num and den:
ratio = f"{num}/{den}"
domain = self._detect_domain_from_term(term)
if ratio not in ratio_groups:
ratio_groups[ratio] = {
'entries': [],
'domains': set(),
'networks': set()
}
ratio_groups[ratio]['entries'].append(node_id)
ratio_groups[ratio]['domains'].add(domain)
ratio_groups[ratio]['networks'].add(node.get('network', ''))
if not ratio_groups:
return
# Define core domains we want each ratio to have
core_domains = {
'musical',
'rhythmic',
'spiritual/geometric',
'maqam'
}
# For each ratio, check missing domains
for ratio, data in ratio_groups.items():
existing_domains = data['domains']
missing_domains = core_domains - existing_domains
if missing_domains:
# Calculate priority: fewer existing domains = higher priority
domain_coverage = len(existing_domains) / len(core_domains)
priority = int((1 - domain_coverage) * 100) # 0-100 scale
# Get a sample network from this ratio's entries
sample_network = next(iter(data['networks'])) if data['networks'] else 'N08'
for missing_domain in sorted(missing_domains):
self.gaps.append({
'type': 'missing_domain_variant',
'ratio': ratio,
'missing_domain': missing_domain,
'existing_domains': sorted(existing_domains),
'network': sample_network,
'entry_count': len(data['entries']),
'priority': priority
})
if missing_domains:
print(f" Found {len(missing_domains)} missing domains for ratio {ratio}")
def find_networks_without_ratios(self):
"""Find networks that should have ratio entries but don't"""
network_entries = {}
for node_id, node in self.graph.items():
net = node.get('network')
if net:
if net not in network_entries:
network_entries[net] = []
network_entries[net].append(node_id)
for net, entries in network_entries.items():
if len(entries) >= 3: # Networks with at least 3 entries
# Check if this network has any ratio entries using ratio extraction
has_ratio = False
for entry_id in entries:
entry = self.graph[entry_id]
term = entry.get('en_term', '')
num, den = self._extract_ratio_from_term(term)
if num and den:
has_ratio = True
break
if not has_ratio:
self.gaps.append({
'type': 'network_needs_ratio',
'network': net,
'entries': entries,
'count': len(entries),
'priority': len(entries) * 2
})
def find_roots_with_multiple_entries(self):
"""Find roots that appear multiple times but aren't in networks"""
for root, entries in self.roots.items():
if len(entries) >= 2:
# Check if these entries share a network
networks = set()
for entry_id in entries:
net = self.graph[entry_id].get('network')
if net:
networks.add(net)
if len(networks) <= 1: # They don't have diverse networks
self.candidates.append({
'type': 'potential_network',
'root': root,
'entries': entries,
'count': len(entries),
'networks': list(networks)
})
def find_entries_without_networks(self):
"""Find entries that don't belong to any network"""
for node_id, node in self.graph.items():
if not node.get('network') and node.get('score', 0) >= 8:
self.gaps.append({
'type': 'entry_needs_network',
'entry': node_id,
'term': node.get('en_term'),
'score': node.get('score'),
'priority': 10 - node.get('score', 0)
})
def find_potential_ratio_entries(self):
"""Find entries that might contain hidden ratios"""
ratio_keywords = ['circle', 'moon', 'light', 'dombra', 'yurt', 'doira',
'drum', 'string', 'maqam', 'proportion', 'measure']
for node_id, node in self.graph.items():
term = node.get('en_term', '').lower()
if any(keyword in term for keyword in ratio_keywords):
if 'ratio' not in term:
self.candidates.append({
'type': 'potential_ratio',
'entry': node_id,
'term': node.get('en_term'),
'keywords': [k for k in ratio_keywords if k in term],
'priority': 5
})
# ============================================================================
# COMPONENT 3: GENERATOR ENGINE - ENHANCED FOR DOMAIN VARIANTS
# ============================================================================
class GeneratorEngine:
"""Creates new entries from patterns and gaps - ENHANCED FOR DOMAIN VARIANTS"""
def __init__(self, reader, detector):
self.reader = reader
self.detector = detector
self.new_entries = []
self.next_id = self._find_next_id()
self.ratio_templates = [
(4, 3, 'musical fourth', 'N08', 'Harmony - fourth interval'),
(5, 3, 'musical sixth', 'N08', 'Harmony - sixth interval'),
(7, 3, 'maqam proportion', 'N08', 'Harmony - maqam Bayyati'),
(7, 5, 'prayer mat proportion', 'N06', 'Fitra - original design'),
(11, 5, 'dombra proportion', 'N16', 'Gatherer - sacred proportion'),
(22, 7, 'circle constant', 'N07', 'Light - circular perfection'),
(19, 7, 'growth constant', 'N10', 'Cognition - angelic measure'),
(12, 7, 'dome proportion', 'N07', 'Light - sacred geometry'),
(28, 7, 'lunar proportion', 'N05', 'Numbering - moon cycles'),
(99, 70, 'high precision √2', 'N05', 'Numbering - sacred precision'),
(355, 113, 'high precision Ο€', 'N05', 'Numbering - divine accuracy')
]
# Domain templates for fractal generation
self.domain_templates = [
("melodic", "musical interval"),
("rhythmic", "tempo ratio"),
("geometric", "sacred proportion"),
("harmonic", "string division"),
("temporal", "time cycle"),
("spatial", "architectural ratio"),
("spiritual", "prayer cycle"),
("botanical", "growth pattern"),
("celestial", "orbital ratio"),
("numerical", "mathematical constant"),
]
def _extract_ratio_from_term(self, term):
"""Extract ratio from terms like 'musical fourth (4/3)'"""
if not term:
return None, None
term_str = str(term)
# Look for patterns like (4/3), (5/3), (7/3) etc.
import re
match = re.search(r'\((\d+)/(\d+)\)', term_str)
if match:
numerator = int(match.group(1))
denominator = int(match.group(2))
return numerator, denominator
# Also check for "4/3" without parentheses
match = re.search(r'(\d+)/(\d+)', term_str)
if match:
numerator = int(match.group(1))
denominator = int(match.group(2))
return numerator, denominator
return None, None
def _get_existing_ratios_for_network(self, network):
"""Get all ratios already present in a network"""
existing_ratios = set()
for node_id, node in self.reader.graph.items():
if node.get('network') == network:
term = node.get('en_term', '')
num, den = self._extract_ratio_from_term(term)
if num and den:
existing_ratios.add(f"{num}/{den}")
return existing_ratios
def _generate_domain_variant(self, ratio_str, domain_name, domain_desc, network):
"""Generate a specific domain variant for a ratio"""
# Parse ratio string
import re
match = re.search(r'(\d+)/(\d+)', ratio_str)
if not match:
return None
numerator = int(match.group(1))
denominator = int(match.group(2))
# Create entry name based on domain
if domain_name == 'maqam':
entry_name = f"maqam proportion ({numerator}/{denominator})"
else:
entry_name = f"{domain_desc} {numerator}/{denominator}"
entry = self._create_ratio_entry(
numerator, denominator,
entry_name,
network,
f"Divine ratio {numerator}/{denominator} manifesting in {domain_name} domain β€” Q54:49"
)
return entry
def _generate_fractal_variants(self, base_num, base_den, network):
"""Generate fractal variants of a ratio across different domains"""
variants = []
# Check existing ratios for this network
existing_ratios = self._get_existing_ratios_for_network(network)
base_ratio_str = f"{base_num}/{base_den}"
# If base ratio already exists, generate variants with different domains
if base_ratio_str in existing_ratios:
for domain_name, domain_desc in self.domain_templates:
# Create a unique variant by combining ratio with domain
entry = self._create_ratio_entry(
base_num, base_den,
f"{domain_desc} {base_num}/{base_den}",
network,
f"Divine ratio {base_num}/{base_den} manifesting in {domain_name} domain β€” Q54:49"
)
variants.append(entry)
# Limit to 3 variants per generation cycle
if len(variants) >= 3:
break
else:
# Base ratio doesn't exist, create it first
entry = self._create_ratio_entry(
base_num, base_den,
f"divine ratio {base_num}/{base_den}",
network,
f"Foundational ratio {base_num}/{base_den} β€” Q54:49"
)
variants.append(entry)
return variants
def _find_next_id(self):
"""Find the next available ENTRY_ID"""
max_num = 0
for node_id in self.reader.graph.keys():
if node_id and node_id.startswith('F'):
try:
num = int(node_id[1:])
max_num = max(max_num, num)
except:
pass
return max_num + 1
def generate_from_gaps(self, gaps, candidates):
"""Generate new entries from detected gaps and candidates - ENHANCED FOR DOMAIN VARIANTS"""
print("\n🌱 Generating new entries from gaps...")
# Process gaps by priority (highest first)
gaps_sorted = sorted(gaps, key=lambda x: x.get('priority', 0), reverse=True)
gap_count = 0
processed_ratios = set() # Track which ratios we've already generated variants for
# First process missing domain variants (highest priority)
for gap in gaps_sorted:
if gap['type'] == 'missing_domain_variant':
self._generate_missing_domain_variant(gap, processed_ratios)
gap_count += 1
elif gap['type'] == 'network_needs_ratio':
self._generate_ratio_for_network(gap)
gap_count += 1
elif gap['type'] == 'entry_needs_network':
self._assign_entry_to_network(gap)
gap_count += 1
# Limit total gaps processed per cycle
if gap_count >= 5:
break
# If no gaps found, process top 3 candidates, avoiding duplicates
if gap_count == 0:
print(" No gaps found, checking candidates...")
candidate_count = 0
for candidate in candidates:
if candidate['type'] == 'potential_ratio':
# Extract ratio from candidate
term = candidate['term']
num, den = self._extract_ratio_from_term(term)
if not num or not den:
continue
ratio_str = f"{num}/{den}"
# Skip if we've already processed this ratio in this cycle
if ratio_str in processed_ratios:
print(f" Skipping duplicate ratio: {ratio_str}")
continue
# Generate fractal variants for this ratio
self._generate_fractal_from_candidate(candidate)
processed_ratios.add(ratio_str)
candidate_count += 1
# Limit to 2 candidates per generation cycle
if candidate_count >= 2:
break
print(f" Generated {len(self.new_entries)} new entries")
return self.new_entries
def _generate_missing_domain_variant(self, gap, processed_ratios):
"""Generate a missing domain variant for a ratio"""
ratio = gap['ratio']
missing_domain = gap['missing_domain']
network = gap['network']
# Skip if we've already processed this ratio in this cycle
if ratio in processed_ratios:
print(f" Skipping duplicate ratio: {ratio}")
return
# Find domain description
domain_desc = "domain variant"
for domain_name, desc in self.domain_templates:
if domain_name == missing_domain:
domain_desc = desc
break
if missing_domain == 'maqam':
domain_desc = "maqam proportion"
# Generate the variant
variant = self._generate_domain_variant(ratio, missing_domain, domain_desc, network)
if variant:
self.new_entries.append(variant)
print(f" Generated missing domain variant: {variant['EN_TERM']}")
processed_ratios.add(ratio)
def _generate_fractal_from_candidate(self, candidate):
"""Generate fractal variants from a potential ratio candidate"""
entry_id = candidate['entry']
term = candidate['term']
# Extract ratio from the candidate term
num, den = self._extract_ratio_from_term(term)
if not num or not den:
print(f" Could not extract ratio from: {term}")
return
# Get the network from the entry
entry = self.reader.graph.get(entry_id, {})
network = entry.get('network')
if not network:
# Default to N08 (Harmony) for ratio entries
network = 'N08'
# Generate fractal variants for this ratio
variants = self._generate_fractal_variants(num, den, network)
# Add variants to new entries
for variant in variants:
self.new_entries.append(variant)
print(f" Generated: {variant['EN_TERM']}")
def _generate_ratio_for_network(self, gap):
"""Generate fractal variant ratio entries for a network"""
network = gap['network']
# Find matching ratio templates for this network
matching_templates = []
for num, den, name, net, desc in self.ratio_templates:
if net == network:
matching_templates.append((num, den, name, net, desc))
if matching_templates:
# Generate fractal variants for each matching template
for num, den, name, net, desc in matching_templates:
variants = self._generate_fractal_variants(num, den, network)
self.new_entries.extend(variants)
# Limit to 5 total variants per generation cycle
if len(self.new_entries) >= 5:
break
else:
# No matching templates, create a generic fractal variant
variants = self._generate_fractal_variants(1, 1, network)
self.new_entries.extend(variants[:2]) # Just 2 generic variants
def _assign_entry_to_network(self, gap):
"""Create a network assignment for an entry"""
entry_id = gap['entry']
term = gap['term']
# Find appropriate network based on term
network = 'N08' # Default to Harmony
if any(word in term.lower() for word in ['light', 'sun', 'moon', 'circle']):
network = 'N07'
elif any(word in term.lower() for word in ['number', 'count', 'zero', 'cipher']):
network = 'N05'
elif any(word in term.lower() for word in ['faith', 'amen', 'secure']):
network = 'N06'
# Create a note about the assignment
entry = {
'ENTRY_ID': f"NOTE{self.next_id:04d}",
'SCORE': 8,
'EN_TERM': f"{term} network assignment",
'AR_WORD': 'β€”',
'ROOT_ID': gap['entry'],
'ROOT_LETTERS': 'β€”',
'QUR_MEANING': f"Should be in {network}",
'PATTERN': 'A',
'NETWORK_ID': network,
'PHONETIC_CHAIN': 'β€”',
'INVERSION_TYPE': 'HIDDEN',
'SOURCE_FORM': 'β€”',
'FOUNDATION_REF': f"GAP:{gap['type']}"
}
self.next_id += 1
self.new_entries.append(entry)
def _create_ratio_entry(self, numerator, denominator, name, network, description):
"""Create a ratio entry"""
entry_id = f"F{self.next_id:04d}"
self.next_id += 1
return {
'ENTRY_ID': entry_id,
'SCORE': 9,
'EN_TERM': name,
'AR_WORD': f"({numerator}/{denominator})",
'ROOT_ID': f"R{self.next_id}",
'ROOT_LETTERS': 'β€”',
'QUR_MEANING': f"Divine ratio {numerator}/{denominator} - {description}",
'PATTERN': 'A',
'NETWORK_ID': network,
'PHONETIC_CHAIN': 'β†’'.join(['?'] * 3),
'INVERSION_TYPE': 'HIDDEN',
'SOURCE_FORM': f"ratio:{numerator}/{denominator}",
'FOUNDATION_REF': f"RATIO:{numerator}/{denominator}"
}
def generate_seed_entries(self):
"""Generate initial seed ratio entries"""
print("\n🌱 Planting seed entries...")
for num, den, name, net, desc in self.ratio_templates:
entry = self._create_ratio_entry(num, den, name, net, desc)
self.new_entries.append(entry)
print(f" Planted {len(self.new_entries)} seeds")
return self.new_entries
# ============================================================================
# COMPONENT 4: VALIDATION ENGINE
# ============================================================================
class ValidationEngine:
"""Validates new entries against existing patterns"""
def __init__(self, reader):
self.reader = reader
def validate_all(self, entries):
"""Validate all new entries"""
print("\nβœ… Validating new entries...")
validated = []
for entry in entries:
valid_entry = self._validate_entry(entry)
if valid_entry:
validated.append(valid_entry)
print(f" Validated {len(validated)} of {len(entries)} entries")
return validated
def _validate_entry(self, entry):
"""Validate a single entry"""
# Check required fields
required = ['ENTRY_ID', 'EN_TERM']
for field in required:
if field not in entry or not entry[field]:
return None
# Auto-calculate score based on completeness
score = 5
if entry.get('ROOT_ID') and entry['ROOT_ID'] != 'β€”':
score += 1
if entry.get('NETWORK_ID'):
score += 1
if entry.get('PHONETIC_CHAIN') and entry['PHONETIC_CHAIN'] != 'β†’'.join(['?'] * 3):
score += 1
if entry.get('QUR_MEANING') and 'Divine' not in entry['QUR_MEANING']:
score += 1
if entry.get('FOUNDATION_REF') and entry['FOUNDATION_REF'].startswith('RATIO'):
score += 1
entry['SCORE'] = min(10, score)
return entry
# ============================================================================
# COMPONENT 5: SELF-WRITER ENGINE
# ============================================================================
class SelfWriter:
"""Writes new entries back to the Excel file"""
def __init__(self, reader):
self.reader = reader
self.filepath = reader.filepath
def write_all(self, new_entries):
"""Write all new entries to the file"""
if not new_entries:
print("\nπŸ’Ύ No new entries to write")
return 0
print("\nπŸ’Ύ Writing new entries to file...")
try:
# Load workbook fresh to avoid conflicts
wb = load_workbook(self.filepath)
# Get or create A1_ENTRIES sheet
if 'A1_ENTRIES' in wb.sheetnames:
sheet = wb['A1_ENTRIES']
else:
sheet = wb.create_sheet('A1_ENTRIES')
# Add headers
headers = ['ENTRY_ID', 'SCORE', 'EN_TERM', 'AR_WORD', 'ROOT_ID',
'ROOT_LETTERS', 'QUR_MEANING', 'PATTERN', 'NETWORK_ID',
'PHONETIC_CHAIN', 'INVERSION_TYPE', 'SOURCE_FORM', 'FOUNDATION_REF']
for col, header in enumerate(headers, 1):
sheet.cell(row=1, column=col, value=header)
# Find next empty row
next_row = sheet.max_row + 1
# Write each new entry
for entry in new_entries:
sheet.cell(row=next_row, column=1, value=entry['ENTRY_ID'])
sheet.cell(row=next_row, column=2, value=entry['SCORE'])
sheet.cell(row=next_row, column=3, value=entry['EN_TERM'])
sheet.cell(row=next_row, column=4, value=entry['AR_WORD'])
sheet.cell(row=next_row, column=5, value=entry['ROOT_ID'])
sheet.cell(row=next_row, column=6, value=entry.get('ROOT_LETTERS', 'β€”'))
sheet.cell(row=next_row, column=7, value=entry.get('QUR_MEANING', 'β€”'))
sheet.cell(row=next_row, column=8, value=entry.get('PATTERN', 'A'))
sheet.cell(row=next_row, column=9, value=entry.get('NETWORK_ID', ''))
sheet.cell(row=next_row, column=10, value=entry.get('PHONETIC_CHAIN', 'β€”'))
sheet.cell(row=next_row, column=11, value=entry.get('INVERSION_TYPE', 'HIDDEN'))
sheet.cell(row=next_row, column=12, value=entry.get('SOURCE_FORM', 'β€”'))
sheet.cell(row=next_row, column=13, value=entry.get('FOUNDATION_REF', 'β€”'))
next_row += 1
# Save
wb.save(self.filepath)
print(f" βœ“ Wrote {len(new_entries)} new entries to {self.filepath.name}")
return len(new_entries)
except Exception as e:
print(f" βœ— Error writing: {e}")
return 0
# ============================================================================
# COMPONENT 6: MAIN SYSTEM - ENHANCED
# ============================================================================
class SelfGrowingSystem:
"""The main orchestrator that runs all 6 components - ENHANCED VERSION"""
def __init__(self, filepath):
self.filepath = Path(filepath)
self.cycle_count = 0
self.growth_log = []
if not self.filepath.exists():
raise FileNotFoundError(f"File not found: {filepath}")
print("\n" + "="*60)
print("🌳 USLaP SELF-GROWING FOREST SYSTEM - V3 DOMAIN-AWARE")
print("="*60)
print(f"πŸ“ File: {self.filepath.name}")
print(f"πŸ“¦ Size: {self.filepath.stat().st_size / 1024 / 1024:.2f} MB")
print("="*60)
def run_full_cycle(self, seed_mode=False):
"""
Run all 6 components in sequence
seed_mode=True: Plant initial ratio seeds
seed_mode=False: Full growth cycle with pattern detection
"""
print(f"\n{'='*60}")
print(f"CYCLE {self.cycle_count + 1} - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"{'='*60}")
# COMPONENT 1: READ
print("\nπŸ“– COMPONENT 1: Self-Reader")
reader = SelfReader(self.filepath)
reader.read_all()
if seed_mode:
# COMPONENT 2: Skip detection for seed mode
gaps = []
candidates = []
# COMPONENT 3: Generate seeds
print("\n🌱 COMPONENT 3: Generator Engine (Seed Mode)")
generator = GeneratorEngine(reader, None)
new_entries = generator.generate_seed_entries()
else:
# COMPONENT 2: Detect patterns - ENHANCED
print("\nπŸ” COMPONENT 2: Pattern Detector (Enhanced)")
detector = PatternDetector(reader)
gaps, candidates = detector.detect_all()
# Show what was detected
print(f"\nπŸ“Š DETECTION RESULTS:")
print(f" Total gaps found: {len(gaps)}")
print(f" Total candidates: {len(candidates)}")
# Show top gaps by type
gap_types = {}
for gap in gaps:
gap_type = gap['type']
gap_types[gap_type] = gap_types.get(gap_type, 0) + 1
if gap_types:
print(f"\n Gap types:")
for gap_type, count in sorted(gap_types.items()):
print(f" {gap_type}: {count}")
# COMPONENT 3: Generate from gaps - ENHANCED
print("\n🌱 COMPONENT 3: Generator Engine (Enhanced)")
generator = GeneratorEngine(reader, detector)
new_entries = generator.generate_from_gaps(gaps, candidates)
# COMPONENT 4: Validate
print("\nβœ… COMPONENT 4: Validation Engine")
validator = ValidationEngine(reader)
validated_entries = validator.validate_all(new_entries)
# COMPONENT 5: Write
print("\nπŸ’Ύ COMPONENT 5: Self-Writer")
writer = SelfWriter(reader)
written = writer.write_all(validated_entries)
# COMPONENT 6: Log
self.cycle_count += 1
self.growth_log.append({
'cycle': self.cycle_count,
'timestamp': datetime.now().isoformat(),
'mode': 'SEED' if seed_mode else 'GROWTH',
'new_entries': written,
'gaps_found': len(gaps) if not seed_mode else 0,
'candidates': len(candidates) if not seed_mode else 0
})
print(f"\n{'='*60}")
print(f"βœ… CYCLE {self.cycle_count} COMPLETE")
print(f" Mode: {'🌱 SEED' if seed_mode else '🌳 GROWTH'}")
print(f" Added: {written} new entries")
print(f" Total entries now: {reader.total_entries + written}")
print(f"{'='*60}\n")
return self
def show_log(self):
"""Display growth log"""
print("\nπŸ“Š GROWTH LOG")
print("-" * 50)
for entry in self.growth_log:
mode_icon = "🌱" if entry['mode'] == 'SEED' else "🌳"
print(f"{mode_icon} Cycle {entry['cycle']}: {entry['new_entries']} entries added")
print("-" * 50)
def run_forever(self, interval_hours=24):
"""Run cycles automatically at specified intervals"""
import time
print(f"\n⏰ Running forever every {interval_hours} hours")
print("Press Ctrl+C to stop\n")
# First cycle: seed mode
self.run_full_cycle(seed_mode=True)
# Subsequent cycles: growth mode
while True:
time.sleep(interval_hours * 3600)
self.run_full_cycle(seed_mode=False)
self.show_log()
# ============================================================================
# MAIN EXECUTION - THIS RUNS EVERYTHING
# ============================================================================
def main():
"""Main function - runs all 6 components"""
# Get file path from command line or use default
if len(sys.argv) > 1:
filepath = sys.argv[1]
else:
filepath = "USLaP_Final_Data_Consolidated_Master.xlsx"
# Check if file exists
if not os.path.exists(filepath):
print(f"\n❌ File not found: {filepath}")
print("\nPlease provide the path to your USLaP Excel file:")
print(" python uslap_forest_v3_domain_aware.py /path/to/your/file.xlsx")
print("\nOr place this script in the same folder as your file.")
return 1
try:
# Create the system
system = SelfGrowingSystem(filepath)
# Ask user what they want to do
print("\nWhat would you like to do?")
print(" 1. Plant seeds (first run only)")
print(" 2. Run one growth cycle")
print(" 3. Run forever (automatic every 24h)")
print(" 4. Just show what would happen (dry run)")
choice = input("\nEnter choice (1-4): ").strip()
if choice == '1':
# Plant seeds only
system.run_full_cycle(seed_mode=True)
system.show_log()
print("\n🌱 Seeds planted! Run again with option 2 for growth.")
elif choice == '2':
# Run one growth cycle
system.run_full_cycle(seed_mode=False)
system.show_log()
elif choice == '3':
# Run forever
hours = input("Enter interval in hours (default 24): ").strip()
interval = int(hours) if hours else 24
system.run_forever(interval_hours=interval)
elif choice == '4':
# Dry run - just read and analyze
print("\nπŸ“– DRY RUN - Reading only...")
reader = SelfReader(filepath)
reader.read_all()
print("\nπŸ” Analyzing patterns (Enhanced)...")
detector = PatternDetector(reader)
gaps, candidates = detector.detect_all()
print("\nπŸ“Š ENHANCED ANALYSIS RESULTS")
print(f" Total entries: {reader.total_entries}")
print(f" Gaps found: {len(gaps)}")
print(f" Network candidates: {len(candidates)}")
# Show gap details
if gaps:
print(f"\n Top gaps (by priority):")
for i, gap in enumerate(sorted(gaps, key=lambda x: x.get('priority', 0), reverse=True)[:10]):
gap_type = gap['type']
if gap_type == 'missing_domain_variant':
print(f" {i+1}. {gap_type}: ratio {gap['ratio']} missing {gap['missing_domain']} domain")
elif gap_type == 'network_needs_ratio':
print(f" {i+1}. {gap_type}: network {gap['network']} needs ratio")
elif gap_type == 'entry_needs_network':
print(f" {i+1}. {gap_type}: entry {gap['entry']} needs network")
else:
print(f" {i+1}. {gap_type}")
print("\nβœ… Dry run complete. No changes made.")
else:
print("Invalid choice. Running default: plant seeds")
system.run_full_cycle(seed_mode=True)
print("\n✨ Done!")
except KeyboardInterrupt:
print("\n\nπŸ‘‹ System stopped by user")
except Exception as e:
print(f"\n❌ Error: {e}")
return 1
return 0
# ============================================================================
# ENTRY POINT
# ============================================================================
if __name__ == "__main__":
sys.exit(main())