sawnergy 1.0.7__py3-none-any.whl → 1.0.8__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 sawnergy might be problematic. Click here for more details.

@@ -23,7 +23,7 @@ _logger = logging.getLogger(__name__)
23
23
  # HELPERS
24
24
  # *----------------------------------------------------*
25
25
 
26
- def _safe_svd_pca(X: np.ndarray, k: int) -> tuple[np.ndarray, np.ndarray]:
26
+ def _safe_svd_pca(X: np.ndarray, k: int, *, row_l2: bool = False) -> tuple[np.ndarray, np.ndarray]:
27
27
  """Compute k principal directions via SVD and project onto them."""
28
28
  if X.ndim != 2:
29
29
  raise ValueError(f"PCA expects 2D array (N, D); got {X.shape}")
@@ -33,6 +33,9 @@ def _safe_svd_pca(X: np.ndarray, k: int) -> tuple[np.ndarray, np.ndarray]:
33
33
  if D < k:
34
34
  raise ValueError(f"Requested k={k} exceeds feature dim D={D}")
35
35
  Xc = X - X.mean(axis=0, keepdims=True)
36
+ if row_l2:
37
+ norms = np.linalg.norm(Xc, axis=1, keepdims=True)
38
+ Xc = Xc / np.clip(norms, 1e-9, None)
36
39
  _, _, Vt = np.linalg.svd(Xc, full_matrices=False)
37
40
  comps = Vt[:k].copy()
38
41
  proj = Xc @ comps.T
@@ -78,7 +81,8 @@ class Visualizer:
78
81
  init_elev: float = 35,
79
82
  init_azim: float = 45,
80
83
  *,
81
- show: bool = False
84
+ show: bool = False,
85
+ normalize_rows: bool = False,
82
86
  ) -> None:
83
87
  # Backend & pyplot
84
88
  visualizer_util.ensure_backend(show)
@@ -106,7 +110,7 @@ class Visualizer:
106
110
  self._residue_norm = mpl.colors.Normalize(0, max(1, self.N - 1))
107
111
 
108
112
  # Figure / axes / artists
109
- self._fig = self._plt.figure(figsize=figsize)
113
+ self._fig = self._plt.figure(figsize=figsize, num="SAWNERGY")
110
114
  self._ax = None
111
115
  self._scatter = None
112
116
  self._marker_size = 30.0
@@ -115,6 +119,7 @@ class Visualizer:
115
119
  self.default_node_color = default_node_color
116
120
  self._antialiased = bool(antialiased)
117
121
  self._depthshade = bool(depthshade)
122
+ self._normalize_rows = bool(normalize_rows)
118
123
 
119
124
  # ------------------------------ PRIVATE ------------------------------ #
120
125
 
@@ -143,7 +148,7 @@ class Visualizer:
143
148
  so that the returned array still has shape (N, 3).
144
149
  """
145
150
  k = 3 if X.shape[1] >= 3 else 2
146
- P, _ = _safe_svd_pca(X, k)
151
+ P, _ = _safe_svd_pca(X, k, row_l2=self._normalize_rows)
147
152
  if k == 2:
148
153
  P = np.c_[P, np.zeros((P.shape[0], 1), dtype=P.dtype)]
149
154
  return P
@@ -214,7 +219,6 @@ class Visualizer:
214
219
  for p, nid in zip(P, idx + 1):
215
220
  self._labels.append(self._ax.text(p[0], p[1], p[2], str(int(nid)), fontsize=8))
216
221
 
217
- # Be friendly to test dummies (they may lack tight_layout/canvas)
218
222
  try:
219
223
  self._fig.tight_layout()
220
224
  except Exception:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sawnergy
3
- Version: 1.0.7
3
+ Version: 1.0.8
4
4
  Summary: Toolkit for transforming molecular dynamics (MD) trajectories into rich graph representations
5
5
  Home-page: https://github.com/Yehor-Mishchyriak/SAWNERGY
6
6
  Author: Yehor Mishchyriak
@@ -52,19 +52,31 @@ keeps the full workflow — from `cpptraj` output to skip-gram embeddings (node2
52
52
 
53
53
  > **Optional:** For GPU training, install PyTorch separately (e.g., `pip install torch`).
54
54
  > **Note:** RIN building requires `cpptraj` (AmberTools). Ensure it is discoverable via `$PATH` or the `CPPTRAJ`
55
- > environment variable. Probably the easiest solution: install AmberTools via conda, activate the environment, and SAWNERGY will find cpptraj executable on its own, so just run your code and don't worry about it.
55
+ > environment variable. Probably the easiest solution: install AmberTools via Conda, activate the environment, and SAWNERGY will find the cpptraj executable on its own, so just run your code and don't worry about it.
56
56
 
57
57
  ---
58
58
 
59
59
  # UPDATES:
60
60
 
61
+ ## v1.0.8 — What’s new:
62
+ - **Temporary deprecation of `SGNS_Torch`**
63
+ - `sawnergy.embedding.SGNS_Torch` currently produces noisy embeddings in practice. The issue likely stems from **weight initialization**, although the root cause has not yet been conclusively determined.
64
+ - **Action:** The class and its `__init__` docstring now carry a deprecation notice. Constructing the class emits a **`DeprecationWarning`** and logs a **warning**.
65
+ - **Use instead:** Prefer **`SG_Torch`** (plain Skip-Gram with full softmax) or the PureML backends **`SGNS_PureML`** / **`SG_PureML`**.
66
+ - **Compatibility:** No breaking API changes; imports remain stable. PureML backends are unaffected.
67
+ - **Embedding visualizer update**
68
+ - Now you can L2 normalize your embeddings before display.
69
+ - **Small improvements in the embedding module**
70
+ - Improved API with a lot of good defaults in place to ease usage out of the box.
71
+ - Small internal model tweaks.
72
+
61
73
  ## v1.0.7 — What’s new:
62
- - **Added plain SkipGram model**
74
+ - **Added plain Skip-Gram model**
63
75
  - Now, the user can choose if they want to apply the negative sampling technique (two binary classifiers) or train a single classifier over the vocabulary (full softmax). For more detail, see: [node2vec](https://arxiv.org/pdf/1607.00653), [word2vec](https://arxiv.org/pdf/1301.3781), and [negative_sampling](https://arxiv.org/pdf/1402.3722).
64
76
  - **Set a harsher default for low interaction energies pruning during RIN construction**
65
77
  - Now we zero out 85% of the lowest interaction energies as opposed to the past 30% default, leading to more meaningful embeddings.
66
78
  - **BUG FIX: Visualizer**
67
- - Previously, the visualizer would silently draw edges of 0 magnitude, meaning they were actually being drawn but were invisible due to full transparency and 0 width. As a result, the displayed image / animation would be very laggy. Now, this was fixed, and given high pruning default, the displayed interaction networks are clean and smooth under rotations, dragging, etc.
79
+ - Previously, the visualizer would silently draw edges of 0 magnitude, meaning they were actually being drawn but were invisible due to full transparency and 0 width. As a result, the displayed image/animation would be very laggy. Now, this was fixed, and given the higher pruning default, the displayed interaction networks are clean and smooth under rotations, dragging, etc.
68
80
  - **New Embedding Visualizer (3D)**
69
81
  - New lightweight viewer for per-frame embeddings that projects embeddings with PCA to a **3D** scatter. Supports the same node coloring semantics, optional node labels, and the same antialiasing/depthshade controls. Works in headless setups using the same backend guard and uses a blocking `show=True` for scripts.
70
82
 
@@ -77,7 +89,7 @@ keeps the full workflow — from `cpptraj` output to skip-gram embeddings (node2
77
89
  - **Deterministic, shareable artifacts**: Every stage produces compressed Zarr archives that contain both data and metadata so runs can be reproduced, shared, or inspected later.
78
90
  - **High-performance data handling**: Heavy arrays live in shared memory during walk sampling to allow parallel processing without serialization overhead; archives are written in chunked, compressed form for fast read/write.
79
91
  - **Flexible objectives & backends**: Train Skip-Gram with **negative sampling** (`objective="sgns"`) or **plain Skip-Gram** (`objective="sg"`), using either **PureML** (default) or **PyTorch**.
80
- - **Visualization out of the box**: Plot and animate residue networks without leaving Python, using the data produced by RINBuilder
92
+ - **Visualization out of the box**: Plot and animate residue networks without leaving Python, using the data produced by RINBuilder.
81
93
 
82
94
  ---
83
95
 
@@ -117,7 +129,7 @@ node indexing, and RNG seeds stay consistent across the toolchain.
117
129
  * Wraps the AmberTools `cpptraj` executable to:
118
130
  - compute per-frame electrostatic (EMAP) and van der Waals (VMAP) energy matrices at the atomic level,
119
131
  - project atom–atom interactions to residue–residue interactions using compositional masks,
120
- - prune, symmetrize, remove self-interactions, and L1-normalise the matrices,
132
+ - prune, symmetrize, remove self-interactions, and L1-normalize the matrices,
121
133
  - compute per-residue centers of mass (COM) over the same frames.
122
134
  * Outputs a compressed Zarr archive with transition matrices, optional pre-normalized energies, COM snapshots, and rich
123
135
  metadata (frame range, pruning quantile, molecule ID, etc.).
@@ -142,13 +154,10 @@ node indexing, and RNG seeds stay consistent across the toolchain.
142
154
 
143
155
  ### `sawnergy.embedding.Embedder`
144
156
 
145
- * Consumes walk archives, generates skip-gram pairs, and normalises them to 0-based indices.
146
- * Provides a unified interface to SGNS implementations:
147
- - **PureML backend** (`SGNS_PureML`): works with the `pureml` ecosystem, optimistic for CPU training.
148
- - **PyTorch backend** (`SGNS_Torch`): uses `torch.nn.Embedding` plays nicely with GPUs.
149
- * Both `SGNS_PureML` and `SGNS_Torch` accept training hyperparameters such as batch_size, LR, optimizer and LR_scheduler, etc.
150
- * Exposes `embed_frame` (single frame) and `embed_all` (all frames, deterministic seeding per frame) which return the
151
- learned input embedding matrices and write them to disk when requested.
157
+ * Consumes walk archives, generates skip-gram pairs, and normalizes them to 0-based indices.
158
+ * Selects skip-gram (SG / SGNS) backends dynamically via `model_base="pureml"|"torch"` with per-backend overrides supplied through `model_kwargs`.
159
+ * Handles deterministic per-frame seeding and returns the requested embedding `kind` (`"in"`, `"out"`, or `"avg"`) from `embed_frame` and `embed_all`.
160
+ * Persists per-frame matrices with rich provenance (walk metadata, objective, hyperparameters, RNG seeds) when `embed_all` targets an output archive.
152
161
 
153
162
  ### Supporting Utilities
154
163
 
@@ -166,11 +175,11 @@ node indexing, and RNG seeds stay consistent across the toolchain.
166
175
  |---|---|---|
167
176
  | **RIN** | `ATTRACTIVE_transitions` → **(T, N, N)**, float32 • `REPULSIVE_transitions` → **(T, N, N)**, float32 (optional) • `ATTRACTIVE_energies` → **(T, N, N)**, float32 (optional) • `REPULSIVE_energies` → **(T, N, N)**, float32 (optional) • `COM` → **(T, N, 3)**, float32 | `time_created` (ISO) • `com_name` = `"COM"` • `molecule_of_interest` (int) • `frame_range` = `(start, end)` inclusive • `frame_batch_size` (int) • `prune_low_energies_frac` (float in [0,1]) • `attractive_transitions_name` / `repulsive_transitions_name` (dataset names or `None`) • `attractive_energies_name` / `repulsive_energies_name` (dataset names or `None`) |
168
177
  | **Walks** | `ATTRACTIVE_RWs` → **(T, N·num_RWs, L+1)**, int32 (optional) • `REPULSIVE_RWs` → **(T, N·num_RWs, L+1)**, int32 (optional) • `ATTRACTIVE_SAWs` → **(T, N·num_SAWs, L+1)**, int32 (optional) • `REPULSIVE_SAWs` → **(T, N·num_SAWs, L+1)**, int32 (optional) <br/>_Note:_ node IDs are **1-based**.| `time_created` (ISO) • `seed` (int) • `rng_scheme` = `"SeedSequence.spawn_per_batch_v1"` • `num_workers` (int) • `in_parallel` (bool) • `batch_size_nodes` (int) • `num_RWs` / `num_SAWs` (ints) • `node_count` (N) • `time_stamp_count` (T) • `walk_length` (L) • `walks_per_node` (int) • `attractive_RWs_name` / `repulsive_RWs_name` / `attractive_SAWs_name` / `repulsive_SAWs_name` (dataset names or `None`) • `walks_layout` = `"time_leading_3d"` |
169
- | **Embeddings** | `FRAME_EMBEDDINGS` → **(frames_written, vocab_size, D)**, typically float32 | `time_created` (ISO) • `seed` (int) • `rng_scheme` = `"SeedSequence.spawn_per_frame_v1"` • `source_walks_path` (str) • `model_base` = `"torch"` or `"pureml"` • `rin_type` = `"attr"` or `"repuls"` • `using_mode` = `"RW"|"SAW"|"merged"` • `window_size` (int) • `alpha` (float; noise exponent) • `dimensionality` = D • `num_negative_samples` (int) • `num_epochs` (int) • `batch_size` (int) • `shuffle_data` (bool) • `frames_written` (int) • `vocab_size` (int) • `frame_count` (int) • `embedding_dtype` (str) • `frame_embeddings_name` = `"FRAME_EMBEDDINGS"` • `arrays_per_chunk` (int) • `compression_level` (int) • `objective` = `"sgns"` or `"sg"` |
178
+ | **Embeddings** | `FRAME_EMBEDDINGS` → **(T, N, D)**, float32 | `created_at` (ISO) • `frame_embeddings_name` = `"FRAME_EMBEDDINGS"` • `time_stamp_count` = T • `node_count` = N • `embedding_dim` = D • `model_base` = `"torch"` or `"pureml"` • `embedding_kind` = `"in"|"out"|"avg"` • `objective` = `"sgns"` or `"sg"` • `negative_sampling` (bool) • `num_negative_samples` (int) • `num_epochs` (int) • `batch_size` (int) • `window_size` (int) • `alpha` (float) • `lr_step_per_batch` (bool) • `shuffle_data` (bool) • `device_hint` (str) • `model_kwargs_repr` (repr string) • `RIN_type` = `"attr"` or `"repuls"` • `using` = `"RW"|"SAW"|"merged"` • `source_WALKS_path` (str) • `walk_length` (int) • `num_RWs` / `num_SAWs` (ints) • `attractive_*_name` / `repulsive_*_name` (dataset names or `None`) • `master_seed` (int) • `per_frame_seeds` (list[int]) • `arrays_per_chunk` (int) • `compression_level` (int) |
170
179
 
171
180
  **Notes**
172
181
 
173
- - In **RIN**, `T` equals the number of frame **batches** written (i.e., `frame_range` swept in steps of `frame_batch_size`). `ATTRACTIVE/REPULSIVE_energies` are **pre-normalised** absolute energies (written only when `keep_prenormalized_energies=True`), whereas `ATTRACTIVE/REPULSIVE_transitions` are the **row-wise L1-normalised** versions used for sampling.
182
+ - In **RIN**, `T` equals the number of frame **batches** written (i.e., `frame_range` swept in steps of `frame_batch_size`). `ATTRACTIVE/REPULSIVE_energies` are **pre-normalized** absolute energies (written only when `keep_prenormalized_energies=True`), whereas `ATTRACTIVE/REPULSIVE_transitions` are the **row-wise L1-normalized** versions used for sampling.
174
183
  - All archives are Zarr v3 groups. ArrayStorage also maintains per-block metadata in root attrs: `array_chunk_size_in_block`, `array_shape_in_block`, and `array_dtype_in_block` (dicts keyed by dataset name). You’ll see these in every archive.
175
184
  - In **Embeddings**, `alpha` and `num_negative_samples` apply to **SGNS** only and are ignored for `objective="sg"`.
176
185
 
@@ -200,7 +209,7 @@ rin_builder.build_rin(
200
209
  prune_low_energies_frac=0.85,
201
210
  output_path=rin_path,
202
211
  include_attractive=True,
203
- include_repulsive=False,
212
+ include_repulsive=False
204
213
  )
205
214
 
206
215
  # 2. Sample walks from the RIN
@@ -208,44 +217,34 @@ walker = Walker(rin_path, seed=123)
208
217
  walks_path = Path("./WALKS_demo.zip")
209
218
  walker.sample_walks(
210
219
  walk_length=16,
211
- walks_per_node=32,
220
+ walks_per_node=100,
212
221
  saw_frac=0.25,
213
222
  include_attractive=True,
214
223
  include_repulsive=False,
215
224
  time_aware=False,
216
225
  output_path=walks_path,
217
- in_parallel=False,
226
+ in_parallel=False
218
227
  )
219
228
  walker.close()
220
229
 
221
230
  # 3. Train embeddings per frame (PyTorch backend)
222
231
  import torch
223
232
 
224
- embedder = Embedder(walks_path, base="torch", seed=999)
233
+ embedder = Embedder(walks_path, seed=999)
225
234
  embeddings_path = embedder.embed_all(
226
235
  RIN_type="attr",
227
236
  using="merged",
237
+ num_epochs=10,
238
+ negative_sampling=False,
228
239
  window_size=4,
229
- objective="sgns",
230
- num_negative_samples=5,
231
- num_epochs=5,
232
- batch_size=1024,
233
- dimensionality=128,
234
- shuffle_data=True,
235
- output_path="./EMBEDDINGS_demo.zip",
236
- sgns_kwargs={
237
- "optim": torch.optim.Adam,
238
- "optim_kwargs": {"lr": 1e-3},
239
- "lr_sched": torch.optim.lr_scheduler.LambdaLR,
240
- "lr_sched_kwargs": {"lr_lambda": lambda _: 1.0},
241
- "device": "cuda" if torch.cuda.is_available() else "cpu",
242
- },
240
+ device="cuda" if torch.cuda.is_available() else "cpu",
241
+ model_base="torch",
242
+ output_path="./EMBEDDINGS_demo.zip"
243
243
  )
244
244
  print("Embeddings written to", embeddings_path)
245
245
  ```
246
246
 
247
- > For the PureML backend, supply the relevant optimiser and scheduler via `sgns_kwargs`
248
- > (for example `optim=pureml.optimizers.Adam`, `lr_sched=pureml.optimizers.CosineAnnealingLR`).
247
+ > For the PureML backend, set `model_base="pureml"` and pass the optimizer / scheduler classes inside `model_kwargs`.
249
248
 
250
249
  ---
251
250
 
@@ -270,7 +269,7 @@ v.build_frame(1,
270
269
  ```python
271
270
  from sawnergy.embedding import Visualizer
272
271
 
273
- viz = sawnergy.embedding.Visualizer("./EMBEDDINGS_demo.zip")
272
+ viz = Visualizer("./EMBEDDINGS_demo.zip", normalize_rows=True)
274
273
  viz.build_frame(1, show=True)
275
274
  ```
276
275
 
@@ -280,8 +279,7 @@ viz.build_frame(1, show=True)
280
279
 
281
280
  - **Time-aware walks**: Set `time_aware=True`, provide `stickiness` and `on_no_options` when calling `Walker.sample_walks`.
282
281
  - **Shared memory lifecycle**: Call `Walker.close()` (or use a context manager) to release shared-memory segments.
283
- - **PureML vs PyTorch**: Choose the backend via `Embedder(..., base="pureml"|"torch")` and provide backend-specific
284
- constructor kwargs through `sgns_kwargs` (optimizer, scheduler, device).
282
+ - **PureML vs PyTorch**: Select the backend at call time with `model_base="pureml"|"torch"` (defaults to `"pureml"`) and pass optimizer / scheduler overrides through `model_kwargs`.
285
283
  - **ArrayStorage utilities**: Use `ArrayStorage` directly to peek into archives, append arrays, or manage metadata.
286
284
 
287
285
  ---
@@ -292,8 +290,9 @@ viz.build_frame(1, show=True)
292
290
  ├── sawnergy/
293
291
  │ ├── rin/ # RINBuilder and cpptraj integration helpers
294
292
  │ ├── walks/ # Walker class and shared-memory utilities
295
- │ ├── embedding/ # Embedder + SGNS backends (PureML / PyTorch)
293
+ │ ├── embedding/ # Embedder + SG/SGNS backends (PureML / PyTorch)
296
294
  │ ├── visual/ # Visualizer and palette utilities
295
+ │ │
297
296
  │ ├── logging_util.py
298
297
  │ └── sawnergy_util.py
299
298
 
@@ -302,7 +301,7 @@ viz.build_frame(1, show=True)
302
301
 
303
302
  ---
304
303
 
305
- ## Acknowledgements
304
+ ## Acknowledgments
306
305
 
307
306
  SAWNERGY builds on the AmberTools `cpptraj` ecosystem, NumPy, Matplotlib, Zarr, and PyTorch (for GPU acceleration if necessary; PureML is available by default).
308
307
  Big thanks to the upstream communities whose work makes this toolkit possible.
@@ -1,11 +1,11 @@
1
1
  sawnergy/__init__.py,sha256=Dq1U38ah6nPRFEDKN41mYphcTynKfnItca6QkYkpSbs,248
2
2
  sawnergy/logging_util.py,sha256=mfYw8IsYtOfCXayjkd4g9jHuupluxRNbqyFegRkiAhQ,1476
3
3
  sawnergy/sawnergy_util.py,sha256=Htx9wr0S8TXt5aHT2mtEdYf1TCo_BC1IUwNNuZdIR-4,49432
4
- sawnergy/embedding/SGNS_pml.py,sha256=LfZDlIF3-KnWUAjhwOT5ggGl2OoReM8_L0TCVYs6GJ0,14299
5
- sawnergy/embedding/SGNS_torch.py,sha256=NIr-RlOmXlEPe3m8Z6XvuG0b8MGTidETfugigcQTwFs,11232
4
+ sawnergy/embedding/SGNS_pml.py,sha256=-S7K7qwbDGUO_KW4gnA3dGyxuezN1ZK-WikPm7krEvs,14291
5
+ sawnergy/embedding/SGNS_torch.py,sha256=NgVQnMtRSYY0IsPhB3XV7K1-uVSah0P77a8ID8zZ7Qw,13940
6
6
  sawnergy/embedding/__init__.py,sha256=T1YXb7S5Zyy_kIqlarDSX3imd_FGFH6nDuvLQ3hMKsE,1764
7
- sawnergy/embedding/embedder.py,sha256=K9I6HYYQFH7SHpgxeTCf8_MMvyLxVaAaltoMwJbgyqo,28749
8
- sawnergy/embedding/visualizer.py,sha256=bweituYNj5dOzFhvU4n_E-RbzZiUKw6bfJchFLfjFD4,8625
7
+ sawnergy/embedding/embedder.py,sha256=02pcf3ies3Nuo19sCoJdMAYg7BFUHj4-wf4AZ5R6PAE,32492
8
+ sawnergy/embedding/visualizer.py,sha256=x0BiSG9_nk9AUQm9RsZ2syKeCiaxX1gTlC85aYycMXY,8830
9
9
  sawnergy/rin/__init__.py,sha256=z19hLfEIp3bwzY-eCHQBQf0NRTCJzVz_FLIpVV5q0W4,162
10
10
  sawnergy/rin/rin_builder.py,sha256=d1cC4KKY9zzNlqhxHWTFM-QyXRXubd2zlCrSM-dV5pc,44624
11
11
  sawnergy/rin/rin_util.py,sha256=5TKywA5qfm76Gl4Cyz7oBPasmE5chclR7UM4hawwQOg,14939
@@ -15,9 +15,9 @@ sawnergy/visual/visualizer_util.py,sha256=7y3kWjHxDQMoG0dmimceHKTC5veVChoyvW7d0q
15
15
  sawnergy/walks/__init__.py,sha256=Z_Kaffhn3oUX13z9jbY0V5Ncdwj9Cnr--n9D-s7gh5k,250
16
16
  sawnergy/walks/walker.py,sha256=scvfZFrSL4AwpmspD0Jb0uhnrVIRRwE_hPCE3bG6zpg,37729
17
17
  sawnergy/walks/walker_util.py,sha256=ETdyPNIDwDQCA8Z5t38keBhYBJ56_ksT_0NhOCY-tHE,15361
18
- sawnergy-1.0.7.dist-info/licenses/LICENSE,sha256=cElK4bCsDhyAEON3H05s35bQZvxBcXBiCOrOdiUhDCY,11346
19
- sawnergy-1.0.7.dist-info/licenses/NOTICE,sha256=eVTbuSasZrmMJVtKoWOzsKyu4ZNm7Ks7dzI3Tx5tEHc,109
20
- sawnergy-1.0.7.dist-info/METADATA,sha256=x0PQa0JilbayBcgywmnCL8IZZwTylzz8gOGnvwJHeDc,15433
21
- sawnergy-1.0.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
- sawnergy-1.0.7.dist-info/top_level.txt,sha256=-67FQD6FD9Gjt74WTmO9hNYA3MLB4HaSxci0sEKC5Lo,9
23
- sawnergy-1.0.7.dist-info/RECORD,,
18
+ sawnergy-1.0.8.dist-info/licenses/LICENSE,sha256=cElK4bCsDhyAEON3H05s35bQZvxBcXBiCOrOdiUhDCY,11346
19
+ sawnergy-1.0.8.dist-info/licenses/NOTICE,sha256=eVTbuSasZrmMJVtKoWOzsKyu4ZNm7Ks7dzI3Tx5tEHc,109
20
+ sawnergy-1.0.8.dist-info/METADATA,sha256=_0u1smFM5oMqaO0xuc4ZX094B6F2swQqUrolOkpikVM,16084
21
+ sawnergy-1.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
22
+ sawnergy-1.0.8.dist-info/top_level.txt,sha256=-67FQD6FD9Gjt74WTmO9hNYA3MLB4HaSxci0sEKC5Lo,9
23
+ sawnergy-1.0.8.dist-info/RECORD,,