Skip to main content
Glama
dmarsters

Quantum ZX-Calculus MCP Server

by dmarsters
quantum_zx_server.py27.6 kB
""" FastMCP Server Implementation for Quantum ZX-Calculus MCP Defines all tools and integrations. Layer 4 handles Claude synthesis. """ from fastmcp import FastMCP import json from typing import Optional, Dict, Any # Import local modules from quantum_zx_ologs import ( lookup_gate_zx_representation, lookup_rewrite_rule, lookup_measurement, get_all_gates, get_gate_count_statistics, QUANTUM_GATE_TAXONOMY ) from quantum_zx_calculus import ( parse_qasm_circuit, analyze_circuit, circuit_to_zx_diagram, select_simplification_strategy, get_circuit_statistics, CircuitFormat, OptimizationStrategy ) # Initialize FastMCP server mcp = FastMCP("quantum-zx-calculus") # ============================================================================ # LAYER 1: GATE TAXONOMY TOOLS # ============================================================================ @mcp.tool() def list_quantum_gates() -> str: """ List all supported quantum gates with their properties. Returns: JSON string with gate taxonomy """ gates = get_all_gates() stats = get_gate_count_statistics() gate_details = [] for gate_name in sorted(gates): gate = QUANTUM_GATE_TAXONOMY.get(gate_name) if gate: gate_details.append({ "name": gate.name, "qiskit_name": gate.qiskit_name, "matrix_symbol": gate.matrix_symbol, "primary_spider": gate.primary_spider.value, "phase": f"{gate.phase}π", "is_clifford": gate.is_clifford, "is_clifford_t": gate.is_clifford_t, "description": gate.description }) return json.dumps({ "statistics": stats, "gates": gate_details }, indent=2) @mcp.tool() def get_gate_zx_representation(gate_name: str) -> str: """ Get the ZX-diagram representation of a quantum gate. Args: gate_name: Name of the gate (e.g., 'h', 'cx', 't') Returns: JSON with ZX representation details """ result = lookup_gate_zx_representation(gate_name) if result: return json.dumps(result, indent=2) return json.dumps({"error": f"Gate '{gate_name}' not found"}) # ============================================================================ # LAYER 2: CIRCUIT ANALYSIS TOOLS # ============================================================================ @mcp.tool() def parse_quantum_circuit(qasm_code: str) -> str: """ Parse an OpenQASM 2.0 quantum circuit. Args: qasm_code: OpenQASM 2.0 circuit code Returns: JSON with parsed circuit structure """ circuit = parse_qasm_circuit(qasm_code) if not circuit: return json.dumps({"error": "Failed to parse circuit"}) return json.dumps({ "num_qubits": circuit.num_qubits, "num_gates": len(circuit.gates), "gates": [ {"name": name, "qubits": qubits} for name, qubits in circuit.gates ] }, indent=2) @mcp.tool() def analyze_quantum_circuit(qasm_code: str) -> str: """ Analyze circuit properties and optimization potential. Args: qasm_code: OpenQASM 2.0 circuit code Returns: JSON with circuit analysis """ circuit = parse_qasm_circuit(qasm_code) if not circuit: return json.dumps({"error": "Failed to parse circuit"}) stats = get_circuit_statistics(circuit) return json.dumps(stats, indent=2) @mcp.tool() def circuit_composition_analysis(qasm_code: str) -> str: """ Get detailed gate composition breakdown. Args: qasm_code: OpenQASM 2.0 circuit code Returns: JSON with composition analysis """ circuit = parse_qasm_circuit(qasm_code) if not circuit: return json.dumps({"error": "Failed to parse circuit"}) gate_counts: Dict[str, int] = {} for gate_name, _ in circuit.gates: gate_counts[gate_name] = gate_counts.get(gate_name, 0) + 1 return json.dumps({ "total_gates": len(circuit.gates), "unique_gates": len(gate_counts), "gate_counts": dict(sorted(gate_counts.items(), key=lambda x: x[1], reverse=True)), "num_qubits": circuit.num_qubits }, indent=2) # ============================================================================ # LAYER 2-3: ZX-DIAGRAM CONVERSION # ============================================================================ @mcp.tool() def convert_circuit_to_zx(qasm_code: str) -> str: """ Convert quantum circuit to ZX-diagram representation. Args: qasm_code: OpenQASM 2.0 circuit code Returns: JSON with ZX-diagram structure """ circuit = parse_qasm_circuit(qasm_code) if not circuit: return json.dumps({"error": "Failed to parse circuit"}) zx_diagram = circuit_to_zx_diagram(circuit) return json.dumps({ "diagram": zx_diagram.to_dict(), "num_spiders": len(zx_diagram.spiders), "num_wires": len(zx_diagram.wires), "bounding_box": zx_diagram.bounding_box }, indent=2) # ============================================================================ # LAYER 2: OPTIMIZATION STRATEGY SELECTION # ============================================================================ @mcp.tool() def recommend_optimization_strategy(qasm_code: str, desired_outcome: str = "balanced") -> str: """ Recommend ZX-calculus optimization strategy based on circuit. Args: qasm_code: OpenQASM 2.0 circuit code desired_outcome: One of 'balanced', 'clifford_simplification', 't_count_reduction', 'measurement_based', 'educational' Returns: JSON with recommended strategy """ circuit = parse_qasm_circuit(qasm_code) if not circuit: return json.dumps({"error": "Failed to parse circuit"}) analysis = analyze_circuit(circuit) strategy = select_simplification_strategy(analysis, desired_outcome) return json.dumps({ "strategy": strategy, "circuit_analysis": { "total_gates": analysis.total_gates, "clifford_gates": analysis.clifford_gates, "t_gates": analysis.t_gates, "two_qubit_gates": analysis.two_qubit_gates, "is_clifford_only": analysis.is_clifford_only, "estimated_t_count": analysis.estimated_t_count } }, indent=2) # ============================================================================ # LAYER 1: REFERENCE TOOLS # ============================================================================ @mcp.tool() def get_zx_rewrite_rules() -> str: """ Get reference guide for ZX-calculus rewrite rules. Returns: JSON with all available rewrite rules """ rules = { "same_type_fusion": lookup_rewrite_rule("same_type_fusion"), "phase_cancellation": lookup_rewrite_rule("phase_cancellation"), "hadamard_removal": lookup_rewrite_rule("hadamard_removal"), "spider_bialgebra": lookup_rewrite_rule("spider_bialgebra"), "color_change": lookup_rewrite_rule("color_change"), } return json.dumps(rules, indent=2) @mcp.tool() def get_quantum_measurement_basis(measurement_type: str) -> str: """ Get quantum measurement basis definition. Args: measurement_type: One of 'measure_z', 'measure_x', 'measure_y' Returns: JSON with measurement definition """ result = lookup_measurement(measurement_type) if result: return json.dumps(result, indent=2) return json.dumps({"error": f"Measurement type '{measurement_type}' not found"}) # ============================================================================ # LAYER 4: SYNTHESIS AND EXPLANATION TOOLS # ============================================================================ @mcp.tool() def explain_zx_diagram_transformation(before_qasm: str, after_qasm: str, transformation_type: str = "simplification") -> str: """ Generate explanation of transformation between two circuits. This is a Layer 4 tool that can accept Claude synthesis. Args: before_qasm: Original circuit in OpenQASM after_qasm: Transformed circuit in OpenQASM transformation_type: Type of transformation applied Returns: Structured explanation (Claude can synthesize natural language version) """ before_circuit = parse_qasm_circuit(before_qasm) after_circuit = parse_qasm_circuit(after_qasm) if not before_circuit or not after_circuit: return json.dumps({"error": "Failed to parse one or both circuits"}) before_analysis = analyze_circuit(before_circuit) after_analysis = analyze_circuit(after_circuit) return json.dumps({ "transformation_type": transformation_type, "before": { "total_gates": before_analysis.total_gates, "clifford_gates": before_analysis.clifford_gates, "t_gates": before_analysis.t_gates, "estimated_t_count": before_analysis.estimated_t_count }, "after": { "total_gates": after_analysis.total_gates, "clifford_gates": after_analysis.clifford_gates, "t_gates": after_analysis.t_gates, "estimated_t_count": after_analysis.estimated_t_count }, "improvements": { "gate_reduction": before_analysis.total_gates - after_analysis.total_gates, "t_count_reduction": before_analysis.estimated_t_count - after_analysis.estimated_t_count, "depth_improvement": before_analysis.clifford_depth - after_analysis.clifford_depth } }, indent=2) @mcp.tool() def generate_zx_diagram_markdown(qasm_code: str, title: str = "ZX-Diagram") -> str: """ Generate markdown documentation of ZX-diagram with ASCII representation. Args: qasm_code: OpenQASM 2.0 circuit code title: Title for the diagram documentation Returns: Markdown string with diagram documentation """ circuit = parse_qasm_circuit(qasm_code) if not circuit: return "Error: Failed to parse circuit" analysis = analyze_circuit(circuit) zx_diagram = circuit_to_zx_diagram(circuit) markdown = f"""# {title} ## Circuit Properties - **Number of Qubits:** {circuit.num_qubits} - **Total Gates:** {analysis.total_gates} - **Clifford Gates:** {analysis.clifford_gates} - **T Gates:** {analysis.t_gates} - **Two-Qubit Gates:** {analysis.two_qubit_gates} - **Is Clifford-Only:** {analysis.is_clifford_only} ## ZX-Diagram Statistics - **Number of Spiders:** {len(zx_diagram.spiders)} - **Number of Wires:** {len(zx_diagram.wires)} - **Bounding Box:** {zx_diagram.bounding_box} ## Gate Sequence ``` """ for i, (gate_name, qubits) in enumerate(circuit.gates, 1): qubit_str = ", ".join(f"q{q}" for q in qubits) markdown += f"{i:3d}. {gate_name:8s} {qubit_str}\n" markdown += """``` ## Spiders """ for spider in zx_diagram.spiders: phase_str = f" (phase: {zx_diagram.phases.get(spider['id'], 0)}π)" if zx_diagram.phases.get(spider['id'], 0) != 0 else "" markdown += f"- Spider {spider['id']}: {spider['type']}-spider {spider['inputs']} in, {spider['outputs']} out{phase_str}\n" return markdown # ============================================================================ # LAYER 1: INTENT CLASSIFICATION (NATURAL LANGUAGE INTERFACE) # ============================================================================ # Intent patterns for natural language understanding INTENT_PATTERNS = { "gate_query": { "keywords": ["what", "about", "property", "tell me", "explain", "gate", "is the"], "description": "Questions about specific quantum gates" }, "circuit_analysis": { "keywords": ["analyze", "circuit", "count", "clifford", "entangle", "what does", "what is"], "description": "Questions about analyzing existing circuits" }, "optimization": { "keywords": ["optimize", "simplify", "reduce", "minimize", "shorten", "improve"], "description": "Questions about circuit optimization" }, "zx_diagram": { "keywords": ["zx", "spider", "diagram", "visualize", "rewrite", "transformation"], "description": "Questions about ZX-diagram representation" }, "learning": { "keywords": ["teach", "learn", "show", "example", "demonstrate", "how", "bell state", "make"], "description": "Educational questions and examples" } } def classify_intent(user_question: str) -> tuple[str, float]: """ Classify user question into intent categories (Layer 1). Pure deterministic classification based on keyword matching. No LLM involved. 0 tokens. Args: user_question: Natural language question from user Returns: (intent_name, confidence_score) """ question_lower = user_question.lower() scores = {} for intent, data in INTENT_PATTERNS.items(): score = 0 for keyword in data["keywords"]: if keyword in question_lower: score += 1 scores[intent] = score # Find highest scoring intent best_intent = max(scores, key=scores.get) confidence = scores[best_intent] / sum(scores.values()) if sum(scores.values()) > 0 else 0 # Default to circuit_analysis if no clear match if confidence < 0.2: best_intent = "circuit_analysis" confidence = 0.5 return best_intent, confidence def extract_gate_name(user_question: str) -> Optional[str]: """ Extract gate name from question. Layer 1: Pattern matching for gate names. Args: user_question: Question that mentions a gate Returns: Normalized gate name or None """ gate_aliases = { "hadamard": "h", "pauli-x": "x", "pauli_x": "x", "pauli-y": "y", "pauli_y": "y", "pauli-z": "z", "pauli_z": "z", "cnot": "cx", "control-not": "cx", "control_not": "cx", "control-z": "cz", "control_z": "cz", "t-gate": "t", "s-gate": "s", "phase gate": "s" } question_lower = user_question.lower() # Check aliases first for alias, standard_name in gate_aliases.items(): if alias in question_lower: return standard_name # Check standard single-letter gates standard_gates = ["h", "x", "y", "z", "s", "t", "cx", "cz", "swap", "rx", "ry", "rz"] for gate in standard_gates: if gate in question_lower: return gate return None def format_gate_response(gate_data: Dict[str, Any]) -> str: """ Format gate information for natural language output. Returns JSON that Claude will synthesize into prose. Args: gate_data: Gate information from get_gate_zx_representation Returns: JSON with response type and synthesis instruction """ return json.dumps({ "response_type": "gate_info", "data": gate_data, "synthesis_instruction": ( "Format this gate information as a clear, educational explanation. " "Include: what the gate does, its ZX representation (spider type and phase), " "whether it's Clifford, and where it's used. Keep it concise but complete." ) }, indent=2) def format_circuit_analysis_response(analysis: Dict[str, Any]) -> str: """ Format circuit analysis for natural language output. Args: analysis: Circuit analysis from analyze_quantum_circuit Returns: JSON with response type and synthesis instruction """ return json.dumps({ "response_type": "circuit_analysis", "data": analysis, "synthesis_instruction": ( "Explain this circuit analysis conversationally. Describe: what the circuit does, " "its key properties (Clifford gates, T-count, entanglement score), and any obvious " "optimization opportunities. Make it accessible to someone learning quantum computing." ) }, indent=2) def format_optimization_response(strategy: Dict[str, Any]) -> str: """ Format optimization recommendation for natural language output. Args: strategy: Strategy recommendation from recommend_optimization_strategy Returns: JSON with response type and synthesis instruction """ return json.dumps({ "response_type": "optimization", "data": strategy, "synthesis_instruction": ( "Explain the optimization strategy clearly. Describe: the current circuit state, " "which ZX-rewrite rules could apply, what the simplified form would look like, " "and what's gained (e.g., '50% gate reduction'). Make it motivating—explain why " "the optimization matters." ) }, indent=2) def format_learning_response(circuit_qasm: str, explanation: Dict[str, Any]) -> str: """ Format educational content for natural language output. Args: circuit_qasm: The circuit being explained explanation: Analysis of the circuit Returns: JSON with response type and synthesis instruction """ return json.dumps({ "response_type": "learning", "circuit": circuit_qasm, "data": explanation, "synthesis_instruction": ( "Explain this quantum circuit pedagogically. Go step-by-step through what each " "gate does, showing the evolving quantum state. Use Dirac notation (|0⟩, |1⟩) " "and show the final state. Explain why this circuit is interesting or useful." ) }, indent=2) def format_zx_response(zx_data: Dict[str, Any]) -> str: """ Format ZX-diagram information for natural language output. Args: zx_data: ZX-diagram from convert_circuit_to_zx Returns: JSON with response type and synthesis instruction """ return json.dumps({ "response_type": "zx_diagram", "data": zx_data, "synthesis_instruction": ( "Explain this ZX-diagram representation. Describe the spiders (nodes), " "wires (connections), and phases. Explain what each spider type (Z, X, H) " "represents and why this representation is useful for quantum circuit optimization." ) }, indent=2) def route_and_execute( intent: str, user_question: str, circuit_qasm: Optional[str] = None ) -> Dict[str, Any]: """ Route question to appropriate tools and execute (Layers 2-3). All execution is deterministic - no LLM involved. 0 tokens. Args: intent: Classified intent from classify_intent user_question: Original user question circuit_qasm: Optional circuit code Returns: Result dictionary with formatted data """ if intent == "gate_query": gate_name = extract_gate_name(user_question) if not gate_name: return { "error": "Could not identify which gate you're asking about", "suggestion": "Try: 'Tell me about the H-gate' or 'What is a CNOT?'" } result = get_gate_zx_representation(gate_name) result_dict = json.loads(result) if isinstance(result, str) else result return { "intent": intent, "gate": gate_name, "result": result_dict } elif intent == "circuit_analysis": if not circuit_qasm: return { "error": "Please provide a circuit to analyze", "example": "Analyze this circuit: h q[0]; cx q[0],q[1]" } analysis = analyze_quantum_circuit(circuit_qasm) analysis_dict = json.loads(analysis) if isinstance(analysis, str) else analysis return { "intent": intent, "circuit": circuit_qasm, "analysis": analysis_dict } elif intent == "optimization": if not circuit_qasm: return { "error": "Please provide a circuit to optimize", "example": "Optimize this circuit: h q[0]; t q[0]; h q[0]; t q[0]" } strategy = recommend_optimization_strategy(circuit_qasm, "balanced") strategy_dict = json.loads(strategy) if isinstance(strategy, str) else strategy return { "intent": intent, "circuit": circuit_qasm, "strategy": strategy_dict } elif intent == "zx_diagram": if not circuit_qasm: return { "error": "Please provide a circuit for ZX-diagram conversion", "example": "Show me the ZX-diagram for: h q[0]; cx q[0],q[1]" } zx_result = convert_circuit_to_zx(circuit_qasm) zx_dict = json.loads(zx_result) if isinstance(zx_result, str) else zx_result return { "intent": intent, "circuit": circuit_qasm, "zx_diagram": zx_dict } elif intent == "learning": # Common examples examples = { "bell state": "h q[0]; cx q[0],q[1]", "superposition": "h q[0]", "entanglement": "cx q[0],q[1]", "ghz state": "h q[0]; cx q[0],q[1]; cx q[1],q[2]" } circuit_qasm = None for key, value in examples.items(): if key.lower() in user_question.lower(): circuit_qasm = value break if not circuit_qasm: circuit_qasm = "h q[0]; cx q[0],q[1]" # Default Bell state analysis = analyze_quantum_circuit(circuit_qasm) analysis_dict = json.loads(analysis) if isinstance(analysis, str) else analysis return { "intent": intent, "circuit": circuit_qasm, "analysis": analysis_dict } return {"error": f"Unknown intent: {intent}"} # ============================================================================ # LAYER 4: NATURAL LANGUAGE INTERFACE TOOL # ============================================================================ @mcp.tool() def ask_about_quantum_circuit( user_question: str, circuit_qasm: Optional[str] = None ) -> str: """ Natural language interface to quantum ZX-calculus brick. Ask questions about quantum circuits in plain English instead of calling specific tools. This tool intelligently routes to the appropriate analysis tools based on your question. Args: user_question: Your question in natural language. Examples: - "Tell me about the T-gate" - "What's a Bell state?" - "Analyze this circuit" - "Optimize this circuit" - "Show me the ZX-diagram" circuit_qasm: Optional OpenQASM circuit code to analyze Returns: Structured data formatted for Claude to synthesize into prose Examples: ask_about_quantum_circuit("Tell me about the T-gate") ask_about_quantum_circuit("What's a Bell state?") ask_about_quantum_circuit("Optimize this circuit", circuit_qasm="h q[0]; t q[0]; h q[0]; t q[0]") ask_about_quantum_circuit("Show me entanglement examples") ask_about_quantum_circuit("Analyze h q[0]; cx q[0],q[1]; t q[1]") Cost profile: Layer 1 (Intent classification): 0 tokens Layer 2 (Tool routing): 0 tokens Layer 3 (Execution): 0 tokens (all deterministic) Layer 4 (Synthesis): ~50-100 tokens (Claude formats response) Total: ~50-100 tokens vs 400-600 tokens for pure LLM approach Savings: 75-85% """ # Layer 1: Classify intent (0 tokens) intent, confidence = classify_intent(user_question) # Layer 2-3: Route and execute (0 tokens) execution_result = route_and_execute(intent, user_question, circuit_qasm) # Check for errors if "error" in execution_result: return json.dumps({ "status": "error", "error_message": execution_result["error"], "suggestion": execution_result.get("suggestion", execution_result.get("example")), "intent_detected": intent, "confidence": confidence }, indent=2) # Layer 4: Format for Claude synthesis if intent == "gate_query": formatted = format_gate_response(execution_result["result"]) elif intent == "circuit_analysis": formatted = format_circuit_analysis_response(execution_result["analysis"]) elif intent == "optimization": formatted = format_optimization_response(execution_result["strategy"]) elif intent == "zx_diagram": formatted = format_zx_response(execution_result["zx_diagram"]) elif intent == "learning": formatted = format_learning_response(execution_result["circuit"], execution_result["analysis"]) else: formatted = json.dumps(execution_result) return formatted # ============================================================================ # SERVER INFO # ============================================================================ @mcp.tool() def get_server_info() -> str: """ Get information about the Quantum ZX-Calculus MCP server. Returns: JSON with server information and capabilities """ return json.dumps({ "server": "quantum-zx-calculus-mcp", "version": "0.2.0", "description": "Convert quantum circuits to ZX-diagrams and analyze optimization strategies", "architecture": { "layer1": "Foundation - Gate taxonomy, intent classification (0 tokens)", "layer2": "Structure - Circuit parsing, tool routing (0 tokens)", "layer3": "Relational - Optimization strategy selection (0 tokens)", "layer4": "Contextual - Claude synthesis for explanations (~50-100 tokens)" }, "supported_formats": ["qasm", "description"], "natural_language_interface": "ask_about_quantum_circuit tool", "cost_optimization": "75-85% savings through deterministic layers", "based_on": "Bob Coecke's Categorical Quantum Mechanics (2008+)", "tools": { "layer1_gates": ["list_quantum_gates", "get_gate_zx_representation", "get_zx_rewrite_rules", "get_quantum_measurement_basis"], "layer2_analysis": ["parse_quantum_circuit", "analyze_quantum_circuit", "circuit_composition_analysis", "convert_circuit_to_zx"], "layer3_optimization": ["recommend_optimization_strategy"], "layer4_synthesis": ["explain_zx_diagram_transformation", "generate_zx_diagram_markdown", "ask_about_quantum_circuit"], "meta": ["get_server_info"] }, "references": [ "Coecke & Kissinger, 'Picturing Quantum Processes' (Cambridge, 2017)", "ZX-calculus: https://zxcalculus.com/", "PyZX: https://github.com/zxcalc/pyzx" ] }, indent=2) if __name__ == "__main__": mcp.run()

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/dmarsters/quantum-zx-calculus-mcp'

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