package statalign.model.ext.plugins.structalign; import java.util.HashMap; import java.util.List; import java.util.ArrayList; import statalign.base.Tree; import statalign.base.Utils; import statalign.mcmc.ContinuousPositiveParameterMove; import statalign.mcmc.ParameterInterface; import statalign.mcmc.PriorDistribution; import statalign.mcmc.ProposalDistribution; import statalign.model.ext.plugins.StructAlign; public class ContinuousPositiveStructAlignMove extends ContinuousPositiveParameterMove { StructAlign structAlign; double[][] oldcovar; //private HashMap<Integer, MultiNormCholesky> oldMultiNorms; // TODO handle this inside StructAlign //private HashMap<Integer, MultiNormCholesky> oldMultiNormsLocal; // TODO handle this inside StructAlign boolean oldFixedToParent; List<HierarchicalContinuousPositiveStructAlignMove> parentPriors = null; /** * <code>true</code> if this parameter is currently constrained to be * equal to its parent (where uniquely defined). This is used in cases * such as spike and slab priors. */ boolean fixedToParent = false; public StructAlignMoveParams moveParams = new StructAlignMoveParams(); public void addParent(HierarchicalContinuousPositiveStructAlignMove p) { if (parentPriors == null) { parentPriors = new ArrayList<HierarchicalContinuousPositiveStructAlignMove>(); } parentPriors.add(p); } public ContinuousPositiveStructAlignMove (StructAlign s, ParameterInterface p, PriorDistribution<Double> pr, ProposalDistribution<Double> prop, String n) { super(s,p,pr,prop,n); structAlign = s; } public void copyState(Object externalState) { super.copyState(externalState); oldcovar = structAlign.fullCovar; // TODO handle in more abstract fashion oldll = structAlign.curLogLike; // TODO handle in more abstract fashion //oldMultiNorms = structAlign.multiNorms; // TODO handle in more abstract fashion structAlign.beforeContinuousParamChange(tree); oldFixedToParent = fixedToParent; } @Override public double proposal(Object externalState) { if (externalState instanceof Tree) { if (tree == null) { tree = (Tree) externalState; } } else { throw new IllegalArgumentException("ContinuousPositiveStructAlignMove.proposal must take an argument of type Tree."); } proposalDistribution.updateProposal(proposalWidthControlVariable,param.get()); double logProposalDensity = 0; boolean allowKeepingOfParameter = false; if (parentPriors != null) { for (HierarchicalContinuousPositiveStructAlignMove parent : parentPriors) { if (parent.allowSpikeSelection) { double[] spikeParams = parent.spikeParams; double m = (double) parent.countFixedToParent(); double n = (double) parent.countChildren(); allowKeepingOfParameter = owner.isBurnin() & (n-m < 2); // In this case the hierarchical variance could become very small, // so we need to allow switching of z_k to zero without resampling // its parameter, during the burnin (where no adjustment to // the M-H ratio is strictly necessary) double fixProb = spikeParams[0]/(spikeParams[0]+spikeParams[1]); fixedToParent = (Utils.generator.nextDouble()<fixProb); if (fixedToParent) { param.set(parent.param.get()); if (!oldFixedToParent) { // z_k = 0 to begin with // Ratio of proposal probabilities for z_k logProposalDensity += Math.log((1-fixProb)/fixProb); // Then also include ratio of priors for the vector z logProposalDensity += Math.log((n-m)/(m+1) * (spikeParams[0]+m) / (spikeParams[1]+n-m-1)); } } else { if (oldFixedToParent) { // z_k = 1 to begin with // Ratio of proposal probabilities for z_k logProposalDensity += Math.log(fixProb/(1-fixProb)); // Then also include ratio of priors for the vector z logProposalDensity += Math.log(m/(n-m+1) * (spikeParams[1]+n-m) / (spikeParams[0]+m-1)); } } // Assume only one parent allows spikes, and break once we've // found it. break; } } } moveParams.setFixedToParent(fixedToParent); if (!allowKeepingOfParameter || (Utils.generator.nextDouble() < 0.8)) { // If allowKeepingOfParameter, we still resample the parameter // with 0.8 probability, to cover the cases where the non-spike // density is lower at the mode. In the cases where it's higher, // and the variance is very low (less likely), then we can // keep the current parameter and just allow switching to the higher // density. if (!fixedToParent) param.set(proposalDistribution.sample()); if (param.get() < minValue || param.get() > maxValue) { return(Double.NEGATIVE_INFINITY); } /** - log p(new | old) */ if (!fixedToParent) logProposalDensity -= proposalDistribution.logDensity(param.get()); proposalDistribution.updateProposal(proposalWidthControlVariable,param.get()); /** + log p(old | new) */ if (!oldFixedToParent) logProposalDensity += proposalDistribution.logDensity(oldpar); } return logProposalDensity; } @Override public double logPriorDensity(Object externalState) { if (param.get() < minValue) { return(Double.NEGATIVE_INFINITY); } else { // Contribution from independent prior on this variable double logDensity = prior.logDensityUnnormalised(param.get()); // Since we're only using this in ratios, there's no // need to compute the normalising constant, which is good, // because some priors may be improper. // NB be careful with this though -- an improper prior should // only be used if the posterior can be shown to be proper. // Then add the contribution from the hierarchical prior if (!fixedToParent && parentPriors != null) { for (HierarchicalContinuousPositiveStructAlignMove parent : parentPriors) { logDensity += parent.getLogChildDensity(this); // The normalising constant of this density will depend // on the parent, so we may need the normalised density here. } } return logDensity; } } public void updateLikelihood(Object externalState) { if (!(fixedToParent && oldFixedToParent) && param.get() > minValue) { owner.setLogLike( structAlign.logLikeContinuousParamChange(tree) ); } } public void restoreState(Object externalState) { super.restoreState(externalState); if (!structAlign.globalSigmaSpike || !(fixedToParent && oldFixedToParent)) { structAlign.fullCovar = oldcovar; // TODO handle in more abstract fashion structAlign.curLogLike = oldll; // TODO handle in more abstract fashion //structAlign.multiNorms = oldMultiNorms; // TODO handle in more abstract fashion //System.out.println(lastMoveAccepted); structAlign.afterContinuousParamChange(tree, false); // Can't refer to lastMoveAccepted here, because the restoreState // function may be called from an McmcCombinationMove. if (structAlign.globalSigmaSpike) { fixedToParent = oldFixedToParent; moveParams.setFixedToParent(fixedToParent); } } } public void setParam(double x) { param.set(x); } public void afterMove(Object externalState) { super.afterMove(externalState); autoTune = !fixedToParent; if (fixedToParent) proposalWidthControlVariable = parentPriors.get(0).proposalWidthControlVariable; } }