{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# **Using indicator constraints**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "***indicators.ipynb***\n",
    "\n",
    "This example constructs a problem from scratch with variables of various types, adds indicator constraints and shows how to retrieve data regarding indicator coinstraints once they have been added to the problem using the API functions.\n",
    "\n",
    "&copy; Copyright 2025 Fair Isaac Corporation\n",
    "\n",
    "Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.\n",
    " \n",
    "Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.\n",
    "\n",
    "This example uses FICO&reg; Xpress software. By running it, you agree to the Community License terms of the [Xpress Shrinkwrap License Agreement](https://www.fico.com/en/shrinkwrap-license-agreement-fico-xpress-optimization-suite-on-premises) with respect to the FICO&reg; Xpress software. See the [licensing options](https://www.fico.com/en/fico-xpress-trial-and-licensing-options) overview for additional details and information about obtaining a paid license."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Install the xpress package\n",
    "%pip install -q xpress"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Start by creating a problem and adding all variables used in this example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import xpress as xp\n",
    "\n",
    "p = xp.problem()\n",
    "\n",
    "p.controls.miprelstop = 0\n",
    "\n",
    "# All variables used in this example\n",
    "v1 = p.addVariable(lb=0, ub=10, vartype=xp.continuous)\n",
    "v2 = p.addVariable(lb=1, ub=7, vartype=xp.continuous)\n",
    "v3 = p.addVariable(lb=5, ub=10, threshold=7, vartype=xp.semicontinuous)\n",
    "vb = p.addVariable(vartype=xp.binary)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Indicator constraints consist of **a tuple** with a condition on a binary variable and a constraint. If the condition on the binary variable holds, then the constraint must apply. The conditional constraints can be linear or nonlinear, equalities or inequalities.\n",
    "\n",
    "The code cell below creates two objects, each containing a tuple for creating an indicator constraint."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "ind1 = (vb == 0, v1 + v2 >= 6)   # if variable 'vb' is equal to 0, constraint 'v1 + v2 >= 6' applies\n",
    "\n",
    "ind2 = (vb == 1, v1 + v3 >= 4)   # if variable 'vb' is equal to 1, constraint 'v1 + v3 >= 4' applies"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Indicator constraints can conveniently be added by using the [problep.addIndicator()](https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/python/HTML/problem.addIndicator.html) method (note the \"==\" as the symbol for the equality on the indicator).\n",
    "\n",
    "The method accepts objects that have been created previously as well as new tuples stated directly as arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.addIndicator(ind1)                            # adds the first indicator constraint\n",
    "\n",
    "p.addIndicator((vb == 1, v2 + v3 >= 5), ind2)   # adds another indicator constraint and the second one defined above"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The method [problem.getindicators](https://www.fico.com/fico-xpress-optimization/docs/latest/solver/optimizer/python/HTML/problem.getindicators.html) can be used to get the indicator constraint conditions (indicator variables and complement flags) associated to the rows in a given range.\n",
    "\n",
    "The complement flags can be:\n",
    "  - 0: not an indicator constraint\n",
    "  - 1: for indicator constraints with condition \" bin = 1\"\n",
    "  - -1: for indicator constraints with condition \" bin = 0\"\n",
    "\n",
    "As we can observe in the output log, all three indicator conditions have the column *C4* as the indicator variable. The first constraint applies when *vb == 0* while the last two constraints are to be applied if *vb == 1*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Indicator variables and flags:  [3, 3, 3] [-1, 1, 1]\n"
     ]
    }
   ],
   "source": [
    "ind_inds, ind_flags = p.getIndicators(0, 2)\n",
    "\n",
    "print(\"Indicator variables and flags: \", ind_inds, ind_flags)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Lastly, we define the objective function as the sum of the variables *v1,v2,v3* and optimize the problem, before printing the solution values for *v1* and *v2*, as well as the whole solution vector and the final objective value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "p.setObjective(xp.Sum(v1,v2,v3))\n",
    "\n",
    "p.optimize()\n",
    "\n",
    "print(\"v1: \", p.getSolution(v1),\n",
    "      \", v2: \", p.getSolution(v2),\n",
    "      \"; sol vector: \", p.getSolution(),\n",
    "      \"; obj: \", p.attributes.objval,\n",
    "      sep=\"\")  # default separator between strings is \" \""
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "xpress9.8 (3.12.9)",
   "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.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
