aisp 0.3.2__py3-none-any.whl → 0.4.0__py3-none-any.whl

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.
aisp/utils/display.py ADDED
@@ -0,0 +1,185 @@
1
+ """Utility functions for displaying algorithm information."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import locale
6
+ import sys
7
+ import time
8
+ from typing import Mapping, Union
9
+
10
+
11
+ def _supports_box_drawing() -> bool:
12
+ """
13
+ Check if the terminal supports boxed characters.
14
+
15
+ Returns
16
+ -------
17
+ bool
18
+ True if the terminal likely supports boxed characters, False otherwise.
19
+ """
20
+ enc = (sys.stdout.encoding or locale.getpreferredencoding(False)).lower()
21
+ if not enc.startswith("utf"):
22
+ return False
23
+ try:
24
+ "┌".encode(enc)
25
+ except UnicodeEncodeError:
26
+ return False
27
+ return True
28
+
29
+
30
+ class TableFormatter:
31
+ """
32
+ Format tabular data into strings for display in the console.
33
+
34
+ Parameters
35
+ ----------
36
+ headers : Mapping[str, int]
37
+ Mapping of column names to their respective widths, in the format
38
+ {column_name: column_width}.
39
+ """
40
+
41
+ def __init__(self, headers: Mapping[str, int]) -> None:
42
+ if not headers or not isinstance(headers, Mapping):
43
+ raise ValueError("'headers' must be a non-empty dictionary.")
44
+ self.headers: Mapping[str, int] = headers
45
+ self._ascii_only = not _supports_box_drawing()
46
+
47
+ def _border(self, left: str, middle: str, right: str, line: str, new_line: bool = True) -> str:
48
+ """
49
+ Create a horizontal border for the table.
50
+
51
+ Parameters
52
+ ----------
53
+ left: str
54
+ Character on the left side of the border.
55
+ middle: str
56
+ Character separator between columns.
57
+ right: str
58
+ Character on the right side of the border.
59
+ line: str
60
+ Character used to fill the border.
61
+ new_line: bool, optional
62
+ If True, adds a line break before the border (default is True).
63
+
64
+ Returns
65
+ -------
66
+ str
67
+ String representing the horizontal border.
68
+ """
69
+ nl = '\n' if new_line else ''
70
+ return f"{nl}{left}{middle.join(line * w for w in self.headers.values())}{right}"
71
+
72
+ def get_header(self):
73
+ """
74
+ Generate the table header, including the top border, column headings, and separator line.
75
+
76
+ Returns
77
+ -------
78
+ str
79
+ Formatted string of the table header.
80
+ """
81
+ if self._ascii_only:
82
+ top = self._border("+", "+", "+", "-")
83
+ sep = self._border("+", "+", "+", "-")
84
+ cell_border = "|"
85
+ else:
86
+ top = self._border("┌", "┬", "┐", "─")
87
+ sep = self._border("├", "┼", "┤", "─")
88
+ cell_border = "│"
89
+
90
+ titles = '\n' + cell_border + cell_border.join(
91
+ f"{h:^{self.headers[h]}}" for h in self.headers
92
+ ) + cell_border
93
+
94
+ return top + titles + sep
95
+
96
+ def get_row(self, values: Mapping[str, Union[str, int, float]]):
97
+ """
98
+ Generate a formatted row for the table data.
99
+
100
+ Parameters
101
+ ----------
102
+ values : Mapping[str, Union[str, int, float]]
103
+ Dictionary with values for each column, in the format
104
+ {column_name: value}.
105
+
106
+ Returns
107
+ -------
108
+ str
109
+ Formatted string of the table row.
110
+ """
111
+ border = "|" if self._ascii_only else "│"
112
+ row = border + border.join(
113
+ f"{values.get(h, ''):^{self.headers[h]}}" for h in self.headers
114
+ ) + border
115
+
116
+ return row
117
+
118
+ def get_bottom(self, new_line: bool = False):
119
+ """
120
+ Generate the table's bottom border.
121
+
122
+ Parameters
123
+ ----------
124
+ new_line : bool, Optional
125
+ If True, adds a line break before the border (default is False).
126
+
127
+ Returns
128
+ -------
129
+ str
130
+ Formatted string for the bottom border.
131
+ """
132
+ if self._ascii_only:
133
+ bottom = self._border("+", "+", "+", "-", new_line)
134
+ else:
135
+ bottom = self._border("└", "┴", "┘", "─", new_line)
136
+ return bottom
137
+
138
+
139
+ class ProgressTable(TableFormatter):
140
+ """
141
+ Display a formatted table in the console to track the algorithm's progress.
142
+
143
+ Parameters
144
+ ----------
145
+ headers : Mapping[str, int]
146
+ Mapping {column_name: column_width}.
147
+ verbose : bool, default=True
148
+ If False, prints nothing to the terminal.
149
+ """
150
+
151
+ def __init__(self, headers: Mapping[str, int], verbose: bool = True) -> None:
152
+ super().__init__(headers)
153
+ if not headers or not isinstance(headers, Mapping):
154
+ raise ValueError("'headers' must be a non-empty dictionary.")
155
+ self.verbose: bool = verbose
156
+ self.headers: Mapping[str, int] = headers
157
+ self._ascii_only = not _supports_box_drawing()
158
+ if self.verbose:
159
+ self._print_header()
160
+ self._start = time.perf_counter()
161
+
162
+ def _print_header(self) -> None:
163
+ """Print the table header."""
164
+ print(self.get_header())
165
+
166
+ def update(self, values: Mapping[str, Union[str, int, float]]) -> None:
167
+ """
168
+ Add a new row of values to the table.
169
+
170
+ Parameters
171
+ ----------
172
+ values: Mapping[str, Union[str, int, float]]
173
+ Keys must match the columns defined in headers.
174
+ """
175
+ if not self.verbose:
176
+ return
177
+ print(self.get_row(values))
178
+
179
+ def finish(self) -> None:
180
+ """End the table display, printing the bottom border and total time."""
181
+ if not self.verbose:
182
+ return
183
+
184
+ print(self.get_bottom())
185
+ print(f"Total time: {time.perf_counter() - self._start:.6f} seconds")
aisp/utils/distance.py CHANGED
@@ -40,7 +40,7 @@ def hamming(u: npt.NDArray[np.bool_], v: npt.NDArray[np.bool_]) -> float64:
40
40
  def euclidean(u: npt.NDArray[np.float64], v: npt.NDArray[np.float64]) -> float64:
41
41
  """Calculate the normalized Euclidean distance between two points.
42
42
 
43
- √( (x₁ x₂)² + (y₁ y₂)² + ... + (yn yn)²)
43
+ √( (x₁ - x₂)² + (y₁ - y₂)² + ... + (yn - yn)²)
44
44
 
45
45
  Parameters
46
46
  ----------
@@ -61,7 +61,7 @@ def euclidean(u: npt.NDArray[np.float64], v: npt.NDArray[np.float64]) -> float64
61
61
  def cityblock(u: npt.NDArray[float64], v: npt.NDArray[float64]) -> float64:
62
62
  """Calculate the normalized Manhattan distance between two points.
63
63
 
64
- (|x₁ x₂| + |y₁ y₂| + ... + |yn yn|) / n
64
+ (|x₁ - x₂| + |y₁ - y₂| + ... + |yn - yn|) / n
65
65
 
66
66
  Parameters
67
67
  ----------
@@ -86,7 +86,7 @@ def cityblock(u: npt.NDArray[float64], v: npt.NDArray[float64]) -> float64:
86
86
  def minkowski(u: npt.NDArray[float64], v: npt.NDArray[float64], p: float = 2.0) -> float64:
87
87
  """Calculate the normalized Minkowski distance between two points.
88
88
 
89
- (( |X₁ Y₁|p + |X₂ Y₂|p + ... + |Xn Yn|p) ¹/ₚ.) / n
89
+ (( |X₁ - Y₁|p + |X₂ - Y₂|p + ... + |Xn - Yn|p) ¹/ₚ.) / n
90
90
 
91
91
  Parameters
92
92
  ----------
@@ -124,7 +124,7 @@ def compute_metric_distance(
124
124
  u: npt.NDArray[float64],
125
125
  v: npt.NDArray[float64],
126
126
  metric: int,
127
- p: float64 = 2.0
127
+ p: float = 2.0
128
128
  ) -> float64:
129
129
  """Calculate the distance between two points by the chosen metric.
130
130
 
aisp/utils/sanitizers.py CHANGED
@@ -1,6 +1,9 @@
1
1
  """Utility functions for validation and treatment of parameters."""
2
2
 
3
- from typing import TypeVar, Iterable, Callable, Any, Optional
3
+ from typing import TypeVar, Iterable, Callable, Any, Optional, Dict
4
+
5
+ import numpy as np
6
+ import numpy.typing as npt
4
7
 
5
8
  T = TypeVar('T')
6
9
 
@@ -62,3 +65,52 @@ def sanitize_seed(seed: Any) -> Optional[int]:
62
65
  The original seed if it is a non-negative integer, or None if it is invalid.
63
66
  """
64
67
  return seed if isinstance(seed, int) and seed >= 0 else None
68
+
69
+
70
+ def sanitize_bounds(bounds: Any, problem_size: int) -> Dict[str, npt.NDArray[np.float64]]:
71
+ """Validate and normalize feature bounds.
72
+
73
+ Parameters
74
+ ----------
75
+ bounds : Any
76
+ The input bounds, which must be either None or a dictionary with 'low'
77
+ and 'high' keys.
78
+ problem_size : int
79
+ The expected length for the normalized bounds lists, corresponding to
80
+ the number of features in the problem.
81
+
82
+ Returns
83
+ -------
84
+ Dict[str, list]
85
+ Dictionary {'low': [low_1, ..., low_N], 'high': [high_1, ..., high_N]}.
86
+
87
+ Raises
88
+ ------
89
+ TypeError
90
+ If `bounds` is not None and not a dict with 'low'/'high', or if items are non-numeric.
91
+ ValueError
92
+ If provided iterables have the wrong length.
93
+ """
94
+ if bounds is None or not isinstance(bounds, dict) or set(bounds.keys()) != {'low', 'high'}:
95
+ raise ValueError("bounds expects a dict with keys 'low' and 'high'")
96
+ result = {}
97
+
98
+ for key in ['low', 'high']:
99
+ value = bounds[key]
100
+ if isinstance(value, (float, int)):
101
+ result[key] = np.array([value] * problem_size).astype(dtype=np.float64)
102
+ else:
103
+ if not isinstance(value, (list, np.ndarray)):
104
+ raise TypeError(
105
+ f"{key} must be a list or numpy array, got {type(value).__name__}"
106
+ )
107
+ if not all(isinstance(i, (float, int)) for i in value):
108
+ raise TypeError(f"All elements of {key} must be numeric")
109
+
110
+ value = np.array(value).astype(dtype=np.float64)
111
+ if len(value) != problem_size:
112
+ raise ValueError(
113
+ f"The size of {key} must be equal to the size of the problem ({problem_size})"
114
+ )
115
+ result[key] = value
116
+ return result
aisp/utils/types.py CHANGED
@@ -8,24 +8,32 @@ FeatureType : Literal["binary-features", "continuous-features", "ranged-features
8
8
  - "binary-features": Features with binary values (e.g., 0 or 1).
9
9
  - "continuous-features": Features with continuous numeric values.
10
10
  - "ranged-features": Features represented by ranges or intervals.
11
+ FeatureTypeAll : Literal["binary-features", "continuous-features", "ranged-features"]
12
+ Specifies the type of features in the input data. Can be one of:
13
+ - "binary-features": Features with binary values (e.g., 0 or 1).
14
+ - "continuous-features": Features with continuous numeric values.
15
+ - "ranged-features": Features represented by ranges or intervals.
16
+ - "permutation-features": Features represented by permutation.
11
17
 
12
18
  MetricType : Literal["manhattan", "minkowski", "euclidean"]
13
19
  Specifies the distance metric to use for calculations. Possible values:
14
20
  - "manhattan": The calculation of the distance is given by the expression:
15
- √( (x₁ x₂)² + (y₁ y₂)² + ... + (yn yn)²).
21
+ √( (x₁ - x₂)² + (y₁ - y₂)² + ... + (yn - yn)²).
16
22
  - "minkowski": The calculation of the distance is given by the expression:
17
- ( |X₁ Y₁|p + |X₂ Y₂|p + ... + |Xn Yn|p) ¹/ₚ.
23
+ ( |X₁ - Y₁|p + |X₂ - Y₂|p + ... + |Xn - Yn|p) ¹/ₚ.
18
24
  - "euclidean": The calculation of the distance is given by the expression:
19
- ( |x₁ x₂| + |y₁ y₂| + ... + |yn yn|).
25
+ ( |x₁ - x₂| + |y₁ - y₂| + ... + |yn - yn|).
20
26
  """
21
27
 
22
28
 
23
- from typing import Literal, TypeAlias
24
-
29
+ from typing import Literal, TypeAlias, Union
25
30
 
26
31
  FeatureType: TypeAlias = Literal[
27
32
  "binary-features",
28
33
  "continuous-features",
29
34
  "ranged-features"
30
35
  ]
36
+
37
+ FeatureTypeAll: TypeAlias = Union[FeatureType, Literal["permutation-features"]]
38
+
31
39
  MetricType: TypeAlias = Literal["manhattan", "minkowski", "euclidean"]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aisp
3
- Version: 0.3.2
3
+ Version: 0.4.0
4
4
  Summary: Package with techniques of artificial immune systems.
5
5
  Author-email: João Paulo da Silva Barros <jpsilvabarr@gmail.com>
6
6
  Maintainer-email: Alison Zille Lopes <alisonzille@gmail.com>
@@ -86,6 +86,7 @@ Artificial Immune Systems (AIS) are inspired by the vertebrate immune system, cr
86
86
  > - [x] [**Negative Selection.**](https://ais-package.github.io/docs/aisp-techniques/negative-selection/)
87
87
  > - [x] [**Clonal Selection Algorithms.**](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/)
88
88
  > * [AIRS - Artificial Immune Recognition System](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/airs/)
89
+ > * [CLONALG - Clonal Selection Algorithm](https://ais-package.github.io/docs/aisp-techniques/clonal-selection-algorithms/clonalg)
89
90
  > - [ ] *Danger Theory.*
90
91
  > - [x] [*Immune Network Theory.*](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/)
91
92
  > - [AiNet - Artificial Immune Network para Clustering and Compression](https://ais-package.github.io/docs/aisp-techniques/immune-network-theory/ainet)
@@ -0,0 +1,35 @@
1
+ aisp/__init__.py,sha256=9HfYCxZnkj-4R6uZX6ZM6mJt93qzkDo3_h_UwjY6FW0,1244
2
+ aisp/exceptions.py,sha256=I9JaQx6p8Jo7qjwwcrqnuewQgyBdUnOSSZofPoBeDNE,1954
3
+ aisp/base/__init__.py,sha256=NioBFTZmfG6SVk29kH2aQGqpWjvsNlEqY_sMmYLDdFg,763
4
+ aisp/base/_base.py,sha256=uTVh__hQJGe8RCOzCet4ZV3vQbwgj5fXAt4Jdf0x1r0,1792
5
+ aisp/base/_classifier.py,sha256=XibBY-1xPPc-AX6rJaxTOqSQ-7thzAgeR9VggBX8hYk,3536
6
+ aisp/base/_clusterer.py,sha256=-wzZ4vSI_ZtPvLYryfJ75VaKcp67jxl92lJxfjopVYc,2468
7
+ aisp/base/_optimizer.py,sha256=81nRa_KJqw-UZ4IYUFvWWq6bpOR5TMCKr41MNet6oj8,6517
8
+ aisp/base/mutation.py,sha256=S-oh8O0ndMOE8bYIhhKhzxCS99r3nefFVEZ3cb7X3ps,7522
9
+ aisp/base/populations.py,sha256=5y125iJgTu_S0XI1BlAECZLSFua75l3Oo47x_3uI7ss,1750
10
+ aisp/csa/__init__.py,sha256=8e6zUPxZAlQ4NDpDhM4symeewzAwk8-CkqYfeBiPvGQ,799
11
+ aisp/csa/_ai_recognition_sys.py,sha256=00swgeB2CgpQ4zz2bvuVk50jN7839g9pbb33_9cdsv0,18947
12
+ aisp/csa/_base.py,sha256=CIj6NDNAH-GCuZpDXAHEWe7jSHUum7mUOnnvXnkEy60,3593
13
+ aisp/csa/_cell.py,sha256=KS0zqJ4JVipUI6oikDUkzeL2Ayml_GtCQ_HjDQB-HQc,1879
14
+ aisp/csa/_clonalg.py,sha256=Y-WOy27HmstMamyTBg4K1kqXlrxy7iahS4TmfAcc-kM,13770
15
+ aisp/ina/__init__.py,sha256=D69sBmTCa0LYd_dzTbb0QvhgMpWWztD2VIjLCZig2c4,392
16
+ aisp/ina/_ai_network.py,sha256=fmi7wt8EJPuZKmv-iB6J2HDDv0c4lKED-xknmxy-45o,21497
17
+ aisp/ina/_base.py,sha256=O-jiCgqQG_QnG1xlp7mv5u9BZTmuW4pnAV09L9HvjBE,2797
18
+ aisp/nsa/__init__.py,sha256=33RJ5yDW2W-J5ZdUBB1Fk_XFw4bv8AXWvO_amkm79xA,713
19
+ aisp/nsa/_base.py,sha256=3YKlZzA3yhP2uQHfhyKswbHUutlxkOR4wn6N10nSO-w,4119
20
+ aisp/nsa/_binary_negative_selection.py,sha256=Fj8TnS1E9zJOlEKUW4AREYaqftSCO7DSc7lU4L0s_cc,9767
21
+ aisp/nsa/_negative_selection.py,sha256=u3-dKRA-o5J6PUwsazD0lSG3NuUFrsDXZ08jPeU0LsA,18791
22
+ aisp/nsa/_ns_core.py,sha256=SXkZL-p2VQygU4Pf6J5AP_yPzU4cR6aU6wx-e_vlm-c,5021
23
+ aisp/utils/__init__.py,sha256=RzpKhkg8nCZi4G0C4il97f3ESYs7Bbxq6EjTeOQQUGk,195
24
+ aisp/utils/_multiclass.py,sha256=ZC7D2RG1BgkpzjZMU4IHC6uAKyFcqCx10d2jbxCM9xE,1136
25
+ aisp/utils/display.py,sha256=bKRGaB_VVHtKLbJE_XDJXD2fVR1qYX0wxKOlLAqVdYw,5752
26
+ aisp/utils/distance.py,sha256=Ad9wnNL3TwBTk-Tav0jMuuUWWiPxTkFhIYmgepq5Y0Q,6556
27
+ aisp/utils/metrics.py,sha256=zDAScDbHRnfu24alRcZ6fEIUaWNoCD-QCtOCFBWPPo8,1277
28
+ aisp/utils/sanitizers.py,sha256=-ZMn8L5nnRNhtJ3GNr4KkS9383du6B6QkV48xQl3fzw,3771
29
+ aisp/utils/types.py,sha256=LV2J1m-VG-55CHoLGOqONr3OKsrwqTI2e5APSNr2si4,1833
30
+ aisp/utils/validation.py,sha256=RqcS2VdFXkNcOH_7Y3yPi7FBoGWR_ReLBPDBx0UMCqI,1431
31
+ aisp-0.4.0.dist-info/licenses/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
32
+ aisp-0.4.0.dist-info/METADATA,sha256=RJHKap1PrJm0DGmSQYjDWRz70HF8vgPnUirnJT7DK5s,5292
33
+ aisp-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ aisp-0.4.0.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
35
+ aisp-0.4.0.dist-info/RECORD,,
@@ -1,31 +0,0 @@
1
- aisp/__init__.py,sha256=LuDzEKwj0mzm-0Xqv79qvYMJc7eroVZaHfWRQQRlgFY,708
2
- aisp/exceptions.py,sha256=I9JaQx6p8Jo7qjwwcrqnuewQgyBdUnOSSZofPoBeDNE,1954
3
- aisp/base/__init__.py,sha256=b-YzeW1iIAEp7JReS6TVzTeBZwFVsfxtOqG9W4gDvdc,211
4
- aisp/base/_base.py,sha256=uTVh__hQJGe8RCOzCet4ZV3vQbwgj5fXAt4Jdf0x1r0,1792
5
- aisp/base/_classifier.py,sha256=xmD7SlPFjdWdrK7dkK8GzLFGOq5w_4jkzK4J-eqHlwA,3375
6
- aisp/base/_clusterer.py,sha256=-wzZ4vSI_ZtPvLYryfJ75VaKcp67jxl92lJxfjopVYc,2468
7
- aisp/base/mutation.py,sha256=A_AlGp8S4ooFEMW3Jgv0n0Y6tbhfusaMMWFsoH4HmD8,4762
8
- aisp/csa/__init__.py,sha256=708jwpqia10bqmh-4-_srwwNuBh7jf2Zix-u8Hfbzmk,348
9
- aisp/csa/_ai_recognition_sys.py,sha256=16N2rWyfv9tlwyBUgAcSoFLkgZMF7CPqg1xA7sPH8Tg,18873
10
- aisp/csa/_base.py,sha256=CIj6NDNAH-GCuZpDXAHEWe7jSHUum7mUOnnvXnkEy60,3593
11
- aisp/csa/_cell.py,sha256=GUxnzvPyIbBm1YYkMhSx0tcV_oyDhJ7wAo5gtr_1CoY,1845
12
- aisp/ina/__init__.py,sha256=cOnxGcxrBdg6lLv2w2sdlToMahKMh_Gw57AfUUPQjMo,329
13
- aisp/ina/_ai_network.py,sha256=TN9SBxT1kUDP2_fTV6tohc9Qt7pBn-LndnfA1AiCkfw,20986
14
- aisp/ina/_base.py,sha256=ckKStxiuuwz8O3O2KEb4JFLIifH3I-4O3rhfrmH9oMU,4402
15
- aisp/nsa/__init__.py,sha256=vL_HbASV6aGiiHAMx0UShqkL-ly-OYcqQLasKdu9p-M,425
16
- aisp/nsa/_base.py,sha256=3YKlZzA3yhP2uQHfhyKswbHUutlxkOR4wn6N10nSO-w,4119
17
- aisp/nsa/_binary_negative_selection.py,sha256=aF8YHQNvJmw6-6pqq4OMXn8rSaqWd1-EuBZieh_louI,9752
18
- aisp/nsa/_negative_selection.py,sha256=1BbFbXEzCWRsRtou5ysZfXtL6aKXM9TomKZXXW6Amp4,18794
19
- aisp/nsa/_ns_core.py,sha256=SXkZL-p2VQygU4Pf6J5AP_yPzU4cR6aU6wx-e_vlm-c,5021
20
- aisp/utils/__init__.py,sha256=RzpKhkg8nCZi4G0C4il97f3ESYs7Bbxq6EjTeOQQUGk,195
21
- aisp/utils/_multiclass.py,sha256=ZC7D2RG1BgkpzjZMU4IHC6uAKyFcqCx10d2jbxCM9xE,1136
22
- aisp/utils/distance.py,sha256=pY23YGZpu6qVCCkZfhaEpRazUULfVUy2piyzYuAryN0,6576
23
- aisp/utils/metrics.py,sha256=zDAScDbHRnfu24alRcZ6fEIUaWNoCD-QCtOCFBWPPo8,1277
24
- aisp/utils/sanitizers.py,sha256=u1GizdJ-RKfPWJLnuFiM09lpItZMhDR_EvK8YdVHwDk,1858
25
- aisp/utils/types.py,sha256=KELzr1kSBT7hHdsABoIS1xmEBGj6gRSH5A5YNG36I_c,1324
26
- aisp/utils/validation.py,sha256=RqcS2VdFXkNcOH_7Y3yPi7FBoGWR_ReLBPDBx0UMCqI,1431
27
- aisp-0.3.2.dist-info/licenses/LICENSE,sha256=fTqV5eBpeAZO0_jit8j4Ref9ikBSlHJ8xwj5TLg7gFk,7817
28
- aisp-0.3.2.dist-info/METADATA,sha256=KMn1LvxB9cyRp2YVUZmgS8isganeS0vOOEUGON_e-xE,5156
29
- aisp-0.3.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
30
- aisp-0.3.2.dist-info/top_level.txt,sha256=Q5aJi_rAVT5UNS1As0ZafoyS5dwNibnoyOYV7RWUB9s,5
31
- aisp-0.3.2.dist-info/RECORD,,
File without changes