Skip to content

Commit 5671d59

Browse files
Merge pull request #39 from SpiceSharp/feature/sven
v3.0.4
2 parents 6581aae + b20723e commit 5671d59

65 files changed

Lines changed: 4383 additions & 1013 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using SpiceSharp;
2+
using SpiceSharp.Simulations;
3+
using SpiceSharpBehavioral.Parsers.Nodes;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Numerics;
7+
8+
namespace SpiceSharpBehavioral.Builders.Direct
9+
{
10+
/// <summary>
11+
/// A builder that can compute values.
12+
/// </summary>
13+
/// <seealso cref="IBuilder{T}" />
14+
public class ComplexBuilder : IDirectBuilder<Complex>
15+
{
16+
/// <inheritdoc/>
17+
public event EventHandler<FunctionFoundEventArgs<Complex>> FunctionFound;
18+
19+
/// <inheritdoc/>
20+
public event EventHandler<VariableFoundEventArgs<Complex>> VariableFound;
21+
22+
/// <inheritdoc/>
23+
public double FudgeFactor { get; set; } = 1e-20;
24+
25+
/// <inheritdoc/>
26+
public double RelativeTolerance { get; set; } = 1e-6;
27+
28+
/// <inheritdoc/>
29+
public double AbsoluteTolerance { get; set; } = 1e-12;
30+
31+
/// <summary>
32+
/// Builds the specified value from the specified expression node.
33+
/// </summary>
34+
/// <param name="expression">The expression node.</param>
35+
/// <returns>
36+
/// The value.
37+
/// </returns>
38+
public Complex Build(Node expression)
39+
{
40+
switch (expression)
41+
{
42+
case BinaryOperatorNode bn:
43+
switch (bn.NodeType)
44+
{
45+
case NodeTypes.Add: return Build(bn.Left) + Build(bn.Right);
46+
case NodeTypes.Subtract: return Build(bn.Left) - Build(bn.Right);
47+
case NodeTypes.Multiply: return Build(bn.Left) * Build(bn.Right);
48+
case NodeTypes.Divide: return HelperFunctions.SafeDivide(Build(bn.Left), Build(bn.Right), FudgeFactor);
49+
case NodeTypes.Modulo: return Build(bn.Left).Real % Build(bn.Right).Real;
50+
case NodeTypes.LessThan: return Build(bn.Left).Real < Build(bn.Right).Real ? 1.0 : 0.0;
51+
case NodeTypes.GreaterThan: return Build(bn.Left).Real > Build(bn.Right).Real ? 1.0 : 0.0;
52+
case NodeTypes.LessThanOrEqual: return Build(bn.Left).Real <= Build(bn.Right).Real ? 1.0 : 0.0;
53+
case NodeTypes.GreaterThanOrEqual: return Build(bn.Left).Real >= Build(bn.Right).Real ? 1.0 : 0.0;
54+
case NodeTypes.Equals: return HelperFunctions.Equals(Build(bn.Left), Build(bn.Right), RelativeTolerance, AbsoluteTolerance) ? 1.0 : 0.0;
55+
case NodeTypes.NotEquals: return HelperFunctions.Equals(Build(bn.Left), Build(bn.Right), RelativeTolerance, AbsoluteTolerance) ? 0.0 : 1.0;
56+
case NodeTypes.And: return Build(bn.Left).Real > 0.5 && Build(bn.Right).Real > 0.5 ? 1.0 : 0.0;
57+
case NodeTypes.Or: return Build(bn.Left).Real > 0.5 || Build(bn.Right).Real > 0.5 ? 1.0 : 0.0;
58+
case NodeTypes.Xor: return Build(bn.Left).Real > 0.5 ^ Build(bn.Right).Real > 0.5 ? 1.0 : 0.0;
59+
case NodeTypes.Pow: return HelperFunctions.Power(Build(bn.Left), Build(bn.Right));
60+
}
61+
break;
62+
63+
case UnaryOperatorNode un:
64+
switch (un.NodeType)
65+
{
66+
case NodeTypes.Plus: return Build(un.Argument);
67+
case NodeTypes.Minus: return -Build(un.Argument);
68+
case NodeTypes.Not: return Build(un.Argument).Real > 0.5 ? 0.0 : 1.0;
69+
}
70+
break;
71+
72+
case TernaryOperatorNode tn:
73+
return Build(tn.Condition).Real > 0.5 ? Build(tn.IfTrue) : Build(tn.IfFalse);
74+
75+
case FunctionNode fn:
76+
var fargs = new FunctionFoundEventArgs<Complex>(this, fn);
77+
OnFunctionFound(fargs);
78+
if (!fargs.Created)
79+
throw new SpiceSharpException($"Could not recognized function {fn.Name}");
80+
return fargs.Result;
81+
82+
case ConstantNode cn:
83+
return cn.Literal;
84+
85+
case VariableNode vn:
86+
var vargs = new VariableFoundEventArgs<Complex>(this, vn);
87+
OnVariableFound(vargs);
88+
if (!vargs.Created)
89+
throw new SpiceSharpException($"Could not recognized variable {vn.Name}");
90+
return vargs.Result;
91+
}
92+
return BuildNode(expression);
93+
}
94+
95+
/// <summary>
96+
/// Called when a function was found.
97+
/// </summary>
98+
/// <param name="args">The event arguments.</param>
99+
protected virtual void OnFunctionFound(FunctionFoundEventArgs<Complex> args) => FunctionFound?.Invoke(this, args);
100+
101+
/// <summary>
102+
/// Called when a variable was found.
103+
/// </summary>
104+
/// <param name="args">The event arguments.</param>
105+
protected virtual void OnVariableFound(VariableFoundEventArgs<Complex> args) => VariableFound?.Invoke(this, args);
106+
107+
/// <summary>
108+
/// Builds the node.
109+
/// </summary>
110+
/// <param name="node">The node.</param>
111+
/// <returns>The built value.</returns>
112+
/// <exception cref="SpiceSharpException">Unrecognized node</exception>
113+
protected virtual Complex BuildNode(Node node)
114+
{
115+
throw new SpiceSharpException("Unrecognized expression node {0}".FormatString(node));
116+
}
117+
}
118+
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
using SpiceSharp;
2+
using SpiceSharpBehavioral.Diagnostics;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Numerics;
6+
7+
namespace SpiceSharpBehavioral.Builders.Direct
8+
{
9+
/// <summary>
10+
/// Helper methods for a <see cref="ComplexBuilder"/>.
11+
/// </summary>
12+
public static class ComplexBuilderHelper
13+
{
14+
private static readonly Random _rnd = new Random();
15+
16+
/// <summary>
17+
/// A set of default functions.
18+
/// </summary>
19+
public static readonly Dictionary<string, Func<Complex[], Complex>> Defaults = new Dictionary<string, Func<Complex[], Complex>>(StringComparer.OrdinalIgnoreCase)
20+
{
21+
{ "abs", Abs },
22+
{ "sgn", Sgn },
23+
{ "sqrt", Sqrt },
24+
{ "pow", Pow },
25+
{ "pwr", Pwr },
26+
{ "pwrs", Pwrs },
27+
{ "log", Log }, { "ln", Log },
28+
{ "log10", Log10 },
29+
{ "exp", Exp },
30+
{ "sin", Sin },
31+
{ "cos", Cos },
32+
{ "tan", Tan },
33+
{ "sinh", Sinh },
34+
{ "cosh", Cosh },
35+
{ "tanh", Tanh },
36+
{ "asin", Asin }, { "arcsin", Asin },
37+
{ "acos", Acos }, { "arccos", Acos },
38+
{ "atan", Atan }, { "arctan", Atan },
39+
{ "u", U },
40+
{ "u2", U2 },
41+
{ "uramp", URamp },
42+
{ "ceil", Ceil },
43+
{ "floor", Floor },
44+
{ "nint", Nint },
45+
{ "round", Round },
46+
{ "square", Square },
47+
{ "pwl", Pwl }, { "dpwl(0)", PwlDerivative },
48+
{ "table", Pwl }, { "dtable(0)", PwlDerivative },
49+
{ "tbl", Pwl }, { "dtbl(0)", PwlDerivative },
50+
{ "min", Min },
51+
{ "max", Max },
52+
{ "atan2", Atan2 },
53+
{ "hypot", Hypot },
54+
{ "rnd", Random }, { "rand", Random },
55+
{ "if", If }
56+
};
57+
58+
private static Complex[] Check(this Complex[] args, int expected)
59+
{
60+
if (args == null || args.Length != expected)
61+
throw new ArgumentMismatchException(expected, args?.Length ?? 0);
62+
return args;
63+
}
64+
65+
/// <summary>
66+
/// Registers the default functions.
67+
/// </summary>
68+
/// <param name="builder">The builder.</param>
69+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> is <c>null</c>.</exception>
70+
public static void RegisterDefaultFunctions(this IDirectBuilder<Complex> builder)
71+
{
72+
builder.ThrowIfNull(nameof(builder));
73+
builder.FunctionFound += OnFunctionFound;
74+
}
75+
76+
private static void OnFunctionFound(object sender, FunctionFoundEventArgs<Complex> args)
77+
{
78+
if (!args.Created && Defaults.TryGetValue(args.Function.Name, out var definition))
79+
{
80+
var arguments = new Complex[args.Function.Arguments.Count];
81+
for (var i = 0; i < arguments.Length; i++)
82+
arguments[i] = args.Builder.Build(args.Function.Arguments[i]);
83+
args.Result = definition(arguments);
84+
}
85+
}
86+
87+
// No-argument functions
88+
private static Complex Random(Complex[] args) { args.Check(0); return _rnd.NextDouble(); }
89+
90+
// One-argument functions
91+
private static Complex Abs(Complex[] args) => args.Check(1)[0].Magnitude;
92+
private static Complex Sgn(Complex[] args) => Math.Sign(args.Check(1)[0].Real);
93+
private static Complex Sqrt(Complex[] args) => HelperFunctions.Sqrt(args.Check(1)[0]);
94+
private static Complex URamp(Complex[] args) => HelperFunctions.Ramp(args.Check(1)[0]);
95+
private static Complex U(Complex[] args) => HelperFunctions.Step(args.Check(1)[0]);
96+
private static Complex U2(Complex[] args) => HelperFunctions.Step2(args.Check(1)[0]);
97+
private static Complex Sin(Complex[] args) => Complex.Sin(args.Check(1)[0]);
98+
private static Complex Cos(Complex[] args) => Complex.Cos(args.Check(1)[0]);
99+
private static Complex Tan(Complex[] args) => Complex.Tan(args.Check(1)[0]);
100+
private static Complex Asin(Complex[] args) => Complex.Asin(args.Check(1)[0]);
101+
private static Complex Acos(Complex[] args) => Complex.Acos(args.Check(1)[0]);
102+
private static Complex Atan(Complex[] args) => Complex.Atan(args.Check(1)[0]);
103+
private static Complex Sinh(Complex[] args) => Complex.Sinh(args.Check(1)[0]);
104+
private static Complex Cosh(Complex[] args) => Complex.Cosh(args.Check(1)[0]);
105+
private static Complex Tanh(Complex[] args) => Complex.Tanh(args.Check(1)[0]);
106+
private static Complex Ceil(Complex[] args)
107+
{
108+
var arg = args.Check(1)[0];
109+
return new Complex(Math.Ceiling(arg.Real), Math.Ceiling(arg.Imaginary));
110+
}
111+
private static Complex Floor(Complex[] args)
112+
{
113+
var arg = args.Check(1)[0];
114+
return new Complex(Math.Floor(arg.Real), Math.Floor(arg.Imaginary));
115+
}
116+
private static Complex Exp(Complex[] args) => Complex.Exp(args.Check(1)[0]);
117+
private static Complex Log(Complex[] args) => HelperFunctions.Log(args.Check(1)[0]);
118+
private static Complex Log10(Complex[] args) => HelperFunctions.Log10(args.Check(1)[0]);
119+
private static Complex Square(Complex[] args) { var x = args.Check(1)[0]; return x * x; }
120+
private static Complex Nint(Complex[] args)
121+
{
122+
var arg = args.Check(1)[0];
123+
return new Complex(Math.Round(arg.Real, 0), Math.Round(arg.Imaginary, 0));
124+
}
125+
126+
// Two-argument functions
127+
private static Complex Pow(Complex[] args) { args.Check(2); return Complex.Pow(args[0], args[1]); }
128+
private static Complex Pwr(Complex[] args) { args.Check(2); return HelperFunctions.Power(args[0], args[1]); }
129+
private static Complex Pwrs(Complex[] args) { args.Check(2); return HelperFunctions.Power2(args[0], args[1]); }
130+
private static Complex Min(Complex[] args) { args.Check(2); return Math.Min(args[0].Real, args[1].Real); }
131+
private static Complex Max(Complex[] args) { args.Check(2); return Math.Max(args[0].Real, args[1].Real); }
132+
private static Complex Round(Complex[] args)
133+
{
134+
var arg = args.Check(2)[0];
135+
var n = (int)args[1].Real;
136+
return new Complex(Math.Round(arg.Real, n), Math.Round(arg.Imaginary, n));
137+
}
138+
private static Complex Atan2(Complex[] args) { args.Check(2); return Math.Atan2(args[0].Real, args[1].Real); }
139+
private static Complex Hypot(Complex[] args) { args.Check(2); return HelperFunctions.Hypot(args[0], args[1]); }
140+
141+
// Three-argument functions
142+
private static Complex If(Complex[] args) { args.Check(3); return args[0].Real > 0.5 ? args[1] : args[2]; }
143+
144+
// N-argument functions
145+
private static Complex Pwl(Complex[] args)
146+
{
147+
if (args.Length < 3)
148+
throw new ArgumentMismatchException(3, args.Length);
149+
int points = (args.Length - 1) / 2;
150+
if (args.Length % 2 == 0)
151+
throw new ArgumentMismatchException(points * 2 + 1, args.Length);
152+
153+
var data = new Point[points];
154+
for (var i = 0; i < points; i++)
155+
data[i] = new Point(args[i * 2 + 1].Real, args[i * 2 + 2].Real);
156+
return HelperFunctions.Pwl(args[0].Real, data);
157+
}
158+
private static Complex PwlDerivative(Complex[] args)
159+
{
160+
if (args.Length < 3)
161+
throw new ArgumentMismatchException(3, args.Length);
162+
int points = (args.Length - 1) / 2;
163+
if (args.Length % 2 == 0)
164+
throw new ArgumentMismatchException(points * 2 + 1, args.Length);
165+
166+
var data = new Point[points];
167+
for (var i = 0; i < points; i++)
168+
data[i] = new Point(args[i * 2 + 1].Real, args[i * 2 + 2].Real);
169+
return HelperFunctions.PwlDerivative(args[0].Real, data);
170+
}
171+
}
172+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
using SpiceSharp;
2+
using SpiceSharpBehavioral.Parsers.Nodes;
3+
using System;
4+
5+
namespace SpiceSharpBehavioral.Builders.Direct
6+
{
7+
/// <summary>
8+
/// Event arguments for when a function has been found.
9+
/// </summary>
10+
/// <typeparam param="T">The value type.</typeparam>
11+
public class FunctionFoundEventArgs<T> : EventArgs
12+
{
13+
/// <summary>
14+
/// Gets the builder.
15+
/// </summary>
16+
/// <value>
17+
/// The builder.
18+
/// </value>
19+
public IDirectBuilder<T> Builder { get; }
20+
21+
/// <summary>
22+
/// The function.
23+
/// </summary>
24+
public FunctionNode Function { get; }
25+
26+
/// <summary>
27+
/// Gets or sets the result of the function.
28+
/// </summary>
29+
public T Result
30+
{
31+
get => _result;
32+
set
33+
{
34+
_result = value;
35+
Created = true;
36+
}
37+
}
38+
private T _result;
39+
40+
/// <summary>
41+
/// Gets a flag whether ojr not the function result has been assigned.
42+
/// </summary>
43+
/// <value>
44+
/// <c>true</c> if the function result was assigned; otherwise, <c>false</c>.
45+
/// </value>
46+
public bool Created { get; private set; }
47+
48+
/// <summary>
49+
/// Initializes a new instance of the <see cref="FunctionFoundEventArgs{T}"/> class.
50+
/// </summary>
51+
/// <param name="builder">The builder.</param>
52+
/// <param name="function">The function node.</param>
53+
/// <exception cref="ArgumentNullException">Thrown if <paramref name="builder"/> or <paramref name="function"/> is <c>null</c>.</exception>
54+
public FunctionFoundEventArgs(IDirectBuilder<T> builder, FunctionNode function)
55+
{
56+
Builder = builder.ThrowIfNull(nameof(builder));
57+
Function = function.ThrowIfNull(nameof(function));
58+
_result = default;
59+
Created = false;
60+
}
61+
}
62+
}

0 commit comments

Comments
 (0)