GSL 디자인 문서(*)

참고

본 문서는 Mark Galassi, James Theiler 와 Brian Gough 가 저술한 GNU Scientific Library – Design document 의 번역본 입니다.

이 문서는 프로젝트의 초창기에 작성되었기 때문에 몇몇 내용들은 현재 라이브러리의 제공 기능들과 맞지 않을 수도 있습니다. 차후 제공 예정으로 서술된 기능이 현재 라이브러리에서 이미 구현되어 있기도 합니다. 일부 내용의 갱신이 이루어지지 않았기 때문에 본 문서는 해당 내용을 일부 수정했습니다(*).

프로젝트의 시작

과학자와 공학자들이 수치해석 소프트웨어나 모듈을 개발할 때, 다음과 같은 라이브러리의 필요성이 대두되어왔습니다.

  • 자유롭게 사용이 가능해 사용자들이 사용, 재배포와 수정이 가능한 라이브러리(자유는 공짜의 의미가 아닙니다).

  • 현대적인 코딩, 호출, 스코프 규약을 따라 작성된 C 라이브러리.

  • 정갈하고 교육적인 문서를 제공하는 라이브러리; 가급적 TeXinfo로 작성된 문서를 의미합니다. 이는 온라인 베포 및 TeX 변환에 매우 용이합니다.

  • 고품질의 최신 알고리즘들을 포함하는 라이브러리.

  • autoconfautomake 를 사용해, 손쉬운 이식성과 구성이 가능한 라이브러리.

  • GNUlitically correct 1 한 라이브러리

기존의 수치 해석 라이브러리들을 사용할 수도 있지만 해당 라이브러리들은 다음과 같은 장단점이 있습니다.

표 1 여러 수치해석 라이브러리

라이브러리

설명

Netlib

Netlib는 AT&T에서 관리하는 라이브러리로, 인터넷 상에서 가장 발전된 수치 해석 알고리즘들을 제공합니다. 불행히도, 대부분의 소프트웨어가 포트란으로 작성되어 있어 대다수의 상황에서 낮선 호출 규약을 사용해야하고, 매우 파편화되어 있어 Netlib의 사용에 큰 노력이 필요합니다.

GAMS

GAMS는 과학 계산 소프트웨어들의 집합체입니다. 하지만, Netlib와 같이, 각각의 기능들마다 구현체의 질과 문서화 수준이 매우 천차만별입니다.

Numerical Recipes

Numerical Recipes는 명확한 방법으로 알고리즘들을 잘 설명하는 훌륭한 책입니다. 하지만, 이 책의 저자는 해당 서적에 있는 코드의 사용은 허용하고 있으나, 재배포에 제약이 있습니다. 따라서 Numerical Recipes는 자유롭지 않습니다. 무엇보다도, 해당 책의 코드 구현체는 포트란스럽다( Fortrannitis )는 평과 다른 한계들이 있습니다. Reviews of Numerical Recipes

SLATEC

SLATEC는 1970년도 Department of Energy program에서 작성된 수치 해석 소프트웨어들의 대규모 집합체입니다. 해당 소프트웨어들은 자유 이용 저작물으로 배포되고 있습니다. 각각의 기능들은 잘 검증되어 있고, 그 시기 한계가 있기는 하지만 정갈하게 잘 조직된 구조를 가지고 있습니다. GSL은 현대적인 SLATEC를 목적으로 하고 있습니다.

NSWC

NSWC는 Naval Surface Warfare Center numerical library의 약자입니다. 자유 이용 저작물으로 배포되는 고수준의 대규모 포트란 라이브러리입니다. 이 라이브러리는 문서를 찾기가 매우 힘듭니다. 출판본의 일부 복사본이 확인되었을 뿐입니다.

NAG와 IMSL

NAG와 IMSL는 모두 상업적으로 판매되는 고수준의 수치 해석 라이브러리입니다. NAG 라이브러리는 IMSL보다 더 많은 기능과 발전된 형태를 가지고 있습니다. IMSL 라이브러리는 편의성에 더 치우쳐져있고, 기본 인자들을 광범위한 가변 인자 배열을 사용해 에뮬레이트합니다.

ESSL와 SCSL

ESSL와 SCSL는 각각 IBM과 SGI에서 상업적으로 판매하는 라이브러리입니다.

Forth Scientific Library

Forth Scientific Library는 Forh 사용자들만을 대상으로 합니다.

Numerical Algorithms with C

Numerical Algorithms with C, G. Engeln-Mullges, F. Uhlig는 서적과 함께 제공되는 ANSI C로 작성된 훌륭한 수치 해석 라이브러리입니다. 코드 사용이 가능하지만, 자유 소프트웨어가 아닙니다.

NUMAL

NUMAL 라이브러리의 C 버전은 H.T. Lau에 의해 작성되었으며, “A Numerical Library in C for Scientists and Engineers” 제목의 책과 디스크로 출판되었습니다. 코드 사용이 가능하지만, 자유 소프트웨어가 아닙니다.

C Mathematical Function Handbook

C Mathematical Function Handbook by Louis Baker는 “Handbook of Mathematical Functions” by Abramowitz and Stegun의 수학 함수들에 대응되는 근사와 C 구현체 라이브러리입니다. 코드 사용이 가능하지만, 자유 소프트웨어가 아닙니다.

CCMATH

CCMATH by Daniel A. Atkinson 는 GSL과 비슷한 범주를 다루는 C로 작성된 수치해석 라이브러리입니다. 코드가 간결한 장점이 있습니다. 초기 버전은 GPL 라이센스 하에서 배포되었지만, 불행히도 최근 버전은 LGPL로 바뀌었습니다.

CEPHES

CEPHES는 C로 작성된 고품질의 특수 함수 구현체 모음입니다. GPL 라이센스가 아닙니다.

WNLIB

WNLIB는 소규모의 수치 해석 C 구현체의 집합입니다. Will Naylor가 작성했으며, 자유 이용 저작물입니다.

MESHACH

MESHACH는 C로 작성된 포괄적인 행렬-벡터 선행 대수 라이브러리입니다. 자유롭게 사용가능하나 GPL은 아닙니다.

CERNLIB

CERNLIB는 대규모의 고품질 포트란 라이브러리로 CERN에서 개발되어 많은 세월동안 사용되었습니다. 본래 비자유 소프트웨어였으나 최근 GPL 라이센스로 베포되고 있습니다.

COLT

COLT는 자바로 작성된 자유로은 수치 해석 라이브러리로 CERN에서 Wolfgang Hoschek가 작성했습니다. 이 라이브러리는 BSD 라이센스 아래에서 베포됩니다.

GSL은 실제 수치 해석 전문가나 그들의 대학원생이 기여할 환경을 제공하는 것을 장기적인 목표로 삼고 있습니다.

기여

이 디자인 문서는 1996년도에 작성되었습니다. 2004년에 GSL의 기본 기능들이 완성되었고, 개발자들은 새로운 기능 추가를 주 목표로 하지 않습니다.

프로젝트에서 일반적으로 우선시 하는 작업은 라이브러리 내 함수들의 안전성, 라이브러리의 일관성과 보고된 버그 수정들입니다.

잠재적 기여자들은 GSL CVS 저장소의 BUGS 파일에 나열된 버그들을 조사하고 수정해 라이브러리에 먼저 익숙해지는 것을 권장합니다.

개발한 패키지를 한번에 라이브러리에 추가하는 행위는 일반적으로 권장되지 않습니다. 많은 양의 새 코드들은 다른 구현체들과 완성도 면에서 큰 차이를 야기할 수 있기 때문입니다.

라이브러리의 안전성 유지를 위해 이러한 새 기능들은 GSL 프로젝트 최상단에 패키지로 만들어 개발자가 각각 독립적으로 유지보수하는 것을 권장합니다. 이는 Perl CPAN 아카이브나 TEX CTAN 아카이브등과 같은 자유 소프트웨어 프로젝트에서도 사용하는 방법입니다.

패키지

GSL의 설계는 라이브러리 안에 존재하는 기능들을 추가적인 확장 기능들과 간단하게 연결해 사용할 수 있게 합니다. 예로, 별도의 라이브러리로 제공되는 추가적인 난수 생성기 rngextra 와 함께 다음과 같이 사용할 수 있습니다.

$tar -xvfz rngextra-0.1.tar.gz
$cd rngextra-0.1
$./configure; make; make check; make install
$...
$gcc -Wall main.c -lrngextra -lgsl -lgslcblas -lm

아래 내용은 패키지 디자인 방법에 관한 것입니다. 이 방법은 GSL 스스로 패키지들의 일관성을 보장해 실 사용자들이 사용하기 쉽고, 향후 GSL에 포함될 잘 검증되고 인기 있는 패키지를 해당 패키지만으로 베포할 수 있게하기 위함입니다.

  • 이 문서에서 제공하는 GSL과 GNU 코딩 표준 규약을 준수해야합니다.

    이는 표준 GNU 패키징 도구들을 이용하는 것으로, Automake 의 사용, Texinfo 를 사용한 문서화와 test suite 를 제공함을 의미합니다. test suitemake check 를 실행해 검증해야하고, GSL에서 제공하는 검증 함수들을 사용해 결과가 PASS:/FAIL: 로 출력되도록 해야합니다. libtool 사용은 필수가 아닙니다. 패키지는 충분히 작게 만들 수 있고, 정적 라이브러리는 손쉽게 만들수 있기 때문입니다.

  • 패키지를 위한 새로운 접두사를 만들어야 합니다. gsl_ 은 라이브러리 내부에서 이미 사용되는 접두사이니 사용하면 안됩니다.

    예로, 별도의 난수 생성자는 rngextra 라는 접두사를 사용할 수 있습니다.

    #include<rngextra.h>
    gsl_rng * r = gsl_rng_alloc (rngextra_lsfr32);
    
  • 개발단계를 잘 반영하는 버전 숫자를 사용해야 합니다.

    일반적으로 0.x 는 알파 버전으로 기능의 보증성이 없는 버전을 의미합니다. 0.9.x 는 베타 버전으로 필수적인 기능이 완성되었고, 소소한 변화와 버그 수정만 남은 경우를 의미합니다. 첫번째 정식 베포는 1.0 입니다. 1.0 이나 어느 차후 버전이든 간에, 해당 베포판은 잘 정의된 API를 제공해야합니다. API는 갱신과정에서 변경되어서는 안되며, 기존 코드의 수정이 필요 없도록, 작동에 있어 호환성을 가져야합니다(버그 수정은 제외합니다). 패키지에서 API의 변경이 필요한 경우 새로운 정식 베포가 필요합니다(예, 2.0 과 같은).

  • GNU 일반 공중 사용 허가서 (GPL)을 사용해야 합니다.

    패키지가 향후 GSL에 포함되기를 원한다면, 저작권 고지를 얻는 일반적인 절차를 따를 수 있습니다.

GSL에 추가할 새로운 기능은 별도의 패키지로 만들어 따로 개발되고 해당 패키지의 개발 과정이 일정 수준 이상이 되면 GSL의 정식 패키지로 포함됩니다. 만드는 패키지의 갱신 소식을 다음에 이메일

gsl-discuss@sourceware.org

에 올리면 GSL 공식 사이트 <https://www.gnu.org/software/gsl/#extensions> 에 추가 할 수 있습니다(*) 2 .

GSL의 디자인

언어

GSL 라이브러리는 C 언어 하나 만을 사용합니다.

이미 존재하는 컴파일러의 사용이 가능하며, 구조가 간단하고, 손쉽게 범용성을 얻을 수 있는 장점이 있습니다.

다른 언어를 위한 인터페이스

다른 언어를 위한 랩핑은 “별도의” 패키지로 제공됩니다. “핵심” 라이브러리 패키지로는 제공되지 않습니다. 해당 랩핑은 각각의 기여자들이 별도 관리합니다.

랩핑을 위한 표준 도구들을 사용하는 것을 권장합니다. swing이나 g-warp가 있습니다.

구현하는 기능

존재하는 라이브러리들에 있는 모든 기능을 대상으로합니다.

구현하지 않는 기능

  • GPL 라이센스 하에 베포되는 고품질의 패키지에 있는 기능

  • 너무 광대한 기능 - 하위 기능이 아닌 응용 프로그램 수준을 만드는 것을 의미합니다.

    예를 들어, 편미분 방정식(PDE)의 풀이를 위한 기능은 매우 크고 전문화된 응용 프로그램으로 제공되는 경우가 빈번합니다. 이는 매우 다양한 편미분 방정식과 해, 방법들이 존재하기 때문입니다. 이러한 종류의 기능들은 각각 작은 기능들로 분할해서 남겨야합니다. 이런 경우는 사용자들에게 해당하는 좋은 응용 프로그램들을 추천하는 것이 좋습니다.

  • 독립적으로 별도 제공되었을 때 유용한 것들

    날짜와 시간등을 조작하는 기능이나, 재정 관련 함수들은 “과학 계산” 라이브러리에 포함될 수 있습니다. 이는 의심할 여지가 없지만, 이러한 모듈은 다른 프로그램들에서도 독립적으로 사용할 수 있어, 별도의 라이브러리 사용이 더 유용합니다.

수치해석 라이브러리의 디자인

수치해석 라이브러리의 작성을 할 때, 필연적으로 라이브러리의 완전성간결성 사이에서 갈등하게 됩니다. 완전성은 라이브러리 내부의 객체와 기능들이 서로 서로에게 적용될 때 이러한 연산의 결과들이 라이브러리 내부의 객체들로 표현될 수 있음을 의미합니다. 이러한 성질을 닫혀 있다라 표현합니다 4 .

수학 객체들은 무한히 많은 방법으로 결합하거나 표현할 수 있습니다. 예를 들어서, 스칼라 장을 미분해 벡터 장을 표현할 수도 있고, 벡터 장을 이용해 스칼라 장을 얻을 수도 있습니다.

수학 라이브러리를 작성할 때, 무의식적으로 이러한 모든 가능한 객체를 라이브러리로 구현하려는 경향이 있습니다. 이는 기능을 하나씩 추가하는 과정에서 점점 뚜렷하게 나타납니다. 단지 기능 하나만 더 구현하면 되는 일이기에 하지 않을 이유가 없기 때문입니다.

하지만, 큰 그림을 봅시다. 그 누구도 “모든 가능한 수학 구조와 대상을 C언어로 구조를 이용해 나타내고 싶다.”라고 말하지 않습니다. 이러한 전략은 종국엔 반드시 실패하게 됩니다. C와 같은 프로그래밍 언어로 나타낼 수 있는 복잡도는 한계가 있습니다. 이러한 언어에서 수학의 복잡한 구조와 객체들을 재현하려는 시도는 결국 유지 보수가 불가능한 코드를 만들어냅니다. 그러나 이러한 경향을 미리 제거하면, 손쉽게 라이브러리의 구현에 도달할 수 있습니다.

완전성보다는 간결성이 더 좋은 선택입니다. 라이브러리 내의 새로운 기능을 디자인할 때, 가능한 한 모듈들이 독립적으로 작동할 수 있도록 작성해야 합니다. 만약, 모듈 간의 상호 의존성이 시도된다면, 어디까지 독립성을 위반할지 확실히 정해야 합니다.

코드 재사용

라이브러리 전체를 사용할 필요 없이, 각각의 코드 파일을 사용자가 만드는 프로그램에 포함할 수 있으면 매우 유용합니다. 이와 같은 독립 실행형 파일이 되도록 함이 권장됩니다. 컴파일 과정에서 당연히, 사용자가 GSL_ERROR 와 같은 몇몇 매크로들을 정의해야 할 수도 있습니다. 이런 행위까지는 괜찮습니다. 이러한 예시로 라이브러리 내의 단일 난수 생성기(single random number generator)를 볼 수 있습니다.

표준과 규약

이 프로젝트에 참여하는 사람들은 코딩 표준과 규약을 준수해야합니다. 해당 프로젝트에서는 다음의 표준과 규약들을 따릅니다.

  • GNU 코딩 표준

  • ANSI 표준 C 라이브러리 규약

  • GNU C 라이브러리 규약

  • glib: GTK 지원 라이브러리 규약

이러한 표준을 위한 참고 문헌들으로 다음을 참고할 수 있습니다.

수학 수식은 Abramowitz & Stegun의 Handbook of Mathematical Functions 를 따릅니다. 이 책은 수학계에서 자명한 참고 문헌이며, 자유 이용 저작물으로 사용할 수 있습니다.

본 프로젝트에서 공유하는 정신은 ” C로 생각하라 ” 입니다. 프로젝트가 C로 이루어지기 때문에, 다른 언어의 특징을 흉내 내기 보다는 C에 집중해, 어떤 점이 C에서 자연스러운가를 생각해야 합니다. C에서 부자연스러워 다른 언어의 형태로 시물레이션해야한다면, 해당 사항들은 본 프로젝트에서 포함하지 않을 것입니다. 해당 기능을 없으면 라이브러리에서 특정 기능의 제공이 어렵거나 제한된 버전만 제공한다 하더라도 해당 기능은 제외되어야 합니다. 라이브러리를 지나치게 복잡하게 만드는 일은 가치가 없습니다. 다른 언어들에도 다양한 수치 해석 라이브러리들이 있으며, 해당 언어에서 사용하는 기능이 필요하다면, C 라이브러리를 강제로 사용하는 대신 해당 언어의 라이브러리를 사용하는 것이 현명합니다.

참고

BJG

C 가 매크로 어셈블러라는 사실을 항상 기억하는 것이 좋습니다. 특정 기능이 너무 복잡하다면 스스로 “이 기능을 매크로-어셈블러로 작성할 수 있는가?”를 생각해보길 바랍니다. “아니다”라면 해당 기능은 GSL에 포함하지 말아야 합니다.

다음의 논문을 참고해 볼 수 있습니다.

  • Kiem-Phong Vo, “The Discipline and Method Architecture for Reusable Libraries”, Software - Practice & Experience, v.30, pp.107-128, 2000. DOI:10.1002/(SICI)1097-024X(200002)30:2<107::AID-SPE289>3.0.CO;2-D

이 논문은 Wiley Online Library 에서 찾아보거나, 더 이전의 기술 보고서를 IEEE Xplore 에서 다음의 내용으로

  • Kiem-Phong Vo, “An architecture for reusable libraries,” Proceedings. Fifth International Conference on Software Reuse (Cat. No.98TB100203), 1998, pp. 184-194, DOI: 10.1109/ICSR.1998.685743.

찾아볼 수 있습니다.

이식성 있는 C 라이브러리 디자인에 관련한 다음의 논문들이 있습니다.

  • Kiem-Phong Vo, “Vmallo A General and Efficient Memory Allocator”. Software Practice & Experience, 26:1–18, 1996, DOI: 10.1002/(SICI)1097-024X(199603)26:3<357::AID-SPE15>3.0.CO;2-%23

  • iem-Phong Vo. “Cdt: A Container Data Type Library”. Soft. Prac. & Exp., 27:1177–1197, 1997, DOI: 10.1002/(SICI)1097-024X(199710)27:10<1177::AID-SPE125>3.0.CO;2-7

  • David G. Korn and Kiem-Phong Vo, “Sfio: Safe/Fast String/File IO”, Proceedings of the Summer ‘91 Usenix Conference, pp. 235-256, 1991, DOI: 10.1.1.51.6574

소스 코드들은 GNU Coding Standards에 맞추어 탭이 아닌 스페이스만 사용해야 합니다. 탭으로 작성했을 시 이를 스페이스로 바꾸어 주어야 하는데 여러방법이 있습니다. 예로 indent 프로그램을 사용하면:

indent -gnu -nut *.c *.h

-nut 옵션은 탭을 스페이스들로 바꾸어줍니다.

작업전 확인 사항들

기능을 구현하기 전에 관련 내용들에 관한 철저한 조사가 필요합니다. 이는 장기적으로는 많은 시간을 절약해 줍니다. 가장 중요한 두 가지 단계는 다음과 같습니다.

  1. 해당 기능이 이미 자유 라이브러리(GPL이나 GPL-호환)에서 제공하는 기능인지 판별하기. 만약, 이미 존재한다면 재구현할 필요 없습니다. Netlib, GAMs, na-net, sci.math.num-analysis, 그리고 일반적인 인터넷에서 조사를 해보아야 합니다. 이러한 과정은 관련성이 있는 기존의 독점 라이브러리 목록도 조사할 수 있습니다. 다음 단계에서 참고할 수 있도록 해당 목록을 기록하는 것을 권장합니다.

  2. 기존의 상업/자유 라이브러리들의 구현체들에 대한 비교 조사를 수행합니다. 일반적인 API, 프로그램과 하위 기능들간의 통신 방법을 검사하고, 해당 구현체들이 가지거나 가지지 않는 기능들을 조사하니다. 그리고 이들의 관련 핵심 개념과 기능들에 익숙해지도록 분류합니다. 이미 존재하는 라이러리들의 문서 리뷰는 좋은 레퍼런스가 되어주는 것을 잊지 말아야 합니다.

  3. 해당 주제들을 살펴보고 최신 기술이 무엇인지 파악합니다. 가장 최신의 리뷰 논문들을 찾아보고, 다음의 저널들을 검색해 봅시다.

  • ACM Transactions on Mathematical Software

  • Numerische Mathematik

  • Journal of Computation and Applied Mathematics

  • Computer Physics Communications

  • SIAM Journal of Numerical Analysis

  • SIAM Journal of Scientific Computing

GSL이 연구 프로젝트가 아님을 명심합시다. 좋은 구현체를 만드는 일은, 새로운 알고리즘을 만들지 않더라도 충분히 어려운 작업입니다. 본 프로젝트는 구현 가능하고 존재 가능한 알고리즘의 구현체를 목적으로 합니다. 소소한 개선에 시간을 조금 써도 나쁘지는 않지만, 거기에 몰두하지는 말아야합니다.

알고리즘의 선택

가능하면 잘 확장되는 알고리즘을 선택하고 점근적으로 처리를 해야함을 기억해야합니다. 특히 정수 인자가 있는 함수들에서 주의해야 합니다. Abramowitz & Stegun에서는 재귀적 관계와 같이 함수를 정의하는 데 \(O(n)\) 의 시간 복잡도를 가지는 간단한 알고리즘을 많이 사용하고 있습니다. 해당 알고리즘을 그대로 구현하는 데 사용하고 싶을 수 있습니다. 그러나, 이러한 알고리즘은 \(n=O(10-100)\) 에서는 잘 작동할지 몰라도, \(n=1000000\) 인 경우, 원하는 대로 작동하지 않을 것입니다.

비슷하게, 다변량 자료들이 동일한 크기로 조정된 원소들이나 \(O(1)\) 의 복잡도를 가지고 있다고 가정하지 말아야합니다. 알고리즘들은 반드시 내부적으로 필요한 스케일 조정과 균형을 처리해야 하고, 이를 위해 적절한 노름들을 사용해야합니다. (예를 들어, \(\|x\|\) 보다는 \(\|Dx\|\) 를 사용하는 것이 좋습니다. \(D\) 는 스케일 조정을 위한 대각 행렬입니다.)

문서화

문서화: 프로젝트 관리자는 문서화 방법에 대한 예제를 제공해야합니다. 고품질의 문서화는 반드시 필요한 작업입니다. 각 문서는 주제를 소개하고 제공하는 함수들에 대해 세심한 참고를 제공해야합니다. 우선 순위는 함수에 대한 좋은 참고 문헌을 제공하는 것이라 튜토리얼을 반드시 문서에 포함시킬 필요는 없습니다.

사용 설명서에 사용될 그래프를 그릴 때, GNU Plotutils와 같은 자유 소프트웨어를 사용해야 합니다.

어떤 그래프들은 gnuplot과 같이 완전히 자유(아니면 GNU) 소프트웨어가 아닌 프로그램으로 만들어질 수도 있고, 선호하는 프로그램으로 만들 수도 있습니다. 이런 그래프들은 GNU plitutils를 사용한 결과물로 교체되어야합니다.

문헌을 참고할 때는 그 분야의 가장 자명하고, 표준적이며 좋은 문헌을 참고해야합니다. 많이 일어나는 일이지만 덜 알려진 교재나 입문서(예를 들어 학부에서 사용되기 위한)의 참고는 지양해야 합니다. 각 분야의 자명한 참고 문헌의 예로 알고리즘은 Knuth 5 , 통계학은 Kendall & Stuart 6 , 특수 함수들은 Abramowitz & Stegun (Handbook of Mathematical Functions AMS-55) 등이 있습니다.

표준 참고 문헌들은 라이브러리 사용자들에게 더 좋은 접근성을 제공해 줍니다. 만약 이러한 문헌을 사용할 수 없다면 사용자가 문헌을 참고하기 위해 서적을 구입해야 하는 상황을 위해 가능한 한 고품질의 서적을 사용해야 합니다. 고품질의 기준은 GSL 사용 설명서에서 다루는 다른 참고 문헌들을 최대한 많이 다루는 서적을 의미합니다. 서로 다른 책들이 너무나 많이 인용되어 있다면 알고리즘의 세부 사항들을 보기 위해 문헌을 참고해야 하는 사용자들에게 매우 비효율적이고 비싼 희생을 강요하게 됩니다. 참고 문헌들은 일반적인 대학 교재들 보다 판본이 더 오래 유지되어야 합니다. 대학 교재들은 몇년만에 판본이 바뀌는 경우가 흔합니다.

비슷하게 될 수 있으면 원 논문을 인용해야합니다. 그리고 해당 문서들의 복사본은 나중에 사용할 수 있도록 잘 보관하는 것이 좋습니다. 예를 들어 버그 보고나 앞으로 유지 보수에 필요할 수도 있기 때문입니다.

문헌을 찾아보기 위해 도움이 필요하다면 gsl-discuss 메일링 리스트에 도움을 청할 수 있습니다. GSL 개발자들이 논문의 복사본을 얻는 것을 돕기 위한 봉사자 집단이 있고 그들은 좋은 고품질 자료들(도서관)에 접근할 수 있습니다.

참고

James Theiler 왈

그리고, 소프트웨어 문서화에 열과 성을 다할 것을 약속합니다. 이러한 문서화에는 왜 소프트웨어를 사용해야하는지, 정확히 어떤 기능을 하는지, 어떻게 정확한 호출을 할 수 있을지, 대략적으로 어떻게 알고리즘이 작동하는지, 어디서 알고리즘을 얻었는지, 그리고 우리가 작성하지 않은 부분들은 어디서 코드를 얻었느지를 포함할 것입니다. 우리는 모든 패키지를 계산 알고리즘으로 부터 새로 구축하는 것을 추구하지 않습니다. 이러한 재구축 보다는 이미 존재하는 자유롭게 사용가능한 수학 소프트웨어들의 집합체로써 사용되길 원합니다. 또, 우리가 작성하는 이 소프트웨어도 동일하게 사용될 수 있길 바랍니다.

네임 스페이스

모든 외부 호출용 함수와 변수들은 gsl_ 접두사를 가집니다.

모든 외부 호출용 메크로들은 GSL_ 접두사를 가집니다.

모든 외부 호출용 헤더 파일들은 접두사 gsl_ 로 시작하는 이름을 가져야 합니다.

설치되는 모든 라이브러리는 libgslhistogram.a 와 같은 이름을 가져야 합니다.

실행 가능한 모든 설치 프로그램(예를 들어 유틸리티 프로그램들)들은 접두사 gsl- 을 가져야합니다. (- 하이폰(hypen)입니다. _ (underscore)가 아닙니다.)

모든 함수, 변수 이름등은 소문자로, 매크로와 전처리 변수들은 대문자로 써야합니다.

일부 변수와 함수 이름에 대해 추가로:

1p

\(+1\) 을 의미합니다. 예: 함수 log1p(x)k1p 와 같은 변수. 이 변수는 \(=k+1\) 을 의미합니다.

m1

\(-1\) 을 의미합니다. 예: 함수 expm1(x)km1 와 같은 변수. 이 변수는 \(=k-1\) 을 의미합니다.

헤더 파일

헤더 파일들은 반드시 한 번만 포함되어야 합니다. 이를 idempotent 하다라 부릅니다. 예를 들어, 헤더 파일의 내용을 전처리 문구로 감싸서 이를 가능하게 할 수 있습니다.

#ifndef __GSL_HISTOGRAM_H__
#define __GSL_HISTOGRAM_H__
...
#endif /* __GSL_HISTOGRAM_H__ */

대상 시스템

목표로 하는 대상 시스템은 IEEE 대수를 사용하고, 표준 C 라이브러리를 모두 사용가능한 ANSI C 시스템입니다.

함수 이름

각각의 모듈 이름들은 그 모듈 안의 함수들 이름에 접두사로 작용합니다. 예를 들어서 gsl_fft 모듈에는 gsl_fft_init 함수가 있습니다. 모듈들은 라이브러리 소스 트리의 하위 디렉토리들과 대응됩니다.

객체 지향성

알고리즘들은 ANSI C에서 허용하는 한, 객체 지향적이어야 합니다. 캐스팅의 사용이나 상속을 구현하려는 편법은 권장하지 않고 이들과 비슷한 기능들도 작성하지 않도록 주의해야 합니다. 이는 많은 코딩 패턴들을 금지하지만, 해당 패턴들이 라이브러리에 사용하기에는 너무나 복잡하기 때문에 고려하지 않을 것입니다.

참고

C에서 함수 포인터를 사용해 추상화된 기초적인 클래스를 정의할 수 있습니다. rng 디렉토리를 보면 예시를 볼 수 있습니다.

자유롭게 이용가능한 포트란 코드를 재구현 할 때는 해당 코드를 그대로 배열로 옿기기 보다는 구조체 형태의 적절한 객체를 선언해주시길 바랍니다. 구조체는 파일 내부에서 사용할 때만 유용할 수도 있어 반드시 사용자들에게 제공하지는 않아도 됩니다.

예를 들어서 어느 포트란 프로그램이 다음과 같이 반복작업을 하는 부분이 있다면,

SUBROUTINE RESIZE (X, K, ND, K1)

\(X(K, D)\)\(X(K1, D)\) 로 조정될 격자를 의미합니다. 이러한 형태는 구조체를 도입해 좀 더 읽기 편한 형태로 만들 수 있습니다.

struct grid {
    int nd;    /* number of dimensions */
    int k;    /* number of bins */
    double * x;     /* partition of axes, array of size x[k][nd] */
}

void resize_grid (struct grid * g, int k_new)
{
    ...
}

비슷하게, 단일 파일 내에서 반복적으로 사용되는 코드가 있을 경우 정적 함수나 정적 인라인 함수를 정의해서 사용할 수 있습니다. 이는 코드를 typesafe하게 하고, 해당 코드를 사용하는 모든 곳에서 동일한 기능을 하도록 보장해 줍니다.

주석

GNU 표준 코딩 규약을 따릅니다. 인용구는 다음과 같이 쓸 수 있습니다.

“완전한 문장을 쓰고 첫 단어는 대문자를 써야합니다. 문장의 시작을 소문자인 식별자로 해야한다면 대문자로 바꾸면 안됩니다. 철자를 변경하면 다른 식별자를 의미합니다. 소문자로 문장이 시작되길 원치 않는다면 문장을 다르게 써야합니다(예: “The identifier lower-case is …”).”

최소화 된 구조

구조를 최소화하길 바랍니다. 예를 들어 여러 개의 알고리즘들로 풀 수 있는 문제가 있다면 각 경우를 다룰 수 있는 분리된 구조체를 만드는 것이 더 좋습니다. 다시 말해, 런타임 식별자 사용은 권장하지 않습니다. 해당 상황의 예시로 미분값 정보가 있거나 없는 경우를 모두 사용하는 상황이 있습니다.

알고리즘 분해

반복 알고리즘들은 INITIALIZE(초기화), ITERATE(반복), 그리고 TEST(검증) 단계로 분해해 사용자가 반복 과정을 제어가능하게 하고 중간 단계에서 값을 확인 할 수 있게 해야합니다. 이러한 방식은 call-back을 사용하거나 flag를 이용해 중간 값을 출력하도록 제어하는 것보다 더 좋습니다. 사실 call-back의 사용은 권장하지 않습니다. 만일 call-back의 사용이 필요하다면, 이는 알고리즘을 더 세분화해 사용자가 완전히 제어 가능하도록 만들어야한다는 뜻입니다.

예를 들어서 미분방정식을 풀 때 사용자가 개별적인 단계의 해를 실시간으로 확인하며 진행해야 할 경우가 있습니다. 이러한 상황은 알고리즘이 각 단계별로 분해된 형태일 때만 사용 가능합니다. 높은 수준으로 추상화된 분해 알고리즘은 이러한 유연성 측면에서 적절하지 않습니다.

메모리 할당과 소유권

heap 영역에 할당되어야 하는 함수들은 _alloc 으로 끝나야 합니다(예: gsl_foo_alloc ). 그리고 _free 가 붙은 대응 함수로 해제되어야 합니다(gsl_foo_free ).

부분적으로 초기화된 객체에서 오류를 반환해야 하는 경우 함수에 의해 할당된 메모리를 반드시 해제해야 함을 명심해야 합니다.

위험

절대로, 함수 내부에서 임시로(temporarily) 메모리를 할당하고 반환 전에 해제하면 안됩니다. 이는 사용자의 메모리 할당 관리를 방해합니다.

모든 메모리는 할당과 해제가 각각 분리된 함수로 구현되어야 하고, 작업 공간 인자를 전달받아야 합니다. 이 방법을 이용하면 메모리 할당을 세부적인 반복 과정에서 고려하지 않아도 됩니다.

소유권의 혼동을 방지하기 위해, 작업 공간은 다른 작업공간을 소유하면 안됩니다. 각기 다른 상황에서 정확하고 손쉬운 사용을 위해, 작업 공간은 다른 객체로부터 파생되기 보다는 정수 인자를 이용해 생성되어야 합니다.

메모리 레이아웃

라이브러리에서 행렬과 벡터들을 저장하는 데 C 스타일의 포인터-포인터 배열이 아니라 메모리 블럭을 이용합니다. 행렬은 행 순서로 저장되며, 열은 메모리를 따라 연속적으로 저장됩니다.

선형대수 단계

선형 대수학에서 쓰이는 함수는 두가지 단계로 나뉘어져있습니다.

1차원 함수들은 C 형식 인자들 (double *, stride, size) 을 사용해, 일반적인 C 프로그램에서 gsl_vector 함수들을 호출할 필요 없이 간단하게 사용할 수 있습니다.

이 라이브러리의 구현체는 학습 곡선의 최소화를 목표로 합니다. 만약, 어느 사용자가 어느 함수(예를 들어 fft 등의)를 사용한다고 했을 때, gsl_vector 의 기능을 배우는 데 시간을 쏟지 않아도 되는 상황을 목적으로 합니다.

여기서 왜 행렬에 대해서는 같은 방식을 사용하지 않는지 궁금할 수 있습니다. 행렬의 경우 인자 리스트가 (size1, size2, tda) 로 너무 길고 복잡하며, 행과 열의 순서에서 잠재적인 모호성을 피할 수 없기 때문입니다. 이러한 경우에는 gsl_vectorgsl_matrix 를 사용하는 것이 사용자에게 더 편리합니다.

때문에, 라이브러리에서 사용하는 두 단계 구분은 C 타입들에 기반한 저수준 1차원 연산들과 gsl_matrixgsl_vector 에 기반한 고차원 선형 대수 연산들로 나뉘어져 있습니다.

물론, 벡터로 정의된 저수준 함수들을 정의할 수도 있습니다. 필수적인 기능이 아니라, 아직 구현이 되지않았습니다. 하지만, C 인자들에 v->data , v->stride , v->size 를 대신 입력해 간편하게 사용할 수 있습니다. 저수준의 gsl_vector 함수는 많은 편의성을 제공해 줄 수 있습니다.

효율성을 위해 라이브러리 내에서는 BLAS 기능들을 주로 사용하길 바랍니다.

오차 추정

특수 함수들에서 오차의 크기는 “가우스” 오차의 2배로 정해져 있습니다. 이는 \(2 \sigma\) 값으로 계산 결과는 98%의 정확도를 가집니다. 함수의 참 값이 계산된 값의 +/- 오차 범주 내에 있음을 기대할 수 있습니다. (\(1 \sigma\) 의 경우 32% 비율을 차지해 기대할 수 없습니다.) 물론 실제 계산된 결과의 오차가 가우스 오차는 아니지만 이 두 값들은 실용적으로 잘 쓰입니다.

예외와 오류 관리

기본적인 오류 관리 절차는 오류 값의 반환입니다( gsl_errno.h 에서 가능한 값들을 참고할 수 있습니다). GSL_ERROR 메크로를 사용해 오류를 표시할 수 있습니다. 현재 이 매크로의 정의는 완전하진 않지만, 컴파일 시간에 변경될 수 있습니다.

오류를 나타낼 때, 오류 값를 반환하기 보다 항상 GSL_ERROR 매크로를 사용해야 합니다. 이 매크로는 사용자가 해당 오류들을 디버거를 이용해 잡을 수 있게 해줍니다(gsl_error 함수의 중단점을 정의해서 사용할 수 있습니다).

GSL_ERROR 매크로를 사용하지 말아야 할 상황은 반환 값이 오류를 나타내기보다는 특정한 표기를 위한 경우입니다. 예를 들어서 반복 작업등에서 반환 값은 각 반복 단계의 성공, 실패등을 나타낼 수 있습니다. 일반적으로 반복 알고리즘의 “실패”( GSL_CONTINUE 를 반환합니다.)는 빈번히 일어나는 일이고 이런 경우에 GSL_ERROR 를 사용할 필요는 없습니다.

특정 초기화 객체를 이용한 작업에서 발생한 오류와 같이, 사전에 할당된 메모리에서 오류가 발생했다면, 해당 메모리를 해제하는 것을 잊으면 안됩니다.

영속성

라이브러리를 개발할 때 메모리 블럭을 사용하는 객체(예: vector , matrix , histogram ) foo 를 만든다 칩시다. 이 경우 이러한 블럭들을 읽고 쓸 수 있는 함수들을 제공해야 합니다.

int gsl_foo_fread (FILE * stream, gsl_foo * v);
int gsl_foo_fwrite (FILE * stream, const gsl_foo * v);
int gsl_foo_fscanf (FILE * stream, gsl_foo * v);
int gsl_foo_fprintf (FILE * stream, const gsl_foo * v, const char *format);

이 함수들은 오직 메모리 블럭들만을 인자로 가져야 합니다. 블럭의 길이와 같은 연관된 인자는 가지면 안됩니다. 이는 사용자들이 라이브러리에서 제공하는 함수들을 이용해 고수준의 입/출력 기능들을 작성할 수 있도록 하기 위함입니다. fprintf/fscanf 버전의 함수들은 아키텍처 사이에서 이식 가능하도록 작성되어야 하며, 바이너리 버전은 raw 형태의 데이터를 사용해야 합니다. 다음과 같이 실제로 읽고 쓰는 함수들을 구현하면 됩니다.

int gsl_block_fread (FILE * stream, gsl_block * b);
int gsl_block_fwrite (FILE * stream, const gsl_block * b);
int gsl_block_fscanf (FILE * stream, gsl_block * b);
int gsl_block_fprintf (FILE * stream, const gsl_block * b, const char *format);
int gsl_block_raw_fread (FILE * stream, double * b, size_t n, size_t stride);
int gsl_block_raw_fwrite (FILE * stream, const double * b, size_t n, size_t stride);
int gsl_block_raw_fscanf (FILE * stream, double * b, size_t n, size_t stride);
int gsl_block_raw_fprintf (FILE * stream, const double * b, size_t n, size_t stride, const char *format);

반환값 사용

반환값들을 사용하기 전에 항상 변수에 할당을 하고 사용해야합니다. 이 방법은 함수의 디버깅과 반환값의 검사 수정을 용이하게 해줍니다. 만약, 변수가 중요치 않고 임시로 사용된다면, 적절한 범주 내에 포함시켜 사용해야 합니다.

예를 들어서 다음과 같이 쓰기보다는,

a  = f(g(h(x,y)))

중간값을 저장하는 임시 변수들을 사용해 다음과 같이 작성해야 합니다.

{
    double u = h(x,y);
    double v = g(u);
    a = f(v);
}

이러한 작성법은 디버거에서 좀 더 쉽게 검사를 수행할 수 있게하며, 정지점(breakpoint)을 더 정확하게 잡을 수 있게해줍니다. 프로그램의 최적화를 수행하는 컴파일러에서는 이러한 임시 변수들이 자동으로 제거됩니다.

변수 이름

변수 이름에 다음의 이름 규약들을 사용해야 합니다.

dim : 차원의 수.

w : 작업 공간을 가르키는 포인터.

state : 상태 변수를 가르키는 포인터. ( 문자를 저장해야 한다면, s 를 사용합시다.)

result : 결과(반환 값) 포인터.

abserr : 절대 오차.

relerr : 상대 오차.

epsabs : 절대 허용 오차

epsrel : 상대 허용 오차

size : 배열이나, 백터의 크기. 예: double array[size]

stride : 벡터의 stride

size1 : 행렬의 행 갯수.

size2 : 행렬의 열 갯수.

n : 일반적인 정수. 예: 배열의 원소 숫자, fft 등등.

r : 난수 발생자 (gsl_rng ).

자료형 크기

ANSI C가 제공하는 int 자료형은 16bit 크기를 보장함을 명심해야합니다 7 . 시스템에 따라 더 큰 크기를 제공할 수도 있지만 해당 자료형의 크기는 C에서 보장하지 않습니다. 따라서, 32bit 크기의 자료형이 필요하다면 long int 를 사용해야 합니다. 이 데이터형은 최소 32bit의 크기를 보장합니다. 물론 많은 플랫폼에서 int 자료형의 크기가 32bit인 경우가 많습니다. 하지만 이 라이브러리의 코드들은 특정 플랫폼보다는 ANSI 표준을 준수할 것입니다.

size_t

모든 객체(예: 메모리 블럭)들은 size_t 로 크기가 측정되어야 합니다. 따라서, 모든 반복 과정(예: for(i=0; i<N; i++) )은 size_t 의 형태를 가지는 인덱스를 사용해야 합니다.

intsize_t 를 혼용하면 안됩니다. 이 둘은 교환 불가능 합니다.

감소하는 반복문을 사용하고 싶다면 주의해야 하는 데, size_t 자료형은 부호가 없는 자료형이기 때문입니다. 일반적인 감소 반복문보다는,

for (i = N - 1; i >= 0; i--) { ... } /* DOESN'T WORK */

다음과 같이 쓰는 것을 권장합니다. 이는 i=0 근처에서 발생하는 문제를 해결해줍니다.

for (i = N; i > 0 && i--;) { ... }

혼동을 피하고 싶다면 독립적인 변수를 반복문 안에 삽입해 반복 순서를 반대로 바꾸는 것이 좋습니다.

for (i = 0; i < N; i++) { j = N - i; ... }

참고

BJG

원래 제시한 방법은

Note that the post-decrement ensures that the loop variable is tested before it reaches zero. Beware that i will wraparound on exit from the loop. (This could also be written as for (i = N; i--;) since the test for i>0 is equivalent to i!=0` for an unsigned integer)

If you really want to avoid confusion use a separate variable to invert the loop order,

참고

BJG

Originally, I suggested using

for (i = N; i > 0 && i--;) { ... }

which makes the test for i>0 explicit and leaves i=0 on exit from the loop. However, it is slower as there is an additional branch which prevents unrolling. Thanks to J. Seward for pointing this out.

참고

As a matter of style, please use post-increment (i++) and post-decrement (i--) operators by default and only use pre-increment (++i) and pre-decrement (--i) operators where specifically needed.

배열 vs 포인터

함수의 선언과정에서 포인터 인자나 배열 인자들을 모두 사용할 수 있습니다. 표준 C에서는 이 둘이 동일하다고 간주합니다. 그러나, 실용적으로 이 둘을 구분지어서 사용하는 것이 매우 유용합니다. 포인터는 수정할 단일 객체를 나타내고, 배열은 구분 단위를 가지는 객체의 집합으로 간주합니다. 배열의 수정 여부는 const 의 유무에 따릅니다. 벡터의 경우 구분 단위가 별도로 필요하지 않고 포인터 형식이 선호됩니다.

/* real value, set on output */
int foo (double * x);

/* real vector, modified */
int foo (double * x, size_t stride, size_t n);

/* constant real vector */
int foo (const double * x, size_t stride, size_t n);

/* real array, modified */
int bar (double x[], size_t n);

/* real array, not modified */
int baz (const double x[], size_t n);

포인터

가능한 한 수식의 우변에 포인터의 역참고를 사용하지 말아야합니다. 이러한 코드가 필요한 경우 임시 변수의 활용이 더 적절합니다. 이는 컴파일러가 최적화를 더 쉽게 할 수 있게 해주며 가독성이 좋은 코드를 만듭니다. 이에 더해 곱셈이나 역참고에 모두 쓰이는 * 기호의 혼동을 줄여줍니다.

다시 말해,

while (fabs (f) < 0.5)
{
  *e = *e - 1;
  f  *= 2;
}

보다는 다음과 같이 작성하는 것이 좋습니다.

{
    int p = *e;

    while (fabs(f) < 0.5)
    {
      p--;
      f *= 2;
    }

    *e = p;
}

상수화

함수의 선언에서 const 는 포인터에 의해 가리켜지고 있는 객체가 상수일 때 사용합니다. 함수나 특정 범주 내에서 의미있는 변수들도 const 를 사용할 수 있습니다. 이는 상수인 값들을 실수로 수정하는 행위들을 막아줍니다. 이러한 예시로 배열의 길이 등이 있습니다. 이러한 작성방법은 컴파일러의 최적화에도 도움을 줍니다. const 키워드는 함수로 전달되는 인자가 중요한 의미를 가질 때도 사용할 수 있습니다.

의사 템플릿

몇몇 의사 템플릿 매크로들이 templates_on.htemplates_off.h 에 있습니다. block 디렉토리에서 이 기능들의 자세한 사용을 참고해볼 수 있습니다. 가능한 한 사용을 자제해야 하는 것이 좋습니다. 이 기능들을 악몽과 같지만, 사용을 피할 수는 없었습니다.

특히, 주의할 규약은 다음과 같습니다. 템플릿들은 “data”에 작용하는 연산에만 사용되어야 됩니다. 이러한 대상으로 벡터, 행렬, 통계 그리고 정렬이 있습니다. 이 기능은 프로그램이 정해진 형태의 데이터 타입을 생성하는 외부 자료원과 함께 사용해야하는 경우를 다루기 위함입니다. 예로 8 비트로 couter로 생성되는 큰 규모의 문자 배열이 있습니다.

다른 함수들은 부동 소수점에 대해 double 을 사용하거나 정수들에 대해 적절한 정수형을 사용할 수 있습니다. 정수형의 예로 난수에 대해 unsinged long int 가 있습니다. 템플릿의 사용은 라이브러리의 전체 기능들을 제공하기 위함이 아닙니다. 이는 나무 위에서 물고기를 찾는 일과 같이 불가능한 일입니다 8 . 요약하자면, 대부분의 모든 기능들은 일반적인 용도에 적합한 자연적인 자료형 으로 구성되어야 한다는 것입니다. 템플릿은 다른 데이터형이 발생하는 것을 발생하는 몇몇 경우를 처리하기 위해 존재할 뿐입니다.

부동 소수점 작업에서 double 이 ‘자연적인 자료형’으로 간주됩니다. 이는 C 언어의 기본 정신이기도 합니다.

임의의 상수

임의의 상수 사용은 피해야 합니다.

예를 들어서, 1e-30 , 1e-100 이나 10*GSL_DBL_EPSILON 과 같은 “작은” 값들을 구현체 안에 하드 코딩하는 행위를 해서는 안됩니다. 이런 작성법은 일반적인 라이브러리에는 적합하지 않습니다 3 .

변수들의 계산은 IEEE 대수를 따라 정확히 계산해야 합니다. 만약, 계산에서 잠재적으로 오차가 중요해질 수도 있다면, 오차항을 상대적으로 계산한 후 사용자에게 제공해야합니다. 이 과정은 수식의 오차 전파를 해석적으로 분석해 제공해야합니다. 어림짐작으로 제공해서는 안됩니다.

주의 깊게 잘 설계된 알고리즘은 일반적으로 임의의 상수가 불필요하고 사용자가 중요한 계수들에 접근할 수 있어야 합니다.

예를 들어서 다음의 코드를 생각해 봅시다.

if (residual < 1e-30){
    return 0.0; /* residual is zero within round-off error */
}

이 코드는 다음과 같이 수정해 residual 값을 반환하게 해야합니다.

return residual;

이를 이용해 사용자가 residual 값이 계산에 큰 영향을 끼치는 지, 아닌 지 판단할 수 있게 할 수 있습니다.

GSL_DBL_EPSILON 과 같은 상수들을 사용하는 것이 허용되는 경우는 함수를 근사하는 경우입니다. 테일러 급수나 점근적 확장(asymptotic expansions)등을 사용할 수 있습니다. 이러한 경우 _EPSILON 접미사가 붙은 상수들은 임의의 상수가 아닌 알고리즘의 한 구성요소입니다.

검증

각 모듈의 구현체들은 각 기능들에 대한 적절한 검증 절차를 함께 제공해야합니다.

이러한 검증 절차는 라이브러리를 사용해 알려진 값과 일치하는 지 확인하거나, 여러번의 호출을 통해 나온 결과를 통계적으로 분석하는 프로그램들을 의미합니다. 후자의 예로 난수 생성자가 있습니다.

가장 이상적인 상황은 각 디렉토리마다 있는 검증 프로그램이 작성된 코드의 \(100\%\) 를 모두 범주에 두고 있어야합니다. 자명하게도 많은 노력이 필요한 작업입니다. 따라서 가장 중요한 부분을 먼저 검증하고 나머지를 검사하는 방법이 효율적입니다. 검사 과정은 발생할 수 있는 모든 오류 조건들을 명시적으로 유발시켜 검증해야합니다. 함수가 잘못된 인자에 대해 오류를 반환하지 않는 상황은 매우 심각한 결점이기 때문입니다.

참고

Null 포인터를 검증하려하지 말아야 합니다. 사용자가 잘못된 포인터를 전달했을 경우 라이브러리에서 세그멘테이션 오류를 발생시키는 것으로 충분합니다.

검증 과정은 결정적(deterministic)으로 이루어져야합니다. gsl_test 함수를 사용해 각 기능들에 대해 독립적으로 검증을 수행할 수도 있습니다. gsl_test 함수는 주어진 기능들의 검증 결과를 독립적으로 각 줄에 PASS/FAIL 을 내보냅니다. 이를 통해 검증 실패 부분을 명확하게 판정할 수 있습니다.

\(1\)\(0\) 과 같은 간단한 값들은 검증 과정에서 버그를 밝혀내지 못할 수도 있습니다. 예를 들어서, \(x=1\) 변수를 사용하는 경우 \(x\) 와 같이 잠재적 검증 실패를 피할 수 있는 값들을 검증 과정에서 사용해야 합니다.

여러 변수들을 사용해 검증을 하는 경우, 변수들 사이에 관계성이 없는지 확인해야합니다. 변수들 사이에 관계성이 있는 경우 몇몇 버그들이 자동으로 보완되어버릴 수도 있습니다.

검증 프로그램에 난수를 넣어야 할 경우 od -f /dev/random 을 난수의 발생원으로 사용할 수 있습니다.

검증 프로그램에서 sprintf 함수를 사용해서는 안됩니다. sprintf 함수는 검증 프로그램이 자체적으로 가지고 있는 버그를 찾기 힘들게 합니다. gsl_test_... 함수들은 문자열 인자들의 포멧팅을 지원합니다. 이들을 대신 사용해야 합니다.

컴파일

모든 컴파일 과정은 명료하게 이루어져야합니다. 컴파일 과정에서 엄격한 제약들을 넣어 추가로 검사를 수행해야 합니다.

make CFLAGS="-ansi -pedantic -Werror -W -Wall -Wtraditional -Wconversion
-Wshadow -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings
-Wstrict-prototypes -fshort-enums -fno-common -Wmissing-prototypes
-Wnested-externs -Dinline= -g -O4"

그리고 checkergcc 를 사용해 스택(stack)과 힙(heap)에서 발생할 수 있는 메모리 문제를 검증해야합니다. checkergcc 는 최고의 메모리 검사 도구입니다. checkergcc 를 사용할 수 없다면, Electric Fence를 사용해 힙 영역을 검사해야 합니다. 아무런 검사가 없는 것보다는 좋습니다.

메모리 접근을 검사하는 데 valgrind 라는 새로운 도구를 사용할 수도 있습니다.

참고

checkergcc 의 정식 명칭은 GNU Checker 입니다. 해당 프로그램은 개발이 중단 되었고 Valgrind 를 사용할 수 있습니다. 공식 소개 페이지에서도 Valgrind를 권장하고 GNU Checker 페이지는 교육용으로 남겨두었습니다.

Electric Fence는 Bruce Perens 가 작성한 프로그램입니다. 매우 오래된 프로그램이고(manpage는 1993년도 작성되었습니다.) 리눅스 베포판 저장소 등에서 컴파일 된 패키지를 찾을 수 있습니다. github에 소스코드가 공개되어 있습니다. efence

라이브러리가 C++ 컴파일러(g++)로도 컴파일이 이루어지는 지 검사해야합니다. ANSI C로 작성했다면 많은 문제가 발생하지는 않을 것입니다.

스레드 안전성

이 라이브러리는 스레드-안전성을 가지는 프로그램이어야합니다. 모든 함수가 스레드-안전해야하며 정적 변수를 사용하지 않아야합니다.

모든 부분이 스레드-안전해야할 필요는 없지만, 안전하지 않은 부분은 명확히 해야합니다. 예를 들어서 몇몇 전역 변수들이 라이브러리의 전체 행동을 제어하기 위해 사용되기도 합니다. 이러한 예로 범위 확인 기능의 존재 유무나 치명적인 오류 호출 기능 등이 있습니다. 이 값들은 사용자에 의해 직접적으로 접근되고 통제되기 때문에 다중-스레드 프로그램에서 각각의 스레드들에 의해 수정되지 않습니다.

다중 스레드 프로그램에서 GSL 기능들을 호출할 수 없는 경우를 방지하기 위해 명시적으로 스레드 기능을 지원할 필요는 없습니다. 예로 잠금 메커니즘(locking mechanisms) 등이 있습니다.

법적 문제들

  • 모든 기여자들은 작성한 코드들이 GNU 일반 공중 사용 허가서 (GPL) 아래에 베포됨을 명심해야합니다. 이는 당신의 고용인으로 부터 면책 특권을 가짐을 의미합니다.

  • 존재하는 코드와 알고리즘들의 소유권을 명확히 이해해야합니다.

  • 각 기여자들은 선호에 따라 작성한 코드들의 소유권을 유지하거나 FSF로 베포되는 것에 서명할 수도 있습니다. GPL에는 표준적인 면책 특권이 있습니다(확인해 보십시오). 면책 특권을 더 구체적으로 작성수록 고용주가 받아들일 가능성이 커집니다. 예를 들어 다음과 같이 작성할 수 있습니다.

    Yoyodyne, Inc., hereby disclaims all copyright interest in the software
    `GNU Scientific Library - Legendre Functions' (routines for computing
    legendre functions numerically in C) written by James Hacker.
    
    <signature of Ty Coon>, 1 April 1989
    Ty Coon, President of Vice
    
  • 자명하게도, 비-자유 코드들을 사용하거나 가져오면 안됩니다.

    특히, Numerical RecipesACM TOMS 에서 코드를 가져오거나 번역해오면 안됩니다. Numerical Recipes는 제약 있는 허가서 아래에 있고 자유 소프트웨어가 아닙니다. 출판사인 Cambridge University Press는 책과 그 안의 모든 코드들에 대해 저작권을 행사할 권리가 있고 이는 함수, 변수들의 이름 그리고 수학적으로 정의된 하위식 순서도 포함합니다. GSL에 있는 기능들은 어떠한 방식으로든, Numerical Recipes을 참고하거나 기반해 있으면 안됩니다. TOMS(Transactions on Mathematical Software)에서 출판한 ACM 알고리즘은 자유 이용 저작물이 아닙니다. 물론, 인터넷에 공개되어 있기는 하나, ACM 사용자들은 특수한 비-상업적 허가서 아래에 사용가능하고 GPL과 호환되지 않습니다. 해당 허가서의 자세한 내용은 ACM Transactions on Mathematical Software의 표지나, ACM 웹사이트에서 확인가능합니다. 확실하게 자유로운 허가서 GPL이나 자유 이용 저작물 아래에서 사용가능한 코드만을 사용해야 합니다.

    허가서가 없다고 해당 코드들이 자유 이용 저작물인 것이 아닙니다. 명백한 허가서 조항이 필요하고, 저자에게 재확인 해야합니다.

    참고

    사견으로, 수치 해석에 관한 고전적인 책의 알고리즘들은 참고할 수 있다고 생각합니다.

    BIJ 왈: 코드가 독립적으로 구현되고, 기존 소프트웨어에서 복사된 경우가 아니라면 가능합니다.

비 유닉스 이식성

비 유닉스 시스템에서도 이 라이브러리를 사용할 이유는 충분합니다. DOS는 무시하고, Windows95/Windows 등에서의 사용만을 고려하는 것이 현명합니다.

참고

사견으로, 파일 이름이 길어질 수 있을 것 같습니다.

반면에 개발에 있어 비-유닉스 시스템 사용을 강요받아서는 안됩니다.

가장 좋은 방법은 “꼭 필요하지 않으면 XYZ를 사용하지 마십시오.”와 같은 이식성 관련 지침을 내리는 것입니다. 그러면, Windows 유저들은 필요시 스스로 포팅을 할게 할 수 있을 것입니다.

다른 라이브러리와의 호환성

이 프로젝트는 다른 라이브러리들과의 호환성을 우선 순위로 두지 않습니다.

그러나 Numerical Recipes와 같이 광범위하게 쓰이는 라이브러리와 같은 경우, 해당 라이브러리의 사용을 그대로 대체 가능하다면 사용자들에게 유용할 수 있습니다. 이러한 작업이 완성된다면 해당 구현은 프로젝트와 독립적으로 관리될 것입니다.

몇몇 시스템 라이브러리들에 관한 독립적인 문제들이 있습니다. 예로 BSD 수학 함수와 expm1, log1p, hypot 과 같은 함수들이 있습니다. 라이브러리에 포함된 이 함수들은 가까운 시일 내에 거의 모든 플랫폼에서 사용가능해 질 것입니다.

이러한 네이티브 함수들을 작성에서 가장 좋은 방법은 시스템 공급 업체가 제공하는 라이브러리의 장점을 취할 수 있도록 작성하는 것입니다. 예를 들어서 log1p 는 인텔 x86 시스템에서 기계 명령어를 사용할 수 있습니다. 라이브러리에서는 gsl_hypot 과 같이, 필요시 자동으로 이식성있는 구현체들을 자동으로 교체하는 기능들을 autoconf 를 통해 제공합니다. gsl/complex/math.c 에서 hypot 가 어떻게 사용되고 있는지 참고해볼 수 있습니다. gsl_hypot 의 구현체와 대응되는 파일들인 configure.inconfig.h.in 을 예시로 볼 수 있습니다.

병렬 처리 지원

라이브러리의 설계에서 병렬 처리 지원은 고려하지 않습니다. 병렬 처리 라이브러리는 완전히 다른 설계가 필요하고, 다른 응용 프로그램에서 필요로 하지않는 사항들을 요구합니다.

정밀도

알고리즘에서 분지 절단이나 다른 정밀도에 관련된 항들이 있다면 이 항들을 GSL_DBL_EPSILONGSL_DBL_MIN 를 이용해 이들의 거듭 제곱과 조합으로 작성하길 바랍니다. 이러한 방법은은 각 구현체들을 다른 정밀도로 손쉽게 이식할 수 있게 합니다.

잡다한 사항

변수 이름에 l 는 사용하지 말아야 합니다. 숫자 1 과 구분하기 힘듭니다. 오래된 포트란 프로그램에서 매우 흔한 일이었습니다.

마지막 첨언

하나의 완벽한 구현체가 오류 있는 많은 구현체보다 낫습니다.

참고 문헌

수치 해석

  • Numerical Computation (2 Volumes) by C.W. Ueberhuber, Springer 1997, ISBN 3540620583 (Vol 1) and ISBN 3540620575 (Vol 2).

  • Accuracy and Stability of Numerical Algorithms by N.J. Higham, SIAM, ISBN 0898715210.

  • Sources and Development of Mathematical Software edited by W.R. Cowell, Prentice Hall, ISBN 0138235015.

  • A Survey of Numerical Mathematics (2 vols) by D.M. Young and R.T. Gregory, ISBN 0486656918, ISBN 0486656926.

  • Methods and Programs for Mathematical Functions by Stephen L. Moshier, Hard to find (ISBN 13578980X or 0135789982, possibly others).

  • Numerical Methods That Work by Forman S. Acton, ISBN 0883854503.

  • Real Computing Made Real: Preventing Errors in Scientific and Engineering Calculations by Forman S. Acton, ISBN 0486442217.

표준 문헌

  • Handbook of Mathematical Functions edited by Abramowitz & Stegun, Dover, ISBN 0486612724.

  • The Art of Computer Programming (3rd Edition, 3 Volumes) by D. Knuth, Addison Wesley, ISBN 0201485419.

특정 주제

  • Matrix Computations (3rd Ed) by G.H. Golub, C.F. Van Loan, Johns Hopkins University Press 1996, ISBN 0801854148.

  • LAPACK Users’ Guide (3rd Edition), SIAM 1999, ISBN 0898714478.

  • Treatise on the Theory of Bessel Functions 2ND Edition by G N Watson, ISBN 0521483913.

  • Higher Transcendental Functions satisfying nonhomogeneous linear differential equations by A W Babister, ISBN 1114401773.

이용

GNU 과학 계산 라이브러리의 소스코드와 부속 기능들은 “자유”롭게 사용가능합니다. 이 의미는 자유롭게 이들을 사용하고 재 베포할 수 있다는 뜻입니다. 그러나 이 라이브러리가 자유 이용 저작물을 의미하지는 않습니다. 저작권이 존재하며 베포 절차에 제약이 있습니다. 하지만 이 제약들은 협력을 원하는 모든 시민들이 원하는 행위를 보장하기 위해 설계되었습니다. 이 절차는 라이브러리를 이용하고 다시 베포할 때, 또다른 사람이 이 프로그램의 어느 판본도 얻지 못하게 하는 행위를 금지하고 있습니다.

분명히 하고 싶은 점은 이 프로그램의 사용자는 GNU 과학 계산 라이브러리와 관련된 프로그램의 복사본을 제공할 수 있으며, 소스코드를 받거나 원하는 경우 습득할수 있고, 이러한 프로그램을 변간하거나 새로운 무료 프로그램에서 사용할 수 있다는 점입니다. 그리고 이러한 사항들을 모두 알고 있어야 합니다.

모두가 이 권리를 가지고 있고, 어느 개인이 타인으로 부터 이 권리를 박탈하는 행위를 금지합니다. 예를 들어서 GNU 과학 계산 라이브러리와 관련된 코드를 베포할 경우, 라이브러리의 이용으로 부여된 모든 권리를 다른 사람들에게도 똑같이 허용해야합니다. 다른 사람들도 소스 코드를 제공 받거나 가져올 수 있는지 확인하고 이러한 권리를 그들에게 알려야 합니다.

또 스스로를 보호하기 위해 덧붙이면, GNU 과학 계산 라이브러리와 관련된 모든 프로그램은 특정한 보증이 없음을 모두가 알고 있어야합니다. 프로그램들이 누군가에 의해 수정되고 전달되었다면, 해당 판본의 수령자들이 받은 파일은 공식적으로 베포한 파일이 아님을 알아야합니다. 따라서 다른 이들에 의해 알려진 어느 문제들도 신용에 영향을 끼치지 말아야 합니다.

GNU 과학 계산 라이브러리와 관련 소프트웨어 이용에 관한 정확한 조건과 허가서는 라이브러리와 함께 제공되는 GNU 일반 공중 사용 허가서에서 확인할 수 있습니다.

각주

1

GNUlitically correct 은 GNU 코딩 규약을 따르고 autoconf 를 사용하는 프로그램을 뜻합니다(*).

2

원본 내용은 “만드는 패키지의 갱신 소식 정보를 sources.redhat.comgsl-discuss 에 올리면, GSL 웹사이트에 추가시킬 수 있습니다. 예시 패키지 rngextra 는 두 개의 난수 발생기를 가지고 있습니다. 이들은 http://www.network-theory.co.uk/download/rngextra/ 에서 찾을 수 있습니다.” 였습니다. rngextra 는 BJG가 만든 예시 패키지입니다. 공식 홈페이지에도 확장 기능으로 소개되어 있지만 해당 링크는 출판사 서버에 대한 아카이브 링크로 Network Theory가 폐업됨에 따라 해당 파일은 찾을 수 없습니다(*). Monte Carlo and Quasi-Monte Carlo Wiki(roth.cs.kuleuven.be/wiki/Rngextra)에 따르면, Tiny Encryption Algorithm 을 사용한 별도의 예시 난수 발생자였다고 합니다(*).

3

임베디드 프로그래밍에서는 시스템의 한계로 인해 이렇게 프로그래밍하기도 합니다. 하지만 GSL은 과학 계산 라이브러리입니다. 그러한 시스템은 고려 대상이 아닙니다(*).

4

이러한 표현은 수학에서 대수 구조를 정의할 때, 연산에 대해 닫혀 있다라는 정의에서 왔습니다. 수학적으로는 집합 위에 정의된 연산의 모든 결과가 정의된 집합에 있을 때 이를 닫혀 있다라 합니다. 여기서 닫혀 있다는 뜻은 라이브러리에서 제공하는 객체와 기능들이 충분이 방대해 어떠한 연산을 수행하든지 해당 연산의 결과가 표현하는 수학적 구조가 라이브러리 내부의 기능과 객체들에 이미 구현되어 있음을 의미합니다(*).

5

The Art of Computer Programming (TAOCP) (*)

6

THE ADVANCED THEORY OF STATISTICS (*)

7

int는 플랫폼에 따라서 다양한 크기를 가질 수 있습니다. 어떤 플랫폼에서는 32bit, 64bit 크기를 가지고 어떤 플랫폼에서는 16bit의 크기를 가질 수도 있습니다. 대표적으로 아두이노와 같은 AVR 시스템에서 16bit 크기를 가진 경우가 흔합니다. 시스템에 따른 이러한 자료형 크기의 차이는 ISO C 표준 문서의 규약이 int 자료형의 최소 크기 16bit와 자료형에 따른 상대적 크기만을 정해 놓았기 때문입니다. 이로 인해 시스템마다 자료형의 실제 크기는 최소 크기보다 같거나 크기만 하면 다양하게 나올 수 있습니다. int 자료형은 일반적으로 구동 플랫폼의 기본 데이터 처리 타입을 따릅니다. 이는 실행 환경에서 가장 빠른 동작을 보장하기 위함입니다(*).

8

원문은 “putting a quart into a pint pot”로 실현 불가능한 일을 일컫는 표현입니다. quart 는 약 946.353ml이고 pint 는 약 473.176ml입니다(*).