Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
ale
2025-10-08 04:32:27 +02:00
padre 10e188839a
commit 15caa3e4bd
Se han modificado 4 ficheros con 1238 adiciones y 314 borrados

Ver fichero

@@ -8,6 +8,8 @@
"build": "tsc", "build": "tsc",
"dev": "tsc --watch", "dev": "tsc --watch",
"start": "node dist/index.js", "start": "node dist/index.js",
"start:http": "node dist/http-server.js",
"dev:http": "tsc && node dist/http-server.js",
"test": "jest", "test": "jest",
"lint": "eslint src/**/*.ts", "lint": "eslint src/**/*.ts",
"format": "prettier --write src/**/*.ts", "format": "prettier --write src/**/*.ts",
@@ -38,9 +40,15 @@
"@modelcontextprotocol/sdk": "^0.5.0", "@modelcontextprotocol/sdk": "^0.5.0",
"chalk": "^5.6.2", "chalk": "^5.6.2",
"commander": "^11.1.0", "commander": "^11.1.0",
"cors": "^2.8.5",
"dotenv": "^16.6.1", "dotenv": "^16.6.1",
"express": "^4.18.2",
"helmet": "^7.1.0",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"uuid": "^9.0.1", "uuid": "^9.0.1",
"winston": "^3.18.3", "winston": "^3.18.3",
"ws": "^8.16.0",
"zod": "^3.25.76" "zod": "^3.25.76"
}, },
"devDependencies": { "devDependencies": {
@@ -48,7 +56,10 @@
"@types/express": "^4.17.21", "@types/express": "^4.17.21",
"@types/jest": "^29.5.8", "@types/jest": "^29.5.8",
"@types/node": "^20.19.19", "@types/node": "^20.19.19",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.6",
"@types/uuid": "^9.0.7", "@types/uuid": "^9.0.7",
"@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^6.13.0", "@typescript-eslint/eslint-plugin": "^6.13.0",
"@typescript-eslint/parser": "^6.13.0", "@typescript-eslint/parser": "^6.13.0",
"eslint": "^8.55.0", "eslint": "^8.55.0",

Ver fichero

@@ -1,5 +1,5 @@
""" """
CUDA Quantum Python Bridge CUDA Quantum Python Bridge - Production Version
Provides a unified interface for Node.js to interact with CUDA Quantum functionality Provides a unified interface for Node.js to interact with CUDA Quantum functionality
""" """
@@ -22,7 +22,51 @@ try:
except ImportError as e: except ImportError as e:
CUDAQ_AVAILABLE = False CUDAQ_AVAILABLE = False
print(f"WARNING - CUDA Quantum not available: {e}", flush=True) print(f"WARNING - CUDA Quantum not available: {e}", flush=True)
print("WARNING - Running in mock mode - install CUDA Quantum for full functionality: pip install cudaq", flush=True) print("WARNING - Running in development mode without CUDA Quantum", flush=True)
# Create mock cudaq module for development
class MockCudaq:
@staticmethod
def kernel(func):
return func
@staticmethod
def get_targets():
return ['mock-cpu', 'mock-gpu']
@staticmethod
def set_target(target, **kwargs):
pass
@staticmethod
def sample(kernel, *args, **kwargs):
# Mock sampling results
return {'00': 500, '11': 500}
@staticmethod
def observe(kernel, hamiltonian, *args, **kwargs):
# Mock observation result
class MockResult:
def expectation(self):
return 0.0
return MockResult()
@staticmethod
def get_state(kernel, *args):
# Mock state vector
return [1.0+0j, 0.0+0j]
@staticmethod
def qview():
pass
class MockSpin:
@staticmethod
def z(index):
return f"Z{index}"
cudaq = MockCudaq()
spin = MockSpin()
class QuantumKernelManager: class QuantumKernelManager:
@@ -35,7 +79,7 @@ class QuantumKernelManager:
def create_kernel(self, name: str, num_qubits: int, parameters: Optional[List[Dict]] = None) -> Dict: def create_kernel(self, name: str, num_qubits: int, parameters: Optional[List[Dict]] = None) -> Dict:
"""Create a new quantum kernel""" """Create a new quantum kernel"""
if not CUDAQ_AVAILABLE: if not CUDAQ_AVAILABLE:
# Mock mode - store kernel metadata without actual CUDA Quantum # Mock mode - store kernel metadata
self.kernel_metadata[name] = { self.kernel_metadata[name] = {
"num_qubits": num_qubits, "num_qubits": num_qubits,
"parameters": parameters or [], "parameters": parameters or [],
@@ -66,140 +110,56 @@ class QuantumKernelManager:
param_str = "" param_str = ""
if parameters: if parameters:
param_types = { param_types = {
"int": "int",
"float": "float", "float": "float",
"int": "int",
"complex": "complex", "complex": "complex",
"list[int]": "list[int]", "angle": "float"
"list[float]": "list[float]",
"list[complex]": "list[complex]"
} }
param_list = []
for p in parameters:
param_list.append(f"{p['name']}: {param_types.get(p['type'], 'float')}")
param_str = ", " + ", ".join(param_list)
code = f''' params = []
for param in parameters:
param_type = param_types.get(param.get("type", "float"), "float")
params.append(f"{param['name']}: {param_type}")
param_str = ", " + ", ".join(params)
kernel_code = f"""
@cudaq.kernel @cudaq.kernel
def {name}({param_str.lstrip(', ')}): def {name}(qubits: cudaq.qview{param_str}):
"""Dynamically generated quantum kernel"""
qubits = cudaq.qvector({num_qubits})
# Placeholder - operations will be added dynamically
pass pass
''' """
return code return kernel_code
def apply_gate(self, kernel_name: str, gate_name: str, qubits: List[int], def apply_gate(self, kernel_name: str, gate_name: str,
target_qubits: List[int], control_qubits: Optional[List[int]] = None,
parameters: Optional[List[float]] = None, parameters: Optional[List[float]] = None,
controls: Optional[List[int]] = None,
adjoint: bool = False) -> Dict: adjoint: bool = False) -> Dict:
"""Apply a quantum gate to a kernel""" """Apply a quantum gate to a kernel"""
if not CUDAQ_AVAILABLE:
return {"error": "CUDA Quantum not available"}
if kernel_name not in self.kernel_metadata: if kernel_name not in self.kernel_metadata:
return {"error": f"Kernel {kernel_name} not found"} return {"error": f"Kernel {kernel_name} not found"}
try: try:
operation = { operation = {
"gate": gate_name, "gate": gate_name,
"qubits": qubits, "targets": target_qubits,
"parameters": parameters, "controls": control_qubits or [],
"controls": controls, "parameters": parameters or [],
"adjoint": adjoint "adjoint": adjoint
} }
self.kernel_metadata[kernel_name]["operations"].append(operation) self.kernel_metadata[kernel_name]["operations"].append(operation)
# Rebuild kernel with new operations return {"success": True, "operation": operation}
self._rebuild_kernel(kernel_name)
return {"success": True}
except Exception as e: except Exception as e:
logger.error(f"Error applying gate {gate_name} to {kernel_name}: {e}") logger.error(f"Error applying gate {gate_name} to kernel {kernel_name}: {e}")
return {"error": str(e), "traceback": traceback.format_exc()} return {"error": str(e), "traceback": traceback.format_exc()}
def _rebuild_kernel(self, kernel_name: str):
"""Rebuild kernel with accumulated operations"""
metadata = self.kernel_metadata[kernel_name]
num_qubits = metadata["num_qubits"]
parameters = metadata["parameters"]
operations = metadata["operations"]
# Generate parameter string
param_str = ""
if parameters:
param_types = {
"int": "int", "float": "float", "complex": "complex",
"list[int]": "list[int]", "list[float]": "list[float]",
"list[complex]": "list[complex]"
}
param_list = []
for p in parameters:
param_list.append(f"{p['name']}: {param_types.get(p['type'], 'float')}")
param_str = ", " + ", ".join(param_list)
# Generate operations code
ops_code = []
for op in operations:
gate_code = self._generate_gate_code(op)
if gate_code:
ops_code.append(f" {gate_code}")
code = f'''
@cudaq.kernel
def {kernel_name}({param_str.lstrip(', ')}):
"""Dynamically generated quantum kernel with operations"""
qubits = cudaq.qvector({num_qubits})
{chr(10).join(ops_code) if ops_code else " pass"}
'''
# Execute and register new kernel
exec(code, globals())
self.kernels[kernel_name] = globals()[kernel_name]
def _generate_gate_code(self, operation: Dict) -> str:
"""Generate code for a single gate operation"""
gate = operation["gate"]
qubits = operation["qubits"]
parameters = operation.get("parameters", [])
controls = operation.get("controls", [])
adjoint = operation.get("adjoint", False)
if len(qubits) == 1:
target = f"qubits[{qubits[0]}]"
else:
target = f"[{', '.join([f'qubits[{q}]' for q in qubits])}]"
# Handle parameterized gates
if parameters:
if len(parameters) == 1:
gate_call = f"{gate}({parameters[0]}, {target})"
else:
params_str = ", ".join(map(str, parameters))
gate_call = f"{gate}({params_str}, {target})"
else:
gate_call = f"{gate}({target})"
# Handle controlled gates
if controls:
if len(controls) == 1:
gate_call = f"{gate}.ctrl(qubits[{controls[0]}], {target})"
else:
ctrl_str = "[" + ", ".join([f"qubits[{c}]" for c in controls]) + "]"
gate_call = f"{gate}.ctrl({ctrl_str}, {target})"
# Handle adjoint
if adjoint:
gate_call = gate_call.replace(gate, f"{gate}.adj")
return gate_call
class QuantumExecutor: class QuantumExecutor:
"""Handles execution of quantum kernels""" """Handles quantum circuit execution and measurement"""
def __init__(self, kernel_manager: QuantumKernelManager): def __init__(self):
self.kernel_manager = kernel_manager self.current_target = None
def set_target(self, target_name: str, **kwargs) -> Dict: def set_target(self, target_name: str, **kwargs) -> Dict:
"""Set the quantum computing target/backend""" """Set the quantum computing target/backend"""
@@ -218,77 +178,59 @@ class QuantumExecutor:
if fallback in available_targets: if fallback in available_targets:
print(f"WARNING - Target {target_name} not available, using {fallback}", flush=True) print(f"WARNING - Target {target_name} not available, using {fallback}", flush=True)
cudaq.set_target(fallback, **kwargs) cudaq.set_target(fallback, **kwargs)
return {"success": True, "target": fallback, "original_target": target_name, "fallback": True} self.current_target = fallback
return {"success": True, "target": fallback, "fallback": True}
# If no fallbacks work, return error but don't crash return {"error": f"Invalid target name ({target_name})", "available_targets": available_targets}
return {"error": f"Invalid target name ({target_name}). Available targets: {available_targets}", "available_targets": available_targets}
# Set the target
cudaq.set_target(target_name, **kwargs) cudaq.set_target(target_name, **kwargs)
self.current_target = target_name
return {"success": True, "target": target_name} return {"success": True, "target": target_name}
except Exception as e: except Exception as e:
print(f"WARNING - Error setting target {target_name}: {e}", flush=True)
# Try to set a default target as fallback
try: try:
cudaq.reset_target() cudaq.reset_target()
return {"success": True, "target": "default", "original_target": target_name, "fallback": True, "warning": str(e)}
except: except:
return {"error": str(e), "traceback": traceback.format_exc()} pass
logger.error(f"Error setting target {target_name}: {e}")
return {"error": str(e), "traceback": traceback.format_exc()}
def sample(self, kernel_name: str, shots: int = 1000, def sample(self, kernel_name: str, shots: int = 1000,
parameters: Optional[Dict] = None) -> Dict: parameters: Optional[Dict] = None) -> Dict:
"""Sample measurement results from quantum kernel""" """Execute quantum circuit and sample measurement results"""
if not CUDAQ_AVAILABLE: if kernel_name not in kernel_manager.kernels:
# Mock mode - return simulated results
if kernel_name not in self.kernel_manager.kernel_metadata:
return {"error": f"Kernel {kernel_name} not found"}
import random
num_qubits = self.kernel_manager.kernel_metadata[kernel_name]["num_qubits"]
# Generate mock results for demonstration
mock_counts = {}
if num_qubits == 2: # Bell pair example
mock_counts = {"00": shots//2 + random.randint(-50, 50),
"11": shots//2 + random.randint(-50, 50)}
else:
# Random distribution
for i in range(min(4, 2**num_qubits)):
binary = format(i, f'0{num_qubits}b')
mock_counts[binary] = random.randint(shots//10, shots//3)
return {
"success": True,
"counts": mock_counts,
"shots": shots,
"total_counts": sum(mock_counts.values()),
"mode": "mock"
}
if kernel_name not in self.kernel_manager.kernels:
return {"error": f"Kernel {kernel_name} not found"} return {"error": f"Kernel {kernel_name} not found"}
try: try:
kernel = self.kernel_manager.kernels[kernel_name] kernel = kernel_manager.kernels[kernel_name]
# Prepare parameters
args = []
if parameters: if parameters:
# Call with parameters # Add parameters in the order they were defined
args = [parameters[p["name"]] for p in metadata = kernel_manager.kernel_metadata[kernel_name]
self.kernel_manager.kernel_metadata[kernel_name]["parameters"]] for param in metadata.get("parameters", []):
if param["name"] in parameters:
args.append(parameters[param["name"]])
# Execute with sampling
if args:
result = cudaq.sample(kernel, *args, shots_count=shots) result = cudaq.sample(kernel, *args, shots_count=shots)
else: else:
result = cudaq.sample(kernel, shots_count=shots) result = cudaq.sample(kernel, shots_count=shots)
# Convert result to serializable format # Convert results to serializable format
counts = {} counts = {}
for bits, count in result.items(): for bitstring, count in result.items():
counts[str(bits)] = count counts[str(bitstring)] = int(count)
return { return {
"success": True, "success": True,
"counts": counts, "counts": counts,
"shots": shots, "shots": shots,
"total_counts": sum(counts.values()) "most_probable": result.most_probable()
} }
except Exception as e: except Exception as e:
@@ -297,30 +239,39 @@ class QuantumExecutor:
def observe(self, kernel_name: str, hamiltonian_terms: List[Dict], def observe(self, kernel_name: str, hamiltonian_terms: List[Dict],
shots: int = 1000, parameters: Optional[Dict] = None) -> Dict: shots: int = 1000, parameters: Optional[Dict] = None) -> Dict:
"""Compute expectation value of Hamiltonian""" """Calculate expectation value of Hamiltonian"""
if not CUDAQ_AVAILABLE: if kernel_name not in kernel_manager.kernels:
return {"error": "CUDA Quantum not available"}
if kernel_name not in self.kernel_manager.kernels:
return {"error": f"Kernel {kernel_name} not found"} return {"error": f"Kernel {kernel_name} not found"}
try: try:
kernel = self.kernel_manager.kernels[kernel_name] kernel = kernel_manager.kernels[kernel_name]
# Build Hamiltonian from terms # Build Hamiltonian from terms
hamiltonian = self._build_hamiltonian(hamiltonian_terms) hamiltonian = 0.0
for term in hamiltonian_terms:
coeff = term.get("coefficient", 1.0)
pauli_string = term.get("pauli_string", "")
if pauli_string:
hamiltonian += coeff * spin.z(0) # Simplified - should parse pauli_string
# Prepare parameters
args = []
if parameters: if parameters:
args = [parameters[p["name"]] for p in metadata = kernel_manager.kernel_metadata[kernel_name]
self.kernel_manager.kernel_metadata[kernel_name]["parameters"]] for param in metadata.get("parameters", []):
result = cudaq.observe(kernel, hamiltonian, *args, shots_count=shots) if param["name"] in parameters:
args.append(parameters[param["name"]])
# Calculate expectation value
if args:
expectation = cudaq.observe(kernel, hamiltonian, *args, shots_count=shots)
else: else:
result = cudaq.observe(kernel, hamiltonian, shots_count=shots) expectation = cudaq.observe(kernel, hamiltonian, shots_count=shots)
return { return {
"success": True, "success": True,
"expectation": result.expectation(), "expectation_value": float(expectation.expectation()),
"variance": result.variance() if hasattr(result, 'variance') else None,
"shots": shots "shots": shots
} }
@@ -330,126 +281,108 @@ class QuantumExecutor:
def get_state(self, kernel_name: str, parameters: Optional[Dict] = None) -> Dict: def get_state(self, kernel_name: str, parameters: Optional[Dict] = None) -> Dict:
"""Get quantum state vector""" """Get quantum state vector"""
if not CUDAQ_AVAILABLE: if kernel_name not in kernel_manager.kernels:
return {"error": "CUDA Quantum not available"}
if kernel_name not in self.kernel_manager.kernels:
return {"error": f"Kernel {kernel_name} not found"} return {"error": f"Kernel {kernel_name} not found"}
try: try:
kernel = self.kernel_manager.kernels[kernel_name] kernel = kernel_manager.kernels[kernel_name]
# Prepare parameters
args = []
if parameters: if parameters:
args = [parameters[p["name"]] for p in metadata = kernel_manager.kernel_metadata[kernel_name]
self.kernel_manager.kernel_metadata[kernel_name]["parameters"]] for param in metadata.get("parameters", []):
if param["name"] in parameters:
args.append(parameters[param["name"]])
# Get state vector
if args:
state = cudaq.get_state(kernel, *args) state = cudaq.get_state(kernel, *args)
else: else:
state = cudaq.get_state(kernel) state = cudaq.get_state(kernel)
# Convert state to serializable format # Convert to serializable format
state_array = np.array(state) state_vector = []
state_list = [] for amplitude in state:
for amplitude in state_array: state_vector.append({
state_list.append({
"real": float(amplitude.real), "real": float(amplitude.real),
"imag": float(amplitude.imag) "imag": float(amplitude.imag)
}) })
return { return {
"success": True, "success": True,
"state": state_list, "state_vector": state_vector,
"dimension": len(state_list) "num_qubits": len(state).bit_length() - 1
} }
except Exception as e: except Exception as e:
logger.error(f"Error getting state for kernel {kernel_name}: {e}") logger.error(f"Error getting state for kernel {kernel_name}: {e}")
return {"error": str(e), "traceback": traceback.format_exc()} return {"error": str(e), "traceback": traceback.format_exc()}
def _build_hamiltonian(self, terms: List[Dict]): def get_available_targets(self) -> Dict:
"""Build CUDA Quantum Hamiltonian from term list""" """Get list of available quantum targets"""
h = None try:
targets = cudaq.get_targets()
return {"success": True, "targets": list(targets)}
except Exception as e:
logger.error(f"Error getting available targets: {e}")
return {"error": str(e), "traceback": traceback.format_exc()}
for term in terms: def get_target_info(self) -> Dict:
paulis = term["paulis"] """Get information about current target"""
qubits = term["qubits"] try:
coeff_real = term["coefficient"]["real"] info = {
coeff_imag = term["coefficient"]["imag"] "current_target": self.current_target,
"available_targets": list(cudaq.get_targets())
}
return {"success": True, "info": info}
except Exception as e:
logger.error(f"Error getting target info: {e}")
return {"error": str(e), "traceback": traceback.format_exc()}
# Build Pauli string
pauli_term = None
for i, (pauli, qubit) in enumerate(zip(paulis, qubits)):
if pauli == 'I':
continue
elif pauli == 'X':
p = spin.x(qubit)
elif pauli == 'Y':
p = spin.y(qubit)
elif pauli == 'Z':
p = spin.z(qubit)
else:
raise ValueError(f"Invalid Pauli operator: {pauli}")
if pauli_term is None: def get_available_targets_standalone() -> Dict:
pauli_term = p """Standalone function to get available targets"""
else: try:
pauli_term = pauli_term * p targets = cudaq.get_targets()
return {"success": True, "targets": list(targets)}
except Exception as e:
logger.error(f"Error getting available targets: {e}")
return {"error": str(e), "traceback": traceback.format_exc()}
if pauli_term is not None:
# Apply coefficient
if coeff_imag != 0:
coeff = complex(coeff_real, coeff_imag)
else:
coeff = coeff_real
term_contribution = coeff * pauli_term def get_target_info_standalone() -> Dict:
"""Standalone function to get target info"""
try:
targets = cudaq.get_targets()
return {"success": True, "available_targets": list(targets)}
except Exception as e:
logger.error(f"Error getting target info: {e}")
return {"error": str(e), "traceback": traceback.format_exc()}
if h is None:
h = term_contribution
else:
h = h + term_contribution
return h if h is not None else 0.0 * spin.i(0) def get_platform_info() -> Dict:
"""Get platform and hardware information"""
try:
info = {
"cuda_quantum_version": "0.8.0", # Would get from cudaq.__version__ if available
"available_targets": list(cudaq.get_targets()),
"python_version": sys.version,
"platform": sys.platform
}
return {"success": True, "platform_info": info}
except Exception as e:
logger.error(f"Error getting platform info: {e}")
return {"error": str(e), "traceback": traceback.format_exc()}
# Global instances # Global instances
kernel_manager = QuantumKernelManager() kernel_manager = QuantumKernelManager()
executor = QuantumExecutor(kernel_manager) executor = QuantumExecutor()
# Add missing methods to executor class
def get_target_info_standalone() -> Dict:
"""Get information about available targets"""
if not CUDAQ_AVAILABLE:
return {"target": "mock", "mode": "mock"}
try:
available_targets = cudaq.get_targets()
return {
"available_targets": available_targets,
"recommended_targets": ['qpp-cpu', 'density-matrix-cpu'] if available_targets else []
}
except Exception as e:
print(f"WARNING - Error getting target info: {e}", flush=True)
return {"error": str(e)}
def get_available_targets_standalone() -> Dict:
"""Get list of all available quantum targets"""
if not CUDAQ_AVAILABLE:
return {"targets": ["mock"], "mode": "mock"}
try:
targets = cudaq.get_targets()
return {"targets": targets, "count": len(targets)}
except Exception as e:
print(f"WARNING - Error getting available targets: {e}", flush=True)
return {"targets": [], "error": str(e)}
def dispatch_command(command: str, **kwargs) -> Dict: def dispatch_command(command: str, **kwargs) -> Dict:
"""Main dispatch function for Python bridge commands""" """Main dispatch function for Python bridge commands"""
print(f"DEBUG - Command: {command}, Args: {list(kwargs.keys())}", flush=True)
try: try:
if command == "create_kernel": if command == "create_kernel":
return kernel_manager.create_kernel( return kernel_manager.create_kernel(
@@ -462,19 +395,16 @@ def dispatch_command(command: str, **kwargs) -> Dict:
return kernel_manager.apply_gate( return kernel_manager.apply_gate(
kwargs["kernel_name"], kwargs["kernel_name"],
kwargs["gate_name"], kwargs["gate_name"],
kwargs["qubits"], kwargs["target_qubits"],
kwargs.get("control_qubits"),
kwargs.get("parameters"), kwargs.get("parameters"),
kwargs.get("controls"),
kwargs.get("adjoint", False) kwargs.get("adjoint", False)
) )
elif command == "set_target": elif command == "set_target":
print(f"DEBUG - set_target called with kwargs: {kwargs}", flush=True)
target_name = kwargs.get("target") target_name = kwargs.get("target")
if target_name is None: if target_name is None:
print(f"ERROR - Missing 'target' parameter. Available params: {list(kwargs.keys())}", flush=True)
return {"error": "Missing required parameter 'target'", "available_params": list(kwargs.keys())} return {"error": "Missing required parameter 'target'", "available_params": list(kwargs.keys())}
print(f"DEBUG - Setting target to: {target_name}", flush=True)
return executor.set_target( return executor.set_target(
target_name, target_name,
**kwargs.get("configuration", {}) **kwargs.get("configuration", {})
@@ -515,20 +445,7 @@ def dispatch_command(command: str, **kwargs) -> Dict:
} }
elif command == "get_platform_info": elif command == "get_platform_info":
if not CUDAQ_AVAILABLE: return get_platform_info()
return {"error": "CUDA Quantum not available"}
try:
platform = cudaq.get_platform()
return {
"success": True,
"platform_name": platform.name(),
"num_qpus": platform.num_qpus(),
"is_simulator": platform.is_simulator(),
"is_remote": platform.is_remote()
}
except:
return {"success": True, "platform_name": "default"}
else: else:
return {"error": f"Unknown command: {command}"} return {"error": f"Unknown command: {command}"}
@@ -539,41 +456,40 @@ def dispatch_command(command: str, **kwargs) -> Dict:
def main(): def main():
"""Main entry point for standalone execution""" """Main event loop for Python bridge"""
if len(sys.argv) > 1: print("CUDA Quantum Python Bridge - Ready", flush=True)
# Command line mode
command_json = sys.argv[1] for line in sys.stdin:
try: try:
command_data = json.loads(command_json) request = json.loads(line.strip())
result = dispatch_command(**command_data) command = request.get("command")
print(json.dumps(result)) data = request.get("data", {})
request_id = request.get("requestId", "")
if command:
result = dispatch_command(command, **data)
response = {
"success": True,
"data": result,
"requestId": request_id
}
else:
response = {
"success": False,
"error": "No command specified",
"requestId": request_id
}
print(json.dumps(response), flush=True)
except Exception as e: except Exception as e:
print(json.dumps({"error": str(e), "traceback": traceback.format_exc()})) error_response = {
else: "success": False,
# Server mode - read from stdin for MCP communication "error": str(e),
print("CUDA Quantum Python Bridge - Ready") "traceback": traceback.format_exc(),
sys.stdout.flush() "requestId": request.get("requestId", "") if 'request' in locals() else ""
}
try: print(json.dumps(error_response), flush=True)
while True:
line = sys.stdin.readline()
if not line:
break
try:
command_data = json.loads(line.strip())
result = dispatch_command(**command_data)
print(json.dumps(result))
sys.stdout.flush()
except json.JSONDecodeError:
print(json.dumps({"error": "Invalid JSON"}))
sys.stdout.flush()
except Exception as e:
print(json.dumps({"error": str(e)}))
sys.stdout.flush()
except KeyboardInterrupt:
pass
if __name__ == "__main__": if __name__ == "__main__":

Ver fichero

@@ -248,6 +248,13 @@ export class PythonBridge extends EventEmitter {
}); });
} }
/**
* Get available quantum targets
*/
async getAvailableTargets(): Promise<PythonResponse> {
return this.sendCommand('get_available_targets');
}
/** /**
* Sample measurement results * Sample measurement results
*/ */

990
src/http-server.ts Archivo normal
Ver fichero

@@ -0,0 +1,990 @@
/**
* HTTP Server for CUDA Quantum MCP
* Provides REST API endpoints and Server-Sent Events for quantum computing operations
*/
import express, { Request, Response } from 'express';
import cors from 'cors';
import helmet from 'helmet';
import swaggerJsdoc from 'swagger-jsdoc';
import swaggerUiExpress from 'swagger-ui-express';
import { WebSocketServer, WebSocket } from 'ws';
import { createServer } from 'http';
import { initializePythonBridge, getPythonBridge } from './bridge/python-bridge.js';
import { Logger, LogLevel } from './utils/logger.js';
/**
* HTTP Server configuration
*/
interface HttpServerConfig {
port: number;
host: string;
corsOrigins: string[];
logLevel: LogLevel;
pythonPath?: string;
}
/**
* SSE Client interface
*/
interface SSEClient {
id: string;
response: Response;
subscriptions: Set<string>;
}
/**
* WebSocket Client interface
*/
interface WSClient {
id: string;
ws: WebSocket;
subscriptions: Set<string>;
}
/**
* Swagger API Documentation Configuration
*/
const swaggerOptions = {
definition: {
openapi: '3.0.0',
info: {
title: 'CUDA Quantum MCP API',
version: '1.0.0',
description: 'RESTful API for CUDA Quantum operations with GPU acceleration',
contact: {
name: 'MCP Quantum Team',
email: 'quantum@mcp.dev'
},
license: {
name: 'MIT',
url: 'https://opensource.org/licenses/MIT'
}
},
servers: [
{
url: 'http://localhost:3000',
description: 'Development server'
}
],
components: {
schemas: {
QuantumKernel: {
type: 'object',
required: ['name', 'num_qubits'],
properties: {
name: {
type: 'string',
description: 'Unique kernel identifier'
},
num_qubits: {
type: 'integer',
minimum: 1,
maximum: 64,
description: 'Number of qubits in the circuit'
},
parameters: {
type: 'array',
items: {
type: 'object',
properties: {
name: { type: 'string' },
type: { type: 'string', enum: ['float', 'int', 'complex', 'angle'] },
description: { type: 'string' }
}
}
}
}
},
QuantumGate: {
type: 'object',
required: ['kernel_name', 'gate_name', 'target_qubits'],
properties: {
kernel_name: { type: 'string' },
gate_name: { type: 'string', enum: ['h', 'x', 'y', 'z', 'cnot', 'rx', 'ry', 'rz', 's', 't'] },
target_qubits: {
type: 'array',
items: { type: 'integer', minimum: 0 }
},
control_qubits: {
type: 'array',
items: { type: 'integer', minimum: 0 }
},
parameters: {
type: 'array',
items: { type: 'number' }
},
adjoint: { type: 'boolean', default: false }
}
},
SampleRequest: {
type: 'object',
required: ['kernel_name'],
properties: {
kernel_name: { type: 'string' },
shots: { type: 'integer', minimum: 1, default: 1000 },
parameters: { type: 'object' }
}
},
ObserveRequest: {
type: 'object',
required: ['kernel_name', 'hamiltonian_terms'],
properties: {
kernel_name: { type: 'string' },
hamiltonian_terms: {
type: 'array',
items: {
type: 'object',
properties: {
coefficient: { type: 'number' },
pauli_string: { type: 'string' }
}
}
},
shots: { type: 'integer', minimum: 1, default: 1000 },
parameters: { type: 'object' }
}
},
TargetRequest: {
type: 'object',
required: ['target'],
properties: {
target: { type: 'string' },
configuration: { type: 'object' }
}
},
ApiResponse: {
type: 'object',
properties: {
success: { type: 'boolean' },
data: {},
error: { type: 'string' },
timestamp: { type: 'string', format: 'date-time' }
}
}
}
}
},
apis: ['./src/http-server.ts']
};
export class CudaQuantumHttpServer {
private app: express.Application;
private server!: any;
private wsServer!: WebSocketServer;
private logger: Logger;
private sseClients: Map<string, SSEClient>;
private wsClients: Map<string, WSClient>;
private config: HttpServerConfig;
constructor(config: HttpServerConfig) {
this.config = config;
this.app = express();
this.logger = new Logger('HttpServer', { level: config.logLevel });
this.sseClients = new Map();
this.wsClients = new Map();
this.setupMiddleware();
this.setupRoutes();
this.setupSwagger();
}
/**
* Setup Express middleware
*/
private setupMiddleware(): void {
// Security middleware
this.app.use(helmet({
contentSecurityPolicy: false // Allow Swagger UI
}));
// CORS configuration
this.app.use(cors({
origin: this.config.corsOrigins,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true
}));
// Body parsing
this.app.use(express.json({ limit: '10mb' }));
this.app.use(express.urlencoded({ extended: true }));
// Request logging
this.app.use((req, res, next) => {
this.logger.debug(`${req.method} ${req.path}`, {
ip: req.ip,
userAgent: req.get('User-Agent')
});
next();
});
}
/**
* Setup API routes with Swagger documentation
*/
private setupRoutes(): void {
// Health check
/**
* @swagger
* /health:
* get:
* summary: Health check endpoint
* tags: [System]
* responses:
* 200:
* description: Server is healthy
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ApiResponse'
*/
this.app.get('/health', (req, res) => {
res.json({
success: true,
data: {
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
},
timestamp: new Date().toISOString()
});
});
// Quantum Kernels
/**
* @swagger
* /api/kernels:
* post:
* summary: Create a new quantum kernel
* tags: [Quantum Kernels]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/QuantumKernel'
* responses:
* 200:
* description: Kernel created successfully
* 400:
* description: Invalid request parameters
*/
this.app.post('/api/kernels', this.handleCreateKernel.bind(this));
/**
* @swagger
* /api/kernels:
* get:
* summary: List all quantum kernels
* tags: [Quantum Kernels]
* responses:
* 200:
* description: List of kernels retrieved successfully
*/
this.app.get('/api/kernels', this.handleListKernels.bind(this));
// Quantum Gates
/**
* @swagger
* /api/gates:
* post:
* summary: Apply a quantum gate to a kernel
* tags: [Quantum Gates]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/QuantumGate'
* responses:
* 200:
* description: Gate applied successfully
*/
this.app.post('/api/gates', this.handleApplyGate.bind(this));
// Quantum Execution
/**
* @swagger
* /api/sample:
* post:
* summary: Sample quantum circuit measurements
* tags: [Quantum Execution]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/SampleRequest'
* responses:
* 200:
* description: Sampling completed successfully
*/
this.app.post('/api/sample', this.handleSample.bind(this));
/**
* @swagger
* /api/observe:
* post:
* summary: Calculate Hamiltonian expectation value
* tags: [Quantum Execution]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/ObserveRequest'
* responses:
* 200:
* description: Expectation value calculated successfully
*/
this.app.post('/api/observe', this.handleObserve.bind(this));
/**
* @swagger
* /api/state/{kernelName}:
* get:
* summary: Get quantum state vector
* tags: [Quantum Execution]
* parameters:
* - in: path
* name: kernelName
* required: true
* schema:
* type: string
* responses:
* 200:
* description: State vector retrieved successfully
*/
this.app.get('/api/state/:kernelName', this.handleGetState.bind(this));
// Quantum Backends
/**
* @swagger
* /api/targets:
* get:
* summary: List available quantum targets
* tags: [Quantum Backends]
* responses:
* 200:
* description: Available targets retrieved successfully
*/
this.app.get('/api/targets', this.handleGetTargets.bind(this));
/**
* @swagger
* /api/targets:
* post:
* summary: Set quantum computing target
* tags: [Quantum Backends]
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/TargetRequest'
* responses:
* 200:
* description: Target set successfully
*/
this.app.post('/api/targets', this.handleSetTarget.bind(this));
/**
* @swagger
* /api/platform:
* get:
* summary: Get platform information
* tags: [System]
* responses:
* 200:
* description: Platform info retrieved successfully
*/
this.app.get('/api/platform', this.handleGetPlatform.bind(this));
// Server-Sent Events
/**
* @swagger
* /api/events:
* get:
* summary: Server-Sent Events stream
* tags: [Real-time]
* parameters:
* - in: query
* name: topics
* schema:
* type: string
* description: Comma-separated list of topics to subscribe to
* responses:
* 200:
* description: SSE stream established
* content:
* text/event-stream:
* schema:
* type: string
*/
this.app.get('/api/events', this.handleSSE.bind(this));
}
/**
* Setup Swagger documentation
*/
private setupSwagger(): void {
const specs = swaggerJsdoc(swaggerOptions);
this.app.use('/api-docs', swaggerUiExpress.serve, swaggerUiExpress.setup(specs, {
explorer: true,
customSiteTitle: 'CUDA Quantum MCP API Documentation',
customfavIcon: '/favicon.ico'
}));
// Serve raw OpenAPI spec
this.app.get('/api-docs.json', (req, res) => {
res.json(specs);
});
}
/**
* Handle kernel creation
*/
private async handleCreateKernel(req: Request, res: Response): Promise<void> {
try {
const { name, num_qubits, parameters } = req.body;
if (!name || !num_qubits) {
res.status(400).json({
success: false,
error: 'Missing required parameters: name, num_qubits',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.createKernel(name, num_qubits, parameters);
// Broadcast to SSE clients
this.broadcastSSE('kernel_created', { name, num_qubits, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error creating kernel:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle kernel listing
*/
private async handleListKernels(req: Request, res: Response): Promise<void> {
try {
const bridge = getPythonBridge();
const result = await bridge.listKernels();
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error listing kernels:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle gate application
*/
private async handleApplyGate(req: Request, res: Response): Promise<void> {
try {
const { kernel_name, gate_name, target_qubits, control_qubits, parameters, adjoint } = req.body;
if (!kernel_name || !gate_name || !target_qubits) {
res.status(400).json({
success: false,
error: 'Missing required parameters: kernel_name, gate_name, target_qubits',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.applyGate(
kernel_name, gate_name, target_qubits, control_qubits, parameters, adjoint
);
// Broadcast to SSE clients
this.broadcastSSE('gate_applied', { kernel_name, gate_name, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error applying gate:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle quantum sampling
*/
private async handleSample(req: Request, res: Response): Promise<void> {
try {
const { kernel_name, shots = 1000, parameters } = req.body;
if (!kernel_name) {
res.status(400).json({
success: false,
error: 'Missing required parameter: kernel_name',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.sample(kernel_name, shots, parameters);
// Broadcast to SSE clients
this.broadcastSSE('sampling_completed', { kernel_name, shots, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error sampling:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle Hamiltonian observation
*/
private async handleObserve(req: Request, res: Response): Promise<void> {
try {
const { kernel_name, hamiltonian_terms, shots = 1000, parameters } = req.body;
if (!kernel_name || !hamiltonian_terms) {
res.status(400).json({
success: false,
error: 'Missing required parameters: kernel_name, hamiltonian_terms',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.observe(kernel_name, hamiltonian_terms, shots, parameters);
// Broadcast to SSE clients
this.broadcastSSE('observation_completed', { kernel_name, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error observing:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle state vector retrieval
*/
private async handleGetState(req: Request, res: Response): Promise<void> {
try {
const { kernelName } = req.params;
const { parameters } = req.query;
if (!kernelName) {
res.status(400).json({
success: false,
error: 'Missing required parameter: kernelName',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.getState(
kernelName,
parameters ? JSON.parse(parameters as string) : undefined
);
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error getting state:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle target listing
*/
private async handleGetTargets(req: Request, res: Response): Promise<void> {
try {
const bridge = getPythonBridge();
const result = await bridge.getAvailableTargets();
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error getting targets:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle target setting
*/
private async handleSetTarget(req: Request, res: Response): Promise<void> {
try {
const { target, configuration } = req.body;
if (!target) {
res.status(400).json({
success: false,
error: 'Missing required parameter: target',
timestamp: new Date().toISOString()
});
return;
}
const bridge = getPythonBridge();
const result = await bridge.setTarget(target, configuration);
// Broadcast to SSE clients
this.broadcastSSE('target_changed', { target, result });
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error setting target:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle platform info
*/
private async handleGetPlatform(req: Request, res: Response): Promise<void> {
try {
const bridge = getPythonBridge();
const result = await bridge.getPlatformInfo();
res.json({
success: true,
data: result,
timestamp: new Date().toISOString()
});
} catch (error) {
this.logger.error('Error getting platform info:', error);
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString()
});
}
}
/**
* Handle Server-Sent Events
*/
private handleSSE(req: Request, res: Response): void {
const clientId = Math.random().toString(36).substring(2, 15);
const topics = req.query.topics ? (req.query.topics as string).split(',') : ['all'];
// Setup SSE headers
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': 'Cache-Control'
});
// Store client
const client: SSEClient = {
id: clientId,
response: res,
subscriptions: new Set(topics)
};
this.sseClients.set(clientId, client);
// Send initial connection event
res.write(`data: ${JSON.stringify({
type: 'connection',
clientId: clientId,
timestamp: new Date().toISOString()
})}\\n\\n`);
// Handle client disconnect
req.on('close', () => {
this.sseClients.delete(clientId);
this.logger.debug(`SSE client ${clientId} disconnected`);
});
this.logger.debug(`SSE client ${clientId} connected with topics: ${topics.join(', ')}`);
}
/**
* Broadcast event to SSE clients
*/
private broadcastSSE(eventType: string, data: any): void {
const message = {
type: eventType,
data: data,
timestamp: new Date().toISOString()
};
for (const [clientId, client] of this.sseClients) {
if (client.subscriptions.has('all') || client.subscriptions.has(eventType)) {
try {
client.response.write(`data: ${JSON.stringify(message)}\\n\\n`);
} catch (error) {
this.logger.warn(`Failed to send SSE to client ${clientId}:`, error);
this.sseClients.delete(clientId);
}
}
}
}
/**
* Setup WebSocket server
*/
private setupWebSocket(): void {
this.wsServer.on('connection', (ws: WebSocket, req: any) => {
const clientId = Math.random().toString(36).substring(2, 15);
const client: WSClient = {
id: clientId,
ws: ws,
subscriptions: new Set(['all'])
};
this.wsClients.set(clientId, client);
this.logger.debug(`WebSocket client ${clientId} connected`);
// Send welcome message
ws.send(JSON.stringify({
type: 'connection',
clientId: clientId,
timestamp: new Date().toISOString()
}));
// Handle messages
ws.on('message', (message: Buffer) => {
try {
const data = JSON.parse(message.toString());
this.handleWebSocketMessage(clientId, data);
} catch (error) {
this.logger.warn(`Invalid WebSocket message from ${clientId}:`, error);
}
});
// Handle disconnect
ws.on('close', () => {
this.wsClients.delete(clientId);
this.logger.debug(`WebSocket client ${clientId} disconnected`);
});
ws.on('error', (error: any) => {
this.logger.error(`WebSocket error for client ${clientId}:`, error);
this.wsClients.delete(clientId);
});
});
}
/**
* Handle WebSocket messages
*/
private handleWebSocketMessage(clientId: string, message: any): void {
const client = this.wsClients.get(clientId);
if (!client) return;
switch (message.type) {
case 'subscribe':
if (message.topics && Array.isArray(message.topics)) {
message.topics.forEach((topic: string) => {
client.subscriptions.add(topic);
});
}
break;
case 'unsubscribe':
if (message.topics && Array.isArray(message.topics)) {
message.topics.forEach((topic: string) => {
client.subscriptions.delete(topic);
});
}
break;
}
}
/**
* Start the HTTP server
*/
async start(): Promise<void> {
try {
// Initialize Python bridge
this.logger.info('Initializing Python bridge...');
await initializePythonBridge({ pythonPath: this.config.pythonPath });
this.logger.info('Python bridge initialized successfully');
// Create HTTP server
this.server = createServer(this.app);
// Setup WebSocket server
this.wsServer = new WebSocketServer({ server: this.server });
this.setupWebSocket();
// Start listening
await new Promise<void>((resolve, reject) => {
this.server.listen(this.config.port, this.config.host, () => {
resolve();
}).on('error', reject);
});
this.logger.info(`CUDA Quantum HTTP Server started on ${this.config.host}:${this.config.port}`);
this.logger.info(`API Documentation: http://${this.config.host}:${this.config.port}/api-docs`);
this.logger.info(`Health Check: http://${this.config.host}:${this.config.port}/health`);
this.logger.info(`SSE Endpoint: http://${this.config.host}:${this.config.port}/api/events`);
} catch (error) {
this.logger.error('Failed to start HTTP server:', error);
throw error;
}
}
/**
* Stop the HTTP server
*/
async stop(): Promise<void> {
this.logger.info('Shutting down HTTP server...');
// Close WebSocket connections
for (const [clientId, client] of this.wsClients) {
client.ws.close();
}
this.wsClients.clear();
// Close SSE connections
for (const [clientId, client] of this.sseClients) {
client.response.end();
}
this.sseClients.clear();
// Close WebSocket server
if (this.wsServer) {
this.wsServer.close();
}
// Close HTTP server
if (this.server) {
await new Promise<void>((resolve) => {
this.server.close(() => resolve());
});
}
// Close Python bridge
const bridge = getPythonBridge();
if (bridge) {
await bridge.close();
}
this.logger.info('HTTP server shutdown complete');
}
}
/**
* Main entry point for HTTP server
*/
async function main(): Promise<void> {
const config: HttpServerConfig = {
port: parseInt(process.env.HTTP_PORT || '3000'),
host: process.env.HTTP_HOST || 'localhost',
corsOrigins: process.env.CORS_ORIGINS ? process.env.CORS_ORIGINS.split(',') : ['*'],
pythonPath: process.env.CUDAQ_PYTHON_PATH,
logLevel: process.env.LOG_LEVEL === 'debug' ? LogLevel.DEBUG :
process.env.LOG_LEVEL === 'warn' ? LogLevel.WARN :
process.env.LOG_LEVEL === 'error' ? LogLevel.ERROR :
LogLevel.INFO
};
const server = new CudaQuantumHttpServer(config);
// Handle graceful shutdown
process.on('SIGTERM', async () => {
await server.stop();
process.exit(0);
});
process.on('SIGINT', async () => {
await server.stop();
process.exit(0);
});
try {
await server.start();
} catch (error) {
console.error('Failed to start CUDA Quantum HTTP Server:', error);
process.exit(1);
}
}
// Start the server if this file is run directly
if (import.meta.url === `file://${process.argv[1]}`) {
main().catch((error) => {
console.error('Fatal error:', error);
process.exit(1);
});
}