SDK Solver Plugin Development Guide
[!NOTE] See Feature Implementation Status (Remaining Functionality) for the latest implementation status.
Overview
EvoSpikeNet provides a SolverPlugin mechanism that allows any combinatorial optimisation,
QAOA, or quantum-annealing backend to be swapped in as a plugin.
Typical use cases: - Classical SA / Greedy — default for real-time constrained scenarios - QAOA Simulator — prototype evaluation with classical approximation - Qiskit (IBMQ / Aer) — quantum hardware connectivity - D-Wave — quantum annealing device connectivity
Architecture
QPFCQAOABridge
└─ get_optimizer(name) # evospikenet/optimizer.py
├─ PluginFactory.SOLVER # plugin registry (checked first)
│ ├─ "classical" → ClassicalSolverPlugin (SA)
│ ├─ "qaoa_simulator" → QAOASimulatorSolverPlugin
│ ├─ "qiskit" → QiskitSolverPlugin (Aer / IBMQ)
│ └─ "dwave" → DWaveSolverPlugin (QPU / SA fallback)
└─ built-in backends (fallback)
Installation
Minimal (classical / qaoa_simulator only):
pip install -e .
Enable Qiskit backend:
pip install qiskit qiskit-aer
Enable D-Wave backend:
pip install dwave-ocean-sdk
SolverPlugin Interface
Required methods:
| Method | Description |
|---|---|
get_metadata() |
Return PluginMetadata with plugin_type=PluginType.SOLVER |
initialize() |
Load dependencies, verify device connectivity |
activate() |
Transition plugin to active state |
deactivate() |
Release resources |
solve(problem, timeout, options) |
Run optimisation, return sorted candidate list |
solve() signature and return value:
def solve(
self,
problem: Dict[str, Any], # vehicles, stops, distances, etc.
timeout: float = 5.0, # seconds
options: Dict[str, Any] = None, # solver-specific parameters
) -> List[Dict[str, Any]]:
# Returns candidates sorted by ascending score (lower is better)
# Each element: {"assignment": {...}, "score": float, "meta": {...}}
Creating a Custom Solver Plugin
1. Subclass SolverPlugin
from evospikenet.plugins.solver_plugin import SolverPlugin
from evospikenet.plugins import PluginMetadata, PluginStatus, PluginType
class MyAnnealerPlugin(SolverPlugin):
def get_metadata(self) -> PluginMetadata:
return PluginMetadata(
name="my_annealer",
version="1.0.0",
plugin_type=PluginType.SOLVER,
description="Custom annealing backend",
author="Your Team",
)
def initialize(self) -> bool:
self.status = PluginStatus.INITIALIZED
return True
def activate(self) -> bool:
self.status = PluginStatus.ACTIVE
return True
def deactivate(self) -> bool:
self.status = PluginStatus.UNLOADED
return True
def solve(self, problem, timeout=5.0, options=None):
# Implement: optimise problem and return candidates
...
return [{"assignment": result, "score": cost, "meta": {"backend": "my_annealer"}}]
2. Register with PluginFactory
import evospikenet.plugin_factory as _pf_mod
from evospikenet.optimizer import get_optimizer
factory = _pf_mod._global_factory # or PluginFactory()
plugin = MyAnnealerPlugin()
plugin.initialize()
plugin.activate()
factory.register_plugin(plugin)
# Now accessible by name
opt = get_optimizer("my_annealer")
result = opt.solve(problem, timeout=5.0)
3. Allow custom module via EVOSPIKENET_PLUGIN_ALLOWLIST
export EVOSPIKENET_PLUGIN_ALLOWLIST="evospikenet.plugins,myorg.plugins"
Built-in Backend Details
classical (SimulatedAnnealing)
- Dependencies: none
- Notes: Random-swap SA. Default for real-time scenarios.
- options:
seed(int)
qaoa_simulator
- Dependencies: none
- Notes: Classical QAOA approximation using SA/SPSA/greedy.
- options:
seed(int)
qiskit
- Dependencies:
qiskit,qiskit-aer - Notes: Local Aer simulator or IBMQ cloud QPU. VRP→Pauli mapping not yet implemented; falls back to classical.
- Auth: Set
IBMQ_TOKENenv var. - options:
provider,token_env,reps,shots
export IBMQ_TOKEN="<your_ibmq_token>"
dwave
- Dependencies:
dwave-ocean-sdk - Notes: D-Wave QPU / SimulatedAnnealing / Tabu sampler. Falls back to SA when SDK is absent.
- Auth: Set
DWAVE_API_TOKENandDWAVE_API_ENDPOINTenv vars. - options:
sampler("QPU"|"SimulatedAnnealing"|"Tabu"),num_reads
export DWAVE_API_TOKEN="<your_dwave_token>"
Integration with Q-PFC
Pass the backend name directly to QPFCQAOABridge:
from evospikenet.qpfc_qaoa_bridge import QPFCQAOABridge
bridge = QPFCQAOABridge(backend="classical") # or "qaoa_simulator", "qiskit", "dwave"
candidates = bridge.propose(vehicles=..., stops=..., distances_callable=...,
uncertainty={"global": 0.3}, risk=0.4, deadline=10.0)
Benchmarking
# 100 trials × 4 sizes × 2 backends
PYTHONPATH=.:EvoSpikeNet-Core python EvoSpikeNet-Core/scripts/bench_qpfc_qaoa_bulk.py --n 100
# View effect-size report
cat EvoSpikeNet-Core/bench_reports/qpfc_qaoa_effect_sizes.md
Verified Backends (June 2026)
| Backend | Verification | meta.backend |
|---|---|---|
classical |
SA, no fallback needed | simulated_annealing |
qaoa_simulator |
Classical SA/SPSA approximation | qaoa_simulator |
openjij |
OpenJij SASampler on real hardware (no token) | openjij_sa |
fixstars_amplify |
Fixstars Amplify AE live connection (.env token auto-load) |
fixstars_amplify_fixstars |
dwave |
SA fallback when dwave-ocean-sdk absent |
simulated_annealing |
qiskit |
SA fallback when IBMQ_TOKEN not set |
qiskit_fallback |
Environment Variables / .env Configuration
Place secrets in Products/.env (already listed in .gitignore).
evospikenet.optimizer loads this file automatically at import time.
# Products/.env
FIXSTARS_TOKEN=<your_fixstars_amplify_token>
IBMQ_TOKEN=<your_ibmq_token>
DWAVE_API_TOKEN=<your_dwave_token>
DWAVE_API_ENDPOINT=<your_dwave_endpoint>