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.
Files changed (35) hide show
  1. complexeva-0.2.0/PKG-INFO +23 -0
  2. complexeva-0.2.0/README.md +11 -0
  3. {complexeva-0.1.16/complexeva/measures/dimension_1 → complexeva-0.2.0/complexeva/sequence}/__init__.py +3 -8
  4. complexeva-0.2.0/complexeva/sequence/compression.py +18 -0
  5. {complexeva-0.1.16/complexeva/measures/dimension_1 → complexeva-0.2.0/complexeva/sequence}/higuchi.py +7 -31
  6. complexeva-0.2.0/complexeva/sequence/lempel_ziv.py +76 -0
  7. {complexeva-0.1.16/complexeva/measures/dimension_1 → complexeva-0.2.0/complexeva/sequence}/lyapunov_exponent.py +0 -11
  8. {complexeva-0.1.16/complexeva/measures → complexeva-0.2.0/complexeva}/validate.py +6 -8
  9. complexeva-0.2.0/complexeva.egg-info/PKG-INFO +23 -0
  10. complexeva-0.2.0/complexeva.egg-info/SOURCES.txt +13 -0
  11. {complexeva-0.1.16 → complexeva-0.2.0}/setup.py +2 -2
  12. complexeva-0.1.16/PKG-INFO +0 -16
  13. complexeva-0.1.16/README.md +0 -8
  14. complexeva-0.1.16/complexeva/dummies/__init__.py +0 -4
  15. complexeva-0.1.16/complexeva/dummies/matrix.py +0 -150
  16. complexeva-0.1.16/complexeva/dummies/networks.py +0 -111
  17. complexeva-0.1.16/complexeva/measures/__init__.py +0 -5
  18. complexeva-0.1.16/complexeva/measures/dimension_1/compression.py +0 -25
  19. complexeva-0.1.16/complexeva/measures/dimension_1/lempel_ziv.py +0 -201
  20. complexeva-0.1.16/complexeva/measures/dimension_2/__init__.py +0 -0
  21. complexeva-0.1.16/complexeva/measures/dimension_2/cbb_matrix.py +0 -134
  22. complexeva-0.1.16/complexeva/measures/network_space/__init__.py +0 -0
  23. complexeva-0.1.16/complexeva/measures/network_space/cbb_networkx.py +0 -288
  24. complexeva-0.1.16/complexeva/signal/__init__.py +0 -0
  25. complexeva-0.1.16/complexeva/signal/co_activation.py +0 -100
  26. complexeva-0.1.16/complexeva/utils/__init__.py +0 -2
  27. complexeva-0.1.16/complexeva/utils/convert_image_matrix.py +0 -48
  28. complexeva-0.1.16/complexeva/utils/convert_matrix_network.py +0 -71
  29. complexeva-0.1.16/complexeva/utils/device.py +0 -29
  30. complexeva-0.1.16/complexeva.egg-info/PKG-INFO +0 -16
  31. complexeva-0.1.16/complexeva.egg-info/SOURCES.txt +0 -27
  32. {complexeva-0.1.16 → complexeva-0.2.0}/complexeva/__init__.py +0 -0
  33. {complexeva-0.1.16 → complexeva-0.2.0}/complexeva.egg-info/dependency_links.txt +0 -0
  34. {complexeva-0.1.16 → complexeva-0.2.0}/complexeva.egg-info/top_level.txt +0 -0
  35. {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 donware import inspect_package
11
- from complexeva.measures.dimension_1.higuchi import hfd
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 plot_hfd_vs_hurst(self):
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.plot_hfd_vs_hurst()
114
+ validator.plot_vs_hurst()
117
115
 
118
- validator = ComplexityMeasuresValidator2D([lempel_ziv_v1, lempel_ziv_v2, lempel_ziv, lempel_ziv_v4])
119
- validator.plot_hfd_vs_hurst()
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.1.16",
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.11",
14
+ pyhon_requires=">=3.12",
15
15
  )
@@ -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
@@ -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,4 +0,0 @@
1
- from .matrix import DummyAdjacencyMatrix
2
- from .networks import WebFractalNetwork, SierpinskiFractal
3
-
4
- __all__ = ["DummyAdjacencyMatrix", "WebFractalNetwork", "SierpinskiFractal"]
@@ -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,5 +0,0 @@
1
- from .dimension_1 import *
2
- from .dimension_2.cbb_matrix import FractalHandlerMatrix
3
- from .network_space.cbb_networkx import FractalHandlerNetworkX
4
-
5
- __all__ = ["FractalHandlerMatrix", "FractalHandlerNetworkX", *dimension_1.__all__]
@@ -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