MathExpr.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.4.0).
003  * Copyright (c) 2007-2019 Franz Wilhelmstötter
004  *
005  * Licensed under the Apache License, Version 2.0 (the "License");
006  * you may not use this file except in compliance with the License.
007  * You may obtain a copy of the License at
008  *
009  *      http://www.apache.org/licenses/LICENSE-2.0
010  *
011  * Unless required by applicable law or agreed to in writing, software
012  * distributed under the License is distributed on an "AS IS" BASIS,
013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014  * See the License for the specific language governing permissions and
015  * limitations under the License.
016  *
017  * Author:
018  *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019  */
020 package io.jenetics.prog.op;
021 
022 import static java.nio.charset.StandardCharsets.UTF_8;
023 import static java.util.Objects.requireNonNull;
024 
025 import java.io.DataInput;
026 import java.io.DataOutput;
027 import java.io.IOException;
028 import java.io.InvalidObjectException;
029 import java.io.ObjectInputStream;
030 import java.io.Serializable;
031 import java.util.Comparator;
032 import java.util.Objects;
033 import java.util.TreeSet;
034 import java.util.function.Function;
035 import java.util.stream.Collectors;
036 import java.util.stream.DoubleStream;
037 
038 import io.jenetics.internal.util.Lazy;
039 import io.jenetics.util.ISeq;
040 
041 import io.jenetics.ext.util.Tree;
042 import io.jenetics.ext.util.TreeNode;
043 
044 /**
045  * This class allows you to create a math operation tree from an expression
046  * string. The expression string may only contain functions/operations defined
047  * in {@link MathOp}.
048  *
049  <pre>{@code
050  * final MathExpr expr = MathExpr.parse("5 + 6*x + sin(x)^34 + (1 + sin(x*5)/4)/6");
051  * final double result = expr.eval(4.32);
052  * assert result == 31.170600453465315;
053  *
054  * assert 12.0 == MathExpr.eval("3*4");
055  * assert 24.0 == MathExpr.eval("3*4*x", 2);
056  * assert 28.0 == MathExpr.eval("3*4*x + y", 2, 4);
057  * }</pre>
058  *
059  @see MathOp
060  *
061  @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
062  @version 4.3
063  @since 4.1
064  */
065 public final class MathExpr
066     implements
067         Function<Double[], Double>,
068         Serializable
069 {
070 
071     private static final long serialVersionUID = 1L;
072 
073     private final Tree<? extends Op<Double>, ?> _tree;
074 
075     private final Lazy<ISeq<Var<Double>>> _vars;
076 
077     // Primary constructor.
078     private MathExpr(final Tree<? extends Op<Double>, ?> tree, boolean primary) {
079         _tree = requireNonNull(tree);
080         _vars = Lazy.of(() -> ISeq.of(
081             _tree.stream()
082                 .filter(node -> node.getValue() instanceof Var)
083                 .map(node -> (Var<Double>)node.getValue())
084                 .collect(Collectors.toCollection(() ->
085                     new TreeSet<>(Comparator.comparing(Var::name))))
086         ));
087     }
088 
089     /**
090      * Create a new {@code MathExpr} object from the given operation tree.
091      *
092      @param tree the underlying operation tree
093      @throws NullPointerException if the given {@code program} is {@code null}
094      @throws IllegalArgumentException if the given operation tree is invalid,
095      *         which means there is at least one node where the operation arity
096      *         and the node child count differ.
097      */
098     public MathExpr(final Tree<? extends Op<Double>, ?> tree) {
099         this(TreeNode.ofTree(tree)true);
100         Program.check(tree);
101     }
102 
103     /**
104      * Return the variable list of this <em>math</em> expression.
105      *
106      @return the variable list of this <em>math</em> expression
107      */
108     public ISeq<Var<Double>> vars() {
109         return _vars.get();
110     }
111 
112     /**
113      * Return the math expression as operation tree.
114      *
115      @return a new expression tree
116      */
117     public Tree<? extends Op<Double>, ?> toTree() {
118         return TreeNode.ofTree(_tree);
119     }
120 
121     /**
122      @see #eval(double...)
123      @see #eval(String, double...)
124      */
125     @Override
126     public Double apply(final Double[] args) {
127         return Program.eval(_tree, args);
128     }
129 
130     /**
131      * Convenient method, which lets you apply the program function without
132      * explicitly create a wrapper array.
133      *
134      <pre>{@code
135      *  final double result = MathExpr.parse("2*z + 3*x - y").eval(3, 2, 1);
136      *  assert result == 9.0;
137      * }</pre>
138      *
139      @see #apply(Double[])
140      @see #eval(String, double...)
141      *
142      @param args the function arguments
143      @return the evaluated value
144      @throws NullPointerException if the given variable array is {@code null}
145      @throws IllegalArgumentException if the length of the arguments array
146      *         is smaller than the program arity
147      */
148     public double eval(final double... args) {
149         final double val = apply(
150             DoubleStream.of(args)
151                 .boxed()
152                 .toArray(Double[]::new)
153         );
154         return val == -0.0 0.0 : val;
155     }
156 
157     @Override
158     public int hashCode() {
159         return _tree.hashCode();
160     }
161 
162     @Override
163     public boolean equals(final Object obj) {
164         return obj == this ||
165             obj instanceof MathExpr &&
166             Objects.equals(((MathExpr)obj)._tree, _tree);
167     }
168 
169     /**
170      * Return the string representation of this {@code MathExpr} object. The
171      * string returned by this method can be parsed again and will result in the
172      * same expression object.
173      <pre>{@code
174      *  final String expr = "5.0 + 6.0*x + sin(x)^34.0 + (1.0 + sin(x*5.0)/4.0) + 6.5";
175      *  final MathExpr tree = MathExpr.parse(expr);
176      *  assert tree.toString().equals(expr);
177      * }</pre>
178      *
179      @return the expression string
180      */
181     @Override
182     public String toString() {
183         return format(_tree);
184     }
185 
186     /**
187      * Tries to simplify {@code this} math expression.
188      *
189      <pre>{@code
190      * final MathExpr expr = MathExpr.parse("4.0 + 4.0 + x*(5.0 + 13.0)");
191      * final MathExpr simplified = expr.simplify()
192      * System.out.println(simplified);
193      * }</pre>
194      * The simplified expression will be look like this: {@code 8.0 + (x*18.0)}.
195      *
196      @see #prune(TreeNode)
197      @see #simplify(Tree)
198      *
199      @return a new simplified math expression
200      */
201     public MathExpr simplify() {
202         return new MathExpr(simplify(_tree));
203     }
204 
205 
206     /* *************************************************************************
207      *  Java object serialization
208      * ************************************************************************/
209 
210     private Object writeReplace() {
211         return new Serial(Serial.MATH_EXPR, this);
212     }
213 
214     private void readObject(final ObjectInputStream stream)
215         throws InvalidObjectException
216     {
217         throw new InvalidObjectException("Serialization proxy required.");
218     }
219 
220     void write(final DataOutput outthrows IOException {
221         final byte[] data = toString().getBytes(UTF_8);
222         out.writeInt(data.length);
223         out.write(data);
224     }
225 
226     static MathExpr read(final DataInput inthrows IOException {
227         final byte[] data = new byte[in.readInt()];
228         in.readFully(data);
229         return parse(new String(data, UTF_8));
230     }
231 
232 
233     /* *************************************************************************
234      * Static helper methods.
235      * ************************************************************************/
236 
237     /**
238      * Return the string representation of the given {@code tree} object. The
239      * string returned by this method can be parsed again and will result in the
240      * same expression object.
241      <pre>{@code
242      *  final String expr = "5.0 + 6.0*x + sin(x)^34.0 + (1.0 + sin(x*5.0)/4.0) + 6.5";
243      *  final MathExpr tree = MathExpr.parse(expr);
244      *  assert MathExpr.toString(tree.tree()).equals(expr);
245      * }</pre>
246      *
247      @param tree the tree object to convert to a string
248      @return a new expression string
249      @throws NullPointerException if the given {@code tree} is {@code null}
250      *
251      @deprecated Use {@link #format(Tree)} instead
252      */
253     @Deprecated
254     public static String toString(final Tree<? extends Op<Double>, ?> tree) {
255         return format(tree);
256     }
257 
258     /**
259      * Return the string representation of the given {@code tree} object. The
260      * string returned by this method can be parsed again and will result in the
261      * same expression object.
262      <pre>{@code
263      *  final String expr = "5.0 + 6.0*x + sin(x)^34.0 + (1.0 + sin(x*5.0)/4.0) + 6.5";
264      *  final MathExpr tree = MathExpr.parse(expr);
265      *  assert MathExpr.format(tree.tree()).equals(expr);
266      * }</pre>
267      *
268      @since 4.3
269      *
270      @param tree the tree object to convert to a string
271      @return a new expression string
272      @throws NullPointerException if the given {@code tree} is {@code null}
273      */
274     public static String format(final Tree<? extends Op<Double>, ?> tree) {
275         return MathExprFormatter.format(tree);
276     }
277 
278     /**
279      * Parses the given {@code expression} into a AST tree.
280      *
281      @param expression the expression string
282      @return the tree representation of the given {@code expression}
283      @throws NullPointerException if the given {@code expression} is {@code null}
284      @throws IllegalArgumentException if the given expression is invalid or
285      *         can't be parsed.
286      */
287     public static MathExpr parse(final String expression) {
288         final Tree<? extends Op<Double>, ?> tree = parseTree(expression);
289         Program.check(tree);
290         return new MathExpr(tree, true);
291     }
292 
293     /**
294      * Parses the given mathematical expression string and returns the
295      * mathematical expression tree. The expression may contain all functions
296      * defined in {@link MathOp}.
297      <pre>{@code
298      * final Tree<? extends Op<Double>, ?> tree = MathExpr
299      *     .parseTree("5 + 6*x + sin(x)^34 + (1 + sin(x*5)/4)/6");
300      * }</pre>
301      * The example above will lead to the following tree:
302      <pre> {@code
303      *  add
304      *  ├── add
305      *  │   ├── add
306      *  │   │   ├── 5.0
307      *  │   │   └── mul
308      *  │   │       ├── 6.0
309      *  │   │       └── x
310      *  │   └── pow
311      *  │       ├── sin
312      *  │       │   └── x
313      *  │       └── 34.0
314      *  └── div
315      *      ├── add
316      *      │   ├── 1.0
317      *      │   └── div
318      *      │       ├── sin
319      *      │       │   └── mul
320      *      │       │       ├── x
321      *      │       │       └── 5.0
322      *      │       └── 4.0
323      *      └── 6.0
324      * }</pre>
325      *
326      @param expression the expression string
327      @return the parsed expression tree
328      @throws NullPointerException if the given {@code expression} is {@code null}
329      @throws IllegalArgumentException if the given expression is invalid or
330      *         can't be parsed.
331      */
332     public static Tree<? extends Op<Double>, ?>
333     parseTree(final String expression) {
334         return MathExprParser.parse(expression);
335     }
336 
337     /**
338      * Evaluates the given {@code expression} with the given arguments.
339      *
340      <pre>{@code
341      *  final double result = MathExpr.eval("2*z + 3*x - y", 3, 2, 1);
342      *  assert result == 9.0;
343      * }</pre>
344      *
345      @see #apply(Double[])
346      @see #eval(double...)
347      *
348      @param expression the expression to evaluate
349      @param args the expression arguments, in alphabetical order
350      @return the evaluation result
351      @throws NullPointerException if the given {@code expression} is
352      *         {@code null}
353      @throws IllegalArgumentException if the given operation tree is invalid,
354      *         which means there is at least one node where the operation arity
355      *         and the node child count differ.
356      */
357     public static double eval(final String expression, final double... args) {
358         return parse(expression).eval(args);
359     }
360 
361     /**
362      * Evaluates the given {@code expression} with the given arguments.
363      *
364      @see #apply(Double[])
365      @see #eval(double...)
366      @see #eval(String, double...)
367      *
368      @since 4.4
369      *
370      @param expression the expression to evaluate
371      @param args the expression arguments, in alphabetical order
372      @return the evaluation result
373      @throws NullPointerException if the given {@code expression} is
374      *         {@code null}
375      */
376     public static double eval(
377         final Tree<? extends Op<Double>, ?> expression,
378         final double... args
379     ) {
380         return new MathExpr(expression, true).eval(args);
381     }
382 
383     /**
384      * Tries to simplify the given math tree.
385      *
386      <pre>{@code
387      * final Tree<? extends Op<Double>, ?> tree =
388      *     MathExpr.parseTree("4.0 + 4.0 + x*(5.0 + 13.0)");
389      * final Tree<? extends Op<Double>, ?> simplified = MathExpr.simplify(tree)
390      * System.out.println(simplified);
391      * }</pre>
392      * The simplified tree will be look like this:
393      <pre> {@code
394      *  add
395      *  ├── 8.0
396      *  └── mul
397      *      ├── x
398      *      └── 18.0
399      * }</pre>
400      *
401      @see #prune(TreeNode)
402      @see #simplify()
403      *
404      @param tree the math tree to simplify
405      @return the new simplified tree
406      @throws NullPointerException if the given {@code tree} is {@code null}
407      */
408     public static Tree<? extends Op<Double>, ?>
409     simplify(final Tree<? extends Op<Double>, ?> tree) {
410         return MathExprRewriter.prune(TreeNode.ofTree(tree));
411     }
412 
413     /**
414      * Tries to simplify the given math tree in place.
415      *
416      @see #simplify(Tree)
417      @see #simplify()
418      *
419      @param tree the math tree to simplify
420      @throws NullPointerException if the given {@code tree} is {@code null}
421      */
422     public static void prune(final TreeNode<Op<Double>> tree) {
423         MathExprRewriter.prune(tree);
424     }
425 
426 }