content: import pytest
  import numpy as np
  import qutip
  from qutip.core.energy_restricted import EnrSpace
  
  
  class TestVonNeumannEntropy:
      @pytest.mark.parametrize("p", np.linspace(0, 1, 17))
      def test_binary(self, p):
          dm = qutip.qdiags([p, 1 - p], 0)
          expected = 0 if p in [0, 1] else p*np.log2(p) + (1-p)*np.log2(1-p)
          assert abs(-qutip.entropy_vn(dm, 2) - expected) < 1e-12
  
      @pytest.mark.repeat(10)
      @pytest.mark.parametrize("dim", [10, EnrSpace([3, 3], 2)])
      def test_pure_state(self, dim):
          assert abs(qutip.entropy_vn(qutip.rand_ket(dim))) < 1e-12
  
      @pytest.mark.repeat(10)
      @pytest.mark.parametrize("dim", [10, EnrSpace([3, 3], 2)])
      def test_mixed_state(self, dim):
          assert qutip.entropy_vn(qutip.rand_dm(dim)) > 0
  
  
  class TestLinearEntropy:
      @pytest.mark.repeat(10)
      @pytest.mark.parametrize("dim", [10, EnrSpace([3, 3], 2)])
      def test_less_than_von_neumann(self, dim):
          dm = qutip.rand_dm(dim)
          assert qutip.entropy_linear(dm) <= qutip.entropy_vn(dm)
  
      @pytest.mark.repeat(10)
      @pytest.mark.parametrize("dim", [10, EnrSpace([3, 3], 2)])
      def test_pure_state(self, dim):
          assert abs(qutip.entropy_linear(qutip.rand_ket(dim))) < 1e-12
  
      @pytest.mark.repeat(10)
      @pytest.mark.parametrize("dim", [10, EnrSpace([3, 3], 2)])
      def test_mixed_state(self, dim):
          assert qutip.entropy_linear(qutip.rand_dm(dim)) > 0
  
  
  class TestConcurrence:
      @pytest.mark.parametrize("dm", [
          pytest.param(qutip.bell_state(x).proj(), id='bell'+x)
          for x in ['00', '01', '10', '11']
      ])
      def test_maximally_entangled(self, dm):
          assert abs(qutip.concurrence(dm) - 1) < 1e-12
  
      @pytest.mark.repeat(10)
      def test_nonzero(self):
          dm = qutip.rand_dm([2, 2])
          assert qutip.concurrence(dm) >= 0
  
  
  @pytest.mark.repeat(10)
  class TestMutualInformation:
      def test_pure_state_additive(self):
          # Verify mutual information = S(A) + S(B) for pure states.
          dm = qutip.rand_dm([5, 5], distribution="pure")
          expect = (qutip.entropy_vn(dm.ptrace(0))
                    + qutip.entropy_vn(dm.ptrace(1)))
          assert abs(qutip.entropy_mutual(dm, [0], [1]) - expect) < 1e-13
  
      def test_component_selection(self):
          dm = qutip.rand_dm([2, 2, 2], distribution="pure")
          expect = (qutip.entropy_vn(dm.ptrace([0, 2]))
                    + qutip.entropy_vn(dm.ptrace(1)))
          assert abs(qutip.entropy_mutual(dm, [0, 2], [1]) - expect) < 1e-13
  
  
  class TestRelativeEntropy:
      def _simple_relative_entropy_implementation(
              self, rho, sigma, log_base=np.log, tol=1e-12):
          """ A simplified relative entropy implementation for use in
              double-checking the optimised implementation within
              QuTiP itself.
          """
          # S(rho || sigma) = sum_i(p_i log p_i) - sum_ij(p_i P_ij log q_i)
          rvals, rvecs = rho.eigenstates()
          svals, svecs = sigma.eigenstates()
          rvecs = np.hstack([vec.full() for vec in rvecs]).T
          svecs = np.hstack([vec.full() for vec in svecs]).T
          # Calculate S
          S = 0
          for i in range(len(rvals)):
              if abs(rvals[i]) >= tol:
                  S += rvals[i] * log_base(rvals[i])
              for j in range(len(svals)):
                  P_ij = (
                      np.dot(rvecs[i], svecs[j].conjugate()) *
                      np.dot(svecs[j], rvecs[i].conjugate())
                  )
                  if abs(svals[j]) < tol and not (
                          abs(rvals[i]) < tol or abs(P_ij) < tol):
                      # kernel of sigma intersects support of rho
                      return np.inf
                  if abs(svals[j]) >= tol:
                      S -= rvals[i] * P_ij * log_base(svals[j])
          return np.real(S)
  
      def test_rho_or_sigma_not_oper(self):
          rho = qutip.bra("00")
          sigma = qutip.bra("01")
          with pytest.raises(TypeError) as exc:
              qutip.entropy_relative(rho.dag(), sigma)
          assert str(exc.value) == "Inputs must be density matrices."
          with pytest.raises(TypeError) as exc:
              qutip.entropy_relative(rho, sigma.dag())
          assert str(exc.value) == "Inputs must be density matrices."
          with pytest.raises(TypeError) as exc:
              qutip.entropy_relative(rho, sigma)
          assert str(exc.value) == "Inputs must be density matrices."
  
      def test_rho_and_sigma_have_different_shape_and_dims(self):
          # test different shape and dims
          rho = qutip.ket("00")
          sigma = qutip.ket("0")
          with pytest.raises(ValueError) as exc:
              qutip.entropy_relative(rho, sigma)
          assert str(exc.value) == "Inputs must have the same shape and dims."
          # test same shape, difference dims
          rho = qutip.basis([2, 3], [0, 0])
          sigma = qutip.basis([3, 2], [0, 0])
          with pytest.raises(ValueError) as exc:
              qutip.entropy_relative(rho, sigma)
          assert str(exc.value) == "Inputs must have the same shape and dims."
  
      def test_base_not_2_or_e(self):
          rho = qutip.ket("00")
          sigma = qutip.ket("01")
          with pytest.raises(ValueError) as exc:
              qutip.entropy_relative(rho, sigma, base=3)
          assert str(exc.value) == "Base must be 2 or e."
  
      def test_infinite_relative_entropy(self):
          rho = qutip.ket("00")
          sigma = qutip.ket("01")
          assert qutip.entropy_relative(rho, sigma) == np.inf
  
      def test_base_2_or_e(self):
          rho = qutip.ket2dm(qutip.ket("00"))
          sigma = rho + qutip.ket2dm(qutip.ket("01"))
          sigma = sigma.unit()
          assert (
              qutip.entropy_relative(rho, sigma) == pytest.approx(np.log(2))
          )
          assert (
              qutip.entropy_relative(rho, sigma, base=np.e)
              == pytest.approx(0.69314718)
          )
          assert qutip.entropy_relative(rho, sigma, base=2) == pytest.approx(1)
  
      def test_pure_vs_maximally_mixed_state(self):
          rho = qutip.ket("00")
          sigma = sum(
              qutip.ket2dm(qutip.ket(psi)) for psi in ["00", "01", "10", "11"]
          ).unit()
          assert qutip.entropy_relative(rho, sigma, base=2) == pytest.approx(2)
  
      def test_density_matrices_with_non_real_eigenvalues(self):
          rho = qutip.ket2dm(qutip.ket("00"))
          sigma = qutip.ket2dm(qutip.ket("01"))
          with pytest.raises(ValueError) as exc:
              qutip.entropy_relative(rho + 1j, sigma)
          assert str(exc.value) == "Input rho has non-real eigenvalues."
          with pytest.raises(ValueError) as exc:
              qutip.entropy_relative(rho - 1j, sigma)
          assert str(exc.value) == "Input rho has non-real eigenvalues."
          with pytest.raises(ValueError) as exc:
              qutip.entropy_relative(rho, sigma + 1j)
          assert str(exc.value) == "Input sigma has non-real eigenvalues."
          with pytest.raises(ValueError) as exc:
              qutip.entropy_relative(rho, sigma - 1j)
          assert str(exc.value) == "Input sigma has non-real eigenvalues."
  
      @pytest.mark.repeat(20)
      def test_random_dm_with_self(self):
          rho = qutip.rand_dm(8)
          rel = qutip.entropy_relative(rho, rho)
          assert abs(rel) < 1e-13
  
      @pytest.mark.repeat(20)
      def test_random_rho_sigma(self):
          rho = qutip.rand_dm(8)
          sigma = qutip.rand_dm(8)
          rel = qutip.entropy_relative(rho, sigma)
          assert rel >= 0
          assert rel == pytest.approx(
              self._simple_relative_entropy_implementation(rho, sigma, np.log)
          )
  
      @pytest.mark.repeat(20)
      def test_with_enr(self):
          dims = EnrSpace([3, 3], 2)
          rho = qutip.rand_dm(dims)
          sigma = qutip.rand_dm(dims)
          rel = qutip.entropy_relative(rho, sigma)
          assert rel >= 0
          assert rel == pytest.approx(
              self._simple_relative_entropy_implementation(rho, sigma, np.log)
          )
  
  
  @pytest.mark.repeat(20)
  class TestConditionalEntropy:
      def test_inequality_3_qubits(self):
          # S(A | B,C) <= S(A|B)
          full = qutip.rand_dm([2]*3, distribution="pure")
          ab = full.ptrace([0, 1])
          assert (qutip.entropy_conditional(full, [1, 2])
                  <= qutip.entropy_conditional(ab, 1))
  
      def test_triangle_inequality_4_qubits(self):
          # S(A,B | C,D) <= S(A|C) + S(B|D)
          full = qutip.rand_dm([2]*4, distribution="pure")
          ac, bd = full.ptrace([0, 2]), full.ptrace([1, 3])
          assert (qutip.entropy_conditional(full, [2, 3])
                  <= (qutip.entropy_conditional(ac, 1)
                      + qutip.entropy_conditional(bd, 1)))
  
  
  _alpha = 2*np.pi * np.random.rand()
  
  
  @pytest.mark.parametrize(["gate", "expected"], [
      pytest.param(qutip.gates.cnot(), 2/9, id="CNOT"),
      pytest.param(qutip.gates.iswap(), 2/9, id="ISWAP"),
      pytest.param(qutip.gates.berkeley(), 2/9, id="Berkeley"),
      pytest.param(qutip.gates.swap(), 0, id="SWAP"),
      pytest.param(qutip.gates.sqrtswap(), 1/6, id="sqrt(SWAP)"),
      pytest.param(qutip.gates.swapalpha(_alpha),
                   np.sin(np.pi*_alpha)**2 / 6, id="SWAP(alpha)"),
  ])
  def test_entangling_power(gate, expected):
      assert abs(qutip.entangling_power(gate) - expected) < 1e-12
  