complexeva 0.1.16__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- complexeva-0.2.0/PKG-INFO +23 -0
- complexeva-0.2.0/README.md +11 -0
- {complexeva-0.1.16/complexeva/measures/dimension_1 → complexeva-0.2.0/complexeva/sequence}/__init__.py +3 -8
- complexeva-0.2.0/complexeva/sequence/compression.py +18 -0
- {complexeva-0.1.16/complexeva/measures/dimension_1 → complexeva-0.2.0/complexeva/sequence}/higuchi.py +7 -31
- complexeva-0.2.0/complexeva/sequence/lempel_ziv.py +76 -0
- {complexeva-0.1.16/complexeva/measures/dimension_1 → complexeva-0.2.0/complexeva/sequence}/lyapunov_exponent.py +0 -11
- {complexeva-0.1.16/complexeva/measures → complexeva-0.2.0/complexeva}/validate.py +6 -8
- complexeva-0.2.0/complexeva.egg-info/PKG-INFO +23 -0
- complexeva-0.2.0/complexeva.egg-info/SOURCES.txt +13 -0
- {complexeva-0.1.16 → complexeva-0.2.0}/setup.py +2 -2
- complexeva-0.1.16/PKG-INFO +0 -16
- complexeva-0.1.16/README.md +0 -8
- complexeva-0.1.16/complexeva/dummies/__init__.py +0 -4
- complexeva-0.1.16/complexeva/dummies/matrix.py +0 -150
- complexeva-0.1.16/complexeva/dummies/networks.py +0 -111
- complexeva-0.1.16/complexeva/measures/__init__.py +0 -5
- complexeva-0.1.16/complexeva/measures/dimension_1/compression.py +0 -25
- complexeva-0.1.16/complexeva/measures/dimension_1/lempel_ziv.py +0 -201
- complexeva-0.1.16/complexeva/measures/dimension_2/__init__.py +0 -0
- complexeva-0.1.16/complexeva/measures/dimension_2/cbb_matrix.py +0 -134
- complexeva-0.1.16/complexeva/measures/network_space/__init__.py +0 -0
- complexeva-0.1.16/complexeva/measures/network_space/cbb_networkx.py +0 -288
- complexeva-0.1.16/complexeva/signal/__init__.py +0 -0
- complexeva-0.1.16/complexeva/signal/co_activation.py +0 -100
- complexeva-0.1.16/complexeva/utils/__init__.py +0 -2
- complexeva-0.1.16/complexeva/utils/convert_image_matrix.py +0 -48
- complexeva-0.1.16/complexeva/utils/convert_matrix_network.py +0 -71
- complexeva-0.1.16/complexeva/utils/device.py +0 -29
- complexeva-0.1.16/complexeva.egg-info/PKG-INFO +0 -16
- complexeva-0.1.16/complexeva.egg-info/SOURCES.txt +0 -27
- {complexeva-0.1.16 → complexeva-0.2.0}/complexeva/__init__.py +0 -0
- {complexeva-0.1.16 → complexeva-0.2.0}/complexeva.egg-info/dependency_links.txt +0 -0
- {complexeva-0.1.16 → complexeva-0.2.0}/complexeva.egg-info/top_level.txt +0 -0
- {complexeva-0.1.16 → complexeva-0.2.0}/setup.cfg +0 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: complexeva
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Home-page:
|
|
5
|
+
Author: Don Yin
|
|
6
|
+
Author-email: don_yin@outlook.com
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Dynamic: author
|
|
9
|
+
Dynamic: author-email
|
|
10
|
+
Dynamic: description
|
|
11
|
+
Dynamic: description-content-type
|
|
12
|
+
|
|
13
|
+
# overview
|
|
14
|
+
implementations of complexity measures in 1D sequences
|
|
15
|
+
|
|
16
|
+
# examples
|
|
17
|
+
```python
|
|
18
|
+
from complexeva import lyapunov_exponent, hfd, lempel_ziv, compression_complexity
|
|
19
|
+
|
|
20
|
+
signal = numpy.random.rand(1000) # shape: (1000,)
|
|
21
|
+
complexity = hfd(signal)
|
|
22
|
+
print(complexity)
|
|
23
|
+
```
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# overview
|
|
2
|
+
implementations of complexity measures in 1D sequences
|
|
3
|
+
|
|
4
|
+
# examples
|
|
5
|
+
```python
|
|
6
|
+
from complexeva import lyapunov_exponent, hfd, lempel_ziv, compression_complexity
|
|
7
|
+
|
|
8
|
+
signal = numpy.random.rand(1000) # shape: (1000,)
|
|
9
|
+
complexity = hfd(signal)
|
|
10
|
+
print(complexity)
|
|
11
|
+
```
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
from .compression import compression_complexity
|
|
2
|
-
from .lyapunov_exponent import lyapunov_exponent
|
|
3
1
|
from .higuchi import hfd
|
|
4
2
|
from .lempel_ziv import lempel_ziv
|
|
3
|
+
from .compression import compression_complexity
|
|
4
|
+
from .lyapunov_exponent import lyapunov_exponent
|
|
5
5
|
|
|
6
|
-
__all__ = [
|
|
7
|
-
"compression_complexity",
|
|
8
|
-
"lyapunov_exponent",
|
|
9
|
-
"hfd",
|
|
10
|
-
"lempel_ziv",
|
|
11
|
-
]
|
|
6
|
+
__all__ = ["compression_complexity", "lyapunov_exponent", "hfd", "lempel_ziv"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/Users/donyin/miniconda3/envs/rotation-1/bin/python
|
|
2
|
+
|
|
3
|
+
import zlib
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def compression_complexity(data: np.ndarray) -> float:
|
|
8
|
+
"""
|
|
9
|
+
calculate compression complexity using zlib compression -> float: compression complexity score
|
|
10
|
+
"""
|
|
11
|
+
data = data.tobytes()
|
|
12
|
+
compressed = zlib.compress(data)
|
|
13
|
+
return len(compressed) / len(data) # Normalized complexity score
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
signal = np.random.rand(1000) # shape: (1000,)
|
|
18
|
+
print(compression_complexity(signal))
|
|
@@ -2,40 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
import numpy
|
|
5
|
-
from scipy.fft import fft, ifft
|
|
6
|
-
import matplotlib.pyplot as plt
|
|
7
|
-
from donware import inspect_package
|
|
8
5
|
from numba import njit
|
|
9
6
|
|
|
10
7
|
|
|
11
|
-
def hfd_matlab_equivalent(data, k_max=16):
|
|
12
|
-
"""
|
|
13
|
-
[translated from MATLAB code]
|
|
14
|
-
|
|
15
|
-
[output]
|
|
16
|
-
- produce values between 1 and 2
|
|
17
|
-
"""
|
|
18
|
-
N = len(data)
|
|
19
|
-
L = numpy.zeros(k_max)
|
|
20
|
-
x = numpy.zeros(k_max)
|
|
21
|
-
y = numpy.zeros(k_max)
|
|
22
|
-
|
|
23
|
-
for k in range(1, k_max + 1):
|
|
24
|
-
Lk = numpy.zeros(k)
|
|
25
|
-
for m in range(1, k + 1):
|
|
26
|
-
norm_factor = (N - 1) / (numpy.round((N - m) / k) * k)
|
|
27
|
-
X = numpy.sum(numpy.abs(numpy.diff(data[m - 1 :: k])))
|
|
28
|
-
Lk[m - 1] = X * norm_factor / k
|
|
29
|
-
|
|
30
|
-
y[k - 1] = numpy.log(numpy.sum(Lk) / k)
|
|
31
|
-
x[k - 1] = numpy.log(1 / k)
|
|
32
|
-
|
|
33
|
-
D = numpy.polyfit(x, y, 1)
|
|
34
|
-
HFD = D[0]
|
|
35
|
-
|
|
36
|
-
return HFD
|
|
37
|
-
|
|
38
|
-
|
|
39
8
|
@njit
|
|
40
9
|
def compute_L_x(X, k_max=16):
|
|
41
10
|
N = len(X)
|
|
@@ -56,7 +25,14 @@ def compute_L_x(X, k_max=16):
|
|
|
56
25
|
|
|
57
26
|
|
|
58
27
|
def hfd(X, k_max=16):
|
|
28
|
+
"""expect a 1d numpy array as input"""
|
|
59
29
|
x, L = compute_L_x(X, k_max)
|
|
60
30
|
A = numpy.column_stack((x, numpy.ones_like(x)))
|
|
61
31
|
beta, _, _, _ = numpy.linalg.lstsq(A, L, rcond=None)
|
|
62
32
|
return beta[0]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
signal = numpy.random.rand(1000) # shape: (1000,)
|
|
37
|
+
complexity = hfd(signal)
|
|
38
|
+
print(complexity)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/Users/donyin/miniconda3/envs/rotation-1/bin/python
|
|
2
|
+
|
|
3
|
+
import numpy
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def lempel_ziv(time_series, method="median", **kwargs):
|
|
7
|
+
"""
|
|
8
|
+
- compared to other implementations, this implementation is makes the most sense
|
|
9
|
+
|
|
10
|
+
Lempel-Ziv complexity as described in Kaspar and Schuster, Phys. Rev. A.
|
|
11
|
+
Counts the number of distinct patterns that need to be copied to reproduce the sequence.
|
|
12
|
+
Returns: int: The number of distinct patterns (complexity measure)
|
|
13
|
+
"""
|
|
14
|
+
sequence = time_series_to_binary(time_series, method=method)
|
|
15
|
+
|
|
16
|
+
history_pos = 0 # position in history we're comparing against
|
|
17
|
+
current_len = 1 # length of current pattern we're checking
|
|
18
|
+
max_pattern_len = 1 # length of longest matching pattern found
|
|
19
|
+
seq_pos = 1 # current position in sequence
|
|
20
|
+
complexity = 1 # start with 1 as first character is always a new pattern
|
|
21
|
+
seq_len = len(sequence)
|
|
22
|
+
|
|
23
|
+
while seq_pos + current_len <= seq_len:
|
|
24
|
+
if sequence[history_pos + current_len - 1] == sequence[seq_pos + current_len - 1]:
|
|
25
|
+
current_len += 1
|
|
26
|
+
else:
|
|
27
|
+
if current_len > max_pattern_len:
|
|
28
|
+
max_pattern_len = current_len
|
|
29
|
+
history_pos += 1
|
|
30
|
+
if history_pos == seq_pos:
|
|
31
|
+
complexity += 1
|
|
32
|
+
seq_pos += max_pattern_len
|
|
33
|
+
history_pos = 0
|
|
34
|
+
current_len = 1
|
|
35
|
+
max_pattern_len = 1
|
|
36
|
+
else:
|
|
37
|
+
current_len = 1
|
|
38
|
+
return complexity
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def time_series_to_binary(vector, method="median"):
|
|
42
|
+
"""
|
|
43
|
+
- convert a time series to a binary sequence based on a threshold.
|
|
44
|
+
- returns: str: A binary sequence as a string of '0's and '1's.
|
|
45
|
+
- https://doi.org/10.1101/2021.09.23.461002 this paper used median, making median the default method.
|
|
46
|
+
- diff also makes somewhat sense
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
vector = numpy.array(vector)
|
|
50
|
+
|
|
51
|
+
match method:
|
|
52
|
+
|
|
53
|
+
case "mean":
|
|
54
|
+
threshold = numpy.mean(vector)
|
|
55
|
+
binary_seq = "".join(["1" if x > threshold else "0" for x in vector])
|
|
56
|
+
|
|
57
|
+
case "median":
|
|
58
|
+
threshold = numpy.median(vector)
|
|
59
|
+
binary_seq = "".join(["1" if x > threshold else "0" for x in vector])
|
|
60
|
+
|
|
61
|
+
case "diff": # this measures whether the value is increasing or decreasing as compared to the previous value
|
|
62
|
+
diffs = numpy.diff(vector)
|
|
63
|
+
binary_seq = "".join(["1" if x > 0 else "0" for x in diffs])
|
|
64
|
+
|
|
65
|
+
case _:
|
|
66
|
+
raise ValueError("Method must be 'mean', 'median', or 'diff'.")
|
|
67
|
+
|
|
68
|
+
return binary_seq
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
sequence = "1001111011000010"
|
|
73
|
+
print(lempel_ziv(sequence))
|
|
74
|
+
signal = numpy.random.rand(1000)
|
|
75
|
+
binary_sequence = time_series_to_binary(signal, method="median")
|
|
76
|
+
print(lempel_ziv(binary_sequence))
|
|
@@ -15,28 +15,17 @@ def lyapunov_exponent(data, tau=1, m=2, dt=1):
|
|
|
15
15
|
Returns:
|
|
16
16
|
float: largest Lyapunov exponent
|
|
17
17
|
"""
|
|
18
|
-
# Convert to numpy array if needed
|
|
19
18
|
x = np.array(data)
|
|
20
19
|
N = len(x)
|
|
21
|
-
|
|
22
|
-
# Create time-delay embedding
|
|
23
20
|
Y = np.zeros((N - (m - 1) * tau, m))
|
|
24
21
|
for i in range(m):
|
|
25
22
|
Y[:, i] = x[i * tau : N - (m - 1) * tau + i * tau]
|
|
26
|
-
|
|
27
|
-
# Calculate pairwise distances
|
|
28
23
|
D = squareform(pdist(Y))
|
|
29
24
|
np.fill_diagonal(D, np.inf) # Exclude self-matches
|
|
30
|
-
|
|
31
|
-
# Find nearest neighbors
|
|
32
25
|
nearest = np.argmin(D, axis=1)
|
|
33
|
-
|
|
34
|
-
# Calculate divergence
|
|
35
26
|
div = np.zeros(N - (m - 1) * tau - 1)
|
|
36
27
|
for i in range(len(div)):
|
|
37
28
|
div[i] = np.abs(x[i + 1] - x[nearest[i] + 1])
|
|
38
|
-
|
|
39
|
-
# Calculate Lyapunov exponent
|
|
40
29
|
positive_div = div[div > 0] # Avoid log(0)
|
|
41
30
|
if len(positive_div) == 0:
|
|
42
31
|
return 0.0
|
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import numpy
|
|
8
|
-
from scipy.fft import fft, ifft
|
|
9
8
|
import matplotlib.pyplot as plt
|
|
10
|
-
from
|
|
11
|
-
from complexeva.
|
|
12
|
-
from complexeva.measures.dimension_1.lempel_ziv import lempel_ziv_v1, lempel_ziv_v2, lempel_ziv, lempel_ziv_v4
|
|
9
|
+
from complexeva.sequence.higuchi import hfd
|
|
10
|
+
from complexeva.sequence.lempel_ziv import lempel_ziv
|
|
13
11
|
|
|
14
12
|
|
|
15
13
|
class ComplexityMeasuresValidator2D:
|
|
@@ -49,7 +47,7 @@ class ComplexityMeasuresValidator2D:
|
|
|
49
47
|
fBm = numpy.fft.ifft(gaussian * numpy.sqrt(psd)).real
|
|
50
48
|
return fBm
|
|
51
49
|
|
|
52
|
-
def
|
|
50
|
+
def plot_vs_hurst(self):
|
|
53
51
|
hurst_exponent = numpy.linspace(0.001, 1, self.num_hurst)
|
|
54
52
|
fBm_series = [self.fbm(h) for h in hurst_exponent]
|
|
55
53
|
expected_hfd = [2 - h for h in hurst_exponent]
|
|
@@ -113,7 +111,7 @@ class ComplexityMeasuresValidator2D:
|
|
|
113
111
|
|
|
114
112
|
if __name__ == "__main__":
|
|
115
113
|
validator = ComplexityMeasuresValidator2D([hfd])
|
|
116
|
-
validator.
|
|
114
|
+
validator.plot_vs_hurst()
|
|
117
115
|
|
|
118
|
-
validator = ComplexityMeasuresValidator2D([
|
|
119
|
-
validator.
|
|
116
|
+
validator = ComplexityMeasuresValidator2D([lempel_ziv])
|
|
117
|
+
validator.plot_vs_hurst()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: complexeva
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Home-page:
|
|
5
|
+
Author: Don Yin
|
|
6
|
+
Author-email: don_yin@outlook.com
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Dynamic: author
|
|
9
|
+
Dynamic: author-email
|
|
10
|
+
Dynamic: description
|
|
11
|
+
Dynamic: description-content-type
|
|
12
|
+
|
|
13
|
+
# overview
|
|
14
|
+
implementations of complexity measures in 1D sequences
|
|
15
|
+
|
|
16
|
+
# examples
|
|
17
|
+
```python
|
|
18
|
+
from complexeva import lyapunov_exponent, hfd, lempel_ziv, compression_complexity
|
|
19
|
+
|
|
20
|
+
signal = numpy.random.rand(1000) # shape: (1000,)
|
|
21
|
+
complexity = hfd(signal)
|
|
22
|
+
print(complexity)
|
|
23
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
complexeva/__init__.py
|
|
4
|
+
complexeva/validate.py
|
|
5
|
+
complexeva.egg-info/PKG-INFO
|
|
6
|
+
complexeva.egg-info/SOURCES.txt
|
|
7
|
+
complexeva.egg-info/dependency_links.txt
|
|
8
|
+
complexeva.egg-info/top_level.txt
|
|
9
|
+
complexeva/sequence/__init__.py
|
|
10
|
+
complexeva/sequence/compression.py
|
|
11
|
+
complexeva/sequence/higuchi.py
|
|
12
|
+
complexeva/sequence/lempel_ziv.py
|
|
13
|
+
complexeva/sequence/lyapunov_exponent.py
|
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name="complexeva",
|
|
5
|
-
version="0.
|
|
5
|
+
version="0.2.0",
|
|
6
6
|
packages=find_packages(),
|
|
7
7
|
install_requires=[],
|
|
8
8
|
author="Don Yin",
|
|
@@ -11,5 +11,5 @@ setup(
|
|
|
11
11
|
long_description=open("README.md").read(),
|
|
12
12
|
long_description_content_type="text/markdown",
|
|
13
13
|
url="",
|
|
14
|
-
pyhon_requires=">=3.
|
|
14
|
+
pyhon_requires=">=3.12",
|
|
15
15
|
)
|
complexeva-0.1.16/PKG-INFO
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: complexeva
|
|
3
|
-
Version: 0.1.16
|
|
4
|
-
Home-page:
|
|
5
|
-
Author: Don Yin
|
|
6
|
-
Author-email: don_yin@outlook.com
|
|
7
|
-
Description-Content-Type: text/markdown
|
|
8
|
-
|
|
9
|
-
# overview
|
|
10
|
-
computing fractal dimensions (and other complexity measures) and complexity measures in networks and matrices. The package includes:
|
|
11
|
-
|
|
12
|
-
# some functions (thus far)
|
|
13
|
-
- Box-counting fractal dimension calculation for networks and matrices
|
|
14
|
-
- Higuchi fractal dimension implementation
|
|
15
|
-
- Network-to-matrix and image-to-matrix conversions
|
|
16
|
-
- Dummy fractal generators for testing
|
complexeva-0.1.16/README.md
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# overview
|
|
2
|
-
computing fractal dimensions (and other complexity measures) and complexity measures in networks and matrices. The package includes:
|
|
3
|
-
|
|
4
|
-
# some functions (thus far)
|
|
5
|
-
- Box-counting fractal dimension calculation for networks and matrices
|
|
6
|
-
- Higuchi fractal dimension implementation
|
|
7
|
-
- Network-to-matrix and image-to-matrix conversions
|
|
8
|
-
- Dummy fractal generators for testing
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Fractal adjacency matrix generation and analysis with varying degrees of complexity
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import numpy as np
|
|
6
|
-
import matplotlib.pyplot as plt
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class DummyAdjacencyMatrix:
|
|
10
|
-
def __init__(self, size, fractal_dimension):
|
|
11
|
-
"""Initialize the FractalAdjacencyMatrix with a given size and fractal dimension.
|
|
12
|
-
|
|
13
|
-
Parameters:
|
|
14
|
-
size (int): The desired size of the adjacency matrix.
|
|
15
|
-
fractal_dimension (float): The target fractal dimension.
|
|
16
|
-
"""
|
|
17
|
-
self.size, self.fractal_dimension = size, fractal_dimension
|
|
18
|
-
|
|
19
|
-
def generate_matrix(self, remove_upper_triangle=False, save_as=None):
|
|
20
|
-
"""Generate the fractal adjacency matrix based on the specified fractal dimension.
|
|
21
|
-
|
|
22
|
-
Parameters:
|
|
23
|
-
fractal_dimension (float): The target fractal dimension.
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
numpy.ndarray: The generated adjacency matrix.
|
|
27
|
-
"""
|
|
28
|
-
max_m = min(self.size, 10)
|
|
29
|
-
m, pattern = self.calculate_m_and_pattern(self.fractal_dimension, max_m)
|
|
30
|
-
iterations = int(np.ceil(np.log(self.size) / np.log(m)))
|
|
31
|
-
actual_size = m**iterations
|
|
32
|
-
matrix = np.ones((actual_size, actual_size), dtype=bool)
|
|
33
|
-
self.apply_pattern(matrix, 0, 0, actual_size, m, pattern, iterations)
|
|
34
|
-
|
|
35
|
-
# Resize matrix if needed
|
|
36
|
-
if actual_size != self.size:
|
|
37
|
-
if actual_size > self.size:
|
|
38
|
-
matrix = matrix[: self.size, : self.size]
|
|
39
|
-
else:
|
|
40
|
-
pad_size = self.size - actual_size
|
|
41
|
-
matrix = np.pad(matrix, ((0, pad_size), (0, pad_size)), mode="constant", constant_values=False)
|
|
42
|
-
|
|
43
|
-
matrix = self.post_processing_matrix(matrix, remove_upper_triangle)
|
|
44
|
-
|
|
45
|
-
if save_as:
|
|
46
|
-
save_as = save_as if save_as.endswith(".png") else f"{save_as}.png"
|
|
47
|
-
plt.figure(figsize=(8, 8))
|
|
48
|
-
plt.imshow(matrix, cmap="binary", interpolation="nearest")
|
|
49
|
-
plt.title("Dummy Fractal Adjacency Matrix / FD ~ {:.2f}".format(self.fractal_dimension))
|
|
50
|
-
plt.axis("off")
|
|
51
|
-
plt.tight_layout()
|
|
52
|
-
plt.savefig(save_as, dpi=420, bbox_inches="tight")
|
|
53
|
-
plt.close()
|
|
54
|
-
|
|
55
|
-
return matrix
|
|
56
|
-
|
|
57
|
-
@staticmethod
|
|
58
|
-
def post_processing_matrix(matrix, remove_upper_triangle=False):
|
|
59
|
-
matrix = matrix.astype(int)
|
|
60
|
-
if remove_upper_triangle:
|
|
61
|
-
matrix = np.tril(matrix)
|
|
62
|
-
return matrix
|
|
63
|
-
|
|
64
|
-
def calculate_m_and_pattern(self, target_dimension, max_scaling_factor=10):
|
|
65
|
-
"""Calculate the scaling factor 'm' and the base pattern for the fractal.
|
|
66
|
-
|
|
67
|
-
Parameters:
|
|
68
|
-
target_dimension (float): The target fractal dimension.
|
|
69
|
-
max_m (int): The maximum value of 'm' to consider (default is 10).
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
tuple: A tuple containing 'm' and the 'pattern' numpy.ndarray.
|
|
73
|
-
"""
|
|
74
|
-
best_dimension_diff = float("inf")
|
|
75
|
-
best_scaling_factor = None
|
|
76
|
-
best_pattern = None
|
|
77
|
-
|
|
78
|
-
for scaling_factor in range(2, max_scaling_factor + 1):
|
|
79
|
-
total_cells = scaling_factor * scaling_factor
|
|
80
|
-
# Iterate over possible numbers of cells to remove
|
|
81
|
-
for removed_cells in range(1, total_cells):
|
|
82
|
-
remaining_cells = total_cells - removed_cells
|
|
83
|
-
fractal_dim = np.log(remaining_cells) / np.log(scaling_factor)
|
|
84
|
-
dimension_diff = abs(fractal_dim - target_dimension)
|
|
85
|
-
|
|
86
|
-
if dimension_diff < best_dimension_diff:
|
|
87
|
-
best_dimension_diff = dimension_diff
|
|
88
|
-
best_scaling_factor = scaling_factor
|
|
89
|
-
# Create the base pattern
|
|
90
|
-
pattern = np.ones((scaling_factor, scaling_factor), dtype=bool)
|
|
91
|
-
# Determine which cells to remove, starting from the center
|
|
92
|
-
indices = [(i, j) for i in range(scaling_factor) for j in range(scaling_factor)]
|
|
93
|
-
center = scaling_factor // 2
|
|
94
|
-
indices_sorted = sorted(
|
|
95
|
-
indices,
|
|
96
|
-
key=lambda x: (abs(x[0] - center) + abs(x[1] - center), x[0], x[1]),
|
|
97
|
-
)
|
|
98
|
-
for i, j in indices_sorted[:removed_cells]:
|
|
99
|
-
pattern[i, j] = False
|
|
100
|
-
best_pattern = pattern.copy()
|
|
101
|
-
|
|
102
|
-
if best_dimension_diff == 0:
|
|
103
|
-
return best_scaling_factor, best_pattern
|
|
104
|
-
|
|
105
|
-
return best_scaling_factor, best_pattern
|
|
106
|
-
|
|
107
|
-
def apply_pattern(self, matrix, x_start, y_start, size, m, pattern, iterations_left):
|
|
108
|
-
"""Recursively apply the fractal pattern to the matrix.
|
|
109
|
-
|
|
110
|
-
Parameters:
|
|
111
|
-
matrix (numpy.ndarray): The matrix to modify.
|
|
112
|
-
x_start (int): The starting x-coordinate in the matrix.
|
|
113
|
-
y_start (int): The starting y-coordinate in the matrix.
|
|
114
|
-
size (int): The size of the current square in the matrix.
|
|
115
|
-
m (int): The scaling factor.
|
|
116
|
-
pattern (numpy.ndarray): The base pattern to apply.
|
|
117
|
-
iterations_left (int): The number of iterations left to apply.
|
|
118
|
-
"""
|
|
119
|
-
if iterations_left == 0:
|
|
120
|
-
return
|
|
121
|
-
step = size // m
|
|
122
|
-
for i, j in np.ndindex(m, m):
|
|
123
|
-
x_slice = slice(x_start + j * step, x_start + (j + 1) * step)
|
|
124
|
-
y_slice = slice(y_start + i * step, y_start + (i + 1) * step)
|
|
125
|
-
if not pattern[i, j]:
|
|
126
|
-
matrix[y_slice, x_slice] = False
|
|
127
|
-
else:
|
|
128
|
-
self.apply_pattern(
|
|
129
|
-
matrix,
|
|
130
|
-
x_start + j * step,
|
|
131
|
-
y_start + i * step,
|
|
132
|
-
step,
|
|
133
|
-
m,
|
|
134
|
-
pattern,
|
|
135
|
-
iterations_left - 1,
|
|
136
|
-
)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
if __name__ == "__main__":
|
|
140
|
-
size = 256
|
|
141
|
-
fractal_dimensions = np.linspace(1.0, 2.0, 10)
|
|
142
|
-
|
|
143
|
-
for idx, dimension in enumerate(fractal_dimensions):
|
|
144
|
-
generator = DummyAdjacencyMatrix(size, dimension)
|
|
145
|
-
plt.figure(figsize=(6, 6))
|
|
146
|
-
plt.title(f"Fractal Dimension ~ {dimension:.2f}", pad=20)
|
|
147
|
-
plt.imshow(generator.matrix, cmap="binary", interpolation="nearest")
|
|
148
|
-
plt.axis("off")
|
|
149
|
-
plt.savefig(f"fractal_{idx}.png")
|
|
150
|
-
plt.close()
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
#!/Users/donyin/miniconda3/envs/rotation-1/bin/python
|
|
2
|
-
|
|
3
|
-
"""
|
|
4
|
-
all the networkx class dummy implementations
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import numpy as np
|
|
8
|
-
import networkx as nx
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class WebFractalNetwork:
|
|
12
|
-
"""
|
|
13
|
-
Generates a pseudo-fractal network using an iterative method.
|
|
14
|
-
|
|
15
|
-
Usage:
|
|
16
|
-
web_fractal = WebFractalNetwork(complexity=3)
|
|
17
|
-
network = web_fractal.network
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
def __init__(self, complexity=3):
|
|
21
|
-
self.complexity = complexity
|
|
22
|
-
self.network = self._generate_network()
|
|
23
|
-
self.diameter = nx.diameter(self.network)
|
|
24
|
-
|
|
25
|
-
def _generate_network(self):
|
|
26
|
-
"""
|
|
27
|
-
Generates the pseudo-fractal network.
|
|
28
|
-
"""
|
|
29
|
-
G = nx.Graph()
|
|
30
|
-
G.add_edges_from([(0, 1), (1, 2), (2, 0)])
|
|
31
|
-
for _ in range(self.complexity):
|
|
32
|
-
G = self._fractal_step(G)
|
|
33
|
-
return G
|
|
34
|
-
|
|
35
|
-
def _fractal_step(self, G):
|
|
36
|
-
"""
|
|
37
|
-
Performs one iteration of the fractal construction.
|
|
38
|
-
"""
|
|
39
|
-
new_edges = []
|
|
40
|
-
max_node = max(G.nodes())
|
|
41
|
-
for u, v in G.edges():
|
|
42
|
-
new_node = max_node + 1
|
|
43
|
-
max_node += 1
|
|
44
|
-
new_edges.extend([(new_node, u), (new_node, v)])
|
|
45
|
-
G.add_edges_from(new_edges)
|
|
46
|
-
return G
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class SierpinskiFractal:
|
|
50
|
-
"""
|
|
51
|
-
Generates a Sierpinski gasket fractal network.
|
|
52
|
-
|
|
53
|
-
Usage:
|
|
54
|
-
sierpinski = SierpinskiFractal(recursion_depth=3)
|
|
55
|
-
network = sierpinski.network
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
|
-
def __init__(self, recursion_depth=3):
|
|
59
|
-
self.recursion_depth = recursion_depth
|
|
60
|
-
self.points = {}
|
|
61
|
-
self.edges = set()
|
|
62
|
-
self.point_index = 0
|
|
63
|
-
self.network = self._generate_network()
|
|
64
|
-
|
|
65
|
-
def _generate_network(self):
|
|
66
|
-
"""
|
|
67
|
-
Generates the Sierpinski gasket fractal network.
|
|
68
|
-
"""
|
|
69
|
-
vertices = [(0, 0), (0.5, np.sqrt(3) / 2), (1, 0)]
|
|
70
|
-
self._sierpinski_recursive(vertices, self.recursion_depth)
|
|
71
|
-
return self._build_graph()
|
|
72
|
-
|
|
73
|
-
def _sierpinski_recursive(self, vertices, depth):
|
|
74
|
-
"""
|
|
75
|
-
Recursively generates the Sierpinski gasket fractal.
|
|
76
|
-
"""
|
|
77
|
-
|
|
78
|
-
def add_triangle(v1, v2, v3, current_depth):
|
|
79
|
-
if current_depth == 0:
|
|
80
|
-
for v in [v1, v2, v3]:
|
|
81
|
-
if v not in self.points:
|
|
82
|
-
self.points[v] = self.point_index
|
|
83
|
-
self.point_index += 1
|
|
84
|
-
i1, i2, i3 = self.points[v1], self.points[v2], self.points[v3]
|
|
85
|
-
self.edges.update([(i1, i2), (i2, i3), (i3, i1)])
|
|
86
|
-
else:
|
|
87
|
-
m1 = ((v1[0] + v2[0]) / 2, (v1[1] + v2[1]) / 2)
|
|
88
|
-
m2 = ((v2[0] + v3[0]) / 2, (v2[1] + v3[1]) / 2)
|
|
89
|
-
m3 = ((v3[0] + v1[0]) / 2, (v3[1] + v1[1]) / 2)
|
|
90
|
-
add_triangle(v1, m1, m3, current_depth - 1)
|
|
91
|
-
add_triangle(m1, v2, m2, current_depth - 1)
|
|
92
|
-
add_triangle(m3, m2, v3, current_depth - 1)
|
|
93
|
-
|
|
94
|
-
add_triangle(vertices[0], vertices[1], vertices[2], depth)
|
|
95
|
-
|
|
96
|
-
def _build_graph(self):
|
|
97
|
-
"""
|
|
98
|
-
Builds the NetworkX graph from the generated points and edges.
|
|
99
|
-
"""
|
|
100
|
-
G = nx.Graph()
|
|
101
|
-
for coord, idx in self.points.items():
|
|
102
|
-
G.add_node(idx, pos=coord)
|
|
103
|
-
G.add_edges_from(self.edges)
|
|
104
|
-
return G
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if __name__ == "__main__":
|
|
108
|
-
network = SierpinskiFractal(recursion_depth=7)
|
|
109
|
-
# network = WebFractalNetwork(complexity=4)
|
|
110
|
-
# handler = FractalHandlerNetworkX(network.network)
|
|
111
|
-
# handler.compute_fractal_dimensions(box_sizes="auto", iterations=10)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import zlib
|
|
2
|
-
import numpy as np
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
def compression_complexity(data):
|
|
6
|
-
"""
|
|
7
|
-
Calculate compression complexity using zlib compression.
|
|
8
|
-
|
|
9
|
-
Args:
|
|
10
|
-
data: numpy array or list of numerical values
|
|
11
|
-
|
|
12
|
-
Returns:
|
|
13
|
-
float: compression complexity score
|
|
14
|
-
"""
|
|
15
|
-
# Convert numerical data to bytes
|
|
16
|
-
if isinstance(data, np.ndarray):
|
|
17
|
-
data = data.tobytes()
|
|
18
|
-
elif isinstance(data, list):
|
|
19
|
-
data = np.array(data).tobytes()
|
|
20
|
-
else:
|
|
21
|
-
raise ValueError("Input must be a numpy array or list")
|
|
22
|
-
|
|
23
|
-
# Calculate compression ratio
|
|
24
|
-
compressed = zlib.compress(data)
|
|
25
|
-
return len(compressed) / len(data) # Normalized complexity score
|