001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.ml.linear.data;
031
032import java.util.Random;
033
034import no.uib.cipr.matrix.DenseVector;
035import no.uib.cipr.matrix.Vector;
036
037import org.openimaj.math.matrix.GramSchmidtProcess;
038import org.openimaj.ml.linear.learner.perceptron.PerceptronClass;
039import org.openimaj.util.pair.IndependentPair;
040
041/**
042 *
043 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
044 */
045public class LinearPerceptronDataGenerator implements DataGenerator<double[],PerceptronClass>{
046
047        
048        private Vector origin;
049        private Vector direction;
050        private Random rng;
051        private double range;
052        private int dims;
053        private double prop;
054        private double error = 0;
055
056        
057        /**
058         * @param range the range of values
059         * @param dims the number of dimentions
060         * @param prop for both the selection of the origin and selection of the direction of the line of seperate, one dimention is chosen to be limited to the middle of range by this proportion
061         */
062        public LinearPerceptronDataGenerator(double range, int dims, double prop) {
063                this(range,dims,prop,-1);
064        }
065        
066        /**
067         * The range for each dimension
068         * @param range 
069         * @param dims 
070         * @param prop 
071         * @param seed 
072         */
073        public LinearPerceptronDataGenerator(double range, int dims, double prop, int seed) {
074                this.range = range;
075                this.dims = dims;
076                this.prop = prop;
077                if(seed < 0){
078                        this.rng = new Random();                        
079                } else {
080                        this.rng = new Random(seed);
081                }
082                
083                // limited dimention 
084                int limitedDim = rng.nextInt(dims);
085                double validRange = this.range * prop;
086                double start = (this.range - validRange)/2.;
087                
088                double[] startPoint = new double[dims];
089                double[] endPoint = new double[dims];
090                
091                double[] originPoint = new double[dims];
092                
093                for (int i = 0; i < endPoint.length; i++) {
094                        if(i == limitedDim){
095                                startPoint[i] = 0;
096                                endPoint[i] = range;
097                                originPoint[i] = start + rng.nextDouble() * validRange;
098                        } else {
099                                startPoint[i] = start + rng.nextDouble() * validRange;
100                                endPoint[i] = start + rng.nextDouble() * validRange;
101                                originPoint[i] = rng.nextDouble() * range;
102                        }
103                }
104                
105                this.direction = (DenseVector) new DenseVector(endPoint).add(-1, new DenseVector(startPoint));
106                this.origin = new DenseVector(originPoint);
107                
108        }
109
110        private double nextRandomValue() {
111                return rng.nextDouble() * range;
112        }
113
114        
115        @Override
116        public IndependentPair<double[], PerceptronClass> generate() {
117                double decide = Math.signum(rng.nextDouble() - 0.5);
118                if(decide == 0) decide = 1;
119                PerceptronClass dec = PerceptronClass.fromSign(decide);
120                while(true){                    
121                        double[] randomPoint = new double[this.dims];
122                        
123                        for (int i = 0; i < randomPoint.length; i++) {
124                                randomPoint[i] = nextRandomValue(); 
125                        }
126                        
127                        Vector v = new DenseVector(randomPoint);
128                        v.add(-1, origin);
129                        Vector d = direction.copy();
130                        double dot = v.dot(d);
131                        double sgn = Math.signum(dot);
132                        if(sgn == 0) sgn = 1;
133                        if(rng.nextDouble() <this.error){
134                                sgn = -sgn; 
135                        }
136                        PerceptronClass sgnClass = PerceptronClass.fromSign(sgn);
137                        if(sgnClass.equals(dec)) {
138                                return IndependentPair.pair(randomPoint, sgnClass);
139                        }
140                }
141        }
142
143        public Vector getOrigin() {
144                return this.origin;
145        }
146        
147        public Vector getNormDirection() {
148                return this.direction;
149        }
150
151        public Vector[] getPlane() {
152                Vector[] allInclusive = new GramSchmidtProcess().apply(new DenseVector(direction).getData());
153                Vector[] ret = new Vector[allInclusive.length - 1];
154                for (int i = 0; i < ret.length; i++) {
155                        ret[i] = allInclusive[i+1];
156                }
157                return ret;
158        }
159
160        public void setError(double d) {
161                this.error  = d;
162        }
163
164}