Skip to main content
Glama
Sharmarajnish

Constrained Optimization MCP Server

solve_linear_programming

Solve linear and mixed-integer programming problems with linear constraints using the HiGHS solver for optimization tasks.

Instructions

Solve linear and mixed-integer programming problems using HiGHS.

This tool is ideal for linear programming, mixed-integer linear programming,
and large-scale optimization problems with linear constraints.

Args:
    sense: Optimization sense, either "minimize" or "maximize"
    objective_coeffs: List of objective function coefficients
    variables: List of variable definitions with optional bounds and types
    constraint_matrix: 2D list representing the constraint matrix (dense format)
    constraint_senses: List of constraint directions ("<=", ">=", "=")
    rhs_values: List of right-hand side values for constraints
    options: Optional solver options dictionary
    description: Optional problem description
    
Returns:
    Solution results including variable values and objective value
    
Example:
    sense = "minimize"
    objective_coeffs = [1.0, 2.0, 3.0]
    variables = [
        {"name": "x1", "lb": 0, "ub": 10, "type": "cont"},
        {"name": "x2", "lb": 0, "ub": None, "type": "int"},
        {"name": "x3", "lb": 0, "ub": 1, "type": "bin"}
    ]
    constraint_matrix = [[1, 1, 0], [0, 1, 1]]
    constraint_senses = ["<=", ">="]
    rhs_values = [5, 3]

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
constraint_matrixYes
constraint_sensesYes
descriptionNo
objective_coeffsYes
optionsNo
rhs_valuesYes
senseYes
variablesYes

Implementation Reference

  • The main handler function for the 'solve_linear_programming' MCP tool. It handles input validation, constructs the HiGHSProblem model from arguments, calls the solver, and returns formatted results as TextContent.
    @app.tool("solve_linear_programming")
    async def solve_linear_programming(
        sense: str,
        objective_coeffs: List[float],
        variables: List[dict[str, Any]],
        constraint_matrix: List[List[float]],
        constraint_senses: List[str],
        rhs_values: List[float],
        options: dict[str, Any] = None,
        description: str = "",
    ) -> List[TextContent]:
        """
        Solve linear and mixed-integer programming problems using HiGHS.
        
        This tool is ideal for linear programming, mixed-integer linear programming,
        and large-scale optimization problems with linear constraints.
        
        Args:
            sense: Optimization sense, either "minimize" or "maximize"
            objective_coeffs: List of objective function coefficients
            variables: List of variable definitions with optional bounds and types
            constraint_matrix: 2D list representing the constraint matrix (dense format)
            constraint_senses: List of constraint directions ("<=", ">=", "=")
            rhs_values: List of right-hand side values for constraints
            options: Optional solver options dictionary
            description: Optional problem description
            
        Returns:
            Solution results including variable values and objective value
            
        Example:
            sense = "minimize"
            objective_coeffs = [1.0, 2.0, 3.0]
            variables = [
                {"name": "x1", "lb": 0, "ub": 10, "type": "cont"},
                {"name": "x2", "lb": 0, "ub": None, "type": "int"},
                {"name": "x3", "lb": 0, "ub": 1, "type": "bin"}
            ]
            constraint_matrix = [[1, 1, 0], [0, 1, 1]]
            constraint_senses = ["<=", ">="]
            rhs_values = [5, 3]
        """
        try:
            # Validate sense
            try:
                from ..models.highs_models import HiGHSSense
                problem_sense = HiGHSSense(sense)
            except ValueError:
                return [
                    TextContent(
                        type="text",
                        text=(
                            f"Invalid sense: {sense}. "
                            f"Must be one of: minimize, maximize"
                        ),
                    )
                ]
    
            # Create objective
            from ..models.highs_models import HiGHSObjective
            objective = HiGHSObjective(linear=objective_coeffs)
    
            # Create variables
            problem_variables = []
            for i, var in enumerate(variables):
                var_name = var.get("name", f"x{i+1}")
                var_lb = var.get("lb", 0.0)
                var_ub = var.get("ub", None)
                var_type_str = var.get("type", "cont")
    
                try:
                    from ..models.highs_models import HiGHSVariableType
                    var_type = HiGHSVariableType(var_type_str)
                except ValueError:
                    return [
                        TextContent(
                            type="text",
                            text=(
                                f"Invalid variable type: {var_type_str}. "
                                f"Must be one of: cont, int, bin"
                            ),
                        )
                    ]
    
                from ..models.highs_models import HiGHSVariable
                problem_variables.append(
                    HiGHSVariable(name=var_name, lb=var_lb, ub=var_ub, type=var_type)
                )
    
            # Create constraints
            constraint_sense_enums = []
            for sense_str in constraint_senses:
                try:
                    from ..models.highs_models import HiGHSConstraintSense
                    constraint_sense_enums.append(HiGHSConstraintSense(sense_str))
                except ValueError:
                    return [
                        TextContent(
                            type="text",
                            text=(
                                f"Invalid constraint sense: {sense_str}. "
                                f"Must be one of: <=, >=, ="
                            ),
                        )
                    ]
    
            from ..models.highs_models import HiGHSConstraints, HiGHSProblemSpec, HiGHSProblem, HiGHSOptions
            constraints = HiGHSConstraints(
                dense=constraint_matrix,
                sparse=None,
                sense=constraint_sense_enums,
                rhs=rhs_values,
            )
    
            # Create problem specification
            problem_spec = HiGHSProblemSpec(
                sense=problem_sense,
                objective=objective,
                variables=problem_variables,
                constraints=constraints,
            )
    
            # Create options if provided
            highs_options = None
            if options:
                highs_options = HiGHSOptions(**options)
    
            # Create full problem
            problem = HiGHSProblem(problem=problem_spec, options=highs_options)
    
            # Solve the problem
            result = solve_highs_problem(problem)
    
            match result:
                case Success(solution):
                    return [
                        TextContent(
                            type="text",
                            text=json.dumps(
                                {
                                    "values": solution.values,
                                    "objective_value": solution.objective_value,
                                    "status": solution.status.value,
                                    "solve_time": solution.solve_time,
                                    "dual_values": solution.dual_values,
                                    "reduced_costs": solution.reduced_costs,
                                }
                            ),
                        )
                    ]
                case Failure(error):
                    return [TextContent(type="text", text=f"Error solving problem: {error}")]
        except Exception as e:
            return [TextContent(type="text", text=f"Error in solve_linear_programming: {e!s}")]
  • Core solver implementation using the highspy library to solve linear/mixed-integer programs defined by the HiGHSProblem model.
    def solve_highs_problem(problem: HiGHSProblem) -> Result[HiGHSProblem, str]:
        """Solve a HiGHs optimization problem."""
        try:
            # Validate problem
            if not problem.validate():
                return Failure("Invalid problem definition")
    
            start_time = time.time()
            
            # Create HiGHs instance
            h = highspy.Highs()
    
            # Always disable output for MCP server compatibility
            h.setOptionValue("output_flag", False)
            h.setOptionValue("log_to_console", False)
    
            # Apply options
            options_result = _apply_options(h, problem.options)
            if isinstance(options_result, Failure):
                return options_result
    
            problem_spec = problem.problem
            num_vars = len(problem_spec.variables)
            num_constraints = len(problem_spec.constraints.sense)
    
            # Set up objective
            obj_coeffs = np.array(problem_spec.objective.linear, dtype=float)
            if len(obj_coeffs) != num_vars:
                return Failure(
                    f"Objective coefficients length ({len(obj_coeffs)}) doesn't match number of variables ({num_vars})"
                )
    
            # Set up variable bounds
            var_lower = np.zeros(num_vars, dtype=float)
            var_upper = np.full(num_vars, highspy.kHighsInf, dtype=float)
    
            for i, var_spec in enumerate(problem_spec.variables):
                lb, ub = _get_variable_bounds(var_spec, i)
                var_lower[i] = lb
                var_upper[i] = ub
    
            # Build constraint matrix
            matrix_result = _build_constraint_matrix(problem_spec)
            if isinstance(matrix_result, Failure):
                return matrix_result
    
            rows, cols, values = matrix_result.unwrap()
    
            # Set up constraint bounds
            constraint_lower = np.zeros(num_constraints, dtype=float)
            constraint_upper = np.zeros(num_constraints, dtype=float)
    
            for i, (sense, rhs) in enumerate(
                zip(
                    problem_spec.constraints.sense,
                    problem_spec.constraints.rhs,
                    strict=False,
                )
            ):
                lb, ub = _convert_constraint_sense(sense, rhs)
                constraint_lower[i] = lb
                constraint_upper[i] = ub
    
            # Add variables
            h.addCols(
                num_vars,
                obj_coeffs,
                var_lower,
                var_upper,
                0,
                np.array([]),
                np.array([]),
                np.array([]),
            )
    
            # Set variable integrality constraints
            integrality = np.zeros(num_vars, dtype=int)  # 0 = continuous
            for i, var_spec in enumerate(problem_spec.variables):
                if var_spec.type == HiGHSVariableType.BINARY:
                    integrality[i] = 1  # 1 = integer (binary is integer with bounds 0-1)
                elif var_spec.type == HiGHSVariableType.INTEGER:
                    integrality[i] = 1  # 1 = integer
                # else: continuous (already 0)
    
            # Apply integrality constraints if any variables are integer/binary
            if np.any(integrality > 0):
                h.changeColsIntegrality(num_vars, np.arange(num_vars), integrality)
    
            # Add constraints using sparse format
            if len(rows) > 0:
                # Convert to row-wise sparse format for HiGHs
                # Group by rows and create start array
                unique_rows = np.unique(rows)
                start_array = np.zeros(num_constraints + 1, dtype=int)
    
                for row in unique_rows:
                    start_array[row] = np.sum(rows < row)
    
                start_array[-1] = len(rows)  # Final start
    
                h.addRows(
                    num_constraints,
                    constraint_lower,
                    constraint_upper,
                    len(values),
                    start_array,
                    cols,
                    values,
                )
            else:
                # No constraints case
                h.addRows(
                    num_constraints,
                    constraint_lower,
                    constraint_upper,
                    0,
                    np.array([0]),
                    np.array([]),
                    np.array([]),
                )
    
            # Set objective sense
            if problem_spec.sense == HiGHSSense.MAXIMIZE:
                h.changeObjectiveSense(highspy.ObjSense.kMaximize)
            else:
                h.changeObjectiveSense(highspy.ObjSense.kMinimize)
    
            # Solve the problem
            h.run()
            solve_time = time.time() - start_time
    
            # Get results
            model_status = h.getModelStatus()
            solution = h.getSolution()
            info = h.getInfo()
    
            # Convert status
            status = _convert_status(model_status)
    
            # Extract solution values
            solution_values = solution.col_value if hasattr(solution, "col_value") else []
            dual_values = solution.row_dual if hasattr(solution, "row_dual") else []
            reduced_costs = solution.col_dual if hasattr(solution, "col_dual") else []
    
            # Get objective value
            objective_value = (
                info.objective_function_value
                if hasattr(info, "objective_function_value")
                else 0.0
            )
    
            # Create solution with variable names
            values = {}
            for i, var_spec in enumerate(problem_spec.variables):
                var_name = var_spec.name or f"x{i}"
                values[var_name] = solution_values[i] if i < len(solution_values) else 0.0
    
            # Create solver statistics
            solver_stats = {
                "solve_time": solve_time,
                "iterations": getattr(info, "simplex_iteration_count", 0),
                "num_variables": num_vars,
                "num_constraints": num_constraints,
            }
    
            solution = HiGHSProblem(
                problem=problem_spec,
                options=problem.options,
                values=values,
                objective_value=objective_value,
                status=status,
                is_optimal=status == HiGHSStatus.OPTIMAL,
                solve_time=solve_time,
                dual_values=dual_values,
                reduced_costs=reduced_costs,
                solver_stats=solver_stats,
            )
    
            return Success(solution)
    
        except Exception as e:
            return Failure(f"Error solving HiGHs problem: {e}")
  • Pydantic models (HiGHSProblem, HiGHSProblemSpec, etc.) providing schema and validation for the linear programming problem inputs and outputs.
    class HiGHSProblemSpec(BaseModel):
        """Problem specification for HiGHs."""
    
        sense: HiGHSSense = Field(..., description="Optimization sense")
        objective: HiGHSObjective = Field(..., description="Objective function")
        variables: List[HiGHSVariable] = Field(..., description="Variable specifications")
        constraints: HiGHSConstraints = Field(..., description="Constraint specifications")
    
    
    class HiGHSProblem(BaseProblem):
        """Complete HiGHS optimization problem."""
    
        problem: HiGHSProblemSpec = Field(..., description="Problem specification")
        options: Optional[HiGHSOptions] = Field(default=None, description="Solver options")
        
        # Base problem properties
        problem_type: ProblemType = Field(default=ProblemType.LINEAR_PROGRAMMING, description="Problem type")
        sense: OptimizationSense = Field(..., description="Optimization sense")
        description: str = Field(default="", description="Problem description")
    
        def get_variables(self) -> List[BaseVariable]:
            """Get all variables in the problem."""
            return self.problem.variables

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Sharmarajnish/MCP-Constrained-Optimization'

If you have feedback or need assistance with the MCP directory API, please join our Discord server