Files
cimai/spin_glass.py
alireza 8940a7d7f9 first
2025-02-19 13:45:07 +03:30

242 lines
9.2 KiB
Python

import numpy as np
from itertools import product
import re
class SpinGlassHamiltonian:
def __init__(self, n_words=4, word_bits=8, text_file='sentences.txt', seed=None):
"""
Initialize a fully connected spin glass Hamiltonian with J_ij couplings
constructed from text patterns
Args:
n_words (int): Number of words per pattern
word_bits (int): Number of bits to encode each word
text_file (str): Path to file containing training sentences
seed (int): Random seed for reproducibility
"""
if seed is not None:
np.random.seed(seed)
self.n_words = n_words
self.word_bits = word_bits
self.n_spins = n_words * word_bits
# Load text from file
try:
with open(text_file, 'r') as f:
text = f.read()
except FileNotFoundError:
print(f"Warning: {text_file} not found. Using default text.")
text = """
The quantum spin glass model shows fascinating behavior
in statistical physics and complex systems research
many body interactions lead to frustration effects
ground state properties reveal emergent phenomena
"""
# Generate patterns from text
self.patterns, self.words = self.text_to_patterns(text, n_words, word_bits)
# Initialize J matrix for 2-point interactions
self.J = np.zeros((self.n_spins, self.n_spins))
# Construct J using patterns
for pattern in self.patterns:
flat_pattern = pattern.flatten()
# 2-point interactions
self.J += np.outer(flat_pattern, flat_pattern)
# Normalize by number of patterns
self.J = self.J / len(self.patterns)
np.fill_diagonal(self.J, 0) # No self-interactions
print("Words used to construct patterns:")
for i, words in enumerate(self.words):
print(f"\nPattern {i} words: {words}")
print(f"Pattern {i} configuration ({self.n_words}x{self.word_bits}):")
print(self.patterns[i])
def text_to_patterns(self, text, n_words, word_bits):
"""Convert text to patterns where each row encodes a full word"""
words = re.findall(r'\b\w+\b', text.lower())
patterns = []
pattern_words = []
# Modified to shift one word at a time
for i in range(len(words) - n_words + 1):
word_group = words[i:i+n_words]
pattern_words.append(word_group)
# Create pattern (n_words x word_bits)
pattern = np.zeros((n_words, word_bits))
for row, word in enumerate(word_group):
# Hash the word to a unique pattern
word_hash = sum(ord(c) for c in word)
# Generate word_bits number of bits
for col in range(word_bits):
bit_val = (word_hash >> col) & 1
pattern[row, col] = 1 if bit_val else -1
patterns.append(pattern)
if len(patterns) >= 5: # Limit to 5 patterns
break
return np.array(patterns), pattern_words
def calculate_energy(self, state):
"""
Calculate energy using only 2-point interactions
Args:
state (numpy.array): Array of +1/-1 spins
Returns:
float: Energy of the configuration
"""
# 2-point interaction energy
energy = -0.5 * np.sum(self.J * np.outer(state, state))
return energy
def state_to_2d(self, state):
"""Convert 1D state array to 2D grid (n_words x word_bits)"""
return state.reshape(self.n_words, self.word_bits)
def state_to_words(self, state_2d):
"""Analyze a 2D state pattern to find closest matching words from patterns"""
closest_words = []
for row_idx, row in enumerate(state_2d):
# Find the pattern row that has the highest overlap with this state row
max_overlap = -1
best_word = None
for pattern_idx, pattern in enumerate(self.patterns):
for word_idx, pattern_row in enumerate(pattern):
overlap = abs(np.sum(row * pattern_row) / self.word_bits)
if overlap > max_overlap:
max_overlap = overlap
best_word = self.words[pattern_idx][word_idx]
closest_words.append(f"{best_word} (overlap: {max_overlap:.2f})")
return closest_words
def generate_states(n_spins):
for state in product([-1, 1], repeat=n_spins):
yield np.array(state)
def main():
# Create a 4x5 spin glass system (4 words per pattern, 5 bits per word)
n_words = 24
word_bits = 16
sg = SpinGlassHamiltonian(n_words=n_words, word_bits=word_bits, seed=42)
# Initialize the best state with random configuration
current_state = np.random.choice([-1, 1], size=n_words * word_bits)
current_energy = sg.calculate_energy(current_state)
# Learn one word at a time
for word_idx in range(n_words):
print(f"\nOptimizing word {word_idx + 1}...")
# Try all possibilities for current word while keeping others fixed
best_energy = current_energy
best_state = current_state.copy()
# Generate all possibilities for one word (2^word_bits combinations)
for word_state in product([-1, 1], repeat=word_bits):
# Create test state by replacing only the current word's bits
test_state = current_state.copy()
start_idx = word_idx * word_bits
end_idx = start_idx + word_bits
test_state[start_idx:end_idx] = word_state
# Calculate energy
energy = sg.calculate_energy(test_state)
# Update if better
if energy < best_energy:
best_energy = energy
best_state = test_state.copy()
# Update current state with best found for this word
current_state = best_state
current_energy = best_energy
# Show intermediate result
state_2d = sg.state_to_2d(current_state)
print(f"Current energy: {current_energy:.4f}")
print("Current state:")
print(state_2d)
words = sg.state_to_words(state_2d)
print("Current words:")
for i, word_info in enumerate(words):
print(f"Word {i+1}: {word_info}")
# Store final result
state_energies = [(current_state, current_energy)]
print(f"\nSpin Glass System with {n_words}x{word_bits} lattice")
print("\nOptimized state:")
# Get the single optimized state
state, energy = state_energies[0]
state_2d = sg.state_to_2d(state)
# Calculate absolute overlap with each pattern
overlaps = []
for p, pattern in enumerate(sg.patterns):
overlap = abs(np.sum(state_2d * pattern) / (n_words*word_bits))
overlaps.append(f"P{p}: {overlap:.2f}")
print(f"\nEnergy: {energy:.4f}")
print(f"State configuration:\n{state_2d}")
print("\nGenerated words:")
closest_words = sg.state_to_words(state_2d)
for row_idx, word_info in enumerate(closest_words):
print(f"Word {row_idx + 1}: {word_info}")
print(f"Absolute overlaps with patterns: {', '.join(overlaps)}")
# Show which original sentence this state is most similar to
best_pattern_idx = np.argmax([float(o.split(': ')[1]) for o in overlaps])
print(f"Most similar to sentence: {' '.join(sg.words[best_pattern_idx])}")
# Generate a 30-word sentence
print("\nGenerating 30-word sentence:")
# Start with the first n_words-1 words from the best matching pattern
sentence = list(sg.words[best_pattern_idx][:-1]) # Take all but last word as initial context
# Generate remaining words until we reach 30
while len(sentence) < 30:
# Create state from current context
context_state = np.zeros((n_words, word_bits))
for i, word in enumerate(sentence[-n_words+1:]):
word_hash = sum(ord(c) for c in word)
for col in range(word_bits):
bit_val = (word_hash >> col) & 1
context_state[i, col] = 1 if bit_val else -1
# Optimize the next word (last row of state)
best_energy = float('inf')
best_word_state = None
# Try all possibilities for the next word
for word_state in product([-1, 1], repeat=word_bits):
test_state = context_state.copy()
test_state[-1] = word_state
# Calculate energy
energy = sg.calculate_energy(test_state.flatten())
if energy < best_energy:
best_energy = energy
best_word_state = test_state.copy()
# Get the predicted word
words = sg.state_to_words(best_word_state)
next_word = words[-1].split(" (")[0] # Get just the word, not the overlap info
sentence.append(next_word)
print(" ".join(sentence))
if __name__ == "__main__":
main()