Skip to content

Commit

Permalink
Refactored RealVectorInitializer based on RefactorFirst analysis (#770)
Browse files Browse the repository at this point in the history
* Refactored RealVectorInitializer

* refactored RealVectorInitializer

* Update CHANGELOG.md
  • Loading branch information
cicirello authored Dec 12, 2024
1 parent 9d56a83 commit a7f2312
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 68 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased] - 2024-11-11
## [Unreleased] - 2024-12-12

### Added

Expand All @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* NaiveGenerationalEvolutionaryAlgorithm removed. This is not a breaking change. See changelog and release notes for release 6.3.0, where it was simultaneously introduced and deprecated for reason, which also indicates what should be used instead.

### Fixed
* Refactored RealVectorInitializer based on RefactorFirst analysis.

### Dependencies
* Bump org.cicirello:rho-mu from 4.1.0 to 4.2.0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Chips-n-Salsa: A library of parallel self-adaptive local search algorithms.
* Copyright (C) 2002-2023 Vincent A. Cicirello
* Copyright (C) 2002-2024 Vincent A. Cicirello
*
* This file is part of Chips-n-Salsa (https://chips-n-salsa.cicirello.org/).
*
Expand All @@ -20,6 +20,8 @@

package org.cicirello.search.operators.reals;

import java.util.function.BiConsumer;
import java.util.function.Function;
import org.cicirello.math.rand.EnhancedSplittableGenerator;
import org.cicirello.search.internal.RandomnessFactory;
import org.cicirello.search.operators.Initializer;
Expand All @@ -39,12 +41,12 @@
*/
public final class RealVectorInitializer implements Initializer<RealVector> {

private final EnhancedSplittableGenerator generator;
private final double[] x;
private final double[] a;
private final double[] b;
private final BiConsumer<double[], EnhancedSplittableGenerator> initializer;
private final Function<double[], RealVector> creator;
private final double[] min;
private final double[] max;
private final EnhancedSplittableGenerator generator;

/**
* Construct a RealVectorInitializer that generates random solutions such that the values of all n
Expand All @@ -60,12 +62,14 @@ public final class RealVectorInitializer implements Initializer<RealVector> {
* @throws NegativeArraySizeException if n &lt; 0
*/
public RealVectorInitializer(int n, double a, double b) {
if (a >= b) throw new IllegalArgumentException("a must be less than b");
if (a >= b) {
throw new IllegalArgumentException("a must be less than b");
}
x = new double[n];
this.a = new double[] {a};
this.b = new double[] {b};
min = max = null;
initializer = (x, generator) -> singleIntervalInit(generator, x, a, b);
creator = x -> new RealVector(x);
generator = RandomnessFactory.createEnhancedSplittableGenerator();
min = max = null;
}

/**
Expand All @@ -85,16 +89,14 @@ public RealVectorInitializer(int n, double a, double b) {
* i, such that a[i] &ge; b[i].
*/
public RealVectorInitializer(double[] a, double[] b) {
if (a.length != b.length)
throw new IllegalArgumentException("lengths of a and b must be identical");
for (int i = 0; i < a.length; i++) {
if (a[i] >= b[i]) throw new IllegalArgumentException("a[i] must be less than b[i]");
}
validateAB(a, b);
x = new double[a.length];
this.a = a.clone();
this.b = b.clone();
min = max = null;
final double[] a2 = a.clone();
final double[] b2 = b.clone();
initializer = (x, generator) -> multiIntervalInit(generator, x, a2, b2);
creator = x -> new RealVector(x);
generator = RandomnessFactory.createEnhancedSplittableGenerator();
min = max = null;
}

/**
Expand All @@ -114,14 +116,19 @@ public RealVectorInitializer(double[] a, double[] b) {
* @throws NegativeArraySizeException if n &lt; 0
*/
public RealVectorInitializer(int n, double a, double b, double min, double max) {
if (a >= b) throw new IllegalArgumentException("a must be less than b");
if (min > max) throw new IllegalArgumentException("min must be less than or equal to max");
if (a >= b) {
throw new IllegalArgumentException("a must be less than b");
}
if (min > max) {
throw new IllegalArgumentException("min must be less than or equal to max");
}
x = new double[n];
this.a = new double[] {a <= min ? min : a};
this.b = new double[] {b > max ? max + Math.ulp(max) : b};
this.min = new double[] {min};
this.max = new double[] {max};
final double a2 = Math.max(a, min);
final double b2 = Math.min(b, max + Math.ulp(max));
initializer = (x, generator) -> singleIntervalInit(generator, x, a2, b2);
creator = x -> new BoundedRealVector(x, min, max);
generator = RandomnessFactory.createEnhancedSplittableGenerator();
this.min = this.max = null;
}

/**
Expand All @@ -144,22 +151,21 @@ public RealVectorInitializer(int n, double a, double b, double min, double max)
* i, such that a[i] &ge; b[i]; or if min &gt; max.
*/
public RealVectorInitializer(double[] a, double[] b, double min, double max) {
if (a.length != b.length)
throw new IllegalArgumentException("lengths of a and b must be identical");
if (min > max) throw new IllegalArgumentException("min must be less than or equal to max");
for (int i = 0; i < a.length; i++) {
if (a[i] >= b[i]) throw new IllegalArgumentException("a[i] must be less than b[i]");
if (min > max) {
throw new IllegalArgumentException("min must be less than or equal to max");
}
validateAB(a, b);
x = new double[a.length];
this.a = new double[a.length];
this.b = new double[b.length];
for (int i = 0; i < a.length; i++) {
this.a[i] = a[i] <= min ? min : a[i];
this.b[i] = b[i] > max ? max + Math.ulp(max) : b[i];
final double[] a2 = a.clone();
final double[] b2 = b.clone();
for (int i = 0; i < a2.length; i++) {
a2[i] = Math.max(a2[i], min);
b2[i] = Math.min(b2[i], max + Math.ulp(max));
}
this.min = new double[] {min};
this.max = new double[] {max};
initializer = (x, generator) -> multiIntervalInit(generator, x, a2, b2);
creator = x -> new BoundedRealVector(x, min, max);
generator = RandomnessFactory.createEnhancedSplittableGenerator();
this.min = this.max = null;
}

/**
Expand All @@ -184,63 +190,77 @@ public RealVectorInitializer(double[] a, double[] b, double min, double max) {
* i, such that a[i] &ge; b[i] or min[i] &gt; max[i].
*/
public RealVectorInitializer(double[] a, double[] b, double[] min, double[] max) {
if (a.length != b.length || min.length != max.length || a.length != min.length) {
if (min.length != max.length || a.length != min.length) {
throw new IllegalArgumentException("lengths of a, b, min, and max must be identical");
}
for (int i = 0; i < a.length; i++) {
if (a[i] >= b[i]) {
throw new IllegalArgumentException("a[i] must be less than b[i]");
}
validateAB(a, b);
for (int i = 0; i < min.length; i++) {
if (min[i] > max[i]) {
throw new IllegalArgumentException("min[i] must be less than or equal to max[i]");
}
}
x = new double[a.length];
this.a = new double[a.length];
this.b = new double[b.length];
final double[] a2 = a.clone();
final double[] b2 = b.clone();
for (int i = 0; i < a.length; i++) {
this.a[i] = a[i] <= min[i] ? min[i] : a[i];
this.b[i] = b[i] > max[i] ? max[i] + Math.ulp(max[i]) : b[i];
a2[i] = Math.max(a2[i], min[i]);
b2[i] = Math.min(b2[i], max[i] + Math.ulp(max[i]));
}
initializer = (x, generator) -> multiIntervalInit(generator, x, a2, b2);
this.min = min.clone();
this.max = max.clone();
creator = x -> new MultiBoundedRealVector(x);
generator = RandomnessFactory.createEnhancedSplittableGenerator();
}

private RealVectorInitializer(RealVectorInitializer other) {
min = other.min == null ? null : other.min.clone();
max = other.max == null ? null : other.max.clone();
a = other.a.clone();
b = other.b.clone();
x = new double[a.length];
// these should be safe to share
min = other.min;
max = other.max;
initializer = other.initializer;
creator = other.creator;

// each instance needs their own independent instances of these
x = new double[other.x.length];
generator = other.generator.split();
}

@Override
public final RealVector createCandidateSolution() {
if (a.length > 1) {
for (int i = 0; i < x.length; i++) {
x[i] = generator.nextDouble(a[i], b[i]);
}
} else {
for (int i = 0; i < x.length; i++) {
x[i] = generator.nextDouble(a[0], b[0]);
}
}
if (min != null) {
return min.length > 1
? new MultiBoundedRealVector(x)
: new BoundedRealVector(x, min[0], max[0]);
} else {
return new RealVector(x);
}
initializer.accept(x, generator);
return creator.apply(x);
}

@Override
public RealVectorInitializer split() {
return new RealVectorInitializer(this);
}

private static void singleIntervalInit(
EnhancedSplittableGenerator generator, double[] x, double a, double b) {
for (int i = 0; i < x.length; i++) {
x[i] = generator.nextDouble(a, b);
}
}

private static void multiIntervalInit(
EnhancedSplittableGenerator generator, double[] x, double[] a, double[] b) {
for (int i = 0; i < x.length; i++) {
x[i] = generator.nextDouble(a[i], b[i]);
}
}

private static void validateAB(double[] a, double[] b) {
if (a.length != b.length) {
throw new IllegalArgumentException("lengths of a and b must be identical");
}
for (int i = 0; i < a.length; i++) {
if (a[i] >= b[i]) {
throw new IllegalArgumentException("a[i] must be less than b[i]");
}
}
}

/**
* Internal class for representing the input to a multivariate function, where the input values
* are bounded between a specified minimum and maximum value. If an attempt is made to set any of
Expand Down Expand Up @@ -286,9 +306,7 @@ public MultiBoundedRealVector(MultiBoundedRealVector other) {
*/
@Override
public final void set(int i, double value) {
if (value < min[i]) super.set(i, min[i]);
else if (value > max[i]) super.set(i, max[i]);
else super.set(i, value);
super.set(i, Math.max(min[i], Math.min(value, max[i])));
}

/*
Expand Down

0 comments on commit a7f2312

Please sign in to comment.