001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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 static java.lang.Math.min; 023import static java.lang.Math.sqrt; 024 025import io.jenetics.ext.util.Tree; 026 027import io.jenetics.prog.op.Op; 028 029/** 030 * Represents a complexity <em>measure</em> if a given program tree. The 031 * program complexity ensures that simpler programs with similar loss function 032 * values are preferred. It is part of the <em>overall</em> {@link Error} 033 * function. 034 * 035 * <pre>{@code 036 * final Error<Double> error = Error.of(LossFunction::mse, Complexity.ofNodeCount(50)); 037 * }</pre> 038 * 039 * @see LossFunction 040 * @see Error 041 * 042 * @param <T> the sample type 043 * 044 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 045 * @version 5.0 046 * @since 5.0 047 */ 048@FunctionalInterface 049public interface Complexity<T> { 050 051 /** 052 * Calculates the complexity of the current program (possibly) relative 053 * to the actual error value. 054 * 055 * @param program the actual program 056 * @return the measure of the program complexity 057 */ 058 double apply(final Tree<? extends Op<T>, ?> program); 059 060 /** 061 * Return a complexity measure which counts the number of nodes of a program. 062 * 063 * @see #count(Tree, int) 064 * 065 * @param <T> the sample type 066 * @param maxNodeCount the maximal node count. The returned complexity will 067 * be one of the program node count is greater or equal the given 068 * {@code count} 069 * @return a program node count complexity measure 070 * @throws IllegalArgumentException if the max node {@code count} is smaller 071 * than one 072 */ 073 static <T> Complexity<T> ofNodeCount(final int maxNodeCount) { 074 if (maxNodeCount < 1) { 075 throw new IllegalArgumentException( 076 "Max node count must be greater than zero: " + maxNodeCount 077 ); 078 } 079 080 return p -> count(p, maxNodeCount); 081 } 082 083 /** 084 * This method uses the node count of a program tree for calculating its 085 * complexity. The returned node count <em>measure</em> is within the range 086 * of {@code [0, 1]}. If the program contains only one node, zero is returned. 087 * If the node count is bigger or equal {@code maxNodes}, one is returned. 088 * <p> 089 * The complexity is calculated in the following way: 090 * <pre>{@code 091 * final double cc = min(program.size() - 1, maxNodes); 092 * return 1.0 - sqrt(1.0 - (cc*cc)/(maxNodes*maxNodes)); 093 * }</pre> 094 * 095 * @see #ofNodeCount(int) 096 * 097 * @param program the program used for the complexity measure 098 * @param maxNodes the maximal expected node count 099 * @return the complexity measure of the given {@code program} 100 * @throws NullPointerException if the given {@code program} is {@code null} 101 * @throws IllegalArgumentException if {@code maxNodes} is smaller than one 102 */ 103 static double count(final Tree<?, ?> program, final int maxNodes) { 104 if (maxNodes < 1) { 105 throw new IllegalArgumentException( 106 "Max node count must be greater than zero: " + maxNodes 107 ); 108 } 109 110 final double cc = min(program.size() - 1, maxNodes); 111 return 1.0 - sqrt(1.0 - (cc*cc)/(maxNodes*maxNodes)); 112 } 113 114}