Changeset 2825


Ignore:
Timestamp:
Jul 20, 2010 4:00:36 PM (9 years ago)
Author:
jwatson
Message:

Modified GLPK plugin to throw an exception when unexpected line parse issues are encountered, so we (and the user) can quickly identify the line at fault. And figure out that GLPK is the issue.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • coopr.plugins/trunk/coopr/plugins/mip/GLPK.py

    r2777 r2825  
    253253        INPUT = open(self.soln_file,"r")
    254254
    255         state = 0 # 0=initial header, 1=constraints, 2=variables, -1=done
    256 
    257         results.problem.number_of_objectives = 1
    258 
    259         number_of_constraints_read = 0 # for validation of the total count read and the order
    260         number_of_variables_read = 0
    261         active_constraint_name = "" # constraint names and their value/bounds can be split across multiple lines
    262         active_variable_name = "" # variable names and their value/bounds can be split across multiple lines
    263 
    264         for line in INPUT:
    265           tokens = re.split('[ \t]+',line.strip())
    266 
    267           if (len(tokens) == 1) and (len(tokens[0]) == 0):
    268              pass
    269           elif state == 0:
    270              #
    271              # Processing initial header
    272              #
    273              if len(tokens) == 2 and tokens[0] == "Problem:":
    274                 # the problem name may be absent, in which case the "Problem:" line will be skipped.
    275                 results.problem.name = tokens[1]
    276              elif len(tokens) == 2 and tokens[0] == "Rows:":
    277                 results.problem.number_of_constraints = eval(tokens[1])
    278              elif len(tokens) == 2 and tokens[0] == "Columns:":
    279                 lp_solution = True
    280                 results.problem.number_of_variables = eval(tokens[1])
    281              elif len(tokens) > 2 and tokens[0] == "Columns:":
    282                 lp_solution = False
    283                 results.problem.number_of_variables = eval(tokens[1])
    284              elif len(tokens) == 2 and tokens[0] == "Non-zeros:":
    285                 results.problem.number_of_nonzeros = eval(tokens[1])
    286              elif len(tokens) >= 2 and tokens[0] == "Status:":
    287                 if tokens[1] == "OPTIMAL":
    288                    soln.status = SolutionStatus.optimal
    289                 elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "NON-OPTIMAL":
    290                    soln.status = SolutionStatus.bestSoFar
    291                 elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "OPTIMAL":
    292                    soln.status = SolutionStatus.optimal
    293                 elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "UNDEFINED":
    294                    soln.status = SolutionStatus.stoppedByLimit
    295                 elif (len(tokens) == 2) and (tokens[1] == "UNDEFINED"):
    296                    soln.status = SolutionStatus.infeasible
    297                 else:
    298                    print "WARNING: Read unknown status while parsing GLPK solution file - status="+" ".join(tokens[1:])
    299              elif len(tokens) >= 2 and tokens[0] == "Objective:":
    300                 if tokens[4] == "(MINimum)":
    301                    results.problem.sense = ProblemSense.minimize
    302                 else:
    303                    results.problem.sense = ProblemSense.maximize
    304                 soln.objective['f']=tokens[3]
    305                 if soln.status is SolutionStatus.optimal:
     255        try:
     256
     257           state = 0 # 0=initial header, 1=constraints, 2=variables, -1=done
     258
     259           results.problem.number_of_objectives = 1
     260
     261           number_of_constraints_read = 0 # for validation of the total count read and the order
     262           number_of_variables_read = 0
     263           active_constraint_name = "" # constraint names and their value/bounds can be split across multiple lines
     264           active_variable_name = "" # variable names and their value/bounds can be split across multiple lines
     265
     266           for line in INPUT:
     267             tokens = re.split('[ \t]+',line.strip())
     268
     269             if (len(tokens) == 1) and (len(tokens[0]) == 0):
     270                pass
     271             elif state == 0:
     272                #
     273                # Processing initial header
     274                #
     275                if len(tokens) == 2 and tokens[0] == "Problem:":
     276                   # the problem name may be absent, in which case the "Problem:" line will be skipped.
     277                   results.problem.name = tokens[1]
     278                elif len(tokens) == 2 and tokens[0] == "Rows:":
     279                   results.problem.number_of_constraints = eval(tokens[1])
     280                elif len(tokens) == 2 and tokens[0] == "Columns:":
     281                   lp_solution = True
     282                   results.problem.number_of_variables = eval(tokens[1])
     283                elif len(tokens) > 2 and tokens[0] == "Columns:":
     284                   lp_solution = False
     285                   results.problem.number_of_variables = eval(tokens[1])
     286                elif len(tokens) == 2 and tokens[0] == "Non-zeros:":
     287                   results.problem.number_of_nonzeros = eval(tokens[1])
     288                elif len(tokens) >= 2 and tokens[0] == "Status:":
     289                   if tokens[1] == "OPTIMAL":
     290                      soln.status = SolutionStatus.optimal
     291                   elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "NON-OPTIMAL":
     292                      soln.status = SolutionStatus.bestSoFar
     293                   elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "OPTIMAL":
     294                      soln.status = SolutionStatus.optimal
     295                   elif len(tokens) == 3 and tokens[1] == "INTEGER" and tokens[2] == "UNDEFINED":
     296                      soln.status = SolutionStatus.stoppedByLimit
     297                   elif (len(tokens) == 2) and (tokens[1] == "UNDEFINED"):
     298                      soln.status = SolutionStatus.infeasible
     299                   else:
     300                      print "WARNING: Read unknown status while parsing GLPK solution file - status="+" ".join(tokens[1:])
     301                elif len(tokens) >= 2 and tokens[0] == "Objective:":
    306302                   if tokens[4] == "(MINimum)":
    307                         results.problem.lower_bound = soln.objective['f'].value
    308                         if "upper_bound" in dir(results.problem):
    309                             del results.problem.upper_bound
    310                    else:
    311                         results.problem.upper_bound = soln.objective['f'].value
    312                         if "lower_bound" in dir(results.problem):
    313                             del results.problem.lower_bound
    314                 # the objective is the last entry in the problem section - move on to constraints.
    315                 state = 1
    316 
    317           elif state == 1:
    318              #
    319              # Process Constraint Info
    320              #
    321 
    322              if (len(tokens) == 2) and (len(active_constraint_name) == 0):
    323 
    324                 number_of_constraints_read = number_of_constraints_read + 1
    325                 active_constraint_name = tokens[1].strip()
    326                 index = eval(tokens[0].strip())
    327 
    328                 # sanity check - the indices should be in sequence.
    329                 if index != number_of_constraints_read:
    330                    raise ValueError,"***ERROR: Unexpected constraint index encountered on line="+line+"; expected value="+str(number_of_constraints_read)+"; actual value="+str(index)
    331 
    332              else:
    333 
    334                 index = None
    335                 activity = None
    336                 lower_bound = None
    337                 upper_bound = None
    338                 marginal = None
    339 
    340                 # extract the field names and process accordingly. there
    341                 # is some wasted processing w.r.t. single versus double-line
    342                 # entries, but it's not significant enough to worry about.
    343 
    344                 index_string = line[0:6].strip()
    345                 name_string = line[7:19].strip()
    346                 activity_string = line[23:36].strip()
    347                 lower_bound_string = line[37:50].strip()
    348                 upper_bound_string = line[51:64].strip()
    349 
    350                 state_string = None
    351                 marginal_string = None
    352 
    353                 # skip any headers
    354                 if (index_string == "------") or (index_string == "No."):
    355                    continue
    356 
    357                 if len(index_string) > 0:
    358                    index = eval(index_string)
    359 
    360                 if lp_solution is True:
    361                    state_string = line[20:22].strip()
    362                    marginal_string = line[65:78].strip()
    363                    if (activity_string != "< eps") and (len(activity_string) > 0):
    364                       activity = eval(activity_string)
    365                    else:
    366                       activity = 0.0
    367                    if (lower_bound_string != "< eps") and (len(lower_bound_string) > 0):
    368                       lower_bound = eval(lower_bound_string)
    369                    else:
    370                       lower_bound = 0.0
    371                    if state_string != "NS":
    372                       if (upper_bound_string != "< eps") and (len(upper_bound_string) > 0):
    373                          upper_bound = eval(upper_bound_string)
    374                       else:
    375                          upper_bound = 0.0
    376                    if (marginal_string != "< eps") and (len(marginal_string) > 0):
    377                       marginal = eval(marginal_string)
    378                    else:
    379                       marginal = 0.0
    380 
    381                 else:
    382                     # no constraint-related attributes/values are extracted currently for MIPs.
    383                     pass
    384 
    385                 constraint_name = None
    386                 if len(active_constraint_name) > 0:
    387                    # if there is an active constraint name, the identifier was
    388                    # too long for everything to be on a single line; the second
    389                    # line contains all of the value information.
    390                    constraint_name = active_constraint_name
    391                    active_constraint_name = ""
    392                 else:
    393                    # everything is on a single line.
    394                    constraint_name = name_string
     303                      results.problem.sense = ProblemSense.minimize
     304                   else:
     305                      results.problem.sense = ProblemSense.maximize
     306                   soln.objective['f']=tokens[3]
     307                   if soln.status is SolutionStatus.optimal:
     308                      if tokens[4] == "(MINimum)":
     309                           results.problem.lower_bound = soln.objective['f'].value
     310                           if "upper_bound" in dir(results.problem):
     311                               del results.problem.upper_bound
     312                      else:
     313                           results.problem.upper_bound = soln.objective['f'].value
     314                           if "lower_bound" in dir(results.problem):
     315                               del results.problem.lower_bound
     316                   # the objective is the last entry in the problem section - move on to constraints.
     317                   state = 1
     318
     319             elif state == 1:
     320                #
     321                # Process Constraint Info
     322                #
     323
     324                if (len(tokens) == 2) and (len(active_constraint_name) == 0):
     325
    395326                   number_of_constraints_read = number_of_constraints_read + 1
     327                   active_constraint_name = tokens[1].strip()
     328                   index = eval(tokens[0].strip())
     329
    396330                   # sanity check - the indices should be in sequence.
    397331                   if index != number_of_constraints_read:
    398332                      raise ValueError,"***ERROR: Unexpected constraint index encountered on line="+line+"; expected value="+str(number_of_constraints_read)+"; actual value="+str(index)
    399333
    400                 if lp_solution is True:
    401                    # GLPK doesn't report slacks directly.
    402                    constraint_dual = activity
    403                    if state_string == "B":
    404                       constraint_dual = 0.0
    405                    elif (state_string == "NS") or (state_string == "NL") or (state_string == "NU"):
    406                       constraint_dual = marginal
    407                    else:
    408                       raise ValueError, "Unknown status="+tokens[0]+" encountered for constraint="+active_constraint_name+" in line="+line+" of solution file="+self.soln_file
    409 
    410                    if extract_duals is True:
    411                       soln.constraint[constraint_name].dual = constraint_dual
    412 
    413334                else:
    414                    # there isn't anything interesting to do with constraints in the MIP case.
    415                    pass
    416 
    417                 # if all of the constraints have been read, exit.
    418                 if number_of_constraints_read == results.problem.number_of_constraints:
    419                    state = 2
    420 
    421           elif state == 2:
    422              #
    423              # Process Variable Info
    424              #
    425 
    426              if (len(tokens) == 2) and (len(active_variable_name) == 0):
    427 
    428                 # in the case of name over-flow, there are only two tokens
    429                 # on the first of two lines for the variable entry.
    430                 number_of_variables_read = number_of_variables_read + 1
    431                 active_variable_name = tokens[1].strip()
    432                 index = eval(tokens[0].strip())
    433 
    434                 # sanity check - the indices should be in sequence.
    435                 if index != number_of_variables_read:
    436                    raise ValueError,"***ERROR: Unexpected variable index encountered on line="+line+"; expected value="+str(number_of_variables_read)+"; actual value="+str(index)
    437 
    438              else:
    439 
    440                 index = None
    441                 activity = None
    442                 lower_bound = None
    443                 upper_bound = None
    444                 marginal = None
    445 
    446                 # extract the field names and process accordingly. there
    447                 # is some wasted processing w.r.t. single versus double-line
    448                 # entries, but it's not significant enough to worry about.
    449 
    450                 index_string = line[0:6].strip()
    451                 name_string = line[7:19].strip()
    452                 activity_string = line[23:36].strip()
    453                 lower_bound_string = line[37:50].strip()
    454                 upper_bound_string = line[51:64].strip()
    455 
    456                 state_string = None
    457                 marginal_string = None
    458 
    459                 # skip any headers
    460                 if (index_string == "------") or (index_string == "No."):
    461                    continue
    462 
    463                 if len(index_string) > 0:
    464                    index = eval(index_string)
    465 
    466                 if lp_solution is True:
    467                    state_string = line[20:22].strip()
    468                    marginal_string = line[65:78].strip()
    469 
    470                    if (activity_string != "< eps") and (len(activity_string) > 0):
    471                       activity = eval(activity_string)
    472                    else:
    473                       activity = 0.0
    474                    if (lower_bound_string != "< eps") and (len(lower_bound_string) > 0):
    475                       lower_bound = eval(lower_bound_string)
    476                    else:
    477                       lower_bound = 0.0
    478                    if state_string != "NS":
    479                       if (upper_bound_string != "< eps") and (len(upper_bound_string) > 0):
    480                          upper_bound = eval(upper_bound_string)
    481                       else:
    482                          upper_bound = 0.0
    483                    if (marginal_string != "< eps") and (len(marginal_string) > 0):
    484                       marginal = eval(marginal_string)
    485                    else:
    486                       marginal = 0.0
    487 
    488                 else:
    489 
    490                    if (activity_string != "< eps") and (len(activity_string) > 0):
    491                       activity = eval(activity_string)
    492                    else:
    493                       activity = 0.0
    494 
    495                 variable_name = None
    496                 if len(active_variable_name) > 0:
    497                    # if there is an active variable name, the identifier was
    498                    # too long for everything to be on a single line; the second
    499                    # line contains all of the value information.
    500                    variable_name = active_variable_name
    501                    active_variable_name = ""
    502                 else:
    503                    # everything is on a single line.
    504                    variable_name = name_string
     335
     336                   index = None
     337                   activity = None
     338                   lower_bound = None
     339                   upper_bound = None
     340                   marginal = None
     341
     342                   # extract the field names and process accordingly. there
     343                   # is some wasted processing w.r.t. single versus double-line
     344                   # entries, but it's not significant enough to worry about.
     345
     346                   index_string = line[0:6].strip()
     347                   name_string = line[7:19].strip()
     348                   activity_string = line[23:36].strip()
     349                   lower_bound_string = line[37:50].strip()
     350                   upper_bound_string = line[51:64].strip()
     351
     352                   state_string = None
     353                   marginal_string = None
     354
     355                   # skip any headers
     356                   if (index_string == "------") or (index_string == "No."):
     357                      continue
     358
     359                   if len(index_string) > 0:
     360                      index = eval(index_string)
     361
     362                   if lp_solution is True:
     363                      state_string = line[20:22].strip()
     364                      marginal_string = line[65:78].strip()
     365                      if (activity_string != "< eps") and (len(activity_string) > 0):
     366                         activity = eval(activity_string)
     367                      else:
     368                         activity = 0.0
     369                      if (lower_bound_string != "< eps") and (len(lower_bound_string) > 0):
     370                         lower_bound = eval(lower_bound_string)
     371                      else:
     372                         lower_bound = 0.0
     373                      if state_string != "NS":
     374                         if (upper_bound_string != "< eps") and (len(upper_bound_string) > 0):
     375                            upper_bound = eval(upper_bound_string)
     376                         else:
     377                            upper_bound = 0.0
     378                      if (marginal_string != "< eps") and (len(marginal_string) > 0):
     379                         marginal = eval(marginal_string)
     380                      else:
     381                         marginal = 0.0
     382
     383                   else:
     384                       # no constraint-related attributes/values are extracted currently for MIPs.
     385                       pass
     386
     387                   constraint_name = None
     388                   if len(active_constraint_name) > 0:
     389                      # if there is an active constraint name, the identifier was
     390                      # too long for everything to be on a single line; the second
     391                      # line contains all of the value information.
     392                      constraint_name = active_constraint_name
     393                      active_constraint_name = ""
     394                   else:
     395                      # everything is on a single line.
     396                      constraint_name = name_string
     397                      number_of_constraints_read = number_of_constraints_read + 1
     398                      # sanity check - the indices should be in sequence.
     399                      if index != number_of_constraints_read:
     400                         raise ValueError,"***ERROR: Unexpected constraint index encountered on line="+line+"; expected value="+str(number_of_constraints_read)+"; actual value="+str(index)
     401
     402                   if lp_solution is True:
     403                      # GLPK doesn't report slacks directly.
     404                      constraint_dual = activity
     405                      if state_string == "B":
     406                         constraint_dual = 0.0
     407                      elif (state_string == "NS") or (state_string == "NL") or (state_string == "NU"):
     408                         constraint_dual = marginal
     409                      else:
     410                         raise ValueError, "Unknown status="+tokens[0]+" encountered for constraint="+active_constraint_name+" in line="+line+" of solution file="+self.soln_file
     411
     412                      if extract_duals is True:
     413                         soln.constraint[constraint_name].dual = constraint_dual
     414
     415                   else:
     416                      # there isn't anything interesting to do with constraints in the MIP case.
     417                      pass
     418
     419                   # if all of the constraints have been read, exit.
     420                   if number_of_constraints_read == results.problem.number_of_constraints:
     421                      state = 2
     422
     423             elif state == 2:
     424                #
     425                # Process Variable Info
     426                #
     427
     428                if (len(tokens) == 2) and (len(active_variable_name) == 0):
     429
     430                   # in the case of name over-flow, there are only two tokens
     431                   # on the first of two lines for the variable entry.
    505432                   number_of_variables_read = number_of_variables_read + 1
     433                   active_variable_name = tokens[1].strip()
     434                   index = eval(tokens[0].strip())
     435
    506436                   # sanity check - the indices should be in sequence.
    507437                   if index != number_of_variables_read:
    508438                      raise ValueError,"***ERROR: Unexpected variable index encountered on line="+line+"; expected value="+str(number_of_variables_read)+"; actual value="+str(index)
    509439
    510                 if lp_solution is True:
    511                    # the "activity" column always specifies the variable value.
    512                    # embedding the if-then-else to validate the basis status.
    513                    # we are currently ignoring all bound-related information.
    514                    variable_value = None
    515                    if state_string == "B":
    516                       variable_value = activity
    517                    elif (state_string == "NL") or (state_string == "NS") or (state_string == "NU"):
    518                       variable_value = activity
    519                    else:
    520                       raise ValueError, "Unknown status="+state_string+" encountered for variable="+active_variable_name+" in line="+line+" of solution file="+self.soln_file
    521 
    522                    soln.variable[variable_name].value = variable_value
    523440                else:
    524                    soln.variable[variable_name].value = activity
    525 
    526              # if all of the variables have been read, exit.
    527              if number_of_variables_read == results.problem.number_of_variables:
    528                 state = -1
    529 
    530           if state==-1:
    531              break
    532 
    533         INPUT.close()
    534 
     441
     442                   index = None
     443                   activity = None
     444                   lower_bound = None
     445                   upper_bound = None
     446                   marginal = None
     447
     448                   # extract the field names and process accordingly. there
     449                   # is some wasted processing w.r.t. single versus double-line
     450                   # entries, but it's not significant enough to worry about.
     451
     452                   index_string = line[0:6].strip()
     453                   name_string = line[7:19].strip()
     454                   activity_string = line[23:36].strip()
     455                   lower_bound_string = line[37:50].strip()
     456                   upper_bound_string = line[51:64].strip()
     457
     458                   state_string = None
     459                   marginal_string = None
     460
     461                   # skip any headers
     462                   if (index_string == "------") or (index_string == "No."):
     463                      continue
     464
     465                   if len(index_string) > 0:
     466                      index = eval(index_string)
     467
     468                   if lp_solution is True:
     469                      state_string = line[20:22].strip()
     470                      marginal_string = line[65:78].strip()
     471
     472                      if (activity_string != "< eps") and (len(activity_string) > 0):
     473                         activity = eval(activity_string)
     474                      else:
     475                         activity = 0.0
     476                      if (lower_bound_string != "< eps") and (len(lower_bound_string) > 0):
     477                         lower_bound = eval(lower_bound_string)
     478                      else:
     479                         lower_bound = 0.0
     480                      if state_string != "NS":
     481                         if (upper_bound_string != "< eps") and (len(upper_bound_string) > 0):
     482                            upper_bound = eval(upper_bound_string)
     483                         else:
     484                            upper_bound = 0.0
     485                      if (marginal_string != "< eps") and (len(marginal_string) > 0):
     486                         marginal = eval(marginal_string)
     487                      else:
     488                         marginal = 0.0
     489
     490                   else:
     491
     492                      if (activity_string != "< eps") and (len(activity_string) > 0):
     493                         activity = eval(activity_string)
     494                      else:
     495                         activity = 0.0
     496
     497                   variable_name = None
     498                   if len(active_variable_name) > 0:
     499                      # if there is an active variable name, the identifier was
     500                      # too long for everything to be on a single line; the second
     501                      # line contains all of the value information.
     502                      variable_name = active_variable_name
     503                      active_variable_name = ""
     504                   else:
     505                      # everything is on a single line.
     506                      variable_name = name_string
     507                      number_of_variables_read = number_of_variables_read + 1
     508                      # sanity check - the indices should be in sequence.
     509                      if index != number_of_variables_read:
     510                         raise ValueError,"***ERROR: Unexpected variable index encountered on line="+line+"; expected value="+str(number_of_variables_read)+"; actual value="+str(index)
     511
     512                   if lp_solution is True:
     513                      # the "activity" column always specifies the variable value.
     514                      # embedding the if-then-else to validate the basis status.
     515                      # we are currently ignoring all bound-related information.
     516                      variable_value = None
     517                      if state_string == "B":
     518                         variable_value = activity
     519                      elif (state_string == "NL") or (state_string == "NS") or (state_string == "NU"):
     520                         variable_value = activity
     521                      else:
     522                         raise ValueError, "Unknown status="+state_string+" encountered for variable="+active_variable_name+" in line="+line+" of solution file="+self.soln_file
     523
     524                      soln.variable[variable_name].value = variable_value
     525                   else:
     526                      soln.variable[variable_name].value = activity
     527
     528                # if all of the variables have been read, exit.
     529                if number_of_variables_read == results.problem.number_of_variables:
     530                   state = -1
     531
     532             if state==-1:
     533                break
     534
     535           INPUT.close()           
     536
     537        except ValueError:
     538           INPUT.close()
     539           raise RuntimeError
     540        except Exception:
     541           INPUT.close()           
     542           raise RuntimeError, "Unexpected input encountered in GLPK solution file, yielding parse failure; line="+str(line)
    535543
    536544class MockGLPK(GLPK,mockmip.MockMIP):
Note: See TracChangeset for help on using the changeset viewer.