-
Notifications
You must be signed in to change notification settings - Fork 4
add the Unit-Disk Mapping Module #125
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
b171769
93035f0
c39c025
54ec5b6
4fa5c48
37059e7
143bd61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,11 @@ | ||
from qamomile.core import circuit as circuit_module | ||
from qamomile.core.bitssample import * # noqa | ||
from qamomile.core.converters import qaoa as qaoa | ||
from qamomile.core.ising_qubo import IsingModel, UnitDiskGraph | ||
|
||
circuit = circuit_module | ||
|
||
__all__ = ["qaoa", "circuit", "BitsSample", "BitsSampleSet"] | ||
__all__ = [ | ||
"qaoa", "circuit", "BitsSample", "BitsSampleSet", | ||
"IsingModel", "UnitDiskGraph" | ||
] |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -1,7 +1,8 @@ | ||||
import dataclasses | ||||
import typing as typ | ||||
|
||||
import copy | ||||
import numpy as np | ||||
from ..udm import map_qubo, solve_qubo, qubo_result_to_networkx, QUBOResult | ||||
|
||||
|
||||
@dataclasses.dataclass | ||||
|
@@ -116,6 +117,139 @@ def normalize_by_rms(self): | |||
self.linear[key] /= normalization_factor | ||||
for key in self.quad: | ||||
self.quad[key] /= normalization_factor | ||||
|
||||
def to_unit_disk_graph(self, normalize: bool = True) -> 'UnitDiskGraph': | ||||
"""Convert the Ising model to a unit disk graph representation. | ||||
|
||||
Args: | ||||
normalize: Whether to normalize the coefficients before conversion | ||||
|
||||
Returns: | ||||
UnitDiskGraph object representing the Ising model | ||||
""" | ||||
if normalize: | ||||
# Create a copy of the model to avoid modifying the original | ||||
model = IsingModel( | ||||
quad=copy.deepcopy(self.quad), | ||||
linear=copy.deepcopy(self.linear), | ||||
constant=self.constant, | ||||
index_map=copy.deepcopy(self.index_map) if self.index_map else None | ||||
) | ||||
model.normalize_by_abs_max() | ||||
else: | ||||
model = self | ||||
|
||||
return UnitDiskGraph(model) | ||||
|
||||
|
||||
@dataclasses.dataclass | ||||
class UnitDiskGraph: | ||||
""" | ||||
A representation of an Ising model as a unit disk graph suitable for neutral atom quantum computers. | ||||
|
||||
This class wraps the UDM module to map QUBO/Ising problems to unit disk graphs. | ||||
""" | ||||
ising_model: IsingModel | ||||
_qubo_result: typ.Optional[QUBOResult] = None | ||||
_networkx_graph: typ.Optional[object] = None | ||||
_delta: typ.Optional[float] = None | ||||
|
||||
def __post_init__(self): | ||||
"""Initialize the unit disk graph mapping after construction.""" | ||||
self._create_mapping() | ||||
|
||||
def _create_mapping(self): | ||||
"""Create the unit disk graph mapping from the Ising model.""" | ||||
# Convert the Ising model to J and h matrices/vectors | ||||
n = self.ising_model.num_bits() | ||||
|
||||
# Initialize J matrix and h vector | ||||
J = np.zeros((n, n)) | ||||
h = np.zeros(n) | ||||
|
||||
# Fill in the J matrix from quad terms | ||||
for (i, j), value in self.ising_model.quad.items(): | ||||
J[i, j] = value | ||||
J[j, i] = value # Make symmetric | ||||
|
||||
# Fill in the h vector from linear terms | ||||
for i, value in self.ising_model.linear.items(): | ||||
h[i] = value | ||||
|
||||
# Calculate delta parameter for weight scaling | ||||
self._delta = 1.5 * max(np.max(np.abs(h)), np.max(np.abs(J))) | ||||
|
||||
# Map the QUBO problem to a unit disk graph | ||||
self._qubo_result = map_qubo(J, h, self._delta) | ||||
|
||||
# Convert to a NetworkX graph for visualization and analysis | ||||
self._networkx_graph = qubo_result_to_networkx(self._qubo_result) | ||||
|
||||
def solve(self, use_brute_force: bool = False, binary_variables: bool = False) -> dict: | ||||
""" | ||||
Solve the Ising model using the unit disk graph mapping. | ||||
|
||||
Args: | ||||
use_brute_force: Whether to use brute force enumeration for small problems | ||||
binary_variables: Whether to use {0,1} variables (True) or {-1,1} variables (False) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in Qamomile workflow, library only deals with Ising model (spin variables), so you need to care about {0,1} case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I removed the {0,1} encoding option. It was for other type of problem. |
||||
|
||||
Returns: | ||||
Dictionary containing solution information including: | ||||
- original_config: Configuration for the original Ising variables | ||||
- energy: Energy of the solution | ||||
- solution_method: Method used to find the solution ("brute_force" or "mwis") | ||||
""" | ||||
# Convert to J and h matrices/vectors | ||||
n = self.ising_model.num_bits() | ||||
|
||||
J = np.zeros((n, n)) | ||||
h = np.zeros(n) | ||||
|
||||
for (i, j), value in self.ising_model.quad.items(): | ||||
J[i, j] = value | ||||
J[j, i] = value | ||||
|
||||
for i, value in self.ising_model.linear.items(): | ||||
h[i] = value | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there big reasons to create matrix version of Ising model?
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok. I use |
||||
|
||||
# Solve the QUBO problem | ||||
result = solve_qubo( | ||||
J, h, | ||||
binary_variables=binary_variables, | ||||
use_brute_force=use_brute_force, | ||||
max_brute_force_size=20 # Adjust this value based on performance needs | ||||
) | ||||
|
||||
return result | ||||
|
||||
@property | ||||
def qubo_result(self) -> QUBOResult: | ||||
"""Get the QUBOResult object from the UDM module.""" | ||||
return self._qubo_result | ||||
|
||||
@property | ||||
def networkx_graph(self) -> object: | ||||
"""Get the NetworkX graph representation.""" | ||||
return self._networkx_graph | ||||
|
||||
@property | ||||
def pins(self) -> list: | ||||
"""Get the list of pins (indices of nodes corresponding to original variables).""" | ||||
if self._qubo_result: | ||||
return self._qubo_result.pins | ||||
return [] | ||||
|
||||
@property | ||||
def nodes(self) -> list: | ||||
"""Get the list of nodes in the unit disk graph.""" | ||||
if self._qubo_result and hasattr(self._qubo_result.grid_graph, 'nodes'): | ||||
return self._qubo_result.grid_graph.nodes | ||||
return [] | ||||
|
||||
@property | ||||
def delta(self) -> float: | ||||
"""Get the delta parameter used for scaling the weights.""" | ||||
return self._delta | ||||
|
||||
|
||||
def calc_qubo_energy(qubo: dict[tuple[int, int], float], state: list[int]) -> float: | ||||
|
@@ -201,4 +335,4 @@ def qubo_to_ising( | |||
ising_J = _J | ||||
ising_h = _h | ||||
return IsingModel(ising_J, ising_h, constant, index_map) | ||||
return IsingModel(ising_J, ising_h, constant) | ||||
return IsingModel(ising_J, ising_h, constant) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
""" | ||
Unit Disk Mapping (UDM) module for Qamomile. | ||
|
||
This module implements algorithms for mapping various optimization problems (like QUBO and Ising models) | ||
to unit disk grid graphs, which can be naturally encoded in neutral-atom quantum computers. | ||
""" | ||
|
||
from .dragondrop import ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The module name There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed from |
||
map_qubo, | ||
map_simple_wmis, | ||
solve_qubo, | ||
solve_mwis_scipy, | ||
qubo_result_to_networkx, | ||
QUBOResult, | ||
WMISResult | ||
) | ||
|
||
__all__ = [ | ||
"map_qubo", "map_simple_wmis", "solve_qubo", | ||
"solve_mwis_scipy", "qubo_result_to_networkx", | ||
"QUBOResult", "WMISResult", | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think
udm
is independent subpackage, so it should not use incore
.It is better to move this to inside of
udm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Now the entire udm related files are outside of the core. I created a
Ising_UnitDiskGraph
in the udm module instead.