/// <summary> /// Does a Product of this Multiset and another Multiset. /// </summary> /// <param name="other">Other Multiset.</param> /// <returns></returns> public virtual BaseMultiset Product(BaseMultiset other) { if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } #if NET40 if (Options.UsePLinqEvaluation) { // Determine partition sizes so we can do a parallel product // Want to parallelize over whichever side is larger PartitionedMultiset partitionedSet; if (this.Count >= other.Count) { partitionedSet = new PartitionedMultiset(this.Count, other.Count); this.Sets.AsParallel().ForAll(x => this.EvalProduct(x, other, partitionedSet)); } else { partitionedSet = new PartitionedMultiset(other.Count, this.Count); other.Sets.AsParallel().ForAll(y => this.EvalProduct(y, this, partitionedSet)); } return(partitionedSet); } else { // Use serial calculation which is likely to really suck for big products Multiset productSet = new Multiset(); foreach (ISet x in this.Sets) { foreach (ISet y in other.Sets) { productSet.Add(x.Join(y)); } } return(productSet); } #else // Use serial calculation which is likely to really suck for big products var productSet = new Multiset(); foreach (var x in Sets) { foreach (var y in other.Sets) { productSet.Add(x.Join(y)); } } return(productSet); #endif }
/// <summary> /// Calculates the product of two mutlisets asynchronously with a timeout to restrict long running computations /// </summary> /// <param name="multiset">Multiset</param> /// <param name="other">Other Multiset</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed sychronously</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) { return(multiset); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } //If no timeout use default implementation if (timeout <= 0) { return(multiset.Product(other)); } //Otherwise Invoke using an Async call BaseMultiset productSet; #if NET40 && !SILVERLIGHT if (Options.UsePLinqEvaluation) { if (multiset.Count >= other.Count) { productSet = new PartitionedMultiset(multiset.Count, other.Count); } else { productSet = new PartitionedMultiset(other.Count, multiset.Count); } } else { #endif productSet = new Multiset(); #if NET40 && !SILVERLIGHT } #endif StopToken stop = new StopToken(); GenerateProductDelegate d = new GenerateProductDelegate(GenerateProduct); IAsyncResult r = d.BeginInvoke(multiset, other, productSet, stop, null, null); //Wait int t = (int)Math.Min(timeout, Int32.MaxValue); r.AsyncWaitHandle.WaitOne(t); if (!r.IsCompleted) { stop.ShouldStop = true; r.AsyncWaitHandle.WaitOne(); } return(productSet); }
private void EvalProduct(ISet x, BaseMultiset other, PartitionedMultiset productSet) { int id = productSet.GetNextBaseID(); foreach (ISet y in other.Sets) { id++; ISet z = x.Join(y); z.ID = id; productSet.Add(z); } }
/// <summary> /// Calculates the product of two multi-sets asynchronously with a timeout to restrict long running computations. /// </summary> /// <param name="multiset">Multiset.</param> /// <param name="other">Other Multiset.</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed synchronously.</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) { return(multiset); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } // If no timeout use default implementation if (timeout <= 0) { return(multiset.Product(other)); } // Otherwise Invoke using an Async call BaseMultiset productSet; if (Options.UsePLinqEvaluation) { if (multiset.Count >= other.Count) { productSet = new PartitionedMultiset(multiset.Count, other.Count); } else { productSet = new PartitionedMultiset(other.Count, multiset.Count); } } else { productSet = new Multiset(); } var stop = new StopToken(); var t = (int)Math.Min(timeout, int.MaxValue); var productTask = Task.Factory.StartNew(() => GenerateProduct(multiset, other, productSet, stop)); if (!productTask.Wait(t)) { stop.ShouldStop = true; productTask.Wait(); } return(productSet); }
private static void EvalProduct(ISet x, BaseMultiset other, PartitionedMultiset productSet, StopToken stop) { if (stop.ShouldStop) { return; } var id = productSet.GetNextBaseID(); foreach (var y in other.Sets) { id++; var z = x.Join(y); z.ID = id; productSet.Add(z); if (stop.ShouldStop) { return; } } }
private void EvalLeftJoinProduct(ISet x, BaseMultiset other, PartitionedMultiset partitionedSet, ISparqlExpression expr) { LeviathanLeftJoinBinder binder = new LeviathanLeftJoinBinder(partitionedSet); SparqlEvaluationContext subcontext = new SparqlEvaluationContext(binder); bool standalone = false, matched = false; int id = partitionedSet.GetNextBaseID(); foreach (ISet y in other.Sets) { id++; ISet z = x.Join(y); z.ID = id; try { partitionedSet.Add(z); if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) { partitionedSet.Remove(z.ID); standalone = true; } else { matched = true; } } catch { partitionedSet.Remove(z.ID); standalone = true; } } if (standalone && !matched) { id++; ISet z = x.Copy(); z.ID = id; partitionedSet.Add(z); } }
private void EvalFilteredProduct(SparqlEvaluationContext context, ISet x, BaseMultiset other, PartitionedMultiset partitionedSet) { int id = partitionedSet.GetNextBaseID(); foreach (ISet y in other.Sets) { id++; ISet z = x.Join(y); z.ID = id; partitionedSet.Add(z); try { if (!this._expr.Evaluate(context, z.ID).AsSafeBoolean()) { //Means the expression evaluates to false so we discard the solution partitionedSet.Remove(z.ID); } } catch { //Means the solution does not meet the FILTER and can be discarded partitionedSet.Remove(z.ID); } } //Remember to check for timeouts occassionally context.CheckTimeout(); }
/// <summary> /// Evaluates the filtered product /// </summary> /// <param name="context">Evaluation Context</param> /// <returns></returns> public BaseMultiset Evaluate(SparqlEvaluationContext context) { BaseMultiset initialInput = context.InputMultiset; BaseMultiset lhsResults = context.Evaluate(this._lhs); if (lhsResults is NullMultiset || lhsResults.IsEmpty) { //If LHS Results are Null/Empty then end result will always be null so short circuit context.OutputMultiset = new NullMultiset(); } else { context.InputMultiset = initialInput; BaseMultiset rhsResults = context.Evaluate(this._rhs); if (rhsResults is NullMultiset || rhsResults.IsEmpty) { //If RHS Results are Null/Empty then end results will always be null so short circuit context.OutputMultiset = new NullMultiset(); } else if (rhsResults is IdentityMultiset) { //Apply Filter over LHS Results only - defer evaluation to filter implementation context.InputMultiset = lhsResults; UnaryExpressionFilter filter = new UnaryExpressionFilter(this._expr); filter.Evaluate(context); context.OutputMultiset = lhsResults; } else { //Calculate the product applying the filter as we go #if NET40 && !SILVERLIGHT if (Options.UsePLinqEvaluation && this._expr.CanParallelise) { PartitionedMultiset partitionedSet; SparqlResultBinder binder = context.Binder; if (lhsResults.Count >= rhsResults.Count) { partitionedSet = new PartitionedMultiset(lhsResults.Count, rhsResults.Count); context.Binder = new LeviathanLeftJoinBinder(partitionedSet); lhsResults.Sets.AsParallel().ForAll(x => this.EvalFilteredProduct(context, x, rhsResults, partitionedSet)); } else { partitionedSet = new PartitionedMultiset(rhsResults.Count, lhsResults.Count); context.Binder = new LeviathanLeftJoinBinder(partitionedSet); rhsResults.Sets.AsParallel().ForAll(y => this.EvalFilteredProduct(context, y, lhsResults, partitionedSet)); } context.Binder = binder; context.OutputMultiset = partitionedSet; } else { #endif BaseMultiset productSet = new Multiset(); SparqlResultBinder binder = context.Binder; context.Binder = new LeviathanLeftJoinBinder(productSet); foreach (ISet x in lhsResults.Sets) { foreach (ISet y in rhsResults.Sets) { ISet z = x.Join(y); productSet.Add(z); try { if (!this._expr.Evaluate(context, z.ID).AsSafeBoolean()) { //Means the expression evaluates to false so we discard the solution productSet.Remove(z.ID); } } catch { //Means this solution does not meet the FILTER and can be discarded productSet.Remove(z.ID); } } //Remember to check for timeouts occassionaly context.CheckTimeout(); } context.Binder = binder; context.OutputMultiset = productSet; #if NET40 && !SILVERLIGHT } #endif } } return(context.OutputMultiset); }
/// <summary> /// Calculates the product of two mutlisets asynchronously with a timeout to restrict long running computations. /// </summary> /// <param name="multiset">Multiset.</param> /// <param name="other">Other Multiset.</param> /// <param name="timeout">Timeout, if <=0 no timeout is used and product will be computed sychronously.</param> /// <returns></returns> public static BaseMultiset ProductWithTimeout(this BaseMultiset multiset, BaseMultiset other, long timeout) { if (other is IdentityMultiset) { return(multiset); } if (other is NullMultiset) { return(other); } if (other.IsEmpty) { return(new NullMultiset()); } // If no timeout use default implementation if (timeout <= 0) { return(multiset.Product(other)); } // Otherwise Invoke using an Async call #if NET40 BaseMultiset productSet; if (Options.UsePLinqEvaluation) { if (multiset.Count >= other.Count) { productSet = new PartitionedMultiset(multiset.Count, other.Count); } else { productSet = new PartitionedMultiset(other.Count, multiset.Count); } } else { productSet = new Multiset(); } #else var productSet = new Multiset(); #endif var stop = new StopToken(); var t = (int)Math.Min(timeout, int.MaxValue); #if NET40 || NETSTANDARD1_4 || NETSTANDARD2_0 var productTask = Task.Factory.StartNew(() => GenerateProduct(multiset, other, productSet, stop)); if (!productTask.Wait(t)) { stop.ShouldStop = true; productTask.Wait(); } return(productSet); #else GenerateProductDelegate d = new GenerateProductDelegate(GenerateProduct); IAsyncResult r = d.BeginInvoke(multiset, other, productSet, stop, null, null); // Wait r.AsyncWaitHandle.WaitOne(t); if (!r.IsCompleted) { stop.ShouldStop = true; r.AsyncWaitHandle.WaitOne(); } return(productSet); #endif }
/// <summary> /// Does a Left Join of this Multiset to another Multiset where the Join is predicated on the given Expression. /// </summary> /// <param name="other">Other Multiset.</param> /// <param name="expr">Expression.</param> /// <returns></returns> public virtual BaseMultiset LeftJoin(BaseMultiset other, ISparqlExpression expr) { // If the Other is the Identity/Null Multiset the result is this Multiset if (other is IdentityMultiset) { return(this); } if (other is NullMultiset) { return(this); } if (other.IsEmpty) { return(this); } Multiset joinedSet = new Multiset(); LeviathanLeftJoinBinder binder = new LeviathanLeftJoinBinder(joinedSet); SparqlEvaluationContext subcontext = new SparqlEvaluationContext(binder); // Find the First Variable from this Multiset which is in both Multisets // If there is no Variable from this Multiset in the other Multiset then this // should be a Join operation instead of a LeftJoin List <String> joinVars = Variables.Where(v => other.Variables.Contains(v)).ToList(); if (joinVars.Count == 0) { #if NET40 if (Options.UsePLinqEvaluation && expr.CanParallelise) { PartitionedMultiset partitionedSet = new PartitionedMultiset(this.Count, other.Count + 1); this.Sets.AsParallel().ForAll(x => EvalLeftJoinProduct(x, other, partitionedSet, expr)); return(partitionedSet); } #endif // Do a serial Left Join Product // Calculate a Product filtering as we go foreach (ISet x in Sets) { bool standalone = false; bool matched = false; foreach (ISet y in other.Sets) { ISet z = x.Join(y); try { joinedSet.Add(z); if (!expr.Evaluate(subcontext, z.ID).AsSafeBoolean()) { joinedSet.Remove(z.ID); standalone = true; } else { matched = true; } } catch { joinedSet.Remove(z.ID); standalone = true; } } if (standalone && !matched) { joinedSet.Add(x.Copy()); } } #if NET40 #endif } else { // This is the new Join algorithm which is also correct but is O(2n) so much faster and scalable // Downside is that it does require more memory than the old algorithm List <MultiDictionary <INode, List <int> > > values = new List <MultiDictionary <INode, List <int> > >(); List <List <int> > nulls = new List <List <int> >(); foreach (String var in joinVars) { joinedSet.AddVariable(var); values.Add(new MultiDictionary <INode, List <int> >(new FastVirtualNodeComparer())); nulls.Add(new List <int>()); } // First do a pass over the RHS Result to find all possible values for joined variables foreach (ISet y in other.Sets) { int i = 0; foreach (String var in joinVars) { INode value = y[var]; if (value != null) { if (values[i].TryGetValue(value, out List <int> ids)) { ids.Add(y.ID); } else { values[i].Add(value, new List <int> { y.ID }); } } else { nulls[i].Add(y.ID); } i++; } } // Then do a pass over the LHS and work out the intersections #if NET40 if (Options.UsePLinqEvaluation && expr.CanParallelise) { this.Sets.AsParallel().ForAll(x => EvalLeftJoin(x, other, joinVars, values, nulls, joinedSet, subcontext, expr)); } else { // Use a Serial Left Join foreach (ISet x in this.Sets) { this.EvalLeftJoin(x, other, joinVars, values, nulls, joinedSet, subcontext, expr); } } #else // Use a Serial Left Join foreach (var x in Sets) { EvalLeftJoin(x, other, joinVars, values, nulls, joinedSet, subcontext, expr); } #endif } return(joinedSet); }