001/*
002 * Java Genetic Algorithm Library (jenetics-8.1.0).
003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.prog.regression;
021
022import java.util.List;
023
024import io.jenetics.ext.util.CsvSupport;
025
026/**
027 * Represents a sample point used for the symbolic regression task. It consists
028 * of an argument array and a result value. The sample point is comparable
029 * according its {@link #result()} value.
030 *
031 * @implSpec
032 * The dimensionality of the sample point must be at least one, which means
033 * {@code arity() >= 1}.
034 *
035 * @param <T> the sample type
036 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
037 * @version 5.0
038 * @since 5.0
039 */
040public interface Sample<T> {
041
042        /**
043         * Return the dimensionality of the sample point arguments.
044         *
045         * @return the arity of the sample point
046         */
047        int arity();
048
049        /**
050         * Return the argument value with the given {@code index}.
051         *
052         * @see #arity()
053         *
054         * @param index the argument index
055         * @return the argument value with the given {@code index}
056         * @throws ArrayIndexOutOfBoundsException if the given {@code index} is not
057         *         within the given range {@code [0, arity)}
058         */
059        T argAt(final int index);
060
061        /**
062         * Return the result of the sample point.
063         *
064         * @return the result of the sample point
065         */
066        T result();
067
068
069        /* *************************************************************************
070         * Static factory methods.
071         * ************************************************************************/
072
073        /**
074         * Create a new sample point from the given argument and sample result. It
075         * represents the function arguments with the function value:
076         * {@code f: sample[0:sample.length-1] -> sample[sample.length-1]}. The last
077         * array element contains the result, and the first n-1 elements are function
078         * arguments.
079         *
080         * @param <T> the sample type
081         * @param sample the sample point result
082         * @return a new sample point
083         * @throws IllegalArgumentException if the argument array is empty
084         * @throws NullPointerException if the argument array is {@code null}
085         */
086        static <T> Sample<T> of(final T[] sample) {
087                return new ObjectSample<>(sample.clone());
088        }
089
090        /**
091         * Create a new sample point from the given argument and sample result. It
092         * represents the function arguments with the function value:
093         * {@code f: x -> y}
094         *
095         * @param x the argument
096         * @param y the sample point result
097         * @return a new sample point
098         */
099        static Sample<Double> ofDouble(final double x, final double y) {
100                return new DoubleSample(x, y);
101        }
102
103        /**
104         * Create a new sample point from the given argument and sample result. It
105         * represents the function arguments with the function value:
106         * {@code f: (x1, x2) -> y}
107         *
108         * @param x1 the first argument
109         * @param x2 the second argument
110         * @param y the sample point result
111         * @return a new sample point
112         */
113        static Sample<Double> ofDouble(
114                final double x1,
115                final double x2,
116                final double y
117        ) {
118                return new DoubleSample(x1, x2, y);
119        }
120
121        /**
122         * Create a new sample point from the given argument and sample result. It
123         * represents the function arguments with the function value:
124         * {@code f: (x1, x2, x3) -> y}
125         *
126         * @param x1 the first argument
127         * @param x2 the second argument
128         * @param x3 the second argument
129         * @param y the sample point result
130         * @return a new sample point
131         */
132        static Sample<Double> ofDouble(
133                final double x1,
134                final double x2,
135                final double x3,
136                final double y
137        ) {
138                return new DoubleSample(x1, x2, x3, y);
139        }
140
141        /**
142         * Create a new sample point from the given argument and sample result. It
143         * represents the function arguments with the function value:
144         * {@code f: (x1, x2, x3, xn-1) -> y}
145         *
146         * @since 8.1
147         *
148         * @param values the sample data
149         * @return a new sample point
150         */
151        static Sample<Double> ofDouble(final double... values) {
152                return new DoubleSample(values);
153        }
154
155        /**
156         * Parses the given CSV string into a list of double sample points. The
157         * following example shows how to create a list of sample points from a CSV
158         * string.
159         * {@snippet lang=java:
160         * static final List<Sample<Double>> SAMPLES = Sample.parseDoubles("""
161         *      1.0, -8.0000
162         *      0.9, -6.2460
163         *      0.8, -4.7680
164         *      0.7, -3.5420
165         *      0.6, -2.5440
166         *      0.5, -1.7500
167         *      0.4, -1.1360
168         *      0.3, -0.6780
169         *      0.2, -0.3520
170         *      0.1, -0.1340
171         *      0.0,  0.0000
172         *      0.1,  0.0740
173         *      0.2,  0.1120
174         *      0.3,  0.1380
175         *      0.4,  0.1760
176         *      0.5,  0.2500
177         *      0.6,  0.3840
178         *      0.7,  0.6020
179         *      0.8,  0.9280
180         *      0.9,  1.3860
181         *      1.0,  2.0000
182         *      """
183         *  );
184         * }
185         *
186         * @since 8.1
187         *
188         * @param csv the CSV string to parse
189         * @return the parsed double samples
190         */
191        static List<Sample<Double>> parseDoubles(final CharSequence csv) {
192                return CsvSupport.parseDoubles(csv).stream()
193                        .map(Sample::ofDouble)
194                        .toList();
195        }
196
197}