##
#  Copyright : Copyright (c) MOSEK ApS, Denmark. All rights reserved.
#
#  File :      reoptimization.py
#
#  Purpose:   Demonstrates how to solve a  linear
#             optimization problem using the MOSEK API
#             and modify and re-optimize the problem.
import sys
import mosek

# Since the actual value of Infinity is ignored, we define it solely
# for symbolic purposes:
inf = 0.0

def main():
    # Create a task
    with mosek.Task() as task:
        # Bound keys for constraints
        bkc = [mosek.boundkey.up,
               mosek.boundkey.up,
               mosek.boundkey.up]
        # Bound values for constraints
        blc = [-inf, -inf, -inf]
        buc = [100000.0, 50000.0, 60000.0]
        # Bound keys for variables
        bkx = [mosek.boundkey.lo,
               mosek.boundkey.lo,
               mosek.boundkey.lo]
        # Bound values for variables
        blx = [0.0, 0.0, 0.0]
        bux = [+inf, +inf, +inf]
        # Objective coefficients
        csub = [0, 1, 2]
        cval = [1.5, 2.5, 3.0]
        # We input the A matrix column-wise
        # asub contains row indexes
        asub = [0, 1, 2,
                0, 1, 2,
                0, 1, 2]
        # acof contains coefficients
        acof = [2.0, 3.0, 2.0,
                4.0, 2.0, 3.0,
                3.0, 3.0, 2.0]
        # aptrb and aptre contains the offsets into asub and acof where
        # columns start and end respectively
        aptrb = [0, 3, 6]
        aptre = [3, 6, 9]

        numvar = len(bkx)
        numcon = len(bkc)

        # Append the constraints
        task.appendcons(numcon)

        # Append the variables.
        task.appendvars(numvar)

        # Input objective
        task.putcfix(0.0)
        task.putclist(csub, cval)

        # Put constraint bounds
        task.putconboundslice(0, numcon, bkc, blc, buc)

        # Put variable bounds
        task.putvarboundslice(0, numvar, bkx, blx, bux)

        # Input A non-zeros by columns
        for j in range(numvar):
            ptrb, ptre = aptrb[j], aptre[j]
            task.putacol(j,
                         asub[ptrb:ptre],
                         acof[ptrb:ptre])

        # Input the objective sense (minimize/maximize)
        task.putobjsense(mosek.objsense.maximize)

        # Optimize the task
        task.optimize()

        # Output a solution
        xx = task.getsolutionslice(mosek.soltype.bas,
                                   mosek.solitem.xx,
                                   0, numvar)
        print("xx = {}".format(xx))

        ################# Make a change to the A matrix #############
        task.putaij(0, 0, 3.0)
        task.optimize()
        # Output a solution
        xx = task.getsolutionslice(mosek.soltype.bas,
                                   mosek.solitem.xx,
                                   0, numvar)
        print("xx = {}".format(xx))

        ################### Add a new variable ######################
        task.appendvars(1)
        numvar+=1

        # Set bounds on new varaible
        task.putvarbound(task.getnumvar() - 1,
                         mosek.boundkey.lo,
                         0,
                         +inf)

        # Change objective
        task.putcj(task.getnumvar() - 1, 1.0)

        # Put new values in the A matrix
        acolsub = [0, 2]
        acolval = [4.0, 1.0]

        task.putacol(task.getnumvar() - 1, # column index
                     acolsub,
                     acolval)
        # Change optimizer to simplex free and reoptimize
        task.putintparam(mosek.iparam.optimizer,
                         mosek.optimizertype.free_simplex)
        task.optimize()
        # Output a solution
        xx = task.getsolutionslice(mosek.soltype.bas,
                                   mosek.solitem.xx,
                                   0, numvar)
        print("xx = {}".format(xx))

        ############# Add a new constraint #######################
        task.appendcons(1)
        numcon+=1

        # Set bounds on new constraint
        task.putconbound(task.getnumcon() - 1,
                         mosek.boundkey.up, -inf, 30000)

        # Put new values in the A matrix
        arowsub = [0, 1, 2, 3]
        arowval = [1.0, 2.0, 1.0, 1.0]

        task.putarow(task.getnumcon() - 1, # row index
                     arowsub,
                     arowval)

        task.optimize()
        # Output a solution
        xx = task.getsolutionslice(mosek.soltype.bas,
                                   mosek.solitem.xx,
                                   0, numvar)
        print("xx = {}".format(xx))

        ############# Change constraint bounds #######################
        newbkc = [mosek.boundkey.up] * numcon
        newblc = [-inf] * numcon
        newbuc = [ 80000, 40000, 50000, 22000 ]

        task.putconboundslice(0, numcon, newbkc, newblc, newbuc)

        task.optimize()

        # Output a solution
        xx = task.getsolutionslice(mosek.soltype.bas,
                                   mosek.solitem.xx,
                                   0, numvar)
        print("xx = {}".format(xx))



# call the main function
try:
    main()
except mosek.MosekException as e:
    print("ERROR: %s" % str(e.errno))
    print("\t%s" % e.msg)
    sys.exit(1)
except:
    import traceback
    traceback.print_exc()
    sys.exit(1)
