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