public static OuterProduct Factor(Operand operand, Context context) { // TODO: Support symbolic factorization? For example, it would be quite useful // to be able to evaluate factor(n*(a^b)*n). I wouldn't expect any kind // of miracle, though, like finding (n*a*n)^(n*b*n). Sum multivector = CanonicalizeMultivector(operand); OuterProduct factorization = FactorMultivectorAsBlade(multivector, context); operand = Operand.ExhaustEvaluation(factorization.Copy(), context); Sum expansion = CanonicalizeMultivector(operand); if (expansion.operandList.Count != multivector.operandList.Count) { throw new MathException("The multivector is not a blade."); } // Note that this should work by the sorting performed by the sum operation. double commonRatio = 0.0; for (int i = 0; i < expansion.operandList.Count; i++) { Blade bladeA = multivector.operandList[i] as Blade; Blade bladeB = expansion.operandList[i] as Blade; if (!bladeA.IsLike(bladeB)) { throw new MathException("The multivector is not a blade."); } double ratio = 0.0; try { ratio = (bladeA.scalar as NumericScalar).value / (bladeB.scalar as NumericScalar).value; } catch (DivideByZeroException) { ratio = 1.0; } if (Double.IsNaN(ratio)) { ratio = 1.0; } if (commonRatio == 0.0) { commonRatio = ratio; } else if (Math.Abs(ratio - commonRatio) >= context.epsilon) { throw new MathException("The multivector is not a blade."); } } factorization.operandList.Insert(0, new NumericScalar(commonRatio)); return(factorization); }
public static Operand CalculateJoin(List <Operand> operandList, Context context) { OuterProduct[] bladeArray = new OuterProduct[operandList.Count]; int j = -1; for (int i = 0; i < bladeArray.Length; i++) { try { bladeArray[i] = FactorBlade.Factor(operandList[i], context); if (j < 0 || bladeArray[i].Grade > bladeArray[j].Grade) { j = i; } } catch (MathException exc) { throw new MathException($"Failed to factor argument {i} as blade.", exc); } } OuterProduct join = bladeArray[j]; for (int i = 0; i < bladeArray.Length; i++) { if (i != j) { foreach (Operand vector in bladeArray[i].operandList) { Operand operand = Operand.ExhaustEvaluation(new Trim(new List <Operand>() { new OuterProduct(new List <Operand>() { vector.Copy(), join.Copy() }) }), context); if (!operand.IsAdditiveIdentity) { join.operandList.Add(vector); } } } } return(join); }
// Note that factorizations of blades are not generally unique. // Here I'm just going to see if I can find any factorization. // My method here is the obvious one, and probably quite naive. // Lastly, the returned factorization, if any, will be correct up to scale. // It is up to the caller to determine the correct scale. public static OuterProduct FactorMultivectorAsBlade(Sum multivector, Context context) { if (!multivector.operandList.All(operand => operand is Blade)) { throw new MathException("Can only factor elements in multivector form."); } if (!multivector.operandList.All(blade => (blade as Blade).scalar is NumericScalar)) { throw new MathException("Cannot yet perform symbolic factorization of blades."); } OuterProduct factorization = new OuterProduct(); int grade = multivector.Grade; if (grade == -1) { throw new MathException("Could not determine grade of given element. It might not be homogeneous of a single grade."); } else if (grade == 0 || grade == 1) { factorization.operandList.Add(multivector.Copy()); } else { // Given a blade A of grade n>1 and any vector v such that v.A != 0, // our method here is based on the identity L*A = (v.A) ^ ((v.A).A), // where L is a non-zero scalar. Here, v.A is of grade n-1, and // (v.A).A is of grade 1. This suggests a recursive algorithm. // This all, however, assumes a purely euclidean geometric algebra. // For those involving null-vectors, the search for a useful probing // vector requires that we take the algorithm to its conclusion before // we know if a given probing vector worked. bool foundFactorization = false; List <string> basisVectorList = context.ReturnBasisVectors(); foreach (Sum probingVector in GenerateProbingVectors(basisVectorList)) { Operand reduction = Operand.ExhaustEvaluation(new InnerProduct(new List <Operand>() { probingVector, multivector.Copy() }), context); if (!reduction.IsAdditiveIdentity) { Sum reducedMultivector = CanonicalizeMultivector(reduction); Operand vectorFactor = Operand.ExhaustEvaluation(new InnerProduct(new List <Operand>() { reducedMultivector, multivector }), context); if (vectorFactor.Grade == 1) // I'm pretty sure that this check is not necessary in a purely euclidean GA. { OuterProduct subFactorization = FactorMultivectorAsBlade(reducedMultivector, context); if (subFactorization.Grade != grade - 1) { throw new MathException($"Expected sub-factorization to be of grade {grade - 1}."); } factorization.operandList = subFactorization.operandList; factorization.operandList.Add(vectorFactor); // In a purely euclidean geometric algebra, this check is also not necessary. Operand expansion = Operand.ExhaustEvaluation(factorization.Copy(), context); if (!expansion.IsAdditiveIdentity) { foundFactorization = true; break; } } } } if (!foundFactorization) { throw new MathException("Failed to find a vector factor of the given multivector. This does not necessarily mean that the multivector doesn't factor as a blade."); } } return(factorization); }