creyone-layer 1.0.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,7 @@
1
+ from .utils.factory import create_layer
2
+ from . import act, conv, norm, pool # trigger layer registration
3
+
4
+ __all__ = [
5
+ 'create_layer',
6
+ 'act', 'conv', 'norm', 'pool',
7
+ ]
creyone_layer/act.py ADDED
@@ -0,0 +1,93 @@
1
+ from functools import partial
2
+ from typing import Optional
3
+
4
+ import torch
5
+ from torch import nn
6
+
7
+ from .utils.registry import register_layer
8
+
9
+ # A memory-efficient implementation of Swish function
10
+ class SwishImplementation(torch.autograd.Function):
11
+
12
+ @staticmethod
13
+ def forward(ctx, i):
14
+ result = i * torch.sigmoid(i)
15
+ ctx.save_for_backward(i)
16
+ return result
17
+
18
+ @staticmethod
19
+ def backward(ctx, grad_output):
20
+ i = ctx.saved_tensors[0]
21
+ sigmoid_i = torch.sigmoid(i)
22
+ return grad_output * (sigmoid_i * (1 + i * (1 - sigmoid_i)))
23
+
24
+
25
+ class MemoryEfficientSwish(nn.Module):
26
+
27
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
28
+ return SwishImplementation.apply(x)
29
+
30
+
31
+ class QuickGELU(nn.Module):
32
+
33
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
34
+ return x * torch.sigmoid(1.702 * x)
35
+
36
+ class HardSigmoid(nn.Module):
37
+
38
+ def __init__(self, inplace=True):
39
+ super().__init__()
40
+ self.relu = nn.ReLU6(inplace=inplace)
41
+
42
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
43
+ return self.relu(x + 3) / 6
44
+
45
+ class HardSwish(nn.Module):
46
+
47
+ def __init__(self, inplace=True):
48
+ super().__init__()
49
+ self.sigmoid = HardSigmoid(inplace=inplace)
50
+
51
+ def forward(self, x):
52
+ return x * self.sigmoid(x)
53
+
54
+
55
+ @register_layer('act')
56
+ def gelu(overwrite: Optional[nn.Module] = None, **kwargs):
57
+ if overwrite is not None: return overwrite
58
+ return nn.GELU
59
+
60
+ @register_layer('act')
61
+ def relu(overwrite: Optional[nn.Module] = None, inplace: bool = False, **kwargs):
62
+ if overwrite is not None: return overwrite
63
+ return partial(nn.ReLU, inplace=inplace)
64
+
65
+ @register_layer('act')
66
+ def relu6(overwrite: Optional[nn.Module] = None, **kwargs):
67
+ if overwrite is not None: return overwrite
68
+ return nn.ReLU6
69
+
70
+ @register_layer('act')
71
+ def sigmoid(overwrite: Optional[nn.Module] = None, **kwargs):
72
+ if overwrite is not None: return overwrite
73
+ return nn.Sigmoid
74
+
75
+ @register_layer('act')
76
+ def hardsig(overwrite: Optional[nn.Module] = None, **kwargs):
77
+ if overwrite is not None: return overwrite
78
+ return HardSigmoid
79
+
80
+ @register_layer('act')
81
+ def hardswish(overwrite: Optional[nn.Module] = None, **kwargs):
82
+ if overwrite is not None: return overwrite
83
+ return HardSwish
84
+
85
+ @register_layer('act')
86
+ def quickgelu(overwrite: Optional[nn.Module] = None, **kwargs):
87
+ if overwrite is not None: return overwrite
88
+ return QuickGELU
89
+
90
+ @register_layer('act')
91
+ def swisheff(overwrite: Optional[nn.Module] = None, **kwargs):
92
+ if overwrite is not None: return overwrite
93
+ return MemoryEfficientSwish
creyone_layer/conv.py ADDED
@@ -0,0 +1,23 @@
1
+ from torch import nn
2
+
3
+ from .utils.registry import register_layer
4
+ from .wrap import wrap_conv
5
+
6
+
7
+ def _base(dim: int, opt: set | None = None):
8
+ if opt is None: opt = set()
9
+ if dim == 1: return wrap_conv(nn.Conv1d, opt=opt)
10
+ if dim == 2: return wrap_conv(nn.Conv2d, opt=opt)
11
+ if dim == 3: return wrap_conv(nn.Conv3d, opt=opt)
12
+ raise ValueError(f'Unsupported dim: {dim}')
13
+
14
+
15
+ @register_layer('conv')
16
+ def base(dim: int = 2, optional: str = '') -> type[nn.Module]:
17
+ return _base(dim, opt=set(optional.split('+')))
18
+
19
+
20
+ @register_layer('conv')
21
+ def depthwise(dim: int = 2, optional: str = '') -> type[nn.Module]:
22
+ seq = set(optional.split('+')); seq.add('dw')
23
+ return _base(dim, opt=seq)
creyone_layer/norm.py ADDED
@@ -0,0 +1,23 @@
1
+ import torch.nn as nn
2
+
3
+ from .utils.registry import register_layer
4
+
5
+
6
+ def _wrapper(cls, eps: float = 1e-5, **extra_kwargs):
7
+
8
+ def _norm_func(*args, **kwargs):
9
+ _eps = kwargs.pop('eps', eps)
10
+ return cls(*args, eps=_eps, **extra_kwargs, **kwargs)
11
+
12
+ return _norm_func
13
+
14
+
15
+ @register_layer('norm')
16
+ def layer(eps: float = 1e-5, **_):
17
+ return _wrapper(nn.LayerNorm, eps=eps)
18
+
19
+ @register_layer('norm')
20
+ def batch(eps: float = 1e-5, dim: int = 2, mom: float = 0.1):
21
+ if dim == 1: return _wrapper(nn.BatchNorm1d, eps=eps, momentum=mom)
22
+ if dim == 2: return _wrapper(nn.BatchNorm2d, eps=eps, momentum=mom)
23
+ raise ValueError(f"Unsupported dim={dim} for BatchNorm. Expected 1 or 2.")
creyone_layer/pool.py ADDED
@@ -0,0 +1,29 @@
1
+ from torch import nn
2
+
3
+ from .utils.registry import register_layer
4
+ from .wrap import wrap_pool
5
+
6
+
7
+ def _max(dim: int, opt: set | None = None):
8
+ if opt is None: opt = set()
9
+ return wrap_pool(getattr(nn, f"MaxPool{dim}d"), opt=opt)
10
+
11
+
12
+ def _avg(dim: int, opt: set | None = None):
13
+ if opt is None: opt = set()
14
+ return wrap_pool(getattr(nn, f"AvgPool{dim}d"), opt=opt)
15
+
16
+
17
+ @register_layer('pool')
18
+ def max(dim: int = 2, optional: str = '') -> callable:
19
+ return _max(dim, opt=set(optional.split('+')))
20
+
21
+
22
+ @register_layer('pool')
23
+ def avg(dim: int = 2, optional: str = '') -> callable:
24
+ return _avg(dim, opt=set(optional.split('+')))
25
+
26
+
27
+ @register_layer('pool')
28
+ def aavg(dim: int = 2, optional: str = '') -> callable:
29
+ return getattr(nn, f"AdaptiveAvgPool{dim}d")
@@ -0,0 +1,13 @@
1
+ from typing import Callable, Optional, Union
2
+ from torch import nn
3
+
4
+ from .registry import layer_entrypoint
5
+
6
+ def create_layer(
7
+ layer_name: Optional[Union[str, nn.Module]],
8
+ layer_family: str = 'any',
9
+ otherwise: Callable = lambda *a, **b: nn.Identity()) -> Union[nn.Module, Callable]:
10
+
11
+ if layer_name is None: return otherwise
12
+ if isinstance(layer_name, str): return layer_entrypoint(layer_name, layer_family=layer_family)
13
+ return layer_name
@@ -0,0 +1,51 @@
1
+ import sys, warnings
2
+ from collections import defaultdict
3
+ from typing import Any, Callable, Optional
4
+
5
+
6
+ _module_to_layers: dict[str, set[str]] = defaultdict(set) # dict of sets to check membership of model in module
7
+ _layer_to_module: dict[str, dict[str, str]] = defaultdict(dict) # mapping of model names to module names
8
+ _layer_entrypoints: dict[str, dict[str, Callable[..., Any]]] = defaultdict(dict) # mapping of model names to architecture entrypoint fns
9
+
10
+
11
+ def register_layer(layer_family: str = 'any'):
12
+
13
+ def _register_layer(fn: Callable[..., Any]) -> Callable[..., Any]:
14
+
15
+ # lookup containing module
16
+ mod = sys.modules[fn.__module__]
17
+ module_name = fn.__module__.split('.')[-1]
18
+
19
+ # add model to __all__ in module
20
+ layer_name = fn.__name__
21
+ if not hasattr(mod, '__all__'): mod.__all__ = []
22
+ mod.__all__.append(layer_name)
23
+
24
+ # add entries to registry dict/sets
25
+ if layer_name in _layer_entrypoints[layer_family]:
26
+ warnings.warn(
27
+ f'Overwriting {layer_name} in registry with {fn.__module__}.{layer_name}. This is because the name being '
28
+ 'registered conflicts with an existing name. Please check if this is not expected.',
29
+ stacklevel=2,
30
+ )
31
+ _layer_entrypoints[layer_family][layer_name] = fn
32
+ _layer_to_module[layer_family][layer_name] = module_name
33
+ _module_to_layers[module_name].add(layer_name)
34
+
35
+ return fn
36
+
37
+ return _register_layer
38
+
39
+
40
+ def layer_entrypoint(layer_name: str,
41
+ layer_family: Optional[str] = None,
42
+ module_filter: Optional[str] = None) -> Callable[..., Any]:
43
+ """Fetch a model entrypoint for specified model name
44
+ """
45
+ if module_filter and layer_name not in _module_to_layers.get(module_filter, set()):
46
+ raise RuntimeError(f'Model ({layer_name}) not found in module {module_filter}.')
47
+ if layer_family is not None and layer_name in _layer_entrypoints[layer_family]:
48
+ return _layer_entrypoints[layer_family][layer_name]
49
+ if layer_name not in _layer_entrypoints['any']:
50
+ raise RuntimeError(f'Layer [{layer_name}] not found in {layer_family} and `any`.')
51
+ return _layer_entrypoints['any'][layer_name]
creyone_layer/wrap.py ADDED
@@ -0,0 +1,130 @@
1
+ import inspect
2
+ from typing import Optional, Union
3
+
4
+ from torch import nn
5
+ from creyone import cynn
6
+
7
+
8
+ def _consume_args(args: tuple, kwargs: dict) -> tuple[list, dict]:
9
+ """Normalize positional args to (c1, c2, k), pulling missing values from kwargs.
10
+
11
+ Accepts args in (c1, c2, k) order. Any leading args that are omitted are
12
+ extracted from kwargs using the long-form keys ('in_channels', 'out_channels',
13
+ 'kernel_size') or their short aliases ('c1', 'c2', 'k').
14
+ """
15
+ args = list(args)
16
+ if len(args) == 3: return args, kwargs
17
+ k = kwargs.pop('kernel_size', kwargs.pop('k', None))
18
+ if len(args) == 2: return args + [k], kwargs
19
+ c2 = kwargs.pop('out_channels', kwargs.pop('c2', None))
20
+ if len(args) == 1: return args + [c2, k], kwargs
21
+ c1 = kwargs.pop('in_channels', kwargs.pop('c1', None))
22
+ return [c1, c2, k], kwargs
23
+
24
+
25
+ def _consume_pool_args(args: tuple, kwargs: dict) -> tuple:
26
+ """Extract kernel size from positional args or kwargs for pool layers."""
27
+ if args:
28
+ return args[0], kwargs
29
+ k = kwargs.pop('kernel_size', kwargs.pop('k', None))
30
+ return k, kwargs
31
+
32
+
33
+ def _compute_same_padding(k: int, d: int = 1) -> Union[int, list[int]]: # kernel, padding, dilation
34
+ """Pad to 'same' shape outputs."""
35
+ if isinstance(k, int): k = [k]
36
+ if d > 1: k = [d * (x - 1) + 1 for x in k] # actual kernel-size
37
+ p = [x // 2 for x in k] # auto-pad
38
+ return p[0] if len(p) == 1 else p
39
+
40
+
41
+ def _wrapfn(**kwargs):
42
+ """Extract and return common convolution/pooling parameters from kwargs.
43
+
44
+ Pops 'stride'/'s', 'padding'/'p', 'dilation'/'d', and 'groups'/'g' from
45
+ kwargs, returning them as (s, p, d, g). The remaining kwargs are left for
46
+ the caller to pass through to the underlying layer.
47
+ """
48
+ s = kwargs.pop('stride', kwargs.pop('s', 1))
49
+ p = kwargs.pop('padding', kwargs.pop('p', 0))
50
+ d = kwargs.pop('dilation', kwargs.pop('d', 1))
51
+ g = kwargs.pop('groups', kwargs.pop('g', 1))
52
+ return s, p, d, g
53
+
54
+
55
+ class AutoReshape(nn.Module):
56
+
57
+ def __init__(self, mid_layer: nn.Module):
58
+ super().__init__()
59
+ self._inner = mid_layer
60
+
61
+ def forward(self, x: cynn.CreYonT) -> cynn.CreYonT:
62
+ x = x.rearrange('B (H W) C -> B C H W', H=x.H).contiguous()
63
+ return x(self._inner).rearrange('B C H W -> B (H W) C')
64
+
65
+
66
+ def wrap_conv(cls: nn.Conv2d, opt: Union[set, str, None] = None):
67
+ """Wrap a Conv Nd class with flexible argument parsing and optional behaviors.
68
+
69
+ Args:
70
+ cls: A ConvNd-compatible class to wrap.
71
+ opt: A '+'-separated string of option flags:
72
+ - 'grid': use the kernel size arg as the stride (grid-like sampling).
73
+ - 'ap': auto-pad so the output spatial size matches the input.
74
+ - 'dw': set groups = in_channels (depthwise convolution).
75
+
76
+ Returns:
77
+ A factory function that accepts (c1, c2, k, ...) positionally or as kwargs
78
+ and forwards them to ``cls`` with stride, padding, dilation, and groups set.
79
+ """
80
+ if opt is None: opt = set()
81
+ elif isinstance(opt, str): opt = set(opt.split('+'))
82
+
83
+ def _fn(*args, **kwargs):
84
+ args, kwargs = _consume_args(args, kwargs)
85
+ s, p, d, g = _wrapfn(**kwargs)
86
+ if 'grid' in opt: s = args[-1]
87
+ if 'ap' in opt: p = _compute_same_padding(args[-1], d)
88
+ if 'dw' in opt: g = args[0]
89
+
90
+ ins = cls(*args, stride=s, padding=p, dilation=d, groups=g, **kwargs)
91
+ if 'ar' in opt: ins = AutoReshape(ins)
92
+ return ins
93
+
94
+ return _fn
95
+
96
+
97
+ def wrap_pool(cls, opt: Union[set, str, None] = None):
98
+ """Wrap a pooling class with flexible argument parsing and optional behaviors.
99
+
100
+ Args:
101
+ cls: A PoolNd-compatible class to wrap. Dilation is forwarded automatically
102
+ only when the class supports it (MaxPoolNd does, AvgPoolNd does not).
103
+ opt: A '+'-separated string of option flags:
104
+ - 'grid': use the kernel size arg as the stride (grid-like sampling).
105
+ - 'ap': auto-pad so the output spatial size matches the input.
106
+ - 'ar': wrap output in AutoReshape for (B HW C) tensors.
107
+
108
+ Returns:
109
+ A factory function that accepts (k, ...) positionally or as kwargs
110
+ and forwards them to ``cls`` with stride, padding, and dilation set.
111
+ """
112
+ if opt is None: opt = set()
113
+ elif isinstance(opt, str): opt = set(opt.split('+'))
114
+ _has_dilation = 'dilation' in inspect.signature(cls).parameters
115
+
116
+ def _fn(*args, **kwargs):
117
+ k, kwargs = _consume_pool_args(args, kwargs)
118
+ s, p, d, _ = _wrapfn(**kwargs)
119
+ if 'grid' in opt: s = k
120
+ if 'ap' in opt: p = _compute_same_padding(k, d if _has_dilation else 1)
121
+
122
+ extra = {'dilation': d} if _has_dilation else {}
123
+ ins = cls(k, stride=s, padding=p, **extra, **kwargs)
124
+ if 'ar' in opt: ins = AutoReshape(ins)
125
+ return ins
126
+
127
+ return _fn
128
+
129
+
130
+
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: creyone_layer
3
+ Version: 1.0.0
4
+ Summary: PyTorch layer building tools for CREYONE
5
+ Author-email: Linqa Kiriyama <kiriyamalq@gmail.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 QNiLix
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://example.com
29
+ Project-URL: Documentation, https://readthedocs.org
30
+ Project-URL: Repository, https://github.com/qnilix/creyone_layer.git
31
+ Project-URL: Bug Tracker, https://github.com/qnilix/creyone_layer/issues
32
+ Project-URL: Changelog, https://github.com/qnilix/creyone_layer/blob/master/CHANGELOG.md
33
+ Classifier: Development Status :: 4 - Beta
34
+ Classifier: Programming Language :: Python :: 3 :: Only
35
+ Requires-Python: >=3.10
36
+ Description-Content-Type: text/markdown
37
+ License-File: LICENSE
38
+ Requires-Dist: torch>=2.0
39
+ Dynamic: license-file
40
+
41
+ # creyone_layer (Beta Package for CreYoNe)
42
+
43
+ Create Your Network (a.k.a. CreYoNe) is a utility tools for making DNN module via torch. This repository provides Layer-sized building blocks for deep learning.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install creyone-layer
49
+ ```
50
+
51
+ From source:
52
+
53
+ ```bash
54
+ git clone https://github.com/qaiLN/creyone.git
55
+ cd creyone_layer
56
+ pip install -e .
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ ### Layer registry — `create_layer`
62
+
63
+ ```python
64
+ from creyone_layer import create_layer
65
+
66
+ relu = create_layer('relu', 'act')(inplace=True)()
67
+ bn = create_layer('batch', 'norm')(dim=2, eps=1e-5, mom=0.1)(64)
68
+ conv = create_layer('base', 'conv')(dim=2, optional='ap')(32, 64, 3)
69
+ pool = create_layer('max', 'pool')(dim=2, optional='ap')(2)
70
+ ```
71
+
72
+ ## Registered Layers
73
+
74
+ | Family | Names |
75
+ | ------ | ----------------------------------------------------------------------------------- |
76
+ | `conv` | `base`, `depthwise` |
77
+ | `norm` | `batch`, `layer` |
78
+ | `act` | `relu`, `relu6`, `gelu`, `quickgelu`, `sigmoid`, `hardsig`, `hardswish`, `swisheff` |
79
+ | `pool` | `max`, `avg` |
80
+
81
+ ### `conv` options (passed via `optional='...'`, `+`-separated)
82
+
83
+ | Flag | Effect |
84
+ | ------ | --------------------------------------------------- |
85
+ | `ap` | auto-pad — output spatial size matches input |
86
+ | `dw` | depthwise — `groups = in_channels` |
87
+ | `grid` | stride = kernel size (grid-like sampling) |
88
+ | `ar` | AutoReshape — accepts `(B, H*W, C)` token sequences |
@@ -0,0 +1,13 @@
1
+ creyone_layer/__init__.py,sha256=iylQnmzTdB2g7qiUjD06yUaRfiXX5YPcIHQiPtBtk0U,176
2
+ creyone_layer/act.py,sha256=5rRCRIMQGftvdS-pj68ua5h8r8z5oQDKs7H2vFzMY9E,2596
3
+ creyone_layer/conv.py,sha256=gvwN6dRUhxhwy1w_YKlcOvJTTLFjeBAl7OPqqZFmX08,697
4
+ creyone_layer/norm.py,sha256=5S5xweVo6b7iTxM1vgQ1C20WPhzmKUxxi3tfBulavxU,696
5
+ creyone_layer/pool.py,sha256=2oVyV8lIEoC85ohKbc1TGRdOodvastSriDnhzkgH_60,766
6
+ creyone_layer/wrap.py,sha256=MgaGQhXR-h2o3M5K_2Vp-0WzDzWFwF_33vQn5Mv0hLA,4887
7
+ creyone_layer/utils/factory.py,sha256=QniWN96qXSN_K983pCdjMO72b99NPLjug2iB-BtSYd4,475
8
+ creyone_layer/utils/registry.py,sha256=-_Vy2_yJBJhQD5GMA35GefVF0UBlBAAKxfGH0vR8MqY,2265
9
+ creyone_layer-1.0.0.dist-info/licenses/LICENSE,sha256=hclUFguzDZoeugY3tZSGMvKQnec1IV4x14q2WLiyV2k,1063
10
+ creyone_layer-1.0.0.dist-info/METADATA,sha256=dZUkZrbI_xhMLzL_MWcN6lNgNI3moza0286tPX4sguk,3756
11
+ creyone_layer-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
12
+ creyone_layer-1.0.0.dist-info/top_level.txt,sha256=k8aD6BdR6NCB7hFr7L_2C9Cw7TXOlhcPMpi_BDAkjvc,14
13
+ creyone_layer-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QNiLix
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, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ creyone_layer