Skip to content

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_TOKEN env 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_TOKEN and DWAVE_API_ENDPOINT env 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>