Source code for quara.objects.qoperations

from abc import abstractmethod
from typing import List, Dict

import numpy as np
from quara.objects.qoperation import QOperation
from quara.objects.state import State, convert_var_to_state
from quara.objects.gate import Gate, convert_var_to_gate
from quara.objects.povm import Povm, convert_var_to_povm


[docs]class SetQOperations: def __init__( self, states: List[State], gates: List[Gate], povms: List[Povm] ) -> None: # Validation self._validate_type(states, State) self._validate_type(povms, Povm) self._validate_type(gates, Gate) # Set self._states: List[State] = states self._povms: List[Povm] = povms self._gates: List[Gate] = gates # TODO: List[MProcess] self._mprocesses: list = [] 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" ) 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
[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 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_mprosess(self, index: int) -> int: # TODO MProcess raise NotImplementedError()
[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: # TODO MProcess return 0
[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) -> int: # TODO MProcess pass
[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_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_total(self) -> np.ndarray: vars = np.hstack([self.var_states(), self.var_gates(), self.var_povms()]) return vars
def _get_operation_type_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_gates() 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, type_operation: str, index: int ) -> int: # returns the index that is the place of the 'index'-th 'type_operation' starts in the whole var target_operations: List[QOperation] if type_operation == "state": target_operations = self.states get_size_func = self.size_var_state elif type_operation == "gate": target_operations = self.gates get_size_func = self.size_var_gate elif type_operation == "povm": target_operations = self.povms get_size_func = self.size_var_povm else: raise ValueError( "'{}' is an unsupported operation type.".format(type_operation) ) 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, type_operation: 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 type_operation not in supported_types: raise ValueError( "'{}' is an unsupported operation type. Supported Operations: {}.".format( type_operation, ",".join(supported_types) ) ) first_index_map = self._get_operation_type_to_total_index_map() index_var_total = ( first_index_map[type_operation] + self._get_operation_item_var_first_index(type_operation, index_operations) + index_var_local ) return index_var_total
def _get_type_operation_from_index_var_total(self, index_var_total: int) -> str: first_index_map = self._get_operation_type_to_total_index_map() type_operation: str if 0 <= index_var_total < first_index_map["gate"]: type_operation = "state" elif first_index_map["gate"] <= index_var_total < first_index_map["povm"]: type_operation = "gate" elif first_index_map["povm"] <= index_var_total < first_index_map["mprocess"]: type_operation = "povm" else: raise IndexError( f"index_var_total is out of range. index_var_total={index_var_total}" ) return type_operation
[docs] def local_info_from_index_var_total(self, index_var_total: int) -> dict: # Type Operation type_operation = self._get_type_operation_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_type_operation_from_index_var_total, # so if speed is slow, it should be modified. first_index_map = self._get_operation_type_to_total_index_map() mid_level_index = index_var_total - first_index_map[type_operation] # Index Var Total target_operations: List[QOperation] if type_operation == "state": target_operations = self.states get_size_func = self.size_var_state elif type_operation == "gate": target_operations = self.gates get_size_func = self.size_var_gate elif type_operation == "povm": target_operations = self.povms get_size_func = self.size_var_povm first_index = 0 for i, target 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( type_operation=type_operation, 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
[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) q_operation2func_map = { State: convert_var_to_state, Gate: convert_var_to_gate, Povm: convert_var_to_povm, } new_q_operation_dict = {State: [], Gate: [], Povm: []} 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], ) return new_set_qoperations