{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Optimization Problem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this tutorial, we introduce how to build optimization problems using the Optimization Add-on module.\n", "Optimization Add-on introduces the `OptimizationProblem` class to model optimization problems.\n", "Unlike the traditional QuadraticProgram which was limited to quadratic objectives and constraints,\n", "OptimizationProblem supports higher-order polynomial objectives and constraints.\n", "More precisely, it deals with constrained polynomial programs given as follows:\n", "\n", "$$\n", "\\begin{align}\n", "\\text{minimize}\\quad& \\sum_{d=1}^{D} \\;\\; \\sum_{S \\subseteq \\{1,\\dots,n\\}, |S|=d} c_{S} \\prod_{i \\in S} x_i \\\\\n", "\\text{subject to}\\quad& \n", "\\sum_{d=1}^{D} \\;\\; \\sum_{S \\subseteq \\{1,\\dots,n\\}, |S|=d} a^{(j)}_{S} \\prod_{i \\in S} x_i \\;\\; \\leq b_j, \\quad j=1,\\dots,m \\\\\n", "& l_i \\leq x_i \\leq u_i, \\quad i=1,\\dots,n,\n", "\\end{align}\n", "$$\n", "\n", "where the coefficients $c_S$ and $a^{(j)}_S$ may involve terms of any degree up to $D$.\n", "The variables $x_i$ can be defined as binary, integer, continuous, or spin.\n", "In addition to \"$\\leq$\" constraints, OptimizationProblem also supports \"$\\geq$\" and \"$=$\"." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Constructing an `OptimizationProblem`\n", "We now explain how to build a model of an optimization problem using the `OptimizationProblem` class.\n", "Let's start from an empty model." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 0\n", "\n", "Subject to\n", " No constraints\n", "\n", " No variables\n", "\n" ] } ], "source": [ "from qiskit_addon_opt_mapper import OptimizationProblem\n", "\n", "# make an empty problem\n", "mod = OptimizationProblem(\"my problem\")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`OptimizationProblem` has a method `prettyprint` to generate a comprehensive string representation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `OptimizationProblem` supports four types of variables:\n", "\n", "- Binary variables can have the value 0 or 1.\n", "- Integer variables can have any integer value within their bounds.\n", "- Continuous variables can have any value within their bounds\n", "- Spin variables can have the value -1 or 1.\n", "\n", "When defining variables, you can specify their name, type, and optionally set lower and upper bounds." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 0\n", "\n", "Subject to\n", " No constraints\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "# Add variables\n", "mod.binary_var(name=\"x\")\n", "mod.integer_var(name=\"y\", lowerbound=-1, upperbound=5)\n", "mod.continuous_var(name=\"z\", lowerbound=-1, upperbound=5)\n", "mod.spin_var(name=\"s\")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can set the objective function by invoking `OptimizationProblem.minimize` or `OptimizationProblem.maximize`.\n", "The objective function can include:\n", "- a constant (offset),\n", "- linear terms ($c^{\\top} x$),\n", "- quadratic terms ($x^{\\top} Q x$),\n", "- and higher-order terms ($\\sum_S h_S \\prod_{i \\in S} x_i$, where $|S| \\ge 3$).\n", "\n", "These terms can be specified using a list, a matrix, or a dictionary.\n", "For higher-order terms, we use a nested dictionary of the form\n", "`Dict[int, Dict[Tuple[str | int, ...], float]]`, \n", "where the outer key is the degree of the term, and the inner dictionary maps a tuple of variable names (or indices) to its coefficient.\n", "\n", "The example below shows how to define an objective function using a dictionary. \n", "For the linear term, the keys are variable names and the values are their coefficients. \n", "For the quadratic term, the keys are pairs of variables, and the values are their coefficients. \n", "For higher-order terms, the outer key is the degree of the term, and the inner dictionary maps a tuple of variable names to its coefficient." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 4*x*y*z + 2*x*y - z^2 + x + 3\n", "\n", "Subject to\n", " No constraints\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "# Add objective function using dictionaries\n", "mod.minimize(\n", " constant=3,\n", " linear={\"x\": 1},\n", " quadratic={(\"x\", \"y\"): 2, (\"z\", \"z\"): -1},\n", " higher_order={3: {(\"x\", \"y\", \"z\"): 4}},\n", ")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Another way to specify the optimization problem is using arrays.\n", "- For the linear term, the array corresponds to the vector $c$ in the mathematical formulation.\n", "- For the quadratic term, the array corresponds to the matrix $Q$.\n", "- For higher-order terms, multi-dimensional arrays are used: an order-$k$ tensor represents the coefficients of degree-$k$ monomials. For example, a 3rd-order tensor $H^{(3)}$ stores the coefficients $h_{ijk}$ for terms $x_i x_j x_k$.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 4*x*y*z + 2*x*y - z^2 + x + 3\n", "\n", "Subject to\n", " No constraints\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "import numpy as np\n", "\n", "# Add objective function using lists/arrays\n", "# Here, h3 is a 3D array for the cubic terms\n", "h3 = np.zeros((4, 4, 4)) # assuming we have 4 variables\n", "h3[0, 1, 2] = 4 # corresponds to x * y * z\n", "mod.minimize(\n", " constant=3,\n", " linear=[1, 0, 0, 0],\n", " quadratic=[[0, 1, 0, 0], [1, 0, 0, 0], [0, 0, -1, 0], [0, 0, 0, 0]],\n", " higher_order={3: h3},\n", ")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can access the constant, the linear term, the quadratic term, and the higher-order terms by looking at\n", "`OptimizationProblem.objective.{constant, linear, quadratic, higher_order}`, respectively.\n", "\n", "For linear, quadratic, and higher-order terms, you can obtain:\n", "- a dense array (`to_array`),\n", "- a sparse representation (`coefficients`),\n", "- or a dictionary (`to_dict`).\n", "\n", "For dictionaries, you can specify whether to use variable indices or names as keys.\n", "\n", "- **Quadratic terms** are stored in a compressed way. For example, `{('x', 'y'): 1, ('y', 'x'): 2}` is stored as `{('x', 'y'): 3}`.\n", " You can get the quadratic term as a symmetric matrix by calling `to_array(symmetric=True)`.\n", "\n", "- **Higher-order terms** are represented as nested dictionaries:\n", " `Dict[int, Dict[Tuple[str | int, ...], float]]`,\n", " where the outer key is the degree, and the inner dictionary maps a tuple of variables to its coefficient.\n", " For example, `{3: {('x','y','z'): 4}}` corresponds to the cubic term $4xyz$.\n", " Similarly to quadratic terms, you can call `to_array(symmetric=True)` to aggregate coefficients across permutations of the same monomial." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "constant:\t\t\t 3.0\n", "linear dict:\t\t\t {np.int32(0): np.int64(1)}\n", "linear array:\t\t\t [1 0 0 0]\n", "linear array as sparse matrix:\n", " (np.int32(0), np.int32(0))\t1 \n", "\n", "quadratic dict w/ index:\t {(0, 1): np.float64(2.0), (2, 2): np.float64(-1.0)}\n", "quadratic dict w/ name:\t\t {('x', 'y'): np.float64(2.0), ('z', 'z'): np.float64(-1.0)}\n", "symmetric quadratic dict w/ name:\t {('x', 'y'): np.float64(1.0), ('y', 'x'): np.float64(1.0), ('z', 'z'): np.float64(-1.0)}\n", "quadratic matrix:\n", " [[ 0. 2. 0. 0.]\n", " [ 0. 0. 0. 0.]\n", " [ 0. 0. -1. 0.]\n", " [ 0. 0. 0. 0.]] \n", "\n", "symmetric quadratic matrix:\n", " [[ 0. 1. 0. 0.]\n", " [ 1. 0. 0. 0.]\n", " [ 0. 0. -1. 0.]\n", " [ 0. 0. 0. 0.]] \n", "\n", "quadratic matrix as sparse matrix:\n", " (np.int32(0), np.int32(1))\t2.0\n", " (np.int32(2), np.int32(2))\t-1.0\n", "higher-order dict w/ index:\t {(0, 1, 2): 4.0}\n", "higher-order dict w/ name:\t {('x', 'y', 'z'): 4.0}\n", "higher-order matrix:\n", " [[[0. 0. 0. 0. ]\n", " [0. 0. 0.66666667 0. ]\n", " [0. 0.66666667 0. 0. ]\n", " [0. 0. 0. 0. ]]\n", "\n", " [[0. 0. 0.66666667 0. ]\n", " [0. 0. 0. 0. ]\n", " [0.66666667 0. 0. 0. ]\n", " [0. 0. 0. 0. ]]\n", "\n", " [[0. 0.66666667 0. 0. ]\n", " [0.66666667 0. 0. 0. ]\n", " [0. 0. 0. 0. ]\n", " [0. 0. 0. 0. ]]\n", "\n", " [[0. 0. 0. 0. ]\n", " [0. 0. 0. 0. ]\n", " [0. 0. 0. 0. ]\n", " [0. 0. 0. 0. ]]] \n", "\n", "symmetric higher-order matrix:\n", " [[[0. 0. 0. 0.]\n", " [0. 0. 4. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]]\n", "\n", " [[0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]]\n", "\n", " [[0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]]\n", "\n", " [[0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]\n", " [0. 0. 0. 0.]]] \n", "\n", "higher-order matrix as sparse matrix:\n", " {(0, 1, 2): 4.0}\n" ] } ], "source": [ "print(\"constant:\\t\\t\\t\", mod.objective.constant)\n", "print(\"linear dict:\\t\\t\\t\", mod.objective.linear.to_dict())\n", "print(\"linear array:\\t\\t\\t\", mod.objective.linear.to_array())\n", "print(\"linear array as sparse matrix:\\n\", mod.objective.linear.coefficients, \"\\n\")\n", "print(\"quadratic dict w/ index:\\t\", mod.objective.quadratic.to_dict())\n", "print(\"quadratic dict w/ name:\\t\\t\", mod.objective.quadratic.to_dict(use_name=True))\n", "print(\n", " \"symmetric quadratic dict w/ name:\\t\",\n", " mod.objective.quadratic.to_dict(use_name=True, symmetric=True),\n", ")\n", "print(\"quadratic matrix:\\n\", mod.objective.quadratic.to_array(), \"\\n\")\n", "print(\"symmetric quadratic matrix:\\n\", mod.objective.quadratic.to_array(symmetric=True), \"\\n\")\n", "print(\"quadratic matrix as sparse matrix:\\n\", mod.objective.quadratic.coefficients)\n", "print(\"higher-order dict w/ index:\\t\", mod.objective.higher_order[3].to_dict())\n", "print(\"higher-order dict w/ name:\\t\", mod.objective.higher_order[3].to_dict(use_name=True))\n", "print(\"higher-order matrix:\\n\", mod.objective.higher_order[3].to_array(), \"\\n\")\n", "print(\n", " \"symmetric higher-order matrix:\\n\", mod.objective.higher_order[3].to_array(symmetric=True), \"\\n\"\n", ")\n", "print(\"higher-order matrix as sparse matrix:\\n\", mod.objective.higher_order[3].coefficients)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding/removing linear, quadratic and higher-order terms to/from the constraints" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can add linear constraints by setting name, linear expression, sense and right-hand-side value (rhs).\n", "You can use senses `'=='`, `'<='`, and `'>='`." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 4*x*y*z + 2*x*y - z^2 + x + 3\n", "\n", "Subject to\n", " Linear constraints (3)\n", " x + 2*y == 3 'lin_eq'\n", " x + 2*y <= 3 'lin_leq'\n", " x + 2*y >= 3 'lin_geq'\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "# Add linear constraints\n", "mod.linear_constraint(linear={\"x\": 1, \"y\": 2}, sense=\"==\", rhs=3, name=\"lin_eq\")\n", "mod.linear_constraint(linear={\"x\": 1, \"y\": 2}, sense=\"<=\", rhs=3, name=\"lin_leq\")\n", "mod.linear_constraint(linear={\"x\": 1, \"y\": 2}, sense=\">=\", rhs=3, name=\"lin_geq\")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can add quadratic constraints by setting name, quadratic expression, sense and right-hand-side value (rhs)." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 4*x*y*z + 2*x*y - z^2 + x + 3\n", "\n", "Subject to\n", " Linear constraints (3)\n", " x + 2*y == 3 'lin_eq'\n", " x + 2*y <= 3 'lin_leq'\n", " x + 2*y >= 3 'lin_geq'\n", "\n", " Quadratic constraints (3)\n", " x^2 - y*z + x + y == 1 'quad_eq'\n", " x^2 - y*z + x + y <= 1 'quad_leq'\n", " x^2 - y*z + x + y >= 1 'quad_geq'\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "# Add quadratic constraints\n", "mod.quadratic_constraint(\n", " linear={\"x\": 1, \"y\": 1},\n", " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", " sense=\"==\",\n", " rhs=1,\n", " name=\"quad_eq\",\n", ")\n", "mod.quadratic_constraint(\n", " linear={\"x\": 1, \"y\": 1},\n", " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", " sense=\"<=\",\n", " rhs=1,\n", " name=\"quad_leq\",\n", ")\n", "mod.quadratic_constraint(\n", " linear={\"x\": 1, \"y\": 1},\n", " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", " sense=\">=\",\n", " rhs=1,\n", " name=\"quad_geq\",\n", ")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also add higher-order constraints by setting name, higher-order expression, sense and right-hand-side value (rhs)." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 4*x*y*z + 2*x*y - z^2 + x + 3\n", "\n", "Subject to\n", " Linear constraints (3)\n", " x + 2*y == 3 'lin_eq'\n", " x + 2*y <= 3 'lin_leq'\n", " x + 2*y >= 3 'lin_geq'\n", "\n", " Quadratic constraints (3)\n", " x^2 - y*z + x + y == 1 'quad_eq'\n", " x^2 - y*z + x + y <= 1 'quad_leq'\n", " x^2 - y*z + x + y >= 1 'quad_geq'\n", "\n", " Higher-order constraints (3)\n", " 2*s*x*y*z + x*y*z + x^2 - y*z + x == 1 'hoc_eq'\n", " 2*s*x*y*z + x*y*z + x^2 - y*z + x <= 1 'hoc_leq'\n", " 2*s*x*y*z + x*y*z + x^2 - y*z + x >= 1 'hoc_geq'\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "# Add higher-order constraints\n", "mod.higher_order_constraint(\n", " linear={\"x\": 1},\n", " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", " higher_order={3: {(\"x\", \"y\", \"z\"): 1}, 4: {(\"x\", \"y\", \"z\", \"s\"): 2}},\n", " sense=\"==\",\n", " rhs=1,\n", " name=\"hoc_eq\",\n", ")\n", "mod.higher_order_constraint(\n", " linear={\"x\": 1},\n", " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", " higher_order={3: {(\"x\", \"y\", \"z\"): 1}, 4: {(\"x\", \"y\", \"z\", \"s\"): 2}},\n", " sense=\"<=\",\n", " rhs=1,\n", " name=\"hoc_leq\",\n", ")\n", "mod.higher_order_constraint(\n", " linear={\"x\": 1},\n", " quadratic={(\"x\", \"x\"): 1, (\"y\", \"z\"): -1},\n", " higher_order={3: {(\"x\", \"y\", \"z\"): 1}, 4: {(\"x\", \"y\", \"z\", \"s\"): 2}},\n", " sense=\">=\",\n", " rhs=1,\n", " name=\"hoc_geq\",\n", ")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can access linear and quadratic terms of linear and quadratic constraints as in the same way as the objective function." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "lin_geq: {'x': np.float64(1.0), 'y': np.float64(2.0)} ConstraintSense.GE 3\n", "quad_geq: {'x': np.float64(1.0), 'y': np.float64(1.0)} {('x', 'x'): np.float64(1.0), ('y', 'z'): np.float64(-1.0)} ConstraintSense.GE 3\n", "hoc_geq: {'x': np.float64(1.0)} {('x', 'x'): np.float64(1.0), ('y', 'z'): np.float64(-1.0)} {3: {('x', 'y', 'z'): 1.0}, 4: {('x', 'y', 'z', 's'): 2.0}} ConstraintSense.GE 3\n" ] } ], "source": [ "lin_geq = mod.get_linear_constraint(\"lin_geq\")\n", "print(\"lin_geq:\", lin_geq.linear.to_dict(use_name=True), lin_geq.sense, lin_geq.rhs)\n", "quad_geq = mod.get_quadratic_constraint(\"quad_geq\")\n", "print(\n", " \"quad_geq:\",\n", " quad_geq.linear.to_dict(use_name=True),\n", " quad_geq.quadratic.to_dict(use_name=True),\n", " quad_geq.sense,\n", " lin_geq.rhs,\n", ")\n", "hoc_geq = mod.get_higher_order_constraint(\"hoc_geq\")\n", "print(\n", " \"hoc_geq:\",\n", " hoc_geq.linear.to_dict(use_name=True),\n", " hoc_geq.quadratic.to_dict(use_name=True),\n", " {k: v.to_dict(use_name=True) for k, v in hoc_geq.higher_order.items()},\n", " hoc_geq.sense,\n", " lin_geq.rhs,\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also remove linear/quadratic/higher-order constraints by using the `remove_linear_constraint`, `remove_quadratic_constraint`, and `remove_higher_order_constraint` methods, respectively." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " 4*x*y*z + 2*x*y - z^2 + x + 3\n", "\n", "Subject to\n", " Linear constraints (2)\n", " x + 2*y <= 3 'lin_leq'\n", " x + 2*y >= 3 'lin_geq'\n", "\n", " Quadratic constraints (2)\n", " x^2 - y*z + x + y == 1 'quad_eq'\n", " x^2 - y*z + x + y >= 1 'quad_geq'\n", "\n", " Higher-order constraints (2)\n", " 2*s*x*y*z + x*y*z + x^2 - y*z + x <= 1 'hoc_leq'\n", " 2*s*x*y*z + x*y*z + x^2 - y*z + x >= 1 'hoc_geq'\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "# Remove constraints\n", "mod.remove_linear_constraint(\"lin_eq\")\n", "mod.remove_quadratic_constraint(\"quad_leq\")\n", "mod.remove_higher_order_constraint(\"hoc_eq\")\n", "print(mod.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Substituting Variables" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can substitute some of the variables with constants or other variables.\n", "More precisely, `OptimizationProblem` has a method `substitute_variables(constants=..., variables=...)` to deal with the following two cases.\n", "\n", "- $x \\leftarrow c$: when `constants` have a dictionary `{x: c}`.\n", "- $x \\leftarrow c y$: when `variables` have a dictionary `{x: (y, c)}`." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: my problem\n", "\n", "Minimize\n", " -5*z^2 - 2*z + 4\n", "\n", "Subject to\n", " Linear constraints (2)\n", " -2*z <= 2 'lin_leq'\n", " -2*z >= 2 'lin_geq'\n", "\n", " Quadratic constraints (2)\n", " z^2 - z == -1 'quad_eq'\n", " z^2 - z >= -1 'quad_geq'\n", "\n", " Higher-order constraints (2)\n", " -2*s*z^2 <= -1 'hoc_leq'\n", " -2*s*z^2 >= -1 'hoc_geq'\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 1\n", "\n", " Spin variables (1)\n", " -1 <= s <= 1\n", "\n" ] } ], "source": [ "sub = mod.substitute_variables(constants={\"x\": 1}, variables={\"y\": (\"z\", -1)})\n", "print(sub.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the resulting problem is infeasible due to lower bounds or upper bounds, the methods returns the status `Status.INFEASIBLE`.\n", "We try to replace variable `x` with -1, but -1 is out of range of `x` (0 <= `x` <= 1). So, it returns `Status.INFEASIBLE`." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Infeasible substitution for variable: x\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "OptimizationProblemStatus.INFEASIBLE\n" ] } ], "source": [ "sub = mod.substitute_variables(constants={\"x\": -1})\n", "print(sub.status)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You cannot substitute variables multiple times. \n", "The method raises an error in such a case." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Error: Cannot substitute by variable that gets substituted itself: y <- x 1\n" ] } ], "source": [ "from qiskit_addon_opt_mapper import OptimizationError\n", "\n", "try:\n", " sub = mod.substitute_variables(constants={\"x\": -1}, variables={\"y\": (\"x\", 1)})\n", "except OptimizationError as e:\n", " print(f\"Error: {e}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Supporting Docplex models\n", "\n", "The `OptimizationProblem` can be constructed from a Docplex model.\n", "You can use the `from_docplex` method to convert a Docplex model to an `OptimizationProblem`." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\\ This file has been generated by DOcplex\n", "\\ ENCODING=ISO-8859-1\n", "\\Problem name: docplex model\n", "\n", "Minimize\n", " obj: x + [ 4 x*y - 2 z^2 ]/2 + 3\n", "Subject To\n", " lin_eq: x + 2 y = 3\n", " lin_leq: x + 2 y <= 3\n", " lin_geq: x + 2 y >= 3\n", "\n", "Bounds\n", " 0 <= x <= 1\n", " -1 <= y <= 5\n", " -1 <= z <= 5\n", "\n", "Binaries\n", " x\n", "\n", "Generals\n", " y\n", "End\n", "\n" ] } ], "source": [ "from docplex.mp.model import Model\n", "\n", "docplex_mod = Model(name=\"docplex model\")\n", "x = docplex_mod.binary_var(name=\"x\")\n", "y = docplex_mod.integer_var(name=\"y\", lb=-1, ub=5)\n", "z = docplex_mod.continuous_var(name=\"z\", lb=-1, ub=5)\n", "\n", "docplex_mod.minimize(3 + x + 2 * x * y - z * z)\n", "docplex_mod.add_constraint(x + 2 * y == 3, \"lin_eq\")\n", "docplex_mod.add_constraint(x + 2 * y <= 3, \"lin_leq\")\n", "docplex_mod.add_constraint(x + 2 * y >= 3, \"lin_geq\")\n", "\n", "print(docplex_mod.export_as_lp_string())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note:\n", "When you display your problem as LP format using `export_as_lp_string`,\n", "`Binaries` denotes binary variables and `Generals` denotes integer variables.\n", "If variables are not included in either `Binaries` or `Generals`, such variables are continuous ones with default lower bound = 0 and upper bound = infinity.\n", "Note that you cannot use 'e' or 'E' as the first character of names due to the [specification of LP format](https://www.ibm.com/docs/en/icos/22.1.0?topic=representation-variable-names-in-lp-file-format)." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Problem name: docplex model\n", "\n", "Minimize\n", " 2*x*y - z^2 + x + 3\n", "\n", "Subject to\n", " Linear constraints (3)\n", " x + 2*y == 3 'lin_eq'\n", " x + 2*y <= 3 'lin_leq'\n", " x + 2*y >= 3 'lin_geq'\n", "\n", " Integer variables (1)\n", " -1 <= y <= 5\n", "\n", " Continuous variables (1)\n", " -1 <= z <= 5\n", "\n", " Binary variables (1)\n", " x\n", "\n" ] } ], "source": [ "from qiskit_addon_opt_mapper.translators import from_docplex_mp\n", "\n", "mod2 = from_docplex_mp(docplex_mod)\n", "print(mod2.prettyprint())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also convert a `OptimizationProblem` to a Docplex model using the `to_docplex` method." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\\ This file has been generated by DOcplex\n", "\\ ENCODING=ISO-8859-1\n", "\\Problem name: docplex model\n", "\n", "Minimize\n", " obj: x + [ 4 x*y - 2 z^2 ]/2 + 3\n", "Subject To\n", " lin_eq: x + 2 y = 3\n", " lin_leq: x + 2 y <= 3\n", " lin_geq: x + 2 y >= 3\n", "\n", "Bounds\n", " 0 <= x <= 1\n", " -1 <= y <= 5\n", " -1 <= z <= 5\n", "\n", "Binaries\n", " x\n", "\n", "Generals\n", " y\n", "End\n", "\n" ] } ], "source": [ "from qiskit_addon_opt_mapper.translators import to_docplex_mp\n", "\n", "docplex_mod2 = to_docplex_mp(mod2)\n", "print(docplex_mod2.export_as_lp_string())" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "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.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }