def test_call(): """ Test Qobj: Call """ # Make test objects. psi = rand_ket(3) rho = rand_dm_ginibre(3) U = rand_unitary(3) S = rand_super_bcsz(3) # Case 0: oper(ket). assert U(psi) == U * psi # Case 1: oper(oper). Should raise TypeError. with expect_exception(TypeError): U(rho) # Case 2: super(ket). assert S(psi) == vector_to_operator(S * operator_to_vector(ket2dm(psi))) # Case 3: super(oper). assert S(rho) == vector_to_operator(S * operator_to_vector(rho)) # Case 4: super(super). Should raise TypeError. with expect_exception(TypeError): S(S)
def case(map, state): S = to_super(map) A, B = to_stinespring(map) q1 = vector_to_operator(S * operator_to_vector(state)) # FIXME: problem if Kraus index is implicitly # ptraced! q2 = (A * state * B.dag()).ptrace((0, )) assert_((q1 - q2).norm('tr') <= thresh)
def case(map, state): S = to_super(map) A, B = to_stinespring(map) q1 = vector_to_operator( S * operator_to_vector(state) ) # FIXME: problem if Kraus index is implicitly # ptraced! q2 = (A * state * B.dag()).ptrace((0,)) assert_((q1 - q2).norm('tr') <= thresh)
def test_stinespring_agrees(self, dimension): """ Stinespring: Partial Tr over pair agrees w/ supermatrix. """ map = rand_super_bcsz(dimension) state = rand_dm_ginibre(dimension) S = to_super(map) A, B = to_stinespring(map) q1 = vector_to_operator(S * operator_to_vector(state)) # FIXME: problem if Kraus index is implicitly # ptraced! q2 = (A * state * B.dag()).ptrace((0, )) assert (q1 - q2).norm('tr') <= tol
def propagator(H, t, c_op_list, args=None, options=None, sparse=False): """ Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)` where :math:`\\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t if isinstance(H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T # todo: evolving a batch of wave functions: # psi_0_list = [basis(N, n) for n in range(N)] # psi_t_list = mesolve(H, psi_0_list, [0, t], [], [], args, options) # for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) for n in range(0, N): psi0 = basis(N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, [], [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if sparse: for n in range(N * N): psi0 = basis(N * N, n) psi0.dims = [dims[0], 1] rho0 = vector_to_operator(psi0) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = operator_to_vector( output.states[k]).full(squeeze=True) else: for n in range(N * N): psi0 = basis(N * N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
def propagator(H, t, c_op_list, args=None, options=None, sparse=False, progress_bar=None): """ Calculate the propagator U(t) for the density matrix or wave function such that :math:`\psi(t) = U(t)\psi(0)` or :math:`\\rho_{\mathrm vec}(t) = U(t) \\rho_{\mathrm vec}(0)` where :math:`\\rho_{\mathrm vec}` is the vector representation of the density matrix. Parameters ---------- H : qobj or list Hamiltonian as a Qobj instance of a nested list of Qobjs and coefficients in the list-string or list-function format for time-dependent Hamiltonians (see description in :func:`qutip.mesolve`). t : float or array-like Time or list of times for which to evaluate the propagator. c_op_list : list List of qobj collapse operators. args : list/array/dictionary Parameters to callback functions for time-dependent Hamiltonians and collapse operators. options : :class:`qutip.Options` with options for the ODE solver. progress_bar: BaseProgressBar Optional instance of BaseProgressBar, or a subclass thereof, for showing the progress of the simulation. By default no progress bar is used, and if set to True a TextProgressBar will be used. Returns ------- a : qobj Instance representing the propagator :math:`U(t)`. """ if progress_bar is None: progress_bar = BaseProgressBar() elif progress_bar is True: progress_bar = TextProgressBar() if options is None: options = Options() options.rhs_reuse = True rhs_clear() if isinstance(t, (int, float, np.integer, np.floating)): tlist = [0, t] else: tlist = t if isinstance( H, (types.FunctionType, types.BuiltinFunctionType, functools.partial)): H0 = H(0.0, args) elif isinstance(H, list): H0 = H[0][0] if isinstance(H[0], list) else H[0] else: H0 = H if len(c_op_list) == 0 and H0.isoper: # calculate propagator for the wave function N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) output = sesolve(H, psi0, tlist, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = output.states[k].full().T progress_bar.finished() # todo: evolving a batch of wave functions: # psi_0_list = [basis(N, n) for n in range(N)] # psi_t_list = mesolve(H, psi_0_list, [0, t], [], [], args, options) # for n in range(0, N): # u[:,n] = psi_t_list[n][1].full().T elif len(c_op_list) == 0 and H0.issuper: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = H0.dims u = np.zeros([N, N, len(tlist)], dtype=complex) progress_bar.start(N) for n in range(0, N): progress_bar.update(n) psi0 = basis(N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, [], [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() else: # calculate the propagator for the vector representation of the # density matrix (a superoperator propagator) N = H0.shape[0] dims = [H0.dims, H0.dims] u = np.zeros([N * N, N * N, len(tlist)], dtype=complex) if sparse: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) psi0 = basis(N * N, n) psi0.dims = [dims[0], 1] rho0 = vector_to_operator(psi0) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = operator_to_vector( output.states[k]).full(squeeze=True) progress_bar.finished() else: progress_bar.start(N * N) for n in range(N * N): progress_bar.update(n) psi0 = basis(N * N, n) rho0 = Qobj(vec2mat(psi0.full())) output = mesolve(H, rho0, tlist, c_op_list, [], args, options) for k, t in enumerate(tlist): u[:, n, k] = mat2vec(output.states[k].full()).T progress_bar.finished() if len(tlist) == 2: return Qobj(u[:, :, 1], dims=dims) else: return [Qobj(u[:, :, k], dims=dims) for k in range(len(tlist))]
def dnorm(A, B=None, solver="CVXOPT", verbose=False, force_solve=False, sparse=True): """ Calculates the diamond norm of the quantum map q_oper, using the simplified semidefinite program of [Wat12]_. The diamond norm SDP is solved by using CVXPY_. Parameters ---------- A : Qobj Quantum map to take the diamond norm of. B : Qobj or None If provided, the diamond norm of :math:`A - B` is taken instead. solver : str Solver to use with CVXPY. One of "CVXOPT" (default) or "SCS". The latter tends to be significantly faster, but somewhat less accurate. verbose : bool If True, prints additional information about the solution. force_solve : bool If True, forces dnorm to solve the associated SDP, even if a special case is known for the argument. sparse : bool Whether to use sparse matrices in the convex optimisation problem. Default True. Returns ------- dn : float Diamond norm of q_oper. Raises ------ ImportError If CVXPY cannot be imported. .. _cvxpy: http://www.cvxpy.org/en/latest/ """ if cvxpy is None: # pragma: no cover raise ImportError("dnorm() requires CVXPY to be installed.") # We follow the strategy of using Watrous' simpler semidefinite # program in its primal form. This is the same strategy used, # for instance, by both pyGSTi and SchattenNorms.jl. (By contrast, # QETLAB uses the dual problem.) # Check if A and B are both unitaries. If so, then we can without # loss of generality choose B to be the identity by using the # unitary invariance of the diamond norm, # || A - B ||_♢ = || A B⁺ - I ||_♢. # Then, using the technique mentioned by each of Johnston and # da Silva, # || A B⁺ - I ||_♢ = max_{i, j} | \lambda_i(A B⁺) - \lambda_j(A B⁺) |, # where \lambda_i(U) is the ith eigenvalue of U. if ( # There's a lot of conditions to check for this path. not force_solve and B is not None and # Only check if they aren't superoperators. A.type == "oper" and B.type == "oper" and # The difference of unitaries optimization is currently # only implemented for d == 2. Much of the code below is more general, # though, in anticipation of generalizing the optimization. A.shape[0] == 2 ): # Make an identity the same size as A and B to # compare against. I = qeye(A.dims[0]) # Compare to B first, so that an error is raised # as soon as possible. Bd = B.dag() if ( (B * Bd - I).norm() < 1e-6 and (A * A.dag() - I).norm() < 1e-6 ): # Now we are on the fast path, so let's compute the # eigenvalues, then find the diameter of the smallest circle # containing all of them. # # For now, this is only implemented for dim = 2, such that # generalizing here will allow for generalizing the optimization. # A reasonable approach would probably be to use Welzl's algorithm # (https://en.wikipedia.org/wiki/Smallest-circle_problem). U = A * B.dag() eigs = U.eigenenergies() eig_distances = np.abs(eigs[:, None] - eigs[None, :]) return np.max(eig_distances) # Force the input superoperator to be a Choi matrix. J = to_choi(A) if B is not None: J -= to_choi(B) # Watrous 2012 also points out that the diamond norm of Lambda # is the same as the completely-bounded operator-norm (∞-norm) # of the dual map of Lambda. We can evaluate that norm much more # easily if Lambda is completely positive, since then the largest # eigenvalue is the same as the largest singular value. if not force_solve and J.iscp: S_dual = to_super(J.dual_chan()) vec_eye = operator_to_vector(qeye(S_dual.dims[1][1])) op = vector_to_operator(S_dual * vec_eye) # The 2-norm was not implemented for sparse matrices as of the time # of this writing. Thus, we must yet again go dense. return la.norm(op.data.todense(), 2) # If we're still here, we need to actually solve the problem. # Assume square... dim = np.prod(J.dims[0][0]) J_dat = J.data if not sparse: # The parameters and constraints only depend on the dimension, so # we can cache them efficiently. problem, Jr, Ji = dnorm_problem(dim) # Load the parameters with the Choi matrix passed in. Jr.value = sp.csr_matrix((J_dat.data.real, J_dat.indices, J_dat.indptr), shape=J_dat.shape).toarray() Ji.value = sp.csr_matrix((J_dat.data.imag, J_dat.indices, J_dat.indptr), shape=J_dat.shape).toarray() else: # The parameters do not depend solely on the dimension, # so we can not cache them efficiently. problem = dnorm_sparse_problem(dim, J_dat) problem.solve(solver=solver, verbose=verbose) return problem.value
def dnorm(A, B=None, solver="CVXOPT", verbose=False, force_solve=False): """ Calculates the diamond norm of the quantum map q_oper, using the simplified semidefinite program of [Wat12]_. The diamond norm SDP is solved by using CVXPY_. Parameters ---------- A : Qobj Quantum map to take the diamond norm of. B : Qobj or None If provided, the diamond norm of :math:`A - B` is taken instead. solver : str Solver to use with CVXPY. One of "CVXOPT" (default) or "SCS". The latter tends to be significantly faster, but somewhat less accurate. verbose : bool If True, prints additional information about the solution. force_solve : bool If True, forces dnorm to solve the associated SDP, even if a special case is known for the argument. Returns ------- dn : float Diamond norm of q_oper. Raises ------ ImportError If CVXPY cannot be imported. .. _cvxpy: http://www.cvxpy.org/en/latest/ """ if cvxpy is None: # pragma: no cover raise ImportError("dnorm() requires CVXPY to be installed.") # We follow the strategy of using Watrous' simpler semidefinite # program in its primal form. This is the same strategy used, # for instance, by both pyGSTi and SchattenNorms.jl. (By contrast, # QETLAB uses the dual problem.) # Check if A and B are both unitaries. If so, then we can without # loss of generality choose B to be the identity by using the # unitary invariance of the diamond norm, # || A - B ||_♢ = || A B⁺ - I ||_♢. # Then, using the technique mentioned by each of Johnston and # da Silva, # || A B⁺ - I ||_♢ = max_{i, j} | \lambda_i(A B⁺) - \lambda_j(A B⁺) |, # where \lambda_i(U) is the ith eigenvalue of U. if ( # There's a lot of conditions to check for this path. not force_solve and B is not None and # Only check if they aren't superoperators. A.type == "oper" and B.type == "oper" and # The difference of unitaries optimization is currently # only implemented for d == 2. Much of the code below is more general, # though, in anticipation of generalizing the optimization. A.shape[0] == 2 ): # Make an identity the same size as A and B to # compare against. I = qeye(A.dims[0]) # Compare to B first, so that an error is raised # as soon as possible. Bd = B.dag() if ( (B * Bd - I).norm() < 1e-6 and (A * A.dag() - I).norm() < 1e-6 ): # Now we are on the fast path, so let's compute the # eigenvalues, then find the diameter of the smallest circle # containing all of them. # # For now, this is only implemented for dim = 2, such that # generalizing here will allow for generalizing the optimization. # A reasonable approach would probably be to use Welzl's algorithm # (https://en.wikipedia.org/wiki/Smallest-circle_problem). U = A * B.dag() eigs = U.eigenenergies() eig_distances = np.abs(eigs[:, None] - eigs[None, :]) return np.max(eig_distances) # Force the input superoperator to be a Choi matrix. J = to_choi(A) if B is not None: J -= to_choi(B) # Watrous 2012 also points out that the diamond norm of Lambda # is the same as the completely-bounded operator-norm (∞-norm) # of the dual map of Lambda. We can evaluate that norm much more # easily if Lambda is completely positive, since then the largest # eigenvalue is the same as the largest singular value. if not force_solve and J.iscp: S_dual = to_super(J.dual_chan()) vec_eye = operator_to_vector(qeye(S_dual.dims[1][1])) op = vector_to_operator(S_dual * vec_eye) # The 2-norm was not implemented for sparse matrices as of the time # of this writing. Thus, we must yet again go dense. return la.norm(op.data.todense(), 2) # If we're still here, we need to actually solve the problem. # Assume square... dim = np.prod(J.dims[0][0]) # The constraints only depend on the dimension, so # we can cache them efficiently. problem, Jr, Ji, X, rho0, rho1 = dnorm_problem(dim) # Load the parameters with the Choi matrix passed in. J_dat = J.data Jr.value = sp.csr_matrix((J_dat.data.real, J_dat.indices, J_dat.indptr), shape=J_dat.shape) Ji.value = sp.csr_matrix((J_dat.data.imag, J_dat.indices, J_dat.indptr), shape=J_dat.shape) # Finally, set up and run the problem. problem.solve(solver=solver, verbose=verbose) return problem.value
def hinton(rho, xlabels=None, ylabels=None, title=None, ax=None, cmap=None, label_top=True): """Draws a Hinton diagram for visualizing a density matrix or superoperator. Parameters ---------- rho : qobj Input density matrix or superoperator. xlabels : list of strings or False list of x labels ylabels : list of strings or False list of y labels title : string title of the plot (optional) ax : a matplotlib axes instance The axes context in which the plot will be drawn. cmap : a matplotlib colormap instance Color map to use when plotting. label_top : bool If True, x-axis labels will be placed on top, otherwise they will appear below the plot. Returns ------- fig, ax : tuple A tuple of the matplotlib figure and axes instances used to produce the figure. Raises ------ ValueError Input argument is not a quantum object. """ # Apply default colormaps. # TODO: abstract this away into something that makes default # colormaps. cmap = ( (cm.Greys_r if settings.colorblind_safe else cm.RdBu) if cmap is None else cmap ) # Extract plotting data W from the input. if isinstance(rho, Qobj): if rho.isoper: W = rho.full() # Create default labels if none are given. if xlabels is None or ylabels is None: labels = _cb_labels(rho.dims[0]) xlabels = xlabels if xlabels is not None else list(labels[0]) ylabels = ylabels if ylabels is not None else list(labels[1]) elif rho.isoperket: W = vector_to_operator(rho).full() elif rho.isoperbra: W = vector_to_operator(rho.dag()).full() elif rho.issuper: if not _isqubitdims(rho.dims): raise ValueError("Hinton plots of superoperators are " "currently only supported for qubits.") # Convert to a superoperator in the Pauli basis, # so that all the elements are real. sqobj = _super_to_superpauli(rho) nq = int(log2(sqobj.shape[0]) / 2) W = sqobj.full().T # Create default labels, too. if (xlabels is None) or (ylabels is None): labels = list(map("".join, it.product("IXYZ", repeat=nq))) xlabels = xlabels if xlabels is not None else labels ylabels = ylabels if ylabels is not None else labels else: raise ValueError( "Input quantum object must be an operator or superoperator." ) else: W = rho if ax is None: fig, ax = plt.subplots(1, 1, figsize=(8, 6)) else: fig = None if not (xlabels or ylabels): ax.axis('off') ax.axis('equal') ax.set_frame_on(False) height, width = W.shape w_max = 1.25 * max(abs(np.diag(np.matrix(W)))) if w_max <= 0.0: w_max = 1.0 ax.fill(array([0, width, width, 0]), array([0, 0, height, height]), color=cmap(128)) for x in range(width): for y in range(height): _x = x + 1 _y = y + 1 if np.real(W[x, y]) > 0.0: _blob(_x - 0.5, height - _y + 0.5, abs(W[x, y]), w_max, min(1, abs(W[x, y]) / w_max), cmap=cmap) else: _blob(_x - 0.5, height - _y + 0.5, -abs(W[ x, y]), w_max, min(1, abs(W[x, y]) / w_max), cmap=cmap) # color axis norm = mpl.colors.Normalize(-abs(W).max(), abs(W).max()) cax, kw = mpl.colorbar.make_axes(ax, shrink=0.75, pad=.1) mpl.colorbar.ColorbarBase(cax, norm=norm, cmap=cmap) # x axis ax.xaxis.set_major_locator(plt.IndexLocator(1, 0.5)) if xlabels: ax.set_xticklabels(xlabels) if label_top: ax.xaxis.tick_top() ax.tick_params(axis='x', labelsize=14) # y axis ax.yaxis.set_major_locator(plt.IndexLocator(1, 0.5)) if ylabels: ax.set_yticklabels(list(reversed(ylabels))) ax.tick_params(axis='y', labelsize=14) return fig, ax
def hinton(rho, xlabels=None, ylabels=None, title=None, ax=None, cmap=None, label_top=True): """Draws a Hinton diagram for visualizing a density matrix or superoperator. Parameters ---------- rho : qobj Input density matrix or superoperator. xlabels : list of strings or False list of x labels ylabels : list of strings or False list of y labels title : string title of the plot (optional) ax : a matplotlib axes instance The axes context in which the plot will be drawn. cmap : a matplotlib colormap instance Color map to use when plotting. label_top : bool If True, x-axis labels will be placed on top, otherwise they will appear below the plot. Returns ------- fig, ax : tuple A tuple of the matplotlib figure and axes instances used to produce the figure. Raises ------ ValueError Input argument is not a quantum object. """ # Apply default colormaps. # TODO: abstract this away into something that makes default # colormaps. cmap = ((cm.Greys_r if settings.colorblind_safe else cm.RdBu) if cmap is None else cmap) # Extract plotting data W from the input. if isinstance(rho, Qobj): if rho.isoper: W = rho.full() # Create default labels if none are given. if xlabels is None or ylabels is None: labels = _cb_labels(rho.dims[0]) xlabels = xlabels if xlabels is not None else list(labels[0]) ylabels = ylabels if ylabels is not None else list(labels[1]) elif rho.isoperket: W = vector_to_operator(rho).full() elif rho.isoperbra: W = vector_to_operator(rho.dag()).full() elif rho.issuper: if not _isqubitdims(rho.dims): raise ValueError("Hinton plots of superoperators are " "currently only supported for qubits.") # Convert to a superoperator in the Pauli basis, # so that all the elements are real. sqobj = _super_to_superpauli(rho) nq = int(log2(sqobj.shape[0]) / 2) W = sqobj.full().T # Create default labels, too. if (xlabels is None) or (ylabels is None): labels = list(map("".join, it.product("IXYZ", repeat=nq))) xlabels = xlabels if xlabels is not None else labels ylabels = ylabels if ylabels is not None else labels else: raise ValueError( "Input quantum object must be an operator or superoperator.") else: W = rho if ax is None: fig, ax = plt.subplots(1, 1, figsize=(8, 6)) else: fig = None if not (xlabels or ylabels): ax.axis('off') ax.axis('equal') ax.set_frame_on(False) height, width = W.shape w_max = 1.25 * max(abs(np.diag(np.matrix(W)))) if w_max <= 0.0: w_max = 1.0 ax.fill(array([0, width, width, 0]), array([0, 0, height, height]), color=cmap(128)) for x in range(width): for y in range(height): _x = x + 1 _y = y + 1 if np.real(W[x, y]) > 0.0: _blob(_x - 0.5, height - _y + 0.5, abs(W[x, y]), w_max, min(1, abs(W[x, y]) / w_max), cmap=cmap) else: _blob(_x - 0.5, height - _y + 0.5, -abs(W[x, y]), w_max, min(1, abs(W[x, y]) / w_max), cmap=cmap) # color axis norm = mpl.colors.Normalize(-abs(W).max(), abs(W).max()) cax, kw = mpl.colorbar.make_axes(ax, shrink=0.75, pad=.1) mpl.colorbar.ColorbarBase(cax, norm=norm, cmap=cmap) # x axis ax.xaxis.set_major_locator(plt.IndexLocator(1, 0.5)) if xlabels: ax.set_xticklabels(xlabels) if label_top: ax.xaxis.tick_top() ax.tick_params(axis='x', labelsize=14) # y axis ax.yaxis.set_major_locator(plt.IndexLocator(1, 0.5)) if ylabels: ax.set_yticklabels(list(reversed(ylabels))) ax.tick_params(axis='y', labelsize=14) return fig, ax
print("rho_targ:\n{}\n".format(rho_targ)) rho_targ_vec = operator_to_vector(rho_targ) print("rho_targ_vec:\n{}\n".format(rho_targ_vec)) #print("L0:\n{}\n".format(L0)) #print("LC_x:\n{}\n".format(LC_x)) #print("LC_y:\n{}\n".format(LC_y)) #print("LC_z:\n{}\n".format(LC_z)) print("Fidelity rho0, rho_targ: {}".format(fidelity(rho0, rho_targ))) rho_diff = (rho0 - rho_targ) fid_err = 0.5 * (rho_diff.dag() * rho_diff).tr() print("fid_err: {}, fid: {}".format(fid_err, np.sqrt(1 - fid_err))) rho0_evo_map = vector_to_operator(E_targ * rho0_vec) print("Fidelity rho_targ, rho0_evo_map: {}".format( fidelity(rho_targ, rho0_evo_map))) #Drift drift = L0 #Controls #ctrls = [LC_x, LC_z] ctrls = [LC_y] #ctrls = [LC_x] # Number of ctrls n_ctrls = len(ctrls) # ***** Define time evolution parameters *****