{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PyVMCON Example\n",
    "\n",
    "The following example is 'Test 1' described in the VMCON paper, which can be found referenced in our documentation.\n",
    "\n",
    "-----\n",
    "\n",
    "First, we need to define our problem. A problem consists of:\n",
    "\n",
    "* a function $f(\\vec{x})$ with a derivative $\\nabla f(\\vec{x})$\n",
    "* a set of non-linear equality constraints $c_i(\\vec{x}) = 0, \\quad i = 1,...,k$ with derivatives $\\nabla c_i(\\vec{x})$\n",
    "* a set of non-linear inequality constraints $c_i(\\vec{x}) \\geq 0, \\quad i = k+1,...,m$  with derivatives $\\nabla c_i(\\vec{x})$\n",
    "\n",
    "'Test 1' provides the following problem:\n",
    "\n",
    "* $f([x_1, x_2]) = (x_1-2)^2+(x_2-1)^2$ with $\\nabla f([x_1, x_2]) = [2(x_1-2),\\:2(x_2-1)]$\n",
    "* $c_1([x_1, x_2]) = x_1 - 2x_2 + 1 = 0$ with $\\nabla c_1([x_1, x_2]) = [1,\\:-2]$\n",
    "* $c_2([x_1, x_2]) = -\\frac{x_1^2}{4} - x_2^2 + 1 \\geq 0$ with $\\nabla c_2([x_1, x_2]) = [-\\frac{x_1}{2},\\:-2x_2]$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import List\n",
    "import numpy as np\n",
    "from pyvmcon import Problem\n",
    "\n",
    "def f(x: List):\n",
    "    return (x[0] - 2) ** 2 + (x[1] - 1) ** 2\n",
    "\n",
    "problem = Problem(\n",
    "    f,\n",
    "    lambda x: np.array([2 * (x[0] - 2), 2 * (x[1] - 1)]),\n",
    "    [lambda x: x[0] - (2 * x[1]) + 1],\n",
    "    [lambda x: -((x[0] ** 2) / 4) - (x[1] ** 2) + 1],\n",
    "    [lambda _: np.array([1, -2])],\n",
    "    [lambda x: np.array([-0.5 * x[0], -2 * x[1]])],\n",
    ")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Following that, we can call the `solve` method to use VMCON to solve our problem. The only other information the solver requires is the initial starting point. 'Test 1' has an initial starting point of $\\vec{x}_0 = [2, 2]$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pyvmcon import solve\n",
    "\n",
    "initial_x = np.array([2,2])\n",
    "\n",
    "x, lamda_equality, lamda_inequality, result = solve(problem, initial_x)\n",
    "\n",
    "print(f\"Final solution vector (x): {x}\")\n",
    "print(f\"Lagrange multipliers for equality constraints: {lamda_equality}\")\n",
    "print(f\"Lagrange multipliers for inequality constraints: {lamda_inequality}\")\n",
    "print(f\"Value of f(x) at the final solution: {result.f}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In 'Test 1', we expect a final solution of $~[0.82, 0.91]$. \n",
    "\n",
    "The `solve` method returns the final solution vector along with three helpful quantities:\n",
    "\n",
    "* The Lagrange multipliers for the equality constraints when evaluated at the final solution vector.\n",
    "* The Lagrange multipliers for the inequality constraints when evaluated at the final solution vector.\n",
    "* The `Result` of evaluating the `problem` with the final solution vector.\n",
    "\n",
    "----\n",
    "\n",
    "We could also choose to bound our problem above. Imposing bounds of $[-10, 10]$ for $x_1$ and $x_2$ will no affect the solution, but will demonstrate the interface to impose such bounds."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "x, lamda_equality, lamda_inequality, result = solve(\n",
    "    problem, \n",
    "    initial_x, \n",
    "    lbs=np.array([-10, -10]), \n",
    "    ubs=np.array([10, 10]),\n",
    ")\n",
    "\n",
    "print(f\"Final solution vector (x): {x}\")\n",
    "print(f\"Lagrange multipliers for equality constraints: {lamda_equality}\")\n",
    "print(f\"Lagrange multipliers for inequality constraints: {lamda_inequality}\")\n",
    "print(f\"Value of f(x) at the final solution: {result.f}\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are several other cutsomisations available on the `solve` function. These can be found in the API reference in the documentation."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "env",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.12"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}