Upload 2 files
Browse files# Análisis de Características del Sistema
Ambos archivos, enhanced_ml_features.ts y time_dependent_lindblad.py, son representaciones de sistemas físicos complejos que buscan modelar o predecir su comportamiento. Aunque están en lenguajes de programación diferentes (TypeScript y Python) y se aplican a dominios distintos (procesamiento de audio/señales y física cuántica), comparten la misma lógica de ingeniería: la extracción y preparación de características para una tarea de aprendizaje automático [cite: 2025-06-18].
enhanced_ml_features.ts se centra en la extracción de 32 características distintas de una señal de audio (amplitudes de onda de presión). Esto incluye medidas espectrales (centroide, contraste, energía por banda) y temporales (tasa de cruces por cero, RMS). El objetivo es transformar una señal de audio cruda en un vector de características numéricas que pueda ser fácilmente procesado por un modelo de ML para tareas como clasificación de sonido, reconocimiento de voz o detección de anomalías.
time_dependent_lindblad.py describe un sistema cuántico abierto que evoluciona con el tiempo. El estado del sistema se representa por una matriz de densidad, y su evolución se rige por la ecuación de Lindblad. Las "características" en este caso no se extraen directamente de datos, sino que son parámetros de entrada que describen el sistema, como el Hamiltoniano (H(t)) y los operadores de decoherencia (L
k
(t)), que a su vez dependen de parámetros físicos como la frecuencia de un pulso de láser o la tasa de decaimiento. Los "observables" (población, coherencia) son el resultado de la simulación, y son el tipo de datos que un modelo de ML podría usar para predecir o clasificar el comportamiento del sistema.
- enhanced_ml_features.ts +405 -0
- time_dependent_lindblad.py +472 -0
|
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Extractor mejorado de 32 características de amplitudes de onda de presión
|
| 2 |
+
export const FEATURE_VECTOR_SIZE = 32;
|
| 3 |
+
|
| 4 |
+
interface AdvancedFeatures {
|
| 5 |
+
spectralCentroid: number;
|
| 6 |
+
spectralRolloff: number;
|
| 7 |
+
spectralFlux: number;
|
| 8 |
+
zeroCrossingRate: number;
|
| 9 |
+
rms: number;
|
| 10 |
+
peak: number;
|
| 11 |
+
crest: number;
|
| 12 |
+
spectralSpread: number;
|
| 13 |
+
spectralFlatness: number;
|
| 14 |
+
spectralSlope: number;
|
| 15 |
+
harmonicRatio: number;
|
| 16 |
+
noiseRatio: number;
|
| 17 |
+
tonalPower: number;
|
| 18 |
+
spectralContrast: number[]; // 7 valores
|
| 19 |
+
spectralBandEnergy: number[]; // 8 valores
|
| 20 |
+
temporalFeatures: number[]; // 4 valores
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
// Función principal mejorada para extraer características ML
|
| 24 |
+
export const extractMLFeatures = (
|
| 25 |
+
magnitudes: number[],
|
| 26 |
+
rawData: Uint8Array,
|
| 27 |
+
previousAmplitudes: number[],
|
| 28 |
+
sampleRate: number
|
| 29 |
+
): number[] => {
|
| 30 |
+
const features: number[] = new Array(FEATURE_VECTOR_SIZE).fill(0);
|
| 31 |
+
|
| 32 |
+
try {
|
| 33 |
+
// Convertir datos raw a amplitudes normalizadas
|
| 34 |
+
const amplitudes = convertRawToAmplitudes(rawData);
|
| 35 |
+
|
| 36 |
+
// Extraer características avanzadas
|
| 37 |
+
const advancedFeatures = extractAdvancedFeatures(magnitudes, amplitudes, previousAmplitudes, sampleRate);
|
| 38 |
+
|
| 39 |
+
// Mapear a vector de 32 características
|
| 40 |
+
const featureVector = mapToFeatureVector(advancedFeatures);
|
| 41 |
+
|
| 42 |
+
// Copiar al array de salida
|
| 43 |
+
for (let i = 0; i < Math.min(FEATURE_VECTOR_SIZE, featureVector.length); i++) {
|
| 44 |
+
features[i] = featureVector[i];
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
return features;
|
| 48 |
+
} catch (error) {
|
| 49 |
+
console.warn('Error extracting ML features:', error);
|
| 50 |
+
// Fallback a extracción básica
|
| 51 |
+
return extractBasicFeatures(magnitudes, rawData, sampleRate);
|
| 52 |
+
}
|
| 53 |
+
};
|
| 54 |
+
|
| 55 |
+
// Convertir datos raw a amplitudes normalizadas
|
| 56 |
+
function convertRawToAmplitudes(rawData: Uint8Array): number[] {
|
| 57 |
+
const amplitudes: number[] = [];
|
| 58 |
+
|
| 59 |
+
// Convertir de Uint8 a valores signed y normalizar
|
| 60 |
+
for (let i = 0; i < rawData.length - 1; i += 2) {
|
| 61 |
+
// Combinar bytes para 16-bit sample
|
| 62 |
+
const sample = (rawData[i + 1] << 8) | rawData[i];
|
| 63 |
+
const signed = sample > 32767 ? sample - 65536 : sample;
|
| 64 |
+
amplitudes.push(signed / 32768.0); // Normalizar a [-1, 1]
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
return amplitudes;
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
// Extractor de características avanzadas
|
| 71 |
+
function extractAdvancedFeatures(
|
| 72 |
+
magnitudes: number[],
|
| 73 |
+
amplitudes: number[],
|
| 74 |
+
previousAmplitudes: number[],
|
| 75 |
+
sampleRate: number
|
| 76 |
+
): AdvancedFeatures {
|
| 77 |
+
|
| 78 |
+
const N = magnitudes.length;
|
| 79 |
+
const nyquist = sampleRate / 2;
|
| 80 |
+
|
| 81 |
+
// 1. Centroide espectral
|
| 82 |
+
const spectralCentroid = calculateSpectralCentroid(magnitudes, nyquist);
|
| 83 |
+
|
| 84 |
+
// 2. Rolloff espectral (85% de energía)
|
| 85 |
+
const spectralRolloff = calculateSpectralRolloff(magnitudes, nyquist, 0.85);
|
| 86 |
+
|
| 87 |
+
// 3. Flujo espectral
|
| 88 |
+
const spectralFlux = calculateSpectralFlux(magnitudes, previousAmplitudes);
|
| 89 |
+
|
| 90 |
+
// 4. Tasa de cruces por cero
|
| 91 |
+
const zeroCrossingRate = calculateZeroCrossingRate(amplitudes);
|
| 92 |
+
|
| 93 |
+
// 5. RMS (Root Mean Square)
|
| 94 |
+
const rms = calculateRMS(amplitudes);
|
| 95 |
+
|
| 96 |
+
// 6. Valor pico
|
| 97 |
+
const peak = Math.max(...amplitudes.map(Math.abs));
|
| 98 |
+
|
| 99 |
+
// 7. Factor de cresta
|
| 100 |
+
const crest = rms > 0 ? peak / rms : 0;
|
| 101 |
+
|
| 102 |
+
// 8. Dispersión espectral
|
| 103 |
+
const spectralSpread = calculateSpectralSpread(magnitudes, spectralCentroid, nyquist);
|
| 104 |
+
|
| 105 |
+
// 9. Planitud espectral
|
| 106 |
+
const spectralFlatness = calculateSpectralFlatness(magnitudes);
|
| 107 |
+
|
| 108 |
+
// 10. Pendiente espectral
|
| 109 |
+
const spectralSlope = calculateSpectralSlope(magnitudes, nyquist);
|
| 110 |
+
|
| 111 |
+
// 11-12. Ratio armónico y de ruido
|
| 112 |
+
const { harmonicRatio, noiseRatio } = calculateHarmonicNoiseRatio(magnitudes);
|
| 113 |
+
|
| 114 |
+
// 13. Potencia tonal
|
| 115 |
+
const tonalPower = calculateTonalPower(magnitudes);
|
| 116 |
+
|
| 117 |
+
// 14-20. Contraste espectral (7 bandas)
|
| 118 |
+
const spectralContrast = calculateSpectralContrast(magnitudes, 7);
|
| 119 |
+
|
| 120 |
+
// 21-28. Energía por bandas de frecuencia (8 bandas)
|
| 121 |
+
const spectralBandEnergy = calculateBandEnergy(magnitudes, 8);
|
| 122 |
+
|
| 123 |
+
// 29-32. Características temporales
|
| 124 |
+
const temporalFeatures = calculateTemporalFeatures(amplitudes, previousAmplitudes);
|
| 125 |
+
|
| 126 |
+
return {
|
| 127 |
+
spectralCentroid,
|
| 128 |
+
spectralRolloff,
|
| 129 |
+
spectralFlux,
|
| 130 |
+
zeroCrossingRate,
|
| 131 |
+
rms,
|
| 132 |
+
peak,
|
| 133 |
+
crest,
|
| 134 |
+
spectralSpread,
|
| 135 |
+
spectralFlatness,
|
| 136 |
+
spectralSlope,
|
| 137 |
+
harmonicRatio,
|
| 138 |
+
noiseRatio,
|
| 139 |
+
tonalPower,
|
| 140 |
+
spectralContrast,
|
| 141 |
+
spectralBandEnergy,
|
| 142 |
+
temporalFeatures
|
| 143 |
+
};
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
// Mapear características avanzadas a vector de 32 elementos
|
| 147 |
+
function mapToFeatureVector(features: AdvancedFeatures): number[] {
|
| 148 |
+
const vector: number[] = [];
|
| 149 |
+
|
| 150 |
+
// Características espectrales básicas (13 elementos)
|
| 151 |
+
vector.push(
|
| 152 |
+
features.spectralCentroid,
|
| 153 |
+
features.spectralRolloff,
|
| 154 |
+
features.spectralFlux,
|
| 155 |
+
features.zeroCrossingRate,
|
| 156 |
+
features.rms,
|
| 157 |
+
features.peak,
|
| 158 |
+
features.crest,
|
| 159 |
+
features.spectralSpread,
|
| 160 |
+
features.spectralFlatness,
|
| 161 |
+
features.spectralSlope,
|
| 162 |
+
features.harmonicRatio,
|
| 163 |
+
features.noiseRatio,
|
| 164 |
+
features.tonalPower
|
| 165 |
+
);
|
| 166 |
+
|
| 167 |
+
// Contraste espectral (7 elementos)
|
| 168 |
+
vector.push(...features.spectralContrast);
|
| 169 |
+
|
| 170 |
+
// Energía por bandas (8 elementos)
|
| 171 |
+
vector.push(...features.spectralBandEnergy);
|
| 172 |
+
|
| 173 |
+
// Características temporales (4 elementos)
|
| 174 |
+
vector.push(...features.temporalFeatures);
|
| 175 |
+
|
| 176 |
+
return vector.slice(0, 32); // Asegurar exactamente 32 elementos
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
// Funciones de cálculo específicas
|
| 180 |
+
|
| 181 |
+
function calculateSpectralCentroid(magnitudes: number[], nyquist: number): number {
|
| 182 |
+
let weightedSum = 0;
|
| 183 |
+
let magnitudeSum = 0;
|
| 184 |
+
|
| 185 |
+
for (let i = 0; i < magnitudes.length; i++) {
|
| 186 |
+
const freq = (i * nyquist) / magnitudes.length;
|
| 187 |
+
weightedSum += freq * magnitudes[i];
|
| 188 |
+
magnitudeSum += magnitudes[i];
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
return magnitudeSum > 0 ? weightedSum / magnitudeSum : 0;
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
function calculateSpectralRolloff(magnitudes: number[], nyquist: number, threshold: number): number {
|
| 195 |
+
const totalEnergy = magnitudes.reduce((sum, mag) => sum + mag * mag, 0);
|
| 196 |
+
const targetEnergy = totalEnergy * threshold;
|
| 197 |
+
|
| 198 |
+
let cumulativeEnergy = 0;
|
| 199 |
+
for (let i = 0; i < magnitudes.length; i++) {
|
| 200 |
+
cumulativeEnergy += magnitudes[i] * magnitudes[i];
|
| 201 |
+
if (cumulativeEnergy >= targetEnergy) {
|
| 202 |
+
return (i * nyquist) / magnitudes.length;
|
| 203 |
+
}
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
return nyquist;
|
| 207 |
+
}
|
| 208 |
+
|
| 209 |
+
function calculateSpectralFlux(current: number[], previous: number[]): number {
|
| 210 |
+
if (previous.length === 0) return 0;
|
| 211 |
+
|
| 212 |
+
let flux = 0;
|
| 213 |
+
const minLength = Math.min(current.length, previous.length);
|
| 214 |
+
|
| 215 |
+
for (let i = 0; i < minLength; i++) {
|
| 216 |
+
const diff = current[i] - previous[i];
|
| 217 |
+
if (diff > 0) flux += diff * diff;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
return Math.sqrt(flux / minLength);
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
function calculateZeroCrossingRate(amplitudes: number[]): number {
|
| 224 |
+
let crossings = 0;
|
| 225 |
+
|
| 226 |
+
for (let i = 1; i < amplitudes.length; i++) {
|
| 227 |
+
if ((amplitudes[i] >= 0) !== (amplitudes[i-1] >= 0)) {
|
| 228 |
+
crossings++;
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
|
| 232 |
+
return crossings / (amplitudes.length - 1);
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
function calculateRMS(amplitudes: number[]): number {
|
| 236 |
+
const sumSquares = amplitudes.reduce((sum, amp) => sum + amp * amp, 0);
|
| 237 |
+
return Math.sqrt(sumSquares / amplitudes.length);
|
| 238 |
+
}
|
| 239 |
+
|
| 240 |
+
function calculateSpectralSpread(magnitudes: number[], centroid: number, nyquist: number): number {
|
| 241 |
+
let weightedVariance = 0;
|
| 242 |
+
let magnitudeSum = 0;
|
| 243 |
+
|
| 244 |
+
for (let i = 0; i < magnitudes.length; i++) {
|
| 245 |
+
const freq = (i * nyquist) / magnitudes.length;
|
| 246 |
+
const deviation = freq - centroid;
|
| 247 |
+
weightedVariance += deviation * deviation * magnitudes[i];
|
| 248 |
+
magnitudeSum += magnitudes[i];
|
| 249 |
+
}
|
| 250 |
+
|
| 251 |
+
return magnitudeSum > 0 ? Math.sqrt(weightedVariance / magnitudeSum) : 0;
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
function calculateSpectralFlatness(magnitudes: number[]): number {
|
| 255 |
+
let geometricMean = 1;
|
| 256 |
+
let arithmeticMean = 0;
|
| 257 |
+
let count = 0;
|
| 258 |
+
|
| 259 |
+
for (const mag of magnitudes) {
|
| 260 |
+
if (mag > 0) {
|
| 261 |
+
geometricMean *= Math.pow(mag, 1 / magnitudes.length);
|
| 262 |
+
arithmeticMean += mag;
|
| 263 |
+
count++;
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
arithmeticMean /= count;
|
| 268 |
+
return arithmeticMean > 0 ? geometricMean / arithmeticMean : 0;
|
| 269 |
+
}
|
| 270 |
+
|
| 271 |
+
function calculateSpectralSlope(magnitudes: number[], nyquist: number): number {
|
| 272 |
+
let sumXY = 0, sumX = 0, sumY = 0, sumX2 = 0;
|
| 273 |
+
const n = magnitudes.length;
|
| 274 |
+
|
| 275 |
+
for (let i = 0; i < n; i++) {
|
| 276 |
+
const x = (i * nyquist) / n; // frecuencia
|
| 277 |
+
const y = magnitudes[i]; // magnitud
|
| 278 |
+
|
| 279 |
+
sumXY += x * y;
|
| 280 |
+
sumX += x;
|
| 281 |
+
sumY += y;
|
| 282 |
+
sumX2 += x * x;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
const denominator = n * sumX2 - sumX * sumX;
|
| 286 |
+
return denominator !== 0 ? (n * sumXY - sumX * sumY) / denominator : 0;
|
| 287 |
+
}
|
| 288 |
+
|
| 289 |
+
function calculateHarmonicNoiseRatio(magnitudes: number[]): { harmonicRatio: number, noiseRatio: number } {
|
| 290 |
+
// Simplificación: basado en picos vs valle promedio
|
| 291 |
+
const sortedMags = [...magnitudes].sort((a, b) => b - a);
|
| 292 |
+
const peakEnergy = sortedMags.slice(0, Math.floor(sortedMags.length * 0.1)).reduce((a, b) => a + b, 0);
|
| 293 |
+
const totalEnergy = magnitudes.reduce((a, b) => a + b, 0);
|
| 294 |
+
|
| 295 |
+
const harmonicRatio = totalEnergy > 0 ? peakEnergy / totalEnergy : 0;
|
| 296 |
+
const noiseRatio = 1 - harmonicRatio;
|
| 297 |
+
|
| 298 |
+
return { harmonicRatio, noiseRatio };
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
function calculateTonalPower(magnitudes: number[]): number {
|
| 302 |
+
// Potencia de componentes tonales vs total
|
| 303 |
+
let tonalPower = 0;
|
| 304 |
+
const threshold = Math.max(...magnitudes) * 0.1;
|
| 305 |
+
|
| 306 |
+
for (const mag of magnitudes) {
|
| 307 |
+
if (mag > threshold) {
|
| 308 |
+
tonalPower += mag * mag;
|
| 309 |
+
}
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
const totalPower = magnitudes.reduce((sum, mag) => sum + mag * mag, 0);
|
| 313 |
+
return totalPower > 0 ? tonalPower / totalPower : 0;
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
function calculateSpectralContrast(magnitudes: number[], numBands: number): number[] {
|
| 317 |
+
const bandSize = Math.floor(magnitudes.length / numBands);
|
| 318 |
+
const contrasts: number[] = [];
|
| 319 |
+
|
| 320 |
+
for (let band = 0; band < numBands; band++) {
|
| 321 |
+
const start = band * bandSize;
|
| 322 |
+
const end = Math.min(start + bandSize, magnitudes.length);
|
| 323 |
+
const bandMags = magnitudes.slice(start, end);
|
| 324 |
+
|
| 325 |
+
if (bandMags.length > 0) {
|
| 326 |
+
const sortedBand = [...bandMags].sort((a, b) => b - a);
|
| 327 |
+
const peakMean = sortedBand.slice(0, Math.max(1, Math.floor(sortedBand.length * 0.2)))
|
| 328 |
+
.reduce((a, b) => a + b, 0) / Math.max(1, Math.floor(sortedBand.length * 0.2));
|
| 329 |
+
const valleyMean = sortedBand.slice(Math.floor(sortedBand.length * 0.8))
|
| 330 |
+
.reduce((a, b) => a + b, 0) / Math.max(1, sortedBand.length - Math.floor(sortedBand.length * 0.8));
|
| 331 |
+
|
| 332 |
+
contrasts.push(valleyMean > 0 ? Math.log(peakMean / valleyMean) : 0);
|
| 333 |
+
} else {
|
| 334 |
+
contrasts.push(0);
|
| 335 |
+
}
|
| 336 |
+
}
|
| 337 |
+
|
| 338 |
+
return contrasts;
|
| 339 |
+
}
|
| 340 |
+
|
| 341 |
+
function calculateBandEnergy(magnitudes: number[], numBands: number): number[] {
|
| 342 |
+
const bandSize = Math.floor(magnitudes.length / numBands);
|
| 343 |
+
const energies: number[] = [];
|
| 344 |
+
|
| 345 |
+
for (let band = 0; band < numBands; band++) {
|
| 346 |
+
const start = band * bandSize;
|
| 347 |
+
const end = Math.min(start + bandSize, magnitudes.length);
|
| 348 |
+
|
| 349 |
+
let energy = 0;
|
| 350 |
+
for (let i = start; i < end; i++) {
|
| 351 |
+
energy += magnitudes[i] * magnitudes[i];
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
energies.push(energy / (end - start));
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
return energies;
|
| 358 |
+
}
|
| 359 |
+
|
| 360 |
+
function calculateTemporalFeatures(current: number[], previous: number[]): number[] {
|
| 361 |
+
const features: number[] = [];
|
| 362 |
+
|
| 363 |
+
// 1. Cambio de energía
|
| 364 |
+
const currentEnergy = current.reduce((sum, amp) => sum + amp * amp, 0);
|
| 365 |
+
const previousEnergy = previous.length > 0 ? previous.reduce((sum, amp) => sum + amp * amp, 0) : currentEnergy;
|
| 366 |
+
const energyChange = previousEnergy > 0 ? (currentEnergy - previousEnergy) / previousEnergy : 0;
|
| 367 |
+
features.push(energyChange);
|
| 368 |
+
|
| 369 |
+
// 2. Autocorrelación en lag=1
|
| 370 |
+
let autocorr = 0;
|
| 371 |
+
if (current.length > 1) {
|
| 372 |
+
for (let i = 1; i < current.length; i++) {
|
| 373 |
+
autocorr += current[i] * current[i-1];
|
| 374 |
+
}
|
| 375 |
+
autocorr /= (current.length - 1);
|
| 376 |
+
}
|
| 377 |
+
features.push(autocorr);
|
| 378 |
+
|
| 379 |
+
// 3. Varianza de amplitudes
|
| 380 |
+
const mean = current.reduce((a, b) => a + b, 0) / current.length;
|
| 381 |
+
const variance = current.reduce((sum, amp) => sum + (amp - mean) * (amp - mean), 0) / current.length;
|
| 382 |
+
features.push(variance);
|
| 383 |
+
|
| 384 |
+
// 4. Asimetría (skewness)
|
| 385 |
+
const std = Math.sqrt(variance);
|
| 386 |
+
let skewness = 0;
|
| 387 |
+
if (std > 0) {
|
| 388 |
+
skewness = current.reduce((sum, amp) => sum + Math.pow((amp - mean) / std, 3), 0) / current.length;
|
| 389 |
+
}
|
| 390 |
+
features.push(skewness);
|
| 391 |
+
|
| 392 |
+
return features;
|
| 393 |
+
}
|
| 394 |
+
|
| 395 |
+
// Función de fallback para extracción básica
|
| 396 |
+
function extractBasicFeatures(magnitudes: number[], rawData: Uint8Array, sampleRate: number): number[] {
|
| 397 |
+
const features: number[] = new Array(FEATURE_VECTOR_SIZE).fill(0);
|
| 398 |
+
|
| 399 |
+
// Usar magnitudes FFT básicas y rellenar
|
| 400 |
+
for (let i = 0; i < Math.min(FEATURE_VECTOR_SIZE, magnitudes.length); i++) {
|
| 401 |
+
features[i] = magnitudes[i];
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
return features;
|
| 405 |
+
}
|
|
@@ -0,0 +1,472 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
+
"""
|
| 3 |
+
Extensión del simulador Lindblad para manejar Hamiltonianos dependientes del tiempo.
|
| 4 |
+
Combina evolución coherente (Schrödinger) con procesos disipativos (Lindblad).
|
| 5 |
+
|
| 6 |
+
Estrategias implementadas:
|
| 7 |
+
1. Hamiltoniano parametrizado H(t, params)
|
| 8 |
+
2. Operadores de Lindblad también dependientes del tiempo L_k(t)
|
| 9 |
+
3. Integración adaptativa con dependencia temporal explícita
|
| 10 |
+
4. Ejemplos: pulsos láser, campos oscilantes, rampas adiabáticas
|
| 11 |
+
|
| 12 |
+
Autor: Extension del código base de Jacobo Tlacaelel Mina Rodriguez
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
import numpy as np
|
| 16 |
+
from scipy.integrate import solve_ivp
|
| 17 |
+
import matplotlib.pyplot as plt
|
| 18 |
+
from typing import Callable, List, Optional, Dict, Any
|
| 19 |
+
from dataclasses import dataclass
|
| 20 |
+
import logging
|
| 21 |
+
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
+
@dataclass
|
| 25 |
+
class TimeDependentSystemParameters:
|
| 26 |
+
"""Parámetros para sistemas con dependencia temporal."""
|
| 27 |
+
name: str
|
| 28 |
+
dimension: int
|
| 29 |
+
H_func: Callable[[float, Dict], np.ndarray] # H(t, params)
|
| 30 |
+
L_func: Callable[[float, Dict], List[np.ndarray]] # L_operators(t, params)
|
| 31 |
+
rho_0: np.ndarray
|
| 32 |
+
t_span: tuple
|
| 33 |
+
time_params: Dict[str, Any] # Parámetros para funciones temporales
|
| 34 |
+
observables: Dict[str, np.ndarray]
|
| 35 |
+
|
| 36 |
+
class TimeDependentLindladSimulator:
|
| 37 |
+
"""Simulador Lindblad con Hamiltoniano dependiente del tiempo."""
|
| 38 |
+
|
| 39 |
+
def __init__(self, config=None):
|
| 40 |
+
self.config = config
|
| 41 |
+
self.results = {}
|
| 42 |
+
|
| 43 |
+
def lindblad_rhs_time_dependent(self, t: float, rho_vec: np.ndarray,
|
| 44 |
+
H_func: Callable, L_func: Callable,
|
| 45 |
+
params: Dict) -> np.ndarray:
|
| 46 |
+
"""
|
| 47 |
+
Ecuación de Lindblad con dependencia temporal explícita.
|
| 48 |
+
|
| 49 |
+
drho/dt = -i[H(t), rho] + Σ_k (L_k(t) rho L_k†(t) - 1/2 {L_k†(t)L_k(t), rho})
|
| 50 |
+
"""
|
| 51 |
+
try:
|
| 52 |
+
n = int(np.sqrt(len(rho_vec)))
|
| 53 |
+
rho = rho_vec.reshape((n, n))
|
| 54 |
+
|
| 55 |
+
# Hamiltoniano en el tiempo t
|
| 56 |
+
H_t = H_func(t, params)
|
| 57 |
+
|
| 58 |
+
# Operadores de Lindblad en el tiempo t
|
| 59 |
+
L_operators_t = L_func(t, params)
|
| 60 |
+
|
| 61 |
+
# Término coherente: -i[H(t), rho]
|
| 62 |
+
drho_dt = -1j * (H_t @ rho - rho @ H_t)
|
| 63 |
+
|
| 64 |
+
# Términos disipativos
|
| 65 |
+
for L_t in L_operators_t:
|
| 66 |
+
L_dagger_t = L_t.conj().T
|
| 67 |
+
drho_dt += (L_t @ rho @ L_dagger_t -
|
| 68 |
+
0.5 * (L_dagger_t @ L_t @ rho + rho @ L_dagger_t @ L_t))
|
| 69 |
+
|
| 70 |
+
return drho_dt.flatten()
|
| 71 |
+
|
| 72 |
+
except Exception as e:
|
| 73 |
+
logger.error(f"Error en lindblad_rhs_time_dependent en t={t}: {e}")
|
| 74 |
+
raise
|
| 75 |
+
|
| 76 |
+
def simulate(self, params: TimeDependentSystemParameters,
|
| 77 |
+
t_eval: Optional[np.ndarray] = None) -> Dict:
|
| 78 |
+
"""Simula sistema con dependencia temporal."""
|
| 79 |
+
|
| 80 |
+
if t_eval is None:
|
| 81 |
+
t_eval = np.linspace(params.t_span[0], params.t_span[1], 200)
|
| 82 |
+
|
| 83 |
+
try:
|
| 84 |
+
sol = solve_ivp(
|
| 85 |
+
fun=self.lindblad_rhs_time_dependent,
|
| 86 |
+
t_span=params.t_span,
|
| 87 |
+
y0=params.rho_0.flatten(),
|
| 88 |
+
args=(params.H_func, params.L_func, params.time_params),
|
| 89 |
+
t_eval=t_eval,
|
| 90 |
+
rtol=1e-8,
|
| 91 |
+
atol=1e-10,
|
| 92 |
+
method='RK45'
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
if not sol.success:
|
| 96 |
+
raise RuntimeError(f"Integración falló: {sol.message}")
|
| 97 |
+
|
| 98 |
+
# Procesar resultados
|
| 99 |
+
rho_t = np.array([s.reshape((params.dimension, params.dimension))
|
| 100 |
+
for s in sol.y.T])
|
| 101 |
+
|
| 102 |
+
# Calcular observables
|
| 103 |
+
observables = {}
|
| 104 |
+
for obs_name, obs_op in params.observables.items():
|
| 105 |
+
observables[obs_name] = [np.trace(rho @ obs_op) for rho in rho_t]
|
| 106 |
+
|
| 107 |
+
# Métricas
|
| 108 |
+
traces = [np.trace(rho).real for rho in rho_t]
|
| 109 |
+
purities = [np.trace(rho @ rho).real for rho in rho_t]
|
| 110 |
+
|
| 111 |
+
results = {
|
| 112 |
+
'success': True,
|
| 113 |
+
'name': params.name,
|
| 114 |
+
'time': sol.t,
|
| 115 |
+
'rho_t': rho_t,
|
| 116 |
+
'observables': observables,
|
| 117 |
+
'traces': traces,
|
| 118 |
+
'purities': purities,
|
| 119 |
+
'H_evolution': [params.H_func(t, params.time_params) for t in sol.t],
|
| 120 |
+
'L_evolution': [params.L_func(t, params.time_params) for t in sol.t]
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
return results
|
| 124 |
+
|
| 125 |
+
except Exception as e:
|
| 126 |
+
logger.error(f"Error en simulación: {e}")
|
| 127 |
+
return {'success': False, 'error': str(e)}
|
| 128 |
+
|
| 129 |
+
# ============================================================================
|
| 130 |
+
# EJEMPLOS DE SISTEMAS CON DEPENDENCIA TEMPORAL
|
| 131 |
+
# ============================================================================
|
| 132 |
+
|
| 133 |
+
def create_driven_qubit_system():
|
| 134 |
+
"""
|
| 135 |
+
Qubit controlado por pulsos láser + decoherencia.
|
| 136 |
+
|
| 137 |
+
H(t) = ω₀/2 σz + Ω(t)/2 σx # Campo de control
|
| 138 |
+
L(t) = √γ(t) σ₋ # Decaimiento variable
|
| 139 |
+
"""
|
| 140 |
+
|
| 141 |
+
# Operadores base
|
| 142 |
+
sigma_z = np.array([[1, 0], [0, -1]], dtype=complex)
|
| 143 |
+
sigma_x = np.array([[0, 1], [1, 0]], dtype=complex)
|
| 144 |
+
sigma_minus = np.array([[0, 0], [1, 0]], dtype=complex)
|
| 145 |
+
|
| 146 |
+
def H_driven_qubit(t, params):
|
| 147 |
+
"""Hamiltoniano con pulso Gaussiano."""
|
| 148 |
+
omega_0 = params['omega_0']
|
| 149 |
+
omega_drive = params['omega_drive']
|
| 150 |
+
pulse_duration = params['pulse_duration']
|
| 151 |
+
pulse_center = params['pulse_center']
|
| 152 |
+
|
| 153 |
+
# Pulso Gaussiano
|
| 154 |
+
pulse_envelope = np.exp(-((t - pulse_center)/pulse_duration)**2)
|
| 155 |
+
Omega_t = omega_drive * pulse_envelope
|
| 156 |
+
|
| 157 |
+
return 0.5 * omega_0 * sigma_z + 0.5 * Omega_t * sigma_x
|
| 158 |
+
|
| 159 |
+
def L_driven_qubit(t, params):
|
| 160 |
+
"""Operadores de Lindblad con decay modulado."""
|
| 161 |
+
gamma_base = params['gamma_base']
|
| 162 |
+
|
| 163 |
+
# Ejemplo: decaimiento que aumenta durante el pulso
|
| 164 |
+
pulse_center = params['pulse_center']
|
| 165 |
+
pulse_duration = params['pulse_duration']
|
| 166 |
+
pulse_factor = 1 + 0.5 * np.exp(-((t - pulse_center)/pulse_duration)**2)
|
| 167 |
+
|
| 168 |
+
gamma_t = gamma_base * pulse_factor
|
| 169 |
+
return [np.sqrt(gamma_t) * sigma_minus]
|
| 170 |
+
|
| 171 |
+
# Parámetros temporales
|
| 172 |
+
time_params = {
|
| 173 |
+
'omega_0': 1.0,
|
| 174 |
+
'omega_drive': 2.0,
|
| 175 |
+
'pulse_duration': 1.0,
|
| 176 |
+
'pulse_center': 5.0,
|
| 177 |
+
'gamma_base': 0.1
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
# Estado inicial (ground state)
|
| 181 |
+
rho_0 = np.array([[1, 0], [0, 0]], dtype=complex)
|
| 182 |
+
|
| 183 |
+
# Observables
|
| 184 |
+
observables = {
|
| 185 |
+
'sigma_z': sigma_z,
|
| 186 |
+
'sigma_x': sigma_x,
|
| 187 |
+
'population_excited': np.array([[0, 0], [0, 1]], dtype=complex)
|
| 188 |
+
}
|
| 189 |
+
|
| 190 |
+
return TimeDependentSystemParameters(
|
| 191 |
+
name="driven_qubit",
|
| 192 |
+
dimension=2,
|
| 193 |
+
H_func=H_driven_qubit,
|
| 194 |
+
L_func=L_driven_qubit,
|
| 195 |
+
rho_0=rho_0,
|
| 196 |
+
t_span=(0, 10),
|
| 197 |
+
time_params=time_params,
|
| 198 |
+
observables=observables
|
| 199 |
+
)
|
| 200 |
+
|
| 201 |
+
def create_adiabatic_passage_system():
|
| 202 |
+
"""
|
| 203 |
+
Transferencia adiabática poblacional estimulada (STIRAP).
|
| 204 |
+
Sistema de 3 niveles con dos campos láser contrapropagantes.
|
| 205 |
+
|
| 206 |
+
|1⟩ ←→ |2⟩ ←→ |3⟩
|
| 207 |
+
Ω₁(t) Ω₂(t)
|
| 208 |
+
"""
|
| 209 |
+
|
| 210 |
+
# Operadores de transición
|
| 211 |
+
sigma_12 = np.array([[0, 1, 0], [0, 0, 0], [0, 0, 0]], dtype=complex)
|
| 212 |
+
sigma_23 = np.array([[0, 0, 0], [0, 0, 1], [0, 0, 0]], dtype=complex)
|
| 213 |
+
sigma_13 = np.array([[0, 0, 1], [0, 0, 0], [0, 0, 0]], dtype=complex)
|
| 214 |
+
|
| 215 |
+
# Proyectores
|
| 216 |
+
P1 = np.array([[1, 0, 0], [0, 0, 0], [0, 0, 0]], dtype=complex)
|
| 217 |
+
P2 = np.array([[0, 0, 0], [0, 1, 0], [0, 0, 0]], dtype=complex)
|
| 218 |
+
P3 = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 1]], dtype=complex)
|
| 219 |
+
|
| 220 |
+
def H_stirap(t, params):
|
| 221 |
+
"""Hamiltoniano STIRAP con pulsos contrapropagantes."""
|
| 222 |
+
delta = params['detuning']
|
| 223 |
+
Omega_max = params['Omega_max']
|
| 224 |
+
T_pulse = params['T_pulse']
|
| 225 |
+
t_delay = params['t_delay']
|
| 226 |
+
|
| 227 |
+
# Pulsos Gaussianos con retraso
|
| 228 |
+
Omega1_t = Omega_max * np.exp(-((t - T_pulse)/T_pulse * 2)**2)
|
| 229 |
+
Omega2_t = Omega_max * np.exp(-((t - T_pulse - t_delay)/T_pulse * 2)**2)
|
| 230 |
+
|
| 231 |
+
H = delta * P2 # Desintonía del nivel intermedio
|
| 232 |
+
H += 0.5 * Omega1_t * (sigma_12 + sigma_12.conj().T) # Acoplamiento 1↔2
|
| 233 |
+
H += 0.5 * Omega2_t * (sigma_23 + sigma_23.conj().T) # Acoplamiento 2↔3
|
| 234 |
+
|
| 235 |
+
return H
|
| 236 |
+
|
| 237 |
+
def L_stirap(t, params):
|
| 238 |
+
"""Decaimiento espontáneo desde el nivel excitado."""
|
| 239 |
+
gamma2 = params['gamma2']
|
| 240 |
+
|
| 241 |
+
# Solo el nivel 2 decae
|
| 242 |
+
L1 = np.sqrt(gamma2) * (sigma_12.conj().T) # 2 → 1
|
| 243 |
+
L3 = np.sqrt(gamma2) * (sigma_23.conj().T) # 2 → 3
|
| 244 |
+
|
| 245 |
+
return [L1, L3]
|
| 246 |
+
|
| 247 |
+
time_params = {
|
| 248 |
+
'detuning': 0.0, # Resonancia
|
| 249 |
+
'Omega_max': 1.0, # Frecuencia de Rabi máxima
|
| 250 |
+
'T_pulse': 2.0, # Duración del pulso
|
| 251 |
+
't_delay': 1.0, # Retraso entre pulsos (crucial para STIRAP)
|
| 252 |
+
'gamma2': 0.05 # Decaimiento del nivel intermedio
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
# Estado inicial: |1⟩
|
| 256 |
+
rho_0 = P1.copy()
|
| 257 |
+
|
| 258 |
+
observables = {
|
| 259 |
+
'Population_1': P1,
|
| 260 |
+
'Population_2': P2,
|
| 261 |
+
'Population_3': P3,
|
| 262 |
+
'Coherence_13': sigma_13 + sigma_13.conj().T
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
return TimeDependentSystemParameters(
|
| 266 |
+
name="STIRAP_3level",
|
| 267 |
+
dimension=3,
|
| 268 |
+
H_func=H_stirap,
|
| 269 |
+
L_func=L_stirap,
|
| 270 |
+
rho_0=rho_0,
|
| 271 |
+
t_span=(0, 8),
|
| 272 |
+
time_params=time_params,
|
| 273 |
+
observables=observables
|
| 274 |
+
)
|
| 275 |
+
|
| 276 |
+
def create_parametric_oscillator():
|
| 277 |
+
"""
|
| 278 |
+
Oscilador paramétrico con bomba modulada + decoherencia.
|
| 279 |
+
|
| 280 |
+
H(t) = ω a†a + f(t)(a² + a†²) # Bomba paramétrica
|
| 281 |
+
L = √κ a + √γφ (a + a†) # Pérdidas + dephasing de fase
|
| 282 |
+
"""
|
| 283 |
+
N = 6 # Dimensión del espacio de Fock
|
| 284 |
+
|
| 285 |
+
# Operadores
|
| 286 |
+
a = np.diag(np.sqrt(np.arange(1, N)), k=1).astype(complex)
|
| 287 |
+
adagger = a.conj().T
|
| 288 |
+
n_op = adagger @ a
|
| 289 |
+
|
| 290 |
+
# Operadores cuadráticos
|
| 291 |
+
a_squared = a @ a
|
| 292 |
+
adagger_squared = adagger @ adagger
|
| 293 |
+
|
| 294 |
+
def H_parametric(t, params):
|
| 295 |
+
"""Hamiltoniano con bomba paramétrica modulada."""
|
| 296 |
+
omega = params['omega']
|
| 297 |
+
f_max = params['f_max']
|
| 298 |
+
f_freq = params['f_freq']
|
| 299 |
+
|
| 300 |
+
# Modulación sinusoidal de la bomba
|
| 301 |
+
f_t = f_max * np.sin(f_freq * t)
|
| 302 |
+
|
| 303 |
+
H = omega * n_op + f_t * (a_squared + adagger_squared)
|
| 304 |
+
return H
|
| 305 |
+
|
| 306 |
+
def L_parametric(t, params):
|
| 307 |
+
"""Pérdidas de cavidad + dephasing de fase."""
|
| 308 |
+
kappa = params['kappa']
|
| 309 |
+
gamma_phi = params['gamma_phi']
|
| 310 |
+
|
| 311 |
+
L_loss = np.sqrt(kappa) * a
|
| 312 |
+
L_dephase = np.sqrt(gamma_phi) * (a + adagger) # Dephasing de cuadratura
|
| 313 |
+
|
| 314 |
+
return [L_loss, L_dephase]
|
| 315 |
+
|
| 316 |
+
time_params = {
|
| 317 |
+
'omega': 1.0,
|
| 318 |
+
'f_max': 0.5, # Amplitud de bomba paramétrica
|
| 319 |
+
'f_freq': 2.0, # Frecuencia de modulación
|
| 320 |
+
'kappa': 0.1, # Pérdidas de cavidad
|
| 321 |
+
'gamma_phi': 0.05 # Dephasing de fase
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
# Estado inicial: vacío
|
| 325 |
+
rho_0 = np.zeros((N, N), dtype=complex)
|
| 326 |
+
rho_0[0, 0] = 1.0
|
| 327 |
+
|
| 328 |
+
# Observables
|
| 329 |
+
x_op = (a + adagger) / np.sqrt(2) # Posición
|
| 330 |
+
p_op = -1j * (a - adagger) / np.sqrt(2) # Momento
|
| 331 |
+
|
| 332 |
+
observables = {
|
| 333 |
+
'photon_number': n_op,
|
| 334 |
+
'position': x_op,
|
| 335 |
+
'momentum': p_op,
|
| 336 |
+
'squeezing_x': x_op @ x_op,
|
| 337 |
+
'squeezing_p': p_op @ p_op
|
| 338 |
+
}
|
| 339 |
+
|
| 340 |
+
return TimeDependentSystemParameters(
|
| 341 |
+
name="parametric_oscillator",
|
| 342 |
+
dimension=N,
|
| 343 |
+
H_func=H_parametric,
|
| 344 |
+
L_func=L_parametric,
|
| 345 |
+
rho_0=rho_0,
|
| 346 |
+
t_span=(0, 20),
|
| 347 |
+
time_params=time_params,
|
| 348 |
+
observables=observables
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
def plot_time_dependent_results(results: Dict, save_path: Optional[str] = None):
|
| 352 |
+
"""Visualización especializada para sistemas con dependencia temporal."""
|
| 353 |
+
|
| 354 |
+
if not results.get('success', False):
|
| 355 |
+
print("No se pueden graficar resultados fallidos")
|
| 356 |
+
return
|
| 357 |
+
|
| 358 |
+
time = results['time']
|
| 359 |
+
observables = results['observables']
|
| 360 |
+
|
| 361 |
+
fig = plt.figure(figsize=(15, 10))
|
| 362 |
+
|
| 363 |
+
# Layout de subplots
|
| 364 |
+
gs = fig.add_gridspec(3, 3, hspace=0.3, wspace=0.3)
|
| 365 |
+
|
| 366 |
+
# 1. Evolución de observables principales
|
| 367 |
+
ax1 = fig.add_subplot(gs[0, :2])
|
| 368 |
+
for obs_name, obs_vals in observables.items():
|
| 369 |
+
if 'Population' in obs_name or 'population' in obs_name:
|
| 370 |
+
ax1.plot(time, np.real(obs_vals), 'o-', label=obs_name, markersize=3)
|
| 371 |
+
ax1.set_xlabel('Tiempo')
|
| 372 |
+
ax1.set_ylabel('Población')
|
| 373 |
+
ax1.set_title('Evolución de Poblaciones')
|
| 374 |
+
ax1.legend()
|
| 375 |
+
ax1.grid(True)
|
| 376 |
+
|
| 377 |
+
# 2. Conservación de traza y pureza
|
| 378 |
+
ax2 = fig.add_subplot(gs[0, 2])
|
| 379 |
+
ax2.plot(time, results['traces'], 'b-', label='Traza')
|
| 380 |
+
ax2.plot(time, results['purities'], 'r--', label='Pureza')
|
| 381 |
+
ax2.set_xlabel('Tiempo')
|
| 382 |
+
ax2.set_title('Conservación')
|
| 383 |
+
ax2.legend()
|
| 384 |
+
ax2.grid(True)
|
| 385 |
+
|
| 386 |
+
# 3. Hamiltoniano dependiente del tiempo (elementos seleccionados)
|
| 387 |
+
ax3 = fig.add_subplot(gs[1, :])
|
| 388 |
+
H_evolution = results['H_evolution']
|
| 389 |
+
H_diagonal = [np.real(np.diag(H)) for H in H_evolution]
|
| 390 |
+
H_diagonal = np.array(H_diagonal).T
|
| 391 |
+
|
| 392 |
+
for i in range(min(3, H_diagonal.shape[0])): # Máximo 3 elementos diagonales
|
| 393 |
+
ax3.plot(time, H_diagonal[i], label=f'H_{{{i},{i}}}')
|
| 394 |
+
|
| 395 |
+
# También graficar algunos elementos off-diagonal si existen
|
| 396 |
+
if H_evolution[0].shape[0] > 1:
|
| 397 |
+
H_offdiag = [np.real(H[0, 1]) for H in H_evolution]
|
| 398 |
+
ax3.plot(time, H_offdiag, '--', label='Re(H_{0,1})', alpha=0.7)
|
| 399 |
+
|
| 400 |
+
ax3.set_xlabel('Tiempo')
|
| 401 |
+
ax3.set_ylabel('H(t) [elementos]')
|
| 402 |
+
ax3.set_title('Evolución del Hamiltoniano')
|
| 403 |
+
ax3.legend()
|
| 404 |
+
ax3.grid(True)
|
| 405 |
+
|
| 406 |
+
# 4. Otros observables (coherencias, etc.)
|
| 407 |
+
ax4 = fig.add_subplot(gs[2, :2])
|
| 408 |
+
for obs_name, obs_vals in observables.items():
|
| 409 |
+
if 'Coherence' in obs_name or 'sigma' in obs_name:
|
| 410 |
+
ax4.plot(time, np.real(obs_vals), label=f'Re({obs_name})')
|
| 411 |
+
ax4.plot(time, np.imag(obs_vals), '--', label=f'Im({obs_name})', alpha=0.7)
|
| 412 |
+
ax4.set_xlabel('Tiempo')
|
| 413 |
+
ax4.set_ylabel('Coherencias')
|
| 414 |
+
ax4.set_title('Evolución de Coherencias')
|
| 415 |
+
ax4.legend()
|
| 416 |
+
ax4.grid(True)
|
| 417 |
+
|
| 418 |
+
# 5. Matriz de densidad final (representación)
|
| 419 |
+
ax5 = fig.add_subplot(gs[2, 2])
|
| 420 |
+
rho_final = results['rho_t'][-1]
|
| 421 |
+
im = ax5.imshow(np.abs(rho_final), cmap='viridis', interpolation='nearest')
|
| 422 |
+
ax5.set_title('|ρ(t_final)|')
|
| 423 |
+
ax5.set_xlabel('j')
|
| 424 |
+
ax5.set_ylabel('i')
|
| 425 |
+
plt.colorbar(im, ax=ax5)
|
| 426 |
+
|
| 427 |
+
plt.suptitle(f'Resultados: {results["name"]} (Dependencia Temporal)', fontsize=14)
|
| 428 |
+
|
| 429 |
+
if save_path:
|
| 430 |
+
plt.savefig(save_path, dpi=300, bbox_inches='tight')
|
| 431 |
+
logger.info(f"Figura guardada en {save_path}")
|
| 432 |
+
|
| 433 |
+
plt.show()
|
| 434 |
+
|
| 435 |
+
def demonstrate_time_dependent_systems():
|
| 436 |
+
"""Función de demostración para sistemas con dependencia temporal."""
|
| 437 |
+
|
| 438 |
+
print("\n" + "="*70)
|
| 439 |
+
print("SIMULACIÓN DE SISTEMAS CON DEPENDENCIA TEMPORAL")
|
| 440 |
+
print("="*70)
|
| 441 |
+
|
| 442 |
+
simulator = TimeDependentLindladSimulator()
|
| 443 |
+
|
| 444 |
+
# Ejemplo 1: Qubit controlado
|
| 445 |
+
print("\n1. Simulando qubit con pulso láser...")
|
| 446 |
+
driven_qubit = create_driven_qubit_system()
|
| 447 |
+
results_qubit = simulator.simulate(driven_qubit)
|
| 448 |
+
|
| 449 |
+
if results_qubit['success']:
|
| 450 |
+
print(" ✓ Simulación exitosa")
|
| 451 |
+
plot_time_dependent_results(results_qubit)
|
| 452 |
+
|
| 453 |
+
# Ejemplo 2: STIRAP
|
| 454 |
+
print("\n2. Simulando transferencia adiabática (STIRAP)...")
|
| 455 |
+
stirap_system = create_adiabatic_passage_system()
|
| 456 |
+
results_stirap = simulator.simulate(stirap_system)
|
| 457 |
+
|
| 458 |
+
if results_stirap['success']:
|
| 459 |
+
print(" ✓ Simulación exitosa")
|
| 460 |
+
plot_time_dependent_results(results_stirap)
|
| 461 |
+
|
| 462 |
+
# Ejemplo 3: Oscilador paramétrico
|
| 463 |
+
print("\n3. Simulando oscilador paramétrico...")
|
| 464 |
+
param_osc = create_parametric_oscillator()
|
| 465 |
+
results_osc = simulator.simulate(param_osc)
|
| 466 |
+
|
| 467 |
+
if results_osc['success']:
|
| 468 |
+
print(" ✓ Simulación exitosa")
|
| 469 |
+
plot_time_dependent_results(results_osc)
|
| 470 |
+
|
| 471 |
+
if __name__ == "__main__":
|
| 472 |
+
demonstrate_time_dependent_systems()
|