Example #1
0
    def _create_solver_agent(self, aname):
        """ Create a new solver agent from its name.

        Args:
            name: Name of the agent
        Returns:
            Solver agent instance
        Raises:
            CpoException: Agent creation error
        """
        # Get agent context
        sctx = self.context.solver.get(aname)
        if not isinstance(sctx, Context):
            raise CpoException("Unknown solving agent '" + aname + "'. Check config.context.solver.agent parameter.")
        if sctx.is_log_enabled(3):
            sctx.log(3, "Context for solving agent '", aname, "':")
            sctx.write(out=sctx.get_log_output())
        cpath = sctx.class_name
        if cpath is None:
            raise CpoException("Solving agent '" + aname + "' context should contain an attribute 'class_name'")

        # Retrieve solver agent class
        try:
            sclass = utils.get_module_element_from_path(cpath)
        except Exception as e:
            raise CpoException("Unable to retrieve solver agent class '{}': {}".format(cpath, e))
        if not inspect.isclass(sclass):
            raise CpoException("Solver agent '{}' is not a class.".format(cpath))
        if not issubclass(sclass, CpoSolverAgent):
            raise CpoException("Solver agent class '{}' should extend CpoSolverAgent.".format(cpath))

        # Create agent instance
        agent = sclass(self, sctx.params, sctx)
        return agent
Example #2
0
    def _add_listener_from_class(self, lstnr):
        """ Add a solver listener from its class (instance is created).

        Args:
            lstnr:  Solver listener class, or string identifying the class
        """
        if is_string(lstnr):
            # Get listener class from string
            try:
                lclass = utils.get_module_element_from_path(lstnr)
            except Exception as e:
                raise CpoException("Unable to retrieve solver listener class '{}': {}".format(lstnr, e))
            if not inspect.isclass(lclass):
                raise CpoException("Solver listener '{}' is not a class.".format(lstnr))
            if not issubclass(lclass, CpoSolverListener):
                raise CpoException("Solver listener class '{}' should extend CpoSolverListener.".format(lstnr))
        else:
            # Listener is assumed to directly be a class
            lclass = lstnr
            if not inspect.isclass(lclass):
                raise CpoException("Solver listener '{}' is not a class.".format(lclass))
            if not issubclass(lclass, CpoSolverListener):
                raise CpoException("Solver listener class '{}' should extend CpoSolverListener.".format(lclass))
        # Add listener
        self.add_listener(lclass())
Example #3
0
    def _check_status(self, ests):
        """ Throws an exception if solver status is not the expected one

        Args:
            ests:  Expected status, or list of expected statuses
        Raise:
            CpoException if solver is not in the right status
        """
        if self.status != ests:
           raise CpoException("Unexpected solver status. Should be '{}' instead of '{}'".format(ests, self.status))
Example #4
0
def _get_effective_context(**kwargs):
    """ Build a effective context from a variable list of arguments that may specify changes to default.

    Args:
        context (optional):   Source context, if not default.
        params (optional):    Solving parameters (CpoParameters) that overwrite those in the solving context
        (others) (optional):  All other context parameters that can be changed.
    Returns:
        Updated (cloned) context
    """
    # If 'url' and 'key' are defined, force agent to be docloud
    if ('agent' not in kwargs) and not ENVIRONMENT_PRESENT:
        url = kwargs.get('url')
        key = kwargs.get('key')
        if url and key and is_string(url) and is_string(key) and url.startswith('http'):
            kwargs['agent'] = 'docloud'

    # Determine source context
    ctx = kwargs.get('context')
    if (ctx is None) or (ctx in DEFAULT_VALUES):
        ctx = context
    ctx = ctx.clone()
    # print("\n*** Source context");
    # ctx.print_context()

    # First set parameters if given
    prms = kwargs.get('params')
    if prms is not None:
        ctx.params.add(prms)

    # Process other changes
    rplist = []  # List of replacements to be done in solving parameters
    for k, v in kwargs.items():
        if (k != 'context') and (k != 'params') and (v not in DEFAULT_VALUES):
            rp = ctx.search_and_replace_attribute(k, v)
            # If not found, set in solving parameters
            if (rp is None):
                rplist.append((k, v))

     # Replace or set remaining fields in parameters
    if rplist:
        params = ctx.params
        chkparams = not ctx.solver.enable_undocumented_params
        if isinstance(params, CpoParameters):
            for k, v in rplist:
                if chkparams and not k in ALL_PARAMETER_NAMES:
                    raise CpoException("CPO solver does not accept a parameter named '{}'".format(k))
                setattr(params, k, v)

    # Return
    # print("\n*** Result context");
    # ctx.print_context()
    return ctx
Example #5
0
    def _get_solver_agent(self):
        """ Get the solver agent instance that is used to solve the model.

        Returns:
            Solver agent instance
        Raises:
            CpoException:  Agent creation error
        """
        # Determine selectable agent(s)
        sctx = self.context.solver

        alist = sctx.agent
        if alist is None:
            alist = 'docloud'
        elif not (is_string(alist) or is_array(alist)):
            raise CpoException("Agent identifier in config.context.solver.agent should be a string or a list of strings.")

        # Create agent
        if is_string(alist):
            aname = alist
            agent = self._create_solver_agent(alist)
        else:
            # Search first available agent in the list
            agent = None
            aname = None
            errors = []
            for aname in alist:
                try:
                    agent = self._create_solver_agent(aname)
                    break
                except Exception as e:
                    errors.append((aname, str(e)))
                # Agent not found
                errstr = ', '.join(a + ": " + str(e) for (a, e) in errors)
                raise CpoException("Agent creation error: " + errstr)

        # Log solver agent
        sctx.log(1, "Solve model '", self.model.get_name(), "' with agent '", aname, "'")
        agent.process_infos[CpoProcessInfos.SOLVER_AGENT] = aname
        return agent
Example #6
0
    def __init__(self, solver, params, context):
        """ Create a new solver using DOcplexcloud web service

        Args:
            solver:   Parent solver
            params:   Solving parameters
            context:  DOcplexcloud Solver context
        Raises:
            CpoException if jar file does not exists
        """
        if (context.key is None) or (' ' in context.key):
            raise CpoException("Your DOcplexcloud key has not been set")
        super(CpoSolverDocloud, self).__init__(solver, params, context)
    def __init__(self, solver, params, context):
        """ Create a new solver using DOcplexcloud web service

        Args:
            solver:   Parent solver
            params:   Solving parameters
            context:  DOcplexcloud Solver context
        Raises:
            CpoException if jar file does not exists
        """
        warnings.warn(
            'Solve using \'docloud\' agent is deprecated. Consider submitting your model to DOcplexcloud. See https://ibm.biz/BdYhhK',
            DeprecationWarning)
        if (context.key is None) or (' ' in context.key):
            raise CpoException("Your DOcplexcloud key has not been set")
        super(CpoSolverDocloud, self).__init__(solver, params, context)
Example #8
0
    def _create_solver_agent(self, aname):
        """ Create a new solver agent from its name.

        Args:
            name: Name of the agent
        Returns:
            Solver agent instance
        Raises:
            CpoException: Agent creation error
        """
        # Get agent context
        sctx = self.context.solver.get(aname)
        if not isinstance(sctx, Context):
            raise CpoException("Unknown solving agent '" + aname + "'. Check config.context.solver.agent parameter.")
        if sctx.is_log_enabled(3):
            sctx.log(3, "Context for solving agent '", aname, "':")
            sctx.print_context(out=sctx.get_log_output())
        cpath = sctx.class_name
        if cpath is None:
            raise CpoException("Solving agent '" + aname + "' context does not contain attribute 'class_name'")

        # Split class name
        pnx = cpath.rfind('.')
        if pnx < 0:
            raise CpoException("Invalid class name '" + cpath + "' for solving agent '" + aname + "'. Should be <package>.<module>.<class>.")
        mname = cpath[:pnx]
        cname = cpath[pnx + 1:]

        # Load module
        try:
            module = importlib.import_module(mname)
        except Exception as e:
            raise CpoException("Module '" + mname + "' import error: " + str(e))

        # Create and check class
        sclass = getattr(module, cname, None)
        if sclass is None:
            raise CpoException("Module '" + mname + "' does not contain a class '" + cname + "'")
        if not inspect.isclass(sclass):
            raise CpoException("Agent class '" + cpath + "' is not a class.")
        if not issubclass(sclass, CpoSolverAgent):
            raise CpoException("Solver agent class '" + cpath + "' does not extend CpoSolverAgent.")

        # Create agent instance
        agent = sclass(self, sctx.params, sctx)
        return agent
Example #9
0
def _get_solver_agent_class(aname, sctx):
    """ Get a solver agent class from its name

    Args:
        aname:  Solver agent name
        sctx:   Candidate solver context
    Returns:
        Solver agent class
    """
    # Check for solver agent context
    if not isinstance(sctx, Context):
        raise CpoException("Unknown solving agent '" + aname + "'. Check config.context.solver.agent parameter.")
    cpath = sctx.class_name
    if cpath is None:
        raise CpoException("Solving agent '" + aname + "' context does not contain attribute 'class_name'")

    # Split class name
    pnx = cpath.rfind('.')
    if pnx < 0:
        raise CpoException("Invalid class name '" + cpath + "' for solving agent '" + aname + "'. Should be <package>.<module>.<class>.")
    mname = cpath[:pnx]
    cname = cpath[pnx + 1:]

    # Load module
    try:
        module = importlib.import_module(mname)
    except Exception as e:
        raise CpoException("Module '" + mname + "' import error: " + str(e))

    # Create and check class
    sclass = getattr(module, cname, None)
    if sclass is None:
        raise CpoException("Module '" + mname + "' does not contain a class '" + cname + "'")
    if not inspect.isclass(sclass):
        raise CpoException("Agent class '" + cpath + "' is not a class.")
    if not issubclass(sclass, CpoSolverAgent):
        raise CpoException("Solver agent class '" + cpath + "' does not extend CpoSolverAgent.")

    # Return
    return sclass
    def _submit_job(self, cmd, rclass):
        """ Submit a solving job with given command.

        According to the value of the context parameter 'verbose', the following information is logged
        if the log output is set:
         * 1: Total time spent to solve the model
         * 2: Calls to DOcplexcloud solving
         * 3: Detailed DOcplexcloud job information
         * 4: REST requests and response codes

        Args:
            cmd:    Solving command, in (Solve, RefineConflict, Propagate)
            rclass: Result object class
        Returns:
            Model solve result (object of class CpoSolveResult)
        Raises:
            CpoException if error occurs
        """
        # Create DOcplexcloud client
        ctx = self.context
        client = JobClient(ctx)

        # Convert model into CPO format
        cpostr = self._get_cpo_model_string()

        # Encode model
        stime = time.time()
        cpostr = cpostr.encode('utf-8')
        self.process_infos[
            CpoProcessInfos.TOTAL_UTF8_ENCODE_TIME] = time.time() - stime

        # Solve model and retrieve solution
        name = self.model.get_name()
        if is_number(ctx.params.TimeLimit):
            maxwait = ctx.params.TimeLimit + ctx.request_timeout + ctx.result_wait_extra_time
        else:
            maxwait = 0
        try:
            # Create job and start execution
            stime = time.time()
            client.create_cpo_job(name, cpostr, cmd)
            self.process_infos[
                CpoProcessInfos.TOTAL_DATA_SEND_TIME] = time.time() - stime

            # Start execution
            client.execute_job()

            # Wait job termination
            lgntf = None
            if self.log_enabled:
                lgntf = (
                    lambda recs: self._add_log_data(''.join(r['message'] + "\n"
                                                            for r in recs)))
            client.wait_job_termination(maxwait=maxwait, lognotif=lgntf)
            jinfo = client.get_info()

            # Trace response if required
            if ctx.is_log_enabled(3):
                ctx.log(3, "Job info:")
                for k in jinfo.keys():
                    ctx.log(3, k, " : ", jinfo[k])

            # Check failure
            fail = jinfo.get('failure', None)
            if fail is not None:
                raise CpoException(fail.get('message', "Unknown failure"))

            # Get solve status
            ssts = _get_solve_status(jinfo)
            if (cmd == "Solve") and (ssts not in _STATUS_WITH_RESULT):
                jsol = None
            else:
                # Get solution
                try:
                    stime = time.time()
                    jsol = client.get_attachment("solution.json")
                    self.process_infos[
                        CpoProcessInfos.
                        RESULT_RECEIVE_TIME] = time.time() - stime
                    self.process_infos[CpoProcessInfos.RESULT_DATA_SIZE] = len(
                        jsol)
                    stime = time.time()
                    jsol = jsol.decode('utf-8')
                    self.process_infos[
                        CpoProcessInfos.
                        TOTAL_UTF8_DECODE_TIME] = time.time() - stime
                except Exception as e:
                    raise CpoException("Model solution access error: " +
                                       str(e))

            # Create solution structure
            msol = self._create_result_object(rclass, jsol)
            if not jsol:
                msol.solve_status = ssts
            sinfos = msol.solver_infos

            # Add solve details if no json solution
            detls = jinfo.get('details', None)
            if detls is not None:
                # Retrieve common infos
                for k in detls:
                    if not (('.' in k) or ('_' in k)):
                        sinfos[k] = _value_of(detls[k])
                # Retrieve special hard-coded values
                if isinstance(msol, CpoSolveResult
                              ) and msol.solution.objective_values is None:
                    oval = detls.get('PROGRESS_CURRENT_OBJECTIVE', None)
                    if oval is not None:
                        msol.solution.objective_values = [
                            float(v) for v in oval.split(";")
                        ]
                sinfos.setdefault(
                    CpoSolverInfos.NUMBER_OF_CONSTRAINTS,
                    int(detls.get('MODEL_DETAIL_CONSTRAINTS', 0)))
                sinfos.setdefault(
                    CpoSolverInfos.NUMBER_OF_INTEGER_VARIABLES,
                    int(detls.get('MODEL_DETAIL_INTEGER_VARS', 0)))
                sinfos.setdefault(
                    CpoSolverInfos.NUMBER_OF_INTERVAL_VARIABLES,
                    int(detls.get('MODEL_DETAIL_INTERVAL_VARS', 0)))
                sinfos.setdefault(
                    CpoSolverInfos.NUMBER_OF_SEQUENCE_VARIABLES,
                    int(detls.get('MODEL_DETAIL_SEQUENCE_VARS', 0)))

            # Add solve time
            if CpoSolverInfos.SOLVE_TIME not in sinfos:
                sinfos[CpoSolverInfos.SOLVE_TIME] = (
                    float(jinfo.get('endedAt', 0)) -
                    float(jinfo.get('startedAt', 0))) / 1000

        finally:
            # Delete job
            if ctx.clean_job_after_solve:
                client.clean_job()

        # Return
        return msol
Example #11
0
    def __init__(self, solver, params, context):
        """ Create a new solver that solves locally with CP Optimizer Interactive.

        Args:
            solver:  Parent solver
            params:  Solving parameters
            context: Solver context
        Raises:
            CpoException if proxy executable does not exists
        """
        # Call super
        self.process = None
        self.active = True
        super(CpoSolverLocal, self).__init__(solver, params, context)

        # Check if executable file exists
        if context.execfile is None:
            raise CpoException(
                "Executable file should be given in 'execfile' context attribute."
            )
        if not is_string(context.execfile):
            raise CpoException(
                "Executable file should be given in 'execfile' as a string.")
        #if not os.path.isfile(context.execfile):
        #    raise CpoException("Executable file '" + str(context.execfile) + "' does not exists")

        # Create solving process
        cmd = [context.execfile]
        if context.parameters is not None:
            cmd.extend(context.parameters)
        context.log(2, "Solver exec command: '", ' '.join(cmd), "'")
        try:
            self.process = subprocess.Popen(cmd,
                                            stdin=subprocess.PIPE,
                                            stderr=subprocess.STDOUT,
                                            stdout=subprocess.PIPE,
                                            universal_newlines=False)
        except:
            raise CpoException(
                "Can not execute command '{}'. Please check availability of required executable file."
                .format(' '.join(cmd)))
        self.pout = self.process.stdin
        self.pin = self.process.stdout

        # Read initial version info from process
        self.version_info = None
        timer = threading.Timer(
            1, lambda: self.process.kill()
            if self.version_info is None else None)
        timer.start()
        evt, data = self._read_message()
        timer.cancel()
        if evt != EVT_VERSION_INFO:
            raise LocalSolverException(
                "Unexpected event {} received instead of version info event {}."
                .format(evt, EVT_VERSION_INFO))
        self.version_info = verinf = json.loads(data)
        self.available_command = self.version_info['AvailableCommands']
        # Normalize information
        verinf['AgentModule'] = __name__
        iver = verinf.get('AngelVersion')
        if iver:
            verinf['InterfaceVersion'] = iver

        context.log(3, "Local solver info: '", verinf, "'")

        # Check solver version if any
        sver = self.version_info.get('SolverVersion')
        mver = solver.get_model_format_version()
        if sver and mver and compare_natural(mver, sver) > 0:
            raise LocalSolverException(
                "Solver version {} is lower than model format version {}.".
                format(sver, mver))

        # Send CPO model to process
        cpostr = self._get_cpo_model_string()
        self._write_message(CMD_SET_CPO_MODEL, cpostr)
        self._wait_json_result(EVT_SUCCESS)  # JSON stored
        context.log(3, "Model sent.")

        # Initialize CPO callback setting
        self.callback_added = False
    def __init__(self, solver, params, context):
        """ Create a new solver that solves locally with CP Optimizer Interactive.

        Args:
            solver:  Parent solver
            params:  Solving parameters
            context: Solver context
        Raises:
            CpoException if proxy executable does not exists
        """
        # Call super
        self.process = None
        self.active = True
        self.timeout_kill = False
        super(CpoSolverLocal, self).__init__(solver, params, context)

        # Check if executable file exists
        xfile = context.execfile
        if xfile is None or not is_string(xfile):
            raise CpoException(
                "Executable file should be given in 'execfile' context attribute."
            )
        if not os.path.isfile(xfile):
            raise CpoException(
                "Executable file '{}' does not exists".format(xfile))
        if not is_exe_file(xfile):
            raise CpoException(
                "Executable file '{}' is not executable".format(xfile))

        # Create solving process
        cmd = [context.execfile]
        if context.parameters is not None:
            cmd.extend(context.parameters)
        context.log(2, "Solver exec command: '", ' '.join(cmd), "'")
        try:
            self.process = subprocess.Popen(cmd,
                                            stdin=subprocess.PIPE,
                                            stderr=subprocess.STDOUT,
                                            stdout=subprocess.PIPE,
                                            universal_newlines=False)
        except:
            raise CpoException(
                "Can not execute command '{}'. Please check availability of required executable file."
                .format(' '.join(cmd)))
        self.pout = self.process.stdin
        self.pin = self.process.stdout
        self.out_lock = threading.Lock()

        # Read initial version info from process
        self.version_info = None
        timeout = context.process_start_timeout
        timer = threading.Timer(timeout, self._process_start_timeout)
        timer.start()
        try:
            evt, data = self._read_message()
        except Exception as e:
            if self.timeout_kill:
                raise CpoSolverException(
                    "Solver process was too long to start and respond ({} seconds). Process has been killed."
                    .format(timeout))
            raise CpoSolverException(
                "Solver sub-process start failure: {}".format(e))
        timer.cancel()

        # Check received message
        if evt != EVT_VERSION_INFO:
            raise CpoSolverException(
                "Unexpected event {} received instead of version info event {}."
                .format(evt, EVT_VERSION_INFO))
        self.version_info = verinf = json.loads(data)
        self.available_commands = self.version_info['AvailableCommands']
        # Normalize information
        verinf['AgentModule'] = __name__

        context.log(3, "Local solver info: '", verinf, "'")

        # Transfer infos in process info
        for x in ('ProxyVersion', 'AngelVersion', 'SourceDate',
                  'SolverVersion'):
            self.process_infos[x] = self.version_info.get(x)

        # Check solver version if any
        sver = self.version_info.get('SolverVersion')
        mver = solver.get_model_format_version()
        if sver and mver and compare_natural(mver, sver) > 0:
            raise CpoSolverException(
                "Solver version {} is lower than model format version {}.".
                format(sver, mver))
    def __init__(self, solver, params, context):
        """ Create a new solver that solves locally with CP Optimizer Interactive.

        Args:
            solver:   Parent solver
            params:   Solving parameters
            context:  Proxy solver context
        Raises:
            CpoException if proxy executable does not exists
        """
        # Call super
        self.process = None
        self.active = True
        super(CpoSolverLocal, self).__init__(solver, params, context)

        # Check if executable file exists
        if context.execfile is None:
            raise CpoException("Executable file should be given in 'execfile' context attribute.")
        if not is_string(context.execfile):
            raise CpoException("Executable file should be given in 'execfile' as a string.")
        #if not os.path.isfile(context.execfile):
        #    raise CpoException("Executable file '" + str(context.execfile) + "' does not exists")

        # Create solving process
        cmd = [context.execfile]
        if context.parameters is not None:
            cmd.extend(context.parameters)
        context.log(2, "Solver exec command: '", ' '.join(cmd), "'")
        try:
            self.process = subprocess.Popen(cmd, stdin=subprocess.PIPE, stderr=subprocess.STDOUT, stdout=subprocess.PIPE, universal_newlines=False)
        except:
            raise CpoException("Can not execute command '{}'. Please check availability of required executable file.".format(' '.join(cmd)))
        self.pout = self.process.stdin
        self.pin = self.process.stdout

        # Read initial version info from process
        self.version_info = None
        timer = threading.Timer(1, lambda: self.process.kill() if self.version_info is None else None)
        timer.start()
        evt, data = self._read_message()
        timer.cancel()
        if evt != EVT_VERSION_INFO:
            raise LocalSolverException("Unexpected event {} received instead of version info event {}.".format(evt, EVT_VERSION_INFO))
        self.version_info = json.loads(data.decode('utf-8'))
        self.available_command = self.version_info['AvailableCommands']
        context.log(3, "Local solver info: '", self.version_info, "'")

        # Convert model into CPO format
        cpostr = self._get_cpo_model_string()

        # Encode model
        stime = time.time()
        cpostr = cpostr.encode('utf-8')
        self.process_infos[CpoProcessInfos.MODEL_ENCODE_TIME] = time.time() - stime

        # Send CPO model to process
        stime = time.time()
        self._write_message(CMD_SET_CPO_MODEL, cpostr)
        self.process_infos[CpoProcessInfos.MODEL_SEND_TIME] = time.time() - stime
        context.log(3, "Model sent.")
        self._wait_json_result(EVT_SUCCESS)  # JSON stored