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