Spaces:
Sleeping
Sleeping
File size: 9,743 Bytes
6a86ad5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
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))
"""
@classmethod
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()
|