Source code for qlazy.Observable

# -*- coding: utf-8 -*-
""" Obserbavle for quantum many-body spin system. """

from qlazy.util import is_num
from qlazy.ObservableBase import ObservableBase
from qlazy.PauliProduct import PauliProduct

[docs]def X(q): """ Parameters ---------- q : int qubit id Returns ------- ob : instance of Observable obserbable X(q) """ ob = Observable().add_wpp(weight=1.0, pp=PauliProduct(pauli_str='X', qid=[q])) return ob
[docs]def Y(q): """ Parameters ---------- q : int qubit id Returns ------- ob : instance of Observable obserbable Y(q) """ ob = Observable().add_wpp(weight=1.0, pp=PauliProduct(pauli_str='Y', qid=[q])) return ob
[docs]def Z(q): """ Parameters ---------- q : int qubit id Returns ------- ob : instance of Observable obserbable X(q) """ ob = Observable().add_wpp(weight=1.0, pp=PauliProduct(pauli_str='Z', qid=[q])) return ob
[docs]class Observable: """ Observable for quantum many-body spin system. Attributes ---------- base : instance of ObservableBase observable string : str string of observable description ex) "-2.0+Z_0*Z_1+X_0+X_1" for 2-qubit system weighted_pp_list : list of dict ({'weight':weight, 'pp':pauli_product}) weighted pauli product list qubit_num : int total qubit number """ def __init__(self, string=None): """ Parameters ---------- string : str string of observable description ex) "-2.0+Z_0*Z_1+X_0+X_1" for 2-qubit system """ if string is not None: self.weighted_pp_list = self.get_weighted_pp_list(string) else: self.weighted_pp_list = [] def __str__(self): ob = self.clone() if ob.recalc_weight() is False: raise ValueError("Observable is not hermitian.") term_str_list = [] for weighted_pp in ob.weighted_pp_list: if weighted_pp['weight'] > 0.0: sign = '+ ' else: sign = '- ' p_str_list = [] for i, p_str in enumerate(weighted_pp['pp'].pauli_list): if p_str == 'I': continue q = weighted_pp['pp'].qid[i] p_str_list.append("{}({})".format(p_str.upper(), str(q))) if abs(weighted_pp['weight']) == 1.0: if p_str_list == []: weight_str = str(abs(weighted_pp['weight'])) else: weight_str = '' else: weight_str = str(abs(weighted_pp['weight'])) if p_str_list == []: pp_str = sign + weight_str elif weight_str != '': pp_str = sign + weight_str + ' ' + ' '.join(p_str_list) else: pp_str = sign + weight_str + ' '.join(p_str_list) term_str_list.append(pp_str) s = " ".join(term_str_list) s = s.strip() if s != '' and list(s)[0] == '+': s = s.lstrip('+').lstrip(' ') return s def __eq__(self, ob): """ Parameters ---------- ob : instance of Observable obervable Returns ------- ans : bool True if equal """ ob_1 = self.clone() ob_2 = ob.clone() if ob_1.recalc_weight() is False: raise ValueError("Observable is not hermitian.") if ob_2.recalc_weight() is False: raise ValueError("Observable is not hermitian.") if len(ob_1.weighted_pp_list) != len(ob_2.weighted_pp_list): ans = False return ans for wpp in ob_1.weighted_pp_list: pp = wpp['pp'] i, w = ob_2.get_idx_weight(pp) if w == wpp['weight']: ans = True else: ans = False break return ans def __ne__(self, ob): """ Parameters ---------- ob : instance of Observable obervable Returns ------- ans : bool True if not equal """ ans = not self.__eq__(ob) return ans def __pos__(self): """ Parameters ---------- None Returns ------- out : instance of Observable observable (result) """ return self def __neg__(self): """ Parameters ---------- None Returns ------- out : instance of Observable observable (result) """ self *= -1.0 return self def __add__(self, other): """ Parameters ---------- other : instance of Observable observable Returns ------- out : instance of Observable observable (result) """ out = Observable() for wpp in self.weighted_pp_list: if wpp['weight'] != 0.0: out.add_wpp(weight=wpp['weight'], pp=wpp['pp']) if isinstance(other, self.__class__): for wpp in other.weighted_pp_list: if wpp['weight'] != 0.0: out.add_wpp(weight=wpp['weight'], pp=wpp['pp']) elif isinstance(other, int) or isinstance(other, float): if other != 0.0: out.add_wpp(weight=other, pp=PauliProduct('I', [0])) else: raise TypeError("Can't add a Observable with {}".format(type(other))) return out __radd__ = __add__ def __iadd__(self, other): """ Parameters ---------- other : instance of Observable observable Returns ------- self : instance of Observable observable (result) """ out = self.__add__(other) self.weighted_pp_list = out.weighted_pp_list[:] return self def __sub__(self, other): """ Parameters ---------- other : instance of Observable observable Returns ------- out : instance of Observable observable (result) """ out = Observable() for wpp in self.weighted_pp_list: if wpp['weight'] != 0.0: out.add_wpp(weight=wpp['weight'], pp=wpp['pp']) if isinstance(other, self.__class__): for wpp in other.weighted_pp_list: if wpp['weight'] != 0.0: out.add_wpp(weight=-wpp['weight'], pp=wpp['pp']) elif isinstance(other, int) or isinstance(other, float): if other != 0.0: out.add_wpp(weight=-other, pp=PauliProduct('I', [0])) else: raise TypeError("Can't sub a Observable with {}".format(type(other))) return out def __rsub__(self, other): out = -1.0 * self.__sub__(other) return out def __isub__(self, other): """ Parameters ---------- other : instance of Observable observable Returns ------- self : instance of Observable observable (result) """ out = self.__sub__(other) self.weighted_pp_list = out.weighted_pp_list[:] return self def __mul__(self, other): """ Parameters ---------- other : instance of Observable / int / float observable / number Returns ------- self : instance of Observable observable (result) """ if isinstance(other, self.__class__): out = Observable() for wpp_1 in self.weighted_pp_list: for wpp_2 in other.weighted_pp_list: pp = wpp_1['pp'] * wpp_2['pp'] weight = wpp_1['weight'] * wpp_2['weight'] if weight != 0.0: out.add_wpp(weight=weight, pp=pp) elif isinstance(other, int) or isinstance(other, float): out = Observable() for wpp in self.weighted_pp_list: if wpp['weight']*other != 0.0: out.add_wpp(weight=wpp['weight']*other, pp=wpp['pp']) else: raise TypeError("Can't mul a Observable with {}".format(type(other))) return out __rmul__ = __mul__ def __imul__(self, other): """ Parameters ---------- other : instance of Observable / int / float observable / number Returns ------- self : instance of Observable observable (result) """ out = self.__mul__(other) self.weighted_pp_list = out.weighted_pp_list[:] return self def __pow__(self, other): """ Parameters ---------- other : instance of Observable / int / float observable / number Returns ------- out : instance of Observable observable (result) """ if isinstance(other, int): if other < 1: raise ValueError("Can't execute {}-th power".format(other)) out = self.clone() for _ in range(other - 1): out = out * self else: raise TypeError("Can't pow a Observable with {}".format(type(other))) return out def __truediv__(self, other): """ Parameters ---------- other : int / float number Returns ------- out : instance of Observable observable (result) """ if isinstance(other, int) or isinstance(other, float): out = Observable() for wpp in self.weighted_pp_list: out.add_wpp(weight=wpp['weight']/other, pp=wpp['pp']) else: raise TypeError("Can't div a Observable with {}".format(type(other))) return out def __itruediv__(self, other): """ Parameters ---------- other : int / float number Returns ------- out : instance of Observable observable (result) """ out = self.__truediv__(other) self.weighted_pp_list = out.weighted_pp_list[:] return self
[docs] def is_hermitian(self): """ hermitian or not Parameters ---------- None Returns ------- ans : bool is the observable hermitian or not """ ans = True for wpp in self.weighted_pp_list: weight = wpp['weight'] * wpp['pp'].factor if weight.imag != 0.0: ans = False break return ans
[docs] def recalc_weight(self): """ recalculate the weights of weighted_pp_list (weight <= weight * pp.factor, pp.factor <= 1.0) Parameters ---------- None Returns ------- ans : bool is the observable hermitian or not """ ans = True for wpp in self.weighted_pp_list: weight = wpp['weight'] * wpp['pp'].factor if weight.imag != 0.0: ans = False break wpp['weight'] = weight.real wpp['pp'].factor = 1.+0.j return ans
@property def base(self): return self.get_observable_base()
[docs] def get_observable_base(self): """ get ObservableBase instance. Parameters ---------- None Returns ------- ob_base : instance of ObservableBase observable """ ob_base = ObservableBase(self.string) return ob_base
@property def string(self): return self.get_string()
[docs] def get_string(self): """ get string of observable. Parameters ---------- None Returns ------- s : str string of observable """ ob = self.clone() if ob.recalc_weight() is False: raise ValueError("Observable is not hermitian.") term_str_list = [] for weighted_pp in ob.weighted_pp_list: if weighted_pp['weight'] > 0.0: sign = '+' else: sign = '-' p_str_list = [] for i, p_str in enumerate(weighted_pp['pp'].pauli_list): if p_str == 'I': continue q = weighted_pp['pp'].qid[i] p_str_list.append("{}_{}".format(p_str.upper(), str(q))) if abs(weighted_pp['weight']) == 1.0: if p_str_list == []: weight_str = str(abs(weighted_pp['weight'])) else: weight_str = '' else: weight_str = str(abs(weighted_pp['weight'])) + '*' if p_str_list == []: pp_str = (sign + weight_str).rstrip('*') else: pp_str = sign + weight_str + "*".join(p_str_list) term_str_list.append(pp_str) s = "".join(term_str_list) s = s.strip() if s != '' and list(s)[0] == '+': s = s.lstrip('+') return s
[docs] def get_weighted_pp_list(self, string): """ get weighted pauli product list. Parameters ---------- string : str string of observable Returns ------- weighted_pp_list : list of dict (weight:pauli_product) weighted pauli product list """ weighted_pp_list = [] terms_list = string.replace(' ', '').replace('+', '+1.0*').replace('-', '+-1.0*').split('+') for term in terms_list: if term == '': continue items_list = term.split('*') weight = 1.0 spin_product = [] for x in items_list: item = x.replace(' ', '') if is_num(item) is True: weight = weight * float(item) continue kind_idx = [x.strip() for x in item.split('_')] # ex) kind_idx = ['X', '1'] if (len(kind_idx) != 2 or kind_idx[0] not in ('x', 'y', 'z', 'X', 'Y', 'Z') or kind_idx[1].isdecimal() is False): raise ValueError("invalid string of observable") spin_product.append(kind_idx) # spin_product to pauli_product # ex) spin_product = [['X', '1'], ['Z', 0], ..] pauli_str = "" qid = [] for spin in spin_product: pauli_str += spin[0].upper() qid.append(int(spin[1])) if spin_product == []: pauli_str = 'I' qid = [0] pp = PauliProduct(pauli_str=pauli_str, qid=qid) if weight != 0.0: weighted_pp_list.append({'weight': weight, 'pp': pp}) return weighted_pp_list
[docs] def get_idx_weight(self, pp): """ get index and weight value correspond to the pauli product. Parameters ---------- pp : instance of PauliProduct pauli product Returns ------- idx : int index number of the list (self.weighted_pp_list) correspond to the pauli product weight : float weight value correspond to the pauli product """ pp_list = [self.weighted_pp_list[i]['pp'] for i in range(len(self.weighted_pp_list))] weight_list = [self.weighted_pp_list[i]['weight'] for i in range(len(self.weighted_pp_list))] if pp in pp_list: idx = pp_list.index(pp) weight = self.weighted_pp_list[idx]['weight'] else: idx = None weight = 0.0 return idx, weight
[docs] def clone(self): """ clone observable. Parameters ---------- None Returns ------- ob : instance of Observable observable """ ob = Observable() ob.weighted_pp_list = self.weighted_pp_list[:] return ob
[docs] def add_wpp(self, weight=1.0, pp=None): """ add the weighted pauli product to the list (self.weighted_pp_list). Parameters ---------- weight : float weight value correspond to the pauli product pp : instance of PauliProduct pauli product Returns ------- self : instance of Ovservable observable after adding weighted pauli product """ if weight == 0.0: return self if pp is None: # constant term, not include pauli product pp = PauliProduct(pauli_str='I', qid=[0]) i, w = self.get_idx_weight(pp) if w == 0.0: w = weight self.weighted_pp_list.append({'weight': w, 'pp': pp}) else: w += weight if w == 0.0: self.weighted_pp_list.pop(i) else: self.weighted_pp_list[i]['weight'] = w return self
@property def qubit_num(self): return self.get_qubit_num()
[docs] def get_qubit_num(self): """ get the total qubit number considerd Parameters ---------- None Returns ------- qubit_num : inst total qubit number considerd by the observable. """ qubit_num = 0 for wpp in self.weighted_pp_list: qubit_num = max(qubit_num, wpp['pp'].get_qubit_num()) return qubit_num