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