View
218
Download
0
Category
Preview:
Citation preview
1. Introducción
La interpolación es un método usado para conocer, de un modo aproximado, los valores que toma
cierta función de la cual sólo se conoce su imagen en un número finito de abscisas. A menudo, ni
siquiera se conocerá la expresión de la función y sólo se dispondrá de los valores que toma para
dichas abscisas.
El objetivo será hallar una función que cumpla lo antes mencionado y que permita hallar
aproximaciones de otros valores desconocidos para la función con una precisión deseable fijada.
Existen varios métodos que permiten hacer interpolación de funciones, unos tratan de buscar una
función aproximada a la función original, y otros en cambio tratan de buscar sólo los valores
interpolados sin interesarse en cómo luce la función original, entre los métodos más conocidos
tenemos:
• Interpolación lineal, donde la función original se aproxima a una recta.
• Interpolación cuadrática o de Lagrange, donde la función original se aproxima a una función
cuadrática.
• Interpolación polinómica, donde la función original se aproxima a un polinomio de grado N.
• Interpolación de Hermite, el cual consiste en buscar un polinomio cúbico por pedazos dentro
de la función a aproximar.
• Interpolación por series de Fourier, donde la función original es traspasada al dominio de la
frecuencia con el objetivo de buscar los valores a ser interpolados dentro del dominio del
tiempo.
El presente trabajo tiene el objetivo de, por un lado describir la técnica de interpolación de
funciones por medio de series de Fourier, y por otro lado, y debido a las cargas computacionales
incurridas dentro del cálculo de la serie de Fourier de una función, el maximizar la velocidad de
cálculo de los valores de la función interpolada.
2. Interpolación de funciones por medio de Series de Fourier
La interpolación de funciones por Series de Fourier, es un método que como ya se mencionó, no
trata de buscar la función que representa fielmente a la función original, sino más bien trata de
buscar los valores que representa dicha función.
Este tipo de interpolación tiene la ventaja de ser muy preciso con la penalización de ser complejo
computacionalmente hablando. Este método es especialmente indicado cuando es necesario
preservar no sólo el valor de la señal sino también su fase.
El método consiste en obtener la transformada de Fourier de la función que se desea interpolar, una
vez en el dominio de la frecuencia se agregan tantos ceros en las frecuencias bajas como se desee
interpolar la función, y luego se regresa al dominio del tiempo con los valores interpolados. Este
método, como veremos más tarde, es muy preciso para interpolar funciones simples sin saltos
bruscos que se traduzcan en frecuencias infinitas.
En resumen los pasos que debemos tomar para interpolar una función mediante series de Fourier es
el siguiente:
1. La función original la pasamos al dominio de la frecuencia usando la FFT. Con este primer
paso obtenemos la función original en el dominio de la frecuencia.
Fig. 2-1. Función original en el dominio del (a) tiempo y (b) frecuencia.
Como vemos en la Fig.2-1 al realizar la transformada de Fourier de una señal de N muestras
obtenemos la misma señal de N muestras en el dominio de la frecuencia.
2. Agregamos tantos ceros como queramos interpolar la función, por ejemplo en este caso
tenemos una función con 256 valores que queremos interpolar a una función de 512 valores,
te tal forma que agregaremos 256 ceros.
Fig. 2-2. Se colocan ceros en las frecuencias extremas localizadas en la función original.
Como vemos en la Fig.2-2, los ceros son agregados de tal forma que no afectemos a la
información contenida en el dominio de la frecuencia. Los ceros son agregados en los
extremos de la función que contiene información de las frecuencias extremas encontradas
en la función original.
3. Regresamos al dominio del tiempo usando la IFFT.
Como vemos en la Fig.2-3, la función contiene los valores interpolados, en este caso hemos
hecho una interpolación de 1 a 2, esto es, que hemos obtenido el doble de muestras
contenidas en la función original.
Fig. 2-3. Función interpolada.
3. Implementación
La implementación de la implementación por Series de Fourier fue desarrollada en el lenguaje C. Los
siguientes ficheros corresponden a la implementación del interpolador:
Interpolator.cpp Implementación de las funciones para realizar interpolación de funciones basada en la transformada de Fourier.
Utils.cpp Implementación de los métodos de creación de funciones de prueba, así como aquellas que nos ayudan a dar formato a las funciones de entrada al interpolador.
Interpolation.cpp Implementación de la interpolación de una recta, una función de impulso, y una función sinusoidal. Nos permite realizar las pruebas y obtener los resultados del interpolador. Crea tres ficheros de MatLab con los resultados numéricos y gráficos de la interpolación.
La transformada de Fourier implementada en Interpolator.cpp es la denominada decimation-
intime o Cooley-Tukey FFT presentada por N.M Brenner, la cual permite obtener la transformada
rápida de Fourier y su inversa utilizando la misma función con distinto parámetro de entrada.
La función que implementa la transformada de Fourier implementada en Interpolator.cpp
llamada four1 (float data[], unsigned long nn, int isign), propuesta en el texto de
Numerical Recipes: The Art of Scientific Computing1, recibe como parámetro el vector imaginario
que se desea aplicar la transformada de Fourier (data), el tamaño (nn) y el tipo de transformación
(isign) 1 para la FFT y -1 para la IFFT.
Tanto el vector complejo de entrada (que debe tener un tamaño de potencia de 2) como el de salida
es un vector que contiene datos reales e imaginarios alternados de forma consecutiva, vea Fig. 3-1.
Así mismo el vector imaginario de salida que contiene los datos de frecuencia del vector de entrada,
empieza con la frecuencia cero, hasta la frecuencia positiva más alta localizada. Luego contiene la
frecuencia más negativa hasta la menos negativa localizada justo antes de la frecuencia cero.
1 Numerical Recipes: The Art of Scientific Computing, Third Edition (2007), 1256 pp. Cambridge University
Press, ISBN-10: 0521880688, http://www.nr.com/
Fig. 3-1. Vectores de entrada y salida de la FFT. (a) El vector de entrada contiene N (potencia de 2) muestras complejas en
un vector real de tamaño 2N, alternando parte real e imaginaria de manera consecutiva. (b) El vector de salida contiene el
espectro complejo de Fourier de N valores de frecuencia, con la parte real e imaginaria se alterna consecutivamente. El
vector empieza con la frecuencia cero, hasta la frecuencia positiva más alta localizada. Luego contiene la frecuencia más
negativa hasta la menos negativa localizada justo antes de la frecuencia cero.
4. Primeros resultados
Utilizando el código descrito en Interpolation.cpp obtuvimos la interpolación de una recta, una
función de impulso y una función sinusoidal.
Los resultados para la interpolación de 256 a 512 muestras fueron los siguientes:
Parameters readed: NINPUT=256 NOUTPUT=512 Increment factor=2 *** INTERPOLATION ***
Setting parameters..
W=50, Bandwidth=50, Sample Period=0.01, Sample Frecuency=100 Increment factor=2 Setting data for interpolation... Setting data time ...
Time vector was succefully created... Setting perfect data to compare interpolation Setting data time ...
Time vector was succefully created... MAKING LINE INTERPOLATION
Setting function values ... Line was succefully created... Line was succefully created...
Starting interpolation... Make interpolation from:256 to:512 Interpolation completed in 0 seconds
Analyzing results... Data analyzed
Maximun error=4096 CME=10.1548
MAKING PULSE INTERPOLATION
Setting function values ... Starting interpolation... Make interpolation from:256 to:512
Interpolation completed in 0 seconds Analyzing results... Data analyzed
Maximun error=0.992203 CME=0.00318133
MAKING SINOSOIDAL INTERPOLATION
Setting function values ... Starting interpolation... Make interpolation from:256 to:512 Interpolation completed in 0 seconds
Analyzing results... Data analyzed Maximun error=0.0713742 CME=0.000178336
En la Fig.4-1, podemos observar las gráficas obtenidas para la función original y la interpolada en
cada caso:
Fig. 4-1. Gráficas obtenidas de para una interpolación 1 a 2.
Al observar los resultados obtenidos notamos cómo la interpolación es muy precisa, salvo el caso en aquellos lugares en que la función tiene saltos bruscos (zonas de frecuencia infinita), en donde la función interpolada muestra una especie de balanceos.
Una manera de corregir estos “balanceos” es tratar de evitar que la función original contenga saltos bruscos, realizando alguna transformación u ordenación previa de los datos que elimine dichos saltos, por ejemplo.
5. Optimización
Si analizamos los resultados obtenidos en el apartado anterior notamos como el tiempo tomado en
realizar la interpolación resulta casi despreciable al tratarse de una pequeña cantidad de muestras.
En cambio si queremos interpolar muestras de mayor tamaño, como por ejemplo la interpolación de
una función con 220 muestras a otra de 222, obtenemos los siguientes resultados:
Parameters readed: NINPUT=1048576 NOUTPUT=4194304 Increment factor=4
*** INTERPOLATION *** Setting parameters.. W=50, Bandwidth=50, Sample Period=0.01, Sample Frecuency=100 Increment factor=4
Setting data for interpolation... Setting data time ...
Time vector was succefully created... Setting perfect data to compare interpolation Setting data time ...
Time vector was succefully created...
MAKING LINE INTERPOLATION Setting function values ...
Line was succefully created... Line was succefully created... Starting interpolation...
Make interpolation from:1048576 to:4194304 Interpolation completed in 4.515 seconds Analyzing results...
Data analyzed Maximun error=1.67472e+011 CME=68748.4
MAKING PULSE INTERPOLATION Setting function values ... Starting interpolation...
Make interpolation from:1048576 to:4194304 Interpolation completed in 4.531 seconds
Analyzing results...
Data analyzed Maximun error=1.28829 CME=1.33643e-006
MAKING SINOSOIDAL INTERPOLATION Setting function values ... Starting interpolation... Make interpolation from:1048576 to:4194304
Interpolation completed in 4.561 seconds Analyzing results... Data analyzed
Maximun error=0.362624 CME=1.66753e-007
Como podemos observar, el tiempo tomado por hacer la interpolación de cada función es de 4.5
segundos aproximadamente. En este apartado trataremos de mejorar estos tiempos mediante la
optimización de código.
La estrategia que tomaremos para la optimización del código será la siguiente:
1. Utilizar el compilador de Intel.
2. Utilizar el optimizador de Intel VTunes para localizar los lugares dentro del código donde
se gasta la mayor parte del tiempo del proceso de interpolación.
3. Aplicar las recomendaciones hechas por VTunes para optimizar el código y analizar los
tiempos obtenidos.
4. Analizar las posibilidades de optimización por compilación y elegir la que nos da mejores
tiempos de ejecución.
5. Utilizar pragmas para tratar de mejorar aún más los tiempos.
Una vez seleccionado el compilador de Intel, realizamos un Quick Performance Analysis con VTunes
para analizar los lugares dentro del código en el cual se emplea la mayor parte del tiempo. Los
resultados pueden ser observados en la Fig. 5-1.
Fig. 5-1. Resultados obtenidos con el VTunes.
Al analizar los resultados obtenidos con el VTunes notamos que las recomendaciones hechas por
VTunes en su mayor parte se refieren a la vectorización y desenrollado de lazos.
El desenrollado no será aplicado ya que existe dependencia entre datos, y el esfuerzo en cambiar
dicha dependencia no hará que la velocidad sea incrementada de manera considerable. De igual
forma, la vectorización del lazo se tratará de hacer de manera automática por medio de pragmas y
directivas de compilación.
Lo siguiente que haremos será utilizar un conjunto de directivas de compilación y analizar los
resultados que iremos obteniendo. Los resultados obtenidos son reflejados en la Tabla 5-1.
Compilador Opciones de compilación Tiempos de ejecución para cada
interpolación (seg)
Intel -O2 4.5 – 4.469 – 4.469
Intel -G6 -QaxB -Qpc64 -Qvec_report3 -O2 4.422 – 4.391 – 4.485
Intel -QaxB -Qvec_report3 -O2 4.398 – 4.391 – 4.355
Tabla 5-1. Tabla de tiempos de ejecución, con diferentes parámetros de compilación.
Como podemos observar en la Tabla 5-1, la mejora es casi inapreciable. Al observar el reporte d
compilación, notamos que el compilador pudo vectorizar aquellos lazos que no toman mucho
tiempo de ejecución y que no están dentro de la función que realiza la transformada de Fourier, los
cuales toman la mayor cantidad de tiempo de proceso:
Compiling... Intel(R) C++ Compiler for 32-bit applications, Version 8.1 Build 20041119Z Package ID: W_CC_PC_8.1.022
Copyright (C) 1985-2004 Intel Corporation. All rights reserved. icl -Qvc6 "-Qlocation,link,D:\Archivos de programa\Microsoft Visual Studio\VC98\Bin" /MLd /Zi /O2 "/FoDebug 2/" "/FdDebug 2/" /QaxB /Qvec_report3 "AllCode.cpp"
AllCode.cpp AllCode.cpp(49): (col. 1) remark: _main has been targeted for automatic cpu dispatch. AllCode.cpp(318): (col. 2) remark: LOOP WAS VECTORIZED.
AllCode.cpp(317): (col. 57) remark: ?createSin@@YAXQAM0H@Z has been targeted for automatic cpu dispatch. AllCode.cpp(343): (col. 2) remark: loop was not vectorized: vectorization possible but seems
inefficient. AllCode.cpp(368): (col. 89) remark: ?insertZerosInTheMiddle@@YAXQAMH0H@Z has been targeted for automatic cpu dispatch.
AllCode.cpp(363): (col. 2) remark: LOOP WAS VECTORIZED.
AllCode.cpp(362): (col. 57) remark: ?insertZeros@@YAXQAMHH@Z has been targeted for automatic cpu dispatch.
AllCode.cpp(356): (col. 2) remark: LOOP WAS VECTORIZED. AllCode.cpp(355): (col. 100) remark: ?copyVectorData@@YAXQAM0HHH@Z has been targeted for automatic cpu dispatch. AllCode.cpp(469): (col. 2) remark: loop was not vectorized: not inner loop.
AllCode.cpp(476): (col. 3) remark: loop was not vectorized: unsupported loop structure. AllCode.cpp(485): (col. 2) remark: loop was not vectorized: not inner loop. AllCode.cpp(495): (col. 3) remark: loop was not vectorized: not inner loop.
AllCode.cpp(496): (col. 4) remark: loop was not vectorized: unsupported loop structure. AllCode.cpp(282): (col. 2) remark: loop was not vectorized: unsupported loop structure. AllCode.cpp(324): (col. 2) remark: LOOP WAS VECTORIZED.
AllCode.cpp(323): (col. 84) remark: ?multMatrixConstant@@YAXQAM0HM@Z has been targeted for automatic cpu dispatch.
AllCode.cpp(395): (col. 3) remark: loop was not vectorized: mixed data types. AllCode.cpp(289): (col. 3) remark: loop was not vectorized: nested conditional statements.
AllCode.cpp(305): (col. 2) remark: LOOP WAS VECTORIZED. AllCode.cpp(304): (col. 88) remark: ?createTimeVector@@YAXQAMHMM@Z has been targeted for automatic
cpu dispatch. AllCode.cpp(330): (col. 2) remark: LOOP WAS VECTORIZED.
AllCode.cpp(329): (col. 83) remark: ?addMatrixConstant@@YAXQAM0HM@Z has been targeted for automatic cpu dispatch.
D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\streambuf(146) : (col. 3) remark: loop was not vectorized: unsupported loop structure. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\streambuf(159) : (col. 3) remark: loop
was not vectorized: unsupported loop structure. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\fstream(165) : (col. 4) remark: loop was not vectorized: unsupported loop structure. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\xlocnum(628) : (col. 4) remark: loop
was not vectorized: contains unvectorizable statement at line 629. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\xlocnum(598) : (col. 4) remark: loop was not vectorized: unsupported loop structure.
D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\xlocnum(617) : (col. 4) remark: loop was not vectorized: unsupported loop structure. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\xlocnum(632) : (col. 4) remark: loop
was not vectorized: contains unvectorizable statement at line 633. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\xlocnum(636) : (col. 4) remark: loop was not vectorized: contains unvectorizable statement at line 637.
AllCode.cpp(227) : (col. 2) remark: loop was not vectorized: contains unvectorizable statement at line 228. AllCode.cpp(232) : (col. 2) remark: loop was not vectorized: contains unvectorizable statement at
line 233. AllCode.cpp(238) : (col. 2) remark: loop was not vectorized: contains unvectorizable statement at line 239.
AllCode.cpp(243) : (col. 2) remark: loop was not vectorized: contains unvectorizable statement at line 244. AllCode.cpp(248) : (col. 2) remark: loop was not vectorized: contains unvectorizable statement at line 249.
D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\ostream(299) : (col. 4) remark: loop was not vectorized: unsupported loop structure. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\ostream(308) : (col. 4) remark: loop
was not vectorized: unsupported loop structure. AllCode.cpp(298) : (col. 2) remark: loop was not vectorized: contains unvectorizable statement at line 299.
AllCode.cpp(336) : (col. 2) remark: LOOP WAS VECTORIZED.
AllCode.cpp(335) : (col. 63) remark: ?copyVector@@YAXQAM0H@Z has been targeted for automatic cpu dispatch.
D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\xlocale(219) : (col. 9) remark: loop was not vectorized: contains unvectorizable statement at line 220. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\fstream(49) : (col. 49) remark: loop
was not vectorized: unsupported loop structure. D:\Archivos de programa\Microsoft Visual Studio\VC98\INCLUDE\fstream(54) : (col. 4) remark: loop was not vectorized: contains unvectorizable statement at line 55.
Microsoft (R) Incremental Linker Version 6.00.8168 Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
-out:AllCode.exe -debug:notmapped,full -debugtype:cv -pdb:AllCode.pdb
"Debug 2\AllCode.obj"
AllCode.obj - 0 error(s), 0 warning(s)
El siguiente paso consiste en utilizar pragmas. En general utilizaremos dos pragmas:
• #pragma ivdep, que permite decirle al compilador que ignore la posible dependencia
entre datos.
• #pragma vector always, que permite decirle al compilador que vectorice el bucle
siempre que sea posible, ignorando posibles decisiones sobre rentabilidad o conveniencia.
Ambos pragmas fueron agregados dentro la función que realiza la transformada de Fourier, en los
siguientes lugares:
void four1(float data[], unsigned long nn, int isign) {
unsigned long n,mmax,m,j,istep,i; double wtemp,wr,wpr,wpi,wi,theta; float tempr,tempi;
n=nn << 1; j=1;
#pragma vector always for (i=1;i<n;i+=2) { if (j > i) {
SWAP(data[j],data[i]); SWAP(data[j+1],data[i+1]); } m=n >> 1;
while (m >= 2 && j > m) { j -= m;
m >>= 1; } j += m;
} //Herebegins the Daniel-Lanczos section of the routire, outer loop exectured log2(nn) times
mmax=2; #pragma ivdep
while (n > mmax) { istep=mmax << 1; // Trigonometric recurrence
theta=isign*(6.28318530717959/mmax); wtemp=sin(0.5*theta); wpr = -2.0*wtemp*wtemp; wpi=sin(theta);
wr=1.0; wi=0.0;
for (m=1;m<mmax;m+=2) { #pragma ivdep
for (i=m;i<=n;i+=istep) {
// Danielson-Lanczos Formula j=i+mmax;
tempr=wr*data[j]-wi*data[j+1]; tempi=wr*data[j+1]+wi*data[j]; data[j]=data[i]-tempr;
data[j+1]=data[i+1]-tempi; data[i] += tempr; data[i+1] += tempi;
} // Trigonometric recuerrence wr=(wtemp=wr)*wpr-wi*wpi+wr;
wi=wi*wpr+wtemp*wpi+wi; } mmax=istep; }
}
Los resultados en tiempos fueron 4.311, 4.356 y 4.322 segundos en cada caso respectivamente. Así
mismo se analizó los resultados para verificar que la especificación de no dependencia entre datos
en el código pudiera o no afectar el resultado del interpolador. Los resultados fueron correctos, de
tal forma que podemos decir que asumir dicha independencia fue lo correcto.
Si bien en cierto los tiempos mejoraron un poco, dicha mejora aún no es apreciable. Una posible
mejora sería utilizar instrucciones específicas del procesador (como MMX considerando que fuera un
procesador Intel) para incrementar la velocidad de procesamiento.
6. Conclusiones
Al finalizar el trabajo podemos obtener las siguientes conclusiones:
� El método de interpolación por series de Fourier es un método que a pesar de ser costoso
computacionalmente hablando, resulta ser muy preciso en la obtención de los valores
interpolados para funciones simples con un rango de frecuencias acotado.
� Debido a la gran cantidad de dependencia de datos, la implementación de la FFT no puede
ser optimizada aplicando vectorización y desenrollado de los lazos presentes.
7. Referencias
[1] Numerical Recipes-in C (The Art of Scientific Computing 2ª Ed.). William H. Press, Saul A.
Teukolsky, William T. Vetterling, Brian P. Flannery. Cambridge University Press. 1996. ISBN 0 521
43108 5.
Recommended