# Copyright 2023 Jij Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import annotations
from dimod import SPIN
import openjij
import openjij as oj
from openjij.model.model import make_BinaryQuadraticModel
[docs]
def make_KingGraph(linear=None, quadratic=None, king_graph=None):
"""KingGraph factory
Returns:
generated KingGraph class
"""
mock_linear = {}
mock_quadratic = {}
if linear is not None:
mock_linear = linear
if quadratic is not None:
mock_quadratic = quadratic
if mock_linear == {} and mock_quadratic == {}:
# no elements in linear and quadratic
# fetch first element of the king_graph
label = (king_graph[0][0], king_graph[0][0])
# add to linear
mock_linear[label] = 1.0
class KingGraph(make_BinaryQuadraticModel(mock_linear, mock_quadratic)):
"""
BQM for king graph of HITACHI CMOS Annealer
Attributes:
xrange (list(int)): represents hardware (CMOS) restricts for coordinate. [xmin, xmax]
yrange (list(int)): represents hardware (CMOS) restricts for coordinate. [ymin, ymax]
prange (list(int)): represents hardware (CMOS) restricts for the strength of interactions 'p'. [pmin, pmax]
king_graph (list(int)):
Annealing cloud Web API format representation of interaction coefficients
Quadratic term [x1, y1, x2, y2, value]
Linear term [x1, y1, x1, y1, value]
"""
def __init__(
self,
linear: dict = None,
quadratic: dict = None,
offset: float = 0.0,
king_graph=None,
vartype=SPIN,
machine_type: str = "",
):
"""__init__.
Args:
linear (dict): linear biases
quadratic (dict): quadratic biases
offset (float): offset
king_graph: represents ising or QUBO interaction.
Each spins are decided by 2-d corrdinate x, y.
Quadratic term [x1, y1, x2, y2, value]
Linear term [x1, y1, x1, y1, value]
vartype: 'SPIN' or 'BINARY'
machine_type (str): choose 'ASIC' or 'FPGA'
"""
vartype = oj.variable_type.cast_vartype(vartype)
# set parameter ranges
self.machine_type = machine_type
if self.machine_type == "ASIC":
self.xrange = [0, 351 + 1]
self.yrange = [0, 175 + 1]
self.prange = [-3, 3]
elif self.machine_type == "FPGA":
self.xrange = [0, 79 + 1]
self.yrange = [0, 79 + 1]
self.prange = [-127, 127]
else:
raise ValueError("machine type should be ASIC or FPGA")
# convert format h, J, Q and initilize BQM
if king_graph is not None:
linear, quadratic = self._convert_to_BQM_format(king_graph, vartype)
super().__init__(linear, quadratic, offset=offset, vartype=vartype)
# reformat to ising king graph (which is Web API format)
if king_graph is not None and vartype == SPIN:
self._ising_king_graph = king_graph
else:
# generate Ising h and J and create ising_king_graph format
lin, quad, _ = self.to_ising()
self._ising_king_graph = []
for index, h in lin.items():
if h != 0:
x, y = self._convert_to_xy(index)
self._ising_king_graph.append([x, y, x, y, h])
for (i, j), J in quad.items():
if J != 0:
x1, y1 = self._convert_to_xy(i)
x2, y2 = self._convert_to_xy(j)
self._ising_king_graph.append([x1, y1, x2, y2, J])
self._validation_ising_king_graph()
def _convert_to_BQM_format(self, king_graph, vartype):
linear, quad = {}, {}
for x1, y1, x2, y2, value in king_graph:
if (x1, y1) == (x2, y2):
linear[(x1, y1)] = value
else:
quad[(x1, y1), (x2, y2)] = value
return linear, quad
def get_ising_king_graph(self):
return self._ising_king_graph
def king_indices(self):
if isinstance(self.indices[0], tuple):
return self.indices
else:
return [self._convert_to_xy(i) for i in self.indices]
def _convert_to_xy(self, index):
if isinstance(index, tuple):
return index[0], index[1]
else:
y = int(index / self.xrange[1])
return int(index - y * self.xrange[1]), y
def convert_to_index(self, x, y):
return y * self.xrange[1] + x
def _validation_ising_king_graph(self):
for xi, yi, xj, yj, p in self._ising_king_graph:
if yi >= self.yrange[1] or yj >= self.yrange[1]:
raise ValueError(
"Graph is incomplete xi: {}, yi: {}, xj: {}, yj: {}, p:{}".format(
xi, yi, xj, yj, p
)
)
if not (xi in [xj, xj - 1, xj + 1]) or not (yi in [yj, yj - 1, yj + 1]):
raise ValueError(
"Graph is incomplete xi: {}, yi: {}, xj: {}, yj: {}, p:{}".format(
xi, yi, xj, yj, p
)
)
if not (self.prange[0] <= p <= self.prange[1]):
raise ValueError(
"Graph is incomplete xi: {}, yi: {}, xj: {}, yj: {}, p: {}".format(
xi, yi, xj, yj, p
)
)
def energy(self, sample):
return super().energy(sample, sparse=True)
def energies(self, samples_like):
return super().energies(sample_like, sparse=True)
return KingGraph
[docs]
def make_KingGraph_from_JSON(obj):
"""KingGraph factory for JSON
Args:
obj (dict): JSON object
Returns:
generated KingGraph class
"""
label = obj["variable_labels"][0]
if isinstance(label, list):
# convert to tuple
label = tuple(label)
mock_linear = {label: 1.0}
return make_KingGraph(mock_linear, {})
[docs]
def KingGraph(
linear=None,
quadratic=None,
offset=0.0,
king_graph=None,
vartype=SPIN,
machine_type="",
):
"""Generate KingGraph model.
Args:
linear (dict): linear biases
quadratic (dict): quadratic biases
offset (float): offset
king_graph: represents ising or QUBO interaction.
Each spins are decided by 2-d corrdinate x, y.
* Quadratic term: [x1, y1, x2, y2, value]
* Linear term: [x1, y1, x1, y1, value]
vartype: 'SPIN' or 'BINARY'
machine_type (str): choose 'ASIC' or 'FPGA'
Returns:
generated KingGraphModel
Examples:
The following code shows intialization of KingGraph::
>>> h = {}
>>> J = {(0, 1): -1.0, (1, 2): -3.0}
>>> king_graph = oj.KingGraph(machine_type="ASIC", linear=h, quadratic=J)
You can initialize it from `king_interaction`::
>>> king_interaction = [[0, 0, 1, 0, -1.0], [1, 0, 2, 0, -3.0]]
>>> king_graph = oj.KingGraph(machine_type="ASIC", king_graph=king_interaction)
"""
Model = make_KingGraph(linear, quadratic, king_graph)
return Model(linear, quadratic, offset, king_graph, vartype, machine_type)
# classmethods
KingGraph.from_qubo = lambda Q, offset=0.0, **kwargs: make_KingGraph({}, Q).from_qubo(
Q, offset, **kwargs
)
KingGraph.from_ising = lambda linear, quadratic, offset=0.0, **kwargs: make_KingGraph(
linear, quadratic
).from_ising(linear, quadratic, offset, **kwargs)
KingGraph.from_serializable = lambda obj: make_KingGraph_from_JSON(
obj
).from_serializable(obj)