MathExpr.java
001 /*
002  * Java Genetic Algorithm Library (jenetics-4.3.0).
003  * Copyright (c) 2007-2018 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         return apply(DoubleStream.of(args).boxed().toArray(Double[]::new));
150     }
151 
152     @Override
153     public int hashCode() {
154         return _tree.hashCode();
155     }
156 
157     @Override
158     public boolean equals(final Object obj) {
159         return obj == this ||
160             obj instanceof MathExpr &&
161             Objects.equals(((MathExpr)obj)._tree, _tree);
162     }
163 
164     /**
165      * Return the string representation of this {@code MathExpr} object. The
166      * string returned by this method can be parsed again and will result in the
167      * same expression object.
168      <pre>{@code
169      *  final String expr = "5.0 + 6.0*x + sin(x)^34.0 + (1.0 + sin(x*5.0)/4.0) + 6.5";
170      *  final MathExpr tree = MathExpr.parse(expr);
171      *  assert tree.toString().equals(expr);
172      * }</pre>
173      *
174      @return the expression string
175      */
176     @Override
177     public String toString() {
178         return format(_tree);
179     }
180 
181     /**
182      * Tries to simplify {@code this} math expression.
183      *
184      <pre>{@code
185      * final MathExpr expr = MathExpr.parse("4.0 + 4.0 + x*(5.0 + 13.0)");
186      * final MathExpr simplified = expr.simplify()
187      * System.out.println(simplified);
188      * }</pre>
189      * The simplified expression will be look like this: {@code 8.0 + (x*18.0)}.
190      *
191      @see #prune(TreeNode)
192      @see #simplify(Tree)
193      *
194      @return a new simplified math expression
195      */
196     public MathExpr simplify() {
197         return new MathExpr(simplify(_tree));
198     }
199 
200 
201     /* *************************************************************************
202      *  Java object serialization
203      * ************************************************************************/
204 
205     private Object writeReplace() {
206         return new Serial(Serial.MATH_EXPR, this);
207     }
208 
209     private void readObject(final ObjectInputStream stream)
210         throws InvalidObjectException
211     {
212         throw new InvalidObjectException("Serialization proxy required.");
213     }
214 
215     void write(final DataOutput outthrows IOException {
216         final byte[] data = toString().getBytes(UTF_8);
217         out.writeInt(data.length);
218         out.write(data);
219     }
220 
221     static MathExpr read(final DataInput inthrows IOException {
222         final byte[] data = new byte[in.readInt()];
223         in.readFully(data);
224         return parse(new String(data, UTF_8));
225     }
226 
227 
228     /* *************************************************************************
229      * Static helper methods.
230      * ************************************************************************/
231 
232     /**
233      * Return the string representation of the given {@code tree} object. The
234      * string returned by this method can be parsed again and will result in the
235      * same expression object.
236      <pre>{@code
237      *  final String expr = "5.0 + 6.0*x + sin(x)^34.0 + (1.0 + sin(x*5.0)/4.0) + 6.5";
238      *  final MathExpr tree = MathExpr.parse(expr);
239      *  assert MathExpr.toString(tree.tree()).equals(expr);
240      * }</pre>
241      *
242      @param tree the tree object to convert to a string
243      @return a new expression string
244      @throws NullPointerException if the given {@code tree} is {@code null}
245      *
246      @deprecated Use {@link #format(Tree)} instead
247      */
248     @Deprecated
249     public static String toString(final Tree<? extends Op<Double>, ?> tree) {
250         return format(tree);
251     }
252 
253     /**
254      * Return the string representation of the given {@code tree} object. The
255      * string returned by this method can be parsed again and will result in the
256      * same expression object.
257      <pre>{@code
258      *  final String expr = "5.0 + 6.0*x + sin(x)^34.0 + (1.0 + sin(x*5.0)/4.0) + 6.5";
259      *  final MathExpr tree = MathExpr.parse(expr);
260      *  assert MathExpr.format(tree.tree()).equals(expr);
261      * }</pre>
262      *
263      @since 4.3
264      *
265      @param tree the tree object to convert to a string
266      @return a new expression string
267      @throws NullPointerException if the given {@code tree} is {@code null}
268      */
269     public static String format(final Tree<? extends Op<Double>, ?> tree) {
270         return MathExprFormatter.format(tree);
271     }
272 
273     /**
274      * Parses the given {@code expression} into a AST tree.
275      *
276      @param expression the expression string
277      @return the tree representation of the given {@code expression}
278      @throws NullPointerException if the given {@code expression} is {@code null}
279      @throws IllegalArgumentException if the given expression is invalid or
280      *         can't be parsed.
281      */
282     public static MathExpr parse(final String expression) {
283         final Tree<? extends Op<Double>, ?> tree = parseTree(expression);
284         Program.check(tree);
285         return new MathExpr(tree, true);
286     }
287 
288     /**
289      * Parses the given mathematical expression string and returns the
290      * mathematical expression tree. The expression may contain all functions
291      * defined in {@link MathOp}.
292      <pre>{@code
293      * final Tree<? extends Op<Double>, ?> tree = MathExpr
294      *     .parseTree("5 + 6*x + sin(x)^34 + (1 + sin(x*5)/4)/6");
295      * }</pre>
296      * The example above will lead to the following tree:
297      <pre> {@code
298      *  add
299      *  ├── add
300      *  │   ├── add
301      *  │   │   ├── 5.0
302      *  │   │   └── mul
303      *  │   │       ├── 6.0
304      *  │   │       └── x
305      *  │   └── pow
306      *  │       ├── sin
307      *  │       │   └── x
308      *  │       └── 34.0
309      *  └── div
310      *      ├── add
311      *      │   ├── 1.0
312      *      │   └── div
313      *      │       ├── sin
314      *      │       │   └── mul
315      *      │       │       ├── x
316      *      │       │       └── 5.0
317      *      │       └── 4.0
318      *      └── 6.0
319      * }</pre>
320      *
321      @param expression the expression string
322      @return the parsed expression tree
323      @throws NullPointerException if the given {@code expression} is {@code null}
324      @throws IllegalArgumentException if the given expression is invalid or
325      *         can't be parsed.
326      */
327     public static Tree<? extends Op<Double>, ?>
328     parseTree(final String expression) {
329         return MathExprParser.parse(expression);
330     }
331 
332     /**
333      * Evaluates the given {@code expression} with the given arguments.
334      *
335      <pre>{@code
336      *  final double result = MathExpr.eval("2*z + 3*x - y", 3, 2, 1);
337      *  assert result == 9.0;
338      * }</pre>
339      *
340      @see #apply(Double[])
341      @see #eval(double...)
342      *
343      @param expression the expression to evaluate
344      @param args the expression arguments, in alphabetical order
345      @return the evaluation result
346      @throws NullPointerException if the given {@code program} is {@code null}
347      @throws IllegalArgumentException if the given operation tree is invalid,
348      *         which means there is at least one node where the operation arity
349      *         and the node child count differ.
350      */
351     public static double eval(final String expression, final double... args) {
352         return parse(expression).eval(args);
353     }
354 
355     /**
356      * Tries to simplify the given math tree.
357      *
358      <pre>{@code
359      * final Tree<? extends Op<Double>, ?> tree =
360      *     MathExpr.parseTree("4.0 + 4.0 + x*(5.0 + 13.0)");
361      * final Tree<? extends Op<Double>, ?> simplified = MathExpr.simplify(tree)
362      * System.out.println(simplified);
363      * }</pre>
364      * The simplified tree will be look like this:
365      <pre> {@code
366      *  add
367      *  ├── 8.0
368      *  └── mul
369      *      ├── x
370      *      └── 18.0
371      * }</pre>
372      *
373      @see #prune(TreeNode)
374      @see #simplify()
375      *
376      @param tree the math tree to simplify
377      @return the new simplified tree
378      @throws NullPointerException if the given {@code tree} is {@code null}
379      */
380     public static Tree<? extends Op<Double>, ?>
381     simplify(final Tree<? extends Op<Double>, ?> tree) {
382         return MathExprSimplifier.prune(TreeNode.ofTree(tree));
383     }
384 
385     /**
386      * Tries to simplify the given math tree in place.
387      *
388      @see #simplify(Tree)
389      @see #simplify()
390      *
391      @param tree the math tree to simplify
392      @throws NullPointerException if the given {@code tree} is {@code null}
393      */
394     public static void prune(final TreeNode<Op<Double>> tree) {
395         MathExprSimplifier.prune(tree);
396     }
397 
398 }