View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.samples;
23  
24  import java.awt.Color;
25  import java.awt.Dimension;
26  import java.awt.Graphics;
27  import java.awt.Graphics2D;
28  import java.awt.GridBagConstraints;
29  import java.awt.GridBagLayout;
30  import java.awt.Insets;
31  import java.awt.RenderingHints;
32  import java.awt.geom.Rectangle2D;
33  import java.util.ArrayList;
34  import java.util.List;
35  
36  import javax.swing.JComponent;
37  import javax.swing.JLabel;
38  import javax.swing.JTextArea;
39  
40  import org.hipparchus.exception.LocalizedCoreFormats;
41  import org.hipparchus.exception.MathIllegalArgumentException;
42  import org.hipparchus.geometry.euclidean.twod.Vector2D;
43  import org.hipparchus.random.HaltonSequenceGenerator;
44  import org.hipparchus.random.JDKRandomGenerator;
45  import org.hipparchus.random.MersenneTwister;
46  import org.hipparchus.random.RandomGenerator;
47  import org.hipparchus.random.RandomVectorGenerator;
48  import org.hipparchus.random.SobolSequenceGenerator;
49  import org.hipparchus.random.UncorrelatedRandomVectorGenerator;
50  import org.hipparchus.random.UniformRandomGenerator;
51  import org.hipparchus.samples.ExampleUtils.ExampleFrame;
52  import org.hipparchus.util.FastMath;
53  import org.hipparchus.util.Pair;
54  
55  /**
56   * Plots 2D samples drawn from various pseudo / quasi-random generators.
57   */
58  // CHECKSTYLE: stop HideUtilityClassConstructor
59  public class LowDiscrepancyGeneratorComparison {
60  
61      /** Empty constructor.
62       * <p>
63       * This constructor is not strictly necessary, but it prevents spurious
64       * javadoc warnings with JDK 18 and later.
65       * </p>
66       * @since 3.0
67       */
68      public LowDiscrepancyGeneratorComparison() { // NOPMD - unnecessary constructor added intentionally to make javadoc happy
69          // nothing to do
70      }
71  
72      /** Generate points within a circle.
73       * @param samples number of points
74       * @param generator random generator to generate points
75       * @return generated points
76       */
77      public static List<Vector2D> makeCircle(int samples, final RandomVectorGenerator generator) {
78          List<Vector2D> points = new ArrayList<Vector2D>();
79          for (double i = 0; i < samples; i++) {
80              double[] vector = generator.nextVector();
81              Vector2D point = new Vector2D(vector);
82              points.add(point);
83          }
84  
85          // normalize points first
86          points = normalize(points);
87  
88          // now test if the sample is within the unit circle
89          List<Vector2D> circlePoints = new ArrayList<Vector2D>();
90          for (Vector2D p : points) {
91              if (p.getNorm() < 1.0) {
92                  circlePoints.add(p);
93              }
94          }
95  
96          return circlePoints;
97      }
98  
99      /** Generate points.
100      * @param samples number of points
101      * @param generator random generator to generate points
102      * @return generated points
103      */
104     public static List<Vector2D> makeRandom(int samples, RandomVectorGenerator generator) {
105         List<Vector2D> points = new ArrayList<Vector2D>();
106         for (double i = 0; i < samples; i++) {
107             double[] vector = generator.nextVector();
108             Vector2D point = new Vector2D(vector);
109             points.add(point);
110         }
111 
112         return normalize(points);
113     }
114 
115     /** Normalize points.
116      * @param input input points
117      * @return normalized points in the [-1, 1 ] range
118      */
119     public static List<Vector2D> normalize(final List<Vector2D> input) {
120         // find the mininum and maximum x value in the dataset
121         double minX = Double.MAX_VALUE;
122         double maxX = Double.MIN_VALUE;
123         for (Vector2D p : input) {
124             minX = FastMath.min(minX, p.getX());
125             maxX = FastMath.max(maxX, p.getX());
126         }
127 
128         double minY;
129         double maxY;
130 
131         // use the minimum to detect if we either have input values in the range [0, 1] or [-sqrt(3), sqrt(3)]
132         if (FastMath.abs(minX) < 0.1) {
133             minX = minY = 0.0;
134             maxX = maxY = 1.0;
135         } else {
136             minX = minY = -FastMath.sqrt(3);
137             maxX = maxY = FastMath.sqrt(3);
138         }
139 
140         double rangeX = maxX - minX;
141         double rangeY = maxY - minY;
142         List<Vector2D> points = new ArrayList<Vector2D>();
143         for (Vector2D p : input) {
144             double[] arr = p.toArray();
145             // normalize to the range [-1, 1]
146             arr[0] = (arr[0] - minX) / rangeX * 2 - 1;
147             arr[1] = (arr[1] - minY) / rangeY * 2 - 1;
148             points.add(new Vector2D(arr));
149         }
150         return points;
151     }
152 
153     /** Main frame for displaying low discrepancy points. */
154     @SuppressWarnings("serial")
155     public static class Display extends ExampleFrame {
156 
157         /** Simple constructor.
158          */
159         public Display() {
160             setTitle("Hipparchus: Pseudo/Quasi-random examples");
161             setSize(800, 800);
162 
163             setLayout(new GridBagLayout());
164 
165             int[] datasets = new int[] { 256, 1000, 2500, 1000 };
166             List<Pair<String, RandomVectorGenerator>> generators = new ArrayList<Pair<String, RandomVectorGenerator>>();
167 
168             generators.add(new Pair<String, RandomVectorGenerator>("Uncorrelated\nUniform(JDK)",
169                     new UncorrelatedRandomVectorGenerator(2, new UniformRandomGenerator(new JDKRandomGenerator()))));
170             generators.add(new Pair<String, RandomVectorGenerator>("Independent\nRandom(MT)", new RandomVectorGenerator() {
171 
172                 private final RandomGenerator[] rngs = new RandomGenerator[] {
173                     new MersenneTwister(0),
174                     new MersenneTwister(1)
175                 };
176 
177                 public double[] nextVector() {
178                     final double[] vector = new double[2];
179                     vector[0] = rngs[0].nextDouble();
180                     vector[1] = rngs[1].nextDouble();
181                     return vector;
182                 }
183 
184             }));
185             generators.add(new Pair<String, RandomVectorGenerator>("HaltonSequence", new HaltonSequenceGenerator(2)));
186             generators.add(new Pair<String, RandomVectorGenerator>("SobolSequence", new SobolSequenceGenerator(2)));
187 
188             GridBagConstraints c = new GridBagConstraints();
189             c.fill = GridBagConstraints.VERTICAL;
190             c.gridx = 1;
191             c.gridy = 0;
192             c.insets = new Insets(2, 2, 2, 2);
193 
194             for (Pair<String, RandomVectorGenerator> pair : generators) {
195                 JTextArea text = new JTextArea(pair.getFirst());
196                 text.setEditable(false);
197                 text.setOpaque(false);
198                 add(text, c);
199                 c.gridx++;
200             }
201             int saveY = ++c.gridy;
202 
203             c.gridx = 0;
204             for (int type = 0; type < 4; type++) {
205                 JLabel text = new JLabel("n=" + String.valueOf(datasets[type]));
206                 text.setOpaque(false);
207                 add(text, c);
208                 c.gridy++;
209             }
210 
211             c.gridy = saveY;
212             for (int type = 0; type < 4; type++) {
213                 c.gridx = 1;
214 
215                 for (Pair<String, RandomVectorGenerator> pair : generators) {
216                     List<Vector2D> points = null;
217                     int samples = datasets[type];
218                     switch (type) {
219                         case 0:
220                             points = makeRandom(samples, pair.getValue());
221                             break;
222                         case 1:
223                             points = makeRandom(samples, pair.getValue());
224                             break;
225                         case 2:
226                             points = makeRandom(samples, pair.getValue());
227                             break;
228                         case 3:
229                             points = makeCircle(samples, pair.getValue());
230                             break;
231                         default:
232                             throw new MathIllegalArgumentException(LocalizedCoreFormats.INTERNAL_ERROR);
233                     }
234                     add(new Plot(points), c);
235                     c.gridx++;
236                 }
237 
238                 c.gridy++;
239             }
240         }
241     }
242 
243     /** Plotting component. */
244     @SuppressWarnings("serial")
245     public static class Plot extends JComponent {
246 
247         /** Padding. */
248         private static final double PAD = 10;
249 
250         /** Points to plot. */
251         private List<Vector2D> points;
252 
253         /** Simple constructor.
254          * @param points points to plot
255          */
256         public Plot(final List<Vector2D> points) {
257             this.points = points;
258         }
259 
260         /** {@inheritDoc} */
261         @Override
262         protected void paintComponent(Graphics g) {
263             super.paintComponent(g);
264             Graphics2D g2 = (Graphics2D)g;
265             g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
266                                 RenderingHints.VALUE_ANTIALIAS_ON);
267 
268             int w = getWidth();
269             int h = getHeight();
270 
271             g2.clearRect(0, 0, w, h);
272 
273             g2.setPaint(Color.black);
274             g2.drawRect(0, 0, w - 1, h - 1);
275 
276             for (Vector2D point : points) {
277                 Vector2D p = transform(point, w, h);
278                 double[] arr = p.toArray();
279                 g2.draw(new Rectangle2D.Double(arr[0] - 1, arr[1] - 1, 2, 2));
280             }
281         }
282 
283         /** {@inheritDoc} */
284         @Override
285         public Dimension getPreferredSize() {
286             return new Dimension(140, 140);
287         }
288 
289         /** Transform a point.
290          * @param point initial point
291          * @param width plot width
292          * @param height plot height
293          * @return transformed point
294          */
295         private Vector2D transform(Vector2D point, int width, int height) {
296             double[] arr = point.toArray();
297             return new Vector2D(new double[] { PAD + (arr[0] + 1) / 2.0 * (width - 2 * PAD),
298                                                   height - PAD - (arr[1] + 1) / 2.0 * (height - 2 * PAD) });
299         }
300     }
301 
302     /** Program entry point.
303      * @param args program arguments (unused here)
304      */
305     public static void main(String[] args) {
306         ExampleUtils.showExampleFrame(new Display());
307     }
308 
309 }