def __init__(self, name: str, probs: typing.List[float], level: int): """ Parameters ---------- name: str what the name of the noise is. Determines how many probabilites are needed additionally. probs: list: a list of probabilities with which to apply the requested noise level: int: the number of qubits in the gates this noise acts upon. """ probs = list_assignement(probs) if name not in noises_available: raise TequilaException( 'The name you asked for, {}, is not recognized'.format(name)) self._name = name self._level = int(level) if len(probs) != self.prob_length[name]: raise TequilaException( '{} noise requires {} probabilities; recieved {}'.format( name, self.prob_length[name], len(probs))) if name in krausses: assert sum(probs) <= 1. self.probs = list_assignement(probs)
def __init__(self, name: str, probs: typing.List[float], level: int): probs = list_assignement(probs) if name not in noises_available: raise TequilaException( 'The name you asked for, {}, is not recognized'.format(name)) self._name = name self._level = int(level) if len(probs) != self.prob_length[name]: raise TequilaException( '{} noise requires {} probabilities; recieved {}'.format( name, self.prob_length[name], len(probs))) if name in krausses: assert sum(probs) <= 1. self.probs = list_assignement(probs)
def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction: """ Simulate the wavefunction :param returntype: specifies how the result should be given back :param initial_state: The initial state of the simulation, if given as an integer this is interpreted as the corresponding multi-qubit basis state :return: The resulting state """ self.update_variables(variables) if isinstance(initial_state, BitString): initial_state = initial_state.integer if isinstance(initial_state, QubitWaveFunction): if len(initial_state.keys()) != 1: raise TequilaException("only product states as initial states accepted") initial_state = list(initial_state.keys())[0].integer all_qubits = [i for i in range(self.abstract_circuit.n_qubits)] if self.use_mapping: active_qubits = self.abstract_circuit.qubits # maps from reduced register to full register keymap = KeyMapSubregisterToRegister(subregister=active_qubits, register=all_qubits) else: keymap = KeyMapSubregisterToRegister(subregister=all_qubits, register=all_qubits) result = self.do_simulate(variables=variables, initial_state=keymap.inverted(initial_state).integer, *args, **kwargs) result.apply_keymap(keymap=keymap, initial_state=initial_state) return result
def from_dict(d): if isinstance(d, dict): return Noise(**d) elif isinstance(d, Noise): return d else: raise TequilaException('who the f**k do you think you are?')
def sample_all_z_hamiltonian(self, samples: int, hamiltonian, variables, *args, **kwargs): """ Sample from a Hamiltonian which only consists of Pauli-Z and unit operators Parameters ---------- samples number of samples to take hamiltonian the tequila hamiltonian args arguments for do_sample kwargs keyword arguments for do_sample Returns ------- samples, evaluated and summed Hamiltonian expectationvalue """ # make measurement instruction (measure all qubits in the Hamiltonian that are also in the circuit) abstract_qubits_H = hamiltonian.qubits assert len( abstract_qubits_H) != 0 # this case should be filtered out before # assert that the Hamiltonian was mapped before if not all(q in self.qubit_map.keys() for q in abstract_qubits_H): raise TequilaException( "Qubits in {}-qubit Hamiltonian were not traced out for {}-qubit circuit" .format(hamiltonian.n_qubits, self.n_qubits)) # run simulators counts = self.sample(samples=samples, read_out_qubits=abstract_qubits_H, variables=variables, *args, **kwargs) read_out_map = {q: i for i, q in enumerate(abstract_qubits_H)} # compute energy E = 0.0 for paulistring in hamiltonian.paulistrings: n_samples = 0 Etmp = 0.0 for key, count in counts.items(): # get all the non-trivial qubits of the current PauliString (meaning all Z operators) # and mapp them to the backend qubits mapped_ps_support = [ read_out_map[i] for i in paulistring._data.keys() ] # count all measurements that resulted in |1> for those qubits parity = [ k for i, k in enumerate(key.array) if i in mapped_ps_support ].count(1) # evaluate the PauliString sign = (-1)**parity Etmp += sign * count n_samples += count E += (Etmp / samples) * paulistring.coeff # small failsafe assert n_samples == samples return E
def simulate(self, variables, *args, **kwargs): self.update_variables(variables) result = [] for H in self.H: final_E = 0.0 if self.use_mapping: # The hamiltonian can be defined on more qubits as the unitaries qubits_h = H.qubits qubits_u = self.U.qubits all_qubits = list( set(qubits_h) | set(qubits_u) | set(range(self.U.abstract_circuit.max_qubit() + 1))) keymap = KeyMapSubregisterToRegister(subregister=qubits_u, register=all_qubits) else: if H.qubits != self.U.qubits: raise TequilaException( "Can not compute expectation value without using qubit mappings." " Your Hamiltonian and your Unitary do not act on the same set of qubits. " "Hamiltonian acts on {}, Unitary acts on {}".format( H.qubits, self.U.qubits)) keymap = KeyMapSubregisterToRegister(subregister=self.U.qubits, register=self.U.qubits) # TODO inefficient, let the backend do it if possible or interface some library simresult = self.U.simulate(variables=variables, *args, **kwargs) wfn = simresult.apply_keymap(keymap=keymap) final_E += wfn.compute_expectationvalue(operator=H) result.append(to_float(final_E)) return numpy.asarray(result)
def do_sample(self, samples, circuit, noise, abstract_qubits=None, *args, **kwargs) -> QubitWaveFunction: """ helper function for sampling. MUST be overwritten by inheritors. Parameters ---------- samples: int: the number of samples to take circuit: the circuit to sample from. Note: Not necessarily self.circuit! noise: the noise to apply to the sampled circuit. abstract_qubits: specify which qubits to measure. Default is all args kwargs Returns ------- QubitWaveFunction: the result of sampling. """ TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def __call__(self, variables, samples: int = None, *args, **kwargs): variables = format_variable_dictionary(variables=variables) if self._variables is not None and len(self._variables) > 0: if variables is None or ( not set(self._variables) <= set(variables.keys())): raise TequilaException( "BackendExpectationValue received not all variables. Circuit depends on variables {}, you gave {}" .format(self._variables, variables)) if samples is None: data = self.simulate(variables=variables, *args, **kwargs) else: data = self.sample(variables=variables, samples=samples, *args, **kwargs) if self._shape is None and self._contraction is None: # this is the default return numpy.sum(data) if self._shape is not None: data = data.reshape(self._shape) if self._contraction is None: return data else: return self._contraction(data)
def from_dict(d): if type(d) is dict: return QuantumNoise(**d) elif type(d) is QuantumNoise: return d else: raise TequilaException( 'object provided is neither a dictionary nor a QuantumNoise.')
def __call__(self, variables: typing.Dict[Variable, numbers.Real] = None, samples: int = None, *args, **kwargs): variables = format_variable_dictionary(variables=variables) if self._variables is not None and len(self._variables) > 0: if variables is None or set(self._variables) != set(variables.keys()): raise TequilaException("BackendCircuit received not all variables. Circuit depends on variables {}, you gave {}".format(self._variables, variables)) if samples is None: return self.simulate(variables=variables, noise_model=self.noise_model, *args, **kwargs) else: return self.sample(variables=variables, samples=samples, noise_model=self.noise_model, *args, **kwargs)
def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction: """ simulate the circuit via the backend. Parameters ---------- variables: the parameters with which to simulate the circuit. initial_state: Default = 0: one of several types; determines the base state onto which the circuit is applied. Default: the circuit is applied to the all-zero state. args kwargs Returns ------- QubitWaveFunction: the wavefunction of the system produced by the action of the circuit on the initial state. """ self.update_variables(variables) if isinstance(initial_state, BitString): initial_state = initial_state.integer if isinstance(initial_state, QubitWaveFunction): if len(initial_state.keys()) != 1: raise TequilaException( "only product states as initial states accepted") initial_state = list(initial_state.keys())[0].integer all_qubits = [i for i in range(self.abstract_circuit.n_qubits)] if self.use_mapping: active_qubits = self.abstract_circuit.qubits # maps from reduced register to full register keymap = KeyMapSubregisterToRegister(subregister=active_qubits, register=all_qubits) else: keymap = KeyMapSubregisterToRegister(subregister=all_qubits, register=all_qubits) result = self.do_simulate( variables=variables, initial_state=keymap.inverted(initial_state).integer, *args, **kwargs) result.apply_keymap(keymap=keymap, initial_state=initial_state) return result
def check_device(self, device): """ Verify if a device can be used in the selected backend. Overwritten by inheritors. Parameters ---------- device: the device to verify. Returns ------- Raises ------ TequilaException """ if device is not None: TequilaException('Devices not enabled for {}'.format( str(type(self))))
def step(self, objective: Objective, parameters: typing.Dict[Variable, numbers.Real]) -> \ typing.Dict[Variable, numbers.Real]: """ perform a single optimization step and return suggested parameters. Parameters ---------- objective: Objective: the compiled objective, to perform an optimization step for. MUST be one returned by prepare. parameters: dict: the parameters to use in performing the optimization step. Returns ------- dict dict of new suggested parameters. """ s = id(objective) try: gradients = self.gradient_lookup[s] active_keys = self.active_key_lookup[s] last_moment = self.moments_lookup[s] adam_step = self.step_lookup[s] except: raise TequilaException( 'Could not retrieve necessary information. Please use the prepare function before optimizing!' ) new, moments, grads = self.f(step=adam_step, gradients=gradients, active_keys=active_keys, moments=last_moment, v=parameters) back = {**parameters} for k in new.keys(): back[k] = new[k] save_grad = {} self.moments_lookup[s] = moments self.moments_trajectory[s].append(moments) if self.save_history: for i, k in enumerate(active_keys): save_grad[k] = grads[i] self.history.gradients.append(save_grad) self.step_lookup[s] += 1 self.__dx = grads # most recent gradient return back
def __call__(self, variables: typing.Dict[Variable, numbers.Real] = None, samples: int = None, *args, **kwargs): """ Simulate or sample the backend circuit. Parameters ---------- variables: dict: dictionary assigning values to the variables of the circuit. samples: int, optional: how many shots to sample with. If None, perform full wavefunction simulation. args kwargs Returns ------- Float: the result of simulating or sampling the circuit. """ variables = format_variable_dictionary(variables=variables) if self._variables is not None and len(self._variables) > 0: if variables is None or set(self._variables) != set( variables.keys()): raise TequilaException( "BackendCircuit received not all variables. Circuit depends on variables {}, you gave {}" .format(self._variables, variables)) if samples is None: return self.simulate(variables=variables, noise=self.noise, *args, **kwargs) else: return self.sample(variables=variables, samples=samples, noise=self.noise, *args, **kwargs)
def retrieve_device(self, device): """ get the instantiated backend device object, from user provided object (e.g, a string). Must be overwritten by inheritors, to use devices. Parameters ---------- device: object which points to the device in question, to be returned. Returns ------- Type: varies by backend. """ if device is None: return device else: TequilaException('Devices not enabled for {}'.format( str(type(self))))
def do_simulate(self, variables, initial_state, *args, **kwargs) -> QubitWaveFunction: """ helper for simulation. MUST be overwritten by inheritors. Parameters ---------- variables: the variables with which the circuit may be simulated. initial_state: the initial state in which the system is in prior to the application of the circuit. args kwargs Returns ------- QubitWaveFunction the result of simulating the circuit. """ TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def step(self, objective, parameters): s = id(objective) try: gradients = self.gradient_lookup[s] active_keys = self.active_key_lookup[s] last_moment = self.moments_lookup[s] adam_step = self.step_lookup[s] except: raise TequilaException( 'Could not retrieve necessary information. Please use the prepare function before optimizing!') new, moments, grads = self.f(step=adam_step, gradients=gradients, active_keys=active_keys, moments=last_moment, v=parameters) back = {**parameters} for k in new.keys(): back[k] = new[k] save_grad = {} self.moments_lookup[s] = moments self.moments_trajectory[s].append(moments) if self.save_history: for i, k in enumerate(active_keys): save_grad[k] = grads[i] self.history.gradients.append(save_grad) self.step_lookup[s] += 1 return back
def do_sample(self, samples, circuit, noise, *args, **kwargs) -> QubitWaveFunction: TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def do_simulate(self, variables, initial_state, *args, **kwargs) -> QubitWaveFunction: TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def convert_measurements(self, backend_result) -> QubitWaveFunction: TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def add_parametrized_gate(self, gate, circuit, *args, **kwargs): raise TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def initialize_circuit(self, *args, **kwargs): TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def add_measurement(self, circuit, target_qubits, *args, **kwargs): TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def add_basic_gate(self, gate, circuit, *args, **kwargs): TequilaException( "Backend Handler needs to be overwritten for supported simulators")
def __init__(self, abstract_circuit: QCircuit, variables, noise=None, device=None, qubit_map=None, optimize_circuit=True, *args, **kwargs): """ Parameters ---------- abstract_circuit: QCircuit: the circuit which is to be rendered in the backend language. variables: values for the variables of abstract_circuit noise: optional: noise to apply to abstract circuit. device: optional: device on which to sample (or emulate sampling) abstract circuit. qubit_map: dictionary: a qubit map which maps the abstract qubits in the abstract_circuit to the qubits on the backend there is no need to initialize the corresponding backend types the dictionary should simply be {int:int} (preferred) or {int:name} if None the default will map to qubits 0 ... n_qubits -1 in the backend optimize_circuit: bool: whether or not to attempt backend depth optimization. Defaults to true. args kwargs """ self._input_args = { "abstract_circuit": abstract_circuit, "variables": variables, "noise": noise, "qubit_map": qubit_map, "optimize_circuits": optimize_circuit, "device": device, **kwargs } self.no_translation = False self._variables = tuple(abstract_circuit.extract_variables()) compiler_arguments = self.compiler_arguments if noise is not None: compiler_arguments["cc_max"] = True compiler_arguments["controlled_phase"] = True compiler_arguments["controlled_rotation"] = True compiler_arguments["hadamard_power"] = True # compile the abstract_circuit c = compiler.Compiler(**compiler_arguments) if qubit_map is None: qubit_map = {q: i for i, q in enumerate(abstract_circuit.qubits)} elif not qubit_map == { q: i for i, q in enumerate(abstract_circuit.qubits) }: warnings.warn( "reveived custom qubit_map = {}\n" "This is not fully integrated and might result in unexpected behaviour!" .format(qubit_map), TequilaWarning) if len(qubit_map) > abstract_circuit.max_qubit() + 1: raise TequilaException( "Custom qubit_map has too many qubits {} vs {}".format( len(qubit_map), abstract_circuit.max_qubit() + 1)) if max(qubit_map.keys()) > abstract_circuit.max_qubit(): raise TequilaException( "Custom qubit_map tries to assign qubit {} but we only have {}" .format(max(qubit_map.keys()), abstract_circuit.max_qubit())) # qubit map is initialized to have BackendQubits as values (they carry number and instance attributes) self.qubit_map = self.make_qubit_map(qubit_map) # pre-compilation (still an abstract ciruit, but with gates decomposed depending on backend requirements) compiled = c(abstract_circuit) self.abstract_circuit = compiled self.noise = noise self.check_device(device) self.device = self.retrieve_device(device) # translate into the backend object self.circuit = self.create_circuit(abstract_circuit=compiled, variables=variables) if optimize_circuit and noise is None: self.circuit = self.optimize_circuit(circuit=self.circuit)