<commit_before>/* Copyright (C) 2008-2014 University of Massachusetts Amherst.
   This file is part of "FACTORIE" (Factor graphs, Imperative, Extensible)
   http://factorie.cs.umass.edu, http://github.com/factorie
   Licensed 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. */
package cc.factorie.app.topics.lda
import cc.factorie._
import cc.factorie.directed._
import cc.factorie.util.DoubleSeq
import scala.Array
import cc.factorie.directed.{DirectedModel, PlatedCategoricalMixture, DiscreteMixtureCounts}
import cc.factorie.variable.{ProportionsVar, DiscreteSeqVariable, DiscreteDomain, CategoricalDomain}

class SparseLDAInferencer(
    val zDomain:DiscreteDomain,
    val wordDomain:CategoricalDomain[String],
    var phiCounts:DiscreteMixtureCounts[String],
    initialAlphas:DoubleSeq,
    initialBeta1:Double,
    model:DirectedModel,
    random: scala.util.Random,
    val localPhiCounts: DiscreteMixtureCounts[String] = null)
{
  var verbosity = 0
  var smoothingOnlyCount = 0; var topicBetaCount = 0; var topicTermCount = 0 // Just diagnostics
  def samplesCount = smoothingOnlyCount + topicBetaCount + topicTermCount
  private var alphas: Array[Double] = null
  private var beta1: Double = 0.01
  private var betaSum = beta1 * wordDomain.size
  var smoothingMass: Double = 0.0
  val numTopics = zDomain.size
  private val cachedCoefficients = new Array[Double](numTopics)
  private val cachedSmoothing = new Array[Double](numTopics)
  private val cachedDenominators = new Array[Double](numTopics)

  private val topicTermScores = new Array[Double](numTopics)

  if (verbosity > 0) println("Finished initializing phiCounts")
  if (verbosity > 5) println("nt "+phiCounts.mixtureCounts.mkString(" "))

  private var _topicDocCounts: Array[Array[Int]] = null
  def topicDocCounts = _topicDocCounts
  // Initialize alphas, beta1, smoothingMass and cachedCoefficients; must be done after phiCounts initialized
  resetSmoothing(initialAlphas, initialBeta1)

  def resetCached():Unit = {
    smoothingMass = 0

    var t = 0
    while(t < numTopics){
      cachedDenominators(t) = 1.0 / (phiCounts.mixtureCounts(t) + betaSum)
      val topicSmoothing = alphas(t) * cachedDenominators(t)
      cachedCoefficients(t) = topicSmoothing
      cachedSmoothing(t) = topicSmoothing
      smoothingMass += topicSmoothing
      t += 1
    }
    smoothingMass *= beta1
  }

  def resetSmoothing(newAlphas:DoubleSeq, newBeta1:Double): Unit = {
    require(numTopics == newAlphas.length)
    require(newBeta1 > 0.0)
    require(newAlphas.forall(_ >= 0.0))
    alphas = newAlphas.toArray
    beta1 = newBeta1
    betaSum = beta1 * wordDomain.size
    //smoothingMass = recalcSmoothingMass
    // The first term in the per-topic summand for q, just missing *n_{w|t}  [Mimno "Sparse LDA"]

    resetCached()
    assert(smoothingMass > 0.0, smoothingMass)
  }

  def initializeHistograms(maxDocSize: Int) {
    _topicDocCounts = Array.ofDim[Int](numTopics, maxDocSize+1)
    for (i <- 0 until numTopics; j <- 0 to maxDocSize) _topicDocCounts(i)(j) = 0
  }

  private def recalcSmoothingMass: Double = {
    // s = \sum_t ( \alpha_t \beta ) / ( |V| \beta + n_t )  [Mimno "Sparse LDA"]
    var i = 0; var sm = 0.0
    while (i < numTopics) { sm += alphas(i) * beta1 / (phiCounts.mixtureCounts(i) + betaSum); i += 1 }
    sm
  }

  def export(phis:Seq[ProportionsVar], beta1:Double = 0.0, numTopics: Int=0): Unit = {
    phis.foreach(_.value.masses.zero())
    for (wi <- 0 until wordDomain.size)  {
      (0 until numTopics).foreach(ti => phis(ti).value.masses.+=(wi, beta1))
      phiCounts(wi).forCounts((ti,count) => phis(ti).value.masses.+=(wi, count))
    }
  }

  def exportThetas(docs:Iterable[Doc]): Unit = {
    for (doc <- docs) {
      val theta = doc.theta
      theta.value.zero()
      for (dv <- doc.zs.discreteValues) theta.value.masses.+=(dv.intValue, 1.0)
    }<commit_msg>fix bug in SparseLDAInferencer.exportThetas where theta.value.zero() referred to a commented out function<commit_after>/* Copyright (C) 2008-2014 University of Massachusetts Amherst.
   This file is part of "FACTORIE" (Factor graphs, Imperative, Extensible)
   http://factorie.cs.umass.edu, http://github.com/factorie
   Licensed 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. */
package cc.factorie.app.topics.lda
import cc.factorie._
import cc.factorie.directed._
import cc.factorie.util.DoubleSeq
import scala.Array
import cc.factorie.directed.{DirectedModel, PlatedCategoricalMixture, DiscreteMixtureCounts}
import cc.factorie.variable.{ProportionsVar, DiscreteSeqVariable, DiscreteDomain, CategoricalDomain}

class SparseLDAInferencer(
    val zDomain:DiscreteDomain,
    val wordDomain:CategoricalDomain[String],
    var phiCounts:DiscreteMixtureCounts[String],
    initialAlphas:DoubleSeq,
    initialBeta1:Double,
    model:DirectedModel,
    random: scala.util.Random,
    val localPhiCounts: DiscreteMixtureCounts[String] = null)
{
  var verbosity = 0
  var smoothingOnlyCount = 0; var topicBetaCount = 0; var topicTermCount = 0 // Just diagnostics
  def samplesCount = smoothingOnlyCount + topicBetaCount + topicTermCount
  private var alphas: Array[Double] = null
  private var beta1: Double = 0.01
  private var betaSum = beta1 * wordDomain.size
  var smoothingMass: Double = 0.0
  val numTopics = zDomain.size
  private val cachedCoefficients = new Array[Double](numTopics)
  private val cachedSmoothing = new Array[Double](numTopics)
  private val cachedDenominators = new Array[Double](numTopics)

  private val topicTermScores = new Array[Double](numTopics)

  if (verbosity > 0) println("Finished initializing phiCounts")
  if (verbosity > 5) println("nt "+phiCounts.mixtureCounts.mkString(" "))

  private var _topicDocCounts: Array[Array[Int]] = null
  def topicDocCounts = _topicDocCounts
  // Initialize alphas, beta1, smoothingMass and cachedCoefficients; must be done after phiCounts initialized
  resetSmoothing(initialAlphas, initialBeta1)

  def resetCached():Unit = {
    smoothingMass = 0

    var t = 0
    while(t < numTopics){
      cachedDenominators(t) = 1.0 / (phiCounts.mixtureCounts(t) + betaSum)
      val topicSmoothing = alphas(t) * cachedDenominators(t)
      cachedCoefficients(t) = topicSmoothing
      cachedSmoothing(t) = topicSmoothing
      smoothingMass += topicSmoothing
      t += 1
    }
    smoothingMass *= beta1
  }

  def resetSmoothing(newAlphas:DoubleSeq, newBeta1:Double): Unit = {
    require(numTopics == newAlphas.length)
    require(newBeta1 > 0.0)
    require(newAlphas.forall(_ >= 0.0))
    alphas = newAlphas.toArray
    beta1 = newBeta1
    betaSum = beta1 * wordDomain.size
    //smoothingMass = recalcSmoothingMass
    // The first term in the per-topic summand for q, just missing *n_{w|t}  [Mimno "Sparse LDA"]

    resetCached()
    assert(smoothingMass > 0.0, smoothingMass)
  }

  def initializeHistograms(maxDocSize: Int) {
    _topicDocCounts = Array.ofDim[Int](numTopics, maxDocSize+1)
    for (i <- 0 until numTopics; j <- 0 to maxDocSize) _topicDocCounts(i)(j) = 0
  }

  private def recalcSmoothingMass: Double = {
    // s = \sum_t ( \alpha_t \beta ) / ( |V| \beta + n_t )  [Mimno "Sparse LDA"]
    var i = 0; var sm = 0.0
    while (i < numTopics) { sm += alphas(i) * beta1 / (phiCounts.mixtureCounts(i) + betaSum); i += 1 }
    sm
  }

  def export(phis:Seq[ProportionsVar], beta1:Double = 0.0, numTopics: Int=0): Unit = {
    phis.foreach(_.value.masses.zero())
    for (wi <- 0 until wordDomain.size)  {
      (0 until numTopics).foreach(ti => phis(ti).value.masses.+=(wi, beta1))
      phiCounts(wi).forCounts((ti,count) => phis(ti).value.masses.+=(wi, count))
    }
  }

  def exportThetas(docs:Iterable[Doc]): Unit = {
    for (doc <- docs) {
      val theta = doc.theta
      theta.value.masses.zero()
      for (dv <- doc.zs.discreteValues) theta.value.masses.+=(dv.intValue, 1.0)
    }
{'fraction_non_alphanumeric': 0.10538243626062323, 'fraction_numerical': 0.014277620396600566, 'mean_word_length': 3.881637168141593, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6346556', 'n_tokens_mistral': 3058, 'n_tokens_neox': 2905, 'n_words': 765}