/usr/local/lib/python3.10/dist-packages/transformers/tokenization_utils_base.py:1601: FutureWarning: `clean_up_tokenization_spaces` was not set. It will be set to `True` by default. This behavior will be depracted in transformers v4.45, and will be then set to `False` by default. For more details check this issue: https://github.com/huggingface/transformers/issues/31884
  warnings.warn(
/usr/local/lib/python3.10/dist-packages/transformers/generation/configuration_utils.py:615: UserWarning: `num_beams` is set to 1. However, `early_stopping` is set to `True` -- this flag is only used in beam-based generation modes. You should set `num_beams>1` or unset `early_stopping`.
  warnings.warn(
The attention mask and the pad token id were not set. As a consequence, you may observe unexpected behavior. Please pass your input's `attention_mask` to obtain reliable results.
Setting `pad_token_id` to `eos_token_id`:50256 for open-end generation.
ERROR:__main__:Error computing semantic harmony: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
ERROR:__main__:An error occurred during processing: Semantic harmony computation failed: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.

import logging
from typing import Tuple
import numpy as np
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch
from scipy.stats import entropy

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


class SemanticHarmonyModel:
    """
    A model to generate text based on input and compute a semantic harmony score
    between the input and generated output.
    """

    def __init__(self, model_name: str = "gpt2") -> None:
        """
        Initializes the SemanticHarmonyModel with a specified transformer model.

        Args:
            model_name (str): The name of the pre-trained transformer model to use.
                              Defaults to 'gpt2'.
        """
        try:
            logger.info(f"Loading tokenizer for model: {model_name}")
            self.tokenizer = AutoTokenizer.from_pretrained(model_name)
            self.tokenizer.pad_token_id = self.tokenizer.eos_token_id
            logger.info(f"Loading model: {model_name}")
            self.model = AutoModelForCausalLM.from_pretrained(model_name)
            self.model.eval()
            if torch.cuda.is_available():
                self.model.to('cuda')
                logger.info("Model moved to CUDA.")
            else:
                logger.info("CUDA not available. Using CPU.")
        except Exception as e:
            logger.error(f"Error loading model or tokenizer: {e}")
            raise RuntimeError(f"Initialization failed: {e}")

    def generate_text(self, input_text: str, max_length: int = 50) -> str:
        """
        Generates text based on the input text using the transformer model.

        Args:
            input_text (str): The input string to base the generation on.
            max_length (int): The maximum length of the generated text.

        Returns:
            str: The generated text.
        """
        try:
            logger.info("Encoding input text.")
            input_ids = self.tokenizer.encode(input_text, return_tensors='pt')
            if torch.cuda.is_available():
                input_ids = input_ids.to('cuda')

            logger.info("Generating text.")
            with torch.no_grad():
                output_ids = self.model.generate(
                    input_ids,
                    max_length=max_length,
                    num_return_sequences=1,
                    no_repeat_ngram_size=2,
                    early_stopping=True
                )

            generated_text = self.tokenizer.decode(output_ids[0], skip_special_tokens=True)
            logger.info("Text generation successful.")
            return generated_text
        except Exception as e:
            logger.error(f"Error during text generation: {e}")
            raise RuntimeError(f"Text generation failed: {e}")

    def compute_semantic_harmony(self, input_text: str, output_text: str) -> float:
        """
        Computes a semantic harmony score between input and output texts.

        The score ranges from 0 to 1, where 0 indicates complete dissonance
        and 1 indicates perfect semantic harmony.

        Args:
            input_text (str): The input string.
            output_text (str): The generated output string.

        Returns:
            float: The semantic harmony score.
        """
        try:
            logger.info("Computing semantic harmony score.")
            # Tokenize inputs
            input_ids = self.tokenizer.encode(input_text, return_tensors='pt')
            output_ids = self.tokenizer.encode(output_text, return_tensors='pt')

            if torch.cuda.is_available():
                input_ids = input_ids.to('cuda')
                output_ids = output_ids.to('cuda')

            # Obtain hidden states
            with torch.no_grad():
                input_hidden = self.model.transformer(input_ids).last_hidden_state
                output_hidden = self.model.transformer(output_ids).last_hidden_state

            # Dynamic contextual weighting: Compute attention alignment
            attention_scores = torch.matmul(input_hidden, output_hidden.transpose(-1, -2))
            attention_weights = torch.softmax(attention_scores, dim=-1)
            dynamic_weight = attention_weights.mean().item()

            # Semantic entropy: Measure uncertainty in the output
            output_probs = torch.softmax(self.model(output_ids)[0], dim=-1)
            output_entropy = entropy(output_probs.cpu().numpy().flatten())

            # Deep probabilistic interdependencies: Compute cosine similarity
            input_vec = input_hidden.mean(dim=1).squeeze().cpu().numpy()
            output_vec = output_hidden.mean(dim=1).squeeze().cpu().numpy()
            cosine_sim = np.dot(input_vec, output_vec) / (
                np.linalg.norm(input_vec) * np.linalg.norm(output_vec) + 1e-10
            )

            # Normalize entropy to [0,1]
            max_entropy = np.log(output_probs.shape[-1])
            normalized_entropy = 1 - (output_entropy / max_entropy)

            # Combine metrics with weights
            score = (
                0.4 * dynamic_weight +
                0.3 * normalized_entropy +
                0.3 * cosine_sim
            )

            # Clamp score to [0,1]
            score = max(0.0, min(1.0, score))
            logger.info(f"Semantic harmony score computed: {score:.4f}")
            return score
        except Exception as e:
            logger.error(f"Error computing semantic harmony: {e}")
            raise RuntimeError(f"Semantic harmony computation failed: {e}")

    def refine_model(self, new_data: Tuple[str, str]) -> None:
        """
        Refines the model using new data to adapt to unseen linguistic patterns.

        Args:
            new_data (Tuple[str, str]): A tuple containing input and output text.
                                         Example: (input_text, output_text)
        """
        try:
            logger.info("Refining model with new data.")
            # Placeholder for self-adjusting statistical techniques
            # This could involve fine-tuning the model on new data
            # or updating internal metrics based on new observations.
            # Implementation depends on specific requirements and data.

            # Example: Fine-tuning on new data (simple demonstration)
            input_text, output_text = new_data
            inputs = self.tokenizer(input_text, return_tensors="pt")
            labels = self.tokenizer(output_text, return_tensors="pt")["input_ids"]

            if torch.cuda.is_available():
                inputs = {k: v.to('cuda') for k, v in inputs.items()}
                labels = labels.to('cuda')

            self.model.train()
            outputs = self.model(**inputs, labels=labels)
            loss = outputs.loss
            loss.backward()
            # Optimizer step would be here, omitted for brevity
            self.model.eval()
            logger.info("Model refinement successful.")
        except Exception as e:
            logger.error(f"Error refining model: {e}")
            raise RuntimeError(f"Model refinement failed: {e}")


# Example usage:
if __name__ == "__main__":
    try:
        model = SemanticHarmonyModel(model_name="gpt2")
        input_str = "Once upon a time in a land far, far away"
        generated_str = model.generate_text(input_str, max_length=30)
        score = model.compute_semantic_harmony(input_str, generated_str)
        logger.info(f"Input Text: {input_str}")
        logger.info(f"Generated Text: {generated_str}")
        logger.info(f"Semantic Harmony Score: {score:.4f}")
    except Exception as error:
        logger.error(f"An error occurred during processing: {error}")