Skip to content

Commit

Permalink
Diverse improvements.
Browse files Browse the repository at this point in the history
This is an intermediate working version. Please wait with comments until a stable version will be committed.
  • Loading branch information
marek-iiasa committed Jun 27, 2023
1 parent 017c328 commit c029fc8
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 47 deletions.
16 changes: 6 additions & 10 deletions message_ix/tools/lp_diag/lpdiag.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"""

import math
import os

# import os
# import sys # needed for sys.exit()
import typing
from collections import Counter
Expand All @@ -32,15 +32,11 @@ class LPdiag:
Attributes
----------
rep_dir : str
sub-directory for reports (text and plots)
"""

def __init__(self, rep_dir):
self.rep_dir = rep_dir # subdirectory for reports (text, in future also plots)
self.fname = ( # MPS input file (to be defined, if/when rd_mps() is called)
"undefined"
)
def __init__(self):
self.fname = "undefined" # MPS file name, to be defined by rd_mps() call
self.pname = "undefined" # problem name
self.id_rhs = False # True, if rhs_id defined
self.id_range = False # True, if range_id defined
Expand All @@ -53,8 +49,8 @@ def __init__(self, rep_dir):
self.n_rhs = 0 # number of defined RHS
self.n_ranges = 0 # number of defined ranges
self.n_bounds = 0 # number of defined bounds
if not os.path.exists(self.rep_dir):
os.makedirs(self.rep_dir, mode=0o755)
# if not os.path.exists(self.rep_dir):
# os.makedirs(self.rep_dir, mode=0o755)

# dictionaries for searchable names and its indices
# (searching very-long lists is prohibitively slow)
Expand Down
102 changes: 65 additions & 37 deletions message_ix/tools/lp_diag/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,48 @@
"""

import argparse
import os # imported by: from lpdiag import *
import os
from os import access, R_OK
from os.path import isfile
import sys # needed for sys.exit() and redirecting stdout

# import numpy as np
# import pandas as pd
from datetime import datetime as dt

# from datetime import timedelta as td
# noinspection PyUnresolvedReferences
from message_ix.tools.lp_diag.lpdiag import (
LPdiag, # LPdiag class for processing and analysis of LP matrices
)

# from lpdiag import *
# from datetime import timedelta as td
"""
The above import stmt works only in the message-ix editable environment; it is treated as error
by PyCharm (but it works); therefore, the noinspection option is applied for the statement.
In other environments the import from message_ix... does not work; therefore, it has to be
replaced by the below (now commented-out) import statement. The latter, however, mypy flags as error.
"""
# from lpdiag import LPdiag


def read_args():
descr = """
Driver of the LP diagnostics script.
Diagnostics of basic properties of LP Problems represented by the MPS-format.
Example usgae:
python main.py --path "message_ix/tools/lp_diag" --mps "test.mps" -s
Examples of usage:
python main.py
python main.py -h
python main.py --mps data/mps_tst/aez --outp foo.txt
"""
# python main.py --path "message_ix/tools/lp_diag" --mps "test.mps" -s

parser = argparse.ArgumentParser(
description=descr, formatter_class=argparse.RawDescriptionHelpFormatter
)
path = "--path : string\n Working directory of MCA/LPdiag."
parser.add_argument("--path", help=path)
mps = "--mps : string\n Name of mps file with extenstion."
wdir = "--wdir : string\n Working directory."
parser.add_argument("--wdir", help=wdir)
mps = "--mps : string\n Name of the MPS file (optionally with path)."
parser.add_argument("--mps", help=mps)
parser.add_argument("-s", "--save", action="store_true") # on/off flag
outp = "--outp : string\n Redirect output to the named file."
parser.add_argument("--outp", help=outp)
# parser.add_argument("-s", "--save", action="store_true") # on/off flag

# parse cli
cl_args = parser.parse_args()
Expand All @@ -50,22 +60,35 @@ def read_args():
functions of LPdiag class.
"""

dir1 = os.getcwd()
print(f'{dir1 =}')
tstart = dt.now()
# print('Started at:', str(tstart))

# Retrieve and assign arguments
args = read_args()
wrk_dir = args.path or "./"
dir2 = os.getcwd()
print(f'{dir2 =}')
w_dir = args.wdir or "."
# todo: change Data/mps_tst to: Test_mps, remove other Data
# prob_id = args.mps or "Data/mps_tst/diet" # default MPS for testing
prob_id = args.mps or "Data/mps_tst/aez" # default MPS for testing
# prob_id = args.mps or "Data/mps_tst/err_tst" # default MPS for testing
# prob_id = args.mps or "Data/mps_tst/lotfi" # default MPS for testing
# prob_id = args.mps or "Data/mps/of_led1" # default MPS for testing
redir_stdo = args.save
try:
os.chdir(wrk_dir)
except OSError:
print("cannot find", wrk_dir)
if len(w_dir) > 1:
print(f"Changing work-directory to: {w_dir}.")
try:
os.chdir(w_dir)
except OSError:
print(f"Cannot change work-directory to: {w_dir}.")
dir3 = os.getcwd()
print(f'{dir3 =}')
assert isfile(prob_id), (
f"MPS file {prob_id} not accessible from the dir:\n'{dir3}'.\n"
"Try to use the --wdir command option to set the work-directory."
)
assert access(prob_id, R_OK), f"MPS file {prob_id} is not readable."

# small MPSs, for testing the code, posted to Data/mps_tst dir
# err_tst - small MPS with various errors for testing the diagnostics
Expand Down Expand Up @@ -94,25 +117,29 @@ def read_args():
# prob_id = "of_baselin"
# fn_mps = data_dir + prob_id
# repdir = 'rep_shared/' # subdirectory for shared reports (NOT in git-repo)
repdir = "rep_tst/" # subdirectory for test-reports (NOT in git-repo)
# repdir = "rep_tst/" # subdirectory for test-reports (NOT in git-repo)

# redir_stdo = args.save
fn_outp = args.outp or None # optional redirection of stdout

# redir_stdo = False # redirect stdout to the file in repdir
default_stdout = sys.stdout
if redir_stdo:
fn_out = "./" + repdir + prob_id + ".txt" # file for redirected stdout
print(f"Stdout redirected to: {fn_out}")
f_out = open(fn_out, "w")
if fn_outp:
# fn_out = "./" + repdir + prob_id + ".txt" # file for redirected stdout
print(f"Stdout redirected to: {fn_outp}")
f_out = open(fn_outp, "w")
sys.stdout = f_out
else: # defined to avoid warnings (only used when redir_stdo == True)
fn_out = "foo"
f_out = open(fn_out, "w")
# else: # defined to avoid warnings (only used when redir_stdo == True)
# fn_out = "foo"
# f_out = open(fn_out, "w")

# lp = LPdiag(repdir) # LPdiag ctor
dir4 = os.getcwd()
print(f'{dir4 =}')

lp = LPdiag(repdir) # LPdiag ctor
# lp.rd_mps(fn_mps) # read MPS, store the matrix in dataFrame
lp = LPdiag() # LPdiag ctor
lp.rd_mps(prob_id) # read MPS, store the matrix in dataFrame
lp.stat(
lo_tail=-7, up_tail=5
) # statistics of the matrix coefficients, incl. distribution tails
lp.stat(lo_tail=-7, up_tail=5) # stats of matrix coeffs, incl. distrib. tails
# to get numbers of coeffs for each magnitute specify equal/overlapping tails:
# lp.stat(lo_tail=0, up_tail=0)
lp.out_loc(small=True, thresh=-7, max_rec=100) # locations of small-value outliers
Expand All @@ -126,16 +153,17 @@ def read_args():
print("Finished at:", str(tend))
print(f"Wall-clock execution time: {time_diff.seconds} sec.")

if redir_stdo: # close the redirected output
if fn_outp: # close the redirected output
# noinspection PyUnboundLocalVariable
f_out.close()
sys.stdout = default_stdout
print(f"\nRedirected stdout stored in {fn_out}. Now writing to the console.")
print(f"\nRedirected stdout stored in {fn_outp}. Now writing to the console.")
print("\nStarted at: ", str(tstart))
print("Finished at:", str(tend))
print(f"Wall-clock execution time: {time_diff.seconds} sec.")

# TODO: plots of distributions of coeffs, if indeed usefull
# TODO: naive scaling? might not be informative to due the later preprocessing
# TODO: conform(?) to the MPS-standard: reject numbers of
# TODO: naive scaling? might not be informative due to the later preprocessing
# TODO: conform(?) to the MPS-standard: in particular, reject numbers of
# abs(val): greater than 10^{10} or smaller than 10^{-10}
# to be discussed, if desired; also if it should be exception-error or info

0 comments on commit c029fc8

Please sign in to comment.