goldenSectionLiberate.mjs

import get from 'lodash-es/get.js'
import isNumber from 'lodash-es/isNumber.js'
import isnum from 'wsemi/src/isnum.mjs'
import isfun from 'wsemi/src/isfun.mjs'
import cdbl from 'wsemi/src/cdbl.mjs'
import setpBracket from './setpBracket.mjs'
import goldenSection from './goldenSection.mjs'


/**
 * 黃金比例搜尋法(1維),求解最小值所在
 *
 * Fork: {@link https://github.com/scijs/minimize-golden-section-1d/blob/master/index.js minimize-golden-section-1d.js}
 *
 * Unit Test: {@link https://github.com/yuda-lyu/w-optimization/blob/master/test/goldenSectionLiberate.test.js Github}
 * @memberOf w-optimization
 * @param {Function} fun 輸入適應函數,將傳入變數x,需回傳適應函數值,以求解最小值所在起訖範圍為目標
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {Number} [opt.minTolerance=1e-8] 輸入最小收斂門檻值數字,預設1e-8
 * @param {Number} [opt.maxIterations=100] 輸入最大迭代次數整數,預設100
 * @returns {Promise} 回傳Promise,resolve為求解後結果物件,含鍵值x,y,x為求解後變數組,y為最優適應函數值,reject為失敗訊息
 * @example
 *
 * async function test() {
 *
 *     function fun(param) {
 *         let x = param / 180 * Math.PI
 *         return Math.sin(x)
 *     }
 *
 *     console.log(await goldenSectionLiberate(fun, 0))
 *     // => { count: 67, y: -1, x: -89.99999939787351 }
 *
 * }
 *
 * test()
 *     .catch((err) => {
 *         console.log(err)
 *     })
 *
 */
async function goldenSectionLiberate(fun, x, opt = {}) {

    //check fun
    if (!isfun(fun)) {
        return Promise.reject('invalid fun')
    }

    //check x
    if (!isnum(x)) {
        return Promise.reject('x is not a number')
    }

    //xMin
    let xMin = get(opt, 'min')
    if (isnum(xMin)) {
        xMin = cdbl(xMin)
    }

    //xMax
    let xMax = get(opt, 'max')
    if (isnum(xMax)) {
        xMax = cdbl(xMax)
    }

    //countCalc
    let countCalc = 0

    //calc xMin and xMax
    if (!isNumber(xMax) || !isNumber(xMin)) {

        //setpBracket
        let sb = await setpBracket(fun, x, opt)
        // console.log('setpBracket sb', sb)
        xMin = get(sb, 'min')
        xMax = get(sb, 'max')

        //check
        if (!isNumber(xMin) || !isNumber(xMax)) {
            return Promise.reject('can not find bounds by setpBracket')
        }

        //累加countCalc
        countCalc += sb.count

    }

    //goldenSection
    let r = await goldenSection(fun, xMin, xMax, opt)
    // console.log('goldenSection r', r)

    //累加countCalc
    countCalc += r.count

    //save
    r.count = countCalc

    return r
}


export default goldenSectionLiberate