2017년 12월 15일 금요일

외부에서 생성한 그물망(mesh) 정보를 매스매티카에서 불러올 수 없겠느냐는 의뢰를 받았다. 의뢰자가 보낸 그물망 파일을 살펴보니, 다음의 세 단락으로 구성되어 있다.

% Coordinates
8.9230056E-8             -1.1747189E-8            
8.737304E-8              -1.2470854E-8


% Elements (triangular)
2         3         5         
4         5         8  


% Data
0.0                      
0.0


각 노드의 좌표, 그 좌표로 구성된 삼각형 요소, 그리고 각 노드의 계산 값으로 구성된 세 단락으로 구성되어 있으며, 각 단락은 %로 시작하는 줄로 구분된 걸 알 수 있다. 이걸 어떻게 불러들여야 하나? 먼저, STL 형식으로 불러들이려고 했지만, 실패. STL 형식은 3차원 데이터만 다루도록 되어있나 본데, 이건 2차원 그물망이라서 그런가보다.

구글 검색에도 딱히 걸리는 게 없다. 조금 당황했지만, 다행히 Wolfram Documentation Center의 문서 중에서 쓸만한 예제를 찾았다. 먼저 검색해본 건, 실제 그물망 정보를 담고 있는 ElementMesh 함수이다. 이 함수는 Property가 많아서 직접 만들어주는 건 어렵겠다 싶어서 살펴보던 중에 찾은 건, ElementMesh를 생성해주는 ToElementMesh 함수이다. 드디어 이 함수의 예제 중에서 적합한 예제를 찾았다.



각 노드의 좌표와 삼각형 요소 정보만으로 ElementMesh를 만들어주니까, 필요한 정보는 다 있다. 그물망에 관한 함수를 다루려면 FEM 꾸러미를 불러와야 한다.


그럼, 이제 그물망 파일을 어떻게 불러들일 것인가? 다른 언어처럼 한 줄씩 읽어들여서 구문분석(parsing)을 돌리는 건 좀 Wolfram 언어답지 않고 말이지. 먼저 그물망 정보를 Table 형식으로 불러와 보자.


2차원 리스트로 잘 불러왔군. 다행히도 각 단락의 열 길이가 다르다는 걸 이용하면 쉽게 각 단락을 구분할 수 있다. 2열로 구성된 부분만 노드의 좌표를 나타내고 있으므로 {_,_} 패턴을 이용해서 해당 부분만 골라낼 수 있다.


여기서 단락 구분 겸 메타데이터로 쓰이는 “%”로 시작하는 줄을 간단히 빼버리는 방법도 있겠지만, 패턴 검사를 이용하면 조금 더 우아하게 처리할 수 있다. 열의 첫 번째 원소가 “%”인 것들은 빼고 골라내자.



삼각형 요소도 똑같은 방식으로 골라낼 수 있다. 삼각형 단락은 3열로 이루어져 있으므로, {_,_,_}패턴을 이용해서 골라낼 수 있다. 결과는 ToMeshElement의 “MeshElement” 옵션의 형식을 참고해서 결과 형식을 맞춰주면 된다.


모든 준비는 끝났고, 이재 ToElementMesh 함수를 이용해서 ElementMesh를 생성하면 된다.



ElementMesh는 제대로 생성이 되었다. 그물망의 품질이 떨어진다는 경고의 원인을 잘 모르겠지만, 아마도 그물망 좌표의 유효자리수가 문제가 아닐까 한다. 그럼, 매스매티카가 불러들인 그물망을 확인해보자.


내가 할 일은 여기까지인 것 같다. 결과를 송부하고 잠이나 자야겠다.


2014년 1월 17일 금요일

우리 아이의 첫 컴퓨터

아장아장 발걸음을 떼던 아이가 어느덧 이래저래 컴퓨터를 쓸 나이가 되었다. 여유만 있다면야 가볍고 성능 좋은 맥북에어를 사주겠지만, 우리 아이가 그 정도로 삼신할머니 뽑기 운이 좋진 않다 보니 다른 방도를 찾아주기로 했다. 아빠가 쿵작쿵작 손수 만들어준(?) 컴퓨터도 나름대로 정감이 있을 것 같아, 집 한구석에서 긴 잠에 빠져있는 있는 씽크패드 X31(이하 X31)을 살려보기로 했다.
ThinkPad X31 Image
LG-IBM에서 판매하던 씽크패드 X31
먼저 BIOS는 윈도에서 업그레이드하는 게 편하니, 버전을 확인해 보았다. 평소 소프트웨어 업데이트는 열심히 하는 편이라, Lenovo에서 제공하는 마지막 바이오스 버전(3.02,  QET97WW)이 올라가 있는 걸 확인했다.

아무래도 최신 OS를 돌리려면 현재 장착된 1GB의 메모리는 부족하겠다 싶어서, 최대 허용 메모리인 2GB까지 용량을 늘려주었다. 어댑터를 뽑으면 1초도 견디지 못하는 건전지는 더 큰 용량으로 교체해주 었다. CPU도 느린데, IO에서까지 느리면 정말 못 쓰겠다 싶어서 HDD도 SDD로 교체해주었다.

씽크패드 X31 하드웨어 업그레이드 명세
  • 메모리: 삼성전자 DDR 1G PC-2700 * 2, 47,000원
  • ODD: 삼성전자 SE-208DB, 42,000원
  • 건전지: BM-battery 5200mAh, 90,000원
  • SSD: 리뷰안테크 2.5인치 IDE 64GB, 199,000원
이것저것 사다 보니 거의 넷북을 새로 구매하는 비용을 지출했다. 생각보다 큰 지출이다. 영혼이 없는 넷북을 사주느니, 아빠의 손 때가 묻은 명기가 더 정감 있을 거라고 굳게 믿으며 우분투 설치로 넘어간다.
PAE는 32비트 컴퓨터에서 4GB 이상의 메모리를 쓸 수 있게 해주는 기능인데, 최신 우분투는 이 기능을 기본으로 탑재한 커널을 사용한다. 문제는 CPU가 PAE를 지원하지 못할 경우엔 설치가 안 된다는 점이다.

X31의 CPU는 PAE를 지원한다. 하지만 특이하게도 CPU 기능 목록에는 PAE 지원이 빠져있다. 즉, 커널이 CPU의 기능을 확인하면, 실제로는 PAE 기능을 지원함에도 불구하고 미지원으로 나온다. 우분투 최신 버전으로 설치를 해보면 CPU의 PAE 미지원으로 설치를 진행할 수 없다는 에러가 난다.

해결 방법은 non-PAE 커널이 들어있는 예전 설치 이미지로 최소 설치를 하고, CPU가 기능 지원 목록에 PAE 기능을 수동으로 넣어준 후, 최신 버전으로 업그레이드하는 것이다. 먼저, non-PAE 커널이 들어있는 다음의 설치 이미지로 설치를 완료한다. 처음부터 여러 꾸러미를 설치하면 판올림 시에 시간이 오래 걸리므로, 부팅만 될 정도로 최소한의 꾸러미만 설치한다.


설치가 끝나고 재부팅한 후, CPU의 기능 지원 목록에 PAE를 추가한다. 이제 최신 버전으로 판올림을 할 차례이다. 12.04는 LTS로 기본적으로 LTS로의 판올림만 하도록 설정되어 있고, 다음 LTS는 아직 출시되지 않았으므로, 현재 설정으로는 판올림할 수 없다. 이 설정을 수정한다.

한 번에 최신판으로 판올림하면 좋겠지만, 12.04부터 최신판 사이의 모든 판올림을 차례로 수행해야 한다. 다음 과정을 두 번 반복해서 13.04까지 판올림해준다. 13.10은 한글 입력에 문제가 있으므로, 추천하지 않는다.

13.04까지 무사히 판올림했다면, 아이가 쓸 노트북이니 에듀분투를 설치한다. 컴퓨터 사양을 고려하면, Lubuntu가 적합하지만, 아이도 예쁜 사용자 인터페이스를 쓸 권리가 있지 않은가?

많고 많은 꾸러미를 다 설치하고 재부팅을 한다. 기본 환경으로 로그인하니 검은 화면에 커서만 하나 딸랑 보인다. 유니티에 뭔가 버그가 있는 것 같다. 다행히도 Fallback 환경으로 로그인하니 데스크톱 환경을 제대로 뜨는데, 좀 많이 느리다.


비디오 가속

이것저것 만지작 거리다 보면 다른 그래픽 카드에 병목이 생기는 걸 느낄 수 있다. 우분투 기본 설정으론 비디오 가속이 되지 않아서 그렇다. 다음과 같이 수동으로 X윈도를 설정해 줘야 2D와 3D 가속을 활성화할 수 있다. 먼저, X.org의 설정 파일을 하나 생성한다.


현재 디렉터리에 현재 X윈도에 기반한 설정 파일이 Xorg.conf.new라는 파일로 생성된 것을 볼 수 있다. 이걸 다음과 같이 수정하여, /etc/X11/에 xorg.conf란 이름으로 복사한다.


그래픽 카드 오버클록

http://wiki.archlinux.org/index.php/IBM_ThinkPad_X31
비디오 카드의 작동 주파수를 높여서 그래픽 성능을 좀 더 끌어 올릴 수 있다. 다만, 이 설정은 영구적인 하드웨어 손상을 일으킬 수 있으니 주의해서 사용해야 한다. 다음과 같은 rovclock란 이름의 스크립트를 /etc/init.d/ 아래에 생성한다.



이 스크립트에 실행권한을 부여하고, 부팅 절차에 등록한다.


TRIM 설정

IO 속도 개선을 위해 SSD를 달았지만, 처음과 같이 빠른 속도를 유지하려면 TRIM 기능을 켜야 한다. /etc/fstab에 아래와 같이 discard를 추가하면, 컴퓨터가 틈틈이 TRIM 기능을 수행한다.



Suspend/Resume 문제

노트북의 LCD를 닫으면 초승달 모양의 LED에 불이 들어오면서, Suspend 상태로 잘 진입하는 걸 확인할 수 있다. 하지만 LCD를 다시 열면 검은 화면만 보이는 문제가 발생한다. 이 문제의 가장 쉬운 해결책은 /etc/default/grub을 아래와 같이 수정해서, 커널에 nomodeset 옵션을 보내주면 된다.


하지만 이렇게 하면, 콘솔 화면이 나오지 않는 문제와 그래픽 카드 가속을 이용할 수 없는 문제가 발생한다. 임시방편으로 Suspend/Hibernation 상태로의 진입을 막아 놓기로 했다. /usr/share/polkit-1/actions/org.freedesktop.upower.policy를 다음과 같이 수정한다.


동영상 재생

DVD 재생은 아이가 가장 많이 쓰는 기능이다. 그래서 외장 ODD도 심사숙고해서 골랐다. 아래 페이지를 참고해서 필요한 꾸러미를 설치해주면 기본적인 재생은 가능하다. 다만, 느리다. 우분투의 기본 재생 프로그램인 Totem은 X31에서 사용하기엔 너무 무거웠다. 다행하게도 VLC 미디어 재생기를 사용하면 전혀 무리 없이 DVD를 재생할 수 있다.

VLC의 로그는 왜 러버콘일까?

마치며

아이에게 컴퓨터를 전달해 주었는데, 시큰둥해한다. 내가 어렸을 땐, 컴퓨터를 갖게 됐을 때 그렇게 기뻤는데…. 새대 차일까? 오히려 쉽게 쓸 수 있으면서, 갖가지 게임이 들어있는 넥서스7을 더 좋아한다. 아빠가 고생해서 마련해 준 걸 아는지, 그래도 애써 좋은척하는 녀석이 귀엽긴 하다. 주말마다 아이 앉혀놓고, 컴퓨터 교육이나 해봐야겠다.

2013년 9월 5일 목요일

매스매티카 9.0.1에서 NVIDIA 그래픽카드 설정하기

CUDA 지원


우분투 13.04에 NVIDIA의 그래픽칩을 사용하는 그래픽카드가 설치되어 있고, NVIDIA의 독점 그래픽 드라이버가 설치 되어 있다고 가정한다. 매스매티카의 도움말을 따르면 이 단계에서 다음의 테스트를 통과해야 한다.
In[1]:= << CUDALink`
In[2]:= CUDAQ[]
Out[2]= False
하지만 실패! 인터넷을 뒤져보니, 이건 매스매티카가 CUDA 라이브러리를 찾지 못해서 발생하는 문제로 환경 변수 설정이 필요한 걸 알 수 있다. 내 경우엔 NVIDIA 그래픽 드라이버 버전이 319.37이므로 이에 해당하는 라이브러리를 찾아 환경변수에 등록해준다.
--- /etc/profile.d/Mathematica.sh.orig 2013-10-09 00:19:53.671638435 +0900
+++ /etc/profile.d/Mathematica.sh 2013-10-09 00:20:15.543638995 +0900
@@ -1,2 +1,4 @@
 #!/bin/sh
 export MATHEMATICA_HOME=/opt/Wolfram/Mathematica/9.0
+export NVIDIA_DRIVER_LIBRARY_PATH=/usr/lib/nvidia-current/libnvidia-tls.so.319.37
+export CUDA_LIBRARY_PATH=/usr/lib/nvidia-current/libcuda.so
이제 다시 로그인 한 후에 매스매티카를 실행해보면 위의 테스트를 깔끔히 통과한다.

OpenCL 지원


자신의 NVIDIA 칩이 OpenCL을 지원한다면, 매스매티카의 OpenCL 인터페이스도 사용할 수 있다. 지원 여부는 위키피디아에서 확인할 수 있다.
In[1]:= << OpenCLLink`
In[2]:= OpenCLQ[]
Out[2]= False
역시 실패! 여기에 따르면, 매스매티카가 엉뚱한 위치에서 OpenCL 라이브러리 찾기 때문이다. 더욱이 이 위치는 고정되어 바꿀 수 없다. 따라서 매스매티카가 찾는 위치에 맞추어서 링크를 만들어줘야 한다.
$ sudo mkdir -p /usr/lib64
$ sudo ln -s /usr/lib/nvidia-current/libOpenCL.so /usr/lib64/
이제 매스매티카 커널을 재시작하고 위 테스트를 실행해보면, 문제없이 통과!

CUDA 개발환경(Toolkit) 지원 컴파일러


매스매티카에서 제공하는 내장 CUDA 함수는 잘 동작한다. 하지만 외부 CUDA 함수를 매스매티카로 불러오면 gcc 4.7 이상은 지원하지 않는다는 에러가 발생한다.
In[2]:= code = "__global__ void addTwo(mint * in, mint * out, mint length) {
int index = threadIdx.x + blockIdx.x*blockDim.x;
if (index < length)          out[index] = in[index] + 2;  }";


In[3]:= cudaFun = CUDAFunctionLoad[code, "addTwo", {{_Integer, _, "Input"}, {_Integer, _,     "Output"}, _Integer}, 256]
During evaluation of In[3]:= CUDAFunctionLoad::cmperr: -- Message text not found -- (/home/kwchun/.Mathematica/Paclets/Repository/CUDAResources-Lin64-9.0.0.0/CUDAToolkit/bin/../include/host_config.h:82:2: error: #error -- unsupported GNU version! gcc 4.7 and up are not supported!) >>


Out[3]= CUDAFunctionLoad["__global__ void addTwo(mint * in, mint * out, mint length) {int index = threadIdx.x + blockIdx.x*blockDim.x;
if (index < length) out[index] = in[index] + 2;}", "addTwo", {{_Integer, _, "Input"}, {_Integer, _,    "Output"}, _Integer}, 256]
에러에 나와 있듯이 2013년 9월 5일 현재 매스매티카의 CUDA 개발환경(Toolkit) 버전은 9.0.0.0이고, 이 버전은 gcc 4.7 이상을 지원하지 않는다. 이 문제는 "XCompilerInstallation" 옵션으로 CUDA 개발환경이 지원하는 C 컴파일러의 경로를 지정하면 해결된다.
In[4]:= cudaFun = CUDAFunctionLoad[code, "addTwo", {{_Integer, _, "Input"}, {_Integer, _, "Output"}, _Integer}, 256, "XCompilerInstallation" -> "/usr/bin/gcc-4.6"]
Out[4]= CUDAFunction["<>", "addTwo", {{_Integer, _,    "Input"}, {_Integer, _, "Output"}, "Integer64"}]

OpenCL에서 배정도 부동소수점 사용


(아마도) 64비트 환경에서 OpenCL 외장 함수 내에 배정도 부동소수점을 사용하면 아래와 같이 함수를 불러드릴 수 없다.

code = "
  __kernel void addTwo(__global Real_t *in, __global Real_t *out, mint length) {
        int index =  get_global_id(0);
        if (index < length)
            out[index] = in[index] + 2;
    }";
openclFun = 
 OpenCLFunctionLoad[code, 
  "addTwo", {{_Real, _, "Input"}, {_Real, _, "Output"}, _Real}, 256, 
  "ShellOutputFunction" -> Print ]

OpenCLFunctionLoad::cmpf: The kernel compilation failed. Consider setting the option "ShellOutputFunction"->Print to display the compiler error message. >>

:2:31: error: must specify '#pragma OPENCL EXTENSION cl_khr_fp64: enable' before using 'double'
__kernel void addTwo(__global Real_t *in, __global Real_t *out, mint length) {
                              ^
In file included from <built-in>:17306:
<command line>:4:16: note: instantiated from:
#define Real_t double
               ^

이건 에러 메시지에도 나와 있듯이, OpenCL 관련 매크로를 코드의 앞부분에 넣어주면 해결된다.
code = "
  #ifdef USING_DOUBLE_PRECISIONQ
  #ifdef OPENCLLINK_USING_NVIDIA
  #pragma OPENCL EXTENSION cl_khr_fp64: enable
  #else /* OPENCLLINK_USING_NVIDIA */
  #pragma OPENCL EXTENSION cl_amd_fp64: enable
  #endif /* OPENCLLINK_USING_NVIDIA */
  #endif /* USING_DOUBLE_PRECISIONQ */

  __kernel void addTwo(__global Real_t *in, __global Real_t *out, mint length) {
    int index =  get_global_id(0);
    if (index < length)
      out[index] = in[index] + 2;
}";

2012년 6월 12일 화요일

블로그스팟에 조금 더 예쁜 코드 넣기

블로그의 템플릿이 동적뷰로 바꾸고 나니, google-code-prettify가 작동하지 않는다. google-code-prettify가 긴 줄의 표시도 매끄럽지 못하고, 매스매티카 문법도 인식하지 못해서 불편을 겪던 차여서 다른 걸 찾아보기로 했다. 뒤져보니 Alex Gorbatchev가 만든 SyntaxHighlighter란 게 있다. 설치방법은 Way2Blogging에 잘 나와 있고 적용도 잘 된다.

몇 가지 아쉬운 점도 있다.

  • 동적뷰는 html 수정을 하지 못하므로 SyntaxHighlighter도 역시 적용할 수 없다. 그래서 다시 예전 템플릿으로 회귀.
  • 홈페이지에 나온 것과 같이 함수를 써서 깔끔하게 html에 집어넣는 건 실패했다. 일일이 긴 주소를 다 써서 넣어야 한다. 다행히도 Way2Blogging에 스크립트 자동 생성기가 있다.
  • 매스매티카 브러시도 있긴 한데, 내부에 매스매티카 내부 함수 2,200개 정도를 표기해 놓았나 보다. 이것 때문에 브라우저가 느려질 수 있다고 한다. 이 브러시는 본 배포본에 빠져 있어서 추가하려면 별도로 서버에 올려야 한다. 이런 문제로 매스매티카 브러시는 쓰지 않기로 했다.

2012년 3월 14일 수요일

포트란만큼 빠른 C++ 벡터 라이브러리를 파이썬에서 사용하기

GMES의 기하 관련 모듈은 원래 파이썬으로 구현되어 있었는데, 커다란 3차원 구조를 다루기에는 속도가 너무 느렸다. 속도 개선을 위해서 기하 관련 부분을 C++로 포팅하기로 결심했다. 그리곤, 기하 루틴에 많이 들어 있는 벡터 관련 함수를 어떻게 구현해야 할지 고민에 빠졌다. 파이썬에선 벡터의 자료형으로 numpyndarray를 쓰면 되지만, C++에선 어떻게 구현하는 게 가장 좋을까? 시스템 호환성과 관리의 편리성을 고려해 널리 쓰이는 방법 중에서 선택할 수 있는 것들은 아래와 같다. 사용할 벡터는 실수를 원소로 갖는 3차원 벡터로 한정한다.
  1. double *v, int size
  2. std::array<double, 3>
  3. boost::numeric::ublas::bounded_array<double, 3>
  4. blitz::TinyVector<double, 3>
1번은 가장 쉽고 가장 빠른 방법이다. SWIG를 쓴다면, numpy.i를 이용해서 numpy와 연동하기도 쉽다. 하지만 벡터 연산을 일일이 직접 구현해야 하고, 함수의 인자 부분이 못생긴 형태가 되며, 왠지 C++답지 않은 방법이다.

2번은 표준 템플릿 라이브러리에 포함된 방법이며, 훨씬 C++스럽다. 하지만 1번처럼 벡터 관련 연산을 일일이 구현해야 하며 1번보다 느리다는 문제가 있다.

3번은 boost 라이브러리에 들어있는 컨테이너로 사실상 표준이라 할 수 있으며, 벡터 관련 연산도 모두 구현되어 있다. 하지만 속도가 조금 느리다는 단점이 있다. 간단한 벡터 연산뿐만 아니라 다양한 수학연산을 사용해야 한다면 boost 라이브러리를 선택하는 것이 좋다.

4번은 blitz++ 라이브러리에 들어있는 컨테이너로 포트란에 견줄 만한 속도가 장점이다. 과학계산 분야에선 거의 표준이라 할 수 있다.

보는 바와 같이 3번이나 4번이 좋은 선택이다. GMES 구현에는 4번을 선택했는데, 그 이유는 GMES가 SWIG를 이용해서 파이썬과 연동을 하고 있기 때문이다. boost는 자체적인 파이썬 연동 방식을 제공하고 있어서 한 프로그램에서 SWIG와 boost가 동시에 사용하는 것은 뭔가 자연스럽지 못한 느낌이 들었다. 그뿐만 아니라, blitz++가 아직은 더 빠르다.

blitz::TinyVector<double, 3>을 입력이나 출력으로 사용하는 함수를 파이썬에 연결하려면 어떻게 해야 할까? 우선 아래와 같은 간단한 C++ 함수를 생각해보자.

// util.hh

#include <blitz/tinyvec.h>

namespace util {
  typedef blitz::TinyVector<double, 3> Vector3;
  
  double accumulate(const Vector3& v);
  void weight(Vector3& v, double s = 1);
}

util::accumulate는 와 벡터 성분의 합을 구하는 함수이고, util::weight는 벡터에 상수를 곱하는 함수이다. 이들 함수는 아래와 같이 별로 어렵지 않게 구현해볼 수 있다.

// util.cc

#include "util.hh"

using namespace util;

double
util::accumulate(const Vector3& v)
{
  return v[0] + v[1] + v[2];
}

void 
util::weight(Vector3& v, double s)
{
  v *= s;
}

util::accumulate는 상수 인자를 받으니, 파이썬 측의 시퀀스(tuple, list, numpy.ndarray 등)를 C++ 쪽으로 변환해 주기만 하면 된다. 반면, util::weight의 인자는 값이 변경되므로 util::Vector 형을 다시 파이썬의 시퀀스로 변경해 줘야 한다. 일단 파이썬 시퀀스 형태만 만족하면 tuple, list형은 문제가 되지 않는다. numpy.ndarray 형도 numpy가 알아서 처리한다. 우선 blitz::TinyVector<>를 고려하지 않고 기본적인 인터페이스 파일을 만들어 보자.

// util.i

%module util

%{
#include "util.hh"
%}

%include "util.hh"

이렇게만 해도 컴파일이 되고, 파이썬 모듈을 읽어들일 수 있다. 하지만 함수를 실행할 수는 없다.

>>> import util
>>> a=[1,2,3]
>>> util.accumulate(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: in method 'accumulate', argument 1 of type 'util::Vector const &'

역시나 함수 인자의 형태가 맞지 않다는 에러를 내보낼 뿐이다. 즉, 파이썬의 시퀀스 형을 util::Vector 형으로 변환하는 방식을 알려줘야 한다. 문제는 SWIG가 blitz::TinyVector<>를 자동으로 처리해 주지 않는다는 점이다. 이건 %typemap을 써서 직접 변환 규칙을 만들어 줘야 한다.

%typemap(in) util::Vector3&
{
  $1 = convert_to_vector3($input);
  if (!$1) return NULL;
}

%typemap(argout) util::Vector3&
{
  for (int i = 0; i < 3; i++) {
    PyObject *o = PyFloat_FromDouble((*$1)[i]);
    PySequence_SetItem($input, i, o);
  }
}

%typemap(freearg) util::Vector3&
{
  if ($1) delete $1;
}

기본적인 역할은 %typemap(in)이 맡는다. 함수 인자의 util::Vector& 부분을 찾아 이 규칙을 적용한다. $1은 C++ 함수의 첫 번째 인자를 뜻하고, $input은 파이썬 측 함수의 인자를 가리킨다. convert_to_vector에 실제 변환 코드가 들어가는데, 이 코드는 아래에서 다룬다. util::weight 함수가 제대로 작동하려면 %typemap(argout)을 써야 한다. %typemap(argout)은 출력 역할을 맡은 인자를 처리할 수 있도록 해준다. 마지막으로 %typemap(freearg)는 형 변환 중에 힙에 생성한 자료 영역을 지울 수 있도록 해준다. 아래의 convert_to_vector를 보면 %typemap(freearg)를 사용한 이유를 알 수 있다.

%{
static util::Vector3*
convert_to_vector3(PyObject* input)
{
  if (!PySequence_Check(input)) {
    PyErr_SetString(PyExc_ValueError, "Expected a sequence");
      return NULL;
  }
  if (PySequence_Length(input) != 3) {
    PyErr_SetString(PyExc_ValueError, "Size mismatch. Expected 3 elements");
    return NULL;
  }

  util::Vector* v = new util::Vector3();
  for (int i = 0; i < 3; i++) {
    PyObject *o = PySequence_GetItem(input, i);
    if (PyNumber_Check(o))
      (*v)[i] = PyFloat_AsDouble(o);
    else {
      PyErr_SetString(PyExc_ValueError, "Sequence elements must be number");
      return NULL;
    }
  }
  return v;
}
%}

파이썬측의 입력이 시퀀스인지, 길이가 3인지를 확인한 후에 util::Vector를 임시로 생성하고 여기에 인자를 하나씩 복사하도록 했다. 복사하기 전에 각 원소가 숫자인지 확인하도록 했다.

아직 조금 더 남아있다. 형식 변환 규칙이 하나 더 필요한데, 바로 %typemap(typecheck)이다. util::weight 함수의 두 번째 인자가 기본값을 가지고 있는데, SWIG은 이럴 경우, util::weight(util::Vector3&)util::weight(util::Vector3&, double)을 각각 만들고, 동적 자료형 검사를 통해서 어느 함수를 사용할지 결정하도록 코드를 생성한다. 그러므로 다음과 같이 간단한 자료형 비교 코드도 만들어 줘야 한다.

%typemap(typecheck, precedence=SWIG_TYPECHECK_POINTER) util::Vector3&
{
  $1 = false;
  if (PySequence_Check($input) && PySequence_Length($input) == 3)
    $1 = true;
}

이제 distutils를 이용해서 컴파일하려면, 다음과 같은 setup.py를 만들고, python setup.py build_ext -i라고 입력하면 파이썬에서 불러올 수 있는 모듈을 만들어준다.

from distutils.core import setup, Extension

util_module = Extension(name = '_util',
                        sources = ['util.i', 'util.cc'],
                        depends = ['util.hh'],
                        swig_opts = ['-c++'])

setup(ext_modules = [util_module])

이제 파이썬 해석기를 다시 시작하고 모듈을 읽어오면(reload로는 안 된다.) 잘 작동하는 것을 확인할 수 있다.

>>> import numpy, util
>>> v1 = [1,2,3]
>>> v2 = numpy.array(v1, float)
>>> util.accumulate(v1)
6.0
>>> util.weight(v1, 2)
>>> v1
[2.0, 4.0, 6.0]
>>> util.weight(v2, 3)
>>> v2
array([ 3.,  6.,  9.])
>>> 

2011년 10월 15일 토요일

두 개의 대전된 진자

울프램 사에선 Wolfram Demonstrations Project를 운영하고 있다. 이 홈페이지에선 매스매티카로 만든 다양한 프로그램을 만날 수 있는데, 이름에서 볼 수 있듯이 여기 있는 프로그램의 목적은 보여주기 위한 것이다. 예를 들어, 과학 책에 등장하던 고정된 그림과 그래프를 다양하게 변화시켜 가며 체험해 볼 수 있다. 여기에 등록할 프로그램은 유료 프로그램인 매스매티카로 만들어야 하지만, 실행해 보는 데에는 무료 프로그램인 Wolfram CDF Player만 있으면 되므로 누구나 프로그램을 실행해 볼 수 있다.

2차원 진자의 운동
최근에 이 프로젝트에 2차원 진자를 전산 모의 실험해 볼 수 있는 프로그램을 하나 올렸다. 진자(pendulum, 흔들이)란 어떤 물체를 줄에 매달아 흔들리게 둔 장치를 말하는데, 쉽게 괘종시계의 추를 생각하면 된다. 이 간단한 동역학 모델은 생각과 달리 정확한 해를 구할 수 없는데, 꽤 정확한 근사는 할 할 수 있다. 물리학도가 처음으로 만나는 진동계로써 교육적으로 중요한 모델일 뿐만 아니라, 물리학자로 살아가는 동안 계속 만나게 되는 진동계로써 과학에 이바지하는 바도 크다.

내가 만든 프로그램에 나오는 진자는 평면상에서 움직이는 2차원 진자이다. 보통 진자의 움직임을 계산할 때 진자의 움직임 폭이 줄의 길이에 비해 짧다는 가정을 하는데, 이렇게 하면 원래 비선형 움직임을 보이는 진자를 선형계로 근사할 수 있다. 하지만, 내 프로그램은 이 근사를 사용하지 않고 더 정확한 계산을 시도한다. 하지만 이 프로그램도 현실과 다른 사항을 몇 개 가정한다. 추를 매단 줄에 관한 건데, 이 줄의 형태는 변하지 않으며 무게가 0이라고 가정한다. 이 밖에 다른 근사도 사용하지만, 이건 아래에서 다루기로 하자.

Two Charged Conducting Pendulums의 실행 화면
프로그램에는 진자가 한 개가 아닌 두 개가 나온다. 프로그램을 실행해 보면 알겠지만, 이 두 개의 진자는 서로 연결되어 있다. 눈에 보이는 장치에 의해 연결된 건 아니고, 전기력에 의해 연결되어 있다. 바로 진자의 추에 대전 된 전하량을 설정할 수 있게 되어 있기 때문이다. 그리고 두 개의 추는 완전 도체로 만들어진 구라고 가정한다. 물체 근처에 전하를 가져가면 전하와 물체 사이엔 서로 끌어당기는 힘이 작용하게 되는데, 이 물체가 완전 도체로 이루어진 구라면, 이 힘을 쉽게 계산할 수 있다. 다만, 여기에서 문제는 어느 한 쪽의 전하가 공기 중에 있지 않고 양쪽 모두 도체 구에 분포한다는 점이다. 이렇게 되면, 무한수열을 계산해야 하고, 정확한 계산을 위해선 시간이 좀 걸린다. 이 프로그램은 실시간으로 미분방정식을 풀어서 움직임을 보여줘야 하는데, 이래서는 사용자가 좀 지루할 것 같다. 그래서 다시 근사를 한다. 이 수열의 각 항은 크기가 급격히 감소하므로, 수열 중 첫 번째 항만을 이용해서 계산한다. 사실 이렇게 해도 꽤 그럴싸한 값을 얻을 수 있기 때문이다.

이 프로그램에선 거의 모든 값을 사용자가 변경해 볼 수 있다. 또한, 전산 모의 실험에선 일반적으로 이러한 매개 변수의 단위를 없애지만, 이 프로그램의 조정 패널에선 실제 단위를 사용하게 하였다. 덕분에 왼편의 조정 패널에 나타나는 숫자가 조금 지저분해 보이긴 하지만, 사용자가 실제 수치에 조금 더 사실적은 감을 얻을 수 있을 거라 여긴다.

2011년 9월 30일 금요일

새로운 파트너


맥북에어의 미려한 라인에 젖어서 기회를 엿보던 중, 한 가지 사항이 날 돌아서게 했다. 파이썬과 C++을 주로 이용해서 프로그램을 개발하는 나에게 SWIG 컴파일에 애를 먹는 시스템은 선택사항이 아니었기 때문! 불가능하진 않겠지만, 그래도 맥을 쭉 써온 친구들이 단번에 컴파일하지 못하는 장면은 맥에 대한 환상을 깨버렸다. 뭐, 그 밖에도 맥의 부담스러운 가격도 한몫을 했지만.

씽크패드X201i 3249-nr2
대안으로 선택한 건 휴대성이 좋은 씽크패드 X 계열에 우분투를 깔아서 쓰는 것이었다. 스펙으로 들어나지 않는 씽크패드의 매력과 우분투라는 OS의 조합은 맥북에어에 대한 미련을 말끔히 날려주리라 믿었다. 그래서 선택한 모델이 X 계열 중 특이하게 100만원 이하의 가격대를 형성한 씽크패드 X201i 3249-nr2였다. 출시된 지 조금 지나기도 했지만, 원래 저가 모델로 개발된 모양이다. 나중에 안 사실이지만, 모델명에 붙은 i가 저가형이란 뜻이란다. 최신이란 수식어가 붙지 않는 모델의 장점은 리눅스에서 지원하지 않는 장치로 고생할 확률이 그만큼 낮다는 것! 같은 계열인 X201은 우분투 11.04의 인증까지 받았다.

노트북을 받은 날과 다음 날은 각종 깔린 윈도7 홈 프리미엄의 업데이트를 하며 보냈다. 하드웨어 이상이 생겼을 때나 바이오스를 업그레이드할 때 노트북에 깔려나온 윈도가 있으면 편하므로 윈도를 삭제하지는 않았다. 레노버에서 바이오스 업그레이드용 부팅 디스크 이미지를 배포하므로 이걸 이용하면 되지만, 역시나 윈도에서 ThinkVantage 시스템 업데이트를 이용하는 편이 편하다. 나온 지 오래된 녀석답게 업데이트도 참 많았다.

설치된 윈도가 64비트이니 우분투 64비트 버전을 깔기로 하고 보니, 2011년 9월에 설치할 수 있는 옵션은 두 가지가 있었다. 10.04 LTS11.04. 메모리 스틱으로 설치하는 방식을 이용했는데, 11.04는 설치 중에 커널 패닉이 발생했다. 남은 유일한 옵션은 10.04 LTS 64비트였고, 이 녀석은 다행히 별다른 문제가 없었다. 복구 영역과 공장 초기화 영역 등은 그냥 두고, 윈도에는 50GB를 할당했다. 스왑은 하드웨어 최대 지원 메모리의 두 배인 16GB를 할당하고 나니 우분투에는 420GB를 쓸 수 있었다.

디스크 공가 분배

설치 후, 바로 10.10을 거쳐 11.04로 업그레이드를 했다. 사용 중에 하드웨어 오류로 보이는 문제가 세 가지 발생했다. 첫 번짼 단 한 번 발생했지만, 무선랜 장치가 사라지는 문제. 이건 콜드 부팅 후에 장치가 살아났고 아직 이 문제가 다시 발생하지는 않았다. 인터넷 검색을 해보니, 무선 네트워크 신호가 잡히지 않거나, 무선 네트워크를 수동으로 끌 때 무선 네트워크 장치의 커널 모듈이 내려가는 현상이 있는 것 같다. 이럴 땐, 아래와 같이 수동으로 커널 모듈을 올려줘야 한다.
$ sudo modprobe r8192se_pci
두 번째는 화면이 갑자기 꺼거나 아예 재부팅되는 현상으로 하루 사용 중에도 몇 번 일어났다. 노트북의 바이오스를 확인해보니 2010년 10월 26일 자 1.31 버전이었고, 이후 바이오스 변경 목록을 살펴보니 발생 조건은 다르지만, 디스플레이 블랙아웃 문제와 재부팅 문제 해결 등의 내용이 있었다. 바이오스를 업그레이드하고 나니 이 문제는 사라졌다.

세 번째는 사용 중에 갑자기 멈추는 문제로, 노트북을 켠 상태로 들고 다니거나 바닥이 평평하지 않고 고정되지 않은 곳에 두고 사용하면 종종 문제가 발생했다. 이건 원인을 정확히 파악하지 못했는데, 배터리의 결합부가 조금 의심이 간다. 다른 씽크패드는 배터리 결합부가 꽉 끼이는 느낌인데, X201i 3249-nr2의 배터리 결합부는 조금 헐겁다. 배터리가 고정부에 조금 유격이 있는 것 같다. 노트북을 켠 상태로 이동하거나 바닥이 고정되지 않은 곳에서 사용하면 전원부의 접촉불량으로 멈추는 것이 아닌지 의심된다.

여기까지 해서 모든 하드웨어가 윈도7에서처럼 다 작동하는 건 아니다. 먼저, 가장 눈에 띄는 지문스캐너. 이건 씽크위키를 참고 해서 잡으면 된다. 하지만 로그인 방식을 지문인식으로 대체하는 것은 추천하지 않는다. 한 손가락의 지문만 등록할 수 있고 인증이 필요할 때마다 지문을 요구하는 등, 윈도보다 매우 불편하다.

충격을 감지해서 하드디스크를 보호하는 Active Protection System도 직접 손봐줘야 한다. 필요한 패키지를 설치하고, hdapsd 서비스를 시작한다.
$ sudo apt-get install tp-smapi-dkms hdapsd
$ sudo /etc/init.d/hdapsd restart
작동 여부는 hdapsd를 직접 실행해서 확인할 수 있다. 아래의 명령어를 입력한 다음, 노트북을 움직여 보면 하드디스크 파킹이 작동하는 것을 확인할 수 있다.
$ sudo hdapsd
또한 find / 명령을 내린 후 노트북을 움직여 보면, 충격이 감지될 때마다 결과 출력이 멈추는 것을 확인할 수 있다.

냉각팬의 속도는 별도의 설정을 하지 않는다면, 일정한 속도를 유지한다. 일반적인 용도에는 충분하고 CPU 사용량이 많으면 부족한 속도다. 씽크패드의 온도에 따라서 회전 속도를 조절하려면 thinkfan을 이용해야 한다. 이 설정은 잘못 설정하면 노트북에 열 손상을 가할 수도 있으니 주의를 기울여야 한다. X201i가 저가 모델이라서 그런지, 장착된 온도 센서가 하나뿐이어서 각 부분별 온도에 맞춰 세밀하게 냉각팬 설정을 할 수 없다. 더욱이 인터넷에서 아직 X201i 용의 thinkfan 설정을 찾을 수 없다. 하지만, 기본 설정만으로도 만족스럽게 작동한다는 보고가 있니 용감하게 시도해보자. 먼저, thinkfan을 설치한다.
$ sudo apt-get install thinkfan
thinkpad_acpi 모듈이 팬의 회전 속도를 조정하도록, /etc/modprobe.d/thinkpad_acpi.conf 파일에 다음 줄을 추가한다.
options thinkpad_acpi fan_control=1
마지막으로 /etc/default/thinkfan의 다섯 번째 줄을 다음과 같이 수정해서 부팅할 때, thinkfan이 시작되도록 한다.
START=yes
이제 노트북을 재부팅하면 된다.

세 가지 장치를 수동으로 설정해야 하지만, 그 외의 다른 모든 장치가 우분투에서 정상으로 작동한다. 씽크패드 답지 않게 배터리 결합이 조금 느신하지만, 이것 때문에 전원에 문제가 생긴 적은 없다. 누군가 우분투를 깔아 쓸 노트북을 추천해 달라고 한다면, 주저하지 않고 씽크패드 X201i 3249-nr2를 소개해 줄 것 같다.

2011년 9월 1일 목요일

파이어폭스 주소창의 검색 엔진 바꾸기

파이어폭스 속도가 느려서 크롬을 이용했는데, 파이어폭스 6를 써보니 크롬에 못지않은 빠른 속도를 보여준다. 크롬이 순간 검색 기능 덕분인지 웹페이지 로딩이 더 빠르긴 하지만.

그래도 이 정도 속도면 쓸만하다 싶어서 파이어폭스로 돌아와 보니, 크롬의 주소창 검색 기능이 못내 그립다. 예전엔 주소창 옆의 검색창을 가지고도 잘 썼건만, 주소창 검색의 편리함에 물들었나 보다. 파이어폭스는 주소창에 검색어를 입력하면 기본적으로 www.google.com에서 검색을 하도록 되어 있지만,  크롬처럼 검색 엔진을 쉽게 바꿀 수 없다. 난 구글의 SSL Search를 이용하고 싶은데... 어떻게 해야 할까? Beyond Technology란 블로그에서 답을 찾았다.

  1. 파이어폭스를 열고 주소창에 about:config를 입력한다. 
  2. 설정을 함부로 바꾸면 안정성이나 보안에 문제가 발생할 수 있다는 의미심장한 경고가 뜬다. 경고를 마음에 새기고 I'll be careful, I promise!를 클릭한다. 
  3. Filter 창에 keyword.URL을 입력한다.
     
  4. 검색 결과로 나온 keyword.URL 항목을 더블클릭한다.
  5. Enter string value 창이 뜨는데, 여기에 https://encrypted.google.com/search?q=를 입력한다. 다른 검색 엔진을 이용하려면 해당 URL을 입력하면 된다. 
  6. OK를 클릭하면 설정 끝! keyword.URL 항목의 Statususer set으로 변경되었고, Value에는 방금 입력한 주소가 들어가 있는 것을 확인할 수 있다.
     
  7. 이제 주소창에 검색어를 입력하고 엔터를 누르면 구글 SSL Search를 이용해서 검색 결과를 볼 수 있다.

2011년 8월 24일 수요일

C++로 간단하게 구현해보는 희소행렬

C++에서 STL을 이용하여 희소행렬(sparse matrix)을 구현한다면, 원소를 저장하기에 가장 좋은 자료형은 뭘까? 아마 map일 거다. 키를 위치 인덱스로 하고, 데이터에는 실제 값을 넣으면 된다.

STL엔 두 개의 map이 있다. 하나는 std::map이고, 다른 하나는 std::unordered_map이다. 이들의 용도는 다음과 같이 간단히 정리할 수 있다. 킷값에 따라 정렬해주는 map이 필요하다면 std::map을 사용하고, 빠른 검색이 필요하다면 std::unordered_map을 사용한다. 우린 키를 정렬할 필요는 없으므로, std::unordered_map을 이용하기로 하자.

이제 2차원 인덱스를 표현할 자료형을 결정해야 한다. C++의 내장 배열을 사용하자니 번거롭고, std::vector를 사용하자니 너무 무겁다. 2011년 8월 12일에 제정된 새로운 C++ 표준에선 std::array를 정의하고 있다. 이건, 고정된 크기의 std::vector라고 생각하면 되는데, std::vector보다 가볍고 C++ 내장 배열보다 편리하다. 행렬의 인덱스는 길이가 2로 고정되므로 std::array는 인덱스의 자료형에 딱 맞는 컨테이너이다.

이제 구현을 해보자. 먼저, 필요한 헤더를 불러온다.
// smat.cc
#include <array>
#include <cstdlib>
#include <iostream>
#include <numeric>
#include <unordered_map>
cstdlib 헤더는 main 함수의 반환 값으로 사용할 EXIT_SUCCESS 매크로를 정의가 들어 있고, numeric 헤더는 해시 값 생성에 사용할 accumulate 함수의 선언이 들어 있다.

다음으로 할 일은 함수자(functor)를 만드는 것이다. std::unordered_map해시표를 사용하는 데, 키로부터 해시 값을 계산해서 해시표를 생성해야 한다. 기본 자료형에 대해서는 해시 값을 생성하는 함수자가 이미 선언되어 있지만, 그 외의 자료형에 대해선 우리가 직접 만들어줘야 한다. std::unary_function을 이용해서 간단하게 인덱스의 두 값을 더하는 해시 함수자를 만든다.
namespace std
{
  template <>
  struct hash<array<int, 2> >: public unary_function<array<int, 2>, size_t>
  {
    size_t operator()(const array<int, 2>& idx) const
    {
      return accumulate(idx.begin(), idx.end(), 0);
    }
  };
}
이제 희소행렬 자체를 구현할 클래스를 선언한다. 행렬 연산을 구현하는 여러 함수도 구현해야겠지만, 여기선 행렬 원소의 읽기와 쓰기에 관련된 연산자 정도만 구현해보자.
template <typename T> class SparseMatrix
{
public:
  SparseMatrix(int i, int j): i_size(i), j_size(j) {}

  T& operator()(int i, int j)
  {
    std::array<int, 2> idx;
    idx[0] = i;
    idx[1] = j;
    return mat[idx];
  }

  T operator()(int i, int j) const
  {
    std::array<int, 2> idx;
    idx[0] = i;
    idx[1] = j;
    typename std::unordered_map<std::array<int, 2>, T>::const_iterator tmp;
    tmp = mat.find(idx);
   
    if (tmp == mat.end())
      return T();
    else
      return tmp->second;
  }
   
private:
  std::unordered_map<std::array<int, 2>, T> mat;
  std::size_t i_size, j_size;
};
눈여겨볼 점은 행렬의 원소를 설정할 때 사용할 operator()와 원소를 조회할 때 사용할 operator() const를 모두 선언해야 한다는 것이다. operator()만 선언한다면, 행렬의 원소를 조회할 때마다 조회하는 인덱스에 해당하는 항목을 map에 만들게 된다. 예를 들어,
SparseMatrix<double> sm(3, 3);
for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      std::cout << sm(i, j) << " ";
    }
    std::cout << std::endl;
  }
처럼 모든 원소를 조회하기만 해도 내부 저장 장소인 mat에 모든 원소에 해당하는 항목이 만들어져 메모리를 낭비하게 된다.

실제로 원소를 설정하고 조회하는 방법은 아래와 같다.
using namespace std;

int main(int argc, char* argv[])
{
  SparseMatrix<double> sm(3, 3);
  sm(1, 2) = 1;
  sm(2, 0) = 2;

  const SparseMatrix<double>& sm_ref = sm;
  for (int i = 0; i < 3; ++i) {
    for (int j = 0; j < 3; ++j) {
      cout << sm_ref(i, j) << " ";
    }
    cout << endl;
  }

  return EXIT_SUCCESS;
}
operator() const를 호출하려면, 상수 레퍼런스나 상수 포인터를 이용해서 SpareMatrix에 접근해야 한다. 뭐, 대충 이렇다. 이 코드는 C++0x 표준인  std::arraystd::unordered_map을 사용하는데, 이 기능이 시험적으로 구현된 거라서 이용하면 컴파일 할 때 -std=c++0x 옵션을 줘야 한다.
$ g++ -Wall -g -pedantic -std=c++0x -o smat smat.cc
실행해보면, 잘 작동하는 것을 확인할 수 있다.
$ ./smat
0 0 0
0 0 1
2 0 0
$
mat의 크기를 검사해 보면, 단 두 개의 항목만 저장하고 있는 것을 확인할 수 있다. 간단한 희소행렬 완성!

2011년 8월 23일 화요일

C++의 반복자(iterator)를 이용하여 파이썬 생성자(generator) 만들기

최근에 GMES의 C++ 모듈 설계를 변경하면서, 내부 자료형으로 STL의 컨네이너를 사용하는 클래스가 필요하게 되었다. 이 글에서 설명하려는 내용과 관계 없는 부분을 제거하면, 대략적인 형태는 아래와 같다.
// itergen.hh
#include <vector>

class A
{
public:
  A(): a(3)
  {
    for (size_t i = 0; i < a.size(); i++)
      a[i] = i;
  }

  std::vector<int>::const_iterator
  begin() const
  {
    return a.begin();
  }

  std::vector<int>::const_iterator
  end() const
  {
    return a.end();
  }

private:
  std::vector<int> a;
};
// itergen.cc
#include "itergen.hh"
이 코드는 파이썬으로 래핑하여 사용하게 되는데, 아래와 같이 생성자로 이용하고자 한다. 어떻게 해야 할까?
>>> for i in A():
...    print i
1
2
3
파이썬 생성자에 관한 API를 살펴봤는데, 이거 잠깐 보고 구현할 난이도는 아닌 것 같다. 이왕 SWIG를 이용한다면, 뭔가 더 간단한 방법이 없을까? Reference Man의 블로그에 방법이 나와 있다. 이 블로그에 나와 있는 내용을 간단하게 정리해 보자.

일단, C++ 클래스에선 시작과 끝 부분의 반복자를 반환하는 begin, end 메서드만 제공하면 된다. 이는 일반적으로 STL에서 사용하는 방식이므로 C++ 클래스에서 제공하는 것이 자연스럽다. C++ 코드에서 할 일은 여기까지다. SWIG가 제공하는 %extend 키워드를 사용하면 C++ 코드를 더럽히지 않고도 파이썬 래핑에 필요한 코드를 추가할 수 있다. swig의 인터페이스 파일은 아래와 같다.
// itergen.i
%module itergen

%{
#include "itergen.hh"
%}

%extend A {
  std::vector<int>::const_iterator* _begin()
  {
    return new std::vector<int>::const_iterator($self->begin());
  }

  std::vector<int>::const_iterator* _end()
  {
    return new std::vector<int>::const_iterator($self->end());
  }

  int _deref(std::vector<int>::const_iterator* it)
  {
    return **it;
  }

  bool _compref(std::vector<int>::const_iterator* lhs,
  std::vector<int>::const_iterator* rhs)
  {
    return *lhs == *rhs;
  }

  void _incref(std::vector<int>::const_iterator* it)
  {
    ++(*it);
  }

  %pythoncode %{
    def __iter__(self):
      it = self._begin()
      while not self._compref(it, self._end()):
          yield self._deref(it)
          self._incref(it)
  %}
};

%include "itergen.hh"
파이썬은 STL의 반복자를 제대로 다루지 못하므로, 이를 처리해 주는 모든 메서드를 C++ 측에서 만들어줘야 한다. C++ 코드를 수정할 것 없이 인터페이스 파일에서 %extend 키워드를 이용해서 관련 메서드를 추가한다.

파이썬 클래스는 __iter__ 메서드를 선언해주면, 생성자로 이용할 수 있다. 이 메서드는 앞에서 만들어준 C++ 메서드를 이용하여 C++의 반복자를 처리한다. 원리는 간단하다. STL에서 흔히 사용하는, 반복자를 이용한 컨테이너 순회를 이용하는 데, 차이점이라면 값의 반환으로 return 대신에 yield를 사용한다는 점이다. yield를 사용하면, 함수의 값은 반환되는 데, 함수는 종료되지 않고 next 메서드가 호출될 때마다 yield를 호출한 다음 지점부터 연속해서 실행한다. __iter__ 메서드는 %pythoncode 키워드를 이용하여 파이썬 프록시 클래스에 추가한다.

컴파일 과정은 아래와 같다. 인터페이스 파일로부터 프록시 클래스와 파이썬 인터페이스를 생성하고, C++ 코드를 컴파일 한 후, 공유 라이브러리로 만들어 주면 된다.
$ swig -c++ -python itergen.i
$ g++ -Wall -g -I/usr/include/python2.6 -c itergen_wrap.cxx
$ g++ -shared itergen.o itergen_wrap.o -o _itergen.so
이제 파이썬 해석기에서 모듈을 올리고 생성자를 사용해보자.
$ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import itergen
>>> a=itergen.A()
>>> iter(a)
<generator object __iter__ at 0xb7720234>
>>> for i in a: print i
...
0
1
2
>>>
완료!

2011년 6월 27일 월요일

진화론에 관한 오해

진화론에 관해 친구와 대화한 내용을 정리해 보았습니다. 트위터로 나눈 대화라 기본적으로 질문과 답변이 좀 짧습니다. 옮기는 과정에서 수정과 추가 설명을 더했습니다.

  • 나: 진화론은 다른 과학 이론과는 다르게 믿는다는 표현을 쓰죠. 진화론, 쉬워 보이지만 주변에서 제대로 이해한 사람은 아직 한 사람도 만나보지 못했습니다. 다들 이해하려고 하지 않고 믿거나 믿지 않거나, 종교처럼 대합니다.
  • 친구: 저도 창조론자인데, 딴 건 모르겠고 다른 종이 생겨날 때가 진화론으론 이해하기 어려워서요. 창조론자들이 무조건 성경에다 끼워 맞추려고 해서 문제지 나름의 설득력이 있는 것도 많아요.
  • 나: 진화론은 종의 분화를 아주 잘 설명해줘요. 취약한 부분은 생명의 탄생 부분이죠. 리처드 도킨스의 책을 읽어보면 쉽게 알 수 있을 거예요.
  • 친구: 제가 보기엔 진화론 자체도 결함이 많아서요. 그게 창조론의 근거가 되죠. 창조론은 진화론이 틀렸으니까 창조론이 맞는다는 식으로 늘 주장해요.
  • 나: 아직, 결함이 없는 과학 이론은 없습니다. A 이론의 결함이 B 이론의 근거가 될 수는 없어요. 논리적 오류입니다.
  • 친구: 종이 갈라지는 시점이 언제냐가 젤 중요한 듯한데 조상과 생식을 할 수 없으면서 서로 생식 가능한 암수가 나와야 하는데 어떻게 가능한 건가요? 어째 한 쌍이 나왔다 치더라도 근친이라 번성하기 어려울 텐데요.
  • 나: 의 구분은 유전적으로 충분한 차이를 기반으로 하죠. 양자처럼 뚜렷이 구분되는 단위가 아니에요.
    침팬지와 인간도 생식할 수 없을 정도의 유전적 차이가 있지만, 그 사이를 이어주는 유전적 다리가 있었지요. 지금은 그 유전적 다리가 멸종해서 침팬지와 인간이 갑자기 튀어나온 것처럼 보이는 거죠.
    인간은 감각기관의 범위 안에서 사물을 판단하려는 습성이 있지요. 인간의 수명에 준하는 시간 단위론 종이 툭툭 튀어나오는 것 같지만, 지질학적 시간 단위로는 충분히 연속적인 유전적 변이가 일어납니다.
  • 친구: 이것도 창조론자들이 잘 걸고 넘는 것인데, 러시모아 산의 대통령 조각상을 많이 거론하죠. 오랜 시간이 지난다고 해서 스스로 저와 같은 작품이 만들어지는가? 마치 원시생물이 인간으로 진화한 거처럼요.
러시모어 산에 있는 대통령 조각상. (왼쪽부터) 조지 워싱턴, 토머스 제퍼슨, 시어도어 루스벨트, 에이브러햄 링컨.
  • 나: 진화는 누적된 변화입니다. 주사위를 6번 던져서 모두 6이 나올 확률은 6^-6이지만, 누적 시행으로는 1/6입니다. 조금 달라진 형질은 유전되고, 다음 세대에서 또 조금 달라지죠. 이게 바로 바위와는 다르게 생명체가 복잡한 형태로 진화한 이유입니다.
  • 친구: 미씽링크 개념인데 화석을 조사하다 보면 진화의 과정이 매우 불연속이란 걸 알 수 있어요. 침팬지와 사람으로 치면 십 퍼센트 사람 구십 퍼센트 침팬지 이십 퍼센트 사람 팔십 퍼센트 침팬지 이런 식으로 화석이 발견되어야 하는 게 맞지 싶은데요.
  • 나: 화석이 생성되는 조건은 매우 까다롭습니다. 이걸 우리가 발견하기도 어렵고요. 모든 종이 다 화석으로 남을 수는 없지요. 다행히 우리가 진화를 유추할 만큼의 화석은 발견되고 있습니다.
  • 친구: 진화론이 논쟁의 종지부를 찍으려면 새로운 종을 하나 만들어 보이면 될 텐데요.
  • 나: 새로운 종은 유전공학 실험실에서 매일 같이 만들어지고 있습니다. 하지만, 이게 진화론의 증거가 되지는 못합니다.
  • 친구: 종의 개념은 같은 종끼리 교배할 수 있어서 번성할 수 있느냐는 거죠. 무추는 씨앗이 없어서 스스로 번성할 수 없어서 종으로 볼 수 없고요. 제가 아는 한 종의 요건을 제대로 갖춘 새 생명체는 아직 없는 것으로 압니다.
  • 나: 유성생식을 하는 생물은 비용이 많이 들어서 쉽게 실험하지도 못합니다. 단세포 생물 등으로 주로 실험하겠죠. 이들이 생태계에 노출되면 생태계에 자리 잡을 수도 있을 겁니다. 다만, 이렇게 되면 생태계에 혼란을 가져올 수 있기에 철저히 격리합니다.
  • 친구: 제가 지적하는 건 진화론에서 아직 불충분하고 빠진 내용이 많다는 겁니다. 주요근거 들을 재현하기 매우 어렵기 때문에 논란은 계속 될겁니다.
  • 나: 진화론은 과학계에서 가장 완벽한 이론입니다. 진화론이 공격당하는 건 일부 종교의 신념과 맞지 않기 때문이지요. 진화론의 시간 단위가 인간의 시간 개념과 차이가 크다는 점과 어설픈 교육도 한몫하고 있습니다.
  • 친구: 진화론은 초중고 때부터 주구장창 했고 창조론은 한 학기 교양 수업이 다입니다. 교재는 시중에 나오는 얇은 단행본 두 권이고요. 창조론 반박 위주로 된 진화론 책은 없나요?
  • 나: 진화론은 생물학의 근간인데, 교육과정에선 이걸 따로 때어서 다윈과 적자생존만 언급하고 넘어가니 우리나라에 창조론이 넘쳐나는 데 지대한 공헌을 하고 있죠.
    진화론은 다윈이 1859년 출판한 책 《종의 기원》을 통해 널리 알려지게 되었다.
    책 목록은 리차드 도킨스 - Google 검색에 있습니다. 창조론에 쏟은 한 학기 정도의 관심만 진화론에 쏟으면 진화론이 불충분하고 빠진 내용이 많다는 오해는 해결할 수 있을 겁니다.

    2011년 6월 10일 금요일

    Windows 탐색기의 팝업 메뉴에 "IPython here" 추가하기

    윈도에서 파이썬을 사용하려면 EPD(Enthought Python Distribution)를 설치하는 게 가장 편하다. EPD는 파이썬 해석기뿐만 아니라, 여러 가지 파이썬 관련 라이브러리도 한 번에 설치할 수 있는 설치 파일을 제공한다. 교육용 버전과 체험판은 무료로 받을 수 있다.

    EPD를 설치한 후엔 PATH 환경변수를 설정해줘야 한다. 제어판->시스템 및 보안->시스템으로 들어간다. 왼편의 고급 시스템 설정을 누르면 시스템 속성 창이 뜬다. 고급 탭에서 환경 변수 단추를 클릭한다. 시스템 변수 중에서 Path를 선택하고 편집 단추를 클릭한다. 시스템 변수 편집 창이 뜨면 변수 값에 다음을 추가한다. 다음은 EPD를 C:\Program Files\Python26에 설치했을 경우를 가정한다.
    ;C:\Program Files\Python26;C:\Program Files\Python26\Scripts
    
    Path 환경변수에 파이썬 경로를 추가한다.

    이제 다음과 같은 ipython_here_shell_extension.reg 파일을 생성한다.
    Windows Registry Editor Version 5.00
    
    [HKEY_CLASSES_ROOT\Directory\shell\IPython here]
    
    [HKEY_CLASSES_ROOT\Directory\shell\IPython here\Command]
    @="cmd.exe /C ipython -p sh -i -c \"%cd %1\""
    
    이 파일을 더블 클릭하여 실행하면, 레지스트리가 수정된다. 이제 탐색기의 특정 폴더를 선택하고 우클릭을 하여 팝업메뉴를 띄우면 IPython here라는 메뉴를 볼 수 있다. 이 메뉴를 선택하면 해당 폴더에서 실행되는 IPython 셸이 뜬다.
    팝업 메뉴에 Ipython here가 나온다.
    경로명에 한글이 들어 있으면 작동하지 않는 문제가 있다.

    참고자료: http://lists.ipython.scipy.org/pipermail/ipython-user/2007-October/004746.html

    2011년 4월 1일 금요일

    미분방정식의 분석

    y'[t] == y[t] (4 - y[t])미분방정식을 분석해보자. 초기 조건을 y[0] == 0이나 y[0] == 4로 주면 y'[t]==0이니, t에 무관하게 각각 0과 4에 머무는 것을 예측할 수 있다. 더 구체적인 분석에 앞서, 백문이 불여일견이라 했으니 방향장(direction field)을 한 번 그려보자.
    StreamPlot[{1, y (4 - y)}, {t, 0, 5}, {y, -5, 5},
     FrameLabel -> {t, y}]
    
    y'=y(4-y)의 방향장 그래프
    y[0]을 초기 조건으로 잡는다면, y[0]이 0보다 크면, 4로 수렴하고 y[0]이 0보다 작으면 음의 무한대로 발산하는 것처럼 보인다. 과연 그럴까? 초기 조건을 y[0] == -2로 해서 미분방정식을 실제로 풀어보자.
    DSolve[{y'[t] == y[t] (4 - y[t]), y[0] == 4}, y[t], t]
    Equal @@ %[[1, 1]]
    Map[Limit[#, t -> Infinity] &, %]
    

    이게 어찌 된 일일까? 초기 조건을 0보다 작게 두었는데, 발산하지 않고 4로 수렴한다는 결과가 나왔다. 미분방정식의 해를 그래프로 그려보면 이유를 알 수 있다. 
    Plot[(4 Exp[4 t]))/(-3 + Exp[4 t]), {t, 0, 3}, PlotRange -> {-5, 9}, 
     AxesLabel -> {t, y}]
    
    y'=y(4-y)의 해를 그린 그래프

    y[t]는 -2에서 출발해서 Log[3]/4에서 음의 무한대로 발산한다. Log[3]/4의 오른쪽은 양의 무한대에서부터 시작해서 4로 수렴하게 된다. 즉 Log[3]/4라는 특이점(singularity)이 존재한다.

    2011년 2월 10일 목요일

    이맥스의 정규 표현식 치환 기능

    드디어 매스매티카 8을 쓸 수 있게 되었다. 이전 버전에서 작업하던 파일을 열어 실행하니 왠 걸. 떡하니 에러가 뜬다.

    매스매티카 8.0의 변경사항으로 인해 발생하는 오류


    난 매스매티카를 주로 이론적 분석, 즉 수식전개 등에 사용하는데, 종종 중간 결과를 수식번호 형식으로 저장해 놓는다. 해당 장(chapter)과 각 장에서의 수식번호를 나타내는 eq15\[Dash]8과 같은 형식의 변수명을 사용하는데, 여기서 문제가 발생했다. 이전 버전까지는 eq15-8을 단일 변수명으로 봤는데, 8.0에서는 이를 eq15, \[Dash], 8의 곱으로 인식한다.

    어차피 각 장은 별도의 파일에 저장하고 있으므로, 변수명에서 장 번호를 없애기로 했다. 그러면 변수명을 eq8과 같이 \[Dash] 없이 쓸 수 있으므로 문제를 해결할 수 있다. 파일 마다 많게는 100여개에 이르는 수식번호를 어떻게 일일이 다 바꿀까? 매스매티카에서 하기는 어려울 것 같다. 간단한 방법을 생각해보면, sed를 이용할 수도 있고 이맥스의 치환 기능을 이용할 수도 있겠다. 일단 변환할 문자열의 형태를 알아야 하니, 이 파일을 이맥스에서 열어보자.

    이 부분의 해석 방식이 바뀌었다.

    해당 부분의 입력이 위와 같이 저장된 것을 알 수 있다. 이제 이러한 패턴을 찾아서 바꾸면 된다. replace-regexp(C-M-%) 명령을 이용하면, 정규 표현식으로 위와 같은 패턴의 문자열을 모두 찾아서 바꿀 수 있다. 위 그림에서 강조한 부분을 나타내는 정규 표현식은 아래와 같다.
    RowBox\[{"eq15", "\\\[Dash\]", "[0-9][0-9]*"}\]
    
    하지만, 문제가 하나 있다. 패턴은 이게 맞는데 매번 치환마다 수식번호를 그것에 맞게 바꿔줘야 한다. 그렇지 않으면 모든 수식번호가 하나로 바뀌게 된다. 이건 패턴의 변수 기능을 이용하면 해결할 수 있다. 먼저, 수식번호에 해당하는 부분을 괄호로 둘러싼다. Query replace regexp:에 입력할 정규 표현식은 아래와 같다.
    RowBox\[{"eq15", "\\\[Dash\]", "\([0-9][0-9]*\)"}\]
    
    괄호로 둘러싼 부분은 치환된 문자열에서 \1로 불러올 수 있다. 괄호가 두 개 이상이면 \2, \3 등으로 불러올 수 있다. 즉, with:에 입력할 패턴은 아래와 같다.
    "eq\1"
    
    일치하는 문자열을 한번에 모두 치환하려면 Query replacing이 진행 중일 때, !를 입력하면 된다.

    replace-regexp를이용하여 치환하는 모습

    문자열 치환을 마치고, 파일을 저장한다. 이제 수정한 파일을 매스매티카에서 열어보자.

    노트북 파일을 매스매티카 외부에서 수정하면 저장된 임시 계산 결과를 이용할 수 없다.

    경고창이 하나 뜨는며 살짝 겁을 준다. 잘 읽어보면, 수정된 파일이므로 임시 저장 결과를 이용할 수 없다는 안내이다. 친절하게도 내용에는 문제가 없을 거라며 안심 시켜준다. OK를 클릭해서 넘어간다. 수식을 다시 실행해보면, 문제가 해결된 것을 볼 수 있다.

    이맥스와 정규 표현식을 이용하면 문제가 되는 부분을 한 번에 모두 치환할 수 있다.

    2011년 1월 31일 월요일

    원소기호 모으기

    매스매티카를 만든 울프램 사가 선보인 울프램알파는 여느 검색엔진과는 달리 계산 가능한 검색결과를 내놓는다. 즉, 연구에 필요한 자료를 쓰기 좋은 형태로 제공해 준다. 이점을 원소기호로 이루어진 가장 긴 단어를 찾는 파이썬 프로그램의 원소기호 목록을 울프램알파에서 받아오도록 바꿔보자.

    울프램알파 API의 2.0 버전은 개인에 한해 무료로 공개되어 있다. 몇몇 제약이 있긴 하지만, 울프램알파 API를 체험해 보기엔 충분해 보인다. 울프램알파 API를 상용하려면 먼저 울프램알파에 계정을 만들고, AppID를 발급받아야 한다. 프로그램마다 별도의 AppID를 발급받아야 하며, 30개까지 발급받을 수 있다.

    울프램알파 API는 파이썬, 자바 등 다양한 언어에 대한 바인딩도 제공하지만, 별도의 설치과정이 필요없는 웹서비스를 사용해보자. 울프램알파에서 검색에서 원소기호의 목록을 얻을 수 있는 검색어는 elements symbol이다.

    울프램알파의 원소기호 검색 결과

    몇몇 옵션으로 검색 결과에 제한을 두어 검색 결과를 더 간단하게 받을 수 있다. 먼저, 우리는 검색 결과를 웹브라우저를 통해 볼 것이 아니므로, html 형식으로 받을 필요가 없다. 또한 Input interpretation, Result, Table 등의 팟 중에서 Result만 필요하다. 이러한 옵션을 포함한 URL은 다음과 같다. xxx 부분에는 발급받은 AppID를 넣는다.

    http://api.wolframalpha.com/v2/query?appid=xxx&input=elements%20symbol&format=plaintext&scanner=Identity&includepodid=Result

    이 URL을 웹 브라우저에 한 번 넣어보자.

    울프램알파 API를 이용한 원소기호 질의 결과

    결과를 보면, 원하는 정보가 없다. 대신 recalculate라는 항목이 보인다. 이유를 알 수는 없지만, 울프램알파는 바로 원소기호를 알려주지 않고 별도의 파일에 결과를 기록한 후, 이 파일의 위치를 알려준다. 이 값을 다시 웹브라우저로 열어보자.

    울프램알파 API를 이용한 원소기호 질의 결과

    이제야 원하는 결과가 나왔다. plaintext 항목의 값이 우리가 원하는 원소기호이다. 이걸 이용하는 파이썬 코드는 아래와 같다.
    #!/usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import urllib, sys
    from xml.dom.minidom import parse
    
    url = 'http://api.wolframalpha.com/v2/\
    query?appid=xxx\
    &input=elements%20symbol&format=plaintext&\
    scanner=Identity&includepodid=Result'
    
    try:
       f = urllib.urlopen(url)
       dom = parse(f)
       queryresult = dom.getElementsByTagName('queryresult')
       recalculate = queryresult[0].getAttribute('recalculate')
       
       f2 = urllib.urlopen(recalculate)
       dom2 = parse(f2)
       plaintext = dom2.getElementsByTagName('plaintext')[0]
       symbols = ''.join(plaintext.firstChild.data.split())
       pattern = r"^(" + symbols + ")+$"
    except Exception, e:
       print e
       sys.exit(1)
    
    이렇게 만든 정규식 패턴을 조승연씨의 파이썬 코드에 적용하면 가장 긴 단어를 찾을 수 있다.

    2011년 1월 14일 금요일

    블로그스팟에 예쁜 코드 넣기

    google-code-prettify라는 자바스크립트 프로그램을 이용하면 블로그스팟에 코드를 예쁘게 넣을 수 있다. 이 프로그램을 블로그스팟에 적용하는 방법을 살펴본다.

    블로거에 로그인해서 ‘꾸미기’-’HTML 편집’으로 들어간다. ‘스킨 수정’에서 <head> 태그 다음 줄에 아래 내용을 추가한다.
    <link href='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css' rel='stylesheet' type='text/css'/>
    <script src='http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js' type='text/javascript'>  </script>
    
    <body> 태그를 수정한다.
     <body onload="prettyPrint()">
    
    글을 작성하고, ‘HTML 편집’을 눌러서 글에 넣을 코드를 다음 태그로 둘러싼다.
    <pre class="prettyprint">
        // add your code here
    </pre> 
    써보니, C++의 템플릿 코드에서 약간 문제가 발생한다. 이 문제는 C++ 코드를 '글쓰기'에서 입력하면 말썽을 일으키는 문자를 자동으로 변경해주니 해결된다. 매스매티카 언어를 지원하지 않는 문제도 있는데, 이건 직접 src/lang-nb.js 파일을 만들어서 등록해야 할 것 같다.

    GMES 이야기를 시작하며...

    내 전공은 정보통신공학인데, 국내의 여느 과와 달리 포토닉스 관련 분야를 다룬다. 그중에서도 컴퓨터 모의실험을 주로 하는데, 그 결과로 개발하고 있는 프로그램이 GMES이다. 모의실험에 사용하는 수치해석 프로그램을 개발하다 보면, 수많은 버그와 만나게 되는데 단순한 자판 입력 실수에서부터 프로그램 언어나 알고리듬의 구현 오류와 물리적 이해의 부족에서 발생하는 오류까지, 유형이 참 다양하다. 앞의 두 이유는 다른 분야의 프로그래밍에도 흔하니, 나는 물리적 이해의 부족으로 발생하는 버그에 대해 다뤄보고자 한다.

    GMES는 맥스웰 방정식을 수치해석적으로 푸는 FDTD라는 방법을 구현한 프로그램이다. 맥스웰 방정식을 푼다는 점은 전자기 현상을 다룬다는 의미와 이를 양자역학적 해석보다는 고전역학적 해석으로 다룬다는 의미가 있다. FDTD는 우리가 생활하는 공간과 같은 시공간을 그대로 사용하는 방식으로 주파수 공간이나 파수 공간을 사용하는 방식과는 달리, 실제 실험과 유사한 방식으로 계산 공간을 설정할 수 있는 장점이 있다. 즉, 실험 모형과 입력 광원 등을 모두 머릿속에 쉽게 그려볼 수 있다.

    GMES의 검증도 겸할까 해서, 학부 물리학과 2학년에 전자기학에서 배우는 쉬운 실험 모형을 하나 골랐다. 바로 두 유전체의 경계면에 입사하는 평면파 모형이다. 이중에서 입력 광원에 대해 살펴보자. 먼저, 이 모의실험에 사용할, 제대로 된 평면파의 모습은 아래와 같다.
    정상적인 평면파의 모습
    대략 y축의 0.8부터 -1.2까지 사선 방향으로, 완벽하진 않지만, 일정한 위상을 갖는 평면파의 모습을 관찰할 수 있다. 그림의 위와 아랫부분에 평면파가 없는 것은 흡수경계조건과 입력 광원의 위치 때문이다. x축의 -1.0부터 -2.0까지와 1.0부터 2.0까지는 CPML이라는 흡수경계조건을 사용해서 반사 없이 전자기파를 흡수하고, x축의 0.8에 위치한 입력 평면에서 오른쪽 위를 향해 평면파를 입력한다. y축의 왼쪽과 오른쪽 끝은 주기적경계조건(Periodic Boundary Conditions, PBC)이 설정되어 있어서 오른쪽 끝에 다다른 전자기파는 다시 왼쪽으로 입력된다. 그런데 이렇게 나와야 하는 녀석이 아래 그림처럼 나왔다.
    허수부 위상을 맞추지 못해 변형된 평면파
    사실 연구실에서 직접 만든 모의실험 프로그램은 뭔가 새로운 것을 시도할 때마다 한번에 제대로 된 결과를 내놓는 경우가 거의 없다. 앞에서 말한 세 가지 버그의 유형 중의 하나 이상이 꼭 말썽을 부린다. 이번에는 내 세심함이 부족해서 생긴 문제인데, 세 가지 유형 중의 하나로 따지자면, 물리적 이해의 깊이 부족이지 않을까 한다.

    첫 그림의 왼쪽과 오른쪽 끝을 비교해보자. 오른쪽 경계에 다다른 전자기파는 다시 왼쪽 경계로 다시 입력된다고 했는데, 뭔가 이상하지 않은가? 왼쪽과 오른쪽의 경계를 맞춰보면 일치하지 않고 약간 틀어져 있는 것을 알 수 있다. 즉, 어느 정도의 위상차가 있다. 평면파의 위상을 맞추려면, 오른쪽 끝의 전자기파와 왼쪽 끝의 전자기파에 일정 위상차를 주어야 하는데, 이런 위상차를 주려면 전자기파를 복소수로 나타내야 한다. 복소수로 나타낸 전자기파는 단순한 곱셈으로 진폭의 변화 없이 일정 위상만큼 변화를 줄 수 있다.

    그럼, 위에서 열거한 것 중에서 뭐가 문제일까? 개별 요소들은 각기 다른 환경에서 검증을 진행해 온 상태이다. 아직 검증하지 못한 부분에 버그가 존재할 수도 있고, 이들의 조합에서 문제가 발생할 수도 있다. 아니면, 내가 뭔가 잘 못 생각하고 있을 수도 있다. 내가 뭘 놓치고 있을까? 모든 가능성을 염두에 두고 print를 이용한 디버깅 정보 수집에 나섰다. 그러던 중 우연히 프로그램 일부가 실수 전자기파를 사용하고 있음을 알 수 있었다. 그리고 그 부분을 찾아보니 입력 광원 구현부였다. 입력 평면을 따라 전자기파의 실수부는 계속 변하는데, 허수부는 일정 위상(0)을 유지하고 있으니, 당연히 문제가 되었던 것이다. 이를 수정하니, 첫 번째 그림과 같은 정상적인 평면파를 볼 수 있었다.

    2010년 12월 29일 수요일

    파이어폭스 검색을 더욱 은밀하게

    구글은 검색 정보를 암호화해서 전송할 수 있는 Google search over SSL을 제공한다. 이 사이트에 직접 들어가서 검색을 하면 검색어, 검색결과 등이 암호화되어 네트워크에서 여러분의 검색정보를 영탐할 수 없게 할 수 있다. 하지만, 파이어폭스의 우측 상단에 있는 검색바를 이용한다면, 검색정보가 네트워크에 그대로 드러나게 된다.

    Google search over SSL

    이 검색창도 Google search over SSL 서비스를 이용하도록 하면, 여러분의 검색정보를 보호할 수 있다. 검색바의 왼쪽에 있는 화살표를 아이콘을 누르면 제일 아래에 '검색 사이트 관리...'를 볼 수 있다. 여기서 '추가할 검색 사이트 찾기...'를 클릭하면, 파이어폭스 애드온 페이지로 연결된다. 여기서 Google search over SSL 관련 애드온을 찾을 수 있는데, 페이지 오른쪽 위편의 '부가 기능 검색하기' 서식에 'Google SSL'이라고 검색하면 관련 SSL을 찾을 수 있다. 몇몇 애드온을 찾을 수 있는데, SSL Search Bar가 제작한 Google (SSL)을 설치해보자. 오른쪽의 녹색 'Firefox에 추가' 버튼을 누르면, 설치가 진행된다. 이제 브라우저의 검색창 아이콘을 다시 클릭해보면, 'Google (SSL)'이 설치된 것을 알 수 있다.

    Google (SSL)이 제대로 작동하는 것은 몇 가지 사항을 통해서 확인할 수 있다. 먼저, 브라우저의 검색바에 Google 대신에 Google (SSL)이라고 쓰여 있는 것을 확인할 수 있다. 한번 검색을 해보자. 검색 결과 페이지의 구글 마크에 조가만 SSL 문구를 볼 수 있다. 또, 주소창을 보면 프로토콜이 http가 아니라 https인 것을 알 수 있다. 이제 좀 더 안전한 검색을 할 수 있는 환경이 구축되었다.

    Google (SSL)을 사용하도록 설정한 검색바

    참고로, SSL 검색에서는 지원되지 않는 기능이 많이 있다. 가장 눈에 띄는 것은 특정일에 따라 변하는 구글 로고를 볼 수 없다는 것! 점차 많은 서비스를 SSL로 제공한다고 하니 일단 기다려보자.