def set_quadratic_constraint_sense(self, qct, arg_newsense): new_sense = ComparisonType.parse(arg_newsense) if new_sense != qct.sense: self._engine.update_quadratic_constraint(qct, UpdateEvent.LinearConstraintType, new_sense) qct._internal_set_sense(new_sense)
def read_model(self, filename, model_name=None, verbose=False, model_class=None, **kwargs): """ Reads a model from a CPLEX export file. Accepts all formats exported by CPLEX: LP, SAV, MPS. If an error occurs while reading the file, the message of the exception is printed and the function returns None. Args: filename: The file to read. model_name: An optional name for the newly created model. If None, the model name will be the path basename. verbose: An optional flag to print informative messages, default is False. model_class: An optional class type; must be a subclass of Model. The returned model is built using this model_class and the keyword arguments kwargs, if any. By default, the model is class is `Model` (see kwargs: A dict of keyword-based arguments that are used when creating the model instance. Example: `m = read_model("c:/temp/foo.mps", model_name="docplex_foo", solver_agent="docloud", output_level=100)` Returns: An instance of Model, or None if an exception is raised. See Also: :class:`docplex.mp.model.Model` """ if not Cplex: # pragma: no cover raise RuntimeError("read_prm() requires CPLEX to run") if not os.path.exists(filename): raise IOError("* file not found: {0}".format(filename)) # extract basename if model_name: name_to_use = model_name else: basename = os.path.basename(filename) dotpos = basename.find(".") if dotpos > 0: name_to_use = basename[:dotpos] else: name_to_use = basename model_class = model_class or Model if 0 == os.stat(filename).st_size: print("* file is empty: {0} - exiting".format(filename)) return model_class(name=name_to_use, **kwargs) # print("-> start reading file: {0}".format(filename)) cpx = self._cplex_read(filename, verbose=verbose) if not cpx: # pragma: no cover return None range_map = {} final_output_level = kwargs.get("output_level", "info") debug_read = kwargs.get("debug", False) try: # force no tck if 'checker' in kwargs: final_checker = kwargs['checker'] else: final_checker = 'default' # build the model with no checker, then restore final_checker in the end. kwargs['checker'] = 'off' # ------------- mdl = model_class(name=name_to_use, **kwargs) lfactory = mdl._lfactory qfactory = mdl._qfactory mdl.set_quiet() # output level set to ERROR vartype_cont = mdl.continuous_vartype vartype_map = { 'B': mdl.binary_vartype, 'I': mdl.integer_vartype, 'C': mdl.continuous_vartype, 'S': mdl.semicontinuous_vartype } # 1 upload variables cpx_nb_vars = cpx.variables.get_num() cpx_var_names = self._safe_call_get_names(cpx.variables) if cpx._is_MIP(): cpx_vartypes = [ vartype_map.get(cpxt, vartype_cont) for cpxt in cpx.variables.get_types() ] else: cpx_vartypes = [vartype_cont] * cpx_nb_vars cpx_var_lbs = cpx.variables.get_lower_bounds() cpx_var_ubs = cpx.variables.get_upper_bounds() # map from cplex variable indices to docplex's # use to skip range vars # cplex : [x, Rg1, y] -> {0:0, 2: 1} var_index_map = {} d = 0 model_varnames = [] model_lbs = [] model_ubs = [] model_types = [] for v in range(cpx_nb_vars): varname = cpx_var_names[v] if cpx_var_names else None if varname and varname.startswith("Rg"): # generated var for ranges range_map[v] = self._RangeData(var_index=v, var_name=varname, ub=cpx_var_ubs[v]) else: # docplex_var = lfactory.new_var(vartype, lb, ub, varname) var_index_map[v] = d model_varnames.append(varname) model_types.append(cpx_vartypes[v]) model_lbs.append(cpx_var_lbs[v]) model_ubs.append(cpx_var_ubs[v]) d += 1 # vars model_vars = lfactory.new_multitype_var_list( d, model_types, model_lbs, model_ubs, model_varnames) cpx_var_index_to_docplex = { v: model_vars[var_index_map[v]] for v in var_index_map.keys() } # 2. upload linear constraints and ranges (mixed in cplex) cpx_linearcts = cpx.linear_constraints nb_linear_cts = cpx_linearcts.get_num() all_rows = cpx_linearcts.get_rows() all_rhs = cpx_linearcts.get_rhs() all_senses = cpx_linearcts.get_senses() all_range_values = cpx_linearcts.get_range_values() cpx_ctnames = self._safe_call_get_names(cpx_linearcts) has_range = range_map or any(s == "R" for s in all_senses) deferred_cts = [] for c in range(nb_linear_cts): row = all_rows[c] sense = all_senses[c] rhs = all_rhs[c] ctname = cpx_ctnames[c] if cpx_ctnames else None range_val = all_range_values[c] indices = row.ind coefs = row.val range_data = None if not has_range: expr = mdl._aggregator._scal_prod( (cpx_var_index_to_docplex[idx] for idx in indices), coefs) op = ComparisonType.parse(sense) ct = lfactory._new_binary_constraint(lhs=expr, rhs=rhs, sense=op) ct.name = ctname deferred_cts.append(ct) else: expr = lfactory.linear_expr() rcoef = 1 for idx, koef in izip(indices, coefs): var = cpx_var_index_to_docplex.get(idx, None) if var: expr._add_term(var, koef) elif idx in range_map: # this is a range: coeff must be 1 or -1 abscoef = koef if koef >= 0 else -koef rcoef = koef assert abscoef == 1, "range var has coef different from 1: {}".format( koef) assert range_data is None, "range_data is not None: {0!s}".format( range_data) # cannot use two range vars range_data = range_map[idx] else: # pragma: no cover # this is an internal error. raise ModelReaderError( "ERROR: index not in var map or range map: {0}" .format(idx)) if range_data: label = ctname or 'c#%d' % (c + 1) if sense not in "EL": # pragma: no cover raise ModelReaderError( "{0} range sense is not E: {1!s}".format( label, sense)) if rcoef < 0: # -1 actually rng_lb = rhs rng_ub = rhs + range_data.ub elif rcoef > 0: # koef is 1 here rng_lb = rhs - range_data.ub rng_ub = rhs else: # pragma: no cover raise ModelReaderError( "unexpected range coef: {}".format(rcoef)) mdl.add_range(lb=rng_lb, expr=expr, ub=rng_ub, rng_name=ctname) else: if sense == 'R': # range min is rangeval range_lb = rhs range_ub = rhs + range_val mdl.add_range(lb=range_lb, ub=range_ub, expr=expr, rng_name=ctname) else: op = ComparisonType.cplex_ctsense_to_python_op( sense) ct = op(expr, rhs) mdl.add_constraint(ct, ctname) if deferred_cts: # add constraint as a block lfactory._post_constraint_block(posted_cts=deferred_cts) # 3. upload Quadratic constraints cpx_quadraticcts = cpx.quadratic_constraints nb_quadratic_cts = cpx_quadraticcts.get_num() all_rhs = cpx_quadraticcts.get_rhs() all_linear_nb_non_zeros = cpx_quadraticcts.get_linear_num_nonzeros( ) all_linear_components = cpx_quadraticcts.get_linear_components() all_quadratic_nb_non_zeros = cpx_quadraticcts.get_quad_num_nonzeros( ) all_quadratic_components = cpx_quadraticcts.get_quadratic_components( ) all_senses = cpx_quadraticcts.get_senses() cpx_ctnames = self._safe_call_get_names(cpx_quadraticcts) for c in range(nb_quadratic_cts): rhs = all_rhs[c] linear_nb_non_zeros = all_linear_nb_non_zeros[c] linear_component = all_linear_components[c] quadratic_nb_non_zeros = all_quadratic_nb_non_zeros[c] quadratic_component = all_quadratic_components[c] sense = all_senses[c] ctname = cpx_ctnames[c] if cpx_ctnames else None if linear_nb_non_zeros > 0: indices, coefs = linear_component.unpack() linexpr = mdl._aggregator._scal_prod( (cpx_var_index_to_docplex[idx] for idx in indices), coefs) else: linexpr = None if quadratic_nb_non_zeros > 0: qfactory = mdl._qfactory ind1, ind2, coefs = quadratic_component.unpack() quads = qfactory.term_dict_type() for idx1, idx2, coef in izip(ind1, ind2, coefs): quads[VarPair(cpx_var_index_to_docplex[idx1], cpx_var_index_to_docplex[idx2])] = coef else: # pragma: no cover # should not happen, but who knows quads = None quad_expr = mdl._aggregator._quad_factory.new_quad( quads=quads, linexpr=linexpr, safe=True) op = ComparisonType.cplex_ctsense_to_python_op(sense) ct = op(quad_expr, rhs) mdl.add_constraint(ct, ctname) # 4. upload indicators cpx_indicators = cpx.indicator_constraints nb_indicators = cpx_indicators.get_num() all_ind_names = self._safe_call_get_names(cpx_indicators) all_ind_bvars = cpx_indicators.get_indicator_variables() all_ind_rhs = cpx_indicators.get_rhs() all_ind_linearcts = cpx_indicators.get_linear_components() all_ind_senses = cpx_indicators.get_senses() all_ind_complemented = cpx_indicators.get_complemented() lfactory = mdl._lfactory for i in range(nb_indicators): ind_bvar = all_ind_bvars[i] ind_name = all_ind_names[i] if all_ind_names else None ind_rhs = all_ind_rhs[i] ind_linear = all_ind_linearcts[i] # SparsePair(ind, val) ind_sense = all_ind_senses[i] ind_complemented = all_ind_complemented[i] # 1 . check the bvar is ok ind_bvar = cpx_var_index_to_docplex[ind_bvar] # each var appears once ind_linexpr = self._build_linear_expr_from_sparse_pair( lfactory, cpx_var_index_to_docplex, ind_linear) op = ComparisonType.cplex_ctsense_to_python_op(ind_sense) ind_ct = op(ind_linexpr, ind_rhs) indct = lfactory.new_indicator_constraint(ind_bvar, ind_ct, active_value=1 - ind_complemented, name=ind_name) mdl.add(indct) # 5. upload Piecewise linear constraints try: cpx_pwl = cpx.pwl_constraints cpx_pwl_defs = cpx_pwl.get_definitions() pwl_fallback_names = [""] * cpx_pwl.get_num() cpx_pwl_names = self._safe_call_get_names( cpx_pwl, pwl_fallback_names) for (vary_idx, varx_idx, preslope, postslope, breakx, breaky), pwl_name in izip(cpx_pwl_defs, cpx_pwl_names): varx = cpx_var_index_to_docplex.get(varx_idx, None) vary = cpx_var_index_to_docplex.get(vary_idx, None) breakxy = [(brkx, brky) for brkx, brky in zip(breakx, breaky)] pwl_func = mdl.piecewise(preslope, breakxy, postslope, name=pwl_name) pwl_expr = mdl._lfactory.new_pwl_expr( pwl_func, varx, 0, add_counter_suffix=False, resolve=False) pwl_expr._f_var = vary pwl_expr._ensure_resolved() except AttributeError: # pragma: no cover pass # Do not check for PWLs if Cplex version does not support them # 6. upload objective cpx_obj = cpx.objective cpx_sense = cpx_obj.get_sense() cpx_all_lin_obj_coeffs = cpx_obj.get_linear() # noinspection PyPep8 all_obj_vars = [] all_obj_coefs = [] for v in range(cpx_nb_vars): if v in cpx_var_index_to_docplex: obj_coeff = cpx_all_lin_obj_coeffs[v] all_obj_coefs.append(obj_coeff) all_obj_vars.append(cpx_var_index_to_docplex[v]) # obj_expr._add_term(idx_to_var_map[v], cpx_all_obj_coeffs[v]) obj_expr = mdl._aggregator._scal_prod(all_obj_vars, all_obj_coefs) if cpx_obj.get_num_quadratic_variables() > 0: cpx_all_quad_cols_coeffs = cpx_obj.get_quadratic() quads = qfactory.term_dict_type() for v, col_coefs in izip(cpx_var_index_to_docplex, cpx_all_quad_cols_coeffs): var1 = cpx_var_index_to_docplex[v] indices, coefs = col_coefs.unpack() for idx, coef in izip(indices, coefs): vp = VarPair(var1, cpx_var_index_to_docplex[idx]) quads[vp] = quads.get(vp, 0) + coef / 2 obj_expr += qfactory.new_quad(quads=quads, linexpr=None) obj_expr += cpx.objective.get_offset() is_maximize = cpx_sense == ObjSense.maximize if is_maximize: mdl.maximize(obj_expr) else: mdl.minimize(obj_expr) # upload sos cpx_sos = cpx.SOS cpx_sos_num = cpx_sos.get_num() if cpx_sos_num > 0: cpx_sos_types = cpx_sos.get_types() cpx_sos_indices = cpx_sos.get_sets() cpx_sos_names = cpx_sos.get_names() if not cpx_sos_names: cpx_sos_names = [None] * cpx_sos_num for sostype, sos_sparse, sos_name in izip( cpx_sos_types, cpx_sos_indices, cpx_sos_names): sos_var_indices = sos_sparse.ind isostype = int(sostype) sos_vars = [ cpx_var_index_to_docplex[var_ix] for var_ix in sos_var_indices ] mdl.add_sos(dvars=sos_vars, sos_arg=isostype, name=sos_name) mdl.output_level = final_output_level if final_checker: # need to restore checker mdl.set_checker(final_checker) except CplexError as cpx_e: # pragma: no cover print("* CPLEX error: {0!s} reading file {1}".format( cpx_e, filename)) mdl = None if debug_read: raise except ModelReaderError as mre: # pragma: no cover print("! Model reader error: {0!s} while reading file {1}".format( mre, filename)) mdl = None if debug_read: raise except DOcplexException as doe: # pragma: no cover print("! Internal DOcplex error: {0!s} while reading file {1}". format(doe, filename)) mdl = None if debug_read: raise except Exception as any_e: # pragma: no cover print("Internal exception raised: {0!s} while reading file {1}". format(any_e, filename)) mdl = None if debug_read: raise finally: # clean up CPLEX instance... del cpx return mdl