Create quantum_bayes_mahalanobis.py
Browse files-------------------------------------------------------------------------------
Notas de la versión refinada:
1) Se agregaron validaciones de dimensión para evitar errores silenciosos.
2) Se incorporó manejo de excepción cuando la matriz de covarianza no es invertible,
recurriendo a la pseudo-inversa.
3) Se incluyeron docstrings en español más descriptivos para cada clase y método.
4) Se agregó un argumento learning_rate al método optimize_quantum_state para mayor flexibilidad.
5) Se salió del bloque de gradient y se volvió a evaluar la función objetivo
para comparar si el valor mejoró y así actualizar el mejor estado.
6) El registro de la distancia de Mahalanobis se realiza cuando sea necesario (e.g. en la función objetivo),
evitando duplicar demasiadas llamadas si no se requiere.
7) El método simulate_wave_collapse mantiene la estructura original pero con más comentarios y
simplificaciones de nombres de variables.
8) Se agrega una funcion de entropia que incluye el metodo de Von Neumann para una dimendion densa cuanticamente correcta
- quantum_bayes_mahalanobis.py +503 -0
|
@@ -0,0 +1,503 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import tensorflow as tf
|
| 3 |
+
import tensorflow_probability as tfp
|
| 4 |
+
from scipy.spatial.distance import mahalanobis
|
| 5 |
+
from sklearn.covariance import EmpiricalCovariance
|
| 6 |
+
|
| 7 |
+
# Ajusta el import a tus necesidades reales:
|
| 8 |
+
# Se asume que en bayes_logic.py se tienen las clases BayesLogic, PRN, además de las
|
| 9 |
+
# funciones shannon_entropy, calculate_cosines, etc.
|
| 10 |
+
from bayes_logic import (
|
| 11 |
+
BayesLogic,
|
| 12 |
+
PRN,
|
| 13 |
+
shannon_entropy,
|
| 14 |
+
calculate_cosines
|
| 15 |
+
)
|
| 16 |
+
|
| 17 |
+
class QuantumBayesMahalanobis(BayesLogic):
|
| 18 |
+
"""
|
| 19 |
+
Clase que combina la lógica de Bayes con el cálculo de la distancia de Mahalanobis
|
| 20 |
+
aplicada a estados cuánticos, permitiendo proyecciones vectorizadas e inferencias
|
| 21 |
+
de coherencia/entropía.
|
| 22 |
+
"""
|
| 23 |
+
def __init__(self):
|
| 24 |
+
"""
|
| 25 |
+
Constructor que inicializa el estimador de covarianza para su posterior uso.
|
| 26 |
+
"""
|
| 27 |
+
super().__init__()
|
| 28 |
+
self.covariance_estimator = EmpiricalCovariance()
|
| 29 |
+
|
| 30 |
+
def _get_inverse_covariance(self, data: np.ndarray) -> np.ndarray:
|
| 31 |
+
"""
|
| 32 |
+
Ajusta el estimador de covarianza con los datos y retorna la inversa de la
|
| 33 |
+
matriz de covarianza. Si la matriz no fuera invertible, se retorna la
|
| 34 |
+
pseudo-inversa (pinv).
|
| 35 |
+
|
| 36 |
+
Parámetros:
|
| 37 |
+
-----------
|
| 38 |
+
data: np.ndarray
|
| 39 |
+
Datos con forma (n_muestras, n_dimensiones).
|
| 40 |
+
|
| 41 |
+
Retorna:
|
| 42 |
+
--------
|
| 43 |
+
inv_cov_matrix: np.ndarray
|
| 44 |
+
Inversa o pseudo-inversa de la matriz de covarianza estimada.
|
| 45 |
+
"""
|
| 46 |
+
if data.ndim != 2:
|
| 47 |
+
raise ValueError("Los datos deben ser una matriz bidimensional (n_muestras, n_dimensiones).")
|
| 48 |
+
self.covariance_estimator.fit(data)
|
| 49 |
+
cov_matrix = self.covariance_estimator.covariance_
|
| 50 |
+
try:
|
| 51 |
+
inv_cov_matrix = np.linalg.inv(cov_matrix)
|
| 52 |
+
except np.linalg.LinAlgError:
|
| 53 |
+
inv_cov_matrix = np.linalg.pinv(cov_matrix)
|
| 54 |
+
return inv_cov_matrix
|
| 55 |
+
|
| 56 |
+
def compute_quantum_mahalanobis(self,
|
| 57 |
+
quantum_states_A: np.ndarray,
|
| 58 |
+
quantum_states_B: np.ndarray) -> np.ndarray:
|
| 59 |
+
"""
|
| 60 |
+
Calcula la distancia de Mahalanobis para cada estado en 'quantum_states_B'
|
| 61 |
+
respecto a la distribución de 'quantum_states_A'. Retorna un arreglo 1D
|
| 62 |
+
con tantas distancias como filas tenga 'quantum_states_B'.
|
| 63 |
+
|
| 64 |
+
Parámetros:
|
| 65 |
+
-----------
|
| 66 |
+
quantum_states_A: np.ndarray
|
| 67 |
+
Representa el conjunto de estados cuánticos de referencia.
|
| 68 |
+
Forma esperada: (n_muestras, n_dimensiones).
|
| 69 |
+
|
| 70 |
+
quantum_states_B: np.ndarray
|
| 71 |
+
Estados cuánticos para los que calcularemos la distancia
|
| 72 |
+
de Mahalanobis. Forma: (n_muestras, n_dimensiones).
|
| 73 |
+
|
| 74 |
+
Retorna:
|
| 75 |
+
--------
|
| 76 |
+
distances: np.ndarray
|
| 77 |
+
Distancias de Mahalanobis calculadas para cada entrada de B.
|
| 78 |
+
"""
|
| 79 |
+
if quantum_states_A.ndim != 2 or quantum_states_B.ndim != 2:
|
| 80 |
+
raise ValueError("Los estados cuánticos deben ser matrices bidimensionales.")
|
| 81 |
+
if quantum_states_A.shape[1] != quantum_states_B.shape[1]:
|
| 82 |
+
raise ValueError("La dimensión (n_dimensiones) de A y B debe coincidir.")
|
| 83 |
+
|
| 84 |
+
inv_cov_matrix = self._get_inverse_covariance(quantum_states_A)
|
| 85 |
+
mean_A = np.mean(quantum_states_A, axis=0)
|
| 86 |
+
|
| 87 |
+
diff_B = quantum_states_B - mean_A # (n_samples_B, n_dims)
|
| 88 |
+
aux = diff_B @ inv_cov_matrix # (n_samples_B, n_dims)
|
| 89 |
+
dist_sqr = np.einsum('ij,ij->i', aux, diff_B) # Producto elemento a elemento y sumatoria por fila
|
| 90 |
+
distances = np.sqrt(dist_sqr)
|
| 91 |
+
return distances
|
| 92 |
+
|
| 93 |
+
def quantum_cosine_projection(self,
|
| 94 |
+
quantum_states: np.ndarray,
|
| 95 |
+
entropy: float,
|
| 96 |
+
coherence: float) -> tf.Tensor:
|
| 97 |
+
"""
|
| 98 |
+
Proyecta los estados cuánticos usando cosenos directores y calcula la
|
| 99 |
+
distancia de Mahalanobis entre dos proyecciones vectorizadas (A y B).
|
| 100 |
+
Finalmente retorna las distancias normalizadas (softmax).
|
| 101 |
+
|
| 102 |
+
Parámetros:
|
| 103 |
+
-----------
|
| 104 |
+
quantum_states: np.ndarray
|
| 105 |
+
Estados cuánticos de entrada con forma (n_muestras, 2).
|
| 106 |
+
entropy: float
|
| 107 |
+
Entropía del sistema a usar en la función calculate_cosines.
|
| 108 |
+
coherence: float
|
| 109 |
+
Coherencia del sistema a usar en la función calculate_cosines.
|
| 110 |
+
|
| 111 |
+
Retorna:
|
| 112 |
+
--------
|
| 113 |
+
normalized_distances: tf.Tensor
|
| 114 |
+
Tensor 1D con las distancias normalizadas (softmax).
|
| 115 |
+
"""
|
| 116 |
+
if quantum_states.shape[1] != 2:
|
| 117 |
+
raise ValueError("Se espera que 'quantum_states' tenga exactamente 2 columnas.")
|
| 118 |
+
cos_x, cos_y, cos_z = calculate_cosines(entropy, coherence)
|
| 119 |
+
|
| 120 |
+
# Proyección A: multiplicar cada columna por (cos_x, cos_y)
|
| 121 |
+
projected_states_A = quantum_states * np.array([cos_x, cos_y])
|
| 122 |
+
# Proyección B: multiplicar cada columna por (cos_x*cos_z, cos_y*cos_z)
|
| 123 |
+
projected_states_B = quantum_states * np.array([cos_x * cos_z, cos_y * cos_z])
|
| 124 |
+
|
| 125 |
+
# Calcular distancias de Mahalanobis vectorizadas
|
| 126 |
+
mahalanobis_distances = self.compute_quantum_mahalanobis(
|
| 127 |
+
projected_states_A,
|
| 128 |
+
projected_states_B
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
# Convertir a tensor y normalizar con softmax
|
| 132 |
+
mahalanobis_distances_tf = tf.convert_to_tensor(mahalanobis_distances, dtype=tf.float32)
|
| 133 |
+
normalized_distances = tf.nn.softmax(mahalanobis_distances_tf)
|
| 134 |
+
return normalized_distances
|
| 135 |
+
|
| 136 |
+
def calculate_quantum_posterior_with_mahalanobis(self,
|
| 137 |
+
quantum_states: np.ndarray,
|
| 138 |
+
entropy: float,
|
| 139 |
+
coherence: float):
|
| 140 |
+
"""
|
| 141 |
+
Calcula la probabilidad posterior usando la distancia de Mahalanobis
|
| 142 |
+
en proyecciones cuánticas e integra la lógica de Bayes.
|
| 143 |
+
|
| 144 |
+
Parámetros:
|
| 145 |
+
-----------
|
| 146 |
+
quantum_states: np.ndarray
|
| 147 |
+
Matriz de estados cuánticos (n_muestras, 2).
|
| 148 |
+
entropy: float
|
| 149 |
+
Entropía del sistema.
|
| 150 |
+
coherence: float
|
| 151 |
+
Coherencia del sistema.
|
| 152 |
+
|
| 153 |
+
Retorna:
|
| 154 |
+
--------
|
| 155 |
+
posterior: tf.Tensor
|
| 156 |
+
Probabilidad posterior calculada combinando la lógica bayesiana.
|
| 157 |
+
quantum_projections: tf.Tensor
|
| 158 |
+
Proyecciones cuánticas normalizadas (distancias softmax).
|
| 159 |
+
"""
|
| 160 |
+
quantum_projections = self.quantum_cosine_projection(
|
| 161 |
+
quantum_states,
|
| 162 |
+
entropy,
|
| 163 |
+
coherence
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
# Calcular covarianza en las proyecciones
|
| 167 |
+
tensor_projections = tf.convert_to_tensor(quantum_projections, dtype=tf.float32)
|
| 168 |
+
quantum_covariance = tfp.stats.covariance(tensor_projections, sample_axis=0)
|
| 169 |
+
|
| 170 |
+
# Calcular prior cuántico basado en la traza de la covarianza
|
| 171 |
+
dim = tf.cast(tf.shape(quantum_covariance)[0], tf.float32)
|
| 172 |
+
quantum_prior = tf.linalg.trace(quantum_covariance) / dim
|
| 173 |
+
|
| 174 |
+
# Calcular otros componentes para la posteriori (usando métodos heredados de BayesLogic).
|
| 175 |
+
prior_coherence = self.calculate_high_coherence_prior(coherence)
|
| 176 |
+
joint_prob = self.calculate_joint_probability(
|
| 177 |
+
coherence,
|
| 178 |
+
1, # variable arbitraria: "evento" = 1
|
| 179 |
+
tf.reduce_mean(tensor_projections)
|
| 180 |
+
)
|
| 181 |
+
cond_prob = self.calculate_conditional_probability(joint_prob, quantum_prior)
|
| 182 |
+
posterior = self.calculate_posterior_probability(quantum_prior,
|
| 183 |
+
prior_coherence,
|
| 184 |
+
cond_prob)
|
| 185 |
+
return posterior, quantum_projections
|
| 186 |
+
|
| 187 |
+
def predict_quantum_state(self,
|
| 188 |
+
quantum_states: np.ndarray,
|
| 189 |
+
entropy: float,
|
| 190 |
+
coherence: float):
|
| 191 |
+
"""
|
| 192 |
+
Predice el siguiente estado cuántico con base en la proyección y la distancia
|
| 193 |
+
de Mahalanobis, generando un "estado futuro".
|
| 194 |
+
|
| 195 |
+
Parámetros:
|
| 196 |
+
-----------
|
| 197 |
+
quantum_states: np.ndarray
|
| 198 |
+
Estados cuánticos de entrada (n_muestras, 2).
|
| 199 |
+
entropy: float
|
| 200 |
+
Entropía del sistema.
|
| 201 |
+
coherence: float
|
| 202 |
+
Coherencia del sistema.
|
| 203 |
+
|
| 204 |
+
Retorna:
|
| 205 |
+
--------
|
| 206 |
+
next_state_prediction: tf.Tensor
|
| 207 |
+
Predicción del siguiente estado cuántico.
|
| 208 |
+
posterior: tf.Tensor
|
| 209 |
+
Probabilidad posterior que se usó en la predicción.
|
| 210 |
+
"""
|
| 211 |
+
posterior, projections = self.calculate_quantum_posterior_with_mahalanobis(
|
| 212 |
+
quantum_states,
|
| 213 |
+
entropy,
|
| 214 |
+
coherence
|
| 215 |
+
)
|
| 216 |
+
|
| 217 |
+
# Generar un estado futuro ponderado por la posterior
|
| 218 |
+
# Posterior es escalar, mientras que projections es un vector
|
| 219 |
+
next_state_prediction = tf.reduce_sum(
|
| 220 |
+
tf.multiply(projections, tf.expand_dims(posterior, -1)),
|
| 221 |
+
axis=0
|
| 222 |
+
)
|
| 223 |
+
return next_state_prediction, posterior
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
class EnhancedPRN(PRN):
|
| 227 |
+
"""
|
| 228 |
+
Extiende la clase PRN para registrar distancias de Mahalanobis y con ello
|
| 229 |
+
definir un 'ruido cuántico' adicional en el sistema.
|
| 230 |
+
"""
|
| 231 |
+
def __init__(self, influence: float = 0.5, algorithm_type: str = None, **parameters):
|
| 232 |
+
"""
|
| 233 |
+
Constructor que permite definir la influencia y el tipo de algoritmo,
|
| 234 |
+
además de inicializar una lista para conservar registros promedio de
|
| 235 |
+
distancias de Mahalanobis.
|
| 236 |
+
"""
|
| 237 |
+
super().__init__(influence, algorithm_type, **parameters)
|
| 238 |
+
self.mahalanobis_records = []
|
| 239 |
+
|
| 240 |
+
def record_quantum_noise(self, probabilities: dict, quantum_states: np.ndarray):
|
| 241 |
+
"""
|
| 242 |
+
Registra un 'ruido cuántico' basado en la distancia de Mahalanobis
|
| 243 |
+
calculada para los estados cuánticos.
|
| 244 |
+
|
| 245 |
+
Parámetros:
|
| 246 |
+
-----------
|
| 247 |
+
probabilities: dict
|
| 248 |
+
Diccionario de probabilidades (ej. {"0": p_0, "1": p_1, ...}).
|
| 249 |
+
quantum_states: np.ndarray
|
| 250 |
+
Estados cuánticos (n_muestras, n_dimensiones).
|
| 251 |
+
|
| 252 |
+
Retorna:
|
| 253 |
+
--------
|
| 254 |
+
(entropy, mahal_mean): Tuple[float, float]
|
| 255 |
+
- Entropía calculada a partir de probabilities.
|
| 256 |
+
- Distancia promedio de Mahalanobis.
|
| 257 |
+
"""
|
| 258 |
+
# Calculamos la entropía (este método se asume en la clase base PRN o BayesLogic).
|
| 259 |
+
entropy = self.record_noise(probabilities)
|
| 260 |
+
|
| 261 |
+
# Ajuste del estimador de covarianza
|
| 262 |
+
cov_estimator = EmpiricalCovariance().fit(quantum_states)
|
| 263 |
+
mean_state = np.mean(quantum_states, axis=0)
|
| 264 |
+
inv_cov = np.linalg.pinv(cov_estimator.covariance_)
|
| 265 |
+
|
| 266 |
+
# Cálculo vectorizado de la distancia
|
| 267 |
+
diff = quantum_states - mean_state
|
| 268 |
+
aux = diff @ inv_cov
|
| 269 |
+
dist_sqr = np.einsum('ij,ij->i', aux, diff)
|
| 270 |
+
distances = np.sqrt(dist_sqr)
|
| 271 |
+
mahal_mean = np.mean(distances)
|
| 272 |
+
|
| 273 |
+
def von_neumann_entropy(density_matrix: np.ndarray) -> float:
|
| 274 |
+
"""Calcula la entropía de von Neumann para una matriz de densidad."""
|
| 275 |
+
# Se calculan los valores propios (eigenvalues)
|
| 276 |
+
eigenvalues = np.linalg.eigvalsh(density_matrix)
|
| 277 |
+
# Se filtran los valores propios que son cero o negativos para evitar errores en el logaritmo
|
| 278 |
+
non_zero_eigenvalues = eigenvalues[eigenvalues > 0]
|
| 279 |
+
# Se calcula la entropía: S = -Tr(ρ log(ρ)) = -Σ λ_i log(λ_i)
|
| 280 |
+
entropy = -np.sum(non_zero_eigenvalues * np.log(non_zero_eigenvalues))
|
| 281 |
+
# Se registra la distancia promedio
|
| 282 |
+
self.mahalanobis_records.append(mahal_mean)
|
| 283 |
+
|
| 284 |
+
return entropy, mahal_mean
|
| 285 |
+
|
| 286 |
+
class QuantumNoiseCollapse(QuantumBayesMahalanobis):
|
| 287 |
+
"""
|
| 288 |
+
Combina la lógica bayesiana cuántica (QuantumBayesMahalanobis) y el registro ExtendedPRN
|
| 289 |
+
para simular el 'colapso de onda' usando distancias de Mahalanobis como parte del ruido.
|
| 290 |
+
"""
|
| 291 |
+
def __init__(self, prn_influence: float = 0.5):
|
| 292 |
+
"""
|
| 293 |
+
Constructor que crea internamente un EnhancedPRN por defecto, con una
|
| 294 |
+
influencia configurable.
|
| 295 |
+
"""
|
| 296 |
+
super().__init__()
|
| 297 |
+
self.prn = EnhancedPRN(influence=prn_influence)
|
| 298 |
+
|
| 299 |
+
def simulate_wave_collapse(self,
|
| 300 |
+
quantum_states: np.ndarray,
|
| 301 |
+
prn_influence: float,
|
| 302 |
+
previous_action: int):
|
| 303 |
+
"""
|
| 304 |
+
Simula el colapso de onda incorporando ruido cuántico (a través de PRN) e
|
| 305 |
+
integra el resultado para determinar una acción bayesiana.
|
| 306 |
+
|
| 307 |
+
Parámetros:
|
| 308 |
+
-----------
|
| 309 |
+
quantum_states: np.ndarray
|
| 310 |
+
Estados cuánticos de entrada.
|
| 311 |
+
prn_influence: float
|
| 312 |
+
Influencia del PRN en el sistema (se puede alinear con self.prn.influence).
|
| 313 |
+
previous_action: int
|
| 314 |
+
Acción previa del sistema que se utiliza como condicionante.
|
| 315 |
+
|
| 316 |
+
Retorna:
|
| 317 |
+
--------
|
| 318 |
+
dict con llaves:
|
| 319 |
+
"collapsed_state": tf.Tensor
|
| 320 |
+
Representación final colapsada del estado.
|
| 321 |
+
"action": int
|
| 322 |
+
Acción tomada según lógica bayesiana.
|
| 323 |
+
"entropy": float
|
| 324 |
+
Entropía calculada.
|
| 325 |
+
"coherence": float
|
| 326 |
+
Coherencia derivada.
|
| 327 |
+
"mahalanobis_distance": float
|
| 328 |
+
Distancia promedio de Mahalanobis.
|
| 329 |
+
"cosines": Tuple[float, float, float]
|
| 330 |
+
Valores de (cos_x, cos_y, cos_z) usados en la proyección.
|
| 331 |
+
"""
|
| 332 |
+
# Diccionario de probabilidades a modo de ejemplo
|
| 333 |
+
probabilities = {str(i): np.sum(state) for i, state in enumerate(quantum_states)}
|
| 334 |
+
|
| 335 |
+
# Registro de entropía y distancia de Mahalanobis
|
| 336 |
+
entropy, mahalanobis_mean = self.prn.record_quantum_noise(probabilities, quantum_states)
|
| 337 |
+
|
| 338 |
+
# Cálculo de los cosenos directores como ejemplo de proyección
|
| 339 |
+
cos_x, cos_y, cos_z = calculate_cosines(entropy, mahalanobis_mean)
|
| 340 |
+
|
| 341 |
+
# Definimos coherencia a partir de la distancia de Mahalanobis y los cosenos
|
| 342 |
+
coherence = np.exp(-mahalanobis_mean) * (cos_x + cos_y + cos_z) / 3.0
|
| 343 |
+
|
| 344 |
+
# Llamada a un método de BayesLogic para decidir la acción
|
| 345 |
+
bayes_probs = self.calculate_probabilities_and_select_action(
|
| 346 |
+
entropy=entropy,
|
| 347 |
+
coherence=coherence,
|
| 348 |
+
prn_influence=prn_influence,
|
| 349 |
+
action=previous_action
|
| 350 |
+
)
|
| 351 |
+
|
| 352 |
+
# Proyectar estados cuánticos
|
| 353 |
+
projected_states = self.quantum_cosine_projection(
|
| 354 |
+
quantum_states,
|
| 355 |
+
entropy,
|
| 356 |
+
coherence
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
# Ejemplo de 'colapso' multiplicando la proyección por la acción que se toma
|
| 360 |
+
collapsed_state = tf.reduce_sum(
|
| 361 |
+
tf.multiply(
|
| 362 |
+
projected_states,
|
| 363 |
+
tf.cast(bayes_probs["action_to_take"], tf.float32)
|
| 364 |
+
)
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
return {
|
| 368 |
+
"collapsed_state": collapsed_state,
|
| 369 |
+
"action": bayes_probs["action_to_take"],
|
| 370 |
+
"entropy": entropy,
|
| 371 |
+
"coherence": coherence,
|
| 372 |
+
"mahalanobis_distance": mahalanobis_mean,
|
| 373 |
+
"cosines": (cos_x, cos_y, cos_z)
|
| 374 |
+
}
|
| 375 |
+
|
| 376 |
+
def objective_function_with_noise(self,
|
| 377 |
+
quantum_states: np.ndarray,
|
| 378 |
+
target_state: np.ndarray,
|
| 379 |
+
entropy_weight: float = 1.0) -> tf.Tensor:
|
| 380 |
+
"""
|
| 381 |
+
Función objetivo que combina fidelidad, entropía y distancia de Mahalanobis
|
| 382 |
+
para encontrar un compromiso entre mantener la fidelidad al estado objetivo
|
| 383 |
+
y el ruido cuántico en el sistema.
|
| 384 |
+
|
| 385 |
+
Parámetros:
|
| 386 |
+
-----------
|
| 387 |
+
quantum_states: np.ndarray
|
| 388 |
+
Estados cuánticos actuales (n_muestras, n_dimensiones).
|
| 389 |
+
target_state: np.ndarray
|
| 390 |
+
Estado objetivo que se desea alcanzar.
|
| 391 |
+
entropy_weight: float
|
| 392 |
+
Factor que pondera la influencia de la entropía en la función objetivo.
|
| 393 |
+
|
| 394 |
+
Retorna:
|
| 395 |
+
--------
|
| 396 |
+
objective_value: tf.Tensor
|
| 397 |
+
Valor de la función objetivo (cuanto menor, mejor).
|
| 398 |
+
"""
|
| 399 |
+
# Calcular fidelidad (simple ejemplo): |<ψ|φ>|^2
|
| 400 |
+
# Suponiendo que (quantum_states y target_state) sean vectores compatibles
|
| 401 |
+
fidelity = tf.abs(tf.reduce_sum(quantum_states * tf.cast(target_state, quantum_states.dtype)))**2
|
| 402 |
+
|
| 403 |
+
# Registrar 'ruido': entropía y distancia de Mahalanobis
|
| 404 |
+
probabilities = {str(i): np.sum(st) for i, st in enumerate(quantum_states)}
|
| 405 |
+
entropy, mahalanobis_dist = self.prn.record_quantum_noise(probabilities, quantum_states)
|
| 406 |
+
|
| 407 |
+
# Combinar métricas: (1 - fidelidad) + factor * entropía + penalización por distancia
|
| 408 |
+
objective_value = ((1.0 - fidelity)
|
| 409 |
+
+ entropy_weight * entropy
|
| 410 |
+
+ (1.0 - np.exp(-mahalanobis_dist)))
|
| 411 |
+
|
| 412 |
+
return objective_value
|
| 413 |
+
|
| 414 |
+
def optimize_quantum_state(self,
|
| 415 |
+
initial_states: np.ndarray,
|
| 416 |
+
target_state: np.ndarray,
|
| 417 |
+
max_iterations: int = 100,
|
| 418 |
+
learning_rate: float = 0.01):
|
| 419 |
+
"""
|
| 420 |
+
Optimiza los estados cuánticos para acercarlos al estado objetivo,
|
| 421 |
+
mediante un descenso de gradiente (Adam).
|
| 422 |
+
|
| 423 |
+
Parámetros:
|
| 424 |
+
-----------
|
| 425 |
+
initial_states: np.ndarray
|
| 426 |
+
Estados cuánticos iniciales.
|
| 427 |
+
target_state: np.ndarray
|
| 428 |
+
Estado objetivo.
|
| 429 |
+
max_iterations: int
|
| 430 |
+
Número máximo de iteraciones de optimización.
|
| 431 |
+
learning_rate: float
|
| 432 |
+
Tasa de aprendizaje para Adam.
|
| 433 |
+
|
| 434 |
+
Retorna:
|
| 435 |
+
--------
|
| 436 |
+
best_states: np.ndarray
|
| 437 |
+
Estados optimizados que reportan el menor valor de la función objetivo.
|
| 438 |
+
best_objective: float
|
| 439 |
+
Valor final alcanzado por la función objetivo.
|
| 440 |
+
"""
|
| 441 |
+
# Convertir a tf.Variable para permitir gradientes
|
| 442 |
+
current_states = tf.Variable(initial_states, dtype=tf.float32)
|
| 443 |
+
|
| 444 |
+
best_objective = float('inf')
|
| 445 |
+
best_states = current_states.numpy().copy()
|
| 446 |
+
|
| 447 |
+
optimizer = tf.keras.optimizers.Adam(learning_rate=learning_rate)
|
| 448 |
+
|
| 449 |
+
for _ in range(max_iterations):
|
| 450 |
+
with tf.GradientTape() as tape:
|
| 451 |
+
# Usar numpy() en la llamada para separar lógicamente la parte TF de la parte numpy
|
| 452 |
+
objective = self.objective_function_with_noise(current_states.numpy(), target_state)
|
| 453 |
+
grads = tape.gradient(objective, [current_states])
|
| 454 |
+
|
| 455 |
+
if grads[0] is None:
|
| 456 |
+
# Si no hay gradiente, rompe el bucle
|
| 457 |
+
break
|
| 458 |
+
|
| 459 |
+
optimizer.apply_gradients(zip(grads, [current_states]))
|
| 460 |
+
|
| 461 |
+
# Re-evaluar después de actualizar los parámetros
|
| 462 |
+
new_objective = self.objective_function_with_noise(current_states.numpy(), target_state)
|
| 463 |
+
if new_objective < best_objective:
|
| 464 |
+
best_objective = new_objective
|
| 465 |
+
best_states = current_states.numpy().copy()
|
| 466 |
+
|
| 467 |
+
return best_states, best_objective
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
# ====================
|
| 471 |
+
# Ejemplo de uso
|
| 472 |
+
# ====================
|
| 473 |
+
if __name__ == "__main__":
|
| 474 |
+
qnc = QuantumNoiseCollapse()
|
| 475 |
+
|
| 476 |
+
# Estados cuánticos iniciales
|
| 477 |
+
initial_states = np.array([
|
| 478 |
+
[0.8, 0.2],
|
| 479 |
+
[0.9, 0.4],
|
| 480 |
+
[0.1, 0.7]
|
| 481 |
+
])
|
| 482 |
+
|
| 483 |
+
# Estado objetivo
|
| 484 |
+
target_state = np.array([1.0, 0.0])
|
| 485 |
+
|
| 486 |
+
# Optimizar estados
|
| 487 |
+
optimized_states, final_objective = qnc.optimize_quantum_state(
|
| 488 |
+
initial_states,
|
| 489 |
+
target_state,
|
| 490 |
+
max_iterations=100,
|
| 491 |
+
learning_rate=0.01
|
| 492 |
+
)
|
| 493 |
+
|
| 494 |
+
# Simular colapso final con la acción previa (ej. 0)
|
| 495 |
+
final_collapse = qnc.simulate_wave_collapse(
|
| 496 |
+
optimized_states,
|
| 497 |
+
prn_influence=0.5,
|
| 498 |
+
previous_action=0
|
| 499 |
+
)
|
| 500 |
+
|
| 501 |
+
print("Estados optimizados:", optimized_states)
|
| 502 |
+
print("Valor final de la función objetivo:", final_objective)
|
| 503 |
+
print("Resultado del colapso final:", final_collapse)
|