Skip to content

Instantly share code, notes, and snippets.

@MrYossu
Created October 10, 2023 22:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MrYossu/423b88fd1e845fd6ad5335ffde74cfb4 to your computer and use it in GitHub Desktop.
Save MrYossu/423b88fd1e845fd6ad5335ffde74cfb4 to your computer and use it in GitHub Desktop.
C# code for symbolic differentiation
// See https://www.pixata.co.uk/2023/10/10/symbolic-differentiation-in-c/ for an explanation
// The code below can be pasted as-is into LinqPad (change the language to "C# statements"), or used in a console application in Visual studio.
DifExp de1 = new(new(1), "*", new(2));
Console.WriteLine($"{de1} -> {de1.Differentiate("x")}");
DifExp de2 = new(new("x"), "+", de1);
Console.WriteLine($"{de2} -> {de2.Differentiate("x")}");
DifExp de3 = new(new(2), "*", new("x"));
Console.WriteLine($"{de3} -> {de3.Differentiate("x")}");
DifExp de4 = new(de3, "+", new(10));
Console.WriteLine($"{de4} -> {de4.Differentiate("x")}");
DifExp de5 = new(new("x"), "*", new("x")); // Don't so this, use the power ctor instead. Same with the next one
Console.WriteLine($"{de5} -> {de5.Differentiate("x")}");
DifExp de6 = new(new("x"), "*", new(new("x"), "*", new("x")));
Console.WriteLine($"{de6} -> {de6.Differentiate("x")}");
DifExp de7 = new(new("x"), 2);
Console.WriteLine($"{de7} -> {de7.Differentiate("x")}");
DifExp de8 = new(new("x"), 4);
Console.WriteLine($"{de8} -> {de8.Differentiate("x")}");
class DifExp {
// This isn't so neat, as there is nothing to stop someone accessing the Expression property of a value, etc
private DifExpType Type { init; get; }
private int Value { init; get; }
private string Symbol { init; get; } = "";
private (DifExp E1, string Op, DifExp E2) Expression { init; get; }
public DifExp(int value) {
Type = DifExpType.Value;
Value = value;
}
public DifExp(string symbol) {
Type = DifExpType.Symbol;
Symbol = symbol;
}
public DifExp(string symbol, int power) {
// This ctor is used for raising a symbol to an integer power
Type = DifExpType.Expresssion;
Expression = (new(symbol), "^", new(power));
}
public DifExp(DifExp e1, string op, DifExp e2, bool simplify = true) {
if (!new[] { "+", "*", "-", "^" }.Contains(op)) {
throw new ArgumentException($"Unsupported operator: {op}");
}
//Console.WriteLine($"Ctor({e1}, {op}, {e2})");
if (simplify) {
//Console.WriteLine("ctor - calling Simplify()");
DifExp exp = Simplify(e1, op, e2);
Type = exp.Type;
Value = exp.Value;
Symbol = exp.Symbol;
Expression = exp.Expression;
} else {
//Console.WriteLine("ctor - not calling Simplify()");
Type = DifExpType.Expresssion;
Expression = (e1, op, e2);
}
}
public static DifExp Simplify(DifExp e) {
if (e.Type != DifExpType.Expresssion) {
return e;
}
return Simplify(e.Expression.E1, e.Expression.Op, e.Expression.E2);
}
public static DifExp Simplify(DifExp e1, string op, DifExp e2) {
DifExp e1s = Simplify(e1);
DifExp e2s = Simplify(e2);
if (e1s.Type == DifExpType.Value && e2s.Type == DifExpType.Value) {
// Both expressions are values, so combine them wth the operator specified
return new(op switch {
"+" => e1s.Value + e2s.Value,
"*" => e1s.Value * e2s.Value,
"-" => e1s.Value - e2s.Value
});
} else if (op == "*" && (e1s.Type == DifExpType.Value && e1s.Value == 0) || (e2s.Type == DifExpType.Value && e2s.Value == 0)) {
// We are multiplying, and one expression is a value of zero, then we are a value of zero
return new(0);
} else if (op == "*" && e1s.Type == DifExpType.Value && e1s.Value == 1) {
// We are multiplying, and the first expression is a value of one, then we are the other expression
return e2s;
} else if (op == "*" && e2s.Type == DifExpType.Value && e2s.Value == 1) {
// Ditto the previous comment for the second expression
return e1s;
} else if (op == "+" && e1s.Type == DifExpType.Value && e1s.Value == 0) {
// We are adding, and the first expression is a value of zero, then we are the other expression
return e2s;
} else if (op == "+" && e2s.Type == DifExpType.Value && e2s.Value == 0) {
// Ditto the previous comment for the second expression
return e1s;
}
// Nothing to simplify, so return a new expression
return new(e1s, op, e2s, false);
}
public DifExp Differentiate(string symbol) =>
Type switch {
DifExpType.Value => new(0),
DifExpType.Symbol => symbol == Symbol ? new(1) : new(Symbol),
DifExpType.Expresssion =>
Expression.Op switch {
"+" => new(Expression.E1.Differentiate(symbol), "+", Expression.E2.Differentiate(symbol)),
"-" => new(Expression.E1.Differentiate(symbol), "-", Expression.E2.Differentiate(symbol)),
"*" => new(new(Expression.E1.Differentiate(symbol), "*", Expression.E2), "+", new(Expression.E1, "*", Expression.E2.Differentiate(symbol))),
"^" => new(new(Expression.E2.Value), "*", new(new(symbol), "^", new(Expression.E2.Value - 1)))
}
};
public override string ToString() =>
Type switch {
DifExpType.Value => Value.ToString(),
DifExpType.Symbol => Symbol,
DifExpType.Expresssion => $"({Expression.E1} {Expression.Op} {Expression.E2})"
};
public enum DifExpType {
Value,
Symbol,
Expresssion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment