#include <petsc/private/taoimpl.h> /*I "petsctao.h" I*/

/*@
  TaoSetVariableBounds - Sets the upper and lower bounds for the optimization problem

  Logically Collective

  Input Parameters:
+ tao - the `Tao` context
. XL  - vector of lower bounds
- XU  - vector of upper bounds

  Level: beginner

.seealso: [](ch_tao), `Tao`, `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoGetVariableBounds()`
@*/
PetscErrorCode TaoSetVariableBounds(Tao tao, Vec XL, Vec XU)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (XL) PetscValidHeaderSpecific(XL, VEC_CLASSID, 2);
  if (XU) PetscValidHeaderSpecific(XU, VEC_CLASSID, 3);
  PetscCall(PetscObjectReference((PetscObject)XL));
  PetscCall(PetscObjectReference((PetscObject)XU));
  PetscCall(VecDestroy(&tao->XL));
  PetscCall(VecDestroy(&tao->XU));
  tao->XL      = XL;
  tao->XU      = XU;
  tao->bounded = (PetscBool)(XL || XU);
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  TaoSetVariableBoundsRoutine - Sets a function to be used to compute lower and upper variable bounds for the optimization

  Logically Collective

  Input Parameters:
+ tao  - the `Tao` context
. func - the bounds computation routine
- ctx  - [optional] user-defined context for private data for the bounds computation (may be `NULL`)

  Calling sequence of `func`:
+ tao - the `Tao` solver
. xl  - vector of lower bounds
. xu  - vector of upper bounds
- ctx - the (optional) user-defined function context

  Level: beginner

  Note:
  The func passed to `TaoSetVariableBoundsRoutine()` takes precedence over any values set in `TaoSetVariableBounds()`.

.seealso: [](ch_tao), `Tao`, `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoSetVariableBounds()`
@*/
PetscErrorCode TaoSetVariableBoundsRoutine(Tao tao, PetscErrorCode (*func)(Tao tao, Vec xl, Vec xu, PetscCtx ctx), PetscCtx ctx)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  tao->user_boundsP       = ctx;
  tao->ops->computebounds = func;
  tao->bounded            = func ? PETSC_TRUE : PETSC_FALSE;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoGetVariableBounds - Gets the upper and lower bounds vectors set with `TaoSetVariableBounds()`

  Not Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ XL - vector of lower bounds
- XU - vector of upper bounds

  Level: beginner

.seealso: [](ch_tao), `Tao`, `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoSetVariableBounds()`
@*/
PetscErrorCode TaoGetVariableBounds(Tao tao, Vec *XL, Vec *XU)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (XL) *XL = tao->XL;
  if (XU) *XU = tao->XU;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoComputeVariableBounds - Compute the variable bounds using the
  routine set by `TaoSetVariableBoundsRoutine()`.

  Collective

  Input Parameter:
. tao - the `Tao` context

  Level: developer

.seealso: [](ch_tao), `Tao`, `TaoSetVariableBoundsRoutine()`, `TaoSetVariableBounds()`
@*/
PetscErrorCode TaoComputeVariableBounds(Tao tao)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (tao->ops->computebounds) {
    if (!tao->XL) {
      PetscCall(VecDuplicate(tao->solution, &tao->XL));
      PetscCall(VecSet(tao->XL, PETSC_NINFINITY));
    }
    if (!tao->XU) {
      PetscCall(VecDuplicate(tao->solution, &tao->XU));
      PetscCall(VecSet(tao->XU, PETSC_INFINITY));
    }
    PetscCallBack("Tao callback variable bounds", (*tao->ops->computebounds)(tao, tao->XL, tao->XU, tao->user_boundsP));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoSetInequalityBounds - Sets the upper and lower bounds

  Logically Collective

  Input Parameters:
+ tao - the `Tao` context
. IL  - vector of lower bounds
- IU  - vector of upper bounds

  Level: beginner

.seealso: [](ch_tao), `Tao`, `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoGetInequalityBounds()`
@*/
PetscErrorCode TaoSetInequalityBounds(Tao tao, Vec IL, Vec IU)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (IL) PetscValidHeaderSpecific(IL, VEC_CLASSID, 2);
  if (IU) PetscValidHeaderSpecific(IU, VEC_CLASSID, 3);
  PetscCall(PetscObjectReference((PetscObject)IL));
  PetscCall(PetscObjectReference((PetscObject)IU));
  PetscCall(VecDestroy(&tao->IL));
  PetscCall(VecDestroy(&tao->IU));
  tao->IL               = IL;
  tao->IU               = IU;
  tao->ineq_doublesided = (PetscBool)(IL || IU);
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoGetInequalityBounds - Gets the upper and lower bounds set via `TaoSetInequalityBounds()`

  Logically Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ IL - vector of lower bounds
- IU - vector of upper bounds

  Level: beginner

.seealso: [](ch_tao), `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoSetInequalityBounds()`
@*/
PetscErrorCode TaoGetInequalityBounds(Tao tao, Vec *IL, Vec *IU)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (IL) *IL = tao->IL;
  if (IU) *IU = tao->IU;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoComputeConstraints - Compute the variable bounds using the
  routine set by `TaoSetConstraintsRoutine()`.

  Collective

  Input Parameters:
+ tao - the `Tao` context
- X   - location to evaluate the constraints

  Output Parameter:
. C - the constraints

  Level: developer

.seealso: [](ch_tao), `Tao`, `TaoSetConstraintsRoutine()`, `TaoComputeJacobian()`
@*/
PetscErrorCode TaoComputeConstraints(Tao tao, Vec X, Vec C)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
  PetscValidHeaderSpecific(C, VEC_CLASSID, 3);
  PetscCheckSameComm(tao, 1, X, 2);
  PetscCheckSameComm(tao, 1, C, 3);
  PetscCall(PetscLogEventBegin(TAO_ConstraintsEval, tao, X, C, NULL));
  PetscCallBack("Tao callback constraints", (*tao->ops->computeconstraints)(tao, X, C, tao->user_conP));
  PetscCall(PetscLogEventEnd(TAO_ConstraintsEval, tao, X, C, NULL));
  tao->nconstraints++;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  TaoSetConstraintsRoutine - Sets a function to be used to compute constraints.  Tao only handles constraints under certain conditions, see [](ch_tao) for details

  Logically Collective

  Input Parameters:
+ tao  - the `Tao` context
. c    - A vector that will be used to store constraint evaluation
. func - the bounds computation routine
- ctx  - [optional] user-defined context for private data for the constraints computation (may be `NULL`)

  Calling sequence of `func`:
+ tao - the `Tao` solver
. x   - point to evaluate constraints
. c   - vector constraints evaluated at `x`
- ctx - the (optional) user-defined function context

  Level: intermediate

.seealso: [](ch_tao), `Tao`, `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoSetVariablevBounds()`
@*/
PetscErrorCode TaoSetConstraintsRoutine(Tao tao, Vec c, PetscErrorCode (*func)(Tao tao, Vec x, Vec c, PetscCtx ctx), PetscCtx ctx)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (c) PetscValidHeaderSpecific(c, VEC_CLASSID, 2);
  PetscCall(PetscObjectReference((PetscObject)c));
  PetscCall(VecDestroy(&tao->constraints));
  tao->constrained             = func ? PETSC_TRUE : PETSC_FALSE;
  tao->constraints             = c;
  tao->user_conP               = ctx;
  tao->ops->computeconstraints = func;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoComputeDualVariables - Computes the dual vectors corresponding to the bounds
  of the variables

  Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ DL - dual variable vector for the lower bounds
- DU - dual variable vector for the upper bounds

  Level: advanced

  Note:
  DL and DU should be created before calling this routine.  If calling
  this routine after using an unconstrained solver, `DL` and `DU` are set to all
  zeros.

.seealso: [](ch_tao), `Tao`, `TaoComputeObjective()`, `TaoSetVariableBounds()`
@*/
PetscErrorCode TaoComputeDualVariables(Tao tao, Vec DL, Vec DU)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  PetscValidHeaderSpecific(DL, VEC_CLASSID, 2);
  PetscValidHeaderSpecific(DU, VEC_CLASSID, 3);
  PetscCheckSameComm(tao, 1, DL, 2);
  PetscCheckSameComm(tao, 1, DU, 3);
  if (tao->ops->computedual) PetscUseTypeMethod(tao, computedual, DL, DU);
  else {
    PetscCall(VecSet(DL, 0.0));
    PetscCall(VecSet(DU, 0.0));
  }
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoGetDualVariables - Gets the dual vectors

  Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ DE - dual variable vector for the lower bounds
- DI - dual variable vector for the upper bounds

  Level: advanced

.seealso: [](ch_tao), `Tao`, `TaoComputeDualVariables()`
@*/
PetscErrorCode TaoGetDualVariables(Tao tao, Vec *DE, Vec *DI)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (DE) *DE = tao->DE;
  if (DI) *DI = tao->DI;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  TaoSetEqualityConstraintsRoutine - Sets a function to be used to compute constraints.  Tao only handles constraints under certain conditions, see [](ch_tao) for details

  Logically Collective

  Input Parameters:
+ tao  - the `Tao` context
. ce   - A vector that will be used to store equality constraint evaluation
. func - the bounds computation routine
- ctx  - [optional] user-defined context for private data for the equality constraints computation (may be `NULL`)

  Calling sequence of `func`:
+ tao - the `Tao` solver
. x   - point to evaluate equality constraints
. ce  - vector of equality constraints evaluated at x
- ctx - the (optional) user-defined function context

  Level: intermediate

.seealso: [](ch_tao), `Tao`, `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoSetVariableBounds()`
@*/
PetscErrorCode TaoSetEqualityConstraintsRoutine(Tao tao, Vec ce, PetscErrorCode (*func)(Tao tao, Vec x, Vec ce, PetscCtx ctx), PetscCtx ctx)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (ce) PetscValidHeaderSpecific(ce, VEC_CLASSID, 2);
  PetscCall(PetscObjectReference((PetscObject)ce));
  PetscCall(VecDestroy(&tao->constraints_equality));
  tao->eq_constrained                  = func ? PETSC_TRUE : PETSC_FALSE;
  tao->constraints_equality            = ce;
  tao->user_con_equalityP              = ctx;
  tao->ops->computeequalityconstraints = func;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  TaoGetEqualityConstraintsRoutine - Gets the function used to compute equality constraints.

  Not Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ ci   - the vector to internally hold the constraint computation
. func - the bounds computation routine
- ctx  - the (optional) user-defined context

  Calling sequence of `func`:
+ tao - the `Tao` solver
. x   - point to evaluate equality constraints
. ci  - vector of equality constraints evaluated at x
- ctx - the (optional) user-defined function context

  Level: intermediate

.seealso: [](ch_tao), `Tao`, `TaoSolve()`, `TaoGetObjective()`, `TaoGetGradient()`, `TaoGetHessian()`, `TaoGetObjectiveAndGradient()`, `TaoGetInequalityConstraintsRoutine()`
@*/
PetscErrorCode TaoGetEqualityConstraintsRoutine(Tao tao, Vec *ci, PetscErrorCode (**func)(Tao tao, Vec x, Vec ci, PetscCtx ctx), PetscCtxRt ctx)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (ci) *ci = tao->constraints_equality;
  if (func) *func = tao->ops->computeequalityconstraints;
  if (ctx) *(void **)ctx = tao->user_con_equalityP;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  TaoSetInequalityConstraintsRoutine - Sets a function to be used to compute constraints.  Tao only handles constraints under certain conditions, see [](ch_tao) for details

  Logically Collective

  Input Parameters:
+ tao  - the `Tao` context
. ci   - A vector that will be used to store inequality constraint evaluation
. func - the bounds computation routine
- ctx  - [optional] user-defined context for private data for the inequality constraints computation (may be `NULL`)

  Calling sequence of `func`:
+ tao - the `Tao` solver
. x   - point to evaluate inequality constraints
. ci  - vector of inequality constraints evaluated at x
- ctx - the (optional) user-defined function context

  Level: intermediate

.seealso: [](ch_tao), `Tao`, `TaoSetObjective()`, `TaoSetHessian()`, `TaoSetObjectiveAndGradient()`, `TaoSetVariableBounds()`
@*/
PetscErrorCode TaoSetInequalityConstraintsRoutine(Tao tao, Vec ci, PetscErrorCode (*func)(Tao tao, Vec x, Vec ci, PetscCtx ctx), PetscCtx ctx)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (ci) PetscValidHeaderSpecific(ci, VEC_CLASSID, 2);
  PetscCall(PetscObjectReference((PetscObject)ci));
  PetscCall(VecDestroy(&tao->constraints_inequality));
  tao->constraints_inequality            = ci;
  tao->ineq_constrained                  = func ? PETSC_TRUE : PETSC_FALSE;
  tao->user_con_inequalityP              = ctx;
  tao->ops->computeinequalityconstraints = func;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@C
  TaoGetInequalityConstraintsRoutine - Gets the function used to compute inequality constraints.

  Not Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ ci   - the vector to internally hold the constraint computation
. func - the bounds computation routine
- ctx  - the (optional) user-defined context

  Calling sequence of `func`:
+ tao - the `Tao` solver
. x   - point to evaluate inequality constraints
. ci  - vector of inequality constraints evaluated at x
- ctx - the (optional) user-defined function context

  Level: intermediate

.seealso: [](ch_tao), `Tao`, `TaoSolve()`, `TaoGetObjective()`, `TaoGetGradient()`, `TaoGetHessian()`, `TaoGetObjectiveAndGradient()`, `TaoGetEqualityConstraintsRoutine()`
@*/
PetscErrorCode TaoGetInequalityConstraintsRoutine(Tao tao, Vec *ci, PetscErrorCode (**func)(Tao tao, Vec x, Vec ci, PetscCtx ctx), PetscCtxRt ctx)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  if (ci) *ci = tao->constraints_inequality;
  if (func) *func = tao->ops->computeinequalityconstraints;
  if (ctx) *(void **)ctx = tao->user_con_inequalityP;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoComputeEqualityConstraints - Compute the variable bounds using the
  routine set by `TaoSetEqualityConstraintsRoutine()`.

  Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ X  - point the equality constraints were evaluated on
- CE - vector of equality constraints evaluated at X

  Level: developer

.seealso: [](ch_tao), `Tao`, `TaoSetEqualityConstraintsRoutine()`, `TaoComputeJacobianEquality()`, `TaoComputeInequalityConstraints()`
@*/
PetscErrorCode TaoComputeEqualityConstraints(Tao tao, Vec X, Vec CE)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
  PetscValidHeaderSpecific(CE, VEC_CLASSID, 3);
  PetscCheckSameComm(tao, 1, X, 2);
  PetscCheckSameComm(tao, 1, CE, 3);
  PetscCall(PetscLogEventBegin(TAO_ConstraintsEval, tao, X, CE, NULL));
  PetscCallBack("Tao callback equality constraints", (*tao->ops->computeequalityconstraints)(tao, X, CE, tao->user_con_equalityP));
  PetscCall(PetscLogEventEnd(TAO_ConstraintsEval, tao, X, CE, NULL));
  tao->nconstraints++;
  PetscFunctionReturn(PETSC_SUCCESS);
}

/*@
  TaoComputeInequalityConstraints - Compute the variable bounds using the
  routine set by `TaoSetInequalityConstraintsRoutine()`.

  Collective

  Input Parameter:
. tao - the `Tao` context

  Output Parameters:
+ X  - point the inequality constraints were evaluated on
- CI - vector of inequality constraints evaluated at X

  Level: developer

.seealso: [](ch_tao), `Tao`, `TaoSetInequalityConstraintsRoutine()`, `TaoComputeJacobianInequality()`, `TaoComputeEqualityConstraints()`
@*/
PetscErrorCode TaoComputeInequalityConstraints(Tao tao, Vec X, Vec CI)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(tao, TAO_CLASSID, 1);
  PetscValidHeaderSpecific(X, VEC_CLASSID, 2);
  PetscValidHeaderSpecific(CI, VEC_CLASSID, 3);
  PetscCheckSameComm(tao, 1, X, 2);
  PetscCheckSameComm(tao, 1, CI, 3);
  PetscCall(PetscLogEventBegin(TAO_ConstraintsEval, tao, X, CI, NULL));
  PetscCallBack("Tao callback inequality constraints", (*tao->ops->computeinequalityconstraints)(tao, X, CI, tao->user_con_inequalityP));
  PetscCall(PetscLogEventEnd(TAO_ConstraintsEval, tao, X, CI, NULL));
  tao->nconstraints++;
  PetscFunctionReturn(PETSC_SUCCESS);
}
