hyperglyph-codec 0.1.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.
@@ -0,0 +1,627 @@
1
+ Metadata-Version: 2.4
2
+ Name: hyperglyph-codec
3
+ Version: 0.1.0
4
+ Summary: Hyperdimensional symbolic residual compression for neural network weights
5
+ Author: Robert McMenemy
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: compression,hyperdimensional-computing,machine-learning,model-compression,neural-networks,pytorch,quantization,research
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: numpy>=1.24
21
+ Provides-Extra: dev
22
+ Requires-Dist: build; extra == 'dev'
23
+ Requires-Dist: mypy; extra == 'dev'
24
+ Requires-Dist: pre-commit; extra == 'dev'
25
+ Requires-Dist: pytest; extra == 'dev'
26
+ Requires-Dist: pytest-cov; extra == 'dev'
27
+ Requires-Dist: ruff; extra == 'dev'
28
+ Requires-Dist: twine; extra == 'dev'
29
+ Provides-Extra: docs
30
+ Requires-Dist: mkdocs; extra == 'docs'
31
+ Requires-Dist: mkdocs-material; extra == 'docs'
32
+ Provides-Extra: torch
33
+ Requires-Dist: torch; extra == 'torch'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # Hyper Glyph
37
+
38
+ <p align="center">
39
+ Hyperdimensional symbolic residual compression for neural network weights.
40
+ </p>
41
+
42
+ <p align="center">
43
+ <img width="256" height="256" alt="Hyper Glyph Logo" src="https://github.com/Arkay92/Hyper-Glyph/raw/main/hyperglyph.png" />
44
+ </p>
45
+
46
+ <p align="center">
47
+ <a href="https://pypi.org/project/hyperglyph-codec/"><img alt="PyPI" src="https://img.shields.io/pypi/v/hyperglyph-codec.svg" /></a>
48
+ <img alt="Python" src="https://img.shields.io/pypi/pyversions/hyperglyph-codec.svg" />
49
+ <img alt="License" src="https://img.shields.io/pypi/l/hyperglyph-codec.svg" />
50
+ <img alt="Status" src="https://img.shields.io/badge/status-alpha-orange.svg" />
51
+ </p>
52
+
53
+ > **Package name:** `hyperglyph-codec`
54
+ > **Project name:** Hyper Glyph
55
+ > **Install:** `pip install hyperglyph-codec`
56
+ > **Import:** `from hyperglyph import HyperGlyphCodec`
57
+
58
+ **Hyper Glyph** combines:
59
+
60
+ - **Block-level tensor compression** for NumPy arrays and neural network weights.
61
+ - **Symbolic prototype assignment** to represent repeated weight patterns compactly.
62
+ - **Sparse residual repair** to preserve reconstruction fidelity after prototype decoding.
63
+ - **Configurable compression controls** for block size, prototype count, residual size, and tensor filtering.
64
+ - **State dict compression** for model-like parameter dictionaries.
65
+ - **Optional PyTorch support** for loading, compressing, restoring, and benchmarking `.pt` state dicts.
66
+ - **`.hwz` serialization** for saving compressed models as portable archives.
67
+ - **Compression reports** with size ratio, tensor counts, and reconstruction error metrics.
68
+ - **A small CLI** for compressing, decompressing, inspecting, and benchmarking model archives.
69
+ - **Typed Python API** designed for research, experimentation, and extension.
70
+
71
+ ---
72
+
73
+ ## Before / After
74
+
75
+ ```python
76
+ import numpy as np
77
+
78
+ from hyperglyph import HyperGlyphCodec, HyperGlyphConfig
79
+
80
+ state_dict = {
81
+ "layer.weight": np.arange(1024, dtype=np.float32).reshape(32, 32),
82
+ }
83
+
84
+ config = HyperGlyphConfig(
85
+ block_size=16,
86
+ n_prototypes=32,
87
+ residual_k=4,
88
+ )
89
+
90
+ codec = HyperGlyphCodec(config)
91
+ compressed = codec.compress_state_dict(state_dict)
92
+ restored = codec.decompress_state_dict(compressed)
93
+ report = codec.report(compressed, state_dict, restored)
94
+
95
+ print(report)
96
+ ```
97
+
98
+ Example output:
99
+
100
+ ```text
101
+ CompressionReport(
102
+ original_bytes=4096,
103
+ compressed_bytes=...,
104
+ compression_ratio=...,
105
+ tensors_compressed=1,
106
+ tensors_skipped=0,
107
+ total_mse=...,
108
+ total_mae=...,
109
+ max_abs_error=...
110
+ )
111
+ ```
112
+
113
+ That is the core job: encode large weight tensors as reusable symbolic
114
+ prototypes plus a small residual correction, then report the size and
115
+ reconstruction tradeoff.
116
+
117
+ Hyper Glyph v0.1.0 is an experimental research codec. It is intended for
118
+ testing ideas around hyperdimensional and symbolic weight compression rather
119
+ than guaranteed production compression.
120
+
121
+ ---
122
+
123
+ ## Why Hyperdimensional Weight Compression?
124
+
125
+ Neural network weights often contain repeated local structure, approximate
126
+ patterns, and redundancy that can be represented more compactly than raw
127
+ floating-point tensors:
128
+
129
+ ```text
130
+ Weight tensors
131
+ -> Split into fixed-size blocks
132
+ -> Learn reusable prototype blocks
133
+ -> Assign each block to a prototype
134
+ -> Store per-block scales
135
+ -> Store sparse top-k residual corrections
136
+
137
+ -> Save compressed archive
138
+ -> Restore approximate tensors
139
+ -> Report compression and reconstruction metrics
140
+ ```
141
+
142
+ `Hyper Glyph` is designed for experiments where you want to inspect that
143
+ tradeoff directly:
144
+
145
+ - **Large weight matrices** that can be split into repeated local blocks.
146
+ - **Prototype-based compression** where blocks share learned representatives.
147
+ - **Sparse residual repair** where only the largest reconstruction corrections are stored.
148
+ - **Approximate reconstruction** with measurable MSE, MAE, and max absolute error.
149
+ - **State dict workflows** that match common PyTorch model storage patterns.
150
+ - **Portable archive output** for saving and inspecting compressed runs.
151
+
152
+ ---
153
+
154
+ ## Why Not Just Quantization?
155
+
156
+ Quantization changes the numeric precision of individual weights. Hyper Glyph
157
+ uses a different representation: each tensor block is mapped to a learned
158
+ prototype, scaled, and repaired with a sparse residual.
159
+
160
+ That makes Hyper Glyph useful for experimenting with symbolic and
161
+ hyperdimensional compression ideas, not as a drop-in replacement for mature
162
+ quantization, pruning, or production model compression toolchains.
163
+
164
+ ---
165
+
166
+ ## Architecture
167
+
168
+ ```text
169
+ Input weights
170
+ - NumPy arrays
171
+ - PyTorch state_dict values
172
+ |
173
+ v
174
+ Compression
175
+ - tensor filtering
176
+ - block splitting
177
+ - prototype learning
178
+ - prototype assignment
179
+ - scale calculation
180
+ - sparse residual encoding
181
+ |
182
+ v
183
+ CompressedModel
184
+ - compressed tensors
185
+ - shapes
186
+ - prototype ids
187
+ - scales
188
+ - residuals
189
+ - prototype matrices
190
+ - codec metadata
191
+ |
192
+ v
193
+ Decompression and report
194
+ - reconstructed tensors
195
+ - original/compressed byte estimates
196
+ - compression ratio
197
+ - MSE / MAE / max error
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Install
203
+
204
+ ```bash
205
+ pip install hyperglyph-codec
206
+ ```
207
+
208
+ For PyTorch state dict support:
209
+
210
+ ```bash
211
+ pip install "hyperglyph-codec[torch]"
212
+ ```
213
+
214
+ For documentation dependencies:
215
+
216
+ ```bash
217
+ pip install "hyperglyph-codec[docs]"
218
+ ```
219
+
220
+ For development:
221
+
222
+ ```bash
223
+ pip install -e ".[dev,torch,docs]"
224
+ pytest
225
+ python -m build
226
+ ```
227
+
228
+ ---
229
+
230
+ ## Quick Start
231
+
232
+ ### Compress a NumPy State Dict
233
+
234
+ ```python
235
+ import numpy as np
236
+
237
+ from hyperglyph import HyperGlyphCodec, HyperGlyphConfig
238
+
239
+ state_dict = {
240
+ "weight": np.arange(256, dtype=np.float32).reshape(16, 16),
241
+ }
242
+
243
+ config = HyperGlyphConfig(
244
+ block_size=8,
245
+ n_prototypes=8,
246
+ residual_k=2,
247
+ )
248
+
249
+ codec = HyperGlyphCodec(config)
250
+ compressed = codec.compress_state_dict(state_dict)
251
+ restored = codec.decompress_state_dict(compressed)
252
+
253
+ print(restored["weight"].shape)
254
+ ```
255
+
256
+ ### Compress a PyTorch Model
257
+
258
+ ```python
259
+ import torch
260
+
261
+ from hyperglyph import HyperGlyphCodec, HyperGlyphConfig
262
+
263
+ model = torch.nn.Sequential(
264
+ torch.nn.Linear(32, 32),
265
+ torch.nn.ReLU(),
266
+ torch.nn.Linear(32, 8),
267
+ )
268
+
269
+ config = HyperGlyphConfig(
270
+ block_size=16,
271
+ n_prototypes=16,
272
+ residual_k=4,
273
+ )
274
+
275
+ codec = HyperGlyphCodec(config)
276
+ compressed = codec.compress_state_dict(model.state_dict())
277
+ restored = codec.decompress_state_dict(compressed)
278
+
279
+ print(f"Compressed tensors: {len(compressed.tensors)}")
280
+ print(f"Restored keys: {sorted(restored)}")
281
+ ```
282
+
283
+ ### Save and Load `.hwz` Archives
284
+
285
+ ```python
286
+ from hyperglyph import load_compressed, save_compressed
287
+
288
+ save_compressed(compressed, "model.hwz")
289
+ loaded = load_compressed("model.hwz")
290
+ restored = codec.decompress_state_dict(loaded)
291
+ ```
292
+
293
+ ### Generate a Report
294
+
295
+ ```python
296
+ report = codec.report(
297
+ compressed_model=compressed,
298
+ original_state_dict=state_dict,
299
+ restored_state_dict=restored,
300
+ )
301
+
302
+ print(report.compression_ratio)
303
+ print(report.total_mse)
304
+ print(report.max_abs_error)
305
+ ```
306
+
307
+ ---
308
+
309
+ ## CLI
310
+
311
+ Compress a PyTorch state dict into a `.hwz` archive:
312
+
313
+ ```bash
314
+ hyperglyph compress model.pt model.hwz
315
+ ```
316
+
317
+ Tune compression settings:
318
+
319
+ ```bash
320
+ hyperglyph compress model.pt model.hwz \
321
+ --block-size 16 \
322
+ --hdc-dim 4096 \
323
+ --n-buckets 16 \
324
+ --n-prototypes 128 \
325
+ --residual-k 8 \
326
+ --min-tensor-size 256
327
+ ```
328
+
329
+ Restore a compressed archive back to a PyTorch state dict:
330
+
331
+ ```bash
332
+ hyperglyph decompress model.hwz restored.pt
333
+ ```
334
+
335
+ Inspect archive metadata:
336
+
337
+ ```bash
338
+ hyperglyph inspect model.hwz
339
+ ```
340
+
341
+ Benchmark compression and reconstruction:
342
+
343
+ ```bash
344
+ hyperglyph benchmark model.pt
345
+ ```
346
+
347
+ ---
348
+
349
+ ## Benchmark Example
350
+
351
+ A small practical benchmark is enough to see the current codec behavior:
352
+
353
+ ```bash
354
+ hyperglyph benchmark model.pt
355
+ ```
356
+
357
+ Example report fields:
358
+
359
+ ```text
360
+ original_bytes
361
+ compressed_bytes
362
+ compression_ratio
363
+ tensors_compressed
364
+ tensors_skipped
365
+ total_mse
366
+ total_mae
367
+ max_abs_error
368
+ ```
369
+
370
+ The current package focuses on transparent compression experiments rather than
371
+ claiming universal size reductions. Compare the restored model against your own
372
+ accuracy, latency, and reconstruction thresholds.
373
+
374
+ ---
375
+
376
+ ## Main Features
377
+
378
+ ### 1. **Configurable Codec**
379
+
380
+ Tune the compression shape with one dataclass:
381
+
382
+ ```python
383
+ from hyperglyph import HyperGlyphConfig
384
+
385
+ config = HyperGlyphConfig(
386
+ hdc_dim=4096,
387
+ block_size=16,
388
+ n_buckets=16,
389
+ n_prototypes=128,
390
+ residual_k=8,
391
+ seed=42,
392
+ min_tensor_size=256,
393
+ compress_bias=False,
394
+ )
395
+ ```
396
+
397
+ ### 2. **Array Compression**
398
+
399
+ Compress and reconstruct a single NumPy array:
400
+
401
+ ```python
402
+ import numpy as np
403
+
404
+ from hyperglyph import HyperGlyphCodec
405
+
406
+ codec = HyperGlyphCodec()
407
+ array = np.random.randn(64, 64).astype("float32")
408
+
409
+ compressed = codec.compress_array("layer.weight", array)
410
+ restored = codec.decompress_array(compressed)
411
+ ```
412
+
413
+ ### 3. **State Dict Compression**
414
+
415
+ Compress dictionary-style model weights:
416
+
417
+ ```python
418
+ compressed = codec.compress_state_dict(state_dict)
419
+ restored = codec.decompress_state_dict(compressed)
420
+ ```
421
+
422
+ By default, small tensors and bias tensors are skipped. Set `min_tensor_size`
423
+ and `compress_bias` in `HyperGlyphConfig` to change that behavior.
424
+
425
+ ### 4. **Sparse Residual Repair**
426
+
427
+ Each block is reconstructed from a prototype and scale, then corrected with a
428
+ top-k sparse residual:
429
+
430
+ ```text
431
+ block ~= prototype[prototype_id] * scale + sparse_residual
432
+ ```
433
+
434
+ Increase `residual_k` for better reconstruction fidelity, or reduce it for a
435
+ smaller compressed representation.
436
+
437
+ ### 5. **Serialization**
438
+
439
+ Save compressed models as `.hwz` zip archives:
440
+
441
+ ```python
442
+ from hyperglyph import load_compressed, save_compressed
443
+
444
+ save_compressed(compressed, "model.hwz")
445
+ loaded = load_compressed("model.hwz")
446
+ ```
447
+
448
+ ### 6. **PyTorch Adapter**
449
+
450
+ Install the `torch` extra to convert PyTorch tensors into compressed Hyper Glyph
451
+ models:
452
+
453
+ ```python
454
+ from hyperglyph import compress_state_dict, decompress_state_dict
455
+
456
+ compressed = compress_state_dict(model.state_dict())
457
+ restored = decompress_state_dict(compressed, reference_state_dict=model.state_dict())
458
+ ```
459
+
460
+ ### 7. **Compression Metrics**
461
+
462
+ Measure the compression and reconstruction tradeoff:
463
+
464
+ ```python
465
+ report = codec.report(compressed, state_dict, restored)
466
+
467
+ print(report.original_bytes)
468
+ print(report.compressed_bytes)
469
+ print(report.compression_ratio)
470
+ print(report.total_mse)
471
+ print(report.total_mae)
472
+ print(report.max_abs_error)
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Configuration
478
+
479
+ ```python
480
+ from hyperglyph import HyperGlyphConfig
481
+
482
+ config = HyperGlyphConfig(
483
+ hdc_dim=4096,
484
+ block_size=16,
485
+ n_buckets=16,
486
+ n_prototypes=128,
487
+ residual_k=8,
488
+ seed=42,
489
+ min_tensor_size=256,
490
+ compress_bias=False,
491
+ dtype="float32",
492
+ device="cpu",
493
+ )
494
+ ```
495
+
496
+ Key settings:
497
+
498
+ - **`block_size`** controls how many flattened weights are grouped together.
499
+ - **`n_prototypes`** controls how many reusable block representatives are learned.
500
+ - **`residual_k`** controls how many residual correction values are stored per block.
501
+ - **`min_tensor_size`** skips tensors too small to benefit from compression.
502
+ - **`compress_bias`** enables compression for bias tensors, which are skipped by default.
503
+ - **`seed`** makes prototype selection deterministic.
504
+
505
+ ---
506
+
507
+ ## Examples
508
+
509
+ ```python
510
+ import numpy as np
511
+
512
+ from hyperglyph import HyperGlyphCodec, HyperGlyphConfig
513
+
514
+ state_dict = {
515
+ "encoder.weight": np.random.randn(128, 128).astype("float32"),
516
+ "decoder.weight": np.random.randn(128, 64).astype("float32"),
517
+ }
518
+
519
+ codec = HyperGlyphCodec(
520
+ HyperGlyphConfig(
521
+ block_size=16,
522
+ n_prototypes=64,
523
+ residual_k=8,
524
+ )
525
+ )
526
+
527
+ compressed = codec.compress_state_dict(state_dict)
528
+ restored = codec.decompress_state_dict(compressed)
529
+ report = codec.report(compressed, state_dict, restored)
530
+
531
+ print(report)
532
+ ```
533
+
534
+ ```bash
535
+ hyperglyph compress model.pt model.hwz
536
+ hyperglyph inspect model.hwz
537
+ hyperglyph benchmark model.pt
538
+ ```
539
+
540
+ ---
541
+
542
+ ## Project Structure
543
+
544
+ ```text
545
+ src/hyperglyph/
546
+ __init__.py # Public API
547
+ blocks.py # Tensor flattening, block splitting, shape restore
548
+ cli.py # Command-line interface
549
+ codec.py # HyperGlyphCodec and compressed dataclasses
550
+ config.py # HyperGlyphConfig
551
+ exceptions.py # Package exceptions
552
+ hdc.py # Hyperdimensional vector helpers
553
+ metrics.py # Size and reconstruction metrics
554
+ prototypes.py # Prototype learning and assignment
555
+ residual.py # Sparse residual encoding and repair
556
+ serialization.py # .hwz save/load helpers
557
+ torch_adapter.py # Optional PyTorch integration
558
+ py.typed # Typing marker
559
+ tests/
560
+ test_*.py # Unit tests
561
+ docs/
562
+ algorithm.md # Algorithm overview
563
+ api.md # API notes
564
+ cli.md # CLI examples
565
+ index.md # Documentation home
566
+ roadmap.md # Planned work
567
+ examples/
568
+ compress_mlp.py # PyTorch MLP compression example
569
+ compress_state_dict.py # NumPy state dict compression example
570
+ mnist_demo.py # MNIST-oriented demo
571
+ hyperglyph.png # Project logo
572
+ pyproject.toml # Package metadata and dependencies
573
+ CHANGELOG.md # Release history
574
+ CONTRIBUTING.md # Contribution guide
575
+ LICENSE # MIT license
576
+ ```
577
+
578
+ ---
579
+
580
+ ## Development
581
+
582
+ ```bash
583
+ # Install with dev, PyTorch, and docs extras
584
+ pip install -e ".[dev,torch,docs]"
585
+
586
+ # Run tests
587
+ pytest
588
+
589
+ # Run linting
590
+ ruff check .
591
+
592
+ # Type-check package code
593
+ mypy
594
+
595
+ # Build package
596
+ python -m build
597
+ ```
598
+
599
+ ---
600
+
601
+ ## License
602
+
603
+ MIT
604
+
605
+ ---
606
+
607
+ ## Contributing
608
+
609
+ Contributions are welcome. Open an issue or pull request with the model shape,
610
+ codec configuration, expected compression behavior, reconstruction metrics, and
611
+ any accuracy checks you used.
612
+
613
+ ---
614
+
615
+ ## Citation
616
+
617
+ If you use Hyper Glyph in research, please cite:
618
+
619
+ ```bibtex
620
+ @software{HyperGlyph2026,
621
+ title={Hyper Glyph: Hyperdimensional Symbolic Residual Compression for Neural Network Weights},
622
+ author={Robert McMenemy},
623
+ year={2026},
624
+ version={0.1.0},
625
+ }
626
+ ```
627
+
@@ -0,0 +1,18 @@
1
+ hyperglyph/__init__.py,sha256=y4mLyDGcQqsBygUGK-uYH3JQ11OETKBoR9aOHkrHdPg,542
2
+ hyperglyph/blocks.py,sha256=KIOrQMUYQKzXok5FPULpQNIZ7zEeR-r8TS5d9lxk1k0,1833
3
+ hyperglyph/cli.py,sha256=YRll872p2L0Z_7stIXpTPLi8O9gl78O-yShmVjh3PYU,4220
4
+ hyperglyph/codec.py,sha256=AEkhheZ4Po_B4zIArG74o34I5Y9xXLA2ucDyvMBtC70,7832
5
+ hyperglyph/config.py,sha256=o_NlcObZXsxbfLHVTjnCp3GTRdtk0NEATH16YeZVL_E,1218
6
+ hyperglyph/exceptions.py,sha256=bEybexbnni-qUX53nY9Md-7HQ5hEEjiUHdmpjRf5HcY,231
7
+ hyperglyph/hdc.py,sha256=C4cxsh3t4803mOzmuk3NSRKOYpe0Q-ppPgFBbLDPNbk,2189
8
+ hyperglyph/metrics.py,sha256=-qfu7Q6mCl9lJQwzAqdoT2sbZcq5TiLfErmsOb2ENqI,2141
9
+ hyperglyph/prototypes.py,sha256=aSg8Z0loK8lp36WOu4kEd7tOf0v27GbVPOspqpr-aCo,2479
10
+ hyperglyph/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ hyperglyph/residual.py,sha256=Cr93tx0T-7pC4DVNlECxdSeGHrZtgi_XiTUpaqvHelQ,1580
12
+ hyperglyph/serialization.py,sha256=Uy0ny5ezO8BxpZXx2BYmMEogRMZQFX_UXr_BXmz-4Es,2978
13
+ hyperglyph/torch_adapter.py,sha256=2wnTznlDoEr-8ewgDRNPAM_v_cCd8mqauJjKWOf03qc,2299
14
+ hyperglyph_codec-0.1.0.dist-info/METADATA,sha256=952AN_OTENZ-Wax4BwaSVU41oWkOLqH3aGtwORs1l5s,15233
15
+ hyperglyph_codec-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
16
+ hyperglyph_codec-0.1.0.dist-info/entry_points.txt,sha256=yoRAHIDr2Qm2RaIW17eVOGbZx3IqOP7bfuJiVEzJbWs,51
17
+ hyperglyph_codec-0.1.0.dist-info/licenses/LICENSE,sha256=lyYmqzkoDh1IH05AKxjL_wfMtVWqpqpjrQgjITQR01M,1117
18
+ hyperglyph_codec-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ hyperglyph = hyperglyph.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Robert McMenemy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ACTION OF
20
+ CONTRACT, NEGLIGENCE OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.