1# This file is part of QuTiP: Quantum Toolbox in Python. 2# 3# Copyright (c) 2011 and later, Paul D. Nation and Robert J. Johansson. 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted provided that the following conditions are 8# met: 9# 10# 1. Redistributions of source code must retain the above copyright notice, 11# this list of conditions and the following disclaimer. 12# 13# 2. Redistributions in binary form must reproduce the above copyright 14# notice, this list of conditions and the following disclaimer in the 15# documentation and/or other materials provided with the distribution. 16# 17# 3. Neither the name of the QuTiP: Quantum Toolbox in Python nor the names 18# of its contributors may be used to endorse or promote products derived 19# from this software without specific prior written permission. 20# 21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32############################################################################### 33from collections.abc import Iterable 34import numbers 35 36import numpy as np 37 38from qutip.qobj import Qobj 39from qutip.qobjevo import QobjEvo 40from qutip.qip.operations.gates import globalphase 41from qutip.tensor import tensor 42from qutip.mesolve import mesolve 43from qutip.qip.circuit import QubitCircuit 44from qutip.qip.device.processor import Processor 45 46 47__all__ = ['ModelProcessor'] 48 49 50class ModelProcessor(Processor): 51 """ 52 The base class for a circuit processor simulating a physical device, 53 e.g cavityQED, spinchain. 54 The available Hamiltonian of the system is predefined. 55 The processor can simulate the evolution under the given 56 control pulses either numerically or analytically. 57 It cannot be used alone, please refer to the sub-classes. 58 (Only additional attributes are documented here, for others please 59 refer to the parent class :class:`.Processor`) 60 61 Parameters 62 ---------- 63 N: int 64 The number of component systems. 65 66 correct_global_phase: boolean, optional 67 If true, the analytical solution will track the global phase. It 68 has no effect on the numerical solution. 69 70 t1: list or float 71 Characterize the decoherence of amplitude damping for 72 each qubit. A list of size `N` or a float for all qubits. 73 74 t2: list of float 75 Characterize the decoherence of dephasing for 76 each qubit. A list of size `N` or a float for all qubits. 77 78 Attributes 79 ---------- 80 params: dict 81 A Python dictionary contains the name and the value of the parameters 82 in the physical realization, such as laser frequency, detuning etc. 83 84 correct_global_phase: float 85 Save the global phase, the analytical solution 86 will track the global phase. 87 It has no effect on the numerical solution. 88 """ 89 def __init__(self, N, correct_global_phase=True, t1=None, t2=None): 90 super(ModelProcessor, self).__init__(N, t1=t1, t2=t2) 91 self.correct_global_phase = correct_global_phase 92 self.global_phase = 0. 93 self._params = {} 94 95 def to_array(self, params, N): 96 """ 97 Transfer a parameter to an array. 98 """ 99 if isinstance(params, numbers.Real): 100 return np.asarray([params] * N) 101 elif isinstance(params, Iterable): 102 return np.asarray(params) 103 104 def set_up_params(self): 105 """ 106 Save the parameters in the attribute `params` and check the validity. 107 (Defined in subclasses) 108 109 Notes 110 ----- 111 All parameters will be multiplied by 2*pi for simplicity 112 """ 113 raise NotImplementedError("Parameters should be defined in subclass.") 114 115 @property 116 def params(self): 117 return self._params 118 119 @params.setter 120 def params(self, par): 121 self._params = par 122 123 def run_state(self, init_state=None, analytical=False, qc=None, 124 states=None, **kwargs): 125 """ 126 If `analytical` is False, use :func:`qutip.mesolve` to 127 calculate the time of the state evolution 128 and return the result. Other arguments of mesolve can be 129 given as keyword arguments. 130 If `analytical` is True, calculate the propagator 131 with matrix exponentiation and return a list of matrices. 132 133 Parameters 134 ---------- 135 init_state: Qobj 136 Initial density matrix or state vector (ket). 137 138 analytical: boolean 139 If True, calculate the evolution with matrices exponentiation. 140 141 qc: :class:`.QubitCircuit`, optional 142 A quantum circuit. If given, it first calls the ``load_circuit`` 143 and then calculate the evolution. 144 145 states: :class:`qutip.Qobj`, optional 146 Old API, same as init_state. 147 148 **kwargs 149 Keyword arguments for the qutip solver. 150 151 Returns 152 ------- 153 evo_result: :class:`qutip.Result` 154 If ``analytical`` is False, an instance of the class 155 :class:`qutip.Result` will be returned. 156 157 If ``analytical`` is True, a list of matrices representation 158 is returned. 159 """ 160 if qc is not None: 161 self.load_circuit(qc) 162 return super(ModelProcessor, self).run_state( 163 init_state=init_state, analytical=analytical, 164 states=states, **kwargs) 165 166 def get_ops_and_u(self): 167 """ 168 Get the labels for each Hamiltonian. 169 170 Returns 171 ------- 172 ctrls: list 173 The list of Hamiltonians 174 coeffs: array_like 175 The transposed pulse matrix 176 """ 177 return (self.ctrls, self.get_full_coeffs().T) 178 179 def pulse_matrix(self, dt=0.01): 180 """ 181 Generates the pulse matrix for the desired physical system. 182 183 Returns 184 ------- 185 t, u, labels: 186 Returns the total time and label for every operation. 187 """ 188 ctrls = self.ctrls 189 coeffs = self.get_full_coeffs().T 190 191 # FIXME This might becomes a problem if new tlist other than 192 # int the default pulses are added. 193 tlist = self.get_full_tlist() 194 dt_list = tlist[1:] - tlist[:-1] 195 t_tot = tlist[-1] 196 num_step = int(np.ceil(t_tot / dt)) 197 198 t = np.linspace(0, t_tot, num_step) 199 u = np.zeros((len(ctrls), num_step)) 200 201 t_start = 0 202 for n in range(len(dt_list)): 203 t_idx_len = int(np.floor(dt_list[n] / dt)) 204 mm = 0 205 for m in range(len(ctrls)): 206 u[mm, t_start:(t_start + t_idx_len)] = (np.ones(t_idx_len) * 207 coeffs[n, m]) 208 mm += 1 209 t_start += t_idx_len 210 211 return t, u, self.get_operators_labels() 212