cmenpy 2026a0__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.
Files changed (70) hide show
  1. cmenpy/__init__.py +29 -0
  2. cmenpy/bounds/__init__.py +40 -0
  3. cmenpy/context/__init__.py +96 -0
  4. cmenpy/epoch/__init__.py +60 -0
  5. cmenpy/functools/__init__.py +44 -0
  6. cmenpy/functools/utils.py +0 -0
  7. cmenpy/generator/__init__.py +17 -0
  8. cmenpy/hints/__init__.py +4 -0
  9. cmenpy/hints/array.py +8 -0
  10. cmenpy/hints/dynamic.py +8 -0
  11. cmenpy/hints/option.py +3 -0
  12. cmenpy/hints/primitives.py +5 -0
  13. cmenpy/kernel/__init__.py +5 -0
  14. cmenpy/kernel/buffer.py +86 -0
  15. cmenpy/kernel/index.py +43 -0
  16. cmenpy/kernel/low/__init__.py +17 -0
  17. cmenpy/kernel/mask.py +42 -0
  18. cmenpy/kernel/size.py +34 -0
  19. cmenpy/model/__init__.py +167 -0
  20. cmenpy/model/optimizer/__init__.py +5 -0
  21. cmenpy/model/optimizer/base.py +36 -0
  22. cmenpy/model/optimizer/functions/__init__.py +144 -0
  23. cmenpy/model/optimizer/functions/params.py +90 -0
  24. cmenpy/model/optimizer/functions/protocols.py +31 -0
  25. cmenpy/model/optimizer/template/__init__.py +110 -0
  26. cmenpy/model/optimizer/template/protocols.py +11 -0
  27. cmenpy/model/optimizer/variables/__init__.py +39 -0
  28. cmenpy/population/__init__.py +3 -0
  29. cmenpy/population/agent/__init__.py +12 -0
  30. cmenpy/population/agent/base/__init__.py +53 -0
  31. cmenpy/population/agent/base/attributes.py +25 -0
  32. cmenpy/population/agent/base/template.py +9 -0
  33. cmenpy/population/agent/generator.py +15 -0
  34. cmenpy/population/agent/inmutable.py +34 -0
  35. cmenpy/population/agent/mutable.py +38 -0
  36. cmenpy/population/attributes/__init__.py +8 -0
  37. cmenpy/population/attributes/base.py +81 -0
  38. cmenpy/population/attributes/matrix/__init__.py +21 -0
  39. cmenpy/population/attributes/matrix/mixin.py +14 -0
  40. cmenpy/population/dynamic/__init__.py +0 -0
  41. cmenpy/population/dynamic/base.py +30 -0
  42. cmenpy/population/iterator/__init__.py +0 -0
  43. cmenpy/population/iterator/base.py +28 -0
  44. cmenpy/population/operations/__init__.py +36 -0
  45. cmenpy/population/operations/access.py +24 -0
  46. cmenpy/population/operations/assign.py +40 -0
  47. cmenpy/population/operations/compute.py +30 -0
  48. cmenpy/population/operations/extract.py +21 -0
  49. cmenpy/population/operations/stream/__init__.py +6 -0
  50. cmenpy/population/operations/stream/base.py +21 -0
  51. cmenpy/population/operations/stream/everybody.py +12 -0
  52. cmenpy/population/operations/stream/reader.py +15 -0
  53. cmenpy/population/operations/stream/vectorizable.py +17 -0
  54. cmenpy/population/operations/stream/writer.py +9 -0
  55. cmenpy/population/operations/swap.py +17 -0
  56. cmenpy/population/swarm.py +19 -0
  57. cmenpy/space/__init__.py +0 -0
  58. cmenpy/target/__init__.py +56 -0
  59. cmenpy/tracker/__init__.py +26 -0
  60. cmenpy/tracker/epoch.py +19 -0
  61. cmenpy-2026a0.dist-info/METADATA +84 -0
  62. cmenpy-2026a0.dist-info/RECORD +70 -0
  63. cmenpy-2026a0.dist-info/WHEEL +5 -0
  64. cmenpy-2026a0.dist-info/licenses/LICENSE.md +674 -0
  65. cmenpy-2026a0.dist-info/top_level.txt +3 -0
  66. example/__init__.py +0 -0
  67. example/gwo_algorithm_matrix.py +45 -0
  68. example/hello_world_v1.py +58 -0
  69. example/hello_world_v2.py +70 -0
  70. test/__init__.py +0 -0
cmenpy/__init__.py ADDED
@@ -0,0 +1,29 @@
1
+ __version__ = "2026a0"
2
+
3
+ from cmenpy.bounds import Bounds
4
+ from cmenpy.epoch import EpochIteration, Epoch
5
+ from cmenpy.functools import is_best, is_worst, best_of, worst_of
6
+ from cmenpy.kernel import KernelSize, KernelBuffer
7
+ from cmenpy.model import declare, template, template_check
8
+ from cmenpy.model.optimizer import FunctionOptimizerModel, TemplateOptimizerModel
9
+ from cmenpy.model.optimizer.variables import Argument, Variable
10
+ from cmenpy.population.swarm import PopulationSwarm
11
+
12
+ __all__ = [
13
+ "declare",
14
+ "template",
15
+ "template_check",
16
+ "is_best",
17
+ "is_worst",
18
+ "best_of",
19
+ "worst_of",
20
+ "Argument",
21
+ "Variable",
22
+ "EpochIteration",
23
+ "Epoch",
24
+ "KernelBuffer",
25
+ "KernelSize",
26
+ "PopulationSwarm",
27
+ "FunctionOptimizerModel",
28
+ "TemplateOptimizerModel",
29
+ ]
@@ -0,0 +1,40 @@
1
+ import functools
2
+ import typing
3
+
4
+ from cmenpy.hints import ScalarType
5
+
6
+ InnerSequence = typing.Union[typing.Tuple[ScalarType, ...], typing.List[ScalarType]]
7
+ SequenceStructure = typing.List[InnerSequence[ScalarType]]
8
+
9
+
10
+ class Bounds:
11
+ def __init__(self, bounds: SequenceStructure[ScalarType]):
12
+ self.__bounds = bounds
13
+
14
+ @functools.cached_property
15
+ def ndim(self):
16
+ return len(self.__bounds)
17
+
18
+ @property
19
+ def low(self):
20
+ return self.__bounds[0][0]
21
+
22
+ @property
23
+ def up(self):
24
+ return self.__bounds[0][1]
25
+
26
+ def __iter__(self):
27
+ return (b for b in self.__bounds)
28
+
29
+ def __len__(self):
30
+ return len(self.__bounds)
31
+
32
+ def __class_getitem__(cls, params):
33
+ return create_bounds(params)
34
+
35
+
36
+ def create_bounds(bounds: SequenceStructure[ScalarType] | Bounds) -> Bounds:
37
+ if isinstance(bounds, Bounds):
38
+ return bounds
39
+
40
+ return Bounds(bounds)
@@ -0,0 +1,96 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+
5
+ from cmenpy.bounds import Bounds
6
+ from cmenpy.epoch import EpochIteration
7
+ from cmenpy.generator import DefaultGenerator
8
+ from cmenpy.kernel import KernelBuffer, KernelSize
9
+ from cmenpy.target import Target, TargetFunction
10
+ from cmenpy.tracker import Tracker
11
+ from cmenpy.hints.option import SenseType
12
+
13
+
14
+ class Context:
15
+ def __init__(
16
+ self,
17
+ f: Target,
18
+ bounds: Bounds,
19
+ buffer: KernelBuffer,
20
+ generator: DefaultGenerator,
21
+ sense: SenseType,
22
+ ):
23
+ self.__target = TargetFunction(f, bounds)
24
+ self.__bounds = bounds
25
+ self.__buff = buffer
26
+ self.__generator = generator
27
+ self.__sense = sense
28
+
29
+ @property
30
+ def target(self) -> TargetFunction:
31
+ return self.__target
32
+
33
+ @property
34
+ def bounds(self) -> Bounds:
35
+ return self.__bounds
36
+
37
+ @property
38
+ def buff(self) -> KernelBuffer:
39
+ return self.__buff
40
+
41
+ @property
42
+ def rng(self) -> np.random.Generator:
43
+ return self.__generator.rng
44
+
45
+ @property
46
+ def sense(self) -> SenseType:
47
+ return self.__sense
48
+
49
+
50
+ class MainContextManager:
51
+ def __init__(
52
+ self,
53
+ f: Target,
54
+ bounds: Bounds,
55
+ epochs: int,
56
+ size: KernelSize,
57
+ seed: typing.Optional[int] = None,
58
+ debug: bool = False,
59
+ sense: SenseType = "min",
60
+ ):
61
+ self.__size = size
62
+ self.__target = TargetFunction(f, bounds)
63
+ self.__bounds = bounds
64
+ self.__sense = sense
65
+ self.__k_buff = KernelBuffer(size, self.__bounds, self.__target, self.__sense)
66
+ self.__generator = DefaultGenerator(seed=seed)
67
+ self.__tracker = Tracker(self.__k_buff)
68
+ self.__epoch_it = EpochIteration(epochs, self.__tracker, self.__target, debug)
69
+
70
+ @property
71
+ def sense(self):
72
+ return self.__sense
73
+
74
+ @property
75
+ def epoch_it(self) -> EpochIteration:
76
+ return self.__epoch_it
77
+
78
+ @property
79
+ def buffer(self) -> KernelBuffer:
80
+ return self.__k_buff
81
+
82
+ @property
83
+ def target(self) -> TargetFunction:
84
+ return self.__target
85
+
86
+ @property
87
+ def bounds(self) -> Bounds:
88
+ return self.__bounds
89
+
90
+ @property
91
+ def default_generator(self) -> DefaultGenerator:
92
+ return self.__generator
93
+
94
+ @property
95
+ def tracker(self) -> Tracker:
96
+ return self.__tracker
@@ -0,0 +1,60 @@
1
+ import time
2
+
3
+ from cmenpy.target import Target, TargetFunction
4
+ from cmenpy.tracker import Tracker
5
+
6
+
7
+ class Epoch:
8
+ def __init__(self, e: int):
9
+ self.__e: int = e
10
+
11
+ def __int__(self):
12
+ return self.__e
13
+
14
+ def __float__(self):
15
+ return float(self.__e)
16
+
17
+ def __repr__(self):
18
+ return f"Epoch({self.__e})"
19
+
20
+
21
+ class EpochIteration:
22
+ def __init__(
23
+ self,
24
+ epochs: int,
25
+ tracker: Tracker,
26
+ target: TargetFunction,
27
+ debug: bool = False,
28
+ ):
29
+ if epochs <= 0:
30
+ raise ValueError("Epochs must be a positive integer.")
31
+
32
+ self.__epochs: int = epochs
33
+ self.__current_epoch: int = 0
34
+ self.__cpu_time: float = 0
35
+ self.__tracker: Tracker = tracker
36
+ self.__target: TargetFunction = target
37
+ self.__debug: bool = debug
38
+
39
+ def __iter__(self):
40
+ for e in range(0, self.__epochs):
41
+ self.__current_epoch = e
42
+
43
+ start = time.process_time()
44
+ yield e
45
+ self.__cpu_time = time.process_time() - start
46
+
47
+ if self.__debug:
48
+ self.__tracker.track(e, self.cpu_time, self.__target.nfe)
49
+
50
+ @property
51
+ def current(self):
52
+ return self.__current_epoch
53
+
54
+ @property
55
+ def max(self) -> int:
56
+ return self.__epochs
57
+
58
+ @property
59
+ def cpu_time(self):
60
+ return self.__cpu_time
@@ -0,0 +1,44 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+
5
+ from cmenpy.hints.option import SenseType
6
+
7
+ A = typing.TypeVar("A")
8
+ B = typing.TypeVar("B")
9
+ AB = tuple[A, B]
10
+
11
+
12
+ def is_best(ab: AB, sense: SenseType = "min"):
13
+ a, b = ab
14
+
15
+ if sense == "min":
16
+ mask = np.array(a) < np.array(b)
17
+ else:
18
+ mask = np.array(a) > np.array(b)
19
+
20
+ return np.flatnonzero(mask)
21
+
22
+
23
+ def is_worst(ab: AB, sense: SenseType = "min"):
24
+ a, b = ab
25
+
26
+ if sense == "min":
27
+ mask = np.array(a) > np.array(b)
28
+ else:
29
+ mask = np.array(a) < np.array(b)
30
+
31
+ return np.flatnonzero(mask)
32
+
33
+
34
+
35
+ def best_of(a: A) -> int:
36
+ return a.best
37
+
38
+
39
+
40
+ def worst_of(a: A) -> int:
41
+ return a.worst
42
+
43
+
44
+ __all__ = ["is_best", "is_worst", "best_of", "worst_of"]
File without changes
@@ -0,0 +1,17 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+
5
+
6
+ class DefaultGenerator:
7
+ def __init__(self, seed: typing.Optional[int] = None) -> None:
8
+ self.__seed = seed
9
+ self.__rng = np.random.default_rng(self.__seed)
10
+
11
+ @property
12
+ def seed(self) -> typing.Optional[int]:
13
+ return self.__seed
14
+
15
+ @property
16
+ def rng(self) -> np.random.Generator:
17
+ return self.__rng
@@ -0,0 +1,4 @@
1
+ from cmenpy.hints.array import NDArrayType, ArrayFloatType, ArrayIntegerType
2
+ from cmenpy.hints.primitives import ScalarType
3
+
4
+ __all__ = ["ScalarType", "NDArrayType", "ArrayFloatType", "ArrayIntegerType"]
cmenpy/hints/array.py ADDED
@@ -0,0 +1,8 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+ import numpy.typing as npt
5
+
6
+ NDArrayType = npt.NDArray[np.number[typing.Any]]
7
+ ArrayFloatType = npt.NDArray[np.floating[typing.Any]]
8
+ ArrayIntegerType = npt.NDArray[np.integer[typing.Any]]
@@ -0,0 +1,8 @@
1
+ from collections.abc import Iterable
2
+
3
+ from cmenpy.population.agent.base import BaseAgent
4
+ from cmenpy.hints import NDArrayType
5
+
6
+ Source = NDArrayType | Iterable[float | int]
7
+ SourceIterable = BaseAgent | NDArrayType
8
+ DualSource = tuple[SourceIterable, SourceIterable]
cmenpy/hints/option.py ADDED
@@ -0,0 +1,3 @@
1
+ import typing
2
+
3
+ SenseType = typing.Literal["min", "max"]
@@ -0,0 +1,5 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+
5
+ ScalarType = typing.TypeVar("ScalarType", bound=np.number[typing.Any])
@@ -0,0 +1,5 @@
1
+ from cmenpy.kernel.buffer import KernelBuffer
2
+ from cmenpy.kernel.mask import KernelMask
3
+ from cmenpy.kernel.size import KernelSize
4
+
5
+ __all__ = ["KernelBuffer", "KernelSize", "KernelMask"]
@@ -0,0 +1,86 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+
5
+ from cmenpy.bounds import Bounds
6
+ from cmenpy.kernel.index import BufferIndex
7
+ from cmenpy.kernel.low import init_buffer
8
+ from cmenpy.kernel.mask import KernelMask
9
+ from cmenpy.kernel.size import KernelSize
10
+ from cmenpy.target import TargetFunction
11
+ from cmenpy.hints import NDArrayType
12
+ from cmenpy.hints.option import SenseType
13
+
14
+
15
+ class KernelBuffer:
16
+ def __init__(
17
+ self,
18
+ size: KernelSize,
19
+ bounds: Bounds,
20
+ target: TargetFunction,
21
+ sense: SenseType,
22
+ ):
23
+ self.__size = size
24
+ self.__bounds = bounds
25
+ self.__target = target
26
+ self.__sense = sense
27
+
28
+ self.__buffer: NDArrayType = init_buffer(self.__size.max, bounds.ndim)
29
+ self.__mask = KernelMask(self.__size)
30
+
31
+ def apply(
32
+ self,
33
+ x: NDArrayType,
34
+ idx: typing.Union[int, slice, typing.List[int], None] = None,
35
+ ) -> None:
36
+ """Vectorizes the target function to evaluate and update the solutions matrix.
37
+
38
+ The internal buffer stores "fitness" in column 0 and "solutions" or "x"
39
+ from column 1 onwards. Supports updating a single row, a slice, a list/array
40
+ of indices, or the entire active matrix if `idx` is None.
41
+
42
+ Parameters
43
+ ----------
44
+ x : NDArrayType
45
+ An array containing the new solution data to be inserted into the buffer.
46
+ idx : int, slice, NDArrayType, list, optional
47
+ The row index, slice, or index mask to update. If None, defaults to
48
+ the internal mask.
49
+
50
+ Returns
51
+ -------
52
+ None
53
+ """
54
+ rows = self.__mask.founds if idx is None else idx
55
+ self.__buffer[rows, 1:] = x
56
+
57
+ if isinstance(idx, int):
58
+ self.__buffer[rows, 0] = self.__target.evaluate(x)
59
+ else:
60
+ self.__buffer[rows, 0] = np.apply_along_axis(
61
+ self.__target, 1, self.__buffer[rows, 1:]
62
+ )
63
+
64
+ @property
65
+ def shape(self):
66
+ return self.__buffer.shape
67
+
68
+ @property
69
+ def mask(self) -> KernelMask:
70
+ return self.__mask
71
+
72
+ @property
73
+ def raw(self) -> NDArrayType:
74
+ return self.__buffer
75
+
76
+ @property
77
+ def idx(self) -> BufferIndex:
78
+ return BufferIndex(self.__buffer, self.__mask, self.__sense)
79
+
80
+ @property
81
+ def size(self) -> KernelSize:
82
+ return self.__size
83
+
84
+ @property
85
+ def snapshot(self) -> "KernelBuffer":
86
+ return KernelBuffer(self.__size, self.__bounds, self.__target, self.__sense)
cmenpy/kernel/index.py ADDED
@@ -0,0 +1,43 @@
1
+ import numpy as np
2
+
3
+ from cmenpy.kernel.mask import KernelMask
4
+ from cmenpy.hints import NDArrayType
5
+ from cmenpy.hints.option import SenseType
6
+
7
+
8
+ class BufferIndex:
9
+ def __init__(self, buffer: NDArrayType, mask: KernelMask, sense: SenseType):
10
+ self.__buffer = buffer
11
+ self.__sense = sense
12
+ self.__mask = mask
13
+
14
+ def __iter__(self):
15
+ pop_size, _ = self.__buffer.shape
16
+
17
+ return iter(range(0, pop_size))
18
+
19
+ def __invert__(self):
20
+ return np.where(~np.isnan(self.__buffer[:, 0]))[0].astype(int)
21
+
22
+ @property
23
+ def sort(self):
24
+ _idx = np.argsort(self.__buffer[:, 0])
25
+
26
+ if self.__sense == "max":
27
+ return _idx[::-1]
28
+
29
+ return _idx
30
+
31
+ @property
32
+ def best(self):
33
+ if self.__sense == "max":
34
+ return self.sort[-1]
35
+
36
+ return self.sort[0]
37
+
38
+ @property
39
+ def worst(self):
40
+ if self.__sense == "max":
41
+ return self.sort[0]
42
+
43
+ return self.sort[-1]
@@ -0,0 +1,17 @@
1
+ import numpy as np
2
+
3
+ from cmenpy.hints import NDArrayType
4
+
5
+
6
+ def expand_buffer(
7
+ n: int,
8
+ ndim: int,
9
+ ) -> NDArrayType:
10
+ return np.hstack(([n], np.full((ndim + 1), np.nan)))
11
+
12
+
13
+ def init_buffer(
14
+ n_pop: int,
15
+ ndim: int,
16
+ ) -> NDArrayType:
17
+ return np.full((n_pop, ndim + 1), np.nan)
cmenpy/kernel/mask.py ADDED
@@ -0,0 +1,42 @@
1
+ import typing
2
+
3
+ import numpy as np
4
+
5
+ from cmenpy.kernel.size import KernelSize
6
+
7
+
8
+ class KernelMask:
9
+ def __init__(
10
+ self,
11
+ size: KernelSize,
12
+ ):
13
+ self.__size: KernelSize = size
14
+ self.__mask = np.fromiter((False for _ in range(0, size.max)), dtype=bool)
15
+ self.__mask[0 : self.__size.size] = True
16
+
17
+ def __iter__(self):
18
+ return iter(map(bool, self.__mask))
19
+
20
+ def __setitem__(
21
+ self,
22
+ key: typing.Union[int, slice, typing.List[int], None],
23
+ value: bool,
24
+ ):
25
+ self.__mask[key] = value
26
+
27
+ def __getitem__(
28
+ self, key: typing.Union[int, slice, typing.List[int], None]
29
+ ) -> typing.Any:
30
+ return self.__mask[key]
31
+
32
+ @property
33
+ def active(self):
34
+ return sum(self.__mask)
35
+
36
+ @property
37
+ def passive(self):
38
+ return len(self.__mask) - sum(self.__mask)
39
+
40
+ @property
41
+ def founds(self) -> list[bool]:
42
+ return [*map(bool, self.__mask)]
cmenpy/kernel/size.py ADDED
@@ -0,0 +1,34 @@
1
+ import typing
2
+
3
+
4
+ class KernelSize:
5
+ def __init__(
6
+ self,
7
+ size: int,
8
+ min_size: typing.Optional[int] = None,
9
+ max_size: typing.Optional[int] = None,
10
+ ):
11
+ if min_size is None:
12
+ min_size = size
13
+
14
+ if max_size is None:
15
+ max_size = size
16
+
17
+ if size < min_size or size > max_size:
18
+ raise ValueError("Size must be between 'min_size' and 'max_size'.")
19
+
20
+ self.__size = size
21
+ self.__min = min_size
22
+ self.__max = max_size
23
+
24
+ @property
25
+ def max(self) -> int:
26
+ return self.__max
27
+
28
+ @property
29
+ def min(self) -> int:
30
+ return self.__min
31
+
32
+ @property
33
+ def size(self) -> int:
34
+ return self.__size