content: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the License at
   *
   *       http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *
   * See the License for the specific language governing permissions and limitations under the License.
   *
   */
  //TODO suff. stats for first form to be implemented (if required)
  
  package eu.amidst.core.exponentialfamily;
  
  import eu.amidst.core.distribution.ConditionalDistribution;
  import eu.amidst.core.distribution.Multinomial;
  import eu.amidst.core.utils.ArrayVector;
  import eu.amidst.core.utils.SparseVectorDefaultValue;
  import eu.amidst.core.utils.Vector;
  import eu.amidst.core.variables.Assignment;
  import eu.amidst.core.variables.Variable;
  
  import java.util.ArrayList;
  import java.util.Map;
  
  /**
   *
   * This class extends the abstract class {@link EF_ConditionalDistribution} and defines a conditional Multinomial Dirichlet distribution.
   * variable in exponential canonical form. It used for Bayesian learning tasks.
   *
   * <p> For further details about how exponential family models are considered in this toolbox, take a look at the following paper:
   * <i>Representation, Inference and Learning of Bayesian Networks as Conjugate Exponential Family Models. Technical Report.</i>
   * (<a href="http://amidst.github.io/toolbox/docs/ce-BNs.pdf">pdf</a>) </p>
   */
  public class EF_SparseMultinomial_Dirichlet extends EF_ConditionalDistribution{
  
      /** Represents the conditioning Dirichlet variable of this EF_Multinomial_Dirichlet distribution. */
      Variable dirichletVariable;
  
      /** Represents the number of states of the main multinomial variable. */
      int nOfStates;
  
      /**
       * Creates a new EF_Multinomial_Dirichlet distribution for given Multinomial and Dirichlet variables.
       * @param var a {@link Variable} object with a Multinomial distribution type.
       * @param dirichletVariable a {@link Variable} object with a Dirichlet distribution type.
       */
      public EF_SparseMultinomial_Dirichlet(Variable var, Variable dirichletVariable) {
  
          if (!var.isSparseMultinomial()) {
              throw new UnsupportedOperationException("Creating a Multinomial_Dirichlet EF distribution for a non-multinomial variable.");
          }
          if (!dirichletVariable.isDirichletParameter()) {
              throw new UnsupportedOperationException("Creating a Multinomial_Dirichlet EF distribution with a non-dirichlet variable.");
          }
  
          if (var.getNumberOfStates()!=dirichletVariable.getNumberOfStates()) {
              throw new UnsupportedOperationException("Creating a Multinomial_Dirichlet EF distribution with differetnt number of states.");
          }
  
  
          this.var=var;
          nOfStates = var.getNumberOfStates();
          this.dirichletVariable = dirichletVariable;
          this.parents = new ArrayList<>();
          this.parents.add(dirichletVariable);
      }
  
      /**
       * Returns the Dirichlet conditioning variable of this EF_Multinomial_Dirichlet distribution.
       * @return a {@link Variable} object.
       */
      public Variable getDirichletVariable() {
          return dirichletVariable;
      }
  
  
      public double computeLogProbability(Map<Variable,MomentParameters> momentChildCoParents){
  
          MomentParameters momentParametersDir = momentChildCoParents.get(this.dirichletVariable);
          SparseVectorDefaultValue momentParametersWord = (SparseVectorDefaultValue)momentChildCoParents.get(this.getVariable());
  
          if (momentParametersWord.getDefaultValue()==0 && momentParametersWord.getNonZeroEntries().size()==1) {
              Integer integer = momentParametersWord.getNonZeroEntries().iterator().next();
  
              return momentParametersDir.get(integer)*momentParametersWord.get(integer);
  
          }else {
              return this.getExpectedNaturalFromParents(momentChildCoParents).dotProduct(momentChildCoParents.get(this.getVariable())) - this.getExpectedLogNormalizer(momentChildCoParents);
          }
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public double getExpectedLogNormalizer(Map<Variable, MomentParameters> momentParents) {
          return 0.0;
      }
  
  
      /**
       * {@inheritDoc}
       */
      @Override
      public NaturalParameters getExpectedNaturalFromParents(Map<Variable, MomentParameters> momentParents) {
  
          NaturalParameters naturalParameters = new ArrayVector(this.nOfStates);
          naturalParameters.copy(momentParents.get(this.dirichletVariable));
  
          return naturalParameters;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public NaturalParameters getExpectedNaturalToParent(Variable parent, Map<Variable, MomentParameters> momentChildCoParents) {
  
  
          NaturalParameters naturalParameters = new SparseVectorDefaultValue(this.nOfStates,0.0);
  
          naturalParameters.copy(momentChildCoParents.get(this.var));
  
          return naturalParameters;
  
  /*      /*NaturalParameters naturalParameters = new ArrayVector(this.nOfStates);
  
          SparseVectorDefaultValue moments = (SparseVectorDefaultValue)momentChildCoParents.get(this.var);
  
          if (moments.getDefaultValue()!=0)
              throw new UnsupportedOperationException(" ");
  
          for (Integer integer : moments.getNonZeroEntries()) {
              naturalParameters.set(integer,moments.get(integer));
          }
  
          return naturalParameters;*/
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public <E extends ConditionalDistribution> E toConditionalDistribution() {
          throw new UnsupportedOperationException("This method does not make sense. Parameter variables can not be converted. Use instead" +
                  "public ConditionalDistribution toConditionalDistribution(Map<Variable, Vector> expectedValueParameterVariables);");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public double getExpectedLogNormalizer(Variable parent, Map<Variable, MomentParameters> momentChildCoParents) {
          throw new UnsupportedOperationException("No Implemented. This method is no really needed");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void updateNaturalFromMomentParameters() {
          throw new UnsupportedOperationException("No Implemented. EF_Multinomial_Dirichlet distribution should only be used for learning, use EF_Multinomial for inference.");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public void updateMomentFromNaturalParameters() {
          throw new UnsupportedOperationException("No Implemented. EF_Multinomial_Dirichlet distribution should only be used for learning, use EF_Multinomial for inference.");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public SufficientStatistics getSufficientStatistics(Assignment data) {
          throw new UnsupportedOperationException("No Implemented. EF_Multinomial_Dirichlet distribution should only be used for learning, use EF_Multinomial for inference.");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public int sizeOfSufficientStatistics() {
          return 0;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public double computeLogBaseMeasure(Assignment dataInstance) {
          return 0;
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public double computeLogNormalizer() {
          throw new UnsupportedOperationException("No Implemented. EF_Multinomial_Dirichlet distribution should only be used for learning, use EF_Multinomial for inference.");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public Vector createZeroVector() {
          throw new UnsupportedOperationException("No Implemented. EF_Multinomial_Dirichlet distribution should only be used for learning, use EF_Multinomial for inference.");
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public SufficientStatistics createInitSufficientStatistics() {
          throw new UnsupportedOperationException();
      }
  
      /**
       * {@inheritDoc}
       */
      @Override
      public ConditionalDistribution toConditionalDistribution(Map<Variable, Vector> expectedValueParameterVariables) {
          Multinomial multinomial = new Multinomial(this.getVariable());
  
          Vector vector = expectedValueParameterVariables.get(this.dirichletVariable);
  
          for (int i = 0; i < this.nOfStates; i++) {
              multinomial.setProbabilityOfState(i,vector.get(i));
  
          }
          return multinomial;
      }
  }
  