PyVMCON Example¶
The following example is ‘Test 1’ described in the VMCON paper, which can be found referenced in our documentation.
First, we need to define our problem. A problem consists of:
a function \(f(\vec{x})\) with a derivative \(\nabla f(\vec{x})\)
a set of non-linear equality constraints \(c_i(\vec{x}) = 0, \quad i = 1,...,k\) with derivatives \(\nabla c_i(\vec{x})\)
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})\)
‘Test 1’ provides the following problem:
\(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)]\)
\(c_1([x_1, x_2]) = x_1 - 2x_2 + 1 = 0\) with \(\nabla c_1([x_1, x_2]) = [1,\:-2]\)
\(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]\)
[1]:
from typing import List
import numpy as np
from pyvmcon import Problem
def f(x: List):
return (x[0] - 2) ** 2 + (x[1] - 1) ** 2
problem = Problem(
f,
lambda x: np.array([2 * (x[0] - 2), 2 * (x[1] - 1)]),
[lambda x: x[0] - (2 * x[1]) + 1],
[lambda x: -((x[0] ** 2) / 4) - (x[1] ** 2) + 1],
[lambda _: np.array([1, -2])],
[lambda x: np.array([-0.5 * x[0], -2 * x[1]])],
)
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]\).
[2]:
from pyvmcon import solve
initial_x = np.array([2,2])
x, lamda_equality, lamda_inequality, result = solve(problem, initial_x)
print(f"Final solution vector (x): {x}")
print(f"Lagrange multipliers for equality constraints: {lamda_equality}")
print(f"Lagrange multipliers for inequality constraints: {lamda_inequality}")
print(f"Value of f(x) at the final solution: {result.f}")
Final solution vector (x): [0.82287566 0.91143783]
Lagrange multipliers for equality constraints: [-1.59449112]
Lagrange multipliers for inequality constraints: [1.84659144]
Value of f(x) at the final solution: 1.3934649806878856
In ‘Test 1’, we expect a final solution of \(~[0.82, 0.91]\).
The solve
method returns the final solution vector along with three helpful quantities:
The Lagrange multipliers for the equality constraints when evaluated at the final solution vector.
The Lagrange multipliers for the inequality constraints when evaluated at the final solution vector.
The
Result
of evaluating theproblem
with the final solution vector.
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.
[3]:
x, lamda_equality, lamda_inequality, result = solve(
problem,
initial_x,
lbs=np.array([-10, -10]),
ubs=np.array([10, 10]),
)
print(f"Final solution vector (x): {x}")
print(f"Lagrange multipliers for equality constraints: {lamda_equality}")
print(f"Lagrange multipliers for inequality constraints: {lamda_inequality}")
print(f"Value of f(x) at the final solution: {result.f}")
Final solution vector (x): [0.82287566 0.91143783]
Lagrange multipliers for equality constraints: [-1.59449112]
Lagrange multipliers for inequality constraints: [1.84659144]
Value of f(x) at the final solution: 1.3934649806878856
There are several other cutsomisations available on the solve
function. These can be found in the API reference in the documentation.