Source code for qudas.qudata.qudata_input
from .qudata_base import QuDataBase
from amplify import VariableGenerator, Poly
import csv
import json
from pulp import LpProblem, LpVariable, LpMinimize
import dimod
from pyqubo import Binary, Base
import networkx as nx
import numpy as np
import pandas as pd
import sympy
[docs]class QuDataInput(QuDataBase):
"""量子データ"""
[docs] def __init__(self, prob: dict = None) -> None:
"""
初期データとして最適化問題を受け取るクラス。
Args:
prob (dict, optional): 最適化問題データ。デフォルトはNone。
"""
super().__init__(prob)
self.prob = self.data # dataをprobとして扱う
[docs] def __add__(self, other):
qubo = self.prob.copy()
for k, v in other.prob.items():
if k in qubo:
qubo[k] += v
else:
qubo[k] = v
return QuDataInput(qubo)
[docs] def __sub__(self, other):
qubo = self.prob.copy()
for k, v in other.prob.items():
if k in qubo:
qubo[k] -= v
else:
qubo[k] = -v
return QuDataInput(qubo)
[docs] def __mul__(self, other):
qubo = {}
for k1, v1 in self.prob.items():
for k2, v2 in other.prob.items():
# keyのリストを作成
k = set(k1 + k2)
for _k in qubo.keys():
# 要素が重複する場合
if k == set(_k):
qubo[_k] += v1 * v2
break
else:
# 要素を新規作成
if len(k) == 1:
qubo[(list(k)[0], list(k)[0])] = v1 * v2
else:
qubo[tuple(k)] = v1 * v2
return QuDataInput(qubo)
[docs] def __pow__(self, other: int):
if isinstance(other, int):
qudata = QuDataInput(self.prob)
for _ in range(1, other):
qudata *= self
return qudata
else:
raise TypeError(f"{type(other)}は対応していない型です。")
[docs] def from_pulp(self, prob: LpProblem):
"""pulpデータを読み込む
Args:
prob (LpProblem): 線形計画問題
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, LpProblem):
qubo = {}
for var in prob.objective.to_dict():
qubo[(var['name'], var['name'])] = var['value']
self.prob = qubo
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def from_amplify(self, prob: Poly):
"""amplifyデータを読み込む
Args:
prob (Poly): 組み合わせ最適化問題
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, Poly):
variables = prob.variables
qubo = {}
for key, value in prob.as_dict().items():
# 1変数
if len(key) == 1:
qubo[(variables[key[0]].name, variables[key[0]].name)] = value
# 2変数
elif len(key) == 2:
qubo[(variables[key[0]].name, variables[key[1]].name)] = value
# 3変数以上
else:
raise ValueError("3変数以上は対応していません。")
self.prob = qubo
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def from_pyqubo(self, prob: Base):
"""pyquboデータを読み込む
Args:
prob (Base): 組み合わせ最適化問題
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, Base):
qubo = prob.compile().to_qubo()
self.prob = qubo[0]
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def from_array(self, prob: np.ndarray):
"""numpyデータを読み込む
Args:
prob (np.ndarray): 組み合わせ最適化問題
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, np.ndarray):
qubo = {}
for i, ai in enumerate(prob):
for j, aij in enumerate(ai):
if aij == 0:
continue
if (f"q_{j}", f"q_{i}") in qubo:
qubo[(f"q_{j}", f"q_{i}")] += aij
else:
qubo[(f"q_{i}", f"q_{j}")] = aij
self.prob = qubo
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def from_csv(self, path: str, encoding='utf-8-sig'):
"""csvデータを読み込む
Args:
path (str): ファイルパス文字列
Raises:
Exception: 形式エラー
Returns:
Qudata: 量子データ
"""
try:
with open(path, encoding=encoding, newline='') as f:
qubo = {}
csvreader = csv.reader(f)
for i, ai in enumerate(csvreader):
for j, aij in enumerate(ai):
if float(aij) == 0:
continue
if (f"q_{j}", f"q_{i}") in qubo:
qubo[(f"q_{j}", f"q_{i}")] += float(aij)
else:
qubo[(f"q_{i}", f"q_{j}")] = float(aij)
self.prob = qubo
return self
except Exception as e:
raise ValueError("読み取りエラー") from e
[docs] def from_json(self, path: str):
"""jsonデータを読み込む
Args:
path (str): ファイルパス文字列
Raises:
Exception: 形式エラー
Returns:
Qudata: 量子データ
"""
try:
with open(path) as f:
qubo = {}
jd = json.load(f)
for q in jd["qubo"]:
qubo[(q["key"][0], q["key"][1])] = q["value"]
self.prob = qubo
return self
except Exception as e:
raise ValueError("読み取りエラー") from e
[docs] def from_networkx(self, prob: nx.Graph):
"""グラフデータを読み込む
Args:
prob (nx.Graph): networkxのグラフデータ
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, nx.Graph):
qubo = {}
for e in prob.edges():
if (f"q_{e[0]}", f"q_{e[1]}") in qubo:
qubo[(f"q_{e[0]}", f"q_{e[1]}")] += 1
else:
qubo[(f"q_{e[0]}", f"q_{e[1]}")] = 1
self.prob = qubo
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def from_pandas(self, prob: pd.DataFrame):
"""pandasデータを読み込む
Args:
prob (pd.DataFrame): pandasのデータフレーム
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, pd.DataFrame):
key1_list = prob.columns.tolist()
key2_list = prob.index.tolist()
qubo = {}
for k1 in key1_list:
for k2 in key2_list:
if prob[k1][k2] == 0:
continue
if (k1, k2) in qubo:
qubo[(k1, k2)] += prob[k1][k2]
else:
qubo[(k1, k2)] = prob[k1][k2]
self.prob = qubo
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def from_dimod_bqm(self, prob: dimod.BinaryQuadraticModel):
"""dimodのbqmデータを読み込む
Args:
prob (dimod.BinaryQuadraticModel): dimodのbqmデータ
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, dimod.BinaryQuadraticModel):
qubo = dict(prob.quadratic).copy()
for k, v in prob.linear.items():
if v == 0:
continue
qubo[(k, k)] = v
self.prob = qubo
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def from_sympy(self, prob: sympy.core.expr.Expr):
"""sympyデータを読み込む
Args:
prob (sympy.core.expr.Expr): sympyデータ
Raises:
TypeError: 形式エラー
Returns:
Qudata: 量子データ
"""
if isinstance(prob, sympy.core.expr.Expr):
qubo = {}
for term in prob.as_ordered_terms():
# 係数と変数を取得
v, k = term.as_coeff_mul()
if len(k) == 1:
variable = term.free_symbols
qubo[(str(list(variable)[0]), str(list(variable)[0]))] = v
else:
k = tuple([str(_k) for _k in k])
qubo[k] = v
self.prob = qubo
return self
else:
raise TypeError(f"{type(prob)}は対応していない型です。")
[docs] def to_pulp(self) -> LpProblem:
"""pulp形式に変換
Raises:
ValueError: 変数エラー
Returns:
LpProblem: 線形計画問題
"""
variables = list(set(k for key in self.prob.keys() for k in key))
q = [
LpVariable(name, lowBound=0, upBound=1, cat='Binary') for name in variables
]
qubo = LpProblem('QUBO', LpMinimize)
_qubo = 0
for key, value in self.prob.items():
# 1変数
if key[0] == key[1]:
variable_index = variables.index(key[0])
_qubo += q[variable_index] * value
# 2変数以上
else:
raise ValueError("pulpは2変数以上に対応していません。")
qubo += _qubo
return qubo
[docs] def to_amplify(self) -> Poly:
"""amplify形式に変換
Returns:
Poly: 組み合わせ最適化問題
"""
variables = list(set(k for key in self.prob.keys() for k in key))
gen = VariableGenerator()
q = gen.array("Binary", len(variables)) # default は name="q"
labeled_q = {_q.name: _q for _q in q}
qubo = 0
for key, value in self.prob.items():
sub_qubo = 1
for k in key:
sub_qubo *= labeled_q[k]
qubo += sub_qubo * value
return qubo
[docs] def to_pyqubo(self) -> Base:
"""pyqubo形式に変換
Returns:
Base: 組み合わせ最適化問題
"""
variables = list(set(k for key in self.prob.keys() for k in key))
q = [Binary(variable) for variable in variables]
qubo = 0
for key, value in self.prob.items():
sub_qubo = 1
for k in key:
variable_index = variables.index(k)
sub_qubo *= q[variable_index]
qubo += sub_qubo * value
return qubo
[docs] def to_array(self) -> np.ndarray:
"""numpy形式に変換
Raises:
ValueError: 次元エラー
Returns:
np.ndarray: QUBO行列
"""
# 変数の順序を保持したリストを作成
variables = sorted(list(set(k for key in self.prob.keys() for k in key)))
qubo = np.zeros((len(variables), len(variables)))
for key, value in self.prob.items():
# 1変数 or 2変数
if len(key) == 2:
variable_index_0 = variables.index(key[0])
variable_index_1 = variables.index(key[1])
qubo[variable_index_0, variable_index_1] = value
# 3変数以上
else:
raise ValueError("matrixは3変数以上に対応していません。")
return qubo
[docs] def to_csv(self, name="qudata") -> None:
"""データをCSV形式で保存
Args:
name (str, optional): ファイル名. Defaults to "qudata".
Raises:
ValueError: 書き出しエラー
"""
qubo = self.to_array()
try:
with open(f"{name}.csv", 'w') as f:
writer = csv.writer(f)
writer.writerows(qubo)
except Exception as e:
raise ValueError("書き出しエラー") from e
[docs] def to_json(self, name="qudata") -> None:
"""json形式に変換
Args:
name (str, optional): ファイル名. Defaults to "qudata".
Raises:
ValueError: 書き出しエラー
"""
qubo = [{"key": list(key), "value": value} for key, value in self.prob.items()]
try:
with open(f"{name}.json", 'w') as f:
json.dump(qubo, f, indent=2)
except Exception as e:
raise ValueError("書き出しエラー") from e
[docs] def to_networkx(self) -> nx.Graph:
"""networkx形式に変換
Raises:
ValueError: 次元エラー
Returns:
nx.Graph: networkxのグラフデータ
"""
variables = list(set(k for key in self.prob.keys() for k in key))
G = nx.Graph()
for key, value in self.prob.items():
# 1変数 or 2変数
if len(key) == 2:
variable_index_0 = variables.index(key[0])
variable_index_1 = variables.index(key[1])
G.add_edge(variable_index_0, variable_index_1, value=value)
# 3変数以上
else:
raise ValueError("networkxは3変数以上に対応していません。")
return G
[docs] def to_pandas(self) -> pd.DataFrame:
"""pandas形式に変換
Returns:
pd.DataFrame: pandasデータ
"""
# 変数の順序を保持したリストを作成
variables = sorted(list(set(k for key in self.prob.keys() for k in key)))
array = self.to_array()
return pd.DataFrame(array, columns=variables, index=variables)
[docs] def to_dimod_bqm(self) -> dimod.BinaryQuadraticModel:
"""dimodのbqm形式に変換
Returns:
dimod.BinaryQuadraticModel: dimodのbqmデータ
"""
linear = {k[0]: v for k, v in self.prob.items() if k[0] == k[1]}
quadratic = {k: v for k, v in self.prob.items() if k[0] != k[1]}
return dimod.BinaryQuadraticModel(linear, quadratic, vartype='BINARY')
[docs] def to_sympy(self) -> sympy.core.expr.Expr:
"""sympy形式に変換
Returns:
sympy.core.expr.Expr: sympyの多項式データ
"""
sympy_prob = sum(
(
sympy.Symbol(k[0]) * v
if k[0] == k[1]
else sympy.Symbol(k[0]) * sympy.Symbol(k[1]) * v
)
for k, v in self.prob.items()
)
return sympy_prob