github: https://github.com/devernay/cminpack
主頁: http://devernay.github.io/cminpack/
使用手冊: http://devernay.github.io/cminpack/man.html
CMinpack配置
從github中clone下來原始檔,進入目錄後新建build,使用cmake對上一層目錄內容進行編譯configure->generate。
命令列不熟練可以使用cmake-gui指令,需要選中examples選項才會對樣例進行編譯。
完成後進入build/examples目錄,執行make命令,可以看到已經生成可執行檔案,執行其中任意程式進行測試。
再進入到build/cmake目錄,執行make命令和make install命令,將cminpack.pc安裝到指定目錄(我的電腦上安裝到了/usr/local/lib64/pkgconfig),最後將這個目錄通過/etc/profile新增到pkg的路徑當中(別忘了source執行一下)。
在命令列中輸入pkg-config opencv –libs –cflags
,如果能夠顯示路徑則成功。
[NOTE] 我對pkg-config的使用並不是很瞭解,是模仿著opencv進行配置的。
CMake相關詳解
[NOTE] 因此自己對CMake使用還很不熟練,因此找機會對CMakeList.txt進行學習。
${CMINPACK_SOURCE_DIR}/CMakeList.txt
# 因為Markdown沒有支援CMakeList.txt的高亮,因此用Makefile的高亮將就一下。
# The name of our project is "CMINPACK". CMakeLists files in this project can
# refer to the root source directory of the project as ${CMINPACK_SOURCE_DIR} and
# to the root binary directory of the project as ${CMINPACK_BINARY_DIR}.
# CMINPACK_SOURCE_DIR: CMinpack原始碼的根目錄
# CMINPACK_BINARY_DIR: CMinpack二進位制檔案的根目錄
# 要求的最小CMake版本號
cmake_minimum_required (VERSION 2.6)
# 專案名稱:CMINPACK
project (CMINPACK)
# PROJECT_NAME: CMINPACK
# PROJECT_NAME_LOWER: cminpack
string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
# include其他CMake命令
# 在cminpack_utils.cmake這個檔案中定義了GET_OS_INFO和DISSECT_VERSION兩個巨集指令,後面進行詳細介紹。
include(${PROJECT_SOURCE_DIR}/cmake/cminpack_utils.cmake)
# Set version and OS-specific settings
# CACHE: 快取到本地檔案
set(CMINPACK_VERSION 1.3.6 CACHE STRING "CMinpack version")
set(CMINPACK_SOVERSION 1 CACHE STRING "CMinpack API version")
# 在cminpack_utils.cmake中定義的兩個巨集
DISSECT_VERSION()
GET_OS_INFO()
# Add an "uninstall" target
# CONFIGURE_FILE: 讓普通檔案也能使用CMake中的變數
# 輸入檔案: uninstall_target.cmake.in
# 輸出檔案: uninstall_target.cmake
# IMMEDIATE: 暫時沒找到意思
# @ONLY: 限制只替換被@VAR@引用的變數(${VAR}格式的變數不會被替換)
CONFIGURE_FILE ("${PROJECT_SOURCE_DIR}/cmake/uninstall_target.cmake.in" "${PROJECT_BINARY_DIR}/uninstall_target.cmake" IMMEDIATE @ONLY)
# ADD_CUSTOM_TARGET: 增加一個沒有輸出的目標,使得它總是被構建
# CMAKE_COMMAND: 指向CMake可執行檔案的完整路徑
ADD_CUSTOM_TARGET (uninstall "${CMAKE_COMMAND}" -P "${PROJECT_BINARY_DIR}/uninstall_target.cmake")
# 需要注意,ctest期望在build目錄下找到測試檔案。
enable_testing()
if (OS_LINUX OR ${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
option (USE_FPIC "Use the -fPIC compiler flag." ON)
else (OS_LINUX)
option (USE_FPIC "Use the -fPIC compiler flag." OFF)
endif (OS_LINUX)
# 生成SHARED庫的選項
option (BUILD_SHARED_LIBS "Build shared libraries instead of static." OFF)
if (BUILD_SHARED_LIBS)
message (STATUS "Building shared libraries.")
else ()
message (STATUS "Building static libraries.")
set(CMAKE_RELEASE_POSTFIX _s)
set(CMAKE_RELWITHDEBINFO_POSTFIX _s)
set(CMAKE_DEBUG_POSTFIX _s)
set(CMAKE_MINSIZEREL_POSTFIX _s)
if(WIN32)
add_definitions(-DCMINPACK_NO_DLL)
endif(WIN32)
endif ()
option(USE_BLAS "Compile cminpack using a blas library if possible" ON)
#set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/../build)
# 新增標頭檔案目錄
if(NOT "${CMAKE_PREFIX_PATH}" STREQUAL "")
include_directories(${CMAKE_PREFIX_PATH}/include)
endif()
# cminpack_srcs: 原始碼檔案
set (cminpack_srcs
cminpack.h cminpackP.h
chkder.c enorm.c hybrd1.c hybrj.c lmdif1.c lmstr1.c qrfac.c r1updt.c
dogleg.c fdjac1.c hybrd.c lmder1.c lmdif.c lmstr.c qrsolv.c rwupdt.c
dpmpar.c fdjac2.c hybrj1.c lmder.c lmpar.c qform.c r1mpyq.c covar.c covar1.c
minpack.h
chkder_.c enorm_.c hybrd1_.c hybrj_.c lmdif1_.c lmstr1_.c qrfac_.c r1updt_.c
dogleg_.c fdjac1_.c hybrd_.c lmder1_.c lmdif_.c lmstr_.c qrsolv_.c rwupdt_.c
dpmpar_.c fdjac2_.c hybrj1_.c lmder_.c lmpar_.c qform_.c r1mpyq_.c covar_.c
)
# cminpack_hdrs: 標頭檔案
set (cminpack_hdrs
cminpack.h minpack.h)
# 新增一個名為cminpack的庫
add_library (cminpack ${cminpack_srcs})
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
TARGET_LINK_LIBRARIES(cminpack m)
endif()
# Link with a BLAS library if requested
if (USE_BLAS)
if (NOT BUILD_SHARED_LIBS)
set(BLA_STATIC True)
endif()
find_package(BLAS)
if (BLAS_FOUND)
target_link_libraries(cminpack PUBLIC ${BLAS_LIBRARIES})
set_target_properties(cminpack PROPERTIES LINK_FLAGS "${BLAS_LINKER_FLAGS}")
target_compile_definitions(cminpack PUBLIC -DUSE_CBLAS)
endif()
endif()
# install: 為工程生成安裝規則
# TARGETS版本的install命令
install (TARGETS cminpack
# 模組庫
LIBRARY DESTINATION ${CMINPACK_LIB_INSTALL_DIR} COMPONENT library
# 靜態連結的庫檔案
ARCHIVE DESTINATION ${CMINPACK_LIB_INSTALL_DIR} COMPONENT library
# 動態庫
RUNTIME DESTINATION bin COMPONENT library)
# FILES版本的install命令
# 以相對路徑方式給出的檔名是相對當前原始碼路徑而言的,預設具有OWNER_WRITE, OWNER_READ, GROUP_READ和WORLD_READ許可權
install (FILES ${cminpack_hdrs} DESTINATION ${CMINPACK_INCLUDE_INSTALL_DIR}
COMPONENT cminpack_hdrs)
if (USE_FPIC AND NOT BUILD_SHARED_LIBS)
set_target_properties (cminpack PROPERTIES COMPILE_FLAGS -fPIC)
endif ()
set_target_properties(cminpack PROPERTIES VERSION ${CMINPACK_VERSION} SOVERSION ${CMINPACK_SOVERSION})
# add_subdirectory: 新增子專案
add_subdirectory (cmake)
add_subdirectory (examples)
${CMINPACK_SOURCE_DIR}/cmake/cminpack_utils.cmake
# 獲取系統資訊,設定安裝位置
macro(GET_OS_INFO)
# string(REGEX MATCH 正則表達 輸出變數 )
string(REGEX MATCH "Linux" OS_LINUX ${CMAKE_SYSTEM_NAME})
string(REGEX MATCH "BSD" OS_BSD ${CMAKE_SYSTEM_NAME})
if(WIN32)
set(OS_WIN TRUE)
endif(WIN32)
if(NOT DEFINED CMINPACK_LIB_INSTALL_DIR)
set(CMINPACK_LIB_INSTALL_DIR "lib")
if(OS_LINUX)
if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
set(CMINPACK_LIB_INSTALL_DIR "lib64")
else(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
set(CMINPACK_LIB_INSTALL_DIR "lib")
endif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
message (STATUS "Operating system is Linux")
elseif(OS_BSD)
message (STATUS "Operating system is BSD")
elseif(OS_WIN)
message (STATUS "Operating system is Windows")
else(OS_LINUX)
message (STATUS "Operating system is generic Unix")
endif(OS_LINUX)
endif(NOT DEFINED CMINPACK_LIB_INSTALL_DIR)
# 比如/usr/local/include/cminpack-1
set(CMINPACK_INCLUDE_INSTALL_DIR "include/${PROJECT_NAME_LOWER}-${CMINPACK_MAJOR_VERSION}")
endmacro(GET_OS_INFO)
# 解剖CMinpack版本號
macro(DISSECT_VERSION)
# Find version components
string(REGEX REPLACE "^([0-9]+).*" "\\1"
CMINPACK_MAJOR_VERSION "${CMINPACK_VERSION}")
string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1"
CMINPACK_MINOR_VERSION "${CMINPACK_VERSION}")
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+)" "\\1"
CMINPACK_REVISION_VERSION ${CMINPACK_VERSION})
string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.[0-9]+(.*)" "\\1"
CMINPACK_CANDIDATE_VERSION ${CMINPACK_VERSION})
endmacro(DISSECT_VERSION)
${CMINPACK_SOURCE_DIR}/examples/CMakeLists.txt
# 可選項
# option (選項名 "選項註釋" 預設內容)
option (BUILD_EXAMPLES "Build the examples and tests." ON)
option (BUILD_EXAMPLES_FORTRAN "Build the FORTRAN examples and tests." OFF)
if (BUILD_EXAMPLES)
# Make sure the compiler can find include files from our cminpack library.
include_directories (${CMINPACK_SOURCE_DIR})
# Make sure the linker can find the cminpack library once it is built.
link_directories (${CMINPACK_BINARY_DIR})
# 設定變數內容
# set(變數 內容)
set (FPGM tchkder thybrd thybrd1 thybrj thybrj1 tlmder tlmder1 tlmdif tlmdif1 tlmstr tlmstr1)
set (CPGM ${FPGM} tfdjac2)
# cmpfile: 用於一行行地比較兩個檔案的異同
# cmpfiles could be used by runtest.cmake... for now it's unused
# add_executable(可執行檔名稱 原始檔)
add_executable (cmpfiles cmpfiles.c)
# inspired by http://www.netlib.org/clapack/clapack-3.2.1-CMAKE/TESTING/CMakeLists.txt
# except that here we have to compare the output to a reference
# 測試部分跳過不看
macro(add_minpack_test testname reference)
set(TEST_OUTPUT "${CMINPACK_BINARY_DIR}/examples/${testname}.out")
set(TEST_REFERENCE "${CMINPACK_SOURCE_DIR}/examples/ref/${reference}.ref")
get_target_property(TEST_LOC ${testname} LOCATION)
add_test(${testname} "${CMAKE_COMMAND}"
-DTEST=${TEST_LOC}
-DOUTPUT=${TEST_OUTPUT}
-DREFERENCE=${TEST_REFERENCE}
-DINTDIR=${CMAKE_CFG_INTDIR}
-P "${CMINPACK_SOURCE_DIR}/examples/runtest.cmake")
endmacro(add_minpack_test)
# 遍歷處理每一個目標檔案
foreach (source ${CPGM})
add_executable (${source}_ ${source}_.c)
target_link_libraries (${source}_ cminpack)
if (OS_LINUX)
target_link_libraries (${source}_ m)
endif (OS_LINUX)
add_minpack_test(${source}_ ${source}c)
add_executable (${source}c ${source}c.c)
target_link_libraries (${source}c cminpack)
if (OS_LINUX)
target_link_libraries (${source}c m)
endif (OS_LINUX)
add_minpack_test(${source}c ${source}c)
endforeach(source)
endif (BUILD_EXAMPLES)
# FORTRAN部分跳過不看
第一個CMinpack程式
根據我們閱讀給出的測試樣例的CMake相關檔案,我們可以開始動手寫自己的第一個CMinpack程式。
新建目錄,目錄下放著我們要執行的使用了CMinpack的程式my-cminpack-demo.c。
開始編寫CMakeLists.txt檔案,首先提取出我們需要的內容:
project(my-cminpack-demo)
cmake_minimum_required (VERSION 2.6)
include_directories (${CMINPACK_SOURCE_DIR})
link_directories (${CMINPACK_BINARY_DIR})
add_executable (my-cminpack-demo my-cminpack-demo.c)
target_link_libraries (my-cminpack-demo cminpack)
if (OS_LINUX)
target_link_libraries (my-cminpack-demo m)
endif (OS_LINUX)
然後新建build目錄,在build目錄下執行cmake ..
發現會跳出找不到sqrt函式的錯誤,這個我們能夠一下子聯想到是Linux系統下沒有連線到m庫檔案的原因。雖然我不知道這個判斷為什麼無法執行,但是我們已知在Linux環境下,把它去掉。
得到最終的CMakeLists.txt:
project(my-cminpack-demo)
cmake_minimum_required (VERSION 2.6)
include_directories (${CMINPACK_SOURCE_DIR})
link_directories (${CMINPACK_BINARY_DIR})
add_executable (my-cminpack-demo my-cminpack-demo.c)
target_link_libraries (my-cminpack-demo cminpack)
target_link_libraries (my-cminpack-demo m)
雖然我們make得到可執行檔案,執行後檢視結果(圖中我使用了tlmdif_.c作為測試)
LMDIF使用說明
包括函式名
lmdif
,lmdif1_
- 最小化非線性函式平方和
函式概要
include <minpack.h>
void lmdif1_(void (*fcn)(int *m, int *n, double *x, double *fvec, int *iflag),
int *m, int *n, double *x, double *fvec,
double *tol, int *info, int *iwa, double *wa, int *lwa);
void lmdif_(void (*fcn)(int *m, int *n, double *x, double *fvec, int *iflag),
int *m, int *n, double *x, double *fvec,
double *ftol, double *xtol, double *gtol, int *maxfev, double *epsfcn, double *diag,
int *mode, double *factor, int *nprint, int *info, int *nfev, double *fjac,
int *ldfjac, int *ipvt, double *qtf,
double *wa1, double *wa2, double *wa3, double *wa4 );
詳細描述
lmdif_
的目的是最小化m個n元非線性方程的平方和,使用的方法是LM演算法的改進。使用者需要提供計算方程的子程式。Jacobian矩陣會通過一個前向差分(forward-difference)近似計算得到。
lmdif1_
是相同的目的,但是呼叫方法更簡單一些。
語言備註
這些函式是通過FORTRAN寫的,如果從C呼叫,需要記住以下幾點:
- 名稱重編:
- 2.95/3.0版本的g77下,所有函式以下劃線結尾,後續版本可能會更改;
- 使用g77編譯:
- 即使你的程式全部用C語言寫成,你也需要使用gcc進行連結,因為這樣它會自動匯入FORTRAN庫。只使用g77進行編譯是最方便的(它處理C語言也是OK的);
- 通過引用呼叫:
- 所有函式引數都是指標;
- 列優先陣列:
- z( i , j ) = z[ ( i - 1 ) + ( j - 1 ) * n
- fcn是使用者提供用於計算函式的子程式。在C語言當中fcn需要如下定義:
iflag的值不能被fcn所修改,除非使用者想要終止void fcn(int m, int n, double *x, double *fvec, int *iflag) { /* 計算函式在x點的值,通過fvec返回。*/ }
lmdif
/lmdif1_
。在這個例子中iflag設定為負整數。
lmdif_和lmdif1_的共同引數
m:函式個數;
n:變數個數(n<=m)
x:長度為n的陣列,設定為初始的估計解向量。輸出的時候x內容為最終估計的解向量。
fvec:輸出長度為m的陣列,內容為最終輸出x計算得到的函式解。
lmdif1_的引數
tol:作為輸入,非負數。用於函式終止的條件判斷:
- 平方和小於tol;
- x之間的相對誤差小於tol;
info:作為輸出。如果使用者終止了函式的執行,info將被設定為iflag的值(負數)(詳細見fcn的描述),否則,info的值如下幾種情況:
- 0:輸入引數不合適;
- 1:平方和的相對誤差小於tol;
- 2:x之間的相對誤差小於tol;
- 3:1/2兩種情況同時符合;
- 4: fvec is orthogonal to the columns of the Jacobian to machine precision(這個情況是什麼暫時不是很清楚)
- 5:呼叫fcn的次數達到了200*(n+1)次;
- 6:tol設定過小,平方和無法達到那麼小;
- 7:tol設定過小,x的近似解無法優化到誤差達到那麼小。
iwa:長度n的工作陣列;
wa:長度lwa的工作陣列;
lwa:作為輸入,整數,不能小於mn+5n+m;
[NOTE] 這三個輸入我也不知道作用,從樣例來看不需要初始化。
lmdif_的引數
暫時不用這部分,跳過。
官方樣例解讀
/* lmdif1 例子. */
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <minpack.h>
#define real __minpack_real__
// 使用者自定義的函式f()
// real -> __cminpack_real__ -> 浮點數(double)
void fcn(const int *m, const int *n, const real *x, real *fvec, int *iflag);
int main()
{
int j, m, n, info, lwa, iwa[3], one=1;
real tol, fnorm, x[3], fvec[15], wa[75];
// 函式個數15; 變數數3
m = 15;
n = 3;
// 初始位置做粗略估計
// 1.e0 = 1.0e0 = 1.0
x[0] = 1.e0;
x[1] = 1.e0;
x[2] = 1.e0;
// 為什麼要設定75?
lwa = 75;
/* set tol to the square root of the machine precision. unless high
precision solutions are required, this is the recommended
setting. */
// (建議列印一下看值是多少)
tol = sqrt(__minpack_func__(dpmpar)(&one));
// 需要注意指標
__minpack_func__(lmdif1)(&fcn, &m, &n, x, fvec, &tol, &info, iwa, wa, &lwa);
// 最終的2範數(即平方和開根號)
fnorm = __minpack_func__(enorm)(&m, fvec);
printf(" final l2 norm of the residuals%15.7g\n\n", (double)fnorm);
printf(" exit parameter %10i\n\n", info);
printf(" final approximate solution\n");
for (j=1; j<=n; j++) {
printf("%s%15.7g", j%3==1?"\n ":"", (double)x[j-1]);
}
printf("\n");
return 0;
}
// The problem is to determine the values of x(1), x(2), and x(3)
// which provide the best fit (in the least squares sense) of
// x(1) + u(i)/(v(i)*x(2) + w(i)*x(3)), i = 1, 15
// to the data
// y = (0.14,0.18,0.22,0.25,0.29,0.32,0.35,0.39,
// 0.37,0.58,0.73,0.96,1.34,2.10,4.39),
// where u(i) = i, v(i) = 16 - i, and w(i) = min(u(i),v(i)). The
// i-th component of FVEC is thus defined by
// y(i) - (x(1) + u(i)/(v(i)*x(2) + w(i)*x(3))).
void fcn(const int *m, const int *n, const real *x, real *fvec, int *iflag)
{
/* function fcn for lmdif1 example */
int i;
real tmp1,tmp2,tmp3;
// 實際的y值
real y[15]={1.4e-1,1.8e-1,2.2e-1,2.5e-1,2.9e-1,3.2e-1,3.5e-1,3.9e-1,
3.7e-1,5.8e-1,7.3e-1,9.6e-1,1.34e0,2.1e0,4.39e0};
assert(*m == 15 && *n == 3);
if (*iflag == 0) {
/* insert print statements here when nprint is positive. */
/* if the nprint parameter to lmder is positive, the function is
called every nprint iterations with iflag=0, so that the
function may perform special operations, such as printing
residuals. */
// 這段沒有很看懂,在??情況下列印資訊
return;
}
/* compute residuals */
for (i=0; i<15; i++) {
tmp1 = i+1;
tmp2 = 15 - i;
tmp3 = tmp1;
if (i >= 8) {
tmp3 = tmp2;
}
fvec[i] = y[i] - (x[0] + tmp1/(x[1]*tmp2 + x[2]*tmp3));
}
}
[NOTE] 其他內容有待更新