Friday, 24 April 2015

AMPL reporting solver statistics

This a simple script that I have been working on, which provides some Solver statistics to AMPL. At this moment, the script can handle the MINOS solver output and generate cool charts to summarize the process till the solver reaches a solution.
As usual, I have written a python script that is called by the shell command in AMPL.

option solver minos;
option minos_options "Superbasics_limit=100 summary_file=1 1=summary.txt summary_level = 1 summary_frequency=1";
  
printf "MINOS_report: generating AMPL results...\n";
shell 'C:\Python27\python.exe "MINOS_report.py" summary.txt';
As said, this script was written to work with MINOS, so after choosing MINOS as solver, we must provide to MINOS the output file which will contain the solver's statistics. The python script just parse the output file, clean it and generate the chart shown below. This code is just an example and does not aim to be very fast or optimum in terms of computational efficiency since is just a first test.
import pandas as pd
import matplotlib.pyplot as plt
import sys
import os


class Generate(object):
    def __init__(self, i_file):
        self.f_input = i_file
        self.f_output = "results_clean.csv"
        self.data = []
        self.output = []

    def clean_file(self):
        with open(self.f_input, "r") as f:
            it = 0
            for line in f:
                if "EXIT" in line:
                    break
                elif line[:7].strip().isdigit() and int(line[:7].strip()) > it \
                        and not "T" in line:
                    if len(line) > 10:
                        #print line
                        it += 1
                        self.output.append(line)
        f.close()
        w = open(self.f_output, "w")
        w.writelines(self.output)
        w.close()
        print "MINOS_report: new file > {0}".format(self.f_output)

    def get_data(self):
        cols = [(1, 7), (8, 16), (17, 22), (23, 32), (33, 48), (49, 54), (55, 60), (61, 65)]
        self.data = pd.read_fwf(self.f_output, colspecs=cols, header=None)

    def plots(self):

        itn = len(self.data)
        rgradient = self.data[1]
        ninf = self.data[2]
        sinf = self.data[3]
        objective = self.data[4]
        nobj = self.data[5]
        ncon = self.data[6]
        nsb = self.data[7]

        it = list(range(1, itn + 1))

        _min_obj = objective[itn - 1]
        _max_nsb = nsb[itn - 1]
        _max_nobj = nobj[itn - 1]
        _sinf = sinf[itn - 1]
        _ninf = ninf[itn - 1]
        _ncon = ncon[itn - 1]

        plt.figure("AMPL results")

        # plot 1
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        ax1 = plt.subplot(211)
        ax1.set_title('Iterations={0}. Objective (optimal solution)={1}'
                      .format(itn, _min_obj), size=10)

        ax1.set_xlabel('iterations')
        ax1.set_ylabel('objective function')
        l_obj = ax1.plot(it, objective, '.-', color="#0E3EEB", label="obj")
        ax2 = ax1.twinx()
        ax2.set_ylabel('reduced gradient')
        l_rg = ax2.plot(it, rgradient, '.-', color="#EB710E", label="rg")

        lns = l_obj + l_rg
        labs = [l.get_label() for l in lns]

        ax1.grid()
        ax1.legend(lns, labs, loc=0)

        # plot 2
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        ax3 = plt.subplot(223)
        ax3.set_title('number of superbasics={0}\n # objective and gradient calculations={1}'
                      .format(_max_nsb, _max_nobj), size=10)

        l_sb = ax3.plot(it, nsb, '.-', color="#0E3EEB", label="nsb")
        ax3.set_xlabel('iterations')
        ax3.set_ylabel('no.superbasics')
        ax4 = ax3.twinx()
        l_nobj = ax4.plot(it, nobj, '.-', color="#EB710E", label="nobj")
        ax4.set_ylabel('calculations obj. & gradient')

        lns = l_sb + l_nobj
        labs = [l.get_label() for l in lns]

        ax3.grid()
        ax3.legend(lns, labs, loc=4)

        # plot 3
        # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        plt.subplot(224)
        plt.title('no. of infeasibilities={0}\n sum of infeasibilities={1}\n '
        '# nonlinear constraints evaluations={2}'.format(_ninf, _sinf, _ncon), size=10)
        plt.plot(it, ncon, 'g-', label="ncon")
        plt.plot(it, ninf, 'y-', label="ninf")
        plt.plot(it, sinf, 'm-', label="sinf")
        plt.xlabel('iterations')
        plt.grid()
        plt.legend()

        plt.tight_layout()
        plt.show()


if __name__ == "__main__":
    summary_file = sys.argv[1]
    #summary_file = "C:/Users/Guillermo Navas/Desktop/MSc Statistics and Operations research/Continuous optimization/" \
    #                   "Modelling and solving optimization problems/Lab assignment 2 _ Network flow problems/solver_summary.txt"
    report = Generate(summary_file)
    report.clean_file()
    report.get_data()
    report.plots()