From 10d380161120f65c8353e156c173247e8b89d458 Mon Sep 17 00:00:00 2001
From: Luke McCombs <lmccombs@vols.utk.edu>
Date: Sat, 21 Dec 2024 16:16:57 -0500
Subject: [PATCH 1/2] Fix prob_bin behavior

---
 pymoo/operators/crossover/sbx.py | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/pymoo/operators/crossover/sbx.py b/pymoo/operators/crossover/sbx.py
index 41a1c025a..a75948042 100644
--- a/pymoo/operators/crossover/sbx.py
+++ b/pymoo/operators/crossover/sbx.py
@@ -26,8 +26,8 @@ def cross_sbx(X, xl, xu, eta, prob_var, prob_bin, eps=1.0e-14):
     cross[:, xl == xu] = False
 
     # assign y1 the smaller and y2 the larger value
-    y1 = np.min(X, axis=0)[cross]
-    y2 = np.max(X, axis=0)[cross]
+    p1_low = X[0] < X[1]
+    y1, y2 = np.where(p1_low[None, ...], X, X[::-1])[:, cross]
 
     # mask all the values that should be crossovered
     _xl = np.repeat(xl[None, :], n_matings, axis=0)[cross]
@@ -61,10 +61,8 @@ def calc_betaq(beta):
     c2 = 0.5 * ((y1 + y2) + betaq * delta)
 
     # with the given probability either assign the value from the first or second parent
-    b = np.random.random(len(prob_bin)) < prob_bin
-    tmp = np.copy(c1[b])
-    c1[b] = c2[b]
-    c2[b] = tmp
+    b = (np.random.random(len(prob_bin)) < prob_bin) == p1_low[cross]
+    c1, c2 = np.where(b[None, ...], [c1, c2], [c2, c1])
 
     # first copy the unmodified parents
     Q = np.copy(X)

From ca8370ed841c22ecd0c1101c607256746d15f7f0 Mon Sep 17 00:00:00 2001
From: Luke McCombs <lmccombs@fastmail.com>
Date: Mon, 13 Jan 2025 13:39:46 -0500
Subject: [PATCH 2/2] Fewer masks

---
 pymoo/operators/crossover/sbx.py | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/pymoo/operators/crossover/sbx.py b/pymoo/operators/crossover/sbx.py
index a75948042..d56e6973d 100644
--- a/pymoo/operators/crossover/sbx.py
+++ b/pymoo/operators/crossover/sbx.py
@@ -26,8 +26,9 @@ def cross_sbx(X, xl, xu, eta, prob_var, prob_bin, eps=1.0e-14):
     cross[:, xl == xu] = False
 
     # assign y1 the smaller and y2 the larger value
-    p1_low = X[0] < X[1]
-    y1, y2 = np.where(p1_low[None, ...], X, X[::-1])[:, cross]
+    X_cross = X[:, cross]
+    p1_low = X_cross[0] < X_cross[1]
+    y1, y2 = np.where(p1_low[None,], X_cross, X_cross[::-1])
 
     # mask all the values that should be crossovered
     _xl = np.repeat(xl[None, :], n_matings, axis=0)[cross]
@@ -61,15 +62,15 @@ def calc_betaq(beta):
     c2 = 0.5 * ((y1 + y2) + betaq * delta)
 
     # with the given probability either assign the value from the first or second parent
-    b = (np.random.random(len(prob_bin)) < prob_bin) == p1_low[cross]
-    c1, c2 = np.where(b[None, ...], [c1, c2], [c2, c1])
+    b = (np.random.random(len(prob_bin)) < prob_bin) == p1_low
+    C = np.stack((c1, c2), 0)
+    C = np.where(b[None,], C, C[::-1])
 
     # first copy the unmodified parents
     Q = np.copy(X)
 
     # copy the positions where the crossover was done
-    Q[0, cross] = c1
-    Q[1, cross] = c2
+    Q[:, cross] = C
 
     Q[0] = repair_clamp(Q[0], xl, xu)
     Q[1] = repair_clamp(Q[1], xl, xu)