Feature Extraction
Asteroid
Spanish
English
code
jako6mina commited on
Commit
4e3e150
·
verified ·
1 Parent(s): 077dfdd

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.

Files changed (2) hide show
  1. enhanced_ml_features.ts +405 -0
  2. time_dependent_lindblad.py +472 -0
enhanced_ml_features.ts ADDED
@@ -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
+ }
time_dependent_lindblad.py ADDED
@@ -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()