• CMinpack使用介绍


    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使用说明

    官方英文介绍:http://devernay.github.io/cminpack/lmdif_.html

    包括函数名

    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需要如下定义:
    void fcn(int m, int n, double *x, double *fvec, int *iflag) {
        /* 计算函数在x点的值,通过fvec返回。*/
    }
    

    iflag的值不能被fcn所修改,除非用户想要终止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_的参数

    暂时不用这部分,跳过。

    官方样例解读

    源码: https://github.com/devernay/cminpack/blob/d1f5f5a273862ca1bbcf58394e4ac060d9e22c76/examples/tlmdif1_.c

    /*     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
    
    ", (double)fnorm);
      printf("      exit parameter                %10i
    
    ", info);
      printf("      final approximate solution
    ");
      for (j=1; j<=n; j++) {
        printf("%s%15.7g", j%3==1?"
     ":"", (double)x[j-1]);
      }
      printf("
    ");
      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] 其他内容有待更新

  • 相关阅读:
    ubuntu 完全干净的卸载docker
    thinkphp5 助手函数input的常用方法
    thinkphp6
    docker编排
    Linux下的tar压缩解压缩命令详解
    scp拷贝命令
    一行命令搞定node.js 版本升级
    git 设置文件大小写敏感
    ubuntu1804php安装
    关于权限表的基本设计
  • 原文地址:https://www.cnblogs.com/bemfoo/p/11203993.html
Copyright © 2020-2023  润新知