/// <summary> /// Calls the transformation dependency for the given computation /// </summary> /// <param name="computation">The computation that this dependency is to be called</param> public override void HandleDependency(Computation computation) { if (computation == null) throw new ArgumentNullException("computation"); if (computation.IsDelayed && NeedOutput) { computation.OutputInitialized += CallComputation; return; } if (Filter != null && !Filter(computation)) return; var context = computation.TransformationContext; if (Selector != null) { if (computation.IsDelayed) { var delay = computation.OutputDelay; if (!NeedOutput) { HandleDelayedComputation(computation, context, delay); } } else { HandleNonDelayedComputation(computation, context); } } }
public void InitTestContext() { ruleT1 = new TestRuleT1(); ruleT2 = new TestRuleT2(); ruleTN = new TestRuleTN(); ruleDependent = new OtherRuleT1(); transformation = new MockTransformation(ruleT1, ruleT2, ruleTN, ruleDependent); transformation.Initialize(); context = CreateContext(transformation); trace = context.Trace; c_a = context.CallTransformation(ruleT1, new object[] { "a" }); c_b = context.CallTransformation(ruleT1, new object[] { "b" }); c_ab = context.CallTransformation(ruleT2, new object[] { "a", "b" }); c_bc = context.CallTransformation(ruleT2, new object[] { "b", "c" }); c_abc = context.CallTransformation(ruleTN, new object[] { "a", "b", "c" }); c_bcd = context.CallTransformation(ruleTN, new object[] { "b", "c", "d" }); c_a.InitializeOutput("b"); c_b.InitializeOutput(null); c_ab.InitializeOutput("c"); c_bc.InitializeOutput(null); c_abc.InitializeOutput("d"); c_bcd.InitializeOutput(null); }
public override void MarkRequire(Computation other, bool isRequired) { base.MarkRequire(other, isRequired); if (isRequired) { Interlocked.Increment(ref transformationRequirements); other.Computed += DecreaseTransformationRequirements; } }
public override void MarkRequire(Computation other, bool isRequired) { base.MarkRequire(other, isRequired); var context = other.Context as TaskParallelComputationContext; if (isRequired && context != null && context != this) { if (transformationRequirements == null) transformationRequirements = new List<Task>(); transformationRequirements.Add(context.transformTask); } }
void ITransformationRuleDependency.HandleDependency(Computation computation) { if (computation != null) { if (computation.IsDelayed) { computation.OutputInitialized += computation_OutputInitialized; } else { HandleReadyComputation(computation); } } }
/// <summary> /// Determines whether the current transformation rule can instantiate the output of the given computation /// </summary> /// <param name="computation">The computation that may be instantiated by the current rule</param> /// <returns>True, if the computation instantiates the given computation, otherwise false</returns> public bool IsInstantiating(Computation computation) { if (computation != null) { var rule = computation.TransformationRule; return HasCompliantInput(computation) && (rule.OutputType == OutputType || rule.OutputType.IsAssignableFrom(OutputType)) && (BaseFilter == null || BaseFilter(computation)); } else { return false; } }
/// <summary> /// Handles the computation internally, i.e. calls dependencies, creates output, manages delays, etc /// </summary> /// <param name="transformationRule">The transformation rule</param> /// <param name="input">The input elements for this computation</param> /// <param name="context">The transformation context</param> /// <param name="computations">The computations for the input</param> /// <param name="originalTransformationRule">The transformation rule of the original call</param> /// <param name="comp">The computation</param> /// <param name="compCon">The computation context</param> private void HandleComputation(GeneralTransformationRule transformationRule, object[] input, IEnumerable context, List <ITraceEntry> computations, GeneralTransformationRule originalTransformationRule, Computation comp, ComputationContext compCon) { // The transformation output is only generated when we are handling the base transformation rule, // because this is always required if (compCon.IsDelayed) { Stack <Computation> dependantComputes = new Stack <Computation>(); var ruleStack = Transformation.ComputeInstantiatingTransformationRulePath(comp); if (transformationRule != originalTransformationRule) { ReorderStack(originalTransformationRule, comp, ruleStack); } var delayLevel = comp.Context.MinOutputDelayLevel; var computes = new List <Computation>(); Computation lastComp = null; while (ruleStack.Count > 0) { var rule = ruleStack.Pop(); var comp2 = FindOrCreateDependentComputation(input, computations, comp, dependantComputes, rule); // in case comp2 is not yet handled, a delay does not yet exist and thus // DelayLevel < minDelayLevel delayLevel = Math.Max(delayLevel, Math.Max(comp2.OutputDelayLevel, comp2.Context.MinOutputDelayLevel)); if (lastComp != null) { lastComp.SetBaseComputation(comp2); } lastComp = comp2; computes.Add(comp2); } // delay the call of dependencies // this prevents the issue arising from computations calling their parents that come later in the stack foreach (var comp2 in dependantComputes) { CallDependencies(comp2, true); } if (delayLevel <= currentOutputDelay) { var createRule = computes[0]; // Generate the output var output = createRule.CreateOutput(context); for (int i = computes.Count - 1; i >= 0; i--) { computes[i].InitializeOutput(output); } if (callTransformations) { for (int i = computes.Count - 1; i >= 0; i--) { computes[i].Transform(); } } } else { //Save computations into Delay Delay(delayLevel, computes, context); } if (!callTransformations) { for (int i = computes.Count - 1; i >= 0; i--) { AddToComputationOrder(computes[i], currentTransformationDelay); } } for (int i = computes.Count - 1; i >= 0; i--) { dependencyCallQueue.Enqueue(computes[i]); } } }
/// <summary> /// Creates new event data for the given computation /// </summary> /// <param name="computation">The computation</param> public ComputationEventArgs(Computation computation) { Computation = computation; }
public override void MarkRequire(Computation other, bool isRequired) { base.MarkRequire(other, isRequired); if (isRequired) { var tracingCompContext = other.Context as TracingComputationContext; var tracingContext = TransformationContext as TracingTransformationContext; if (tracingCompContext != null && tracingContext != null) { tracingContext.AppendDependency(Id, tracingCompContext.Id); } } }
private void HandleDelayedDependencyInput(Computation computation, ITransformationContext context, IList list, MultipleResultAwaitingPersistor delayPersistor, object[] dependencyInput) { GeneralTransformationRule dependent = DependencyTransformation; var comp2 = context.CallTransformation(dependent, dependencyInput); if (!comp2.IsDelayed) { if (comp2.Output != null) { if (context.IsThreadSafe) { lock (list) { list.Add(comp2.Output); } } else { list.Add(comp2.Output); } } } else { computation.DelayOutputAtLeast(comp2.Context.MinOutputDelayLevel); delayPersistor.WaitFor(comp2); } if (ExecuteBefore) { computation.DelayTransformationAtLeast(comp2.Context.MinTransformDelayLevel); } else { comp2.DelayTransformationAtLeast(computation.Context.MinTransformDelayLevel); } }
private Computation FindOrCreateDependentComputation(object[] input, List<ITraceEntry> computations, Computation comp, Stack<Computation> dependantComputes, GeneralTransformationRule rule) { lock (computations) { var comp2 = computations.Where(cmp => cmp.TransformationRule == rule).OfType<Computation>().FirstOrDefault(); if (comp2 == null) { comp2 = rule.CreateComputation(input, comp.Context); computations.Add(comp2); AddTraceEntry(comp2); dependantComputes.Push(comp2); } return comp2; } }
/// <summary> /// Connects the computation context with the given computation /// </summary> /// <param name="computation">The computation thst is handled by this computation context</param> public virtual void ConnectWith(Computation computation) { }
public override void ConnectWith(Computation computation) { computations.Add(computation); }
public virtual void SetBaseComputation(Computation baseComputation) { }
public virtual Stack <GeneralTransformationRule> ComputeInstantiatingTransformationRulePath(Computation computation) { if (computation == null) { throw new ArgumentNullException("computation"); } var inputTypes = computation.TransformationRule.InputType; var output = computation.TransformationRule.OutputType; Stack <GeneralTransformationRule> stack = new Stack <GeneralTransformationRule>(); var current = computation.TransformationRule; stack.Push(current); while (current != null && !current.IsLeafTransformation) { current = current.Children.Where(rule => rule.IsInstantiating(computation)).FirstOrDefault(); if (current != null) { stack.Push(current); } } return(stack); }
internal void MarkRequireInternal(Computation other, bool isRequired, ITransformationRuleDependency dependency) { if (other != null) { MarkRequire(other, isRequired, dependency); Context.MarkRequire(other, isRequired); } }
/// <summary> /// Marks that this computations requires another to be transformed. /// </summary> /// <param name="other">The other computation</param> /// <param name="isRequired">A value indicating whether the other computation must be execute before or after the current computation</param> /// <param name="dependency">The dependency that required this</param> /// <remarks>The default implementation does nothing, so feel free to override. This method is intended to be called by NMF.Transformations, only.</remarks> public virtual void MarkRequire(Computation other, bool isRequired, ITransformationRuleDependency dependency) { }
public virtual Stack<GeneralTransformationRule> ComputeInstantiatingTransformationRulePath(Computation computation) { if (computation == null) throw new ArgumentNullException("computation"); var inputTypes = computation.TransformationRule.InputType; var output = computation.TransformationRule.OutputType; Stack<GeneralTransformationRule> stack = new Stack<GeneralTransformationRule>(); var current = computation.TransformationRule; stack.Push(current); while (current != null && !current.IsLeafTransformation) { current = current.Children.Where(rule => rule.IsInstantiating(computation)).FirstOrDefault(); if (current != null) { stack.Push(current); } } return stack; }
private static void ReorderStack(GeneralTransformationRule originalTransformationRule, Computation comp, Stack <GeneralTransformationRule> ruleStack) { var testTransformationRule = originalTransformationRule; var missingStack = new Stack <GeneralTransformationRule>(); while (!ruleStack.Contains(testTransformationRule)) { missingStack.Push(testTransformationRule); testTransformationRule = testTransformationRule.BaseRule; } while (ruleStack.Peek() != testTransformationRule) { ruleStack.Pop(); if (ruleStack.Count == 0) { throw new InvalidOperationException("The rule stack from the transformation rule did not contain the base rule of the computation"); } } while (missingStack.Count > 0) { testTransformationRule = missingStack.Pop(); ruleStack.Push(testTransformationRule); } while (!testTransformationRule.IsLeafTransformation) { var found = false; foreach (var next in testTransformationRule.Children) { if (next.IsInstantiating(comp)) { testTransformationRule = next; ruleStack.Push(next); found = true; break; } } if (!found) { break; } } }
protected abstract void HandleReadyComputation(Computation computation);
/// <summary> /// Calls the transformation dependency for the given computation /// </summary> /// <param name="computation">The computation that this dependency is to be called</param> public override void HandleDependency(Computation computation) { if (computation == null) { throw new ArgumentNullException("computation"); } if (computation.IsDelayed && NeedOutput) { computation.OutputInitialized += CallComputation; return; } if (Filter == null || Filter(computation)) { var context = computation.TransformationContext; if (Selector != null) { if (computation.IsDelayed) { //case delayed var delay = computation.OutputDelay; if (!NeedOutput) { object[] dependencyInput = Selector(computation); if (dependencyInput != null) { GeneralTransformationRule dependent = DependencyTransformation; var comp2 = context.CallTransformation(dependent, dependencyInput); if (Persistor != null) { if (!comp2.IsDelayed) { delay.Persistors.Add(new SingleItemPersistor() { Persistor = Persistor, Output = comp2.Output }); } else { computation.DelayOutputAtLeast(comp2.Context.MinOutputDelayLevel); var delayPersistor = new SingleResultAwaitingPersistor(Persistor); delayPersistor.WaitFor(comp2); delay.Persistors.Add(delayPersistor); } } else // persistor is null { if (ExecuteBefore) { if (comp2.IsDelayed) { computation.DelayOutputAtLeast(comp2.Context.MinOutputDelayLevel); } } } computation.MarkRequireInternal(comp2, ExecuteBefore, this); } } } else // not delayed { // case output is already created var output = computation.Output; object[] dependencyInput = Selector(computation); if (dependencyInput != null) { GeneralTransformationRule dependent = DependencyTransformation; var comp2 = context.CallTransformation(dependent, dependencyInput); if (Persistor != null) { if (!comp2.IsDelayed) { Persistor(output, comp2.Output); } else { var delay = new SingleResultAwaitingPersistor(Persistor, computation.Output); delay.WaitFor(comp2); } } computation.MarkRequireInternal(comp2, ExecuteBefore, this); } } } } }
protected virtual void AddTraceEntry(Computation computation) { List<ITraceEntry> comps; var rule = computation.TransformationRule; if (!computationsByTransformationRule.TryGetValue(rule, out comps)) { comps = new List<ITraceEntry>(); if (!computationsByTransformationRule.TryAdd(rule, comps)) { comps = computationsByTransformationRule[rule]; } } lock (comps) { comps.Add(computation); } }
private void HandleNonDelayedComputation(Computation computation, ITransformationContext context) { var output = computation.Output; var inCollection = Selector(computation); if (inCollection != null) { if (Persistor != null) { Type listType = (typeof(List<>)).MakeGenericType(DependencyTransformation.OutputType); IList list = System.Activator.CreateInstance(listType) as IList; MultipleResultAwaitingPersistor delayPersistor = new MultipleResultAwaitingPersistor() { List = list, Persistor = Persistor, Target = output }; bool needDependencyPersistor = false; GeneralTransformationRule dependent = DependencyTransformation; if (context.IsThreadSafe) { Parallel.ForEach(inCollection, dependencyInput => { if (HandleNonDelayedPersistedDependencyInput(computation, context, list, delayPersistor, dependent, dependencyInput)) { needDependencyPersistor = true; } }); } else { foreach (var dependencyInput in inCollection) { if (HandleNonDelayedPersistedDependencyInput(computation, context, list, delayPersistor, dependent, dependencyInput)) { needDependencyPersistor = true; } } } if (!needDependencyPersistor) Persistor(output, list); } else { GeneralTransformationRule dependent = DependencyTransformation; if (context.IsThreadSafe) { Parallel.ForEach(inCollection, dependencyInput => { var comp2 = context.CallTransformation(dependent, dependencyInput); computation.MarkRequireInternal(comp2, ExecuteBefore, this); }); } else { foreach (var dependencyInput in inCollection) { var comp2 = context.CallTransformation(dependent, dependencyInput); computation.MarkRequireInternal(comp2, ExecuteBefore, this); } } } } }
private static void CallDependencies(Computation c, bool executeBefore) { TaskParallel.ForEach(c.TransformationRule.Dependencies, requirement => { if (requirement.ExecuteBefore == executeBefore) { requirement.HandleDependency(c); } }); }
/// <summary> /// Handles the computation internally, i.e. calls dependencies, creates output, manages delays, etc /// </summary> /// <param name="transformationRule">The transformation rule</param> /// <param name="input">The input elements for this computation</param> /// <param name="context">The transformation context</param> /// <param name="computations">The computations for the input</param> /// <param name="originalTransformationRule">The transformation rule of the original call</param> /// <param name="comp">The computation</param> /// <param name="compCon">The computation context</param> private void HandleComputation(GeneralTransformationRule transformationRule, object[] input, IEnumerable context, List<ITraceEntry> computations, GeneralTransformationRule originalTransformationRule, Computation comp, ComputationContext compCon) { // The transformation output is only generated when we are handling the base transformation rule, // because this is always required if (compCon.IsDelayed) { Stack<Computation> dependantComputes = new Stack<Computation>(); var ruleStack = Transformation.ComputeInstantiatingTransformationRulePath(comp); if (transformationRule != originalTransformationRule) { ReorderStack(originalTransformationRule, comp, ruleStack); } var delayLevel = comp.Context.MinOutputDelayLevel; var computes = new List<Computation>(); Computation lastComp = null; while (ruleStack.Count > 0) { var rule = ruleStack.Pop(); var comp2 = FindOrCreateDependentComputation(input, computations, comp, dependantComputes, rule); // in case comp2 is not yet handled, a delay does not yet exist and thus // DelayLevel < minDelayLevel delayLevel = Math.Max(delayLevel, Math.Max(comp2.OutputDelayLevel, comp2.Context.MinOutputDelayLevel)); if (lastComp != null) { lastComp.SetBaseComputation(comp2); } lastComp = comp2; computes.Add(comp2); } // delay the call of dependencies // this prevents the issue arising from computations calling their parents that come later in the stack foreach (var comp2 in dependantComputes) { CallDependencies(comp2, true); } if (delayLevel <= currentOutputDelay) { var createRule = computes[0]; // Generate the output var output = createRule.CreateOutput(context); for (int i = computes.Count - 1; i >= 0; i--) { computes[i].InitializeOutput(output); } if (callTransformations) { for (int i = computes.Count - 1; i >= 0; i--) { computes[i].Transform(); } } } else { //Save computations into Delay Delay(delayLevel, computes, context); } if (!callTransformations) { for (int i = computes.Count - 1; i >= 0; i--) { AddToComputationOrder(computes[i], currentTransformationDelay); } } for (int i = computes.Count - 1; i >= 0; i--) { dependencyCallQueue.Enqueue(computes[i]); } } }
public virtual void SetBaseComputation(Computation baseComputation) { }
private static void ReorderStack(GeneralTransformationRule originalTransformationRule, Computation comp, Stack<GeneralTransformationRule> ruleStack) { var testTransformationRule = originalTransformationRule; var missingStack = new Stack<GeneralTransformationRule>(); while (!ruleStack.Contains(testTransformationRule)) { missingStack.Push(testTransformationRule); testTransformationRule = testTransformationRule.BaseRule; } while (ruleStack.Peek() != testTransformationRule) { ruleStack.Pop(); if (ruleStack.Count == 0) throw new InvalidOperationException("The rule stack from the transformation rule did not contain the base rule of the computation"); } while (missingStack.Count > 0) { testTransformationRule = missingStack.Pop(); ruleStack.Push(testTransformationRule); } while (!testTransformationRule.IsLeafTransformation) { var found = false; foreach (var next in testTransformationRule.Children) { if (next.IsInstantiating(comp)) { testTransformationRule = next; ruleStack.Push(next); found = true; break; } } if (!found) break; } }
/// <summary> /// Waits for the given computation to initialize its output /// </summary> /// <param name="comp">The computation to wait for</param> public void WaitFor(Computation comp) { Remaining++; comp.OutputInitialized += ItemInitialized; }
private void AddToComputationOrder(Computation c, byte level) { level = Math.Max(level, c.Context.MinTransformDelayLevel); ConcurrentQueue<Computation> list; if (computationOrder.Count > level) { list = computationOrder[level]; if (list == null) { lock (_computationLevelLockObject) { list = CreateLevel(level); } } } else { lock (_computationLevelLockObject) { if (computationOrder.Count <= level) { while (computationOrder.Count < level) { computationOrder.Add(null); } list = new ConcurrentQueue<Computation>(); computationOrder.Add(list); } else { list = CreateLevel(level); } } } list.Enqueue(c); }
/// <summary> /// Waits for the given computation to initialize its output /// </summary> /// <param name="comp">The computation to wait for</param> public void WaitFor(Computation comp) { comp.OutputInitialized += comp_OutputInitialized; }
/// <summary> /// Marks that this computations requires another to be transformed. /// </summary> /// <param name="other">The other computation</param> /// <param name="isRequired">A value indicating whether the other computation must be execute before or after the current computation</param> /// <param name="dependency">The dependency that required this</param> /// <remarks>The default implementation does nothing, so feel free to override. This method is intended to be called by NMF.Transformations, only.</remarks> public virtual void MarkRequire(Computation other, bool isRequired, ITransformationRuleDependency dependency) { }
/// <summary> /// Waits for the given computation to initialize its output /// </summary> /// <param name="comp">The computation to wait for</param> public void WaitFor(Computation comp) { Remaining++; comp.OutputInitialized += ItemInitialized; }
private static string PrintInputs(Computation createRule) { return(string.Join(", ", Enumerable.Range(0, createRule.InputArguments).Select(i => createRule.GetInput(i) != null ? createRule.GetInput(i).ToString() : "(null)"))); }
/// <summary> /// Connects the computation context with the given computation /// </summary> /// <param name="computation">The computation thst is handled by this computation context</param> public virtual void ConnectWith(Computation computation) { }
/// <summary> /// Creates new event data for the given computation /// </summary> /// <param name="computation">The computation</param> public ComputationEventArgs(Computation computation) { Computation = computation; }
/// <summary> /// Mark that this computation context requires another computation context to be done /// </summary> /// <param name="other">The other computation context</param> /// <param name="isRequired">True, if the other context is a strict requirement</param> public virtual void MarkRequire(Computation other, bool isRequired) { if (other == null) throw new ArgumentNullException("other"); var otherContext = other.Context; if (isRequired && otherContext != this) { DelayOutputAtLeast(otherContext.MinOutputDelayLevel); DelayTransformationAtLeast(otherContext.MinTransformDelayLevel); } }
/// <summary> /// Gets a value indicating whether the given computation has a compliant input to be instantiated by the current transformation rule /// </summary> /// <param name="computation">The computation that is a candidate for instantiation</param> /// <returns>True, if the input arguments match the input types of the current transformation rule, otherwise false</returns> public bool HasCompliantInput(Computation computation) { if (computation == null) return false; var inputTypes = InputType; if (computation.InputArguments != inputTypes.Length) return false; for (int i = 0; i < inputTypes.Length; i++) { var instance = computation.GetInput(i); if (instance != null && !inputTypes[i].IsInstanceOfType(instance)) return false; } return true; }
protected virtual void AddTraceEntry(Computation computation) { List<ITraceEntry> comps; if (!computationsByTransformationRule.TryGetValue(computation.TransformationRule, out comps)) { comps = new List<ITraceEntry>(); computationsByTransformationRule.Add(computation.TransformationRule, comps); } comps.Add(computation); }
private Computation FindOrCreateDependentComputation(object[] input, List <ITraceEntry> computations, Computation comp, Stack <Computation> dependantComputes, GeneralTransformationRule rule) { var comp2 = computations.Where(cmp => cmp.TransformationRule == rule).OfType <Computation>().FirstOrDefault(); if (comp2 == null) { comp2 = rule.CreateComputation(input, comp.Context); computations.Add(comp2); AddTraceEntry(comp2); dependantComputes.Push(comp2); } return(comp2); }
private void AddToComputationOrder(Computation c, byte level) { level = Math.Max(level, c.Context.MinTransformDelayLevel); List<Computation> list; if (computationOrder == null) computationOrder = new List<List<Computation>>(); if (computationOrder.Count > level) { list = computationOrder[level]; if (list == null) { list = new List<Computation>(); computationOrder[level] = list; } } else { while (computationOrder.Count < level) { computationOrder.Add(null); } list = new List<Computation>(); computationOrder.Add(list); } list.Add(c); }
private void HandleNonDelayedComputation(Computation computation, ITransformationContext context) { var output = computation.Output; var inCollection = Selector(computation); if (inCollection != null) { if (Persistor != null) { Type listType = (typeof(List <>)).MakeGenericType(DependencyTransformation.OutputType); IList list = System.Activator.CreateInstance(listType) as IList; MultipleResultAwaitingPersistor delayPersistor = new MultipleResultAwaitingPersistor() { List = list, Persistor = Persistor, Target = output }; bool needDependencyPersistor = false; GeneralTransformationRule dependent = DependencyTransformation; if (context.IsThreadSafe) { Parallel.ForEach(inCollection, dependencyInput => { if (HandleNonDelayedPersistedDependencyInput(computation, context, list, delayPersistor, dependent, dependencyInput)) { needDependencyPersistor = true; } }); } else { foreach (var dependencyInput in inCollection) { if (HandleNonDelayedPersistedDependencyInput(computation, context, list, delayPersistor, dependent, dependencyInput)) { needDependencyPersistor = true; } } } if (!needDependencyPersistor) { Persistor(output, list); } } else { GeneralTransformationRule dependent = DependencyTransformation; if (context.IsThreadSafe) { Parallel.ForEach(inCollection, dependencyInput => { var comp2 = context.CallTransformation(dependent, dependencyInput); computation.MarkRequireInternal(comp2, ExecuteBefore, this); }); } else { foreach (var dependencyInput in inCollection) { var comp2 = context.CallTransformation(dependent, dependencyInput); computation.MarkRequireInternal(comp2, ExecuteBefore, this); } } } } }
private static void CallDependencies(Computation c, bool executeBefore) { foreach (var requirement in c.TransformationRule.Dependencies) { if (requirement.ExecuteBefore == executeBefore) { requirement.HandleDependency(c); } } }