Saturday, March 28, 2015

Mengatur Area Blur Pada Image Dengan Mouse Scroll di MATLAB

Dalam tutorial kali saya akan memberikan contoh bagaimana mengatur area blur (region of interest yang akan di-blur) dari gambar dengan menggunakan mouse scroll pada MATLAB. Source codenya saya berikan langsung yakni
% test window scroll function untuk memblur gambar. Gunakan mouse scroll
% untuk memperluas atau mempersempit lokasi blur
function testWindowScroll 
clear all; 
clc; 
f = figure('WindowScrollWheelFcn',@sliderHandle,'menubar', 'none' ); 
ax = axes('parent', f, 'units', 'pix' ); 
im = (imread('gambar 1/lion-test.jpg')); % disesuaikan 

imshow(im,'parent', ax); 

sizeImage = size(im);
w = sizeImage(2); 
h = sizeImage(1); 
halfW = double(w)/2; 
halfH = double(h)/2;
maxD = min(w,h)/2; 

initD = 100; 

blurImage(initD); 

    function sliderHandle(src, event)
        m = get(0, 'PointerLocation'); 
        xMouse = m(1); 
        yMouse = m(2); 
        
        posFig = get(f, 'position'); 
        xFig = posFig(1); 
        yFig = posFig(2); 
        widthFig = posFig(3); 
        heightFig = posFig(4); 
        
        if (xMouse > xFig ) && (yMouse > yFig ) && ... 
                (xMouse < xFig + widthFig ) && ... 
                (yMouse < yFig + heightFig )
            if event.VerticalScrollCount < 0  % up 
                initD = initD + 3; 
            elseif event.VerticalScrollCount > 0 % down 
                if initD - 3 > 0 
                   initD = initD - 3;
                end
            end     
            blurImage(initD);
        end
    end

    function blurImage(d)
        if d < maxD
            [xx,yy] = ndgrid(( 1:h )-halfH , ( 1:w ) - halfW ) ; 
            mask = uint8( ( xx.^2 + yy.^2 ) > d^2);
            G = fspecial('disk', 10); 
            cropped = uint8(zeros(sizeImage)); 
            cropped(:,:,1) = roifilt2(G ,im(:,:,1) , mask ); 
            cropped(:,:,2) = roifilt2(G ,im(:,:,2) , mask ); 
            cropped(:,:,3) = roifilt2(G ,im(:,:,3) , mask ); 
            imshow( cropped );  
            drawnow; 
        end
    end
end 
Di mana video implementasinya di komputer saya adalah sebagai berikut

Friday, March 20, 2015

Cara Sorting Cell di MATLAB

Berikut ini adalah contoh bagaimana men-sorting cell di MATLAB:
clear all; 
clc; 
% test sorting dari cell
M = cell(4,2); 
M{1,1} = 'mawar'; 
M{1,2} = 23;
M{2,1} = 'Anggrek' ;
M{2,2} = 11; 
M{3,1} = 'Durian'; 
M{3,2} = 45; 
M{4,1} = 'Benalu'; 
M{4,2} = 222;

% sorting berdasarkan jarak
N = sortrows(M,2);
disp(M); 
disp(N);


% output 
%     'mawar'      [ 23]
%     'Anggrek'    [ 11]
%     'Durian'     [ 45]
%     'Benalu'     [222]
% 
%     'Anggrek'    [ 11]
%     'mawar'      [ 23]
%     'Durian'     [ 45]
%     'Benalu'     [222]

Monday, February 23, 2015

Memahami Epicycle

Dahulu kala, sekitar 1000 tahun sebelum masehi orang-orang masih berpatokan pada konsep geosentris yakni memposisikan bumi sebagai pusat alam semesta. Data-data pergerakan planet pada waktu itu (yang diamati melalui teleskop) sudah banyak tersedia. Ini kemudian coba dimodelkan untuk mencari tahu bagaimana lintasan planet-planet relatif terhadap bumi sebagai pusat tata surya (alam semesta). Namun ternyata data-data tersebut jika ditafsirkan dengan konsep geosentris, akan terlihat bahwa gerakan planet-planet yang diketahui pada saat itu, tampak sangat tidak beraturan. Dan ini sangat bertentangan dengan teologi bahwa alam semesta diciptakan oleh Tuhan yang menyukai keteraturan.

Datang lah seorang Ptolemeus yang memberikan sebuah penjelasan bahwa lintasan planet-planet tersebut dapat dipandang sebagai lintasan titik dalam dalam geometri epicycle. Ini mampu menjelaskan keteraturannya yang diasumsikan berdasarkan kehendak Tuhan. Ptolemeus dan ilmuan-ilmuan sesudahnya kemudian berasumsi bahwa Tuhan sangat menyukai bentuk lingkaran.

Pada tulisan selanjutnya saya akan membuktikan bahwa dengan epicycle, kita bukan hanya bisa men-track lintasan planet dengan konsep geosentris, akan tetapi lintasan apa saja yang berbentuk kurva tertutup. Ini tidak lain merupakan implikasi dari analisis fourier yang secara kasar menegaskan bahwa semua fungsi perodik bisa dinyatakan sebagai penjumlahan dari fungsi-fungsi sinusoidal

Saya punya project di github tentang bagaimana epicycle dapat menghasilkan sebuah lintasan yang tidak teratur namun tetap berbentuk kurva tertutup.

Tuesday, December 23, 2014

Plot Fungsi Gaussian Dengan MATLAB

Fungsi Gaussian didefenisikan oleh persamaan
\begin{equation} f(x) = a \exp \left ( - \frac{(x - b )^2 }{ 2 \, c^2 } \right ) \label{pers1} \end{equation} Dengan $a$, $b$, dan $c$ konstanta real sembarang. Untuk memahami karakteristik persamaan \ref{pers1} di atas, Anda dapat mencoba potongan script berikut
clear all; 
clc; 
% test sorting dari cell
M = cell(4,2); 
M{1,1} = 'mawar'; 
M{1,2} = 23;
M{2,1} = 'Anggrek' ;
M{2,2} = 11; 
M{3,1} = 'Durian'; 
M{3,2} = 45; 
M{4,1} = 'Benalu'; 
M{4,2} = 222;

% sorting berdasarkan jarak
N = sortrows(M,2);
disp(M); 
disp(N);


% output 
%     'mawar'      [ 23]
%     'Anggrek'    [ 11]
%     'Durian'     [ 45]
%     'Benalu'     [222]
% 
%     'Anggrek'    [ 11]
%     'mawar'      [ 23]
%     'Durian'     [ 45]
%     'Benalu'     [222]

Friday, December 19, 2014

Perkalian matriks secara parallel

Pada posting ini, saya akan menyajikan kelanjutan posting saya sebelumnya, yakni posting mengenai perkalian matriks secara rekursi.  Namun dalam posting ini saya akan menekankan pada aspek manfaatnya.

Orang-orang bisa berkata buat apa kita melakukan rekursi jika hasilnya sama saja? Jawabnya ada di posting kali ini.

Dalam rilis terbaru  bahasa Java yakni Java 7 telah diperkenalkan konsep paralellisme dengan cara yang lebih modern dibandingkan sebelumnya. Yakni dengan memasukkan beberapa class baru ke dalam Java Standard Library yang khusus untuk menangani keperluan parallelisme. Dua class yang dimaksud adalah RecursiveAction dan RecursiveTask. Ada sebuah posting menarik dari oracle yang membahas mengenai masalah ini.

Pada kesempatan kali ini saya akan mengekspose fitur yang disajikan class yang dimaksud untuk keperluan meningkatkan efisiensi dalam perkalian matriks. Mekanismenya sudah dijelaskan pada posting saya sebelumnya (tentang perkalian matriks secara rekursi). Hanya dalam posting ini, matriks C yang sudah dipecah ke dalam sub-sub-sub… matriks C, elemennya di-isi secara parallel. Akibatnya adalah waktu eksekusi bisa berkurang hingga setengahnya. Jadi ketika secara normal dia membutuhkan waktu 10 detik, maka dengan parallelisme dia hanya membutuhkan waktu 5 detik. Saya sudah memposting di github project saya tersebut, dan Anda bisa mencobanya di rumah. Mudah-mudahan hasilnya tidak beda  jauh dari apa yang saya peroleh.

Dalam hasil percobaan saya, dilakukan perkalian dua buah matriks yakni matriks A dengan ukuran (1000,600) dan matriks B dengan ukuran (600,9000) di mana waktu eksekusi dengan cara tradisional membutuhkan waktu 143 detik, sementara waktu eksekusi dengan cara parallel membutuhkan waktu 73 detik.

Wednesday, December 17, 2014

Perkalian Matriks Secara Rekursi

Dalam kesempatan kali ini saya akan memberikan sedikit pencerahan tentang bagaimana mengalikan matriks secara rekursi.

Di dalam dunia pemrograman, rekursi didefenisikan sebagai pemecahan masalah dengan cara membaginya ke dalam sub bagian yang lebih kecil. Hal ini ditandai dengan adanya panggilan terhadap fungsi/method dari dalam method/fungsi itu sendiri.

Kali ini saya akan memberikan contoh bagaimana operasi perkalian matriks bisa dilakukan secara rekursi. Artinya ketimbang melakukan iterasi satu per satu terhadap semua elemen matriks untuk memperoleh hasil akhir. Maka dengan rekursi, kita melakukan perkalian secara simultan terhadap kelompok-kelompok elemen dari matriks tersebut.

Metodenya adalah dengan membagi matriks ke dalam 4 kuadran,  yang masing-masing bagian akan dibagi lagi menjadi 4 kuadran secara berkelanjutan, hingga dijumpai base case. Pada base case ini baru kemudian dilakukan perhitungan secara langsung. Base-case diperlukan dalam rekursi, karena jika tidak ada base-case, maka akan terjadi pemanggilan berulang-ulang tanpa henti yang berakibat alokasi berlebihan terhadap memori stack pada komputer. Dan ujung-ujungnya terjadi stack-overflow.

Prinsip kerjanya adalah, ketika matriks A dengan dimensi (m,n) dengan m menyatakan jumlah baris dan n jumlah kolom dikalikan dengan matriks B dengan dimensi (o,p), maka matriks hasil akan berdimensi (m,p). Jadi yang kita lakukan adalah menerapkan rekursi dalam pengisian elemen dari matriks hasil tadi (katakanlah matriks C).  Perhatikan potongan script berikut

	
public void multiply(int barisA, int kolomB) {
int sum = 0;
for (int k = 0; k < dimensiTengah; k++) {
sum += matriksA[barisA][k] * matriksB[k][kolomB];
}
matriksC[barisA][kolomB] = sum;
}
Dalam potongan script di atas, kita melakukan pengisian elemen ke (i,j) pada matriks C dengan mengalikan elemen pada baris ke-i matriks A dengan kolom ke-j matriks B lantas menjumlahkannya. Di sini kuncinya! Jadi dengan melakukan rekursi terhadap baris dan kolom matriks C  maka kita tidak perlu lagi pusing memikirkan bagaimana mengkondisikan elemen matriks B dan elemen dari matriks A untuk keperluan perkalian matriks secara rekursi. Yang perlu kita pikirkan hanya proses pengisian elemen dari matriks C tadi.

Kita akan melakukan proses pengisian tersebut dengan membagi matriks C ke dalam 4 kuadran secara rekursi sampai diperoleh base case. Base case dijumpai ketika jumlah baris serta jumlah kolom dari sub matriks C---matriks yang diperoleh dari pemecahan matriks C ke dalam 4 kuadran tadi (walaupun hakikatnya dalam memori hanya matriks C juga)---telah melampaui ambang tertentu. Perhatikan potongan script berikut.

public void compute() {

int limit = 10;

int limitKolom = (int) (Math.floor( ((awalKolom - akhirKolom) / 2)));

int limitBaris = (int) (Math.floor( ((akhirBaris - awalBaris) / 2)));

int halfBaris = awalBaris + limitBaris;

int halfKolom = awalKolom + limitKolom;


if (limitKolom <= limit && limitBaris <= limit) {

computeDirectly(awalBaris, akhirBaris, awalKolom, akhirKolom);

return;

} else if (limitBaris <= limit && limitKolom > limit) { // split over column

RecursiveMatrixMultiplication p2 = new RecursiveMatrixMultiplication(matriksA, matriksB,
matriksC, dimensiTengah, awalBaris, akhirBaris, awalKolom,
halfKolom);

RecursiveMatrixMultiplication p4 = new RecursiveMatrixMultiplication(matriksA, matriksB,
matriksC, dimensiTengah, awalBaris, akhirBaris, halfKolom,
akhirKolom);

invokeAll(p2, p4);

} else if (limitKolom <= limit && limitBaris > limit) { // split over
// row

RecursiveMatrixMultiplication p1 = new RecursiveMatrixMultiplication(
matriksA, matriksB,matriksC,
dimensiTengah,
awalBaris, halfBaris,
awalKolom, akhirKolom);

RecursiveMatrixMultiplication p3 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC,
dimensiTengah,
halfBaris, akhirBaris,
awalKolom, akhirKolom);

invokeAll(p1, p3);

} else if (limitKolom > limit && limitBaris > limit) { // split to 4 quadran

RecursiveMatrixMultiplication p1 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC, dimensiTengah,
awalBaris, halfBaris,
awalKolom, halfKolom); // kuadran 1

RecursiveMatrixMultiplication p2 = new RecursiveMatrixMultiplication(
matriksA, matriksB,matriksC, dimensiTengah,
halfBaris, akhirBaris,
awalKolom, halfKolom); // kuadran 2

RecursiveMatrixMultiplication p3 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC , dimensiTengah,
awalBaris, halfBaris,
halfKolom, akhirKolom); // kuadran 3

RecursiveMatrixMultiplication p4 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC, dimensiTengah,
halfBaris, akhirBaris,
halfKolom, akhirKolom); // kuadran 4

invokeAll(p1, p2, p3, p4);

}

}

Dalam potongan di atas, ketika jumlah baris dari sub-matriks C dan jumlah kolomnya masih lebih besar dari ambang, maka bagi lagi secara rekursi menjadi sub-sub matriks berikutnya (dalam 4 kuadran). Ketika jumlah baris masih lebih besar dari ambang, bagi sub-matriks C ke dalam dua sub-matriks berikutnya dalam arah horizontal  (bagi baris-barisnya ke dalam dua bagian). Sementara jika jumlah kolomnya masih lebih besar dari ambang, maka bagi sub-matriks C dalam arah vertikal (bagi kolom-kolomnya menjadi dua bagian).

Ketika dijumpai base-case, yakni ketika jumlah kolom dan jumlah baris dari sub-sub-sub-sub… matrik C lebih kecil dari nilai ambang, maka lakukan perkalian elemen-elemen matriks A dan matriks B untuk mengisi elemen-elemen dari sub-sub-sub… matriks C tersebut.

Secara keseluruhan kode yang digunakan adalah sebagai berikut (di dalamnya terdapat juga method untuk mengecek kevalidan operasi yakni dengan membandingkannya dengan perkalian secara konvensional, tanpa rekursi).  

package algo.test.multiply;

import java.util.Random;

public class RecursiveMatrixMultiplication {

int[][] matriksA;
int[][] matriksB;
int[][] matriksC;

int awalBaris;
int awalKolom;
int akhirBaris;
int akhirKolom;

int dimensiTengah;

public RecursiveMatrixMultiplication(int[][] matriksA, int[][] matriksB,
int[][] matriksC, int dimensiTengah, int awalBaris, int akhirBaris,
int awalKolom, int akhirKolom) {

this.matriksA = matriksA;
this.matriksB = matriksB;
this.matriksC = matriksC;

this.awalBaris = awalBaris;
this.awalKolom = awalKolom;
this.akhirBaris = akhirBaris;
this.akhirKolom = akhirKolom;

this.dimensiTengah = dimensiTengah;
}

public void multiply(int barisA, int kolomB) {
int sum = 0;
for (int k = 0; k < dimensiTengah; k++) {
sum += matriksA[barisA][k] * matriksB[k][kolomB];
}
matriksC[barisA][kolomB] = sum;
}


private void computeDirectly(int awalBaris, int akhirBaris, int awalKolom,
int akhirKolom) {
for (int i = awalBaris; i < akhirBaris; i++) {
for (int j = awalKolom; j < akhirKolom; j++) {
multiply(i, j);
}
}
}


public void invokeAll(RecursiveMatrixMultiplication...matrixMultiplications ){
for(RecursiveMatrixMultiplication m: matrixMultiplications){
m.compute();
}
}

public void invoke(){
invokeAll(this);
}

public void compute() {

int limit = 10;

int limitKolom = (int) (Math.floor( ((awalKolom - akhirKolom) / 2)));

int limitBaris = (int) (Math.floor( ((akhirBaris - awalBaris) / 2)));

int halfBaris = awalBaris + limitBaris;

int halfKolom = awalKolom + limitKolom;


if (limitKolom <= limit && limitBaris <= limit) {

computeDirectly(awalBaris, akhirBaris, awalKolom, akhirKolom);

return;

} else if (limitBaris <= limit && limitKolom > limit) { // split over column

RecursiveMatrixMultiplication p2 = new RecursiveMatrixMultiplication(matriksA, matriksB,
matriksC, dimensiTengah, awalBaris, akhirBaris, awalKolom,
halfKolom);

RecursiveMatrixMultiplication p4 = new RecursiveMatrixMultiplication(matriksA, matriksB,
matriksC, dimensiTengah, awalBaris, akhirBaris, halfKolom,
akhirKolom);

invokeAll(p2, p4);

} else if (limitKolom <= limit && limitBaris > limit) { // split over
// row

RecursiveMatrixMultiplication p1 = new RecursiveMatrixMultiplication(
matriksA, matriksB,matriksC,
dimensiTengah,
awalBaris, halfBaris,
awalKolom, akhirKolom);

RecursiveMatrixMultiplication p3 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC,
dimensiTengah,
halfBaris, akhirBaris,
awalKolom, akhirKolom);

invokeAll(p1, p3);

} else if (limitKolom > limit && limitBaris > limit) { // split to 4 quadran

RecursiveMatrixMultiplication p1 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC, dimensiTengah,
awalBaris, halfBaris,
awalKolom, halfKolom); // kuadran 1

RecursiveMatrixMultiplication p2 = new RecursiveMatrixMultiplication(
matriksA, matriksB,matriksC, dimensiTengah,
halfBaris, akhirBaris,
awalKolom, halfKolom); // kuadran 2

RecursiveMatrixMultiplication p3 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC , dimensiTengah,
awalBaris, halfBaris,
halfKolom, akhirKolom); // kuadran 3

RecursiveMatrixMultiplication p4 = new RecursiveMatrixMultiplication(
matriksA, matriksB, matriksC, dimensiTengah,
halfBaris, akhirBaris,
halfKolom, akhirKolom); // kuadran 4

invokeAll(p1, p2, p3, p4);

}

}


/*
* bandingkan matriksA dan matriksB..
* jika semua elemennya sama, maka kembalikan matriksC yang isinya semua 1...
* jika ada elemen yang beda isi elemen matriksC dengan 0
*/
public static int [][] compareMatriks(int [][] matriksA, int[][] matriksB){
int C[][] = new int [matriksA.length][matriksA[0].length];
for( int i=0; i < matriksA.length ;i++){
for(int j=0; j < matriksA[0].length ;j++){
if(matriksA[i][j] == matriksB[i][j]){
C[i][j] = 1;
}else{
C[i][j] = 0;
}
}
}
return C;
}


/*
* sebagai pembanding untuk mengetahui operasi berjalan dengan benar..
* yakni lakukan perkalian secara Langsung.. tanpa rekursive...
*/
public static int[][] multiplyDirectly(int [][] matriksA, int[][] matriksB){
int result[][] = new int[matriksA.length][matriksB[0].length];
for(int i= 0; i< matriksA.length ;i++){
for(int j=0; j< matriksB[0].length ;j++){
int sum = 0;
for( int k = 0 ; k < matriksB.length ;k ++){
sum += matriksA[i][k] * matriksB[k][j];
}
result[i][j] = sum;
}
}
return result;
}

/*
* generate random matriks
*/
public static int [][] generateMatriks(int baris, int kolom){
int matriks[][] = new int[baris][kolom];
Random rn = new Random();
for(int i=0; i < matriks.length ; i++){
for( int j=0; j < matriks[0].length ;j++){
matriks[i][j] = rn.nextInt(100);
}
}
return matriks ;
}


/*
* pretty print matrix
*/
public static void printMatriks(int [][] matriks) {
for (int i = 0; i < matriks.length; i++) {
for (int j = 0; j < matriks[0].length; j++) {
System.out.print(matriks[i][j] + "|");
}
System.out.print("\n");
}
System.out.print("\n");
}


public int[][] getResult() {
return matriksC;
}


public static void main(String[] args){


int[][] matriksA = generateMatriks(300, 200);
int[][] matriksB = generateMatriks(200, 500);

long now = System.nanoTime();

int[][] matriksResultA = multiplyDirectly(matriksA, matriksB);

now = (System.nanoTime() - now) / 1000000;

long now1 = System.nanoTime();

int[][] matriksC = new int[matriksA.length][matriksB[0].length];

RecursiveMatrixMultiplication parallel = new RecursiveMatrixMultiplication(matriksA, matriksB, matriksC,
matriksB.length, 0 , matriksC.length, 0, matriksC[0].length);

parallel.invoke();

int[][] matriksResultB = parallel.getResult();

now1 = (System.nanoTime() - now1) / 1000000;

int[][] hasilCompare = compareMatriks(matriksResultA, matriksResultB);

printMatriks(hasilCompare);

System.out.println(now);
System.out.println(now1);
System.out.println("FINISH");

}


}

Sunday, December 14, 2014

Penjelasan Algoritma Genetik

Sebenarnya kalo membahas algoritma genetik (GA), maka ini semata-mata persoalan bagaimana mereduksi waktu dalam pencarian solusi dari persoalan optimisasi. Jadi ketimbang kita melakukan brute-force terhadap semua kemungkinan pemecahan masalah (yang menghasilkan nilai optimal) yang tentunya membutuhkan waktu yang sangat lama, yakni O(n!) dengan n menyatakan jumlah input, maka dengan algoritma genetik kita membatasi solusi persoalannya dengan menerapkan metode yang analog dengan apa yang diterapkan oleh alam terhadap makhluk hidup. Solusi yang diberikan oleh algoritma genetik bisa jadi bukan solusi yang benar-benar optimal, namun solusi ini dirasa sudah cukup  untuk keperluan praktis. Artinya ketimbang kita menghabiskan waktu untuk sekian iterasi hanya untuk mencari tahu semua kemungkinan dari solusi, maka dengan algoritma genetik, kita hanya mengambil sebagian dari seluruh kemungkinan namun tetap dengan tujuan mencari nilai optimal. Terdapat 5 bagian utama dari algoritma genetik yakni 1) pemilihan populasi awal, 2) Evaluasi nilai “fitness”, 3) seleksi, 4) crossover, dan 5) mutasi. Populasi awal dipilih secara random dari ruang sampel yang memenuhi solusi persoalan. Evaluasi kemudian diterapkan pada solusi untuk kemudian dikenakan pembobotan berdasarkan tujuan optimisasi (maksimum atau minimum). Seleksi dilakukan untuk memilih mana dari populasi yang layak masuk pada tahap iterasi selanjutnya (dalam terminologinya disebut sebagai generasi berikutnya) dan mana yang dibuang/disingkirkan.  Crossover merupakan pemilihan populasi generasi berikutnya (solusi anak) berdasarkan pertukaran informasi antara populasi sebelumnya  yang melewati tahap seleksi (solusi induk)---karena jumlah populasi harus tetap sementara sebagian sudah disingkirkan dalam tahap seleksi. Sementara mutasi merupakan perubahan informasi pada solusi (solusi anak) secara acak untuk menjaga diversitas dari solusi.

Saya akan memberikan contoh kasus kegunaan algoritma genetik dalam memecahkan persoalan optimisasi traveling salesman. Pernyataan traveling salesman adalah terdapat kumpulan kota yang berjarak tertentu satu sama lain. Dan kita diharuskan untuk mengunjungi seluruh kota-kota tersebut satu persatu di mana satu kota hanya boleh dikunjungi sekali. Kemudian dihitung rute mana yang menghasilkan jarak tempuh yang paling singkat. Dengan metode brute force (mencari semua kemungkinan) tentu saja akan sangat tidak efisien. Misalnya saja ketika jumlah kota nya ada 4, maka ada 24 kemungkinan rute yang bisa ditempuh, dan kita harus mencari tahu jarak pada semua rute tersebut. Bagaimana kalo kotanya ada 100, kan tidak mungkin.

Untuk implementasi algoritma genetik dari kasus ini sudah diberikan oleh sebuah blog yang beralamat di sini. Jadi saya tidak perlu mengulangi apa yang disampaikan pada blog tersebut. Intinya adalah terdapat kota yang harus dikunjungi dengan peta sebagai berikut:

map1

Source dari blog tersebut juga sudah saya host di github.
Dari keseluruhan source code tersebut, saya akan memberikan sedikit penjelasan mengenai mana bagian yang paling penting atau yang menjadi bagian inti dari algoritma genetik dalam menyelesaikan persoalan tersebut. Yakni dapat dilihat pada potongan script berikut


package com.fjr.genetic.main;

public class GA {

    /* GA parameters */
    private static final double mutationRate = 0.015;
    private static final int tournamentSize = 5;
    private static final boolean elitism = true;

    // Evolves a population over one generation
    public static Population evolvePopulation(Population pop) {
        Population newPopulation = new Population(pop.populationSize(), false);

        // Keep our best individual if elitism is enabled
        int elitismOffset = 0;
        if (elitism) {
            newPopulation.saveTour(0, pop.getFittest());
            elitismOffset = 1;
        }

        // Crossover population
        // Loop over the new population's size and create individuals from
        // Current population
        for (int i = elitismOffset; i < newPopulation.populationSize(); i++) {
            // Select parents
            Tour parent1 = tournamentSelection(pop);    // bagian 1
            Tour parent2 = tournamentSelection(pop);
            // Crossover parents
            Tour child = crossover(parent1, parent2);
            // Add child to new population
            newPopulation.saveTour(i, child);
        }

        // Mutate the new population a bit to add some new genetic material
        for (int i = elitismOffset; i < newPopulation.populationSize(); i++) {
            mutate(newPopulation.getTour(i));    // bagian 2
        }

        return newPopulation;
    }

    // Applies crossover to a set of parents and creates offspring
    public static Tour crossover(Tour parent1, Tour parent2) {
        // Create new child tour
        Tour child = new Tour();

        // Get start and end sub tour positions for parent1's tour
        int startPos = (int) (Math.random() * parent1.tourSize());     // bagian 3
        int endPos = (int) (Math.random() * parent1.tourSize());

        // Loop and add the sub tour from parent1 to our child
        for (int i = 0; i < child.tourSize(); i++) {       // bagian 4
            // If our start position is less than the end position
            if (startPos < endPos && i > startPos && i < endPos) {
                child.setCity(i, parent1.getCity(i));
            } // If our start position is larger
            else if (startPos > endPos) {
                if (!(i < startPos && i > endPos)) {
                    child.setCity(i, parent1.getCity(i));
                }
            }
        }

        // Loop through parent2's city tour
        for (int i = 0; i < parent2.tourSize(); i++) {        // bagian 5
            // If child doesn't have the city add it
            if (!child.containsCity(parent2.getCity(i))) {
                // Loop to find a spare position in the child's tour
                for (int ii = 0; ii < child.tourSize(); ii++) {
                    // Spare position found, add city
                    if (child.getCity(ii) == null) {
                        child.setCity(ii, parent2.getCity(i));
                        break;
                    }
                }
            }
        }
        return child;
    }

    // Mutate a tour using swap mutation
    private static void mutate(Tour tour) {       //bagian 6
        // Loop through tour cities
        for(int tourPos1=0; tourPos1 < tour.tourSize(); tourPos1++){
            // Apply mutation rate
            if(Math.random() < mutationRate){
                // Get a second random position in the tour
                int tourPos2 = (int) (tour.tourSize() * Math.random());

                // Get the cities at target position in tour
                City city1 = tour.getCity(tourPos1);
                City city2 = tour.getCity(tourPos2);

                // Swap them around
                tour.setCity(tourPos2, city1);
                tour.setCity(tourPos1, city2);
            }
        }
    }

    // Selects candidate tour for crossover
    private static Tour tournamentSelection(Population pop) {     // bagian 7
        // Create a tournament population
        Population tournament = new Population(tournamentSize, false);
        // For each place in the tournament get a random candidate tour and
        // add it
        for (int i = 0; i < tournamentSize; i++) {
            int randomId = (int) (Math.random() * pop.populationSize());
            tournament.saveTour(i, pop.getTour(randomId));
        }
        // Get the fittest tour
        Tour fittest = tournament.getFittest();
        return fittest;
    }
}

Pada keterangan yang ditandai dengan bagian 1 dari potongan script di atas, akan dilakukan pemilihan 5 anggota populasi secara acak dari seluruh populasi awal  untuk diseleksi. Seleksi kemudian akan mencari rute yang memiliki jarak tempuh paling singkat. Hasilnya kemudian akan dilakukan penerapan operasi crossover yang akan menghasilkan anggota populasi anak untuk dimasukkan ke dalam populasi dari generasi berikutnya. Hal ini di-loop untuk seluruh total populasi.  Bagian 2 menandakan operasi mutasi terhadap seluruh anggota populasi baru generasi berikutnya (populasi anak). Bagian 3 merupakan sub-bagian dari operasi crossover. Di mana akan dipilih secara acak pada bagian string/list/kromosom dari anggota populasi induk (yakni dua anggota populasi induk) yang ditandai sebagai bagian awal (start) dan bagian akhir (end) dari kromosom untuk diterapkan operasi crossover. Untuk bagian elemen string/list/kromosom ke-i dari rute yang masuk dalam rentang start sampai ke end dengan start < end, maka set element ke-i dari kromosom anak  sebagai elemen dari kromosom  rute induk 1. Jika end < start, maka set elemen ke-i dari kromosom anak sebagai  bagian dari kromosom induk 1 jika nilai i tersebut berada di luar rentang start sampai end. Bagian 5 menandakan operasi untuk mengisi elemen dari kromosom anak dengan mengambil dari element kromosom rute induk 2. Yakni dengan mengisi bagian yang belum di-isi oleh elemen dari rute induk 1

Bagian 6 merupakan implementasi dari operasi mutasi di mana akan akan diadakan pertukaran letak kota dalam kromosom rute jika bilangan random yang dihasilkan melampaui nilai laju mutasi. Bagian 7 menandakan pemilihan rute-rute dari populasi induk yang diterapkan operasi crossover. Di mana yang dikeluarkan adalah rute yang memiliki jarak tempuh paling singkat (highest fitness).

Jika Anda melakukan tes pada source code yang diberikan tersebut, maka akan didapati bahwa nilai optimal itu bergantung pada pemilihan populasi awal. Jadi pendekatan algoritma genetik sama sekali tidak mencari mana solusi eksak (urutan kota dalam rute yang menghasilkan nilai jarak tempuh paling singkat), akan tetapi hanya mencari solusi yang sekedar bisa mereduksi jarak tempuh.

Friday, December 12, 2014

Asistensi TA dengan git version control (serta latex)

Biasanya sebagai mahasiswa semester terkahir, kita pasti dihadapkan pada kewajiban penyusunan laporan tugas akhir berupa skripsi atau tesis. Di mana dalam penyusunan laporan ini  tidak serta merta apa yang kita susun  tersebut langsung bisa di-approve oleh dosen pembimbing dalam sekali asistensi. Dengan demikian mau tidak mau kita harus mengeditnya berulang-ulang.  Saya sebagai seorang saintis yang jam terbangnya banyak bergelut dengan studi ilmu  komputer serta komputasi  (walaupun masih dalam tahap amatir) punya cara tersendiri untuk mengakali situasi ini. Cara yang saya pakai tentu sudah sangat lumrah dijumpai pada bidang ini. Namun bagi Anda yang mungkin juga memiliki minat yang tinggi dalam mempelajari apapun, maka saya rasa ini sama sekali bukan hal yang terlalu ribet untuk dicoba.

Teknik yang saya maksud adalah teknik version control dengan menggunakan software git (tersedia untuk windows) serta editor latex (juga tersedia untuk windows). Soal latex sendiri, kebetulan saya ini seorang fisikawan, maka apa yang saya tulis biasanya banyak mengenai penurunan persamaan. Saya punya contoh dokument yang bisa di-download mengenai contoh kasus di mana latex itu “the only way” untuk menulis dokument, yakni

https://drive.google.com/file/d/0B1irLqfPwjq0V0VoQllWR3FTUkE/view?usp=sharing

Jika Anda bisa menghasilkan dokument yang sama (beserta nomor persamaannya di Microsoft Word atau yang lain) saya akan isikan pulsa 20 ribu. Tapi dalam tulisan ini saya tidak lagi mengajak Anda untuk memakai latex, itu pilihan Anda sepenuhnya. Dan nyatanya git  ini bisa juga digunakan pada semua jenis file. Dalam tulisan ini saya lebih menekankan penggunaan teknik version control. Di wikipedia sudah diberikan secara gamblang apa yang dimaksud dengan version control. Intinya dengan menggunakan version control itu kita bisa melacak history (baca:riwayat) dari dokument.

Contoh kasus: saya sudah menyelesaikan TA, dan akan melakukan asistensi pertama kali kepada dosen pembimbing. Oleh dosen pembimbing, dalam asistensi pertama itu saya disuruhnya untuk menambahkan ini itu. Kemudian saya tambahkan lah apa yang diperintahkan, dan kemudian saya asistensi lagi untuk kedua kalinya. Pada asistensi kedua, ternyata si dosen mood nya berubah. Dia ingin agar isi laporan TA kita dirubah kembali ke situasi pertama tadi. Jadinya kita bingung. Maunya sih di-undo (pasangannya redo), tapi kan fasilitas ini hanya berlaku ketika komputer masih nyala, ketika komputer udah mati keadaan yang mau di undo tersebut sudah hilang.  Bisa juga sih kita membuat satu folder untuk satu kali asistensi (jadi untuk asistensi 1 foldernya lain, untuk asistensi 2 foldernya lain, dst). Namun ini bukan cara yang efisien. Bagaimana kalo dokument kita itu saling bergantung dengan beberapa file yang lain dan ukurannya besar, misalnya file gambar. Otomatis file-file tersebut juga kita copy-paste semuanya ke dalam folder yang baru (untuk asistensi 2, 3, dst). Dan untuk navigasi dari folder 1, folder 2, dll itu juga cukup merepotkan. 

Jadi untuk mengatasi masalah tersebut digunakanlah git sebagai version control. Dengan version control kita tidak melakukan teknik copy-paste file, akan tetapi yang di-lacak adalah perubahan dalam file tersebut. Jadi, alih-alih mengcopy-paste seluruh file word untuk asistensi kedua, git hanya mengcopy-paste kalimat yang kita tambahkan ke dalam file word tersebut (kata kasarnya seperti itu, walaupun implementasinya bukan seperti itu). 

Untuk mempelajari git, saya g membahas di tulisan ini, ada banyak contohnya di internet (tujuan dari tulisan ini lebih kepada meningkatkan rating blog ini sekaligus memberikan sedikit pencerahan kepada Anda). Saya punya sebuah buku yang bisa Anda download di internet yang jika Anda punya waktu 5 jam sehari selama seminggu (jika memang Anda tertarik membacanya), maka Anda bisa memahami git itu apaan:

http://en.bookfi.org/book/1046931

(saya juga g sampe selesai membaca buku itu)

Adapun  untuk artikel yang lebih simple dan langsung ke inti permasalahan, bisa Anda baca di sini:

http://git-scm.com/book/en/v2/Getting-Started-Git-Basics

Sementara jika Anda ingin menjadi ahli dalam git, maka ada banyak forum di internet yang membahas mengenai masalah-masalah yang dijumpai dalam menggunakan git, salah satunya di stack-overflow:

http://stackoverflow.com/questions/tagged/git?sort=newest&pageSize=15

Tuesday, November 25, 2014

Membuat Plot Berjalan Dengan MATLAB

Berikut ini saya berikan contoh program MATLAB untuk membuat plot berjalan.  Plot berjalan yang saya maksud adalah bagaimana grafik plotnya itu bergeser sepanjang waktu seiring dengan berubahnya nilai variabel-variabel yang di-plot.

Friday, November 21, 2014

Antara Java Swing (traditional) dan JavaFX FXML

Salah satu dari sekian banyak kegunaan Java reflection adalah untuk membuat GUI layout secara soft-code. Reflection dalam ilmu komputer didefenisikan sebagai kemampuan program komputer untuk menginspeksi, atau menginisialisasi atribut-atribut tertentu selama runtime. Ada sebuah diskusi menarik mengapa reflection tidak tersedia dalam bahasa semisal C++, yang salah satu point pentingnya adalah masalah optimisasi.

Sebenarnya di bahasa .NET sudah menggunakan teknik reflection ini dalam pembuatan GUI nya yakni dalam desain layout di Visual Studio, khususnya C# (walaupun saya g yakin apakah developer .NET mengerti bahwa itu dilakukan dengan bantuan reflection). Namun Java baru belakangan. Yakni dengan mengadopsi teknik yang sama dengan yang digunakan pada pengembangan layout aplikasi Android. Di Java Swing, telah dikenal namanya group-layout  untuk keperluan pembuatan desain GUI dengan menggunakan GUI builder. Salah satunya adalah  GUI builder yang terinstalasi secara default pada Netbeans. Namun ada banyak kesulitan dalam implementasinya. Karena code yang tergenerate itu sangat kompleks. Sehingga ketika diporting ke IDE yang lain, misalnya Eclipse  sudah sangat sulit untuk dipahami. Dan parahnya tidak ada kesepahaman antara developer Eclipe dan developer Netbeans dalam mendesain GUI builder. Netbeans dirancang dengan Java 100 persen. Sementara Eclipse dirancang dengan menerapkan sekian persen code native (JNI). Oleh karena itu, maka digunakan lah metode yang lebih brilian dengan bantuan reflection yang diimplementasi pada JavaFX scene builder.

Saya punya contoh project yang menjelaskan ini. 

Jadi dalam JavaFX itu, digunakan anotasi @FXML  untuk menandakan elemen yang di-injek dengan bantuan reflection selama runtime. Sementara properti-properti dari elemen tersebut diset pada file XML (atau dalam terminologi JavaFX disebut sebagai FXML). Nah, file XML ini yang kemudian diparsing (udah banyak library java untuk keperluan ini) untuk memperoleh nilai-nilai atribut tersebut. Ketika nilai-nilainya sudah diparsing, maka kemudian ditampung selama runtime (misalnya dalam sebuah List). Reflection kemudian menginisialisasi class dan kemudian mencari variabel-variabel yang ditandai dengan anotasi @FXML tadi. Kemudian mengeset nilai-nilai propertinya (misalnya panjang dan lebar dari sebuah button)  berdasarkan nilai yang sudah di-load dari file FXML. 

Keuntungannya adalah kita tidak perlu, memanggil seorang programmer hanya untuk mengganti desain layout dari aplikasi kita. Dan XML adalah universal format. Jadi semua developer aplikasi GUI builder tinggal melihat spesifikasi XML yang digunakan, dan mereka dapat membuat aplikasi GUI builder mereka sendiri.