/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. * * See the License for the specific language governing permissions * and limitations under the License. */ package siebog.agents.xjaf.pso; import java.util.Random; import javax.ejb.Remote; import javax.ejb.Stateful; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import siebog.agents.AID; import siebog.agents.Agent; import siebog.agents.AgentInitArgs; import siebog.agents.XjafAgent; import siebog.interaction.ACLMessage; import siebog.interaction.Performative; /** * * Implementation of a Particle (Particle Swarm Optimization) * * @author <a href="mailto:simic.dragan@hotmail.com">Dragan Simic</a> */ @Stateful @Remote(Agent.class) public class Particle extends XjafAgent { private static final long serialVersionUID = -4667142176673603367L; private static final Logger LOG = LoggerFactory.getLogger(Particle.class); /** * Agent ID of a Swarm agent */ private AID swarmAID; /** * Current Particle position */ private double[] position; /** * Fitness of the current position */ private double fitness; /** * Current velocity */ private double[] velocity; /** * Previous position of the Particle that had the best fitness */ private double[] bestPosition; /** * Best fitness of the previous Particle's positions */ private double bestFitness; /** * Dimension of the solution (dependent on the {@link #objectiveFunction(double[])}) */ private int dimension; private Random random = null; // range for X values private double minX; private double maxX; // range for velocity (calculated based on maxX) private double minV; private double maxV; // inertia weight private double w = 0.729; // cognitive weight private double c1 = 1.49445; // social weight private double c2 = 1.49445; // randomizations private double r1, r2; /** * @see XjafAgent.server.agm().Agent#onInit(java.io.Serializable[]) */ @Override protected void onInit(AgentInitArgs args) { // read arguments dimension = args.getInt("dimension", 0); minX = Double.parseDouble(args.get("minx", "0")); maxX = Double.parseDouble(args.get("maxx", "0")); // initialize variables minV = -1.0 * maxX; maxV = maxX; random = new Random(); // calculate random initial values double[] randomPosition = new double[dimension]; for (int j = 0; j < randomPosition.length; ++j) { double lo = minX; double hi = maxX; randomPosition[j] = (hi - lo) * random.nextDouble() + lo; } double randomFitness = objectiveFunction(randomPosition); double[] randomVelocity = new double[dimension]; for (int j = 0; j < randomVelocity.length; ++j) { double lo = -1.0 * Math.abs(maxX - minX); double hi = Math.abs(maxX - minX); randomVelocity[j] = (hi - lo) * random.nextDouble() + lo; } // initialize Particle with random values fitness = randomFitness; position = new double[dimension]; System.arraycopy(randomPosition, 0, position, 0, dimension); velocity = new double[dimension]; System.arraycopy(randomVelocity, 0, velocity, 0, dimension); // initial values are the best values bestPosition = new double[dimension]; System.arraycopy(position, 0, bestPosition, 0, dimension); bestFitness = fitness; // get the reference to the Starter (main) PSO agent AID swarmAID = agm().getAIDByRuntimeName("Swarm"); // compose the message ACLMessage message = new ACLMessage(); message.performative = Performative.REQUEST; PsoMessage psoMessage = new PsoMessage(PsoMessage.UPDATE_GLOBAL_SOLUTION, bestFitness, bestPosition); message.content = psoMessage.toString(); message.sender = myAid; message.receivers.add(swarmAID); // post the message msm().post(message); } /** * Handles incoming messages. * * @see XjafAgent.server.agm().Agent#onMessage(xjaf2x.server.msm().fipa.acl.ACLMessage) */ @Override protected void onMessage(ACLMessage message) { PsoMessage psoMessage = PsoMessage.valueOf(message.content); if (message.performative == Performative.REQUEST) { if (psoMessage.getAction().equals(PsoMessage.ITERATE_PARTICLE)) { // logger.warning("Particle [" + myAid + "] got the iterate request" ); iteration(psoMessage.getFitness(), psoMessage.getPosition()); // reply to the swarm that the iteration is finished ACLMessage reply = message.makeReply(Performative.INFORM); reply.sender = myAid; message.receivers.add(swarmAID); msm().post(reply); } } } /** * Represents one iteration of a Particle. <br> * Calculates new velocity, position and fitness, and updates local and global best position and * fitness if necessary. * * @param bestGlobalFitness * @param bestGlobalPosition */ private void iteration(double bestGlobalFitness, double[] bestGlobalPosition) { double[] newVelocity = new double[dimension]; double[] newPosition = new double[dimension]; double newFitness; // calculate new velocity for (int j = 0; j < velocity.length; ++j) { r1 = random.nextDouble(); r2 = random.nextDouble(); // main calculation of the PSO newVelocity[j] = (w * velocity[j]) + (c1 * r1 * (bestPosition[j] - position[j])) + (c2 * r2 * (bestGlobalPosition[j] - position[j])); if (newVelocity[j] < minV) { newVelocity[j] = minV; } else if (newVelocity[j] > maxV) { newVelocity[j] = maxV; } } // set new velocity System.arraycopy(newVelocity, 0, velocity, 0, dimension); // calculate new position for (int j = 0; j < position.length; ++j) { newPosition[j] = position[j] + newVelocity[j]; if (newPosition[j] < minX) { newPosition[j] = minX; } else if (newPosition[j] > maxX) { newPosition[j] = maxX; } } // set new position System.arraycopy(newPosition, 0, position, 0, dimension); // calculate new fitness newFitness = objectiveFunction(newPosition); // set new fitness fitness = newFitness; // log line for debugging LOG.warn("Particle {} iteration fitness: {}.", myAid, fitness); // update local best fitness if necessary if (newFitness < bestFitness) { System.arraycopy(newPosition, 0, bestPosition, 0, dimension); bestFitness = newFitness; } // update global best fitness if necessary if (newFitness < bestGlobalFitness) { ACLMessage message = new ACLMessage(); message.performative = Performative.REQUEST; PsoMessage psoMessage = new PsoMessage(PsoMessage.UPDATE_GLOBAL_SOLUTION, newFitness, newPosition); message.content = psoMessage.toString(); message.sender = myAid; message.receivers.add(swarmAID); msm().post(message); } } /** * function to solve (minimize) * * @param x potential solutions (array[dimension]) * @return result (fitness) */ private double objectiveFunction(double[] x) { return 3.0 + (x[0] * x[0]) + (x[1] * x[1]); } }