Source code for qudas.qudata.qudata_input

from .qudata_base import QuDataBase
import csv
import json
import numpy as np


[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): """pulpデータを読み込む Args: prob (LpProblem): 線形計画問題 Raises: TypeError: 形式エラー Returns: Qudata: 量子データ """ from pulp import LpProblem 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): """amplifyデータを読み込む Args: prob (Poly): 組み合わせ最適化問題 Raises: TypeError: 形式エラー Returns: Qudata: 量子データ """ from amplify import Poly if isinstance(prob, Poly): variables = prob.variables qubo = {} for key, value in prob.as_dict().items(): # 定数 if len(key) == 0: pass # 1変数 elif 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): """pyquboデータを読み込む Args: prob (Base): 組み合わせ最適化問題 Raises: TypeError: 形式エラー Returns: Qudata: 量子データ """ from pyqubo import Base 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): """グラフデータを読み込む Args: prob (nx.Graph): networkxのグラフデータ Raises: TypeError: 形式エラー Returns: Qudata: 量子データ """ import networkx as nx 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): """pandasデータを読み込む Args: prob (pd.DataFrame): pandasのデータフレーム Raises: TypeError: 形式エラー Returns: Qudata: 量子データ """ import pandas as pd 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のbqmデータを読み込む Args: prob (dimod.BinaryQuadraticModel): dimodのbqmデータ Raises: TypeError: 形式エラー Returns: Qudata: 量子データ """ import dimod 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データを読み込む Args: prob (sympy.core.expr.Expr): sympyデータ Raises: TypeError: 形式エラー Returns: Qudata: 量子データ """ import sympy 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): """pulp形式に変換 Raises: ValueError: 変数エラー Returns: LpProblem: 線形計画問題 """ from pulp import LpProblem, LpVariable, LpMinimize 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): """amplify形式に変換 Returns: Poly: 組み合わせ最適化問題 """ from amplify import VariableGenerator variables = list(set(k for key in self.prob.keys() for k in key)) gen = VariableGenerator() labeled_q = { str(name): gen.scalar("Binary", name=str(name)) for name in variables } # default は name="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): """pyqubo形式に変換 Returns: Base: 組み合わせ最適化問題 """ from pyqubo import Binary variables = list(set(k for key in self.prob.keys() for k in key)) q = [Binary(str(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): """networkx形式に変換 Raises: ValueError: 次元エラー Returns: nx.Graph: networkxのグラフデータ """ import networkx as nx 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): """pandas形式に変換 Returns: pd.DataFrame: pandasデータ """ import pandas as pd # 変数の順序を保持したリストを作成 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のbqm形式に変換 Returns: dimod.BinaryQuadraticModel: dimodのbqmデータ """ import dimod 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形式に変換 Returns: sympy.core.expr.Expr: sympyの多項式データ """ import 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