Repository: Pyomo/pyomo

Issue:
Deprecate the PICO interface
Include the solver interface, tests, etc.

PICO is not currently being supported, and we don't test it on any platforms.


Discussion:

Resolving patch:
diff --git a/pyomo/solvers/plugins/solvers/PICO.py b/pyomo/solvers/plugins/solvers/PICO.py
deleted file mode 100644
--- a/pyomo/solvers/plugins/solvers/PICO.py
+++ /dev/null
@@ -1,443 +0,0 @@
-#  ___________________________________________________________________________
-#
-#  Pyomo: Python Optimization Modeling Objects
-#  Copyright 2017 National Technology and Engineering Solutions of Sandia, LLC
-#  Under the terms of Contract DE-NA0003525 with National Technology and 
-#  Engineering Solutions of Sandia, LLC, the U.S. Government retains certain 
-#  rights in this software.
-#  This software is distributed under the 3-clause BSD License.
-#  ___________________________________________________________________________
-
-import re
-import os
-import subprocess
-
-from pyomo.common import Executable
-from pyomo.common.errors import  ApplicationError
-from pyomo.common.collections import Bunch
-from pyomo.common.tempfiles import TempfileManager
-
-from pyomo.opt.base import ProblemFormat, ResultsFormat, OptSolver
-from pyomo.opt.base.solvers import _extract_version, SolverFactory
-from pyomo.opt.results import SolverResults, SolverStatus, TerminationCondition, SolutionStatus, ProblemSense, Solution
-from pyomo.opt.solver import  SystemCallSolver
-from pyomo.solvers.mockmip import MockMIP
-
-import logging
-logger = logging.getLogger('pyomo.solvers')
-
-
-@SolverFactory.register('pico', doc='The PICO LP/MIP solver')
-class PICO(OptSolver):
-    """The PICO LP/MIP solver
-    """
-
-    def __new__(cls, *args, **kwds):
-        try:
-            mode = kwds['solver_io']
-            if mode is None:
-                mode = 'lp'
-            del kwds['solver_io']
-        except KeyError:
-            mode = 'lp'
-        #
-        if mode  == 'lp':
-            opt = SolverFactory('_pico_shell', **kwds)
-            opt.set_problem_format(ProblemFormat.cpxlp)
-            return opt
-        # PICO's MPS parser seems too buggy to expose
-        # this option
-#        if mode  == 'mps':
-#            opt = SolverFactory('_pico_shell', **kwds)
-#            opt.set_problem_format(ProblemFormat.mps)
-#            return opt
-        #
-        if mode == 'nl':
-            # PICO does not accept all asl style command line options
-            # (-s in particular, which is required for streaming output of
-            # all asl solvers). Therefore we need to send it through the cbc_shell
-            # instead of ASL
-            opt = SolverFactory('_pico_shell',**kwds)
-            opt.set_problem_format(ProblemFormat.nl)
-        elif mode == 'os':
-            opt = SolverFactory('_ossolver', **kwds)
-        else:
-            logger.error('Unknown IO type: %s' % mode)
-            return
-        opt.set_options('solver=PICO')
-        return opt
-
-
-@SolverFactory.register('_pico_shell', doc='Shell interface to the PICO MIP solver')
-class PICOSHELL(SystemCallSolver):
-    """Shell interface to the PICO LP/MIP solver
-    """
-
-    def __init__(self, **kwds):
-        #
-        # Call base constructor
-        #
-        kwds["type"] = "pico"
-        SystemCallSolver.__init__(self, **kwds)
-        #
-        # Setup valid problem formats, and valid results for each problem format
-        #
-        self._valid_problem_formats=[ProblemFormat.cpxlp, ProblemFormat.nl, ProblemFormat.mps]
-        self._valid_result_formats = {}
-        self._valid_result_formats[ProblemFormat.cpxlp] = [ResultsFormat.soln]
-        self._valid_result_formats[ProblemFormat.nl] = [ResultsFormat.sol]
-        self._valid_result_formats[ProblemFormat.mps] = [ResultsFormat.soln]
-        self.set_problem_format(ProblemFormat.cpxlp)
-
-        # Note: Undefined capabilities default to 'None'
-        self._capabilities = Bunch()
-        self._capabilities.linear = True
-        self._capabilities.integer = True
-        #self._capabilities.sos1 = True
-        #self._capabilities.sos2 = True
-
-    #
-    # Override the base class method so we can update
-    #  _default_variable_value
-    #
-    def set_problem_format(self, format):
-        #
-        # The NL file interface for PICO does not return sparse
-        # results
-        #
-        if format == ProblemFormat.nl:
-            self._default_variable_value = None
-        else:
-            self._default_variable_value = 0.0
-        super(PICOSHELL, self).set_problem_format(format)
-
-    def _default_results_format(self, prob_format):
-        if prob_format == ProblemFormat.nl:
-            return ResultsFormat.sol
-        return ResultsFormat.soln
-
-    def _default_executable(self):
-        executable = Executable("PICO_deprecated_not_supported")
-        if not executable:
-            logger.warning("Could not locate the 'PICO' executable, "
-                           "which is required for solver %s" % self.name)
-            self.enable = False
-            return None
-        return executable.path()
-
-    def _get_version(self):
-        """
-        Returns a tuple describing the solver executable version.
-        """
-        solver_exec = self.executable()
-        if solver_exec is None:
-            return _extract_version('')
-        results = subprocess.run([solver_exec, "--version"], timeout=1,
-                                 stdout=subprocess.PIPE,
-                                 stderr=subprocess.STDOUT,
-                                 universal_newlines=True)
-        # 'PICO --version' seems to print 'pebble <version>, PICO <version>
-        # we don't wan't the pebble version being advertised so we split
-        # the string at the comma before extracting a version number. It
-        # also exits with a nonzero return code so don't bother checking it.
-        return _extract_version(results.stdout.split(',')[1])
-
-    # Nothing needs to be done here
-    #def _presolve(self, *args, **kwds):
-    #    # let the base class handle any remaining keywords/actions.
-    #    SystemCallSolver._presolve(self, *args, **kwds)
-
-    def create_command_line(self,executable,problem_files):
-        #
-        # Define log file
-        #
-        if self._log_file is None:
-            self._log_file = TempfileManager.\
-                            create_tempfile(suffix="PICO.log")
-
-        problem_filename_prefix = problem_files[0]
-        if '.' in problem_filename_prefix:
-            tmp = problem_filename_prefix.split('.')
-            if len(tmp) > 2:
-                problem_filename_prefix = '.'.join(tmp[:-1])
-            else:
-                problem_filename_prefix = tmp[0]
-        if self._results_format is ResultsFormat.sol:
-            self._soln_file = problem_filename_prefix+".sol"
-        else:
-            self._soln_file = problem_filename_prefix+".soln"
-
-        #
-        # Define results file (if the sol external parser is used)
-        #
-        if self._results_format is ResultsFormat.sol:
-            self._results_file = self._soln_file
-
-        #
-        # Eventually, these formats will be added to PICO...
-        #
-        #elif self._results_format == ResultsFormat.osrl:
-            #self._results_file = self.tmpDir+os.sep+"PICO.osrl.xml"
-
-        def _check_and_escape_options(options):
-            for key, val in self.options.items():
-                tmp_k = str(key)
-                _bad = ' ' in tmp_k
-
-                tmp_v = str(val)
-                if ' ' in tmp_v:
-                    if '"' in tmp_v:
-                        if "'" in tmp_v:
-                            _bad = True
-                        else:
-                            tmp_v = "'" + tmp_v + "'"
-                    else:
-                        tmp_v = '"' + tmp_v + '"'
-
-                if _bad:
-                    raise ValueError("Unable to properly escape solver option:"
-                                     "\n\t%s=%s" % (key, val) )
-                yield (tmp_k, tmp_v)
-
-        #
-        # Define command line
-        #
-        cmd = [ executable ]
-        if self._timer:
-            cmd.insert(0, self._timer)
-
-        if (self.options.mipgap is not None):
-            raise ValueError("The mipgap parameter is currently not being "
-                             "processed by PICO solver plugin")
-        env=os.environ.copy()
-        if self._problem_format is None \
-                or self._problem_format == ProblemFormat.nl:
-            cmd.append(problem_files[0])
-            cmd.append('-AMPL')
-
-            opts = ['allowInfiniteIntegerVarBounds=true']
-            if "debug" not in self.options:
-                opts.append("debug=2")
-            for key, val in _check_and_escape_options(self.options):
-                if key == 'solver':
-                    continue
-                opts.append("%s=%s" % ( key, tmp ))
-            opts.append('output='+self._soln_file)
-            env["PICO_options"] = " ".join(opts)
-
-        elif self._problem_format == ProblemFormat.cpxlp \
-                or self._problem_format == ProblemFormat.mps:
-            cmd.append('--allowInfiniteIntegerVarBounds=true')
-            # This option should appear in the next PICO release,
-            # assuming that ever happens
-            if self.version() > (1,3,1,0):
-                cmd.append('--reportDenseSolution=true')
-            if "debug" not in self.options:
-                cmd.extend(["--debug", "2"])
-            for key, val in _check_and_escape_options(self.options):
-                if key == 'mipgap':
-                    continue
-                cmd.extend(['--'+key, val])
-            cmd.extend(['--output', self._soln_file,
-                        problem_files[0]])
-
-        return Bunch(cmd=cmd, log_file=self._log_file, env=env)
-
-    def process_logfile(self):
-        """
-        Process a logfile
-        """
-        results = SolverResults()
-
-        #
-        # Initial values
-        #
-        #results.solver.statistics.branch_and_bound.number_of_created_subproblems=0
-        #results.solver.statistics.branch_and_bound.number_of_bounded_subproblems=0
-        soln = Solution()
-        soln.objective['__default_objective__'] = {'Value': None}
-        #
-        # Process logfile
-        #
-        OUTPUT = open(self._log_file)
-        output = "".join(OUTPUT.readlines())
-        OUTPUT.close()
-        #
-        # Parse logfile lines
-        #
-        for line in output.split("\n"):
-            tokens = re.split('[ \t]+',line.strip())
-            if len(tokens) > 3 and tokens[0] == "ABORTED:":
-                results.solver.status=SolverStatus.aborted
-            elif len(tokens) > 1 and tokens[0].startswith("ERROR"):
-                results.solver.status=SolverStatus.error
-            elif len(tokens) == 3 and tokens[0] == 'Problem' and tokens[2].startswith('infeasible'):
-                results.solver.termination_condition = TerminationCondition.infeasible
-            elif len(tokens) == 2 and tokens[0] == 'Integer' and tokens[1] == 'Infeasible':
-                results.solver.termination_condition = TerminationCondition.infeasible
-            elif len(tokens) == 5 and tokens[0] == "Final" and tokens[1] == "Solution:":
-                soln.objective['__default_objective__']['Value'] = eval(tokens[4])
-                soln.status = SolutionStatus.optimal
-            elif len(tokens) == 3 and tokens[0] == "LP" and tokens[1] == "value=":
-                soln.objective['__default_objective__']['Value'] = eval(tokens[2])
-                soln.status=SolutionStatus.optimal
-                if results.problem.sense == ProblemSense.minimize:
-                    results.problem.lower_bound = eval(tokens[2])
-                else:
-                    results.problem.upper_bound = eval(tokens[2])
-            elif len(tokens) == 2 and tokens[0] == "Bound:":
-                if results.problem.sense == ProblemSense.minimize:
-                    results.problem.lower_bound = eval(tokens[1])
-                else:
-                    results.problem.upper_bound = eval(tokens[1])
-            elif len(tokens) == 3 and tokens[0] == "Created":
-                results.solver.statistics.branch_and_bound.number_of_created_subproblems = eval(tokens[1])
-            elif len(tokens) == 3 and tokens[0] == "Bounded":
-                results.solver.statistics.branch_and_bound.number_of_bounded_subproblems = eval(tokens[1])
-            elif len(tokens) == 2 and tokens[0] == "sys":
-                results.solver.system_time=eval(tokens[1])
-            elif len(tokens) == 2 and tokens[0] == "user":
-                results.solver.user_time=eval(tokens[1])
-            elif len(tokens) == 3 and tokens[0] == "Solving" and tokens[1] == "problem:":
-                results.problem.name = tokens[2]
-            elif len(tokens) == 4 and tokens[2] == "constraints:":
-                results.problem.number_of_constraints = eval(tokens[3])
-            elif len(tokens) == 4 and tokens[2] == "variables:":
-                results.problem.number_of_variables = eval(tokens[3])
-            elif len(tokens) == 4 and tokens[2] == "nonzeros:":
-                results.problem.number_of_nonzeros = eval(tokens[3])
-            elif len(tokens) == 3 and tokens[1] == "Sense:":
-                if tokens[2] == "minimization":
-                    results.problem.sense = ProblemSense.minimize
-                else:
-                    results.problem.sense = ProblemSense.maximize
-
-        if results.solver.status is SolverStatus.aborted:
-            soln.optimality=SolutionStatus.unsure
-        if soln.status is SolutionStatus.optimal:
-            soln.gap=0.0
-            results.problem.lower_bound = soln.objective['__default_objective__']['Value']
-            results.problem.upper_bound = soln.objective['__default_objective__']['Value']
-
-        if soln.status == SolutionStatus.optimal:
-            results.solver.termination_condition = TerminationCondition.optimal
-
-        if not results.solver.status is SolverStatus.error and \
-            results.solver.termination_condition in [TerminationCondition.unknown,
-                        #TerminationCondition.maxIterations,
-                        #TerminationCondition.minFunctionValue,
-                        #TerminationCondition.minStepLength,
-                        TerminationCondition.globallyOptimal,
-                        TerminationCondition.locallyOptimal,
-                        TerminationCondition.optimal,
-                        #TerminationCondition.maxEvaluations,
-                        TerminationCondition.other]:
-                results.solution.insert(soln)
-        return results
-
-
-    def process_soln_file(self,results):
-
-        if self._results_format is ResultsFormat.sol:
-            return
-
-        # the only suffixes that we extract from PICO are
-        # constraint duals. scan through the solver suffix
-        # list and throw an exception if the user has
-        # specified any others.
-        extract_duals = False
-        for suffix in self._suffixes:
-            if re.match(suffix,"dual"):
-                extract_duals = True
-            else:
-                raise RuntimeError("***PICO solver plugin cannot extract solution suffix="+suffix)
-
-        #if os.path.exists(self.sol_file):
-            #results_reader = ReaderFactory(ResultsFormat.sol)
-            #results = results_reader(self.sol_file, results, results.solution(0))
-            #return
-
-        if not os.path.exists(self._soln_file):
-            return
-        if len(results.solution) == 0:
-            return
-        soln = results.solution(0)
-        results.problem.num_objectives=1
-        tmp=[]
-        flag=False
-        INPUT = open(self._soln_file, "r")
-        lp_flag=None
-        var_flag=True
-        for line in INPUT:
-            tokens = re.split('[ \t]+',line.strip())
-            if len(tokens) == 0 or (len(tokens) == 1 and tokens[0]==''):
-                continue
-            if tokens[0] == "Objective":
-                continue
-            if lp_flag is None:
-                lp_flag = (tokens[0] == "LP")
-                continue
-            if tokens[0] == "Dual" and tokens[1] == "solution:":
-                var_flag=False
-                # It looks like we've just been processing primal
-                # variables.
-                for (var,val) in tmp:
-                    if var == 'ONE_VAR_CONSTANT':
-                        continue
-                    soln.variable[var] = {"Value" : val}
-                tmp=[]
-                continue
-            if len(tokens) < 3:
-                print("ERROR", line,tokens)
-            tmp.append( (tokens[0],eval(tokens[2])) )
-        if var_flag:
-            for (var,val) in tmp:
-                if var == 'ONE_VAR_CONSTANT':
-                    continue
-                soln.variable[var] = {"Value" : val}
-        else:
-            range_duals = {}
-            soln_constraints = soln.constraint
-            if (lp_flag is True) and (extract_duals is True):
-                for (var,val) in tmp:
-                    if var.startswith('c_'):
-                        soln_constraints[var] = {"Dual" : val}
-                    elif var.startswith('r_l_'):
-                        range_duals.setdefault(var[4:],[0,0])[0] = val
-                    elif var.startswith('r_u_'):
-                        range_duals.setdefault(var[4:],[0,0])[1] = val
-            # For the range constraints, supply only the dual with the largest
-            # magnitude (at least one should always be numerically zero)
-            for key,(ld,ud) in range_duals.items():
-                if abs(ld) > abs(ud):
-                    soln_constraints['r_l_'+key] = {"Dual" : ld}
-                else:
-                    soln_constraints['r_l_'+key] = {"Dual" : ud}        # Use the same key
-        INPUT.close()
-
-
-@SolverFactory.register('_mock_pico')
-class MockPICO(PICOSHELL,MockMIP):
-    """A Mock PICO solver used for testing
-    """
-
-    def __init__(self, **kwds):
-        try:
-            PICOSHELL.__init__(self,**kwds)
-        except ApplicationError: #pragma:nocover
-            pass                        #pragma:nocover
-        MockMIP.__init__(self,"pico")
-
-    def available(self, exception_flag=True):
-        return PICOSHELL.available(self,exception_flag)
-
-    def create_command_line(self,executable,problem_files):
-        command = PICOSHELL.create_command_line(self,executable,problem_files)
-        MockMIP.create_command_line(self,executable,problem_files)
-        return command
-
-    def _default_executable(self):
-        return MockMIP.executable(self)
-
-    def _execute_command(self,cmd):
-        return MockMIP._execute_command(self,cmd)
diff --git a/pyomo/solvers/plugins/solvers/__init__.py b/pyomo/solvers/plugins/solvers/__init__.py
--- a/pyomo/solvers/plugins/solvers/__init__.py
+++ b/pyomo/solvers/plugins/solvers/__init__.py
@@ -9,7 +9,6 @@
 #  ___________________________________________________________________________
 
 # TODO: Disabled until we can confirm application to Pyomo models
-import pyomo.solvers.plugins.solvers.PICO
 import pyomo.solvers.plugins.solvers.CBCplugin
 import pyomo.solvers.plugins.solvers.GLPK
 import pyomo.solvers.plugins.solvers.CPLEX


Test patch:
diff --git a/pyomo/environ/tests/test_package_layout.py b/pyomo/environ/tests/test_package_layout.py
--- a/pyomo/environ/tests/test_package_layout.py
+++ b/pyomo/environ/tests/test_package_layout.py
@@ -36,7 +36,6 @@
     join('solvers', 'tests', 'mip', 'cbc'),
     join('solvers', 'tests', 'mip', 'cplex'),
     join('solvers', 'tests', 'mip', 'glpk'),
-    join('solvers', 'tests', 'mip', 'pico'),
     join('solvers', 'tests', 'piecewise_linear', 'baselines'),
     join('solvers', 'tests', 'piecewise_linear', 'kernel_baselines'),
     join('solvers', 'tests', 'piecewise_linear', 'kernel_problems'),
diff --git a/pyomo/solvers/tests/mip/pico/test1.out b/pyomo/solvers/tests/mip/pico/test1.out
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/pico/test1.out
+++ /dev/null
@@ -1,21 +0,0 @@
-Solver command line: /home/wehart/src/acro-utilib/bin/timer /Net/proj/opt/bin/LINUX/PICO --debug 2  --output /home/wehart/src/acro-utilib/packages/pyomo/test/plugins/mip/tmpGaCZjAtest1.soln /home/wehart/src/acro-utilib/packages/pyomo/test/plugins/mip/test1.mps
-
-Solving problem: TINYMATCHING
-Optimization Sense: minimization
-MILPProblem Statistics
-Number of variables:                 9
-Number of binary variables:          0
-Number of integer variables:         0
-Number of general integer variables: 0
-Number of special ordered sets:      0
-Number of constraints:               3
-Number of nonzeros:                  9
-No integer variables... solving the LP normally.
-	LP value= 3
-	CPU RunTime=         0.001
-	CPU TotalTime=       0.010998
-	WallClock TotalTime= 0.0103740660707
-real	        0.02
-user	        0.01
-sys	        0.00
-
diff --git a/pyomo/solvers/tests/mip/pico/test1.soln b/pyomo/solvers/tests/mip/pico/test1.soln
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/pico/test1.soln
+++ /dev/null
@@ -1,9 +0,0 @@
-LP solution:
-Objective value = 3
-X11 = 1
-X23 = 1
-X32 = 1
-Dual solution:
-NODEA1 = 1
-NODEA2 = 1
-NODEA3 = 1
diff --git a/pyomo/solvers/tests/mip/pico/test2.out b/pyomo/solvers/tests/mip/pico/test2.out
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/pico/test2.out
+++ /dev/null
@@ -1,73 +0,0 @@
-Solver command line: /home/wehart/src/acro-utilib/bin/timer mock --debug 2  --output /home/wehart/src/acro-utilib/packages/pyomo/test/plugins/mip/tmpMjAyT9test2.soln /home/wehart/src/acro-utilib/packages/pyomo/test/plugins/mip/test2.lp
-
-Solver Commandline: /home/wehart/src/acro-pico/bin/timer /home/wehart/src/acro-pico/bin/PICO --debug 2  --output /home/wehart/src/acro-pico/packages/pyomo/test/opt/mip//pico.soln /home/wehart/src/acro-pico/packages/pyomo/test/opt/mip/test2.lp
-
-glp_read_lp: reading problem data from `/home/wehart/src/acro-pico/packages/pyomo/test/opt/mip/test2.lp'...
-glp_read_lp: 2 rows, 2 columns, 4 non-zeros
-glp_read_lp: 2 integer columns, all of which are binary
-glp_read_lp: 8 lines were read
-MILPProblem Statistics
-Number of variables:                 2
-Number of binary variables:          2
-Number of integer variables:         2
-Number of general integer variables: 0
-Number of special ordered sets:      0
-Number of constraints:               2
-Number of nonzeros:                  4
-Initialized MILP, absTolerance = 0.99999
-Solving problem: test2
-Optimization Sense: maximization
-Clp3002W Empty problem - 0 rows, 0 columns and 0 elements
-***** Randomized rounding found solution 2 *****
-
-Final Solution:  Value = 2
-
-Subproblems
------------
-Created            1  100.0%
-Started Bounding   1  100.0%
-Bounded            1  100.0%
-Started Splitting  0    0.0%
-Split              0    0.0%
-Dead               0    0.0%
-CPU RunTime=         0.0 seconds
-CPU TotalTime=       0.0 seconds
-WallClock TotalTime= 0.0 seconds
-
-MIP Heuristic Usage Summary:
-
-Heuristic           Tried Success Seconds   %Run  %Heur
-------------------- ----- ------- ------- ------ ------
-Randomized rounding     1       1     0.0   0.0%   0.0%
-Dive and cut            0       0     0.0   0.0%   0.0%
-Feasibility pump        0       0     0.0   0.0%   0.0%
-Eckstein-Nediak         0       0     0.0   0.0%   0.0%
-                    ----- ------- ------- ------ ------
-Total                   1       1     0.0   0.0% 100.0%
-
-
-Cut Generator Summary:
-Generator                 # calls  # cuts  # infeas  gain  time (sec)  avg quality
------------------------- -------- ------- --------- ----- ----------- ------------ 
-cutPool                         0       0         0  0.00        0.00            
-CglProbing                      0       0         0  0.00        0.00            
-CglMixedIntegerRounding2        0       0         0  0.00        0.00            
-CglTwomir                       0       0         0  0.00        0.00            
-CglFlowCover                    0       0         0  0.00        0.00            
-CglClique                       0       0         0  0.00        0.00            
-CglGomory                       0       0         0  0.00        0.00            
-
-Running Time breakdown:
-Preprocessing        0.00   0.0%
-Pseudocost Init      0.00   0.0%
-MIP heuristic runs   0.00   0.0%
-1st subprob bounding 0.00   0.0%
-Resolves             0.00   0.0%
-Cut generation       0.00   0.0%
-Some cut overhead    0.00   0.0%
-Other                0.01 100.0%
-real	        0.01
-user	        0.01
-sys	        0.00
-
-
diff --git a/pyomo/solvers/tests/mip/pico/test2.soln b/pyomo/solvers/tests/mip/pico/test2.soln
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/pico/test2.soln
+++ /dev/null
@@ -1,3 +0,0 @@
-MILP solution:
-Objective value = 2
-x2 = 1
diff --git a/pyomo/solvers/tests/mip/pico/test4.out b/pyomo/solvers/tests/mip/pico/test4.out
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/pico/test4.out
+++ /dev/null
@@ -1,25 +0,0 @@
-Solver command line: /home/wehart/src/acro-utilib/bin/timer /Net/proj/opt/bin/LINUX/PICO /home/wehart/src/acro-utilib/packages/pyomo/test/plugins/mip/test4.nl -AMPL
-
-PICO Solver Library 1.0: 
-Processing AMPL Options
-  OPTION: debug 2
-
-Solving problem: test4
-Optimization Sense: minimization
-MILPProblem Statistics
-Number of variables:                 8
-Number of binary variables:          0
-Number of integer variables:         0
-Number of general integer variables: 0
-Number of special ordered sets:      0
-Number of constraints:               8
-Number of nonzeros:                  62
-No integer variables... solving the LP normally.
-	LP value= 88.578
-	CPU RunTime=         0.001
-	CPU TotalTime=       0.009999
-	WallClock TotalTime= 0.00989413261414
-real	        0.02
-user	        0.01
-sys	        0.00
-
diff --git a/pyomo/solvers/tests/mip/pico/test4.sol b/pyomo/solvers/tests/mip/pico/test4.sol
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/pico/test4.sol
+++ /dev/null
@@ -1,23 +0,0 @@
-PICO Solver: final f = 88.578000
-
-Options
-3
-0
-1
-0
-4
-4
-8
-8
-0
-0
-0.126
-0
-0
-0
-0
-0
-46.866666666666674
-0
-0
-0
diff --git a/pyomo/solvers/tests/mip/pico/test4.soln b/pyomo/solvers/tests/mip/pico/test4.soln
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/pico/test4.soln
+++ /dev/null
@@ -1,3 +0,0 @@
-MILP solution:
-Objective value = 2
-x2 = 1
diff --git a/pyomo/solvers/tests/mip/test1_pico.txt b/pyomo/solvers/tests/mip/test1_pico.txt
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/test1_pico.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# ==========================================================
-# = Solver Results                                         =
-# ==========================================================
-
-# ----------------------------------------------------------
-#   Problem Information
-# ----------------------------------------------------------
-Problem: 
-- Name: TINYMATCHING
-  Lower bound: 3
-  Upper bound: 3
-  Number of objectives: 1
-  Number of constraints: 3
-  Number of variables: 9
-  Number of nonzeros: 9
-  Sense: minimize
-  Num objectives: 1
-
-# ----------------------------------------------------------
-#   Solver Information
-# ----------------------------------------------------------
-Solver: 
-- Status: ok
-  Termination condition: optimal
-  Error rc: 0
-
-# ----------------------------------------------------------
-#   Solution Information
-# ----------------------------------------------------------
-Solution: 
-- number of solutions: 1
-  number of solutions displayed: 1
-- Gap: 0.0
-  Status: optimal
-  Objective: 
-    __default_objective__: 
-      Value: 3
-  Variable: 
-    X11: 
-      Value: 1
-    X23: 
-      Value: 1
-    X32: 
-      Value: 1
diff --git a/pyomo/solvers/tests/mip/test2_pico.txt b/pyomo/solvers/tests/mip/test2_pico.txt
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/test2_pico.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-# ==========================================================
-# = Solver Results                                         =
-# ==========================================================
-
-# ----------------------------------------------------------
-#   Problem Information
-# ----------------------------------------------------------
-Problem: 
-- Name: test2
-  Lower bound: 2
-  Upper bound: 2
-  Number of objectives: 1
-  Number of constraints: 2
-  Number of variables: 2
-  Number of nonzeros: 4
-  Sense: maximize
-  Num objectives: 1
-
-# ----------------------------------------------------------
-#   Solver Information
-# ----------------------------------------------------------
-Solver: 
-- Status: ok
-  Termination condition: optimal
-  Statistics: 
-    Branch and bound: 
-      Number of bounded subproblems: 1
-      Number of created subproblems: 1
-  Error rc: 0
-
-# ----------------------------------------------------------
-#   Solution Information
-# ----------------------------------------------------------
-Solution: 
-- number of solutions: 1
-  number of solutions displayed: 1
-- Gap: 0.0
-  Status: optimal
-  Objective: 
-    __default_objective__: 
-      Value: 2
-  Variable: 
-    x2: 
-      Value: 1
diff --git a/pyomo/solvers/tests/mip/test4_pico.txt b/pyomo/solvers/tests/mip/test4_pico.txt
deleted file mode 100644
--- a/pyomo/solvers/tests/mip/test4_pico.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-# ==========================================================
-# = Solver Results                                         =
-# ==========================================================
-
-# ----------------------------------------------------------
-#   Problem Information
-# ----------------------------------------------------------
-Problem: 
-- Name: test4
-  Lower bound: 88.578
-  Upper bound: 88.578
-  Number of objectives: 1
-  Number of constraints: 8
-  Number of variables: 8
-  Number of nonzeros: 62
-  Sense: minimize
-
-# ----------------------------------------------------------
-#   Solver Information
-# ----------------------------------------------------------
-Solver: 
-- Status: ok
-  Message: PICO Solver\x3a final f = 88.578000
-  Termination condition: optimal
-  Error rc: 0
-
-# ----------------------------------------------------------
-#   Solution Information
-# ----------------------------------------------------------
-Solution: 
-- number of solutions: 1
-  number of solutions displayed: 1
-- Gap: 0.0
-  Status: optimal
-  Objective: 
-    __default_objective__: 
-      Value: 88.578
-  Variable: 
-    v4: 
-      Value: 46.8666666667
diff --git a/pyomo/solvers/tests/mip/test_factory.py b/pyomo/solvers/tests/mip/test_factory.py
--- a/pyomo/solvers/tests/mip/test_factory.py
+++ b/pyomo/solvers/tests/mip/test_factory.py
@@ -20,8 +20,7 @@
                        SolverFactory, WriterFactory)
 from pyomo.opt.base.solvers import UnknownSolver
 from pyomo.opt.plugins.sol import ResultsReader_sol
-from pyomo.solvers.plugins.solvers import PICO
-
+from pyomo.solvers.plugins.solvers.CBCplugin import MockCBC
 
 class MockWriter(AbstractProblemWriter):
 
@@ -60,7 +59,7 @@ def test_solver_factory(self):
         """
         SolverFactory.register('stest3')(MockSolver)
         ans = sorted(SolverFactory)
-        tmp = ['_mock_asl', '_mock_cbc', '_mock_cplex', '_mock_glpk', '_mock_pico', 'cbc', 'cplex', 'glpk', 'pico', 'stest3', 'asl']
+        tmp = ['_mock_asl', '_mock_cbc', '_mock_cplex', '_mock_glpk', 'cbc', 'cplex', 'glpk', 'stest3', 'asl']
         tmp.sort()
         self.assertTrue(set(tmp) <= set(ans), msg="Set %s is not a subset of set %s" %(tmp,ans))
 
@@ -70,10 +69,10 @@ def test_solver_instance(self):
         """
         ans = SolverFactory("none")
         self.assertTrue(isinstance(ans, UnknownSolver))
-        ans = SolverFactory("_mock_pico")
-        self.assertEqual(type(ans), PICO.MockPICO)
-        ans = SolverFactory("_mock_pico", name="mymock")
-        self.assertEqual(type(ans), PICO.MockPICO)
+        ans = SolverFactory("_mock_cbc")
+        self.assertEqual(type(ans), MockCBC)
+        ans = SolverFactory("_mock_cbc", name="mymock")
+        self.assertEqual(type(ans), MockCBC)
         self.assertEqual(ans.name,  "mymock")
 
     def test_solver_registration(self):
@@ -84,7 +83,7 @@ def test_solver_registration(self):
         self.assertTrue('stest3' not in SolverFactory)
         SolverFactory.register('stest3')(MockSolver)
         self.assertTrue('stest3' in SolverFactory)
-        self.assertTrue('_mock_pico' in SolverFactory)
+        self.assertTrue('_mock_cbc' in SolverFactory)
 
     def test_writer_factory(self):
         """
diff --git a/pyomo/solvers/tests/mip/test_mip.yml b/pyomo/solvers/tests/mip/test_mip.yml
--- a/pyomo/solvers/tests/mip/test_mip.yml
+++ b/pyomo/solvers/tests/mip/test_mip.yml
@@ -8,13 +8,11 @@ solvers:
     _mock_glpk:
     cplex:
     _mock_cplex:
-    pico:
-    _mock_pico:
     cbc:
     _mock_cbc:
-    asl_pico:
-       name: 'asl:PICO'
     gurobi:
+    asl_gurobi:
+       name: 'asl:gurobi_ampl'
 
 problems:
     test1:
@@ -75,30 +73,14 @@ suites:
                 baseline: test4_gurobi.txt
                 use_pico_convert: True
 
-    pico:
-        categories:
-            - nightly
-            - smoke
-            - pico
-        solvers:
-            pico:
-            _mock_pico:
-        problems:
-            test1:
-                baseline: test1_pico.txt
-            test2:
-                baseline: test2_pico.txt
-            test4:
-                baseline: test4_pico.txt
-                results_format: sol
     asl:
         categories:
             - asl
         solvers:
-            asl_pico:
+            asl_gurobi:
         problems:
             test4:
-                baseline: test4_pico.txt
+                baseline: test4_gurobi.txt
                 results_format: sol
 
     cbc:
diff --git a/pyomo/solvers/tests/mip/test_solver.py b/pyomo/solvers/tests/mip/test_solver.py
--- a/pyomo/solvers/tests/mip/test_solver.py
+++ b/pyomo/solvers/tests/mip/test_solver.py
@@ -17,6 +17,7 @@
 
 import pyomo.opt
 import pyomo.solvers.plugins.solvers
+from pyomo.solvers.plugins.solvers.CBCplugin import MockCBC
 
 class MockSolver2(pyomo.opt.OptSolver):
 
@@ -40,17 +41,17 @@ def test_solver_init1(self):
         """
         Verify the processing of 'type', 'name' and 'doc' options
         """
-        ans = pyomo.opt.SolverFactory("_mock_pico")
-        self.assertEqual(type(ans), pyomo.solvers.plugins.solvers.PICO.MockPICO)
-        self.assertEqual(ans._doc, "pico OptSolver")
+        ans = pyomo.opt.SolverFactory("_mock_cbc")
+        self.assertEqual(type(ans), MockCBC)
+        self.assertEqual(ans._doc, "cbc OptSolver")
 
-        ans = pyomo.opt.SolverFactory("_mock_pico", doc="My Doc")
-        self.assertEqual(type(ans), pyomo.solvers.plugins.solvers.PICO.MockPICO)
+        ans = pyomo.opt.SolverFactory("_mock_cbc", doc="My Doc")
+        self.assertEqual(type(ans), MockCBC)
         self.assertEqual(ans._doc, "My Doc")
 
-        ans = pyomo.opt.SolverFactory("_mock_pico", name="my name")
-        self.assertEqual(type(ans), pyomo.solvers.plugins.solvers.PICO.MockPICO)
-        self.assertEqual(ans._doc, "my name OptSolver (type pico)")
+        ans = pyomo.opt.SolverFactory("_mock_cbc", name="my name")
+        self.assertEqual(type(ans), MockCBC)
+        self.assertEqual(ans._doc, "my name OptSolver (type cbc)")
 
     def test_solver_init2(self):
         """
@@ -59,7 +60,7 @@ def test_solver_init2(self):
         opt = {}
         opt['a'] = 1
         opt['b'] = "two"
-        ans = pyomo.opt.SolverFactory("_mock_pico", name="solver_init2", options=opt)
+        ans = pyomo.opt.SolverFactory("_mock_cbc", name="solver_init2", options=opt)
         self.assertEqual(ans.options['a'], opt['a'])
         self.assertEqual(ans.options['b'], opt['b'])
 
diff --git a/pyomo/solvers/tests/solvers.py b/pyomo/solvers/tests/solvers.py
--- a/pyomo/solvers/tests/solvers.py
+++ b/pyomo/solvers/tests/solvers.py
@@ -281,23 +281,6 @@ def test_solver_cases(*args):
             #capabilities=_cbc_mps_capabilities,
             #import_suffixes=['dual', 'rc'])
 
-        #
-        # PICO
-        #
-        _pico_capabilities = set(['linear', 'integer'])
-
-        _test_solver_cases['pico', 'lp'] = initialize(
-            name='pico',
-            io='lp',
-            capabilities=_pico_capabilities,
-            import_suffixes=['dual'])
-
-        _test_solver_cases['pico', 'nl'] = initialize(
-            name='pico',
-            io='nl',
-            capabilities=_pico_capabilities,
-            import_suffixes=['dual'])
-
         #
         # XPRESS
         #
diff --git a/pyomo/solvers/tests/testcases.py b/pyomo/solvers/tests/testcases.py
--- a/pyomo/solvers/tests/testcases.py
+++ b/pyomo/solvers/tests/testcases.py
@@ -129,81 +129,6 @@
      "Cbc segfaults for SOS constraints in the NL interface "
      "(reported upstream as coin-or/Cbc#388)")
 
-#
-# PICO
-#
-
-ExpectedFailures['pico', 'lp', 'MILP_discrete_var_bounds'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico ignores bounds on Binary variables through the "
-    "LP file interface. A ticket has been filed.")
-
-ExpectedFailures['pico', 'nl', 'LP_piecewise'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico reports an incorrect dual solution for this "
-    "problem when using the NL file interface.")
-
-ExpectedFailures['pico', 'nl', 'LP_duals_maximize'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico classifies certain models with equality "
-    "constraints as infeasible when using the NL "
-    "file interface. A ticket has been filed.")
-
-ExpectedFailures['pico', 'nl', 'LP_duals_minimize'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico classifies certain models with equality "
-    "constraints as infeasible when using the NL "
-    "file interface. A ticket has been filed.")
-
-ExpectedFailures['pico', 'nl', 'LP_inactive_index'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico reports the wrong objective function value.")
-
-ExpectedFailures['pico', 'nl', 'LP_simple'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'nl', 'LP_compiled'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'nl', 'LP_trivial_constraints'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'nl', 'MILP_unbounded'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'lp', 'MILP_unbounded'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'nl', 'LP_unbounded'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'lp', 'LP_unbounded'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-
-ExpectedFailures['pico', 'lp', 'MILP_infeasible1'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'nl', 'MILP_infeasible1'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'nl', 'LP_infeasible1'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
-ExpectedFailures['pico', 'nl', 'LP_infeasible2'] = \
-    (lambda v: v <= _trunk_version,
-    "Pico just gets the wrong answer.")
-
 #
 # XPRESS
 #