Skip to main content
Glama

mcp-solver

MIT License
133
  • Linux
  • Apple
solution.py8.71 kB
""" Z3 solution module for extracting and formatting solutions from Z3 solvers. This module provides functions for extracting solution data from Z3 solvers and converting it to a standardized format. """ import os import sys from typing import Any # IMPORTANT: Properly import the Z3 library (not our local package) # First, remove the current directory from the path to avoid importing ourselves current_dir = os.path.abspath(os.path.dirname(__file__)) parent_dir = os.path.abspath(os.path.join(current_dir, "..")) if current_dir in sys.path: sys.path.remove(current_dir) if parent_dir in sys.path: sys.path.remove(parent_dir) # Add site-packages to the front of the path import site site_packages = site.getsitepackages() for p in reversed(site_packages): if p not in sys.path: sys.path.insert(0, p) # Now try to import Z3 try: import z3 except ImportError: print("Z3 solver not found. Install with: pip install z3-solver>=4.12.1") sys.exit(1) # Track the last solution _LAST_SOLUTION = None def _extract_variable_values(model, variables: dict[str, Any]) -> dict[str, Any]: """ Helper function to extract variable values from a Z3 model. Args: model: Z3 model variables: Dictionary mapping variable names to Z3 variables Returns: Dictionary with variable names and their values """ result = {} if not model or not variables: return result for name, var in variables.items(): try: # Try basic model evaluation val = model.eval(var, model_completion=True) if val is not None: # Convert to appropriate Python type if z3.is_int(val): result[name] = val.as_long() elif z3.is_real(val): # Convert rational to float result[name] = float(val.as_decimal(10)) elif z3.is_bool(val): result[name] = z3.is_true(val) elif hasattr(z3, "is_string") and z3.is_string(val): result[name] = str(val) else: # For other types, convert to string result[name] = str(val) except Exception as e: print(f"Error extracting value for {name}: {e}") result[name] = f"Error: {e!s}" return result def _extract_objective_value(model, objective): """ Helper function to extract objective value from a Z3 model. Args: model: Z3 model objective: Z3 expression being optimized Returns: Objective value in appropriate Python type or None if error """ if not model or objective is None: return None try: obj_val = model.eval(objective, model_completion=True) if z3.is_int(obj_val): return obj_val.as_long() elif z3.is_real(obj_val): return float(obj_val.as_decimal(10)) else: return str(obj_val) except Exception as e: print(f"Error extracting objective value: {e}") return f"Error: {e!s}" def export_solution( solver=None, variables=None, objective=None, satisfiable=None, solution_dict=None, is_property_verification=False, property_verified=None, ) -> dict[str, Any]: """ Extract and format solutions from a Z3 solver. This function collects and standardizes solution data from Z3 solvers. It should be called at the end of Z3 code to export the solution. Args: solver: Z3 Solver or Optimize object variables: Dictionary mapping variable names to Z3 variables objective: Z3 expression being optimized (optional) satisfiable: Explicitly override the satisfiability status (optional) solution_dict: Directly provide a solution dictionary (optional) is_property_verification: Flag indicating if this is a property verification problem (optional) property_verified: Boolean indicating if the property was verified (optional) Returns: Dictionary containing the solution details """ global _LAST_SOLUTION # Initialize result dictionary result = { "satisfiable": False, "values": {}, "objective": None, "status": "unknown", "output": [], } # If solution_dict is provided, use it as the base if solution_dict is not None and isinstance(solution_dict, dict): result.update(solution_dict) # Ensure result has all required fields result.setdefault("satisfiable", False) result.setdefault("values", {}) result.setdefault("objective", None) result.setdefault("status", "unknown") result.setdefault("output", []) # Update status if satisfiable flag has been set if "satisfiable" in solution_dict: result["status"] = "sat" if solution_dict["satisfiable"] else "unsat" # Store result and return _LAST_SOLUTION = result return result # Process the solver if provided if solver is not None: # Get the solver status if isinstance(solver, z3.Solver) or isinstance(solver, z3.Optimize): status = solver.check() result["status"] = str(status) # Extract solution if satisfiable if status == z3.sat: result["satisfiable"] = True if variables is not None: model = solver.model() # Extract variable values result["values"] = _extract_variable_values(model, variables) # Extract objective value if present if objective is not None and isinstance(solver, z3.Optimize): result["objective"] = _extract_objective_value(model, objective) else: print(f"Warning: Unknown solver type: {type(solver)}") # Handle case with variables but no solver elif variables is not None: print( "Warning: Variables provided but no solver. Only variable names will be included." ) result["values"] = {name: None for name in variables} # Override satisfiability if explicitly provided if satisfiable is not None: result["satisfiable"] = bool(satisfiable) # Update status to match satisfiability flag result["status"] = "sat" if result["satisfiable"] else "unsat" # Handle property verification cases if is_property_verification: # If property_verified is explicitly provided, use it if property_verified is not None: # Store the property verification result explicitly result["values"]["property_verified"] = bool(property_verified) # For property verification: # - If we found a counterexample (property not verified), the solver is satisfiable # - If the property is verified for all cases, there's no counterexample, so the negation is unsatisfiable if property_verified: result["output"].append("Property verified successfully.") else: result["output"].append( "Property verification failed. Counterexample found." ) # Ensure satisfiability is set correctly for counterexample result["satisfiable"] = True result["status"] = "sat" else: # Infer property verification status from solver result # If solver returned unsat, property is verified (no counterexample) # If solver returned sat, property is not verified (counterexample found) property_verified = result["status"] == "unsat" result["values"]["property_verified"] = property_verified if property_verified: result["output"].append("Property verified successfully.") else: result["output"].append( "Property verification failed. Counterexample found." ) # Ensure satisfiability is set correctly for counterexample result["satisfiable"] = True result["status"] = "sat" else: # For regular constraint solving, add appropriate output messages if result["satisfiable"]: result["output"].append("Solution found.") else: result["output"].append( "No solution exists that satisfies all constraints." ) # Store result in global variable for the environment to find _LAST_SOLUTION = result 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