@@ -399,19 +399,16 @@ function bobyqa!(f, x::DenseVector{Cdouble};
399399 fx = Ref {Cdouble} (NaN ) # to store f(x) on return
400400 nf = Ref {Cint} (0 ) # to store number of function calls
401401
402- # Create wrapper to objective function and push it on top of per-thread
403- # stack before calling optimizer.
404- fw = ObjFun (f, n, scl) # wrapper to objective function
405- fp = _push_objfun (bobyqa, fw) # pointer to C-callable function
406- try
407- # Call low-level optimizer on the (scaled) variables.
408- isempty (scl) || _scale! (x, / , scl)
409- status = prima_bobyqa (fp, n, x, fx, xl, xu, nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint)
410- isempty (scl) || _scale! (x, * , scl)
411- return Info (; fx = fx[], nf = nf[], status)
412- finally
413- _pop_objfun (fw)
402+ # Call low-level optimizer on the scaled variables and with a wrapper to the objective
403+ # function stored in the task local storage.
404+ isempty (scl) || _scale! (x, / , scl)
405+ fw = ObjFun (f, n, scl) # wrapper to objective function
406+ fp = _objfun_ptr (bobyqa) # pointer to C-callable function
407+ status = task_local_storage (:objfun , fw) do
408+ prima_bobyqa (fp, n, x, fx, xl, xu, nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint)
414409 end
410+ isempty (scl) || _scale! (x, * , scl)
411+ return Info (; fx = fx[], nf = nf[], status)
415412end
416413
417414"""
@@ -440,19 +437,16 @@ function newuoa!(f, x::DenseVector{Cdouble};
440437 fx = Ref {Cdouble} (NaN ) # to store f(x) on return
441438 nf = Ref {Cint} (0 ) # to store number of function calls
442439
443- # Create wrapper to objective function and push it on top of per-thread
444- # stack before calling optimizer.
445- fw = ObjFun (f, n, scl) # wrapper to objective function
446- fp = _push_objfun (newuoa, fw) # pointer to C-callable function
447- try
448- # Call low-level optimizer on the (scaled) variables.
449- isempty (scl) || _scale! (x, / , scl)
450- status = prima_newuoa (fp, n, x, fx, nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint)
451- isempty (scl) || _scale! (x, * , scl)
452- return Info (; fx = fx[], nf = nf[], status)
453- finally
454- _pop_objfun (fw)
440+ # Call low-level optimizer on the scaled variables and with a wrapper to the objective
441+ # function stored in the task local storage.
442+ isempty (scl) || _scale! (x, / , scl)
443+ fw = ObjFun (f, n, scl) # wrapper to objective function
444+ fp = _objfun_ptr (newuoa) # pointer to C-callable function
445+ status = task_local_storage (:objfun , fw) do
446+ prima_newuoa (fp, n, x, fx, nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint)
455447 end
448+ isempty (scl) || _scale! (x, * , scl)
449+ return Info (; fx = fx[], nf = nf[], status)
456450end
457451
458452"""
@@ -479,19 +473,16 @@ function uobyqa!(f, x::DenseVector{Cdouble};
479473 fx = Ref {Cdouble} (NaN ) # to store f(x) on return
480474 nf = Ref {Cint} (0 ) # to store number of function calls
481475
482- # Create wrapper to objective function and push it on top of per-thread
483- # stack before calling optimizer.
484- fw = ObjFun (f, n, scl) # wrapper to objective function
485- fp = _push_objfun (uobyqa, fw) # pointer to C-callable function
486- try
487- # Call low-level optimizer on the (scaled) variables.
488- isempty (scl) || _scale! (x, / , scl)
489- status = prima_uobyqa (fp, n, x, fx, nf, rhobeg, rhoend, ftarget, maxfun, iprint)
490- isempty (scl) || _scale! (x, * , scl)
491- return Info (; fx = fx[], nf = nf[], status)
492- finally
493- _pop_objfun (fw)
476+ # Call low-level optimizer on the scaled variables and with a wrapper to the objective
477+ # function stored in the task local storage.
478+ isempty (scl) || _scale! (x, / , scl)
479+ fw = ObjFun (f, n, scl) # wrapper to objective function
480+ fp = _objfun_ptr (uobyqa) # pointer to C-callable function
481+ status = task_local_storage (:objfun , fw) do
482+ prima_uobyqa (fp, n, x, fx, nf, rhobeg, rhoend, ftarget, maxfun, iprint)
494483 end
484+ isempty (scl) || _scale! (x, * , scl)
485+ return Info (; fx = fx[], nf = nf[], status)
495486end
496487
497488"""
@@ -535,37 +526,35 @@ function cobyla!(f, x::DenseVector{Cdouble};
535526 fx = Ref {Cdouble} (NaN ) # to store f(x) on return
536527 nf = Ref {Cint} (0 ) # to store number of function calls
537528
538- # Create wrapper to objective function and push it on top of per-thread
539- # stack before calling optimizer.
529+ # Call low-level optimizer on the scaled variables and with a wrapper to the objective
530+ # function stored in the task local storage.
531+ isempty (scl) || _scale! (x, / , scl)
540532 fw = ObjFun (f, n, scl, c_nl_eq, length (nl_eq), c_nl_ineq, length (nl_ineq))
541- fp = _push_objfun (cobyla, fw) # pointer to C-callable function
542- try
543- # Call low-level optimizer on the (scaled) variables.
544- isempty (scl) || _scale! (x, / , scl)
545- status = prima_cobyla (length (nl_all), fp, n, x, fx, cstrv, nl_all,
546- length (b_ineq), A_ineq, b_ineq,
547- length (b_eq), A_eq, b_eq,
548- xl, xu, nf, rhobeg, rhoend, ftarget, maxfun, iprint)
549- isempty (scl) || _scale! (x, * , scl)
550- # Unpack constraints.
551- if length (nl_eq) > 0
552- i = firstindex (nl_all)
553- for j in eachindex (nl_eq)
554- nl_eq[j] = nl_all[i]
555- i += 2
556- end
533+ fp = _objfun_ptr (cobyla) # pointer to C-callable function
534+ status = task_local_storage (:objfun , fw) do
535+ prima_cobyla (length (nl_all), fp, n, x, fx, cstrv, nl_all,
536+ length (b_ineq), A_ineq, b_ineq,
537+ length (b_eq), A_eq, b_eq,
538+ xl, xu, nf, rhobeg, rhoend, ftarget, maxfun, iprint)
539+ end
540+ isempty (scl) || _scale! (x, * , scl)
541+
542+ # Unpack constraints.
543+ if length (nl_eq) > 0
544+ i = firstindex (nl_all)
545+ for j in eachindex (nl_eq)
546+ nl_eq[j] = nl_all[i]
547+ i += 2
557548 end
558- if length (nl_ineq) > 0
559- i = firstindex (nl_all) + 2 * length (nl_eq)
560- for j in eachindex (nl_ineq )
561- nl_ineq[j] = nl_all[i]
562- i += 1
563- end
549+ end
550+ if length (nl_ineq) > 0
551+ i = firstindex (nl_all) + 2 * length (nl_eq )
552+ for j in eachindex (nl_ineq)
553+ nl_ineq[j] = nl_all[i]
554+ i += 1
564555 end
565- return Info (; fx = fx[], nf = nf[], status, cstrv = cstrv[], nl_eq, nl_ineq)
566- finally
567- _pop_objfun (fw)
568556 end
557+ return Info (; fx = fx[], nf = nf[], status, cstrv = cstrv[], nl_eq, nl_ineq)
569558end
570559
571560"""
@@ -603,23 +592,20 @@ function lincoa!(f, x::DenseVector{Cdouble};
603592 fx = Ref {Cdouble} (NaN ) # to store f(x) on return
604593 nf = Ref {Cint} (0 ) # to store number of function calls
605594
606- # Create wrapper to objective function and push it on top of per-thread
607- # stack before calling optimizer.
608- fw = ObjFun (f, n, scl) # wrapper to objective function
609- fp = _push_objfun (lincoa, fw) # pointer to C-callable function
610- try
611- # Call low-level optimizer on the (scaled) variables.
612- isempty (scl) || _scale! (x, / , scl)
613- status = prima_lincoa (fp, n, x, fx, cstrv,
614- length (b_ineq), A_ineq, b_ineq,
615- length (b_eq), A_eq, b_eq,
616- xl, xu,
617- nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint)
618- isempty (scl) || _scale! (x, * , scl)
619- return Info (; fx = fx[], nf = nf[], status, cstrv = cstrv[])
620- finally
621- _pop_objfun (fw)
595+ # Call low-level optimizer on the scaled variables and with a wrapper to the objective
596+ # function stored in the task local storage.
597+ isempty (scl) || _scale! (x, / , scl)
598+ fw = ObjFun (f, n, scl) # wrapper to objective function
599+ fp = _objfun_ptr (lincoa) # pointer to C-callable function
600+ status = task_local_storage (:objfun , fw) do
601+ prima_lincoa (fp, n, x, fx, cstrv,
602+ length (b_ineq), A_ineq, b_ineq,
603+ length (b_eq), A_eq, b_eq,
604+ xl, xu,
605+ nf, rhobeg, rhoend, ftarget, maxfun, npt, iprint)
622606 end
607+ isempty (scl) || _scale! (x, * , scl)
608+ return Info (; fx = fx[], nf = nf[], status, cstrv = cstrv[])
623609end
624610
625611# ------------------------------------------------------------------------------
@@ -751,25 +737,24 @@ end
751737# the type of the user-defined Julia functions and (ii) to make possible to
752738# extend `call` or `call!` at the last stage .
753739
754- # C-callable objective function for problems with no non-linear constraints
755- # (for other algorithms than COBYLA).
740+ # C-callable objective function for problems with no non-linear constraints (for other
741+ # algorithms than COBYLA).
756742function _objfun (x_ptr:: Ptr{Cdouble} , # (input) variables
757743 f_ptr:: Ptr{Cdouble} ) # (output) function value
758- # Retrieve objective function object and dispatch on its type to compute
759- # f(x).
760- f = last (_objfun_stack[Threads. threadid ()])
744+ # Retrieve objective function object and dispatch on its type to compute f(x).
745+ f = task_local_storage (:objfun )
761746 unsafe_store! (f_ptr, unsafe_call (f, x_ptr))
762747 return nothing
763748end
764749
765- # C-callable objective function for problems with non-linear constraints (for
766- # COBYLA algorithm).
750+ # C-callable objective function for problems with non-linear constraints (for COBYLA
751+ # algorithm).
767752function _objfun (x_ptr:: Ptr{Cdouble} , # (input) variables
768753 f_ptr:: Ptr{Cdouble} , # (output) function value
769754 c_ptr:: Ptr{Cdouble} ) # (output) constraints
770- # Retrieve objective function object and dispatch on its type to compute
771- # f(x) and the non-linear constraints.
772- f = last (_objfun_stack[Threads . threadid ()] )
755+ # Retrieve objective function object and dispatch on its type to compute f(x) and the
756+ # non-linear constraints.
757+ f = task_local_storage ( :objfun )
773758 unsafe_store! (f_ptr, unsafe_call (f, x_ptr, c_ptr))
774759 return nothing
775760end
@@ -807,42 +792,11 @@ end
807792@noinline corrupted_structure (msg:: AbstractString ) =
808793 throw (AssertionError (" corrupted structure ($msg )" ))
809794
810- # Global variable storing the per-thread stacks of objective functions indexed
811- # by thread identifier and then by execution order. On start of an
812- # optimization, an object linked to the user-defined objective function is
813- # pushed. This object is popped out of the stack on return of the optimization
814- # call, whatever happens. It is therefore necessary to wrap the call to the
815- # optimization method in a `try-finally` clause.
816- const _objfun_stack = Vector {Vector{ObjFun}} (undef, 0 )
817-
818- # Private function `_get_objfun_stack` yields the stack of objective functions
819- # for the caller thread.
820- function _get_objfun_stack ()
821- i = Threads. threadid ()
822- while length (_objfun_stack) < i
823- push! (_objfun_stack, Vector {ObjFun} (undef, 0 ))
824- end
825- return _objfun_stack[i]
826- end
827-
828- # Private functions `_push_objfun` and `_pop_objfun` are to be used in a
829- # `try-finally` clause as explained above.
830- function _push_objfun (algorithm, fw:: ObjFun )
831- _objfun_ptr (:: Any ) =
832- @cfunction (_objfun, Cvoid, (Ptr{Cdouble}, Ptr{Cdouble}))
833- _objfun_ptr (:: typeof (cobyla)) =
834- @cfunction (_objfun, Cvoid, (Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}))
835- push! (_get_objfun_stack (), fw)
836- return _objfun_ptr (algorithm)
837- end
838-
839- function _pop_objfun (fw:: ObjFun )
840- stack = _get_objfun_stack ()
841- last (stack) === fw || error (
842- " objective function is not the last one in the caller thread stask" )
843- resize! (stack, length (stack) - 1 )
844- return nothing
845- end
795+ # Pointer to C callable function.
796+ _objfun_ptr (:: Any ) =
797+ @cfunction (_objfun, Cvoid, (Ptr{Cdouble}, Ptr{Cdouble}))
798+ _objfun_ptr (:: typeof (cobyla)) =
799+ @cfunction (_objfun, Cvoid, (Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}))
846800
847801function _check_rho (rhobeg, rhoend)
848802 isfinite (rhobeg) && rhobeg > zero (rhobeg) || throw (ArgumentError (
0 commit comments