{"id":13736667,"url":"https://github.com/Vindaar/nimnlopt","last_synced_at":"2025-05-08T12:33:28.024Z","repository":{"id":83686464,"uuid":"126350171","full_name":"Vindaar/nimnlopt","owner":"Vindaar","description":"A wrapper for the nonlinear optimization library Nlopt","archived":false,"fork":false,"pushed_at":"2022-02-08T15:30:24.000Z","size":55,"stargazers_count":16,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-22T22:56:39.570Z","etag":null,"topics":["nim","nlopt","non-linear-optimization","nonlinear"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Vindaar.png","metadata":{"files":{"readme":"README.org","changelog":"changelog.org","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2018-03-22T14:45:58.000Z","updated_at":"2024-08-18T07:49:10.000Z","dependencies_parsed_at":"2023-07-02T21:46:37.924Z","dependency_job_id":null,"html_url":"https://github.com/Vindaar/nimnlopt","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vindaar%2Fnimnlopt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vindaar%2Fnimnlopt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vindaar%2Fnimnlopt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Vindaar%2Fnimnlopt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Vindaar","download_url":"https://codeload.github.com/Vindaar/nimnlopt/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224732129,"owners_count":17360416,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["nim","nlopt","non-linear-optimization","nonlinear"],"created_at":"2024-08-03T03:01:26.125Z","updated_at":"2024-11-15T04:32:15.285Z","avatar_url":"https://github.com/Vindaar.png","language":"Nim","funding_links":[],"categories":["Algorithms"],"sub_categories":["Math"],"readme":"* Nlopt\n\nThis library provides a wrapper for the [[https://nlopt.readthedocs.io/en/latest/][Nlopt]] C library, which is a\nlibrary for non-linear optimizations. It provides many different\nalgorithms for easy comparison separated into local / gradient based,\nglobal / gradient based, local / derivative free and global /\nderivative free algorithms.\n\n** Installation\n\nThis library is part of the Nimble repository. You can install it just\nlike any other Nimble package:\n#+BEGIN_SRC sh\nnimble install nlopt\n#+END_SRC\n\n** Usage\nFor general usage look at the test case in\n[[file:tests/tsimple.nim]]. Despite the name it should be reasonably\nconcise to understand how the library interface is used. For an\nexplanation of the different Nim procedures and types, see\nDocumentation below.\n\n** Documentation\n\nThe C library has an \"object oriented like\" structure. A central\n=nlopt_opt= type, which is created by the user. This object needs to\nbe given to all other =nlopt= functions, of which there are many.\nSee the documentation of the C library here: \nhttps://nlopt.readthedocs.io/en/latest/NLopt_Reference/\n\nHere we only cover the functionality related to the usage of the Nim\nlibrary.\n\nThe Nim library partly follows that structure in the sense that it\nprovides a slightly more astract =NloptOpt= type. This type stores the\nuser defined settings (algorithm, dimensionality, bounds, step sizes,\netc.) and writes them to the C library before the call to the\noptimization function.\n\nInstead of depending on a large amount of different getter and setter\nfunctions, the user sets all parameters on the =NloptOpt= object.\n\nSome functionality is still missing from the Nim interface. General\noptimization with and without gradients, including inequality bounds\nis available already. The other functionality can be used using the\nexported wrapper functions. In that case care needs to be taken about\nthe data types.\n\nThe number of functions / types used by the user is reduced to 5\nfunctions / 3 types:\n*** =proc newNloptOpt= \nAs the name suggests, used to create a new =NloptOpt= object.\nSignature:\n#+BEGIN_SRC nim\nproc newNloptOpt(opt_name: string, nDims: int, bounds: seq[tuple[l, u: float]] = @[]): NloptOpt\n#+END_SRC\nNeeds the algorithm =opt_name= as a string (to be changed to a pure enum), the\ndimensionality of the problem =nDims= and potential bounds on each\ndimension. The bounds are given as a sequence of tuples. Each tuple\ncorresponds to one dimension of =nDims= with =(lower bound, upper\nbound)= of that dimension. Either empty, or bounds for all dimensions\nneed to be given. If only a single dimension is to be bounded, set the\nother bounds to =-Inf= and =Inf=.\n\n*** =proc newVarStruct=\nTo create a new =VarStruct= object. It is used to wrap the user\ndefined optimization function and arbitrary data to be used in\nthat function.\nSignature:\n#+BEGIN_SRC nim\nproc newVarStruct[T, U](uFunc: T, data: U): VarStruct[U]\n#+END_SRC\ndue to a bug (?), we cannot constrain =T= unfortunately. =T= needs to\nbe either a =FuncProto= or a =FuncProtoGrad=. See below for an\nexplanation of these. Thus the custom function following either\ntype will be haneded as =uFunc=.\n\nThe =data= argument is an arbitrary user defined object, which is used\nto hand data to the user defined function. To be precise, it is the\nobject, which will be given to =FuncProto= or =FuncProtoGrad= as\nexplained below.\n\n*** =type FuncProto=\nOne generic primitive procedure type, which the custom function to be\noptimized needs to match. This is the type to be used for algorithms,\nwhich are *gradient free* (i.e. the user does not have to calculate the\ngradients manually). \nSignature:\n#+BEGIN_SRC nim\nFuncProto[T] = proc (p: seq[float], func_data: T): float\n#+END_SRC\nThe first argument =p= are the parameters, which are optimized. The\ngeneric type =func_data= is an arbitrary type, which usually contains\nthe data on which the optmization is done. But it can be any type, so\nit allows arbitrary data to be injected into the function, which can\nbe used for the calculation. Internally =FuncProto= is wrapped by a\nfunction following the =nlopt_func= signature. The =func_data= is\nhanded as a raw pointer to the C library and from there upon a\nfunction evaluation back to Nim.\n\nThe return value of the function must be the value of the function\nafter evaluation.\n\n*** =type FuncProtoGrad=\nOne generic primitive procedure type, which the custom function to be\noptimized needs to match. This is the type to be used for algorithms,\nwhich are *not gradient free* (i.e. the user *does* have to calculate the\ngradients manually). \n#+BEGIN_SRC nim\nFuncProtoGrad[T] = proc (p: seq[float], func_data: T): (float, seq[float])\n#+END_SRC\nThe signature of =FuncProtoGrad= is identical to =FuncProto= (the =p=\nand =func_data= arguments serve the same purpose, read above for an\nexplanation), with the exception of the return type.\n\nThe second element of the return tuple has to be the gradients of the\nfunction with respect to the parameters. These will be returned back\nto NLopt to take into account for the calculation of the next parameters.\n\nIn many cases it may be enough to perform numeric differentiation,\ne.g. via the [[https://en.wikipedia.org/wiki/Numerical_differentiation][symmetric difference quotient]].\n\nA possible implementation might look something like:\n#+BEGIN_SRC nim\nproc myFunc(p: seq[float], fitObj: FitObject): (float, seq[float]) =\n  ## `FitObject` takes the place of `func_data`. \n  ## In this case:\n  ## type\n  ##   FitObject = object\n  ##     x, y: seq[float]\n  # NOTE: do not need last gradients\n  let x = fitObj.x\n  let y = fitObj.y\n  # a seq for the resulting gradients\n  var gradRes = newSeq[float](p.len)\n  # a float for the function evaluation at `p`\n  var res = 0.0\n  # a variable for the small change we take in `p_i`\n  var h: float  \n  # a temp variable for the individual part of a `Chi^2` sum\n  var diff = 0.0\n  proc fn(params: seq[float]): float =\n    # calculate the model's Y position to be used to perform a curve fit \n    # of `funcToCall` to `(x, y)` data\n    let fitY = x.mapIt(funcToCall(params, it))\n    for i in 0 .. x.high:\n      diff = (y[i] - fitY[i]) / yErr[i]\n      result += pow(diff, 2.0)\n    # result of our internal `f(p)` is the reduced Pearson's Chi^2\n    result = result / (x.len - p.len).float\n  # the function evaluation is simply our `Chi^2` value of the parameters\n  res = fn(p)\n  # now calculate the numerical derivative\n  for i in 0 .. gradRes.high:\n    # calc some reasonable `h` for this parameter\n    h = p[i] * sqrt(epsilon(float64))\n    var\n      modParsUp = p\n      modParsDown = p\n    modParsUp[i] = p[i] + h\n    modParsDown[i] = p[i] - h\n    # numerical partial derivative according to `symmetric difference quotient`\n    gradRes[i] = (fn(modParsUp) - fn(modParsDown)) / (2.0 * h)\n  result = (res, gradRes)\n#+END_SRC\nThe above can be easily wrapped in a template for instance to lift any\nfunction =funcToCall= to be fitable via Pearson's chi-squared test. \n\n*** =type VarStruct=\n=VarStruct= is the unified container, which stores the user defined\nfunction of either type above and arbitrary user data, which will be\nhanded to that function during each evaluation of the function.\nSignature:\n#+BEGIN_SRC nim\n  VarStruct*[T] = ref object\n    case kind*: FuncKind:\n    of NoGrad:\n      userFunc*: FuncProto[T]\n    of Grad:\n      userFuncGrad*: FuncProtoGrad[T]\n    data*: T\n# where ``FuncKind`` is\n  FuncKind* {.pure.} = enum\n    NoGrad, Grad\n#+END_SRC\nIt's a variant object, which either stores a =FuncProto= if the type\nis =NoGrad= or a =FuncProtoGrad= if it is =Grad=. In principle the\n=newVarStruct= does not need to be used. One can create such a variant\nobject manually, but needs to take care to:\n1. set the =kind= field accordingly\n2. use the correct field name for the user function for this type.\nThis is what the =newVarStruct= procedure takes care of.\n\n*** =proc setFunction= (TODO: rename?)\nThis procedure is used to set the set the =FuncProco(Grad)= function\nas the =nlopt_func= of the C =nlopt_opt= object. If performs the\nwrapping of the user function into a suitable =nlopt_func=. In\naddition it also sets the data, which will be given to the user\ndefined function.\nSignature:\n#+BEGIN_SRC nim\nproc setFunction[T](nlopt: var NloptOpt, vStruct: var VarStruct[T])\n#+END_SRC\nThe first argument is the optmizer and =vStruct= is the user created\n=VarStruct= object. It is a =var= argument as well, since we want to\navoid copying the data internally. \n\n*** =proc addInequalityConstraint=\nUsed to add inequality constraints to the optimization problem. The\nsignature is exactly the same as for =setFunction=. One also creates a\ncustom constraints function and a corresponding =VarStruct=\nobject. This constraints function will be called in between calls to\nthe actual function to be optimized. There may be one constraint for each\ndimension. See the Nlopt doc for more information.\nSignature:\n#+BEGIN_SRC nim\nproc addInequalityConstraint*[T](nlopt: var NloptOpt, vStruct: var VarStruct[T])\n#+END_SRC\nsee the =setFunction= explanation above.\n\n*** =proc optimize=\nThe actual function, which starts the optimization routine after\neverything has been set up. It sets all additional parameters of the\n=NloptOpt= (tolerances, step sizes etc.) before calling the actual\n=nlopt_optimize= function. \nSignature:\n#+BEGIN_SRC nim\nproc optimize*[T](nlopt: var NloptOpt, params: seq[T]): tuple[p: seq[float], f: float] =\n#+END_SRC\nThe first parameter is the configures =NloptOpt= object. =params= is\nthe initial guess for the parameters to be optmized.\nAfter optimization the status of the optimization will be stored in\nthe =status= field of the =nlopt= object. \n\nThe return value is a tuple of the sequence of optmized parameters =p=\nand the function value after the last evaluation of the function =f=.\n\n\n** License\n\nThe license of the C library is found in the [[file:c_header/][c_header]] folder, which\ncontains the headers as they were wrapped using c2nim. \n\nThe Nim code is published under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVindaar%2Fnimnlopt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FVindaar%2Fnimnlopt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FVindaar%2Fnimnlopt/lists"}