Source code for quara.objects.qoperations

from typing import List, Dict, Union

import numpy as np

from quara.objects.qoperation import QOperation
from quara.objects.state import State
from quara.objects.gate import Gate
from quara.objects.povm import Povm
from quara.objects.mprocess import MProcess


[docs]class SetQOperations: def __init__( self, states: List[State] = None, gates: List[Gate] = None, povms: List[Povm] = None, mprocesses: List[MProcess] = None, ) -> None: states = [] if states is None else states gates = [] if gates is None else gates povms = [] if povms is None else povms mprocesses = [] if mprocesses is None else mprocesses # Validation self._validate_type(states, State) self._validate_type(povms, Povm) self._validate_type(gates, Gate) self._validate_type(mprocesses, MProcess) # Set self._states: List[State] = states self._povms: List[Povm] = povms self._gates: List[Gate] = gates self._mprocesses: List[MProcess] = mprocesses def _validate_type(self, targets, expected_type, arg_name: str = None) -> None: for target in targets: if target and not isinstance(target, expected_type): arg_name = ( arg_name if arg_name else expected_type.__name__.lower() + "s" ) # ss -> es (ex: mprocesss -> mprocesses) arg_name = arg_name[:-2] + "ses" if arg_name[-2:] == "ss" else arg_name error_message = "'{}' must be a list of {}.".format( arg_name, expected_type.__name__ ) raise TypeError(error_message) # Setter & Getter @property def states(self) -> List[State]: return self._states @states.setter def states(self, value): self._validate_type(value, State) self._states = value @property def povms(self) -> List[Povm]: return self._povms @povms.setter def povms(self, value): self._validate_type(value, Povm) self._povms = value @property def gates(self) -> List[Gate]: return self._gates @gates.setter def gates(self, value): self._validate_type(value, Gate) self._gates = value @property def mprocesses(self) -> List[MProcess]: return self._mprocesses @mprocesses.setter def mprocesses(self, value): self._validate_type(value, MProcess) self._mprocesses = value
[docs] def qoperations( self, mode: str ) -> Union[List[State], List[Povm], List[Gate], List[MProcess]]: """returns qoperations with specified mode. Parameters ---------- mode : str mode to get qoperations. mode can be "state", "povm", "gate", or "mprocess". Returns ------- Union[List[State], List[Povm], List[Gate], List[MProcess]] qoperations with specified mode. Raises ------ ValueError Unsupported mode is specified. """ if mode == "state": return self.states elif mode == "povm": return self.povms elif mode == "gate": return self.gates elif mode == "mprocess": return self.mprocesses else: raise ValueError(f"Unsupported mode is specified. mode={mode}")
[docs] def num_states(self): return len(self._states)
[docs] def num_povms(self): return len(self._povms)
[docs] def num_gates(self): return len(self._gates)
[docs] def num_mprocesses(self): return len(self._mprocesses)
[docs] def num_qoperations(self, mode: str) -> int: """returns number of qoperations with specified mode. Parameters ---------- mode : str mode to get number of qoperations. mode can be "state", "povm", "gate", or "mprocess". Returns ------- int number of qoperations with specified mode. Raises ------ ValueError Unsupported mode is specified. """ if mode == "state": return self.num_states() elif mode == "povm": return self.num_povms() elif mode == "gate": return self.num_gates() elif mode == "mprocess": return self.num_mprocesses() else: raise ValueError(f"An unsupported mode is specified. mode={mode}")
[docs] def dim_state(self, index: int) -> int: # returns the dimension of the total system of the i-th state return self.states[index].dim
[docs] def dim_gate(self, index: int) -> int: # returns the dimension of the total system of the i-th gate return self.gates[index].dim
[docs] def dim_povm(self, index: int) -> int: # returns the dimension of the total system of the i-th povm return self.povms[index].dim
[docs] def dim_mprocess(self, index: int) -> int: # returns the dimension of the total system of the i-th mprocess return self.mprocesses[index].dim
[docs] def size_var_states(self) -> int: return len(self.var_states())
[docs] def size_var_gates(self) -> int: return len(self.var_gates())
[docs] def size_var_povms(self) -> int: return len(self.var_povms())
[docs] def size_var_mprocesses(self) -> int: return len(self.var_mprocesses())
[docs] def size_var_state(self, index: int) -> int: return len(self.var_state(index=index))
[docs] def size_var_gate(self, index: int) -> int: return len(self.var_gate(index=index))
[docs] def size_var_povm(self, index: int) -> int: return len(self.var_povm(index=index))
[docs] def size_var_mprocess(self, index: int) -> int: return len(self.var_mprocess(index=index))
[docs] def size_var_total(self) -> int: total = sum( [ self.size_var_states(), self.size_var_gates(), self.size_var_povms(), self.size_var_mprocesses(), ] ) return total
[docs] def var_state(self, index: int) -> np.ndarray: return self.states[index].to_var()
[docs] def var_gate(self, index: int) -> np.ndarray: return self.gates[index].to_var()
[docs] def var_povm(self, index: int) -> np.ndarray: return self.povms[index].to_var()
[docs] def var_mprocess(self, index: int) -> np.ndarray: return self.mprocesses[index].to_var()
[docs] def var_states(self) -> List[float]: vars = [state.to_var() for state in self.states] vars = np.hstack(vars) if vars else np.array([]) return vars
[docs] def var_povms(self) -> np.ndarray: vars = [povm.to_var() for povm in self.povms] vars = np.hstack(vars) if vars else np.array([]) return vars
[docs] def var_gates(self) -> np.ndarray: vars = [gate.to_var() for gate in self.gates] vars = np.hstack(vars) if vars else np.array([]) return vars
[docs] def var_mprocesses(self) -> np.ndarray: vars = [mprocess.to_var() for mprocess in self.mprocesses] vars = np.hstack(vars) if vars else np.array([]) return vars
[docs] def var_total(self) -> np.ndarray: vars = np.hstack( [ self.var_states(), self.var_gates(), self.var_povms(), self.var_mprocesses(), ] ) return vars
def _get_operation_mode_to_total_index_map(self) -> Dict[str, int]: states_first_index = 0 gates_first_index = self.size_var_states() povms_first_index = gates_first_index + self.size_var_gates() mprocesses_first_index = povms_first_index + self.size_var_povms() return dict( state=states_first_index, gate=gates_first_index, povm=povms_first_index, mprocess=mprocesses_first_index, ) def _get_operation_item_var_first_index(self, mode: str, index: int) -> int: # returns the index that is the place of the 'index'-th 'mode' starts in the whole var if mode == "state": get_size_func = self.size_var_state elif mode == "gate": get_size_func = self.size_var_gate elif mode == "povm": get_size_func = self.size_var_povm elif mode == "mprocess": get_size_func = self.size_var_mprocess else: raise ValueError("'{}' is an unsupported operation type.".format(mode)) target_item_first_index = 0 for i in range(index): target_item_first_index += get_size_func(i) return target_item_first_index
[docs] def index_var_total_from_local_info( self, mode: str, index_operations: int, index_var_local: int ): # Returns the index in the optimization variable from local information. # The local information consists of type of the operation, its number in the list of operations of that type, # and the index in the variable that characterizes the operation. supported_types = ["state", "povm", "gate", "mprocess"] if mode not in supported_types: raise ValueError( "'{}' is an unsupported operation type. Supported Operations: {}.".format( mode, ",".join(supported_types) ) ) first_index_map = self._get_operation_mode_to_total_index_map() index_var_total = ( first_index_map[mode] + self._get_operation_item_var_first_index(mode, index_operations) + index_var_local ) return index_var_total
def _get_mode_from_index_var_total(self, index_var_total: int) -> str: first_index_map = self._get_operation_mode_to_total_index_map() mode: str if 0 <= index_var_total < first_index_map["gate"]: mode = "state" elif first_index_map["gate"] <= index_var_total < first_index_map["povm"]: mode = "gate" elif first_index_map["povm"] <= index_var_total < first_index_map["mprocess"]: mode = "povm" elif first_index_map["mprocess"] <= index_var_total < self.size_var_total(): mode = "mprocess" else: raise IndexError( f"index_var_total is out of range. index_var_total={index_var_total}" ) return mode
[docs] def local_info_from_index_var_total(self, index_var_total: int) -> dict: # Type Operation mode = self._get_mode_from_index_var_total(index_var_total) # Index Operations # This function is split to make it easier to test. # However, first_index_map is called twice, in this method and in _get_mode_from_index_var_total, # so if speed is slow, it should be modified. first_index_map = self._get_operation_mode_to_total_index_map() mid_level_index = index_var_total - first_index_map[mode] # Index Var Total target_operations: List[QOperation] if mode == "state": target_operations = self.states get_size_func = self.size_var_state elif mode == "gate": target_operations = self.gates get_size_func = self.size_var_gate elif mode == "povm": target_operations = self.povms get_size_func = self.size_var_povm elif mode == "mprocess": target_operations = self.mprocesses get_size_func = self.size_var_mprocess first_index = 0 for i, _ in enumerate(target_operations): local_item_size = get_size_func(i) if first_index <= mid_level_index < first_index + local_item_size: index_operations = i index_var_local = mid_level_index - first_index first_index += local_item_size local_info = dict( mode=mode, index_operations=index_operations, index_var_local=index_var_local, ) return local_info
def _all_qoperations(self) -> List["QOperations"]: # Do NOT change the order return self.states + self.gates + self.povms + self.mprocesses
[docs] def set_qoperations_from_var_total(self, var_total: np.ndarray) -> "SetQOperations": # returns SetQOperations corresponding to var_total actual, expected = len(var_total), self.size_var_total() if actual != expected: error_message = ( "the length of var_total is wrong. expceted: {}, actual: {}".format( expected, actual ) ) raise ValueError(error_message) new_q_operation_dict = {State: [], Gate: [], Povm: [], MProcess: []} all_q_operations = self._all_qoperations() start_index = 0 for q_operation in all_q_operations: end_index = start_index + len(q_operation.to_var()) var = var_total[start_index:end_index] new_q_operation = q_operation.generate_from_var(var=var) new_q_operation_dict[type(q_operation)].append(new_q_operation) start_index = end_index new_set_qoperations = SetQOperations( states=new_q_operation_dict[State], gates=new_q_operation_dict[Gate], povms=new_q_operation_dict[Povm], mprocesses=new_q_operation_dict[MProcess], ) return new_set_qoperations