Skip to content

Commit 56ae42a

Browse files
committed
Merge branch 'develop'
2 parents ac80131 + 43f0262 commit 56ae42a

File tree

13 files changed

+284
-37
lines changed

13 files changed

+284
-37
lines changed

crates/radiate-python/src/bindings/metric.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ impl PyMetricSet {
2424
)
2525
}
2626

27+
pub fn __dict__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyDict>> {
28+
let dict = PyDict::new(py);
29+
let rows = self.to_rows(py, true)?;
30+
for row in rows {
31+
dict.set_item(row.get_item("name")?, row)?;
32+
}
33+
34+
Ok(dict)
35+
}
36+
2737
pub fn __getitem__<'py>(
2838
&self,
2939
py: Python<'py>,

crates/radiate-python/src/bindings/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub use functions::*;
2424
pub use genome::*;
2525
pub use gp::{PyGraph, PyTree};
2626
pub use inputs::{PyEngineInput, PyEngineInputType};
27-
pub use metric::PyMetricSet;
27+
pub use metric::{PyMetric, PyMetricSet};
2828
pub use subscriber::{PyEngineEvent, PySubscriber};
2929

3030
use crate::{AnyChromosome, PyAnyObject};

py-radiate/radiate/__init__.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
)
3232
from .handlers import EventHandler, EventType, EngineEvent
3333
from .gp import Op, Graph, Tree
34+
from .metrics import MetricSet, Metric
3435

3536
from .inputs.executor import Executor
3637
from .fitness import Regression, NoveltySearch, BatchFitness
@@ -81,7 +82,12 @@
8182

8283
from .inputs.limit import SecondsLimit, GenerationsLimit, ScoreLimit, ConvergenceLimit
8384

84-
from .dependancies import _NUMPY_AVAILABLE, _GIL_ENABLED
85+
from .dependancies import (
86+
_NUMPY_AVAILABLE,
87+
_GIL_ENABLED,
88+
_PANDAS_AVAILABLE,
89+
_POLARS_AVAILABLE,
90+
)
8591

8692

8793
__all__ = [
@@ -91,6 +97,8 @@
9197
# Dependencies
9298
"_NUMPY_AVAILABLE",
9399
"_GIL_ENABLED",
100+
"_PANDAS_AVAILABLE",
101+
"_POLARS_AVAILABLE",
94102
# Random
95103
"random",
96104
# Codecs
@@ -174,4 +182,7 @@
174182
# Engine
175183
"GeneticEngine",
176184
"Generation",
185+
# Metrics
186+
"MetricSet",
187+
"Metric",
177188
]

py-radiate/radiate/codec/float.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def __init__(
1616
Initialize the float codec with a PyFloatCodec instance.
1717
:param codec: An instance of PyFloatCodec.
1818
"""
19-
self.codec = self._create_encoding(encoding, use_numpy)
19+
self.codec = self.__create_encoding(encoding, use_numpy)
2020

2121
def encode(self) -> Genotype[float]:
2222
"""
@@ -35,7 +35,7 @@ def decode(self, genotype: Genotype[float]) -> T:
3535
raise TypeError("genotype must be an instance of Genotype.")
3636
return self.codec.decode_py(genotype=genotype.__backend__())
3737

38-
def _create_encoding(
38+
def __create_encoding(
3939
self, encoding: FloatEncoding, use_numpy: bool
4040
) -> PyFloatCodec:
4141
"""
@@ -124,20 +124,21 @@ def matrix(
124124
Create a matrix codec with specified rows and columns.
125125
Args:
126126
shape: A tuple (rows, cols) or a list of integers specifying the shape of the matrix.
127-
value_range: Minimum and maximum value for the Gene's Allele to be init with.
128-
bound_range: Minimum and maximum values the allele is allowed to be within during evolution
127+
init_range: Minimum and maximum value for the Gene's Allele to be init with.
128+
bounds: Minimum and maximum values the allele is allowed to be within during evolution.
129+
use_numpy: Whether or not to decode the Genotype into a numpy array.
129130
Returns:
130131
A new FloatCodec instance with matrix configuration.
131132
132133
Example
133134
--------
134135
Create a codec that will produce a Genotype with 2 Chromosomes both containing 3 FloatGenes
135136
Alleles between 0.0 and 1.0, and bounds between -1.0 and 2.0:
136-
>>> rd.FloatCodec.matrix(shape=(2, 3), value_range=(0.0, 1.0), bound_range=(-1.0, 2.0))
137+
>>> rd.FloatCodec.matrix(shape=(2, 3), init_range=(0.0, 1.0), bounds=(-1.0, 2.0))
137138
FloatCodec(...)
138139
139140
The same can be achieved with a list of shapes:
140-
>>> rd.FloatCodec.matrix(shape=[3, 3], value_range=(0.0, 1.0), bound_range=(-1.0, 2.0))
141+
>>> rd.FloatCodec.matrix(shape=[3, 3], init_range=(0.0, 1.0), bounds=(-1.0, 2.0))
141142
FloatCodec(...)
142143
"""
143144
shapes = None
@@ -180,15 +181,16 @@ def vector(
180181
Create a vector codec with specified length.
181182
Args:
182183
length: Length of the vector.
183-
value_range: Minimum and maximum value for the Gene's Allele to be init with.
184-
bound_range: Minimum and maximum values the allele is allowed to be within during evolution.
184+
init_range: Minimum and maximum value for the Gene's Allele to be init with.
185+
bounds: Minimum and maximum values the allele is allowed to be within during evolution.
186+
use_numpy: Whether or not to decode the Genotype into a numpy array.
185187
Returns:
186188
A new FloatCodec instance with vector configuration.
187189
188190
Example
189191
--------
190192
Create a FloatCodec that will encode a Genotype with a single Chromosome containing 5 FloatGenes
191-
>>> rd.FloatCodec.vector(length=5, value_range=(0.0, 1.0), bound_range=(-1.0, 2.0))
193+
>>> rd.FloatCodec.vector(length=5, init_range=(0.0, 1.0), bounds=(-1.0, 2.0), use_numpy=False)
192194
FloatCodec(...)
193195
"""
194196
if length <= 0:
@@ -222,16 +224,16 @@ def scalar(
222224
"""
223225
Create a scalar codec.
224226
Args:
225-
value_range: Minimum and maximum value for the Gene's Allele to be init with.
226-
bound_range: Minimum and maximum values the allele is allowed to be within during evolution.
227+
init_range: Minimum and maximum value for the Gene's Allele to be init with.
228+
bounds: Minimum and maximum values the allele is allowed to be within during evolution.
227229
Returns:
228230
A new FloatCodec instance with scalar configuration.
229231
230232
Example:
231233
--------
232234
Create a FloatCodec that will encode a Genotype with a single Chromosome containing a single FloatGene
233235
with Alleles between 0.0 and 1.0, and bounds between -1.0 and 2.0:
234-
>>> rd.FloatCodec.scalar(value_range=(0.0, 1.0), bound_range=(-1.0, 2.0))
236+
>>> rd.FloatCodec.scalar(init_range=(0.0, 1.0), bounds=(-1.0, 2.0))
235237
FloatCodec(...)
236238
"""
237239
if init_range is not None:

py-radiate/radiate/codec/graph.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def weighted_directed(
3030
output: NodeValues | None = None,
3131
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
3232
max_nodes: int | None = None,
33-
) -> GraphCodec[Op, Graph]:
33+
) -> GraphCodec:
3434
return GraphCodec.__build_common(
3535
name="weighted_directed",
3636
shape=shape,
@@ -49,7 +49,7 @@ def weighted_recurrent(
4949
output: NodeValues | None = None,
5050
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
5151
max_nodes: int | None = None,
52-
) -> GraphCodec[Op, Graph]:
52+
) -> GraphCodec:
5353
return GraphCodec.__build_common(
5454
name="weighted_recurrent",
5555
shape=shape,
@@ -68,7 +68,7 @@ def directed(
6868
output: NodeValues | None = None,
6969
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
7070
max_nodes: int | None = None,
71-
) -> GraphCodec[Op, Graph]:
71+
) -> GraphCodec:
7272
return GraphCodec.__build_common(
7373
name="directed",
7474
shape=shape,
@@ -87,7 +87,7 @@ def recurrent(
8787
output: NodeValues | None = None,
8888
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
8989
max_nodes: int | None = None,
90-
) -> GraphCodec[Op, Graph]:
90+
) -> GraphCodec:
9191
return GraphCodec.__build_common(
9292
name="recurrent",
9393
shape=shape,
@@ -106,7 +106,7 @@ def gru(
106106
output: NodeValues | None = None,
107107
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
108108
max_nodes: int | None = None,
109-
) -> GraphCodec[Op, Graph]:
109+
) -> GraphCodec:
110110
return GraphCodec.__build_common(
111111
name="gru",
112112
shape=shape,
@@ -125,7 +125,7 @@ def lstm(
125125
output: NodeValues | None = None,
126126
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
127127
max_nodes: int | None = None,
128-
) -> GraphCodec[Op, Graph]:
128+
) -> GraphCodec:
129129
return GraphCodec.__build_common(
130130
name="lstm",
131131
shape=shape,
@@ -145,7 +145,7 @@ def __build_common(
145145
output: NodeValues | None = None,
146146
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
147147
max_nodes: int | None = None,
148-
) -> GraphCodec[Op, Graph]:
148+
) -> GraphCodec:
149149
input_size, output_size = shape
150150

151151
if input_size < 1 or output_size < 1:

py-radiate/radiate/codec/tree.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(
2727
leaf: NodeValues | None = None,
2828
root: NodeValues | None = None,
2929
values: dict[str, list[Op]] | list[tuple[str, list[Op]]] | None = None,
30-
) -> TreeCodec[Op, Tree]:
30+
) -> TreeCodec:
3131
input_size, output_size = shape
3232

3333
if input_size < 1 or output_size < 1:

py-radiate/radiate/dependancies.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,17 @@ def _lazy_import(module_name: str) -> tuple[ModuleType, bool]:
5454
)
5555

5656

57-
if TYPE_CHECKING:
58-
import numpy as np
57+
_PANDAS_AVAILABLE = False
58+
_POLARS_AVAILABLE = False
59+
5960

60-
_NUMPY_AVAILABLE = True
61+
if TYPE_CHECKING:
62+
try:
63+
import numpy as np
64+
_NUMPY_AVAILABLE = True
65+
except ModuleNotFoundError:
66+
_NUMPY_AVAILABLE = False
6167
else:
6268
np, _NUMPY_AVAILABLE = _lazy_import("numpy")
69+
pl, _POLARS_AVAILABLE = _lazy_import("polars")
70+
pd, _PANDAS_AVAILABLE = _lazy_import("pandas")

py-radiate/radiate/generation.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
from typing import Any
2-
31
from datetime import timedelta
42

53
from radiate.genome.ecosystem import Ecosystem
64
from radiate.genome.species import Species
75
from radiate.wrapper import PyObject
6+
from radiate.metrics import MetricSet
87
from .genome import Population
98
from radiate.radiate import PyGeneration
109

@@ -39,12 +38,12 @@ def value(self) -> T:
3938
"""
4039
return self.__backend__().value()
4140

42-
def metrics(self) -> dict[str, Any]:
41+
def metrics(self) -> MetricSet:
4342
"""
4443
Get the metrics of the generation.
4544
:return: The metrics of the generation.
4645
"""
47-
return self.__backend__().metrics()
46+
return MetricSet.from_rust(self.__backend__().metrics())
4847

4948
def objective(self) -> list[str] | str:
5049
"""

py-radiate/radiate/handlers.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from typing import Any, Callable
44
from radiate.radiate import PySubscriber, PyEngineEvent
55
from radiate.wrapper import PyObject
6+
from radiate.metrics import MetricSet
67

78

89
class EventType(Enum):
@@ -106,9 +107,12 @@ def value(self) -> Any:
106107
"""
107108
return self.__backend__().value()
108109

109-
def metrics(self) -> dict[str, Any] | None:
110+
def metrics(self) -> MetricSet | None:
110111
"""
111112
Get the metrics of the event.
112113
:return: The metrics of the event.
113114
"""
114-
return self.__backend__().metrics()
115+
metrics = self.__backend__().metrics()
116+
if metrics is None:
117+
return None
118+
return MetricSet.from_rust(metrics)

0 commit comments

Comments
 (0)