-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #40 from SpiceSharp/development
v3.0.4
- Loading branch information
Showing
65 changed files
with
4,383 additions
and
1,013 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
using SpiceSharp; | ||
using SpiceSharp.Simulations; | ||
using SpiceSharpBehavioral.Parsers.Nodes; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Numerics; | ||
|
||
namespace SpiceSharpBehavioral.Builders.Direct | ||
{ | ||
/// <summary> | ||
/// A builder that can compute values. | ||
/// </summary> | ||
/// <seealso cref="IBuilder{T}" /> | ||
public class ComplexBuilder : IDirectBuilder<Complex> | ||
{ | ||
/// <inheritdoc/> | ||
public event EventHandler<FunctionFoundEventArgs<Complex>> FunctionFound; | ||
|
||
/// <inheritdoc/> | ||
public event EventHandler<VariableFoundEventArgs<Complex>> VariableFound; | ||
|
||
/// <inheritdoc/> | ||
public double FudgeFactor { get; set; } = 1e-20; | ||
|
||
/// <inheritdoc/> | ||
public double RelativeTolerance { get; set; } = 1e-6; | ||
|
||
/// <inheritdoc/> | ||
public double AbsoluteTolerance { get; set; } = 1e-12; | ||
|
||
/// <summary> | ||
/// Builds the specified value from the specified expression node. | ||
/// </summary> | ||
/// <param name="expression">The expression node.</param> | ||
/// <returns> | ||
/// The value. | ||
/// </returns> | ||
public Complex Build(Node expression) | ||
{ | ||
switch (expression) | ||
{ | ||
case BinaryOperatorNode bn: | ||
switch (bn.NodeType) | ||
{ | ||
case NodeTypes.Add: return Build(bn.Left) + Build(bn.Right); | ||
case NodeTypes.Subtract: return Build(bn.Left) - Build(bn.Right); | ||
case NodeTypes.Multiply: return Build(bn.Left) * Build(bn.Right); | ||
case NodeTypes.Divide: return HelperFunctions.SafeDivide(Build(bn.Left), Build(bn.Right), FudgeFactor); | ||
case NodeTypes.Modulo: return Build(bn.Left).Real % Build(bn.Right).Real; | ||
case NodeTypes.LessThan: return Build(bn.Left).Real < Build(bn.Right).Real ? 1.0 : 0.0; | ||
case NodeTypes.GreaterThan: return Build(bn.Left).Real > Build(bn.Right).Real ? 1.0 : 0.0; | ||
case NodeTypes.LessThanOrEqual: return Build(bn.Left).Real <= Build(bn.Right).Real ? 1.0 : 0.0; | ||
case NodeTypes.GreaterThanOrEqual: return Build(bn.Left).Real >= Build(bn.Right).Real ? 1.0 : 0.0; | ||
case NodeTypes.Equals: return HelperFunctions.Equals(Build(bn.Left), Build(bn.Right), RelativeTolerance, AbsoluteTolerance) ? 1.0 : 0.0; | ||
case NodeTypes.NotEquals: return HelperFunctions.Equals(Build(bn.Left), Build(bn.Right), RelativeTolerance, AbsoluteTolerance) ? 0.0 : 1.0; | ||
case NodeTypes.And: return Build(bn.Left).Real > 0.5 && Build(bn.Right).Real > 0.5 ? 1.0 : 0.0; | ||
case NodeTypes.Or: return Build(bn.Left).Real > 0.5 || Build(bn.Right).Real > 0.5 ? 1.0 : 0.0; | ||
case NodeTypes.Xor: return Build(bn.Left).Real > 0.5 ^ Build(bn.Right).Real > 0.5 ? 1.0 : 0.0; | ||
case NodeTypes.Pow: return HelperFunctions.Power(Build(bn.Left), Build(bn.Right)); | ||
} | ||
break; | ||
|
||
case UnaryOperatorNode un: | ||
switch (un.NodeType) | ||
{ | ||
case NodeTypes.Plus: return Build(un.Argument); | ||
case NodeTypes.Minus: return -Build(un.Argument); | ||
case NodeTypes.Not: return Build(un.Argument).Real > 0.5 ? 0.0 : 1.0; | ||
} | ||
break; | ||
|
||
case TernaryOperatorNode tn: | ||
return Build(tn.Condition).Real > 0.5 ? Build(tn.IfTrue) : Build(tn.IfFalse); | ||
|
||
case FunctionNode fn: | ||
var fargs = new FunctionFoundEventArgs<Complex>(this, fn); | ||
OnFunctionFound(fargs); | ||
if (!fargs.Created) | ||
throw new SpiceSharpException($"Could not recognized function {fn.Name}"); | ||
return fargs.Result; | ||
|
||
case ConstantNode cn: | ||
return cn.Literal; | ||
|
||
case VariableNode vn: | ||
var vargs = new VariableFoundEventArgs<Complex>(this, vn); | ||
OnVariableFound(vargs); | ||
if (!vargs.Created) | ||
throw new SpiceSharpException($"Could not recognized variable {vn.Name}"); | ||
return vargs.Result; | ||
} | ||
return BuildNode(expression); | ||
} | ||
|
||
/// <summary> | ||
/// Called when a function was found. | ||
/// </summary> | ||
/// <param name="args">The event arguments.</param> | ||
protected virtual void OnFunctionFound(FunctionFoundEventArgs<Complex> args) => FunctionFound?.Invoke(this, args); | ||
|
||
/// <summary> | ||
/// Called when a variable was found. | ||
/// </summary> | ||
/// <param name="args">The event arguments.</param> | ||
protected virtual void OnVariableFound(VariableFoundEventArgs<Complex> args) => VariableFound?.Invoke(this, args); | ||
|
||
/// <summary> | ||
/// Builds the node. | ||
/// </summary> | ||
/// <param name="node">The node.</param> | ||
/// <returns>The built value.</returns> | ||
/// <exception cref="SpiceSharpException">Unrecognized node</exception> | ||
protected virtual Complex BuildNode(Node node) | ||
{ | ||
throw new SpiceSharpException("Unrecognized expression node {0}".FormatString(node)); | ||
} | ||
} | ||
} |
172 changes: 172 additions & 0 deletions
172
SpiceSharpBehavioral/Builders/Direct/ComplexBuilderHelper.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
using SpiceSharp; | ||
using SpiceSharpBehavioral.Diagnostics; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Numerics; | ||
|
||
namespace SpiceSharpBehavioral.Builders.Direct | ||
{ | ||
/// <summary> | ||
/// Helper methods for a <see cref="ComplexBuilder"/>. | ||
/// </summary> | ||
public static class ComplexBuilderHelper | ||
{ | ||
private static readonly Random _rnd = new Random(); | ||
|
||
/// <summary> | ||
/// A set of default functions. | ||
/// </summary> | ||
public static readonly Dictionary<string, Func<Complex[], Complex>> Defaults = new Dictionary<string, Func<Complex[], Complex>>(StringComparer.OrdinalIgnoreCase) | ||
{ | ||
{ "abs", Abs }, | ||
{ "sgn", Sgn }, | ||
{ "sqrt", Sqrt }, | ||
{ "pow", Pow }, | ||
{ "pwr", Pwr }, | ||
{ "pwrs", Pwrs }, | ||
{ "log", Log }, { "ln", Log }, | ||
{ "log10", Log10 }, | ||
{ "exp", Exp }, | ||
{ "sin", Sin }, | ||
{ "cos", Cos }, | ||
{ "tan", Tan }, | ||
{ "sinh", Sinh }, | ||
{ "cosh", Cosh }, | ||
{ "tanh", Tanh }, | ||
{ "asin", Asin }, { "arcsin", Asin }, | ||
{ "acos", Acos }, { "arccos", Acos }, | ||
{ "atan", Atan }, { "arctan", Atan }, | ||
{ "u", U }, | ||
{ "u2", U2 }, | ||
{ "uramp", URamp }, | ||
{ "ceil", Ceil }, | ||
{ "floor", Floor }, | ||
{ "nint", Nint }, | ||
{ "round", Round }, | ||
{ "square", Square }, | ||
{ "pwl", Pwl }, { "dpwl(0)", PwlDerivative }, | ||
{ "table", Pwl }, { "dtable(0)", PwlDerivative }, | ||
{ "tbl", Pwl }, { "dtbl(0)", PwlDerivative }, | ||
{ "min", Min }, | ||
{ "max", Max }, | ||
{ "atan2", Atan2 }, | ||
{ "hypot", Hypot }, | ||
{ "rnd", Random }, { "rand", Random }, | ||
{ "if", If } | ||
}; | ||
|
||
private static Complex[] Check(this Complex[] args, int expected) | ||
{ | ||
if (args == null || args.Length != expected) | ||
throw new ArgumentMismatchException(expected, args?.Length ?? 0); | ||
return args; | ||
} | ||
|
||
/// <summary> | ||
/// Registers the default functions. | ||
/// </summary> | ||
/// <param name="builder">The builder.</param> | ||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> is <c>null</c>.</exception> | ||
public static void RegisterDefaultFunctions(this IDirectBuilder<Complex> builder) | ||
{ | ||
builder.ThrowIfNull(nameof(builder)); | ||
builder.FunctionFound += OnFunctionFound; | ||
} | ||
|
||
private static void OnFunctionFound(object sender, FunctionFoundEventArgs<Complex> args) | ||
{ | ||
if (!args.Created && Defaults.TryGetValue(args.Function.Name, out var definition)) | ||
{ | ||
var arguments = new Complex[args.Function.Arguments.Count]; | ||
for (var i = 0; i < arguments.Length; i++) | ||
arguments[i] = args.Builder.Build(args.Function.Arguments[i]); | ||
args.Result = definition(arguments); | ||
} | ||
} | ||
|
||
// No-argument functions | ||
private static Complex Random(Complex[] args) { args.Check(0); return _rnd.NextDouble(); } | ||
|
||
// One-argument functions | ||
private static Complex Abs(Complex[] args) => args.Check(1)[0].Magnitude; | ||
private static Complex Sgn(Complex[] args) => Math.Sign(args.Check(1)[0].Real); | ||
private static Complex Sqrt(Complex[] args) => HelperFunctions.Sqrt(args.Check(1)[0]); | ||
private static Complex URamp(Complex[] args) => HelperFunctions.Ramp(args.Check(1)[0]); | ||
private static Complex U(Complex[] args) => HelperFunctions.Step(args.Check(1)[0]); | ||
private static Complex U2(Complex[] args) => HelperFunctions.Step2(args.Check(1)[0]); | ||
private static Complex Sin(Complex[] args) => Complex.Sin(args.Check(1)[0]); | ||
private static Complex Cos(Complex[] args) => Complex.Cos(args.Check(1)[0]); | ||
private static Complex Tan(Complex[] args) => Complex.Tan(args.Check(1)[0]); | ||
private static Complex Asin(Complex[] args) => Complex.Asin(args.Check(1)[0]); | ||
private static Complex Acos(Complex[] args) => Complex.Acos(args.Check(1)[0]); | ||
private static Complex Atan(Complex[] args) => Complex.Atan(args.Check(1)[0]); | ||
private static Complex Sinh(Complex[] args) => Complex.Sinh(args.Check(1)[0]); | ||
private static Complex Cosh(Complex[] args) => Complex.Cosh(args.Check(1)[0]); | ||
private static Complex Tanh(Complex[] args) => Complex.Tanh(args.Check(1)[0]); | ||
private static Complex Ceil(Complex[] args) | ||
{ | ||
var arg = args.Check(1)[0]; | ||
return new Complex(Math.Ceiling(arg.Real), Math.Ceiling(arg.Imaginary)); | ||
} | ||
private static Complex Floor(Complex[] args) | ||
{ | ||
var arg = args.Check(1)[0]; | ||
return new Complex(Math.Floor(arg.Real), Math.Floor(arg.Imaginary)); | ||
} | ||
private static Complex Exp(Complex[] args) => Complex.Exp(args.Check(1)[0]); | ||
private static Complex Log(Complex[] args) => HelperFunctions.Log(args.Check(1)[0]); | ||
private static Complex Log10(Complex[] args) => HelperFunctions.Log10(args.Check(1)[0]); | ||
private static Complex Square(Complex[] args) { var x = args.Check(1)[0]; return x * x; } | ||
private static Complex Nint(Complex[] args) | ||
{ | ||
var arg = args.Check(1)[0]; | ||
return new Complex(Math.Round(arg.Real, 0), Math.Round(arg.Imaginary, 0)); | ||
} | ||
|
||
// Two-argument functions | ||
private static Complex Pow(Complex[] args) { args.Check(2); return Complex.Pow(args[0], args[1]); } | ||
private static Complex Pwr(Complex[] args) { args.Check(2); return HelperFunctions.Power(args[0], args[1]); } | ||
private static Complex Pwrs(Complex[] args) { args.Check(2); return HelperFunctions.Power2(args[0], args[1]); } | ||
private static Complex Min(Complex[] args) { args.Check(2); return Math.Min(args[0].Real, args[1].Real); } | ||
private static Complex Max(Complex[] args) { args.Check(2); return Math.Max(args[0].Real, args[1].Real); } | ||
private static Complex Round(Complex[] args) | ||
{ | ||
var arg = args.Check(2)[0]; | ||
var n = (int)args[1].Real; | ||
return new Complex(Math.Round(arg.Real, n), Math.Round(arg.Imaginary, n)); | ||
} | ||
private static Complex Atan2(Complex[] args) { args.Check(2); return Math.Atan2(args[0].Real, args[1].Real); } | ||
private static Complex Hypot(Complex[] args) { args.Check(2); return HelperFunctions.Hypot(args[0], args[1]); } | ||
|
||
// Three-argument functions | ||
private static Complex If(Complex[] args) { args.Check(3); return args[0].Real > 0.5 ? args[1] : args[2]; } | ||
|
||
// N-argument functions | ||
private static Complex Pwl(Complex[] args) | ||
{ | ||
if (args.Length < 3) | ||
throw new ArgumentMismatchException(3, args.Length); | ||
int points = (args.Length - 1) / 2; | ||
if (args.Length % 2 == 0) | ||
throw new ArgumentMismatchException(points * 2 + 1, args.Length); | ||
|
||
var data = new Point[points]; | ||
for (var i = 0; i < points; i++) | ||
data[i] = new Point(args[i * 2 + 1].Real, args[i * 2 + 2].Real); | ||
return HelperFunctions.Pwl(args[0].Real, data); | ||
} | ||
private static Complex PwlDerivative(Complex[] args) | ||
{ | ||
if (args.Length < 3) | ||
throw new ArgumentMismatchException(3, args.Length); | ||
int points = (args.Length - 1) / 2; | ||
if (args.Length % 2 == 0) | ||
throw new ArgumentMismatchException(points * 2 + 1, args.Length); | ||
|
||
var data = new Point[points]; | ||
for (var i = 0; i < points; i++) | ||
data[i] = new Point(args[i * 2 + 1].Real, args[i * 2 + 2].Real); | ||
return HelperFunctions.PwlDerivative(args[0].Real, data); | ||
} | ||
} | ||
} |
62 changes: 62 additions & 0 deletions
62
SpiceSharpBehavioral/Builders/Direct/FunctionFoundEventArgs.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
using SpiceSharp; | ||
using SpiceSharpBehavioral.Parsers.Nodes; | ||
using System; | ||
|
||
namespace SpiceSharpBehavioral.Builders.Direct | ||
{ | ||
/// <summary> | ||
/// Event arguments for when a function has been found. | ||
/// </summary> | ||
/// <typeparam param="T">The value type.</typeparam> | ||
public class FunctionFoundEventArgs<T> : EventArgs | ||
{ | ||
/// <summary> | ||
/// Gets the builder. | ||
/// </summary> | ||
/// <value> | ||
/// The builder. | ||
/// </value> | ||
public IDirectBuilder<T> Builder { get; } | ||
|
||
/// <summary> | ||
/// The function. | ||
/// </summary> | ||
public FunctionNode Function { get; } | ||
|
||
/// <summary> | ||
/// Gets or sets the result of the function. | ||
/// </summary> | ||
public T Result | ||
{ | ||
get => _result; | ||
set | ||
{ | ||
_result = value; | ||
Created = true; | ||
} | ||
} | ||
private T _result; | ||
|
||
/// <summary> | ||
/// Gets a flag whether ojr not the function result has been assigned. | ||
/// </summary> | ||
/// <value> | ||
/// <c>true</c> if the function result was assigned; otherwise, <c>false</c>. | ||
/// </value> | ||
public bool Created { get; private set; } | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="FunctionFoundEventArgs{T}"/> class. | ||
/// </summary> | ||
/// <param name="builder">The builder.</param> | ||
/// <param name="function">The function node.</param> | ||
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> or <paramref name="function"/> is <c>null</c>.</exception> | ||
public FunctionFoundEventArgs(IDirectBuilder<T> builder, FunctionNode function) | ||
{ | ||
Builder = builder.ThrowIfNull(nameof(builder)); | ||
Function = function.ThrowIfNull(nameof(function)); | ||
_result = default; | ||
Created = false; | ||
} | ||
} | ||
} |
Oops, something went wrong.