CCreateDistribution.java
/*
* @cond LICENSE
* ######################################################################################
* # LGPL License #
* # #
* # This file is part of the LightJason AgentSpeak(L++) #
* # Copyright (c) 2015-19, LightJason (info@lightjason.org) #
* # This program is free software: you can redistribute it and/or modify #
* # it under the terms of the GNU Lesser General Public License as #
* # published by the Free Software Foundation, either version 3 of the #
* # License, or (at your option) any later version. #
* # #
* # This program is distributed in the hope that it will be useful, #
* # but WITHOUT ANY WARRANTY; without even the implied warranty of #
* # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
* # GNU Lesser General Public License for more details. #
* # #
* # You should have received a copy of the GNU Lesser General Public License #
* # along with this program. If not, see http://www.gnu.org/licenses/ #
* ######################################################################################
* @endcond
*/
package org.lightjason.agentspeak.action.builtin.math.statistic;
import org.apache.commons.math3.distribution.AbstractRealDistribution;
import org.apache.commons.math3.distribution.BetaDistribution;
import org.apache.commons.math3.distribution.CauchyDistribution;
import org.apache.commons.math3.distribution.ChiSquaredDistribution;
import org.apache.commons.math3.distribution.ExponentialDistribution;
import org.apache.commons.math3.distribution.FDistribution;
import org.apache.commons.math3.distribution.GammaDistribution;
import org.apache.commons.math3.distribution.GumbelDistribution;
import org.apache.commons.math3.distribution.LaplaceDistribution;
import org.apache.commons.math3.distribution.LevyDistribution;
import org.apache.commons.math3.distribution.LogNormalDistribution;
import org.apache.commons.math3.distribution.LogisticDistribution;
import org.apache.commons.math3.distribution.NakagamiDistribution;
import org.apache.commons.math3.distribution.NormalDistribution;
import org.apache.commons.math3.distribution.ParetoDistribution;
import org.apache.commons.math3.distribution.TDistribution;
import org.apache.commons.math3.distribution.TriangularDistribution;
import org.apache.commons.math3.distribution.UniformRealDistribution;
import org.apache.commons.math3.distribution.WeibullDistribution;
import org.apache.commons.math3.random.ISAACRandom;
import org.apache.commons.math3.random.JDKRandomGenerator;
import org.apache.commons.math3.random.MersenneTwister;
import org.apache.commons.math3.random.RandomGenerator;
import org.apache.commons.math3.random.SynchronizedRandomGenerator;
import org.apache.commons.math3.random.Well1024a;
import org.apache.commons.math3.random.Well19937a;
import org.apache.commons.math3.random.Well19937c;
import org.apache.commons.math3.random.Well44497a;
import org.apache.commons.math3.random.Well44497b;
import org.apache.commons.math3.random.Well512a;
import org.lightjason.agentspeak.action.builtin.IBuiltinAction;
import org.lightjason.agentspeak.error.CIllegalStateException;
import org.lightjason.agentspeak.language.CCommon;
import org.lightjason.agentspeak.language.CRawTerm;
import org.lightjason.agentspeak.language.ITerm;
import org.lightjason.agentspeak.language.execution.IContext;
import org.lightjason.agentspeak.language.fuzzy.CFuzzyValue;
import org.lightjason.agentspeak.language.fuzzy.IFuzzyValue;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* creates a distribution object.
* The action creates a distribution objects, with an individual
* pseudo-random generator and different distribution paramter,
* the action never fails, the following distributions are supported
* with the following number of numeric arguments
*
* + beta distribution with 2 arguments (\f$ \alpha \f$ and \f$ \beta \f$)
* + cauchy distribution with 2 arguments (media and scale)
* + chi-square distribution with 1 argument (degree off freedom)
* + exponential distribution with 1 argument (mean)
* + f distribution with 2 arguments (degrees of freedom, denominator degrees of freedom)
* + gamma distribution with 2 arguments (shape and scale)
* + gumble distribution with 2 arguments (\f$ \mu \f$ and \f$ \beta \f$)
* + laplace distirbution with 2 arguments (\f$ \mu \f$ and \f$ \beta \f$)
* + levy distirbution with 2 arguments (\f$ \mu \f$ and \f$ c \f$)
* + logistic distirbution with 2 arguments (\f$ \mu \f$ and \f$ s \f$)
* + lognormal distirbution with 2 arguments (scale and shape)
* + nakagami distribution with 2 arguments (\f$ \mu \f$ and \f$ \omega \f$)
* + normal distribution with 2 arguments (expected value, variance)
* + pareto distribution with 2 arguments (scale and shape)
* + t distribution with 1 argument (degrees of freedom)
* + triangular distribution with 3 arguments (a, b, c)
* + uniform distribution with 2 arguments (lower and upper)
* + weibull distribution with 2 arguments (\f$ \alpha \f$ and \f$ \beta \f$)
*
* The following pseudo-random number generators are supported:
*
* + mersennetwister (default)
* + synchronizedmersennetwister
* + isaac
* + synchronizedisaac
* + internal
* + synchronizedinternal
* + well512a
* + synchronizedwell512a
* + well1024a
* + synchronizedwell1024a
* + well19937a
* + synchronizedwell19937a
* + well19937c
* + synchronizedwell19937c
* + well4449a
* + synchronizedwell4449a
* + well44497b
* + synchronizedwell44497b
*
* {@code [D1|D2] = math/statistic/createdistribution( "normal", 20, 10, ["beta", "isaac", [8, 12]] );}
* @see https://en.wikipedia.org/wiki/Beta_distribution
* @see https://en.wikipedia.org/wiki/Cauchy_distribution
* @see https://en.wikipedia.org/wiki/Chi-squared_distribution
* @see https://en.wikipedia.org/wiki/Exponential_distribution
* @see https://en.wikipedia.org/wiki/F-distribution
* @see https://en.wikipedia.org/wiki/Gamma_distribution
* @see https://en.wikipedia.org/wiki/Gumbel_distribution
* @see https://en.wikipedia.org/wiki/L%C3%A9vy_distribution
* @see https://en.wikipedia.org/wiki/Logistic_distribution
* @see https://en.wikipedia.org/wiki/Log-normal_distribution
* @see https://en.wikipedia.org/wiki/Nakagami_distribution
* @see https://en.wikipedia.org/wiki/Normal_distribution
* @see https://en.wikipedia.org/wiki/Pareto_distribution
* @see https://en.wikipedia.org/wiki/Student%27s_t-distribution
* @see https://en.wikipedia.org/wiki/Triangular_distribution
* @see https://en.wikipedia.org/wiki/Uniform_distribution_(continuous)
* @see https://en.wikipedia.org/wiki/Weibull_distribution
*/
public final class CCreateDistribution extends IBuiltinAction
{
/**
* serial id
*/
private static final long serialVersionUID = 614460992147593598L;
/**
* ctor
*/
public CCreateDistribution()
{
super( 3 );
}
@Nonnegative
@Override
public final int minimalArgumentNumber()
{
return 1;
}
@Nonnull
@Override
public final IFuzzyValue<Boolean> execute( final boolean p_parallel, @Nonnull final IContext p_context,
@Nonnull final List<ITerm> p_argument, @Nonnull final List<ITerm> p_return )
{
final List<ITerm> l_arguments = CCommon.flatten( p_argument ).collect( Collectors.toList() );
IntStream.range( 0, l_arguments.size() )
.filter( i -> CCommon.rawvalueAssignableTo( l_arguments.get( i ), String.class ) )
.mapToObj( i -> new AbstractMap.SimpleImmutableEntry<>( i, l_arguments.get( i ).<String>raw() ) )
.filter( i -> EDistribution.exist( i.getValue() ) )
.map( i -> new AbstractMap.SimpleImmutableEntry<>( i.getKey(), EDistribution.from( i.getValue() ) ) )
.map( i ->
{
// check if next argument to the distribution name a generator name
final int l_skip;
final EGenerator l_generator;
if ( ( i.getKey() < l_arguments.size() - 1 ) && ( CCommon.rawvalueAssignableTo( l_arguments.get( i.getKey() + 1 ), String.class ) ) )
{
l_skip = 1;
l_generator = EGenerator.from( l_arguments.get( i.getKey() + 1 ).<String>raw() );
}
else
{
l_skip = 0;
l_generator = EGenerator.MERSENNETWISTER;
}
// generate distribution object, arguments after distribution are the initialize parameter
return i.getValue()
.get(
l_generator.get(),
l_arguments.stream()
.skip( i.getKey() + 1 + l_skip )
.limit( i.getValue().getArgumentNumber() )
.map( ITerm::<Number>raw )
.mapToDouble( Number::doubleValue )
.toArray()
);
} )
.map( CRawTerm::from )
.forEach( p_return::add );
return CFuzzyValue.from( true );
}
/**
* usable distributions
*/
private enum EDistribution
{
BETA( 2 ),
CAUCHY( 2 ),
CHISQUARE( 1 ),
EXPONENTIAL( 1 ),
F( 2 ),
GAMMA( 2 ),
GUMBLE( 2 ),
LAPLACE( 2 ),
LEVY( 2 ),
LOGISTIC( 2 ),
LOGNORMAL( 2 ),
NAKAGAMI( 2 ),
NORMAL( 2 ),
PARETO( 2 ),
T( 1 ),
TRIANGULAR( 3 ),
UNIFORM( 2 ),
WEIBULL( 2 );
/**
* enum name list
*/
private static final Set<String> NAMES = Collections.unmodifiableSet(
Arrays.stream( EDistribution.values() )
.map( i -> i.name().toUpperCase( Locale.ROOT ) )
.collect( Collectors.toSet() )
);
/**
* number of arguments
*/
private final int m_arguments;
/**
* ctor
*
* @param p_arguments number of arguments
*/
EDistribution( final int p_arguments )
{
m_arguments = p_arguments;
}
/**
* additional factory
*
* @param p_value string
* @return enum
*/
@Nonnull
public static EDistribution from( @Nonnull final String p_value )
{
return EDistribution.valueOf( p_value.trim().toUpperCase( Locale.ROOT ) );
}
/**
* checks if a name exists within the enum
*
* @param p_value string name
* @return exist boolean
*/
public static boolean exist( @Nonnull final String p_value )
{
return NAMES.contains( p_value.trim().toUpperCase( Locale.ROOT ) );
}
/**
* return number of arguments
*
* @return argument number
*/
public final int getArgumentNumber()
{
return m_arguments;
}
/**
* returns the distribution object
*
* @param p_generator random generator
* @param p_arguments arguments
* @return real distribution
*/
@Nonnull
public final AbstractRealDistribution get( @Nonnull final RandomGenerator p_generator, final double[] p_arguments )
{
switch ( this )
{
case BETA:
return new BetaDistribution( p_generator, p_arguments[0], p_arguments[1] );
case CAUCHY:
return new CauchyDistribution( p_generator, p_arguments[0], p_arguments[1] );
case CHISQUARE:
return new ChiSquaredDistribution( p_generator, p_arguments[0] );
case EXPONENTIAL:
return new ExponentialDistribution( p_generator, p_arguments[0] );
case F:
return new FDistribution( p_generator, p_arguments[0], p_arguments[1] );
case GAMMA:
return new GammaDistribution( p_generator, p_arguments[0], p_arguments[1] );
case GUMBLE:
return new GumbelDistribution( p_generator, p_arguments[0], p_arguments[1] );
case LAPLACE:
return new LaplaceDistribution( p_generator, p_arguments[0], p_arguments[1] );
case LEVY:
return new LevyDistribution( p_generator, p_arguments[0], p_arguments[1] );
case LOGISTIC:
return new LogisticDistribution( p_generator, p_arguments[0], p_arguments[1] );
case LOGNORMAL:
return new LogNormalDistribution( p_generator, p_arguments[0], p_arguments[1] );
case NAKAGAMI:
return new NakagamiDistribution(
p_generator, p_arguments[0], p_arguments[1],
NakagamiDistribution.DEFAULT_INVERSE_ABSOLUTE_ACCURACY
);
case NORMAL:
return new NormalDistribution( p_generator, p_arguments[0], p_arguments[1] );
case PARETO:
return new ParetoDistribution( p_generator, p_arguments[0], p_arguments[1] );
case T:
return new TDistribution( p_generator, p_arguments[0] );
case TRIANGULAR:
return new TriangularDistribution( p_generator, p_arguments[0], p_arguments[1], p_arguments[2] );
case UNIFORM:
return new UniformRealDistribution( p_generator, p_arguments[0], p_arguments[1] );
case WEIBULL:
return new WeibullDistribution( p_generator, p_arguments[0], p_arguments[1] );
default:
throw new CIllegalStateException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "unknown", this ) );
}
}
}
/**
* number generator
*/
private enum EGenerator
{
MERSENNETWISTER,
SYNCHRONIZEDMERSENNETWISTER,
ISAAC,
SYNCHRONIZEDISAAC,
INTERNAL,
SYNCHRONIZEDINTERNAL,
WELL512A,
SYNCHRONIZEDWELL512A,
WELL1024A,
SYNCHRONIZEDWELL1024A,
WELL19937A,
SYNCHRONIZEDWELL19937A,
WELL19937C,
SYNCHRONIZEDWELL19937C,
WELL4449A,
SYNCHRONIZEDWELL4449A,
WELL44497B,
SYNCHRONIZEDWELL44497B;
/**
* additional factory
*
* @param p_value string
* @return enum
*/
@Nonnull
public static EGenerator from( @Nonnull final String p_value )
{
return EGenerator.valueOf( p_value.trim().toUpperCase( Locale.ROOT ) );
}
/**
* returns a number generator
*
* @return generator
*/
@Nonnull
public final RandomGenerator get()
{
switch ( this )
{
case MERSENNETWISTER:
return new MersenneTwister();
case SYNCHRONIZEDMERSENNETWISTER:
return new SynchronizedRandomGenerator( new MersenneTwister() );
case ISAAC:
return new ISAACRandom();
case SYNCHRONIZEDISAAC:
return new SynchronizedRandomGenerator( new ISAACRandom() );
case INTERNAL:
return new JDKRandomGenerator();
case SYNCHRONIZEDINTERNAL:
return new SynchronizedRandomGenerator( new JDKRandomGenerator() );
case WELL512A:
return new Well512a();
case SYNCHRONIZEDWELL512A:
return new SynchronizedRandomGenerator( new Well512a() );
case WELL1024A:
return new Well1024a();
case SYNCHRONIZEDWELL1024A:
return new SynchronizedRandomGenerator( new Well1024a() );
case WELL19937A:
return new Well19937a();
case SYNCHRONIZEDWELL19937A:
return new SynchronizedRandomGenerator( new Well19937a() );
case WELL19937C:
return new Well19937c();
case SYNCHRONIZEDWELL19937C:
return new SynchronizedRandomGenerator( new Well19937c() );
case WELL4449A:
return new Well44497a();
case SYNCHRONIZEDWELL4449A:
return new SynchronizedRandomGenerator( new Well44497a() );
case WELL44497B:
return new Well44497b();
case SYNCHRONIZEDWELL44497B:
return new SynchronizedRandomGenerator( new Well44497b() );
default:
throw new CIllegalStateException( org.lightjason.agentspeak.common.CCommon.languagestring( this, "unknown", this ) );
}
}
}
}