| | 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.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)) |
| | |
| | """ |
| | @classmethod |
| | def _eval_args(cls, args): |
| | |
| | args = super()._eval_args(args) |
| |
|
| | for arg in args: |
| | |
| | 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() |
| | 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.') |
| |
|
| | |
| | |
| | |
| | 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) |
| |
|
| | 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() |
| |
|