Repository: JuliaDynamics/ComplexityMeasures.jl (julia)

Issue:
Electronic, aka Fermi-Dirac entropy
The paper https://www.mdpi.com/1099-4300/20/8/593 cites

![image](https://user-images.githubusercontent.com/19669089/198891377-4d7a890f-1b2b-467a-a442-e8883c57e4a2.png)

![image](https://user-images.githubusercontent.com/19669089/198891384-7aea8cf7-19dc-42d4-b5fe-e3fd3296f262.png)

This is actually called _electronic_ entropy in Wikipedia: https://en.wikipedia.org/wiki/Electronic_entropy

Seems like a straightforward new direct Entropy method to add.

Pull request description:
- Fixes #148.

Since this measure is neither a pure entropy nor a pure extropy, but a combination of them, I decided to name the definition type `ElectronicEntropy`, reflecting the name used in the literature.

Patch:
diff --git a/docs/src/information_measures.md b/docs/src/information_measures.md
index 9bc875c65..446153292 100644
--- a/docs/src/information_measures.md
+++ b/docs/src/information_measures.md
@@ -35,6 +35,7 @@ StretchedExponential
 ShannonExtropy
 RenyiExtropy
 TsallisExtropy
+ElectronicEntropy
 ```
 
 ## Discrete information estimators
diff --git a/src/information_measure_definitions/electronic.jl b/src/information_measure_definitions/electronic.jl
new file mode 100644
index 000000000..5aafc8c3a
--- /dev/null
+++ b/src/information_measure_definitions/electronic.jl
@@ -0,0 +1,46 @@
+export ElectronicEntropy
+
+"""
+    ElectronicEntropy <: InformationMeasure
+    ElectronicEntropy(; h = Shannon(; base = 2), j = ShannonExtropy(; base = 2))
+
+The ["electronic entropy"](https://en.wikipedia.org/wiki/Electronic_entropy) measure is
+defined in discrete form in Lad et al. (2015)[^Lad2015] as
+
+```math
+H_{EL}(p) = H_S(p) + J_S(P),
+```
+
+where ``H_S(p)`` is the [`Shannon`](@ref) entropy and ``J_S(p)`` is the [`ShannonExtropy`](@ref)
+extropy of the probability vector ``p``.
+
+[^Lad2015]:
+    Lad, F., Sanfilippo, G., & Agro, G. (2015). Extropy: Complementary dual of entropy.
+"""
+struct ElectronicEntropy <: InformationMeasure
+    h::Shannon
+    j::ShannonExtropy
+
+    function ElectronicEntropy(; h = Shannon(; base = 2), j = ShannonExtropy(; base = 2))
+        verify_equal_bases(h, j)
+        new(h, j)
+    end
+end
+
+function verify_equal_bases(h::Shannon, j::ShannonExtropy)
+    if h.base != j.base
+        s = "The logarithm base must be the same for both the entropy and extropy measure." *
+        "Got bases $(h.base) (entropy) and $(j.base) (extropy)."
+        throw(ArgumentError(s))
+    end
+end
+
+function information(e::ElectronicEntropy, probs::Probabilities)
+    return information(e.h, probs) + information(e.j, probs)
+end
+
+# The electronic entropy is defined as the sum of two quantities with well-defined
+# maxima. Its maximum value is therefore the sum of the maximum of each term.
+function information_maximum(e::ElectronicEntropy, L::Int)
+    return information_maximum(e.h, L) + information_maximum(e.j, L)
+end
diff --git a/src/information_measure_definitions/information_measure_definitions.jl b/src/information_measure_definitions/information_measure_definitions.jl
index b4157ea2a..97845d779 100644
--- a/src/information_measure_definitions/information_measure_definitions.jl
+++ b/src/information_measure_definitions/information_measure_definitions.jl
@@ -9,3 +9,6 @@ include("identification.jl")
 include("shannon_extropy.jl")
 include("tsallis_extropy.jl")
 include("renyi_extropy.jl")
+
+# Measures that are not strictly entropies nor extropies (but perhaps a combination)
+include("electronic.jl")
diff --git a/src/information_measure_definitions/renyi_extropy.jl b/src/information_measure_definitions/renyi_extropy.jl
index 2c0739ef0..7940fc146 100644
--- a/src/information_measure_definitions/renyi_extropy.jl
+++ b/src/information_measure_definitions/renyi_extropy.jl
@@ -36,16 +36,17 @@ RenyiExtropy(; q = 1.0, base = 2) = RenyiExtropy(q, base)
 
 function information(e::RenyiExtropy, probs::Probabilities)
     (; q, base) = e
+    non0_probs = collect(Iterators.filter(!iszero, vec(probs)))
 
-    if length(probs) == 1
+    if length(non0_probs) == 1
         return 0.0
     end
 
     if q ≈ 1
-        return information(ShannonExtropy(; base), probs)
+        return information(ShannonExtropy(; base), Probabilities(non0_probs))
     else
-        N = length(probs)
-        num = -(N - 1)*log(base, N - 1) + (N - 1)*log(base, sum((1 - pᵢ)^q for pᵢ in probs))
+        N = length(non0_probs)
+        num = -(N - 1)*log(base, N - 1) + (N - 1)*log(base, sum((1 - pᵢ)^q for pᵢ in non0_probs))
         den = 1 - q
         return num / den
     end
diff --git a/src/information_measure_definitions/shannon_extropy.jl b/src/information_measure_definitions/shannon_extropy.jl
index a4d80139a..d1b3bf40d 100644
--- a/src/information_measure_definitions/shannon_extropy.jl
+++ b/src/information_measure_definitions/shannon_extropy.jl
@@ -21,11 +21,12 @@ Base.@kwdef struct ShannonExtropy{B} <: InformationMeasure
 end
 
 function information(e::ShannonExtropy, probs::Probabilities)
-    if length(probs) == 1
+    non0_probs = collect(Iterators.filter(!iszero, vec(probs)))
+    if length(non0_probs) == 1
         return 0.0
     end
 
-    return -sum((1 - pᵢ) * log(e.base, 1 - pᵢ) for pᵢ in probs)
+    return -sum((1 - pᵢ) * log(e.base, 1 - pᵢ) for pᵢ in non0_probs)
 end
 
 function information_maximum(e::ShannonExtropy, L::Int)
diff --git a/src/information_measure_definitions/tsallis_extropy.jl b/src/information_measure_definitions/tsallis_extropy.jl
index e874d63e2..fce0d74b3 100644
--- a/src/information_measure_definitions/tsallis_extropy.jl
+++ b/src/information_measure_definitions/tsallis_extropy.jl
@@ -37,17 +37,18 @@ TsallisExtropy(; q = 1.0, k = 1.0, base = 2) = TsallisExtropy(q, k, base)
 
 function information(e::TsallisExtropy, probs::Probabilities)
     (; q, k, base) = e
+    non0_probs = collect(Iterators.filter(!iszero, vec(probs)))
 
-    if length(probs) == 1
+    if length(non0_probs) == 1
         return 0.0
     end
 
     if q ≈ 1
-        return information(ShannonExtropy(; base), probs)
+        return information(ShannonExtropy(; base), Probabilities(non0_probs))
     else
-        N = length(probs)
+        N = length(non0_probs)
         c = k / (q - 1)
-        return c * (N - 1 - sum((1 - pᵢ)^q for pᵢ in probs))
+        return c * (N - 1 - sum((1 - pᵢ)^q for pᵢ in non0_probs))
     end
 end
 


Test patch:
diff --git a/test/infomeasures/infomeasure_types/electronic_entropy.jl b/test/infomeasures/infomeasure_types/electronic_entropy.jl
new file mode 100644
index 000000000..d5b649059
--- /dev/null
+++ b/test/infomeasures/infomeasure_types/electronic_entropy.jl
@@ -0,0 +1,29 @@
+using Test, ComplexityMeasures
+
+h = Shannon(; base = 2); j = ShannonExtropy(; base = 3)
+@test_throws ArgumentError ElectronicEntropy(; h, j)
+# Analytical tests
+# -----------------------------------
+# Minimal and equal to zero when probability distribution has only one element...
+@test information(ElectronicEntropy(), Probabilities([1.0])) ≈ 0.0
+
+# or minimal when only one probability is nonzero and equal to 1.0
+@test information(ElectronicEntropy(), Probabilities([1.0, 0.0, 0.0, 0.0])) ≈ 0.0
+
+# Minimized for one-element distributions, where there is total order.
+x = [0.1, 0.1, 0.1]
+est = CountOccurrences()
+@test information(ElectronicEntropy(), est, x) == 0.0
+@test information_normalized(ElectronicEntropy(), est, x) == 0.0
+
+# Maximized for a uniform distribution
+x = [0.1, 0.1, 0.2, 0.2, 0.3, 0.3]
+@test information_normalized(ElectronicEntropy(), est, x) ≈ 1.0
+
+# Submaximal for an onuniform distribution
+x = [0.1, 0.1, 0.2, 0.2, 0.3, 0.3, 0.4]
+@test information_normalized(ElectronicEntropy(), est, x) < 1.0
+
+# Maximum is equal to the maxima of the entropy and extropy.
+e = ElectronicEntropy()
+@test information_maximum(e, 5) == information_maximum(e.h, 5) + information_maximum(e.j, 5)
diff --git a/test/infomeasures/infomeasures.jl b/test/infomeasures/infomeasures.jl
index 1bae99219..d5ee65e10 100644
--- a/test/infomeasures/infomeasures.jl
+++ b/test/infomeasures/infomeasures.jl
@@ -14,6 +14,8 @@ include("infomeasure_types/shannon_extropy.jl")
 include("infomeasure_types/tsallis_extropy.jl")
 include("infomeasure_types/renyi_extropy.jl")
 
+include("infomeasure_types/electronic_entropy.jl")
+
 
 # Estimators
 testfile("estimators/estimators.jl")