All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.signalcollect.triplerush.QueryParticle.scala Maven / Gradle / Ivy

The newest version!
/*
 *  @author Philip Stutz
 *  @author Mihaela Verman
 *
 *  Copyright 2013 University of Zurich
 *
 *  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 com.signalcollect.triplerush

import java.util.concurrent.atomic.AtomicInteger

import scala.language.implicitConversions

import com.signalcollect.triplerush.EfficientIndexPattern._
import com.signalcollect.triplerush.sparql.VariableEncoding
import QueryParticle._

object ParticleDebug {
  def apply(p: Array[Int]): ParticleDebug = {
    val particle = new QueryParticle(p)
    ParticleDebug(
      particle.queryId,
      particle.tickets,
      (-1 to -particle.bindings.length by -1).zip(particle.bindings).filter(_._2 != 0).toMap,
      particle.patterns.toList.reverse,
      particle.numberOfBindings,
      particle.r.length - 4)
  }

  def validate(p: Array[Int], msg: String): Unit = {
    if (p != null) {
      val validTotalLength = (p.length - 4 - p.numberOfBindings) % 3 == 0
      val validNumberOfBindingsLength = (p.length - 4 - p.numberOfBindings) >= 0
      if (!validTotalLength) {
        val debug = ParticleDebug(p)
        throw new Exception(s"$msg remaining length after numberofbindings not divisible by 3 (cannot represent TP): $debug")
      }
      if (!validNumberOfBindingsLength) {
        val debug = ParticleDebug(p)
        throw new Exception(s"$msg array too short to accomodate bindings $debug")
      }
    } else {
      throw new Exception(s"$msg validate particle was null")
    }
  }
}

case class ParticleDebug(
  id: Int,
  tickets: Long,
  bindings: Map[Int, Int],
  unmatched: List[TriplePattern],
  numberOfBindings: Int,
  intsForBindingsAndUnmatched: Int)

object QueryParticle {

  implicit def arrayToParticle(a: Array[Int]): QueryParticle = new QueryParticle(a)

  def apply(
    patterns: Seq[TriplePattern],
    queryId: Int,
    numberOfSelectVariables: Int,
    tickets: Long = Long.MaxValue): Array[Int] = {
    val ints = 4 + numberOfSelectVariables + 3 * patterns.length
    val r = new Array[Int](ints)
    r.writeQueryId(queryId)
    r.writeNumberOfBindings(numberOfSelectVariables)
    r.writeTickets(tickets)
    r.writePatterns(patterns)
    r
  }

  val failed: Array[Int] = null.asInstanceOf[Array[Int]]

}

/**
 * The array packs:
 * 0 int:    queryId,
 * 1-2 long:   tickets (long encoded as 2 ints)
 * 3 int:    numberOfBindings
 * 4-? ints:   bindings
 * ? ?*3 ints: triple patterns in reverse matching order (last one
 * gets matched first).
 */
class QueryParticle(val r: Array[Int]) extends AnyVal {

  def validate(msg: String): Unit = ParticleDebug.validate(r, msg)

  def isBindingQuery: Boolean = queryId > 0

  def copy: Array[Int] = {
    val c = new Array[Int](r.length)
    System.arraycopy(r, 0, c, 0, r.length)
    c
  }

  def bindSubject(
    toMatchS: Int,
    toMatchP: Int,
    toMatchO: Int,
    toBindS: Int,
    toBindP: Int,
    toBindO: Int,
    copyBeforeWrite: Boolean): Array[Int] = {
    // Subject is conflicting constant. No binding possible.
    if (toMatchS > 0) {
      return QueryParticle.failed
    }

    // Subject is a variable that needs to be bound to the constant in the triple pattern.
    // Bind value to variable.
    val variable = toMatchS
    val boundValue = toBindS

    if (isBindingIncompatible(toMatchP, toBindP, variable, boundValue)
      || isBindingIncompatible(toMatchO, toBindO, variable, boundValue)) {
      return QueryParticle.failed
    }

    // No conflicts, we bind the value to the variable.
    val currentParticle: Array[Int] = {
      if (copyBeforeWrite) {
        copyWithoutLastPattern
      } else {
        r
      }
    }

    if (isBindingQuery) {
      val variableIndex = VariableEncoding.variableIdToDecodingIndex(variable)
      currentParticle.writeBinding(variableIndex, boundValue)
    }

    currentParticle.bindVariablesInPatterns(variable, boundValue)
    val result = currentParticle.bindPredicate(toMatchP, toMatchO, toBindP, toBindO, false)
    result
  }

  def bindPredicate(
    toMatchP: Int,
    toMatchO: Int,
    toBindP: Int,
    toBindO: Int,
    copyBeforeWrite: Boolean): Array[Int] = {
    if (toMatchP == toBindP) {
      // Predicate is compatible constant. No binding necessary.
      return bindObject(toMatchO, toBindO, copyBeforeWrite)
    }

    // Predicate is conflicting constant. No binding possible.
    if (toMatchP > 0) {
      return QueryParticle.failed
    }

    // Predicate is a variable that needs to be bound to the constant in the triple pattern.
    // Bind value to variable.
    val variable = toMatchP
    val boundValue = toBindP

    if (isBindingIncompatible(toMatchO, toBindO, variable, boundValue)) {
      return QueryParticle.failed
    }

    // No conflicts, we bind the value to the variable.
    val currentParticle = {
      if (copyBeforeWrite) {
        copyWithoutLastPattern
      } else {
        r
      }
    }
    if (isBindingQuery) {
      val variableIndex = -(variable + 1)
      currentParticle.writeBinding(variableIndex, boundValue)
    }
    currentParticle.bindVariablesInPatterns(variable, boundValue)
    val result = currentParticle.bindObject(toMatchO, toBindO, false)
    result
  }

  def bindObject(
    toMatchO: Int,
    toBindO: Int,
    copyBeforeWrite: Boolean): Array[Int] = {
    if (toMatchO == toBindO) {
      // Object is compatible constant. No binding necessary.
      //      val result = {
      //        if (copyBeforeWrite) {
      //          // We need to cut off the last pattern, even if we never write.
      //          particle.copyWithoutLastPattern
      //        } else {
      //          particle
      //        }
      //      }
      // In theory the check above would be necessary. In practice this
      // execution path is only reached if a particle copy was made before.
      return r
    }

    // Object is conflicting constant. No binding possible.
    if (toMatchO > 0) {
      return QueryParticle.failed
    }

    // Object is a variable that needs to be bound to the constant in the triple pattern.
    // Bind value to variable.
    val variable = toMatchO
    val boundValue = toBindO

    // No conflicts, we bind the value to the variable.
    val currentParticle = {
      if (copyBeforeWrite) {
        copyWithoutLastPattern
      } else {
        r
      }
    }
    if (isBindingQuery) {
      val variableIndex = -(variable + 1)
      currentParticle.writeBinding(variableIndex, boundValue)
    }
    currentParticle.bindVariablesInPatterns(variable, boundValue)
    currentParticle
  }

  // If the variable appears multiple times in the same pattern, then all the bindings have to be compatible.
  @inline private[this] def isBindingIncompatible(otherAttribute: Int, tpAttribute: Int, variable: Int, boundValue: Int): Boolean = {
    (otherAttribute == variable && tpAttribute != boundValue)
  }

  // Updates an attribute with a new binding.
  @inline private[this] def updatedAttribute(attribute: Int, variable: Int, boundValue: Int): Int = {
    if (attribute == variable) boundValue else attribute
  }

  def bindings: Array[Int] = {
    val numBindings = numberOfBindings
    val b = new Array[Int](numBindings)
    System.arraycopy(r, 4, b, 0, numBindings)
    b
  }

  def isResult: Boolean = r.length == 4 + numberOfBindings

  def queryId: Int = r(0)

  def writeQueryId(id: Int): Unit = r(0) = id

  def tickets: Long = {
    ((r(1) | 0L) << 32) | (r(2) & 0x00000000FFFFFFFFL)
  }

  def writeTickets(t: Long) = {
    r(1) = (t >> 32).toInt
    r(2) = t.toInt
  }

  def numberOfBindings: Int = r(3)

  def writeNumberOfBindings(numberOfBindings: Int): Unit = {
    r(3) = numberOfBindings
  }

  def writeBindings(bindings: Seq[Int]): Unit = {
    r(3) = bindings.length
    var i = 0
    while (i < bindings.length) {
      writeBinding(i, bindings(i))
      i += 1
    }
  }

  def writeBinding(bindingIndex: Int, boundValue: Int): Unit = {
    val numBindings = numberOfBindings
    if (bindingIndex < numBindings) {
      val baseBindingIndex = 4
      r(baseBindingIndex + bindingIndex) = boundValue
    }
  }

  def binding(bindingIndex: Int): Int = {
    val contentIntIndex = bindingIndex + 4
    r(contentIntIndex)
  }

  def writePatterns(unmatched: Seq[TriplePattern]): Unit = {
    var i = 0
    var tpByteIndex = r.length - 3 // index of subject of last pattern
    while (i < unmatched.length) {
      writePattern(tpByteIndex, unmatched(i))
      tpByteIndex -= 3
      i += 1
    }
  }

  /**
   * Requires the index where the subject will be written.
   * Pattern is written in spo order.
   */
  def writePattern(subjectIndex: Int, p: TriplePattern): Unit = {
    r(subjectIndex) = p.s
    r(subjectIndex + 1) = p.p
    r(subjectIndex + 2) = p.o
  }

  def copyWithTickets(t: Long, complete: Boolean): Array[Int] = {
    // It seems that for small arrays arraycopy is faster than clone:
    // http://www.javaspecialists.co.za/archive/Issue124.html
    val rLength = r.length
    val newR = new Array[Int](rLength)
    System.arraycopy(r, 0, newR, 0, rLength)
    if (complete) {
      newR.writeTickets(t)
    } else {
      newR.writeTickets(-t)
    }
    newR
  }

  def patterns: IndexedSeq[TriplePattern] = {
    for {i <- numberOfPatterns - 1 to 0 by -1} yield pattern(i)
  }

  def numberOfPatterns: Int = (r.length - 4 - numberOfBindings) / 3

  def pattern(index: Int): TriplePattern = {
    val sIndex = 3 * index + 4 + numberOfBindings
    val pIndex = sIndex + 1
    val oIndex = sIndex + 2
    TriplePattern(r(sIndex), r(pIndex), r(oIndex))
  }

  def lastPattern: TriplePattern = {
    val sIndex = r.length - 3
    val pIndex = r.length - 2
    val oIndex = r.length - 1
    TriplePattern(
      r(sIndex),
      r(pIndex),
      r(oIndex))
  }

  def copyWithoutLastPattern: Array[Int] = {
    val copyLength = r.length - 3
    val rCopy = new Array[Int](copyLength)
    System.arraycopy(r, 0, rCopy, 0, copyLength)
    rCopy
  }

  // Update all patterns with this new binding.
  def bindVariablesInPatterns(
    variable: Int,
    boundValue: Int): Unit = {
    // Index of first subject of first TP.
    var i = numberOfBindings + 4
    while (i < r.length) {
      if (r(i) == variable) {
        r(i) = boundValue
      }
      i += 1
    }
  }

  /**
   * Routing address for this query.
   */
  def routingAddress: Long = {
    if (isResult) {
      OperationIds.embedInLong(queryId)
    } else {
      // Query not complete yet, route onwards.
      val s = lastPatternS
      val p = lastPatternP
      val o = lastPatternO
      if (s > 0 && p > 0 && o > 0) {
        EfficientIndexPattern(s, 0, o)
      } else {
        EfficientIndexPattern(math.max(s, 0), math.max(p, 0), math.max(o, 0))
      }
    }
  }

  /**
   * Checks that the last pattern is not fully bound and that no variable appears multiple times in the last pattern.
   * The same variable appearing multiple times might cause a binding to fail.
   */
  def isSimpleToBind: Boolean = {
    val s = lastPatternS
    val p = lastPatternP
    val o = lastPatternO
    !(s > 0 && p > 0 && o > 0) &&
      (s > 0 || (s != p && s != o)) &&
      (o > 0 || (o != p))
  }

  @inline def lastPatternS: Int = r(r.length - 3)

  @inline def lastPatternP: Int = r(r.length - 2)

  @inline def lastPatternO: Int = r(r.length - 1)

  /**
   * Assumption: TP has all constants.
   */
  def bind(toBindS: Int, toBindP: Int, toBindO: Int): Array[Int] = {
    val patternS = lastPatternS
    val patternP = lastPatternP
    val patternO = lastPatternO
    if (toBindS == patternS) {
      // Subject is compatible constant. No binding necessary.
      if (toBindP == patternP) {
        // Predicate is compatible constant. No binding necessary.
        if (toBindO == patternO) {
          // Object is compatible constant. No binding necessary.
          return copyWithoutLastPattern
        }
        return bindObject(patternO, toBindO, true)
      }
      return bindPredicate(patternP, patternO, toBindP, toBindO, true)
    }
    bindSubject(patternS, patternP, patternO, toBindS, toBindP, toBindO, true)
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy