Skip to content

Commit 7c0c3fe

Browse files
committed
Merge branch 'develop'
2 parents 2abe024 + 09a7647 commit 7c0c3fe

File tree

25 files changed

+251
-65
lines changed

25 files changed

+251
-65
lines changed

.github/workflows/publish_py.yaml

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
name: publish
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
os:
10+
description: "Limit to one OS (ubuntu-latest|macos-13|macos-15|windows-latest)"
11+
required: false
12+
default: ""
13+
arch:
14+
description: "Limit to one arch (x86_64|aarch64|arm64|x64)"
15+
required: false
16+
default: ""
17+
py:
18+
description: "Limit to one Python (e.g. 3.12 or 3.13t)"
19+
required: false
20+
default: ""
21+
22+
permissions:
23+
contents: read
24+
id-token: write
25+
26+
env:
27+
CARGO_INCREMENTAL: 0
28+
CARGO_NET_RETRY: 10
29+
RUSTUP_MAX_RETRIES: 10
30+
31+
jobs:
32+
sdist:
33+
runs-on: ubuntu-latest
34+
steps:
35+
- uses: actions/checkout@v4
36+
- uses: actions/setup-python@v5
37+
with: { python-version: '3.12' }
38+
- uses: astral-sh/setup-uv@v6
39+
- name: Build sdist
40+
run: |
41+
cd py-radiate
42+
uvx maturin sdist -o target-sdist
43+
- uses: actions/upload-artifact@v4
44+
with:
45+
name: sdist
46+
path: py-radiate/target-sdist/*.tar.gz
47+
48+
wheels:
49+
continue-on-error: true
50+
name: >-
51+
${{ matrix.os }} • ${{ matrix.arch }} •
52+
${{ contains(matrix.py, 't') && 'nogil' || 'gil' }}
53+
runs-on: ${{ matrix.os }}
54+
strategy:
55+
fail-fast: false
56+
matrix:
57+
include:
58+
# Linux
59+
- { os: ubuntu-latest, arch: x86_64, py: '3.12' }
60+
- { os: ubuntu-latest, arch: aarch64, py: '3.12', target: aarch64-unknown-linux-gnu }
61+
- { os: ubuntu-latest, arch: x86_64, py: '3.13t' }
62+
- { os: ubuntu-latest, arch: aarch64, py: '3.13t', target: aarch64-unknown-linux-gnu }
63+
64+
# macOS
65+
- { os: macos-13, arch: x86_64, py: '3.12' }
66+
- { os: macos-15, arch: arm64, py: '3.12' }
67+
- { os: macos-13, arch: x86_64, py: '3.13t' }
68+
- { os: macos-15, arch: arm64, py: '3.13t' }
69+
70+
# Windows
71+
- { os: windows-latest, arch: x64, py: '3.12' }
72+
- { os: windows-latest, arch: x64, py: '3.13t' }
73+
74+
env:
75+
MATCH: ${{ github.event_name != 'workflow_dispatch' || (
76+
(inputs.os == '' || matrix.os == inputs.os) &&
77+
(inputs.arch == '' || matrix.arch == inputs.arch) &&
78+
(inputs.py == '' || matrix.py == inputs.py)
79+
) }}
80+
81+
steps:
82+
- uses: actions/checkout@v4
83+
if: ${{ env.MATCH }}
84+
85+
- uses: dtolnay/rust-toolchain@stable
86+
if: ${{ env.MATCH }}
87+
88+
- uses: actions/setup-python@v5
89+
if: ${{ env.MATCH }}
90+
with: { python-version: '3.12' }
91+
92+
- uses: astral-sh/setup-uv@v6
93+
if: ${{ env.MATCH }}
94+
95+
- name: Ensure requested interpreter (best effort)
96+
if: ${{ env.MATCH }}
97+
shell: bash
98+
run: |
99+
set -e
100+
uv python install "${{ matrix.py }}" || true
101+
uv python list --only-installed
102+
103+
- name: Select interpreter and flags
104+
id: envsel
105+
if: ${{ env.MATCH }}
106+
shell: bash
107+
run: |
108+
set -e
109+
110+
PY_TAG="${{ matrix.py }}"
111+
112+
if [[ "$PY_TAG" == *t* ]]; then
113+
PY="$(uv python find "$PY_TAG" 2>/dev/null || true)"
114+
FLAGS=""
115+
else
116+
PY="$(which python)"
117+
FLAGS=""
118+
fi
119+
120+
if [[ -z "$PY" ]]; then
121+
echo "skip=1" >> $GITHUB_OUTPUT
122+
exit 0
123+
fi
124+
125+
echo "skip=0" >> $GITHUB_OUTPUT
126+
echo "py=$PY" >> $GITHUB_OUTPUT
127+
echo "flags=$FLAGS" >> $GITHUB_OUTPUT
128+
129+
- name: Build wheel (Linux)
130+
if: ${{ env.MATCH == 'true' && runner.os == 'Linux' && steps.envsel.outputs.skip == '0' }}
131+
uses: PyO3/maturin-action@v1
132+
env:
133+
PYO3_PYTHON: ${{ steps.envsel.outputs.py }}
134+
with:
135+
maturin-version: '1.9.4'
136+
command: build
137+
args: >
138+
--release
139+
-m py-radiate/Cargo.toml
140+
-o dist
141+
${{ steps.envsel.outputs.flags }}
142+
${{ matrix.target && format('--target {0}', matrix.target) || '' }}
143+
manylinux: auto
144+
145+
- name: Build wheel (macOS/Windows)
146+
if: ${{ env.MATCH == 'true' && runner.os != 'Linux' && steps.envsel.outputs.skip == '0' }}
147+
uses: PyO3/maturin-action@v1
148+
env:
149+
PYO3_PYTHON: ${{ steps.envsel.outputs.py }}
150+
with:
151+
maturin-version: '1.9.4'
152+
command: build
153+
args: >
154+
--release
155+
-m py-radiate/Cargo.toml
156+
-o dist
157+
${{ steps.envsel.outputs.flags }}
158+
159+
- uses: actions/upload-artifact@v4
160+
if: always()
161+
with:
162+
name: wheels-${{ matrix.os }}-${{ matrix.arch }}-${{ contains(matrix.py, 't') && 'nogil' || 'gil' }}
163+
path: dist/*.whl
164+
165+
publish:
166+
needs: [ sdist, wheels ]
167+
runs-on: ubuntu-latest
168+
if: ${{ needs.sdist.result == 'success' && startsWith(github.ref, 'refs/tags/v') }}
169+
environment:
170+
name: publish_pypi
171+
steps:
172+
- uses: actions/download-artifact@v4
173+
with:
174+
path: dist
175+
merge-multiple: true
176+
- run: ls -R dist
177+
- name: Publish (Trusted Publisher)
178+
uses: pypa/gh-action-pypi-publish@release/v1
179+
with:
180+
verbose: true
181+
skip-existing: true

crates/radiate-alters/src/crossovers/multipoint.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ pub fn crossover_multi_point<G>(
5959
let mut current_parent = 1;
6060
let mut last_point = 0;
6161

62-
for i in selected_points {
62+
for &mut i in selected_points {
6363
if current_parent == 1 {
64-
chrom_one[last_point..*i].swap_with_slice(&mut chrom_two[last_point..*i]);
64+
chrom_one[last_point..i].swap_with_slice(&mut chrom_two[last_point..i]);
6565
}
6666

6767
current_parent = 3 - current_parent;
68-
last_point = *i;
68+
last_point = i;
6969
}
7070

7171
if current_parent == 1 {

crates/radiate-python/src/any/arithmatic.rs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ macro_rules! bin_numeric_op {
2323
}};
2424
}
2525

26-
/// Like `bin_numeric_op!`, but with integer safe divide (avoid div-by-zero).
2726
macro_rules! bin_numeric_div {
2827
($lhs:expr, $rhs:expr) => {{
2928
use AnyValue::*;
@@ -279,11 +278,11 @@ mod tests {
279278
use super::*;
280279
use AnyValue::*;
281280

282-
fn v(xs: Vec<AnyValue<'static>>) -> AnyValue<'static> {
281+
fn make_vec(xs: Vec<AnyValue<'static>>) -> AnyValue<'static> {
283282
AnyValue::Vector(Box::new(xs))
284283
}
285-
fn s(pairs: Vec<(&'static str, AnyValue<'static>)>) -> AnyValue<'static> {
286-
// Replace crate::Field(...) with your real Field constructor if needed
284+
285+
fn make_struct(pairs: Vec<(&'static str, AnyValue<'static>)>) -> AnyValue<'static> {
287286
let fields = pairs
288287
.into_iter()
289288
.map(|(name, val)| (crate::Field::new(name.into()), val))
@@ -377,50 +376,50 @@ mod tests {
377376
// ---------- Vector elementwise ----------
378377
#[test]
379378
fn vector_elementwise_add_ok() {
380-
let a = v(vec![Int32(1), Int32(2), Int32(3)]);
381-
let b = v(vec![Int32(4), Int32(5), Int32(6)]);
382-
let out = v(vec![Int32(5), Int32(7), Int32(9)]);
379+
let a = make_vec(vec![Int32(1), Int32(2), Int32(3)]);
380+
let b = make_vec(vec![Int32(4), Int32(5), Int32(6)]);
381+
let out = make_vec(vec![Int32(5), Int32(7), Int32(9)]);
383382
assert_eq!(a + b, out);
384383
}
385384

386385
#[test]
387386
fn vector_length_mismatch() {
388-
let a = v(vec![Int32(1), Int32(2)]);
389-
let b = v(vec![Int32(3)]);
387+
let a = make_vec(vec![Int32(1), Int32(2)]);
388+
let b = make_vec(vec![Int32(3)]);
390389
assert_eq!(a + b, Vector(Box::new(vec![Int32(4)])));
391390
}
392391

393392
// ---------- Struct fieldwise ----------
394393
#[test]
395394
fn struct_same_shape_by_order() {
396395
// Current code: length check; name mismatch → per-field Null (keeps left field)
397-
let a = s(vec![("x", Int32(1)), ("y", Int32(2))]);
398-
let b = s(vec![("x", Int32(3)), ("y", Int32(4))]);
399-
let out = s(vec![("x", Int32(4)), ("y", Int32(6))]);
396+
let a = make_struct(vec![("x", Int32(1)), ("y", Int32(2))]);
397+
let b = make_struct(vec![("x", Int32(3)), ("y", Int32(4))]);
398+
let out = make_struct(vec![("x", Int32(4)), ("y", Int32(6))]);
400399
assert_eq!(a + b, out);
401400
}
402401

403402
#[test]
404403
fn struct_length_mismatch_yields_null() {
405-
let a = s(vec![("x", Int32(1))]);
406-
let b = s(vec![("x", Int32(2)), ("y", Int32(3))]);
404+
let a = make_struct(vec![("x", Int32(1))]);
405+
let b = make_struct(vec![("x", Int32(2)), ("y", Int32(3))]);
407406
assert_eq!(a + b, Null);
408407
}
409408

410409
#[test]
411410
fn struct_field_name_mismatch_sets_field_null_under_current_rules() {
412-
let a = s(vec![("x", Int32(1)), ("y", Int32(2))]);
413-
let b = s(vec![("x", Int32(3)), ("z", Int32(9))]);
411+
let a = make_struct(vec![("x", Int32(1)), ("y", Int32(2))]);
412+
let b = make_struct(vec![("x", Int32(3)), ("z", Int32(9))]);
414413
// Current impl: when names differ at a position, that *slot* becomes Null; rest proceed.
415-
let expected = s(vec![("x", Int32(4)), ("y", Null)]);
414+
let expected = make_struct(vec![("x", Int32(4)), ("y", Null)]);
416415
assert_eq!(a + b, expected);
417416
}
418417

419418
#[test]
420419
fn struct_align_by_name_regardless_of_order() {
421-
let a = s(vec![("x", Int32(1)), ("y", Int32(2))]);
422-
let b = s(vec![("y", Int32(4)), ("x", Int32(3))]);
423-
let out = s(vec![("x", Null), ("y", Null)]);
420+
let a = make_struct(vec![("x", Int32(1)), ("y", Int32(2))]);
421+
let b = make_struct(vec![("y", Int32(4)), ("x", Int32(3))]);
422+
let out = make_struct(vec![("x", Null), ("y", Null)]);
424423
assert_eq!(a + b, out);
425424
}
426425

crates/radiate-python/src/any/cell.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
use std::cell::UnsafeCell;
2-
31
use pyo3::{PyResult, Python};
2+
use std::cell::UnsafeCell;
43

54
// Adapted from PYO3 with the only change that
65
// we allow mutable access with when the GIL is held

crates/radiate-python/src/any/value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ impl<'a> AnyValue<'a> {
225225
Float64(v) => Float64(v),
226226
Char(v) => Char(v),
227227
Str(v) => StrOwned(v.to_string()),
228-
Vector(v) => Vector(Box::new(v.into_iter().map(AnyValue::into_static).collect())),
229228
StrOwned(v) => StrOwned(v),
229+
Vector(v) => Vector(Box::new(v.into_iter().map(AnyValue::into_static).collect())),
230230
Binary(v) => Binary(v),
231231
Struct(v) => Struct(
232232
v.into_iter()

crates/radiate-python/src/bindings/codec/any.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ impl PyAnyCodec {
3434
)
3535
})
3636
.with_decoder(move |py, genotype| {
37-
if genotype.len() == 1 && genotype[0].len() == 1 {
38-
return call_creator(py, &genotype[0].get(0)).unwrap();
39-
}
40-
4137
if genotype.len() == 1 {
4238
return PyAnyObject {
4339
inner: PyList::new(

crates/radiate-python/src/events.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,10 @@ where
7777
}
7878

7979
Python::attach(|py| {
80-
let event_dict = self.event_to_py(py, &event).into_py_any(py).unwrap();
80+
let py_event = self.event_to_py(py, &event).into_py_any(py).unwrap();
8181

8282
for handler in subscribers {
83-
let cloned_event = event_dict.clone_ref(py);
83+
let cloned_event = py_event.clone_ref(py);
8484
handler
8585
.function()
8686
.call1(py, (cloned_event,))

crates/radiate/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub use radiate_engines::*;
55
pub use radiate_gp::*;
66

77
pub mod prelude {
8+
pub use radiate_core::{RadiateError, error::RadiateResult};
89
pub use radiate_engines::*;
910

1011
#[cfg(feature = "gp")]

docs/index.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ This simple maximizing problem demonstrates how to use Radiate to solve a string
118118
})
119119
.build();
120120

121-
let result = engine.run(|ctx| {
122-
let best_as_string = ctx.best.iter().collect::<String>();
123-
println!("[ {:?} ]: {:?}", ctx.index(), best_as_string);
121+
let result = engine.run(|generation| {
122+
let best_as_string = generation.value().iter().collect::<String>();
123+
println!("[ {:?} ]: {:?}", generation.index(), best_as_string);
124124

125-
ctx.score().as_usize() == target.len()
125+
generation.score().as_usize() == target.len()
126126
});
127127

128128
println!("{:?}", result);

docs/source/fitness.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ You can implement your own behavioral descriptors by implementing the `Novelty`
304304
// ... rest of impl ...
305305

306306
impl Novelty<MyModel> for MyModelBehaviorDescriptor {
307-
fn description(&self, individual: &MyModel) -> Self::Descriptor {
307+
fn description(&self, individual: &MyModel) -> Vec<f32> {
308308
// Return behavioral characteristics (e.g., outputs on test cases)
309309
individual.get_behavior_vector()
310310
}

0 commit comments

Comments
 (0)