public override Operand EvaluationStep(Context context) { if (operandList.Count != 1) { throw new MathException($"Versor factor function expected exactly one argument; got {operandList.Count}."); } Operand operand = base.EvaluationStep(context); if (operand != null) { return(operand); } GeometricProduct factorization = Factor(operandList[0], context); factorization.freezeFlags |= FreezeFlag.DISTRIBUTION; // Provide a way to get at the individual factors. for (int i = 0; i < factorization.operandList.Count; i++) { context.operandStorage.SetStorage($"factor{i}", factorization.operandList[i].Copy()); } return(factorization); }
public override Operand Explode(ITranslator translator, Context context) { GeometricProduct geometricProduct = new GeometricProduct(); geometricProduct.operandList.Add(translator.Translate(scalar.Copy(), context)); foreach (Factor factor in factorList) { geometricProduct.operandList.Add(factor.Explode(translator, context)); } return(geometricProduct); }
public override Operand Explode(ITranslator translator, Context context) { GeometricProduct geometricProduct = new GeometricProduct(); for (int i = 0; i < Math.Abs(exponent); i++) { geometricProduct.operandList.Add(translator.Translate(new SymbolicScalarTerm(this.name), context)); } if (exponent < 0) { return(new Inverse(new List <Operand>() { geometricProduct })); } return(geometricProduct); }
public override Operand EvaluationStep(Context context) { if (operandList.Count != 1) { throw new MathException(string.Format("Inverse operation expects exactly one operand, got {0}.", operandList.Count)); } if (operandList[0].IsAdditiveIdentity) { throw new MathException("Cannot invert the additive identity."); } if (operandList[0] is GeometricProduct oldGeometricProduct) { GeometricProduct newGeometricProduct = new GeometricProduct(); for (int i = oldGeometricProduct.operandList.Count - 1; i >= 0; i--) { newGeometricProduct.operandList.Add(new Inverse(new List <Operand>() { oldGeometricProduct.operandList[i] })); } return(newGeometricProduct); } Operand operand = base.EvaluationStep(context); if (operand != null) { return(operand); } Operand inverse = operandList[0].Inverse(context); return(inverse); }
public override Operand Inverse(Context context) { GeometricProduct geometricProduct = new GeometricProduct(); // Without loss of generality, we can always write a blade in terms of an orthogonal basis. // It's then easy to realizing that a blade times its reverse is always a scalar. geometricProduct.operandList.Add(new Reverse(new List <Operand>() { this.Copy() })); geometricProduct.operandList.Add(new Inverse(new List <Operand>() { new InnerProduct(new List <Operand>() { this.Copy(), new Reverse(new List <Operand>() { this.Copy() }) }) })); return(geometricProduct); }
public override Operand Explode(ITranslator translator, Context context) { GeometricProduct geometricProduct = new GeometricProduct(); for (int i = 0; i < Math.Abs(exponent); i++) { InnerProduct innerProduct = new InnerProduct(); innerProduct.operandList.Add(translator.Translate(new Blade(this.vectorNameA), context)); innerProduct.operandList.Add(translator.Translate(new Blade(this.vectorNameB), context)); geometricProduct.operandList.Add(innerProduct); } if (exponent < 0) { return(new Inverse(new List <Operand>() { geometricProduct })); } return(geometricProduct); }
public override Operand EvaluationStep(Context context) { if (Grade == 0) { return(scalar); } // Handle an easy case of a linearly dependent set of vectors. for (int i = 0; i < vectorList.Count; i++) { for (int j = i + 1; j < vectorList.Count; j++) { if (vectorList[i] == vectorList[j]) { return(new NumericScalar(0.0)); } } } // The context may also have something to say about linear dependence. if (context.IsLinearlyDependentSet(vectorList)) { return(new NumericScalar(0.0)); } Operand operand = base.EvaluationStep(context); if (operand != null) { return(operand); } int adjacentSwapCount = 0; bool keepGoing = true; while (keepGoing) { keepGoing = false; for (int i = 0; i < vectorList.Count - 1; i++) { string vectorNameA = vectorList[i]; string vectorNameB = vectorList[i + 1]; if (string.Compare(vectorNameA, vectorNameB) > 0) { vectorList[i] = vectorNameB; vectorList[i + 1] = vectorNameA; adjacentSwapCount++; keepGoing = true; } } } if (adjacentSwapCount > 0) { if (adjacentSwapCount % 2 == 1) { scalar = new GeometricProduct(new List <Operand>() { new NumericScalar(-1.0), scalar }); } return(this); } return(null); }
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 != 2) { throw new MathException(string.Format("Power function expected exactly 2 arguments, got {0}.", operandList.Count)); } Operand operand = base.EvaluationStep(context); if (operand != null) { return(operand); } Operand baseOperand = operandList[0]; Operand exponentOperand = operandList[1]; GeometricProduct geometricProduct = new GeometricProduct(); if (exponentOperand is Sum sumExponent) { for (int i = 0; i < sumExponent.operandList.Count; i++) { geometricProduct.operandList.Add(new Power(new List <Operand>() { baseOperand.Copy(), sumExponent.operandList[i] })); } return(geometricProduct); } if (exponentOperand is NumericScalar numericScalarExponent) { if (baseOperand is NumericScalar numericScalarBase) { if (numericScalarBase.value < 0.0 && numericScalarExponent.value < 0.0) { throw new MathException("Imaginary root avoided in power computation."); } return(new NumericScalar(Math.Pow(numericScalarBase.value, numericScalarExponent.value))); } else if (Math.Round(numericScalarExponent.value) == numericScalarExponent.value) { for (int i = 0; i < (int)Math.Abs(numericScalarExponent.value); i++) { geometricProduct.operandList.Add(baseOperand.Copy()); } if (numericScalarExponent.value >= 0.0) { return(geometricProduct); } return(new Inverse(new List <Operand>() { geometricProduct })); } } geometricProduct.operandList.Add(exponentOperand); geometricProduct.operandList.Add(new Logarithm(new List <Operand>() { baseOperand })); return(new Exponent(new List <Operand>() { geometricProduct })); }
public override Operand EvaluationStep(Context context) { if (operandList.Count != 1) { throw new MathException(string.Format("Logarithm function expected exactly 1 argument, got {0}.", operandList.Count)); } Operand operand = operandList[0]; if (operand is GeometricProduct geometricProduct) { Sum sum = new Sum(); foreach (Operand factor in geometricProduct.operandList) { sum.operandList.Add(new Logarithm(new List <Operand>() { factor })); } return(sum); } operand = base.EvaluationStep(context); if (operand != null) { return(operand); } operand = operandList[0]; if (operand is Collectable collectable) { Operand scalar = collectable.scalar; if (!scalar.IsMultiplicativeIdentity) { collectable.scalar = new NumericScalar(1.0); return(new Sum(new List <Operand>() { new Logarithm(new List <Operand>() { scalar }), new Logarithm(new List <Operand>() { collectable }) }));; } } if (operand is NumericScalar numericScalar) { return(new NumericScalar(Math.Log(numericScalar.value))); } if (operand is Inverse inverse && inverse.operandList.Count == 1) { geometricProduct = new GeometricProduct(); geometricProduct.operandList.Add(new NumericScalar(-1.0)); geometricProduct.operandList.Add(new Logarithm(new List <Operand>() { inverse.operandList[0] })); return(geometricProduct); } // TODO: Solve e^x = y, for x in terms of y and e. 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); }
// The algorithm implemented here comes straight out of Christian Perwass' book. // TODO: Also, as noted by Perwass, this algorithm doesn't work for null versors. public static GeometricProduct Factor(Operand operand, Context context) { Operand currentVersor = operand.Copy(); GeometricProduct versorFactorization = new GeometricProduct(); while (true) { HashSet <int> gradeSet = DiscoverGrades(currentVersor, context); int maxGrade = gradeSet.ToList().Aggregate(0, (currentMaxGrade, grade) => grade > currentMaxGrade ? grade : currentMaxGrade); if (maxGrade <= 0) { break; } Operand blade = Operand.ExhaustEvaluation(new GradePart(new List <Operand>() { currentVersor.Copy(), new NumericScalar(maxGrade) }), context); OuterProduct bladeFactorization = null; try { bladeFactorization = FactorBlade.Factor(blade, context); } catch (MathException exc) { throw new MathException("Highest-grade-part of multivector does not factor as a blade.", exc); } Operand nonNullVector = null, magnitude = null; foreach (Operand vector in bladeFactorization.operandList) { if (vector.Grade == 1) { magnitude = Operand.ExhaustEvaluation(new Trim(new List <Operand>() { new Magnitude(new List <Operand>() { vector.Copy() }) }), context); if (!magnitude.IsAdditiveIdentity) { nonNullVector = vector; break; } } } if (nonNullVector == null) { throw new MathException("Failed to find non-null vector in blade factorization."); } Operand unitVector = Operand.ExhaustEvaluation(new Normalize(new List <Operand>() { nonNullVector }), context); versorFactorization.operandList.Insert(0, unitVector); currentVersor = Operand.ExhaustEvaluation(new Trim(new List <Operand>() { new GeometricProduct(new List <Operand>() { currentVersor, unitVector.Copy() }) }), context); } versorFactorization.operandList.Add(currentVersor); return(versorFactorization); }
// Note that here that we do not consider unary operator precedence. // So for example, if we have -1~, we don't try to choose between (-1)~ and -(1~), // though both are the same in this particular case. Also, we don't recognize unary // operator stacking. E.g., -~1 will not parse as -(~1) would. In short, working with // unary operators will sometimes requires parenthesis. public Operand BuildOperandTree(List <Token> tokenList) { while (tokenList.Count > 0) { int count = tokenList.Count; if (tokenList[0].kind == Token.Kind.LEFT_PARAN && tokenList[0].paranType == Token.ParanType.ROUND) { int i = FindMatchingParan(tokenList, 0); if (i == tokenList.Count - 1) { tokenList.RemoveAt(0); tokenList.RemoveAt(tokenList.Count - 1); } } if (tokenList.Count == count) { break; } } if (tokenList.Count == 0) { throw new ParseException("Encountered empty token list."); } if (tokenList.Count == 1) { Token token = tokenList[0]; switch (token.kind) { case Token.Kind.SYMBOL: { if (token.data[0] == '@') { return(new Variable(token.data.Substring(1))); } if (token.data[0] == '$') { return(new SymbolicScalarTerm(token.data.Substring(1))); } string vectorName = token.data; bool isBasisVector = false; List <string> basisVectorList = context.ReturnBasisVectors(); if (basisVectorList != null) { isBasisVector = basisVectorList.Contains(vectorName); if (basisVectorsOnly && !isBasisVector) { Sum sum = new Sum(); foreach (string basisVectorName in basisVectorList) { InnerProduct dot = new InnerProduct(new List <Operand>() { new Blade(vectorName), new Blade(basisVectorName) }); sum.operandList.Add(new GeometricProduct(new List <Operand>() { dot, new Blade(basisVectorName) })); } return(sum); } } generatedSymbolicVector = !isBasisVector; return(new Blade(vectorName)); } case Token.Kind.NUMBER: { double value; if (!double.TryParse(token.data, out value)) { throw new ParseException(string.Format("Encountered non-parsable number ({0}).", token.data)); } return(new NumericScalar(value)); } default: { throw new ParseException(string.Format("Encountered lone token ({0}) that isn't handled.", token.data)); } } } else if (tokenList[0].kind == Token.Kind.OPERATOR && (ParansMatch(tokenList, 1, tokenList.Count - 1) || tokenList.Count == 2 || IsFunctionPattern(tokenList.Skip(1).ToList()))) { Token token = tokenList[0]; if (token.data == "-") { return(new GeometricProduct(new List <Operand>() { new Blade(-1.0), BuildOperandTree(tokenList.Skip(1).ToList()) })); } throw new ParseException(string.Format("Encounterd unary operator ({0}) that isn't recognized on the left.", token.data)); } else if (tokenList[tokenList.Count - 1].kind == Token.Kind.OPERATOR && (ParansMatch(tokenList, 0, tokenList.Count - 2) || tokenList.Count == 2 || IsFunctionPattern(tokenList.Take(tokenList.Count - 1).ToList()))) { Token token = tokenList[tokenList.Count - 1]; if (token.data == "~") { return(new Reverse(new List <Operand>() { BuildOperandTree(tokenList.Take(tokenList.Count - 1).ToList()) })); } throw new ParseException(string.Format("Encountered unary operator ({0}) that isn't recognized on the right.", token.data)); } else if (IsFunctionPattern(tokenList)) { Token token = tokenList[0]; Operation operation = context.CreateFunction(token.data); if (operation == null) { throw new ParseException(string.Format("Encountered unknown function \"{0}\".", token.data)); } List <List <Token> > argumentList = ParseListOfTokenLists(tokenList.Skip(2).Take(tokenList.Count - 3).ToList()); foreach (List <Token> subTokenList in argumentList) { operation.operandList.Add(BuildOperandTree(subTokenList)); } return(operation); } else if (tokenList[0].paranType == Token.ParanType.SQUARE && ParansMatch(tokenList, 0, tokenList.Count - 1)) { List <List <Operand> > listOfOperandLists = new List <List <Operand> >(); List <List <Token> > rowList = ParseListOfTokenLists(tokenList.Skip(1).Take(tokenList.Count - 2).ToList()); foreach (List <Token> rowTokenList in rowList) { listOfOperandLists.Add(new List <Operand>()); List <List <Token> > colList; if (rowTokenList[0].paranType == Token.ParanType.SQUARE && ParansMatch(rowTokenList, 0, rowTokenList.Count - 1)) { colList = ParseListOfTokenLists(rowTokenList.Skip(1).Take(rowTokenList.Count - 2).ToList()); } else { colList = new List <List <Token> >() { rowTokenList } }; foreach (List <Token> subTokenList in colList) { listOfOperandLists[listOfOperandLists.Count - 1].Add(BuildOperandTree(subTokenList)); } } return(new Matrix(listOfOperandLists)); } else { // Our goal here is to find an operator of lowest precedence. It will never be // at the very beginning or end of the entire token sequence. List <Token> opTokenList = null; foreach (Token token in WalkTokensSkipSubexpressions(tokenList)) { if (token.kind != Token.Kind.OPERATOR) { continue; } // Only unary operators can be at the start or end of the token list. if (tokenList.IndexOf(token) == 0 || tokenList.IndexOf(token) == tokenList.Count - 1) { continue; } // Ignore unary operators on left. if (token.data == "-" && tokenList[tokenList.IndexOf(token) - 1].kind == Token.Kind.OPERATOR) { continue; } // Ignore unary operators on right. if (token.data == "~" && tokenList[tokenList.IndexOf(token) + 1].kind == Token.Kind.OPERATOR) { continue; } // At this point we should be reasonably sure it's a binary operator we're looking at. if (opTokenList == null || PrecedenceLevel(opTokenList[0].data) > PrecedenceLevel(token.data)) { opTokenList = new List <Token>() { token } } ; else if (opTokenList != null && PrecedenceLevel(opTokenList[0].data) == PrecedenceLevel(token.data)) { opTokenList.Add(token); } } if (opTokenList == null) { throw new ParseException("Did not encounter binary operator token."); } Token operatorToken = null; switch (OperatorAssociativity(opTokenList[0].data)) { case Associativity.LEFT_TO_RIGHT: operatorToken = opTokenList[opTokenList.Count - 1]; break; case Associativity.RIGHT_TO_LEFT: operatorToken = opTokenList[0]; break; } Operation operation = null; if (operatorToken.data == ";") { operation = new Sequence(); } else if (operatorToken.data == "+" || operatorToken.data == "-") { operation = new Sum(); } else if (operatorToken.data == "*" || operatorToken.data == "/") { operation = new GeometricProduct(); } else if (operatorToken.data == ".") { operation = new InnerProduct(); } else if (operatorToken.data == "^") { operation = new OuterProduct(); } else if (operatorToken.data == "=") { operation = new Assignment(); } else if (operatorToken.data == ":=") { operation = new Assignment(false); } if (operation == null) { throw new ParseException(string.Format("Did not recognized operator token ({0}).", operatorToken.data)); } int i = tokenList.IndexOf(operatorToken); Operand leftOperand = BuildOperandTree(tokenList.Take(i).ToList()); Operand rightOperand = BuildOperandTree(tokenList.Skip(i + 1).Take(tokenList.Count - 1 - i).ToList()); operation.operandList.Add(leftOperand); if (operatorToken.data == "-") { operation.operandList.Add(new GeometricProduct(new List <Operand>() { new NumericScalar(-1.0), rightOperand })); } else if (operatorToken.data == "/") { operation.operandList.Add(new Inverse(new List <Operand>() { rightOperand })); } else { operation.operandList.Add(rightOperand); } return(operation); } }