001    /*
002     * SimuCS - Simulator to use with Classifier Systems 
003     * MSc project - Oxford University 
004     * by Benoit Isaac - Summer 2005
005     */
006    
007    package simuLCS;
008    import java.io.PrintWriter;
009    import java.io.Serializable;
010    
011    /**
012     * Implements the ZClassifier, ie. a Classifier with a strength.
013     * A few lines of code (mutation, crossover) below draw inspiration from the freely available XCSJava
014     * by Martin V. Butz 
015     * @author Benoit, Martin V. Butz
016      */
017    public class ZClassifier extends Classifier implements Serializable,Comparable {
018    
019            /**
020             * The Strength of the classifier.
021             */
022            private double strength = ZCSConfig.strengthIni;
023    
024            /**
025             * Constructs a classifier with specified condition and specified action.
026             *
027             * @param cond The condition of the .
028             * @param act The action of the new classifier.
029             */
030            public ZClassifier(String cond, String act) {
031                    super(cond,act);
032            }
033    
034            /**
035             * Construct matching classifier with random action.
036             *
037             * @param setSize The size of the current set which the new classifier matches.
038             * @param time The actual number of instances the XCS learned from so far.
039             * (This should be set to the number of actions possible in the problem).
040             * @param situation The current problem instance/perception.
041             */
042            public ZClassifier(int actionLength, String situation) {
043                    createMatchingCondition(situation);
044                    createRandomAction(actionLength);
045            }
046    
047            /**
048             * @param strength
049             * @param state
050             * @param act
051             */
052            public ZClassifier(double strength, String state, String act) {
053                    createMatchingCondition(state);
054                    action = act;
055                    setStrength(strength);
056            }
057    
058            /**
059             * Construct a classifier with random condition and random action.
060             *
061             * @param setSize The size of the current set which the new classifier matches.
062             * @param time  The actual number of instances the XCS learned from so far.
063             * @param condLength The length of the condition of the new classifier.
064             * @param numberOfActions The number of different actions to chose from 
065             */
066            public ZClassifier(int condLength, int actionLength) {
067                    createRandomCondition(condLength);
068                    createRandomAction(actionLength);
069            }
070    
071            /**
072             * Constructs an identical XClassifier.
073             * However, the experience of the copy is set to 0 and the numerosity is set to 1 
074             * since this is indeed a new individual in a population.
075             *
076             * @param clOld The to be copied classifier.
077             */
078            public ZClassifier(ZClassifier clOld) {
079                    condition = new String(clOld.condition);
080                    action = new String(clOld.action);
081            }
082    
083            /**
084             * Creates a condition randomly considering the constant <code>P_dontcare<\code>.
085             * @param condLength The number of bits in the condition to be created
086             * @see XCSConstants#P_dontcare
087             */
088            private void createRandomCondition(int condLength) {
089                    char condArray[] = new char[condLength];
090                    for (int i = 0; i < condLength; i++) {
091                            if (ZCSConfig.drand() < ZCSConfig.P_dontcare)
092                                    condArray[i] = ZCSConfig.dontCare;
093                            else if (ZCSConfig.drand() < 0.5)
094                                    condArray[i] = '0';
095                            else
096                                    condArray[i] = '1';
097                    }
098                    condition = new String(condArray);
099            }
100    
101            /**
102             * Creates a matching condition considering the constant <code>P_dontcare<\code>.
103             * @param situation The situation which must satisfy the condition created.
104             * @see XCSConstants#P_dontcare
105             */
106            private void createMatchingCondition(String situation) {
107                    int condLength = situation.length();
108                    char condArray[] = new char[condLength];
109    
110                    for (int i = 0; i < condLength; i++) {
111                            if (ZCSConfig.drand() < ZCSConfig.P_dontcare)
112                                    condArray[i] = ZCSConfig.dontCare;
113                            else
114                                    condArray[i] = situation.charAt(i);
115                    }
116                    condition = new String(condArray);
117            }
118    
119            /**
120             * Creates a random action.
121             * <b>While XCSJava use an <code>int</code> to describe the action, my actions 
122             * are described by a <code>String</code> which defines the vector of the 
123             * response.</b>
124             * @param actionLength The number of bits in the action to be created
125             */
126            private void createRandomAction(int actionLength) {
127                    char actArray[] = new char[actionLength];
128    
129                    for (int i = 0; i < actionLength; i++) {
130                            if (ZCSConfig.drand() < 0.5)
131                                    actArray[i] = '0';
132                            else
133                                    actArray[i] = '1';
134                    }
135                    action = new String(actArray);
136            }
137    
138            /**
139             * Applies one point crossover and returns if the classifiers changed.
140             *
141             * @see XCSConstants#pX
142             * @param cl The second classifier for the crossover application.
143             */
144            // TODO apply the crossover for the action as well ?
145            public boolean onePointCrossover(ZClassifier cl) {
146                    boolean changed = false;
147                    if (ZCSConfig.drand() < ZCSConfig.pX) {
148                            int length = condition.length();
149                            int sep1 = (int) (ZCSConfig.drand() * (length));
150                            char[] cond1 = condition.toCharArray();
151                            char[] cond2 = cl.condition.toCharArray();
152                            for (int i = 0; i < sep1; i++) {
153                                    if (cond1[i] != cond2[i]) {
154                                            changed = true;
155                                            char tmp = cond1[i];
156                                            cond1[i] = cond2[i];
157                                            cond2[i] = tmp;
158                                    }
159                            }
160                            if (changed) {
161                                    condition = new String(cond1);
162                                    cl.condition = new String(cond2);
163    
164                            }
165                    }
166    
167                    return changed;
168            }
169    
170            /**
171             * Applies a niche mutation to the classifier. 
172             * This method calls mutateCondition(state) and mutateAction(numberOfActions) and returns 
173             * if at least one bit or the action was mutated.
174             *
175             * @param state The current situation/problem instance
176             */
177            public boolean applyMutation(String state, int actionLength) {
178                    boolean changed = mutateCondition(state);
179                    if (mutateAction(actionLength))
180                            changed = true;
181                    return changed;
182            }
183    
184            /**
185             * Applies a NON niche mutation to the classifier. 
186             * This method calls mutateCondition() and mutateAction(numberOfActions) and returns 
187             * if at least one bit or the action was mutated.
188             *
189             */
190            public boolean applyMutation(int actionLength) {
191                    boolean changed = mutateCondition();
192                    if (mutateAction(actionLength))
193                            changed = true;
194                    return changed;
195            }
196    
197            /**
198             * Mutates the condition of the classifier. 
199             * If one allele is mutated depends on the constant pM. 
200             * This mutation is a niche mutation. It assures that the resulting classifier
201             * still matches the current situation.
202             *
203             * @see XCSConstants#pM
204             * @param state The current situation/problem instance.
205             */
206            private boolean mutateCondition(String state) {
207                    boolean changed = false;
208                    int condLength = condition.length();
209    
210                    char[] cond = condition.toCharArray();
211                    char[] stateC = state.toCharArray();
212    
213                    for (int i = 0; i < condLength; i++) {
214                            if (ZCSConfig.drand() < ZCSConfig.pM) {
215                                    changed = true;
216                                    if (cond[i] == ZCSConfig.dontCare) {
217                                            cond[i] = stateC[i];
218                                    } else {
219                                            cond[i] = ZCSConfig.dontCare;
220                                    }
221    
222                            }
223                    }
224                    condition = new String(cond);
225                    return changed;
226            }
227    
228            /**
229             * Mutates the condition of the classifier. 
230             * If one allele is mutated depends on the constant pM. 
231             * This mutation is NOT a niche mutation. 
232             * Once the allele is selected for mutation, a character (either '0','1',or '#')
233             * is selected randomly (using an uniform disctribution).
234             *
235             * @see XCSConstants#pM
236             */
237            private boolean mutateCondition() {
238                    boolean changed = false;
239                    int condLength = condition.length();
240    
241                    char[] cond = condition.toCharArray();
242                    for (int i = 0; i < condLength; i++) {
243                            if (ZCSConfig.drand() < ZCSConfig.pM) {
244                                    changed = true;
245                                    double randChar = ZCSConfig.drand();
246                                    switch (cond[i]) {
247                                            case '0' :
248                                                    cond[i] =
249                                                            (randChar < 0.5) ? '1' : ZCSConfig.dontCare;
250                                                    break;
251                                            case '1' :
252                                                    cond[i] =
253                                                            (randChar < 0.5) ? '0' : ZCSConfig.dontCare;
254                                                    break;
255                                            case ZCSConfig.dontCare :
256                                                    if(Config.PRINT_MODE > 9)
257                                                            System.out.println("Don't care mutated into 0 or 1.");
258                                                    cond[i] = (randChar < 0.5) ? '1' : '0';
259                                                    break;
260                                    }
261                            }
262                    }
263                    condition = new String(cond);
264                    return changed;
265            }
266    
267            /**
268             * Mutates the action of the classifier.
269             * <i>Since my actions are <code>String</code>, the mutation is applied for
270             * each allele depending on the constant pM.</i>
271             * @see XCSConstants#pM
272             * @author Benoit Isaac
273             */
274            private boolean mutateAction(int actionLength) {
275                    boolean changed = false;
276                    char[] act = action.toCharArray();
277    
278                    for (int i = 0; i < actionLength; i++) {
279                            if (ZCSConfig.drand() < ZCSConfig.pM) {
280                                    // this bit of the action has to change
281                                    changed = true;
282                                    if (act[i] == '0') {
283                                            act[i] = '1';
284                                    } else {
285                                            act[i] = '0';
286                                    }
287                            }
288                    }
289                    if (changed && Config.PRINT_MODE > 4) {
290                            System.out.println(
291                                    "Mutation ! Before:" + action + " After:" + (new String(act)));
292                    }
293                    action = new String(act);
294    
295                    return changed;
296            }
297    
298            /**
299             * Returns true if the two classifiers are identical in condition and action.
300             *
301             * @param cl The classifier to be compared.
302             */
303            public boolean isIdentical(ZClassifier cl) {
304                    if (cl.condition.equals(condition) && cl.action.equals(action))
305                            return true;
306                    return false;
307            }
308    
309    
310    
311            /**
312             * Prints the classifier to the control panel.
313             * The method prints condition action prediction predictionError fitness numerosity experience actionSetSize timeStamp.
314             */
315            public void printClassifier() {
316                    System.out.println(this);
317            }
318    
319            /**
320             * Prints the classifier to the print writer (normally referencing a file).
321             * The method prints condition action prediction predictionError fitness numerosity experience actionSetSize timeStamp.
322             *
323             * @param pW The writer to which the classifier is written.
324             */
325            public void printClassifier(PrintWriter pW) {
326                    pW.println(this);
327            }
328    
329            /**
330             * Return a <code>String</code> describing the XClassifier.
331             * used to show the classifier in the listPanel.
332             */
333            public String toString() {
334                    return (super.toString() + "\t Strength:" + (float) strength);
335            }
336            /**
337             * @return
338             */
339            public double getStrength() {
340                    return strength;
341            }
342            
343            public double getValue(){
344                    return getStrength();
345            }
346    
347            /**
348             * @param d
349             */
350            public void setStrength(double d) {
351                    strength = d;
352            }
353    
354            /**
355             * @param d
356             */
357            public void addStrength(double d) {
358                    strength += d;          
359            }
360            
361            /**
362             * Used to build a Comparator to sort the Classifier in order
363             * to show them ranked to the user
364             */
365            public int compareTo(Object o) {
366                    ZClassifier z = (ZClassifier) o;
367                    if(this.equals(z))
368                            return 0;
369                    double diff = this.getStrength() - z.getStrength();
370                    if(diff != 0)
371                            return (diff >0)?1:-1;
372                            
373                    return 1; // by default
374            }
375    }