psyke 0.8.2.dev72__py3-none-any.whl → 0.8.4__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.

Potentially problematic release.


This version of psyke might be problematic. Click here for more details.

@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import math
3
4
  from abc import ABC
4
5
  import numpy as np
5
6
  import pandas as pd
@@ -96,12 +97,21 @@ class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC):
96
97
  return prediction, different_prediction_reasons
97
98
 
98
99
  def __get_local_conditions(self, data: dict[str, float], cube: GenericCube) -> dict[list[Value]]:
99
- conditions = {d: [Between(*cube.dimensions[d])] for d in cube.dimensions}
100
+ conditions = {d: [cube.interval_to_value(d, self.unscale)] for d in data.keys()}
100
101
  subcubes = cube.subcubes(self._hypercubes)
101
- for c in [c for c in subcubes if sum(c in sc and c != sc for sc in subcubes) == 0]:
102
- for d in [d for d in c.dimensions if d in data]:
103
- if c.dimensions[d][0] > data[d] or c.dimensions[d][1] < data[d]:
104
- conditions[d].append(Outside(*c.dimensions[d]))
102
+ subsubcubes = [c for cube_list in [c.subcubes(self._hypercubes) for c in subcubes] for c in cube_list]
103
+ for c in [c for c in subcubes if c not in subsubcubes]:
104
+ for d in conditions:
105
+ condition = c.interval_to_value(d, self.unscale)
106
+ if condition is None:
107
+ continue
108
+ elif conditions[d][-1] is None:
109
+ conditions[d][-1] = -condition
110
+ elif (-condition).is_in(data[d]):
111
+ try:
112
+ conditions[d][-1] *= -condition
113
+ except Exception:
114
+ conditions[d].append(-condition)
105
115
  return conditions
106
116
 
107
117
  def predict_why(self, data: dict[str, float]):
@@ -112,51 +122,18 @@ class HyperCubeExtractor(HyperCubePredictor, PedagogicalExtractor, ABC):
112
122
  output = self._predict_from_cubes(data)
113
123
  print(f"The output is {output} because")
114
124
  conditions = self.__get_local_conditions(data, cube)
115
- for d in data.keys():
116
- simplified = HyperCubeExtractor.__simplify(conditions[d])
117
- for i, condition in enumerate(simplified):
118
- if i == 0:
125
+ for d in conditions:
126
+ for i, condition in enumerate(conditions[d]):
127
+ if condition is None:
128
+ continue
129
+ elif i == 0:
119
130
  print(' ', d, 'is', end=' ')
120
131
  else:
121
132
  print('and', end=' ')
122
- if isinstance(condition, Outside):
123
- print('not', end=' ')
124
- print('between', round(condition.lower, 1), 'and', round(condition.upper, 1), end=' ')
125
- if i + 1 == len(simplified):
133
+ print(condition.print(), end='')
134
+ if i + 1 == len(conditions[d]):
126
135
  print()
127
136
 
128
- @staticmethod
129
- def __simplify(conditions):
130
- simplified = []
131
- for condition in conditions:
132
- to_add = True
133
- for i, simple in enumerate(simplified):
134
- if isinstance(condition, Outside) and isinstance(simple, Outside):
135
- if simple.lower <= condition.lower <= simple.upper or \
136
- simple.lower <= condition.upper <= simple.upper or \
137
- condition.lower <= simple.lower <= simple.upper <= condition.upper:
138
- simplified[i].upper = max(condition.upper, simple.upper)
139
- simplified[i].lower = min(condition.lower, simple.lower)
140
- to_add = False
141
- break
142
- elif isinstance(condition, Outside) and isinstance(simple, Between):
143
- if simple.lower >= condition.upper or simple.upper <= condition.lower:
144
- to_add = False
145
- break
146
- elif condition.lower <= simple.lower <= condition.upper <= simple.upper:
147
- simplified[i].lower = condition.upper
148
- to_add = False
149
- break
150
- elif simple.lower <= condition.lower <= simple.upper <= condition.upper:
151
- simplified[i].upper = condition.lower
152
- to_add = False
153
- break
154
- elif condition.lower <= simple.lower <= simple.upper <= condition.upper:
155
- raise ValueError
156
- if to_add:
157
- simplified.append(condition)
158
- return simplified
159
-
160
137
  @staticmethod
161
138
  def _create_head(dataframe: pd.DataFrame, variables: list[Var], output: float | LinearRegression) -> Struct:
162
139
  return create_head(dataframe.columns[-1], variables[:-1], output) \
@@ -13,7 +13,6 @@ from psyke.utils import get_default_precision, get_int_precision, Target, get_de
13
13
  from psyke.utils.logic import create_term, to_rounded_real, linear_function_creator
14
14
  from sklearn.linear_model import LinearRegression
15
15
  from tuprolog.core import Var, Struct
16
- from random import Random
17
16
  import numpy as np
18
17
 
19
18
 
@@ -149,6 +148,9 @@ class HyperCube:
149
148
  else:
150
149
  self._infinite_dimensions[dimension] = [direction]
151
150
 
151
+ def copy_infinite_dimensions(self, dimensions: dict[str, str]):
152
+ self._infinite_dimensions = dimensions.copy()
153
+
152
154
  @property
153
155
  def dimensions(self) -> Dimensions:
154
156
  return self._dimensions
@@ -231,12 +233,14 @@ class HyperCube:
231
233
  return False
232
234
 
233
235
  def copy(self) -> HyperCube:
234
- return HyperCube(self.dimensions.copy(), self._limits.copy(), self.output)
236
+ new_cube = HyperCube(self.dimensions.copy(), self._limits.copy(), self.output)
237
+ new_cube.copy_infinite_dimensions(self._infinite_dimensions)
238
+ return new_cube
235
239
 
236
240
  def count(self, dataset: pd.DataFrame) -> int:
237
241
  return self.filter_dataframe(dataset.iloc[:, :-1]).shape[0]
238
242
 
239
- def _interval_to_value(self, dimension, unscale):
243
+ def interval_to_value(self, dimension, unscale=None):
240
244
  if dimension not in self._infinite_dimensions:
241
245
  return Between(unscale(self[dimension][0], dimension), unscale(self[dimension][1], dimension))
242
246
  if len(self._infinite_dimensions[dimension]) == 2:
@@ -247,7 +251,7 @@ class HyperCube:
247
251
  return LessThan(unscale(self[dimension][1], dimension))
248
252
 
249
253
  def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, normalization=None) -> Iterable[Struct]:
250
- values = [(dim, self._interval_to_value(dim, unscale)) for dim in self.dimensions if dim not in ignore]
254
+ values = [(dim, self.interval_to_value(dim, unscale)) for dim in self.dimensions if dim not in ignore]
251
255
  return [create_term(variables[name], value) for name, value in values
252
256
  if not self.is_default and value is not None]
253
257
 
@@ -439,8 +443,8 @@ class HyperCube:
439
443
 
440
444
 
441
445
  class RegressionCube(HyperCube):
442
- def __init__(self, dimension: dict[str, tuple] = None, output=None):
443
- super().__init__(dimension=dimension, output=LinearRegression() if output is None else output)
446
+ def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None, output=None):
447
+ super().__init__(dimension=dimension, limits=limits, output=LinearRegression() if output is None else output)
444
448
 
445
449
  def update(self, dataset: pd.DataFrame, predictor) -> None:
446
450
  filtered = self.filter_dataframe(dataset.iloc[:, :-1])
@@ -458,7 +462,9 @@ class RegressionCube(HyperCube):
458
462
  output.intercept_ = self.output.intercept_
459
463
  except AttributeError:
460
464
  pass
461
- return RegressionCube(self.dimensions.copy(), output=output)
465
+ new_cube = RegressionCube(self.dimensions.copy(), self._limits.copy(), output)
466
+ new_cube.copy_infinite_dimensions(self._infinite_dimensions)
467
+ return new_cube
462
468
 
463
469
  def body(self, variables: dict[str, Var], ignore: list[str], unscale=None, normalization=None) -> Iterable[Struct]:
464
470
  intercept = self.output.intercept_ if normalization is None else unscale(sum(
@@ -487,12 +493,15 @@ class ClassificationCube(HyperCube):
487
493
  self._barycenter = Point(means.index.values, means.values)
488
494
 
489
495
  def copy(self) -> ClassificationCube:
490
- return ClassificationCube(self.dimensions.copy(), self._limits.copy(), self._output)
496
+ new_cube = ClassificationCube(self.dimensions.copy(), self._limits.copy(), self.output)
497
+ new_cube.copy_infinite_dimensions(self._infinite_dimensions)
498
+ return new_cube
491
499
 
492
500
 
493
501
  class ClosedCube(HyperCube):
494
- def __init__(self, dimension: dict[str, tuple] = None, output: str | LinearRegression | float = 0.0):
495
- super().__init__(dimension=dimension, output=output)
502
+ def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None,
503
+ output: str | LinearRegression | float = 0.0):
504
+ super().__init__(dimension=dimension, limits=limits, output=output)
496
505
 
497
506
  def __contains__(self, obj: dict[str, float] | ClosedCube) -> bool:
498
507
  """
@@ -533,12 +542,14 @@ class ClosedCube(HyperCube):
533
542
  return np.all((v[:, 0] <= ds) & (ds <= v[:, 1]), axis=1)
534
543
 
535
544
  def copy(self) -> ClosedCube:
536
- return ClosedCube(self.dimensions.copy(), output=self._output)
545
+ new_cube = ClosedCube(self.dimensions.copy(), self._limits.copy(), self.output)
546
+ new_cube.copy_infinite_dimensions(self._infinite_dimensions)
547
+ return new_cube
537
548
 
538
549
 
539
550
  class ClosedRegressionCube(ClosedCube, RegressionCube):
540
- def __init__(self, dimension: dict[str, tuple] = None, output=None):
541
- super().__init__(dimension=dimension, output=LinearRegression() if output is None else output)
551
+ def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None, output=None):
552
+ super().__init__(dimension=dimension, limits=limits, output=LinearRegression() if output is None else output)
542
553
 
543
554
  def copy(self) -> ClosedRegressionCube:
544
555
  output = LinearRegression()
@@ -547,15 +558,19 @@ class ClosedRegressionCube(ClosedCube, RegressionCube):
547
558
  output.intercept_ = self.output.intercept_
548
559
  except AttributeError:
549
560
  pass
550
- return ClosedRegressionCube(self.dimensions.copy(), output=output)
561
+ new_cube = ClosedRegressionCube(self.dimensions.copy(), self._limits.copy(), output)
562
+ new_cube.copy_infinite_dimensions(self._infinite_dimensions)
563
+ return new_cube
551
564
 
552
565
 
553
566
  class ClosedClassificationCube(ClosedCube, ClassificationCube):
554
- def __init__(self, dimension: dict[str, tuple] = None, output: str = None):
555
- super().__init__(dimension=dimension, output=output)
567
+ def __init__(self, dimension: dict[str, tuple] = None, limits: set[Limit] = None, output: str = None):
568
+ super().__init__(dimension=dimension, limits=limits, output=output)
556
569
 
557
570
  def copy(self) -> ClosedClassificationCube:
558
- return ClosedClassificationCube(self.dimensions.copy(), output=self._output)
571
+ new_cube = ClosedClassificationCube(self.dimensions.copy(), self._limits.copy(), self.output)
572
+ new_cube.copy_infinite_dimensions(self._infinite_dimensions)
573
+ return new_cube
559
574
 
560
575
 
561
576
  GenericCube = Union[HyperCube, ClassificationCube, RegressionCube,
psyke/schema/__init__.py CHANGED
@@ -85,12 +85,26 @@ class Value:
85
85
  else:
86
86
  return False
87
87
 
88
+ def __neg__(self) -> Value:
89
+ if isinstance(self, Constant):
90
+ return self
91
+ elif isinstance(self, GreaterThan):
92
+ return LessThan(self.value, self.standard)
93
+ elif isinstance(self, LessThan):
94
+ return GreaterThan(self.value, self.standard)
95
+ elif isinstance(self, Between):
96
+ return Outside(self.lower, self.upper, self.standard)
97
+ elif isinstance(self, Outside):
98
+ return Between(self.lower, self.upper, self.standard)
99
+ else:
100
+ raise TypeError
101
+
88
102
  # TODO: handle convention (low priority).
89
103
  def __mul__(self, other) -> Value:
90
104
 
91
105
  def intersection_with_constant(first_value: Constant, second_value: Value) -> Value:
92
106
  if isinstance(first_value, Constant):
93
- if second_value.is_in(first_value.value):
107
+ if first_value in second_value:
94
108
  return first_value
95
109
  else:
96
110
  raise _EMPTY_INTERSECTION_EXCEPTION(first_value, second_value)
@@ -100,42 +114,45 @@ class Value:
100
114
  def intersection_with_outside(first_value: Outside, second_value: Value) -> Value:
101
115
  if isinstance(first_value, Outside):
102
116
  if isinstance(second_value, LessThan):
103
- if second_value.value <= first_value.lower:
104
- return second_value
105
- elif first_value.is_in(second_value.value):
117
+ if second_value.value > first_value.upper:
118
+ # LessThan(first_value.lower) + Between(first_value.lower, second_value.value)
119
+ raise _NOT_IMPLEMENTED_INTERSECTION(first_value, second_value)
120
+ elif second_value.value > first_value.lower:
106
121
  return LessThan(first_value.lower)
107
122
  else:
108
- raise _NOT_IMPLEMENTED_INTERSECTION(first_value, second_value)
123
+ return second_value
109
124
  elif isinstance(second_value, GreaterThan):
110
- if second_value.value >= first_value.lower:
125
+ if second_value.value < first_value.lower:
126
+ # Between(second_value.value, first_value.lower) + GreaterThan(first_value.upper)
127
+ raise _NOT_IMPLEMENTED_INTERSECTION(first_value, second_value)
128
+ elif second_value.value < first_value.upper:
111
129
  return GreaterThan(first_value.upper)
112
- elif first_value.is_in(second_value.value):
113
- return second_value
114
130
  else:
115
- raise _NOT_IMPLEMENTED_INTERSECTION(first_value, second_value)
116
- elif isinstance(second_value, Constant):
117
- if not first_value.is_in(second_value.value):
118
131
  return second_value
119
- else:
120
- raise _EMPTY_INTERSECTION_EXCEPTION(first_value, second_value)
121
132
  elif isinstance(second_value, Between):
122
- if second_value in first_value:
133
+ if second_value.upper <= first_value.lower or second_value.lower >= first_value.upper:
123
134
  return second_value
124
135
  elif second_value.lower <= first_value.lower <= second_value.upper <= first_value.upper:
125
136
  return Between(second_value.lower, first_value.lower)
126
137
  elif first_value.lower <= second_value.lower <= first_value.upper <= second_value.upper:
127
138
  return Between(first_value.upper, second_value.upper)
128
- else:
139
+ elif second_value.lower <= first_value.lower <= first_value.upper <= second_value.upper:
129
140
  raise _NOT_IMPLEMENTED_INTERSECTION(first_value, second_value)
141
+ else:
142
+ raise _EMPTY_INTERSECTION_EXCEPTION(first_value, second_value)
130
143
  elif isinstance(second_value, Outside):
131
- if second_value.lower <= first_value.lower and second_value.upper >= first_value.upper:
144
+ if second_value.lower <= first_value.lower <= first_value.upper <= second_value.upper:
132
145
  return second_value
133
- elif first_value.lower <= second_value.lower and first_value.upper >= second_value.upper:
146
+ elif first_value.lower <= second_value.lower <= second_value.upper <= first_value.upper:
134
147
  return first_value
148
+ elif second_value.lower <= first_value.lower <= second_value.upper <= first_value.upper:
149
+ return Outside(second_value.lower, first_value.upper)
150
+ elif first_value.lower <= second_value.lower <= first_value.upper <= second_value.upper:
151
+ return Outside(first_value.lower, second_value.upper)
135
152
  else:
136
153
  raise _EMPTY_INTERSECTION_EXCEPTION(first_value, second_value)
137
154
  elif isinstance(second_value, Constant):
138
- intersection_with_constant(second_value, first_value)
155
+ return intersection_with_constant(second_value, first_value)
139
156
  else:
140
157
  raise _INTERSECTION_WITH_WRONG_TYPE(first_value, second_value)
141
158
  else:
@@ -173,7 +190,7 @@ class Value:
173
190
  else:
174
191
  raise _EMPTY_INTERSECTION_EXCEPTION(first_value, second_value)
175
192
  elif isinstance(second_value, Constant):
176
- intersection_with_constant(second_value, first_value)
193
+ return intersection_with_constant(second_value, first_value)
177
194
  elif isinstance(second_value, Outside):
178
195
  return intersection_with_outside(second_value, first_value)
179
196
  else:
@@ -264,6 +281,9 @@ class LessThan(Interval):
264
281
  def value(self) -> float:
265
282
  return self.upper
266
283
 
284
+ def print(self) -> str:
285
+ return f"below {round(self.upper, 1)}"
286
+
267
287
  def __str__(self):
268
288
  return f"]-∞, {self.upper:.2f}" + ("]" if self.standard else "[")
269
289
 
@@ -286,6 +306,9 @@ class GreaterThan(Interval):
286
306
  def value(self) -> float:
287
307
  return self.lower
288
308
 
309
+ def print(self) -> str:
310
+ return f"above {round(self.lower, 1)}"
311
+
289
312
  def __str__(self):
290
313
  return ("]" if self.standard else "[") + f"{self.lower:.2f}, ∞["
291
314
 
@@ -304,6 +327,9 @@ class Between(Interval):
304
327
  def is_in(self, other: float) -> bool:
305
328
  return self.lower <= other < self.upper if self.standard else self.lower < other <= self.upper
306
329
 
330
+ def print(self) -> str:
331
+ return f"between {round(self.lower, 1)} and {round(self.upper, 1)}"
332
+
307
333
  def __str__(self):
308
334
  return ("[" if self.standard else "]") + f"{self.lower:.2f}, {self.upper:.2f}" + ("[" if self.standard else "]")
309
335
 
@@ -319,6 +345,9 @@ class Outside(Interval):
319
345
  def is_in(self, other: float) -> bool:
320
346
  return other < self.lower or self.upper <= other if self.standard else other <= self.lower or self.upper < other
321
347
 
348
+ def print(self) -> str:
349
+ return f"not between {round(self.lower, 1)} and {round(self.upper, 1)}"
350
+
322
351
  def __str__(self):
323
352
  return f"]-∞, {self.lower:.2f}" + ("[" if self.standard else "]") + ' U '\
324
353
  + ("[" if self.standard else "]") + f"{self.upper:.2f}, ∞["
@@ -336,6 +365,9 @@ class Constant(Value):
336
365
  def is_in(self, other: float) -> bool:
337
366
  return math.isclose(other, self.value)
338
367
 
368
+ def print(self) -> str:
369
+ return f"equal {round(self.value, 1)}"
370
+
339
371
  def __str__(self):
340
372
  return "{" + str(self.value) + "}"
341
373
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: psyke
3
- Version: 0.8.2.dev72
3
+ Version: 0.8.4
4
4
  Summary: Python-based implementation of PSyKE, i.e. a Platform for Symbolic Knowledge Extraction
5
5
  Home-page: https://github.com/psykei/psyke-python
6
6
  Author: Matteo Magnini
@@ -7,8 +7,8 @@ psyke/clustering/exact/__init__.py,sha256=s4MPvGZ6gle3X9WH3YFHOEdinGcXIXh-7EFRcE
7
7
  psyke/extraction/__init__.py,sha256=ziZ8T9eAOZjKipepE5_j1zfZgyFPONjW8MGERSk83nI,743
8
8
  psyke/extraction/cart/__init__.py,sha256=s8tr7jPhONgF0pfetHieFax2MD8cy6ddOS7dRWzZLjc,3579
9
9
  psyke/extraction/cart/predictor.py,sha256=2-2mv5fI0lTwwfTaEonxKh0ZUdhxuIEE6OP_rJxgmqc,3019
10
- psyke/extraction/hypercubic/__init__.py,sha256=34ZB7zwLs3NKaJBKlpxytnyfGtOl9LVdZX3ppxZrN8k,12042
11
- psyke/extraction/hypercubic/hypercube.py,sha256=Ws0tky0CMvCVuIwBJNxmY97xsG7VapwLQ2uSwpWlb1g,24510
10
+ psyke/extraction/hypercubic/__init__.py,sha256=vV4xIvyUyeQczrTHYuBzHcQrNc_967x7ZWHT3FommUg,10599
11
+ psyke/extraction/hypercubic/hypercube.py,sha256=kDEhYlY7EahWV07AwEZIdEu94HIQUw-CdoKBdle61BQ,25430
12
12
  psyke/extraction/hypercubic/strategy.py,sha256=X-roIsfcpJyMdo2px5JtbhP7-XE-zUNkaEK7XGXoWA8,1636
13
13
  psyke/extraction/hypercubic/utils.py,sha256=D2FN5CCm_T3h23DmLFoTnIcFo7LvIq__ktl4hjUqkcA,1525
14
14
  psyke/extraction/hypercubic/cosmik/__init__.py,sha256=XQUvOtMFpR0vMHYtwIVl3G626HMqN8Clt6BqNm4nvFs,1880
@@ -22,7 +22,7 @@ psyke/extraction/real/__init__.py,sha256=fFqiwgWTpu5Jx9lz5CdSfs1QyqWYFLQDG7tc5M6
22
22
  psyke/extraction/real/utils.py,sha256=eHGU-Y0inn_8jrk9lMcuRUKXpsTkI-s_myXSWz4bALQ,2190
23
23
  psyke/extraction/trepan/__init__.py,sha256=KpZpk0btCWV4bS-DOmpgpYscSQ5FEMyP54ekm7ZedME,6583
24
24
  psyke/extraction/trepan/utils.py,sha256=iSUJ1ooNQT_VO1KfBZuIUeUsyUbGdQf_pSEE87vMeQg,2320
25
- psyke/schema/__init__.py,sha256=gOUWx3gYSkRehlJ5opK0Q16-Tv5fwSTl19k7kzIHALU,15760
25
+ psyke/schema/__init__.py,sha256=R3rH22YdA_aEB62YBJXGHpcVAgxZ12jFzgQFzKTzfDc,17306
26
26
  psyke/tuning/__init__.py,sha256=I-07lLZb02DoIm9AGXPPPOkB55ANu8RU4TMy2j30Pxg,3574
27
27
  psyke/tuning/crash/__init__.py,sha256=zIHEF75EFy_mRIieqzP04qKLG3GLsSc_mYZHpPfkzxU,2623
28
28
  psyke/tuning/orchid/__init__.py,sha256=s64iABbteik27CrRPHSVHNZX25JKlDu7YYjhseOizxw,3618
@@ -33,8 +33,8 @@ psyke/utils/logic.py,sha256=7bbW6qcKof5PlqoQ0n5Kt3Obcot-KqGAvpE8rMXvEPE,12419
33
33
  psyke/utils/metrics.py,sha256=Oo5BOonOSfo0qYsXWT5dmypZ7jiStByFC2MKEU0uMHg,2250
34
34
  psyke/utils/plot.py,sha256=dE8JJ6tQ0Ezosid-r2jqAisREjFe5LqExRzsVi5Ns-c,7785
35
35
  psyke/utils/sorted.py,sha256=C3CPW2JisND30BRk5c1sAAHs3Lb_wsRB2qZrYFuRnfM,678
36
- psyke-0.8.2.dev72.dist-info/LICENSE,sha256=KP9K6Hgezf_xdMFW7ORyKz9uA8Y8k52YJn292wcP-_E,11354
37
- psyke-0.8.2.dev72.dist-info/METADATA,sha256=oNgGeNL3rG3VvOlf-yDu8rVus9SmVZBiI1s_I3ZD8hg,8108
38
- psyke-0.8.2.dev72.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
39
- psyke-0.8.2.dev72.dist-info/top_level.txt,sha256=q1HglxOqqoIRukFtyis_ZNHczZg4gANRUPWkD7HAUTU,6
40
- psyke-0.8.2.dev72.dist-info/RECORD,,
36
+ psyke-0.8.4.dist-info/LICENSE,sha256=KP9K6Hgezf_xdMFW7ORyKz9uA8Y8k52YJn292wcP-_E,11354
37
+ psyke-0.8.4.dist-info/METADATA,sha256=EVJVC6zctQogYTfCiBn1qkTar-2J7X7YiSfHom0TK0U,8102
38
+ psyke-0.8.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
39
+ psyke-0.8.4.dist-info/top_level.txt,sha256=q1HglxOqqoIRukFtyis_ZNHczZg4gANRUPWkD7HAUTU,6
40
+ psyke-0.8.4.dist-info/RECORD,,