Subgraph Isomorphism Example
The Subgraph Isomorphism problem determines whether a smaller pattern graph can be found as a subgraph of a larger target graph, preserving edges. It is NP-complete and used in pattern recognition and database querying.
import getpass
import os
import numpy as np
from dotenv import load_dotenv
from luna_quantum.algorithms import SCIP
from luna_usecases.subgraph_isomorphism import (
SubgraphIsomorphismCollection,
SubgraphIsomorphismData,
SubgraphIsomorphismFormulation,
SubgraphIsomorphismInstance,
)
load_dotenv()
if "LUNA_API_KEY" not in os.environ:
os.environ["LUNA_API_KEY"] = getpass.getpass("Enter your Luna API key: ")
Create Data
Define a 3-node triangle pattern and a 5-node target graph containing it.
adj_pattern = np.array(
[
[0, 1, 1],
[1, 0, 1],
[1, 1, 0],
]
)
adj_target = np.array(
[
[0, 1, 1, 0, 0],
[1, 0, 1, 1, 0],
[1, 1, 0, 0, 1],
[0, 1, 0, 0, 1],
[0, 0, 1, 1, 0],
]
)
node_names_pattern = ["P1", "P2", "P3"]
node_names_target = ["A", "B", "C", "D", "E"]
data = SubgraphIsomorphismData.from_adjacency_matrices(
adjacency_matrix_pattern=adj_pattern,
adjacency_matrix_target=adj_target,
node_names_pattern=node_names_pattern,
node_names_target=node_names_target,
)
print(data.to_string())
Plot Data
Visualize the pattern and target graphs.
Create Formulation
Find a mapping of pattern nodes into target nodes that preserves all edges.
Subgraph Isomorphism Formulation (undirected graphs):
Pattern nodes: 3, Target nodes: 5
Index:
i, k -- pattern node indices with i < k, i = 0, ..., 2
j, m -- target node indices with j < m, j = 0, ..., 4
Decision Variables:
x[i,j] in {0,1} for all i, j
x[i,j] = 1 if pattern node i maps to target node j
Total: 15 binary variables
Objective:
None (feasibility problem)
Constraints:
1. Each pattern node maps to exactly one target node (3 constraints):
sum_j x[i,j] == 1 for all i = 0, ..., 2
2. Each target node mapped from at most one pattern node (5 constraints):
sum_i x[i,j] <= 1 for all j = 0, ..., 4
3. Edge preservation (12 constraints, undirected pairs):
x[i,j] + x[k,m] <= 1
for edge (i,k) in pattern with i < k
and non-edge (j,m) in target with j < m
Create Instance
Combine data and formulation into a solvable instance.
instance = SubgraphIsomorphismInstance(data=data, formulation=formulation)
print(instance.to_string())
Data:Subgraph Isomorphism Data:
Pattern: 3 nodes, 3 edges
Target: 5 nodes, 6 edges
Formulation:Subgraph Isomorphism Formulation (undirected graphs):
Pattern nodes: 3, Target nodes: 5
Index:
i, k -- pattern node indices with i < k, i = 0, ..., 2
j, m -- target node indices with j < m, j = 0, ..., 4
Decision Variables:
x[i,j] in {0,1} for all i, j
x[i,j] = 1 if pattern node i maps to target node j
Total: 15 binary variables
Objective:
None (feasibility problem)
Constraints:
1. Each pattern node maps to exactly one target node (3 constraints):
sum_j x[i,j] == 1 for all i = 0, ..., 2
2. Each target node mapped from at most one pattern node (5 constraints):
sum_i x[i,j] <= 1 for all j = 0, ..., 4
3. Edge preservation (12 constraints, undirected pairs):
x[i,j] + x[k,m] <= 1
for edge (i,k) in pattern with i < k
and non-edge (j,m) in target with j < m
Formulate Model
Translate the instance into a mathematical optimization model.
Solve and Interpret
Solve the model with SCIP and interpret the raw result into a use-case-specific solution.
scip = SCIP()
job = scip.run(model)
sol = job.result()
uc_solution = instance.interpret(sol)
print(uc_solution.to_string())
/Users/maximilianjanetschek/PycharmProjects/luna-usecases/.venv/lib/python3.13/site-packages/rich/live.py:260:
UserWarning: install "ipywidgets" for Jupyter support
warnings.warn('install "ipywidgets" for Jupyter support')
2026-05-29 11:35:57 INFO Sleeping for 5.0 seconds. Waiting and checking a function in a loop.
Plot Solution
Visualize the optimal solution.
Collections
Generate a benchmark collection of random instances for batch processing.
collection = SubgraphIsomorphismCollection.from_random(
min_pattern=3,
max_pattern=5,
num_instances=2,
seed=42,
)
model = collection.instances[0].formulate()
print(model)
Model: subgraph_isomorphism<subgraph_isomorphism>
Minimize
Subject To
pattern_node_0: x_0_0 + x_0_1 + x_0_2 + x_0_3 + x_0_4 == 1
pattern_node_1: x_1_0 + x_1_1 + x_1_2 + x_1_3 + x_1_4 == 1
pattern_node_2: x_2_0 + x_2_1 + x_2_2 + x_2_3 + x_2_4 == 1
target_node_0: x_0_0 + x_1_0 + x_2_0 <= 1
target_node_1: x_0_1 + x_1_1 + x_2_1 <= 1
target_node_2: x_0_2 + x_1_2 + x_2_2 <= 1
target_node_3: x_0_3 + x_1_3 + x_2_3 <= 1
target_node_4: x_0_4 + x_1_4 + x_2_4 <= 1
edge_pres_0_1_0_3: x_0_0 + x_1_3 <= 1
edge_pres_0_1_0_4: x_0_0 + x_1_4 <= 1
edge_pres_0_1_2_3: x_0_2 + x_1_3 <= 1
edge_pres_0_1_2_4: x_0_2 + x_1_4 <= 1
edge_pres_0_1_3_4: x_0_3 + x_1_4 <= 1
edge_pres_0_2_0_3: x_0_0 + x_2_3 <= 1
edge_pres_0_2_0_4: x_0_0 + x_2_4 <= 1
edge_pres_0_2_2_3: x_0_2 + x_2_3 <= 1
edge_pres_0_2_2_4: x_0_2 + x_2_4 <= 1
edge_pres_0_2_3_4: x_0_3 + x_2_4 <= 1
edge_pres_1_2_0_3: x_1_0 + x_2_3 <= 1
edge_pres_1_2_0_4: x_1_0 + x_2_4 <= 1
edge_pres_1_2_2_3: x_1_2 + x_2_3 <= 1
edge_pres_1_2_2_4: x_1_2 + x_2_4 <= 1
edge_pres_1_2_3_4: x_1_3 + x_2_4 <= 1
Binary
x_0_0 x_0_1 x_0_2 x_0_3 x_0_4 x_1_0 x_1_1 x_1_2 x_1_3 x_1_4 x_2_0 x_2_1 x_2_2
x_2_3 x_2_4