public override Operand EvaluationStep(Context context) { Operand operand = base.EvaluationStep(context); if (operand != null) { return(operand); } // To avoid infinite evaluation looping, we must apply... // 1) vB = v.B + v^B, and // 2) v^B = vB - v.B, // ...according to rules that dictate when and where they're appropriate. // Also to avoid infinite looping, the distributive property must take // precedence over anything we do here. // All reduction cases must be eliminated before it is safe to handle the expansion cases. for (int i = 0; i < operandList.Count - 1; i++) { Blade bladeA = operandList[i] as Blade; Blade bladeB = operandList[i + 1] as Blade; if (bladeA != null && bladeB != null && bladeA.Grade > 1 && bladeB.Grade > 1) { GeometricProduct geometricProduct; if (context.useOperandCache) { geometricProduct = new GeometricProduct(new List <Operand>() { new Blade(new NumericScalar(1.0), bladeA.vectorList.ToList()), new Blade(new NumericScalar(1.0), bladeB.vectorList.ToList()) }); string key = geometricProduct.Print(Format.PARSEABLE, context); Operand cachedResult = null; if (!context.operandCache.GetStorage(key, ref cachedResult)) { context.useOperandCache = false; cachedResult = Operand.ExhaustEvaluation(geometricProduct, context); context.useOperandCache = true; context.operandCache.SetStorage(key, cachedResult); } return(new GeometricProduct(new List <Operand>() { bladeA.scalar, bladeB.scalar, cachedResult })); } // Here our choice of which blade to reduce is arbitrary from a stand-point of correctness. // However, we might converge faster by choosing the blade with smaller grade. // Note there is also something arbitrary about how we're reducing the blades. int j = bladeA.Grade <= bladeB.Grade ? i : i + 1; Blade blade = operandList[j] as Blade; Blade subBlade = blade.MakeSubBlade(0); Blade vector = new Blade(blade.vectorList[0]); geometricProduct = new GeometricProduct(new List <Operand>() { vector, subBlade }); InnerProduct innerProduct = new InnerProduct(new List <Operand>() { vector.Copy(), subBlade.Copy() }); operandList[j] = new Sum(new List <Operand>() { geometricProduct, new GeometricProduct(new List <Operand>() { new NumericScalar(-1.0), innerProduct }) }); return(this); } } // All reduction cases eliminated, it is now safe to handle some expansion cases. for (int i = 0; i < operandList.Count - 1; i++) { Blade bladeA = operandList[i] as Blade; Blade bladeB = operandList[i + 1] as Blade; if (bladeA == null || bladeB == null) { continue; } if ((bladeA.Grade == 1 && bladeB.Grade > 1) || (bladeA.Grade > 1 && bladeB.Grade == 1)) { InnerProduct innerProduct = new InnerProduct(new List <Operand>() { bladeA, bladeB }); OuterProduct outerProduct = new OuterProduct(new List <Operand>() { bladeA.Copy(), bladeB.Copy() }); operandList[i] = new Sum(new List <Operand>() { innerProduct, outerProduct }); operandList.RemoveAt(i + 1); return(this); } } // It is now safe to handle the remaining expansion cases. for (int i = 0; i < operandList.Count - 1; i++) { Blade bladeA = operandList[i] as Blade; Blade bladeB = operandList[i + 1] as Blade; if (bladeA == null || bladeB == null) { continue; } if (bladeA.Grade == 1 && bladeB.Grade == 1) { operandList.RemoveAt(i + 1); GeometricProduct innerProduct = new GeometricProduct(new List <Operand>() { bladeA.scalar, bladeB.scalar, context.BilinearForm(bladeA.vectorList[0], bladeB.vectorList[0]) }); Blade outerProduct = new Blade(new GeometricProduct(new List <Operand>() { bladeA.scalar.Copy(), bladeB.scalar.Copy() })); outerProduct.vectorList.Add(bladeA.vectorList[0]); outerProduct.vectorList.Add(bladeB.vectorList[0]); operandList[i] = new Sum(new List <Operand>() { innerProduct, outerProduct }); return(this); } } return(null); }
public override Operand EvaluationStep(Context context) { if (operandList.Count != 1) { throw new MathException(string.Format("Exponential function expected exactly 1 argument, got {0}.", operandList.Count)); } Operand operand = base.EvaluationStep(context); if (operand != null) { return(operand); } Operand exponentOperand = operandList[0]; if (exponentOperand is Sum sumExponent) { GeometricProduct geometricProduct = new GeometricProduct(); for (int i = 0; i < sumExponent.operandList.Count; i++) { geometricProduct.operandList.Add(new Exponent(new List <Operand>() { sumExponent.operandList[i] })); } return(geometricProduct); } if (exponentOperand is NumericScalar numericScalar) { return(new NumericScalar(Math.Exp(numericScalar.value))); } else if (exponentOperand is Blade blade) { Blade basisBlade = new Blade(new NumericScalar(1.0), blade.vectorList.ToList()); InnerProduct innerProduct = new InnerProduct(); innerProduct.operandList.Add(new NumericScalar(-1.0)); innerProduct.operandList.Add(basisBlade.Copy()); innerProduct.operandList.Add(basisBlade.Copy()); Operand result = Operand.ExhaustEvaluation(innerProduct, context); if (result.IsMultiplicativeIdentity) { Sum sum = new Sum(); sum.operandList.Add(new Cosine(new List <Operand>() { blade.scalar.Copy() })); sum.operandList.Add(new GeometricProduct(new List <Operand>() { basisBlade.Copy(), new Sine(new List <Operand>() { blade.scalar.Copy() }) })); return(sum); } else if (result.IsAdditiveIdentity) { // What if it's a null blade? } else { // Hyperbolic cosine/sine? } } return(null); }