Laboratorium MATLA Ćwiczenie 4. Debugowanie. Efektywności kodu. Wektoryzacja. Opracowali: - dr inż. Beata Leśniak-Plewińska Zakład Inżynierii Biomedycznej, Instytut Metrologii i Inżynierii Biomedycznej, Wydział Mechatroniki Politechniki Warszawskiej. Warszawa, 2017
Cel ćwiczenia Celem ćwiczenia jest nabycie przez studentów umiejętności w zakresie debuggowania kodu oraz poprawy jego efektywności poprzez wykorzystanie wektoryzacji. Referencje Andrew P. King Paul Aljabar MATLAB Programming for Biomedical Engineers and Scientists 1st Ed. Academic Press, 2017 Ćwiczenie 1 2/10
1. Przepisz poniższy kod i zapisz go w skrypcie zad41a.m. Zmodyfikuj zaznaczony ramką fragment poniższego kodu tak, aby uzyskać czas realizacji obliczeń (bez tworzenia wykresu) krótszy co najmniej o rząd wielkości. Nowy kod zapisz w skrypcie zad41b.m. N = 200; a = []; for j = 1:N a(j) = j * pi / N; end for k = 1:numel(a) b(k) = sin(a(k)); end for m = 1:numel(a) c(m) = b(m) / a(m); end plot(a,b,'r') hold on plot(a,c,'k') hold off legend('sin','sinc') Wyznacz współczynnik poprawy efektywności kodu jako stosunek czasu realizacji kodu w wersji oryginalnej do czasu realizacji kodu w wersji poprawionej. Zmodyfikowany fragment kodu, uzyskane wartości czasów realizacji obu wersji kodu oraz współczynnik poprawy efektywności kodu (stosunek czasu realizacji kodu poprawionego do czasu realizacji kodu oryginalnego) wpisz w odpowiednich rubrykach Sprawozdania. WSKAZÓWKA: W celu wyznaczenia czasu realizacji obliczeń, zależnie od sytuacji, możesz użyć funkcji timera. Zadbaj o zapewnienie odpowiednich warunków pomiaru czasu (np. w przypadku funkcji timera za wiarygodne przyjmuje się pomiary czasu realizacji kodu wynoszące min. 0.01 s). 2. Napisz skrypt realizujący następujące zadania a) Utworzenie macierzy A o rozmiarze 100 20 zawierającej losowe liczby rzeczywiste z przedziału <0, 1>. b) Utworzenie macierzy B jako kopii macierzy A. c) Zmiana wartości każdego elementu macierzy A, którego początkowa wartość była mniejsza niż 0.6 na 0 z użyciem pętli for (kod zapisz w pliku zad42abc.m). Zmodyfikuj kod zrealizowany w pp. c) tak aby zmiana wartości każdego elementu macierzy B, którego początkowa wartość była mniejsza niż 0.6 na 0 była realizowana za pomocą indeksowania logicznego (nie korzystaj z funkcji find oraz instrukcji sterujących); kod zapisz w pliku zad42log.m. Zastosuj wbudowane funkcje timera do oceny czasu realizacji kodu w pp. a,b,c) i a,b,d). Porównaj uzyskane wartości. Wyznacz współczynnik poprawy efektywności kodu jako stosunek czasu realizacji kodu w wersji oryginalnej do czasu realizacji kodu w wersji poprawionej. Obie wersje kodu oraz uzyskane wartości czasów realizacji obu wersji kodu wpisz w odpowiednich rubrykach Sprawozdania. Ćwiczenie 1 3/10
WSKAZÓWKA: W celu wyznaczenia czasu realizacji obliczeń, zależnie od sytuacji, możesz użyć funkcji timera. Zadbaj o zapewnienie odpowiednich warunków pomiaru czasu (np. w przypadku funkcji timera za wiarygodne przyjmuje się pomiary czasu realizacji kodu wynoszące min. 0.01 s). 3. Napisz funkcję MATLAB a, która będzie pobierać jeden parametr wejściowy wartość całkowitą N, i będzie zwracać macierz reprezentującą tabliczkę mnożenia dla liczb całkowitych z przedziału od 1 do N. Np. dla N=5 macierz powinna mieć postać W tym celu kolejno: 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 5 10 15 20 25 a) Użyj pętli for, skup się na napisaniu przejrzystego i zrozumiałego kodu, który będzie działać poprawnie; kod zapisz w pliku tabmnoz_a.m (PAMIĘTAJ, że nazwa funkcji musi być zgodna z nazwą m-pliku, w którym jest ona zapisana.). b) Napisz drugą implementacje tej funkcji, tym razem nie korzystaj z instrukcji sterujących; kod zapisz w pliku tabmnoz_b.m (PAMIĘTAJ, że nazwa funkcji musi być zgodna z nazwą m-pliku, w którym jest ona zapisana.). c) Uruchom obie implementacje dla N równego kolejno od 1 do 200 i wyznacz czasy realizacji obu implementacji dla każdej wartości N. d) Uruchom obie implementacje dla N równego kolejno od 1 do 200 i wyznacz sumaryczny czasy realizacji obu implementacji. Wyznacz współczynnik poprawy efektywności kodu jako stosunek czasu realizacji kodu uzyskany w pp. d) dal kodu w wersji oryginalnej do czasu realizacji kodu w wersji poprawionej. WSKAZÓWKA: W celu wyznaczenia czasu realizacji obliczeń, zależnie od sytuacji, możesz użyć funkcji timera czy funkcji timeit. W przypadku wykorzystania funkcji timera zadbaj o zapewnienie odpowiednich warunków pomiaru czasu (np. w przypadku funkcji timera za wiarygodne przyjmuje się pomiary czasu realizacji kodu wynoszące min. 0.01 s). Obie wersje kodu, wartości czasów uzyskanych w pp. d) oraz współczynnik poprawy efektywności kodu kodu wpisz w odpowiednich rubrykach Sprawozdania. 4. Funkcja prostokątna bipolarna o okresie T może być zdefiniowana jako: A dla t (n T, n T + τ) f A dla t (n T +τ, n T +T ) (t)={ 0 dla t=n T, n T+ τ Jeśli τ=t/2, mówimy wówczas o bipolarnej fali prostokątnej o wypełnieniu ½. Jej aproksymacja za pomocą szeregu Fouriera jest określona wzorem: S (t)= 4 A π k=0 1 2 k+1 sin ( 2 (2 k +1) π t T ) Stworzono funkcję szeregf([t1, t2], delta_t, n), która wyznacza aproksymację bipolarnej funkcji prostokątnej o wypełnieniu ½ jako skończoną sumę wyrazów szeregu Fouriera dla t z przedziału < t1, t2> i k zmieniającego się od 0 do n. Ćwiczenie 1 4/10
Następnie stworzono skrypt szereg.m. Jego zadaniem jest skorzystanie z funkcji szeregf w celu iteracyjnego wyznaczenia minimalnej liczby wyrazów szeregu Fouriera nmin niezbędnych do aproksymacji bipolarnej funkcji prostokątnej o następujących parametrach: A = 1, T = 1 wyznaczonej w przedziale czasu t = <-1,1 1,1>, dla której wartość błędu średniokwadratowego (MSE ang. Mean Square Error) nie będzie większą niż 0.02. Nawet jeśli kryterium wartości MSE nie zostanie osiągnięte, skrypt powinien przerwać swoje działanie po 100-ej iteracji. Ponadto, skrypt powinien przerwać swoje działanie wówczas gdyby miało dojść do sumowania wyrazu odpowiadającego składowej sinusoidalnej o częstotliwości niespełniającej kryterium Nyquista. Po wyznaczeniu wartości nmin skrypt tworzy animację, w której we wspólnym układzie współrzędnych nałożone będą na siebie: wykres funkcji prostokątnej w funkcji czasu oraz kolejne jej aproksymacje dla n zmieniającego się od 1 do wyznaczonej wartości minimalnej nmin. W celu realizacji obliczeń w skrypcie dodatkowo zdefiniowano dwie funkcje: prostsyg = prostokat([t1, t2], delta_t), która wyznacza wartości bipolarnej funkcji prostokątnej prostsyg o wypełnieniu ½ dla t z przedziału < t1, t2> z krokiem iteracji delta_t dla T=1 i A=1 (plik prostokat.m) mseerr = mseerror(wartosc, estymator), która wyznacza wartość błędu średniokwadratowego mseerr wartość oczekiwana kwadratu błędu czyli różnicy pomiędzy estymatorem (estymator), a wartością estymowaną (wartosc)(plik mseerror.m) określonego wzorem MSE (θ)=e((^θ θ) 2 ) gdzie ^θ - estymator, θ - wartość estymowana. Niestety kod skryptu szereg.m, w tym funkcji lokalnych, oraz kod funkcji szeregf zawierają błędy, w tym błędy uruchomieniowe, składniowe czy logiczne. Przeanalizuj kod zawarty w m-plikach szereg.m, prostokat.m, mseerror.m i szeregf.m. W skrypcie szereg.m wyodrębnij poszczególne bloki kodu odpowiedzialne za określone zadania i zaznacz je dodając odpowiednie komentarze. Następnie korzystając z analizatora kodu oraz debuggera (jeśli zachodzi taka potrzeba użyj brekpointów) usuń WSZYSTKIE zauważone błędy (w tym także stylu), a następnie uruchom skrypt. UWAGA. Nie nadpisuj kodu skryptu i funkcji. Kod skryptu i funkcji po zmianach zapisuj pod nowymi nazwami: szereg_new.m, prostokat_new.m, mseerror_new.m i szeregf_new.m. PAMIĘTAJ, że nazwa funkcji musi być zgodna z nazwą m-pliku, w którym jest ona zapisana. WSKAZÓWKA: Liczba rubryk do opisu błędów nie musi być zgodna z faktyczną liczbą błędów. WSKAZÓWKA: Fragment kodu służący do obsługi poprawności wartości wprowadzonych z klawiatury przez użytkownika za pomocą funkcji input nie zawiera błędów i służy ilustracji. WSKAZÓWKA: Dobrym obyczajem jest dodawanie w m-pliku adnotacji, które ułatwiają szybkie znajdowanie fragmentów kodu wymagającego poprawy, uzupełnienia lub zaktualizowania. Komentarz zawierający adnotację TODO jest zazwyczaj stosowany do zaznaczania tych miejsc w kodzie, które wymagają uzupełnienia. Więcej informacji na ten Ćwiczenie 1 5/10
temat znajduje się w dokumentacji MATLAB a, np. https://www.mathworks.com/help/matlab/matlab_prog/add-reminders-to-files.html czy https://blogs.mathworks.com/community/2008/03/17/whats-on-my-todo-list/. W Sprawozdaniu zanotuj wyznaczoną wartość nmin oraz wymień wszystkie zauważone błędy i podaj sposób ich naprawy. Zoptymalizuj kod funkcji szeregf_new, tak aby czas jej realizacji wyznaczony za pomocą funkcji timeit był o rząd wielkości mniejszy od czasu realizacji funkcji w pierwotnej postaci. Zoptymalizowana funkcję zapisz w pliku szeregf_opt.m. Wyznacz współczynnik poprawy efektywności kodu jako stosunek czasu realizacji kodu w wersji oryginalnej do czasu realizacji kodu w wersji poprawionej. WSKAZÓWKA. Uprość kod unikając obu pętli for. WSKAZÓWKA: W celu wyznaczenia czasu realizacji obliczeń, zależnie od sytuacji, możesz użyć funkcji timera czy funkcji timeit. W przypadku wykorzystania funkcji timera zadbaj o zapewnienie odpowiednich warunków pomiaru czasu (np. w przypadku funkcji timera za wiarygodne przyjmuje się pomiary czasu realizacji kodu wynoszące min. 0.01 s). Ćwiczenie 1 6/10
Sprawozdanie Ćwiczenie nr 1. Wektory, macierze, operatory i podstawowe funkcje. L.p. Imię i nazwisko Grupa Data Punkt cw./ L. punktów Realizacja/wynik Uwagi prowadzącego 1 / 1 zad41a.m Czas realizacji kodu zad41b.m Współczynnik poprawy efektywności kodu a). 2 / 1 b). Ćwiczenie 1 7/10
c). d) zad42abc.m Czas realizacji kodu zad42log.m Współczynnik poprawy efektywności kodu 3 / 1 a)....... b).... Ćwiczenie 1 8/10
tabmnoz_a.m Czas realizacji kodu tabmnoz_b.m Współczynnik poprawy efektywności kodu szereg.m Nr linii kodu z błędem Poprawny kod prostokat.m 4 / 2 mseerror.m szeregf.m Ćwiczenie 1 9/10
Wartość nmin Zoptymalizowany kod funkcji szeregf_new.m............................. szeregf.m Czas realizacji kodu szeregf_opt.m Współczynnik poprawy efektywności kodu Ćwiczenie 1 10/10