11
package.json
11
package.json
@@ -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",
|
||||||
|
|||||||
@@ -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:
|
||||||
|
pass
|
||||||
|
logger.error(f"Error setting target {target_name}: {e}")
|
||||||
return {"error": str(e), "traceback": traceback.format_exc()}
|
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", {})
|
||||||
except Exception as e:
|
request_id = request.get("requestId", "")
|
||||||
print(json.dumps({"error": str(e), "traceback": traceback.format_exc()}))
|
|
||||||
|
if command:
|
||||||
|
result = dispatch_command(command, **data)
|
||||||
|
response = {
|
||||||
|
"success": True,
|
||||||
|
"data": result,
|
||||||
|
"requestId": request_id
|
||||||
|
}
|
||||||
else:
|
else:
|
||||||
# Server mode - read from stdin for MCP communication
|
response = {
|
||||||
print("CUDA Quantum Python Bridge - Ready")
|
"success": False,
|
||||||
sys.stdout.flush()
|
"error": "No command specified",
|
||||||
|
"requestId": request_id
|
||||||
|
}
|
||||||
|
|
||||||
try:
|
print(json.dumps(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:
|
except Exception as e:
|
||||||
print(json.dumps({"error": str(e)}))
|
error_response = {
|
||||||
sys.stdout.flush()
|
"success": False,
|
||||||
|
"error": str(e),
|
||||||
except KeyboardInterrupt:
|
"traceback": traceback.format_exc(),
|
||||||
pass
|
"requestId": request.get("requestId", "") if 'request' in locals() else ""
|
||||||
|
}
|
||||||
|
print(json.dumps(error_response), flush=True)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@@ -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
990
src/http-server.ts
Archivo normal
@@ -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);
|
||||||
|
});
|
||||||
|
}
|
||||||
Referencia en una nueva incidencia
Block a user