Skip to main content
Glama

mcp-solver

MIT License
133
  • Linux
  • Apple
cardinality_templates.py7.19 kB
""" Cardinality constraint templates for PySAT. This module provides template functions for common PySAT patterns related to cardinality constraints. """ import sys from typing import Any # Import PySAT but protect against failure try: from pysat.card import CardEnc, EncType from pysat.formula import CNF except ImportError: print("PySAT solver not found. Install with: pip install python-sat") sys.exit(1) def at_most_k( variables: list[int], k: int, encoding: EncType = EncType.seqcounter ) -> list[list[int]]: """ Create clauses ensuring at most k variables can be true. This is a robust implementation that handles edge cases better and falls back to direct encoding when needed. Args: variables: List of variable IDs k: Maximum number of variables that can be true encoding: Encoding type to try (will fall back to direct encoding if needed) Returns: List of clauses representing the constraint """ # Handle edge cases if k >= len(variables): return [] # No constraint needed # Use direct encoding for small sets (more reliable across versions) if len(variables) <= 20: clauses = [] import itertools for combo in itertools.combinations(variables, k + 1): clauses.append([-v for v in combo]) return clauses # For larger sets, try PySAT's encoding but handle failures try: return CardEnc.atmost(variables, k, encoding=encoding).clauses except Exception: # Fall back to direct encoding if PySAT's fails # This will be slower but more reliable clauses = [] import itertools for combo in itertools.combinations(variables, k + 1): clauses.append([-v for v in combo]) return clauses def at_least_k( variables: list[int], k: int, encoding: EncType = EncType.seqcounter ) -> list[list[int]]: """ Create clauses ensuring at least k variables must be true. Uses De Morgan's law with the robust at_most_k implementation. Args: variables: List of variable IDs k: Minimum number of variables that must be true encoding: Encoding type to try (will fall back to direct encoding if needed) Returns: List of clauses representing the constraint """ # We use De Morgan's law: at least k of variables = at most (n-k) of negated variables # For small instances, we can directly call at_most_k with negated vars if len(variables) <= 100: # Arbitrary threshold to avoid excessive computation neg_vars = [-v for v in variables] return at_most_k(neg_vars, len(variables) - k, encoding=encoding) # For larger instances, try PySAT's direct implementation try: return CardEnc.atleast(variables, k, encoding=encoding).clauses except Exception: # Fall back to at_most_k with De Morgan's law neg_vars = [-v for v in variables] return at_most_k(neg_vars, len(variables) - k, encoding=encoding) def exactly_k( variables: list[int], k: int, encoding: EncType = EncType.seqcounter ) -> CNF: """ Create CNF formula enforcing exactly k variables are true. Args: variables: List of variable IDs k: Exact number of variables that must be true encoding: Encoding type (default: sequential counter) Returns: CNF formula with the constraint """ return CardEnc.equals(variables, k, encoding=encoding) def one_hot_encoding( variables: list[int], encoding: EncType = EncType.seqcounter ) -> CNF: """ Create CNF formula enforcing exactly one variable is true. Args: variables: List of variable IDs encoding: Encoding type (default: sequential counter) Returns: CNF formula with the constraint """ return CardEnc.equals(variables, 1, encoding=encoding) def add_cardinality_constraint( formula: CNF, constraint_type: str, variables: list[int], k: int, encoding: EncType = EncType.seqcounter, ) -> CNF: """ Add a cardinality constraint to an existing CNF formula. Args: formula: Existing CNF formula constraint_type: Type of constraint ("atmost", "atleast", "exactly") variables: List of variable IDs k: Parameter value (max/min/exact number) encoding: Encoding type (default: sequential counter) Returns: CNF formula with the added constraint """ # Create constraint formula if constraint_type.lower() == "atmost": constraint = CardEnc.atmost(variables, k, encoding=encoding) elif constraint_type.lower() == "atleast": constraint = CardEnc.atleast(variables, k, encoding=encoding) elif constraint_type.lower() in ["exactly", "equals"]: constraint = CardEnc.equals(variables, k, encoding=encoding) else: raise ValueError(f"Unknown constraint type: {constraint_type}") # Add constraint clauses to the formula for clause in constraint.clauses: formula.append(clause) return formula def create_balanced_partitioning( variables: list[int], num_parts: int = 2, encoding: EncType = EncType.seqcounter ) -> dict[str, Any]: """ Create a balanced partitioning of variables. Args: variables: List of variable IDs num_parts: Number of partitions (default: 2) encoding: Encoding type (default: sequential counter) Returns: Dictionary with formulas and information about the partitioning """ if num_parts < 2: raise ValueError("Number of partitions must be at least 2") n = len(variables) # Create result dictionary result = {"formula": CNF(), "part_variables": [], "constraints": []} # Create part indicator variables # p_i_j = 1 means variable i is in part j top_var = max(abs(var) for var in variables) part_vars = [] for i, var in enumerate(variables): part_var_row = [] for j in range(num_parts): part_var = top_var + 1 + i * num_parts + j part_var_row.append(part_var) part_vars.append(part_var_row) # Each variable must be in exactly one part one_hot = CardEnc.equals(part_var_row, 1, encoding=encoding) result["constraints"].append(("one_hot", var, one_hot)) for clause in one_hot.clauses: result["formula"].append(clause) result["part_variables"] = part_vars # Calculate target size for each part target_size = n // num_parts remainder = n % num_parts # Create part size constraints for j in range(num_parts): # Variables indicating if each element is in part j part_j_vars = [part_vars[i][j] for i in range(n)] # Part size constraint part_size = target_size + (1 if j < remainder else 0) part_size_constraint = CardEnc.equals(part_j_vars, part_size, encoding=encoding) result["constraints"].append(("part_size", j, part_size_constraint)) # Add constraints to the formula for clause in part_size_constraint.clauses: result["formula"].append(clause) return result

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/szeider/mcp-solver'

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