Spaces:
Sleeping
Sleeping
| from itertools import product | |
| from sympy.core.add import Add | |
| from sympy.core.containers import Tuple | |
| from sympy.core.function import expand | |
| from sympy.core.mul import Mul | |
| from sympy.core.singleton import S | |
| from sympy.functions.elementary.exponential import log | |
| from sympy.matrices.dense import MutableDenseMatrix as Matrix | |
| from sympy.printing.pretty.stringpict import prettyForm | |
| from sympy.physics.quantum.dagger import Dagger | |
| from sympy.physics.quantum.operator import HermitianOperator | |
| from sympy.physics.quantum.represent import represent | |
| from sympy.physics.quantum.matrixutils import numpy_ndarray, scipy_sparse_matrix, to_numpy | |
| from sympy.physics.quantum.tensorproduct import TensorProduct, tensor_product_simp | |
| from sympy.physics.quantum.trace import Tr | |
| class Density(HermitianOperator): | |
| """Density operator for representing mixed states. | |
| TODO: Density operator support for Qubits | |
| Parameters | |
| ========== | |
| values : tuples/lists | |
| Each tuple/list should be of form (state, prob) or [state,prob] | |
| Examples | |
| ======== | |
| Create a density operator with 2 states represented by Kets. | |
| >>> from sympy.physics.quantum.state import Ket | |
| >>> from sympy.physics.quantum.density import Density | |
| >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) | |
| >>> d | |
| Density((|0>, 0.5),(|1>, 0.5)) | |
| """ | |
| def _eval_args(cls, args): | |
| # call this to qsympify the args | |
| args = super()._eval_args(args) | |
| for arg in args: | |
| # Check if arg is a tuple | |
| if not (isinstance(arg, Tuple) and len(arg) == 2): | |
| raise ValueError("Each argument should be of form [state,prob]" | |
| " or ( state, prob )") | |
| return args | |
| def states(self): | |
| """Return list of all states. | |
| Examples | |
| ======== | |
| >>> from sympy.physics.quantum.state import Ket | |
| >>> from sympy.physics.quantum.density import Density | |
| >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) | |
| >>> d.states() | |
| (|0>, |1>) | |
| """ | |
| return Tuple(*[arg[0] for arg in self.args]) | |
| def probs(self): | |
| """Return list of all probabilities. | |
| Examples | |
| ======== | |
| >>> from sympy.physics.quantum.state import Ket | |
| >>> from sympy.physics.quantum.density import Density | |
| >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) | |
| >>> d.probs() | |
| (0.5, 0.5) | |
| """ | |
| return Tuple(*[arg[1] for arg in self.args]) | |
| def get_state(self, index): | |
| """Return specific state by index. | |
| Parameters | |
| ========== | |
| index : index of state to be returned | |
| Examples | |
| ======== | |
| >>> from sympy.physics.quantum.state import Ket | |
| >>> from sympy.physics.quantum.density import Density | |
| >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) | |
| >>> d.states()[1] | |
| |1> | |
| """ | |
| state = self.args[index][0] | |
| return state | |
| def get_prob(self, index): | |
| """Return probability of specific state by index. | |
| Parameters | |
| =========== | |
| index : index of states whose probability is returned. | |
| Examples | |
| ======== | |
| >>> from sympy.physics.quantum.state import Ket | |
| >>> from sympy.physics.quantum.density import Density | |
| >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) | |
| >>> d.probs()[1] | |
| 0.500000000000000 | |
| """ | |
| prob = self.args[index][1] | |
| return prob | |
| def apply_op(self, op): | |
| """op will operate on each individual state. | |
| Parameters | |
| ========== | |
| op : Operator | |
| Examples | |
| ======== | |
| >>> from sympy.physics.quantum.state import Ket | |
| >>> from sympy.physics.quantum.density import Density | |
| >>> from sympy.physics.quantum.operator import Operator | |
| >>> A = Operator('A') | |
| >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) | |
| >>> d.apply_op(A) | |
| Density((A*|0>, 0.5),(A*|1>, 0.5)) | |
| """ | |
| new_args = [(op*state, prob) for (state, prob) in self.args] | |
| return Density(*new_args) | |
| def doit(self, **hints): | |
| """Expand the density operator into an outer product format. | |
| Examples | |
| ======== | |
| >>> from sympy.physics.quantum.state import Ket | |
| >>> from sympy.physics.quantum.density import Density | |
| >>> from sympy.physics.quantum.operator import Operator | |
| >>> A = Operator('A') | |
| >>> d = Density([Ket(0), 0.5], [Ket(1),0.5]) | |
| >>> d.doit() | |
| 0.5*|0><0| + 0.5*|1><1| | |
| """ | |
| terms = [] | |
| for (state, prob) in self.args: | |
| state = state.expand() # needed to break up (a+b)*c | |
| if (isinstance(state, Add)): | |
| for arg in product(state.args, repeat=2): | |
| terms.append(prob*self._generate_outer_prod(arg[0], | |
| arg[1])) | |
| else: | |
| terms.append(prob*self._generate_outer_prod(state, state)) | |
| return Add(*terms) | |
| def _generate_outer_prod(self, arg1, arg2): | |
| c_part1, nc_part1 = arg1.args_cnc() | |
| c_part2, nc_part2 = arg2.args_cnc() | |
| if (len(nc_part1) == 0 or len(nc_part2) == 0): | |
| raise ValueError('Atleast one-pair of' | |
| ' Non-commutative instance required' | |
| ' for outer product.') | |
| # Muls of Tensor Products should be expanded | |
| # before this function is called | |
| if (isinstance(nc_part1[0], TensorProduct) and len(nc_part1) == 1 | |
| and len(nc_part2) == 1): | |
| op = tensor_product_simp(nc_part1[0]*Dagger(nc_part2[0])) | |
| else: | |
| op = Mul(*nc_part1)*Dagger(Mul(*nc_part2)) | |
| return Mul(*c_part1)*Mul(*c_part2) * op | |
| def _represent(self, **options): | |
| return represent(self.doit(), **options) | |
| def _print_operator_name_latex(self, printer, *args): | |
| return r'\rho' | |
| def _print_operator_name_pretty(self, printer, *args): | |
| return prettyForm('\N{GREEK SMALL LETTER RHO}') | |
| def _eval_trace(self, **kwargs): | |
| indices = kwargs.get('indices', []) | |
| return Tr(self.doit(), indices).doit() | |
| def entropy(self): | |
| """ Compute the entropy of a density matrix. | |
| Refer to density.entropy() method for examples. | |
| """ | |
| return entropy(self) | |
| def entropy(density): | |
| """Compute the entropy of a matrix/density object. | |
| This computes -Tr(density*ln(density)) using the eigenvalue decomposition | |
| of density, which is given as either a Density instance or a matrix | |
| (numpy.ndarray, sympy.Matrix or scipy.sparse). | |
| Parameters | |
| ========== | |
| density : density matrix of type Density, SymPy matrix, | |
| scipy.sparse or numpy.ndarray | |
| Examples | |
| ======== | |
| >>> from sympy.physics.quantum.density import Density, entropy | |
| >>> from sympy.physics.quantum.spin import JzKet | |
| >>> from sympy import S | |
| >>> up = JzKet(S(1)/2,S(1)/2) | |
| >>> down = JzKet(S(1)/2,-S(1)/2) | |
| >>> d = Density((up,S(1)/2),(down,S(1)/2)) | |
| >>> entropy(d) | |
| log(2)/2 | |
| """ | |
| if isinstance(density, Density): | |
| density = represent(density) # represent in Matrix | |
| if isinstance(density, scipy_sparse_matrix): | |
| density = to_numpy(density) | |
| if isinstance(density, Matrix): | |
| eigvals = density.eigenvals().keys() | |
| return expand(-sum(e*log(e) for e in eigvals)) | |
| elif isinstance(density, numpy_ndarray): | |
| import numpy as np | |
| eigvals = np.linalg.eigvals(density) | |
| return -np.sum(eigvals*np.log(eigvals)) | |
| else: | |
| raise ValueError( | |
| "numpy.ndarray, scipy.sparse or SymPy matrix expected") | |
| def fidelity(state1, state2): | |
| """ Computes the fidelity [1]_ between two quantum states | |
| The arguments provided to this function should be a square matrix or a | |
| Density object. If it is a square matrix, it is assumed to be diagonalizable. | |
| Parameters | |
| ========== | |
| state1, state2 : a density matrix or Matrix | |
| Examples | |
| ======== | |
| >>> from sympy import S, sqrt | |
| >>> from sympy.physics.quantum.dagger import Dagger | |
| >>> from sympy.physics.quantum.spin import JzKet | |
| >>> from sympy.physics.quantum.density import fidelity | |
| >>> from sympy.physics.quantum.represent import represent | |
| >>> | |
| >>> up = JzKet(S(1)/2,S(1)/2) | |
| >>> down = JzKet(S(1)/2,-S(1)/2) | |
| >>> amp = 1/sqrt(2) | |
| >>> updown = (amp*up) + (amp*down) | |
| >>> | |
| >>> # represent turns Kets into matrices | |
| >>> up_dm = represent(up*Dagger(up)) | |
| >>> down_dm = represent(down*Dagger(down)) | |
| >>> updown_dm = represent(updown*Dagger(updown)) | |
| >>> | |
| >>> fidelity(up_dm, up_dm) | |
| 1 | |
| >>> fidelity(up_dm, down_dm) #orthogonal states | |
| 0 | |
| >>> fidelity(up_dm, updown_dm).evalf().round(3) | |
| 0.707 | |
| References | |
| ========== | |
| .. [1] https://en.wikipedia.org/wiki/Fidelity_of_quantum_states | |
| """ | |
| state1 = represent(state1) if isinstance(state1, Density) else state1 | |
| state2 = represent(state2) if isinstance(state2, Density) else state2 | |
| if not isinstance(state1, Matrix) or not isinstance(state2, Matrix): | |
| raise ValueError("state1 and state2 must be of type Density or Matrix " | |
| "received type=%s for state1 and type=%s for state2" % | |
| (type(state1), type(state2))) | |
| if state1.shape != state2.shape and state1.is_square: | |
| raise ValueError("The dimensions of both args should be equal and the " | |
| "matrix obtained should be a square matrix") | |
| sqrt_state1 = state1**S.Half | |
| return Tr((sqrt_state1*state2*sqrt_state1)**S.Half).doit() | |