Skip to content

Instantly share code, notes, and snippets.

@dejw
Created January 16, 2010 09:04
Show Gist options
  • Select an option

  • Save dejw/278748 to your computer and use it in GitHub Desktop.

Select an option

Save dejw/278748 to your computer and use it in GitHub Desktop.
require "rubygems"
require "Node"
require "Configuration"
require "CoreService"
require "MonitoringObjectsAttributes"
require "sga"
require "SimpleAgents"
require "actions" # FIXME: duza litera
require "example-services"
require "mocha"
require "main" # FIXME: bootstrap o nazwie Starter
# FIXME: ma ladowac wszystkie pliki zeby dalo sie podlaczac gemy
#require "visualization" # FIXME: duza litera
require "NameService"
require "CommunicationProtocol"
include SimpleGeneticAlgorithm
#class NameService
# def initialize(bus)
# (@mock = Mocha::Mock.new).stubs(:register_address).returns("Genetic", "Simple1", "Aggregate1", "Simple2", "Simple3")
# end
# def register_address(suggest)
# @mock.register_address
# end
#end
class CommunicationService
def priority
-1/0.0
end
def initialize(bus)
@bus = bus
@listeners = Hash.new
@queue = []
@mutex = Mutex.new
@cond = ConditionVariable.new
end
def get_nameservice_address(*args)
"localhost"
end
def init
@bus.get(:logger).info("Communication: init")
@running = true
@thread = Thread.new do
@bus.get(:logger).info("Communication: start")
@mutex.synchronize do
while running? do
@bus.get(:logger).info("Communication: wait for message")
@cond.wait(@mutex) if @queue.empty?
until @queue.empty? do
agent, message, listener = @queue.shift
@bus.get(:logger).info("Communication: sending '#{listener}' -> '#{agent}'")
Thread.new do
@listeners[agent].on_received_message(message, listener, listener)
@bus.get(:logger).info("Communication: sent '#{listener}' -> '#{agent}'")
end
end
end
@bus.stopped(self)
end
end
end
def running?
@running
end
def stop
@mutex.synchronize do
@running = false
@cond.signal
end
end
def register_listener(l, name)
@bus.get(:logger).info("Communication: registering '#{name}'")
@listeners[name] = l
end
def send_unicast_message_to_agent(agentname, message, listener)
send_unicast_message(nil, listener, message, listener)
end
def send_unicast_message(node_address, service_name, message, listener)
# if service_name != listener
@mutex.synchronize do
raise "listener named '#{service_name}' does not exist!" unless @listeners.has_key?(service_name)
@queue.push([service_name, message, listener])
@cond.signal
end
# else
# @listeners[service_name].on_received_message(message, listener, listener)
# end
end
end
class Service < CoreService::Service
def priority
100
end
# -----
def start
raise StateError, "Service not initialized!" if @state != :initialized
@state = :running
self.children.each_value { |agent| agent.start }
end
def send_remote_message( msg )
raise StateError, "Service not initialized!" if @state != :initialized
@remote_msg_mutex.synchronize do
@mailbox.put( self, msg )
@new_msg_condition.signal
end
end
def wait_for_remote_message
@thread = Thread.new do
@remote_msg_mutex.synchronize do
while running? or @state == :initialized do
# Oczekuj na wiadomosc z zewnatrz
@bus.get(:logger).info("CoreService: wait for remote message")
@new_msg_condition.wait( @remote_msg_mutex ) unless @mailbox.has_messages?(self)
@bus.get(:logger).info("CoreService: received remote message")
while message = @mailbox.get( self ) do
process_remote_message( message )
end
end
end
end
end
# ----
def notify(who)
self.logger.info("CoreService: '#{who.address}' stopped")
super(who)
end
def detached
self.logger.debug("CoreService: detached")
end
def init
super()
self.children.each do |address, agent|
@bus.get(:communicationService).register_listener(agent, address)
end
self.wait_for_remote_message
@bus.get(:logger).info("CoreService: init")
end
def register_address(suggest = nil)
@bus.get(:nameServiceProxy).register_address
end
end
class GeneticWorkplace < CoreService::Workplace
# ---- Do przeniesienia do CoreService/Agent.rb -----
def on_received_message(message, sender, *args)
raise NotImplementerError, "!!"
end
def dispatch_moa_message(address, message)
if address == self.address
receiveMOAMessage(message)
#self.send_message(address, :moa, message)
else
@agents.each_value do |agent|
agent.dispatch_moa_message(address, message)
end
end
end
# ----
# gen_operators - operatory genetyczne (dowolna liczba argumentow klasy pochodnej od GeneticOperator)
attr_writer :dimension, :gen_operators, :init_size, :iterations
after :init do
@gen_operators ||= []
@fit_function = FitnessFunction.new(@dimension)
@gen_repr = GenotypeRepresentation.new(@dimension)
self.stop_condition = CoreService::Condition::Times.new(@iterations)
self.logger.info("Genetic: dimension = #{@dimension}")
self.logger.info("Genetic: population_size = #{@init_size}")
self.logger.info("Genetic: iterations = #{@iterations}")
self.logger.info("Genetic: gen_operators = [#{@gen_operators.collect(&:class).join(", ")}]")
self.population_size = @init_size
self['best_evaluation'] = @fit_function.evaluate(@population[0])
self['best_inidividual'] = @gen_repr.copy_individual(@population[0])
self[:iteration] = @iterations
@last_population = evaluate_population()
end
def perform
self.fetch_all_messages
pop_distribution = Array.new(@population_size)
pop_evaluation = @last_population
self[:iteration] = self.stop_condition.times if self.stop_condition.times % 100 == 0
eval_sum = pop_evaluation.inject(0) { |sum, eval| sum + eval }
# wygeneruj dystrybuante rozkladu prawdopodobienstw wyboru osobnikow do nowej populacji
pop_distribution[0] = 1 - pop_evaluation[0]/eval_sum
1.upto(@population_size-2) { |i| pop_distribution[i] = pop_distribution[i-1] + (1 - pop_evaluation[0]/eval_sum) }
pop_distribution[@population_size-1] = 1.0
# stworz nowa populacje
new_population = Array.new(@population_size)
0.upto(@population_size-1) do |index|
i = give_index(pop_distribution)
new_population[index] = @gen_repr.copy_individual(@population[i])
end
# przeprowadz mutacje wedlug kolejnych operatorow
selected_individuals = Array.new()
selected_indices = Array.new()
@gen_operators.each do |operator|
@population_size.times do
selected_individuals.clear
selected_indices.clear
(operator.no_of_args).times do
i = rand(@population_size)
selected_individuals.push(new_population[i])
selected_indices.push(i)
end
mutated_individuals = operator.perform(*selected_individuals)
selected_indices.each_with_index { |i, index| new_population[i] = mutated_individuals[index] }
end
end
@population = new_population
# ocen biezaca populacje (po wykonaniu operatorow genetycznych)
@last_population = evaluate_population()
end
protected
# Akcesor umozliwiajacy ustawienie nowej liczebnosci populacji.
def population_size=(pop_size)
if pop_size != @population_size
raise ArgumentError.new("Population size must be positive integer") unless pop_size > 0
@population_size = pop_size
reset_population()
end
end
# Usuwa aktualna populacje i generuje nowa.
def reset_population
(@population ||=[]).clear
0.upto(@population_size-1) { @population.push(@gen_repr.create_individual) }
@best_current_evaluation = @fit_function.evaluate(@population[0])
@best_current_individual = @gen_repr.copy_individual(@population[0])
end
# Ocenia poszczegolne osobniki aktualnej populacji - uaktualniajac
# dane najlepszego dotychczas znalezionego osobnika
def evaluate_population()
pop_evaluation = Array.new(@population_size)
best_current_evaluation = self['best_evaluation']
best_current_individual = self['best_inidividual']
0.upto(@population_size-1) do |i|
pop_evaluation[i] = @fit_function.evaluate(@population[i])
if pop_evaluation[i] < best_current_evaluation
best_current_evaluation = pop_evaluation[i]
best_current_individual = @gen_repr.copy_individual(@population[i])
end
end
if best_current_evaluation < self['best_evaluation']
self['best_evaluation'] = best_current_evaluation
self['best_inidividual'] = @gen_repr.copy_individual(best_current_individual)
end
pop_evaluation
end
# Zwraca losowy indeks elementu wedlug podanej dystrybuanty
def give_index(pop_distr)
rand_num = rand
i = 0
until rand_num < pop_distr[i]
i += 1
end
i
end
end
class GeneticObserver
include MOA::Observer
def initialize(bus)
@bus = bus
@mutex = Mutex.new
@cond = ConditionVariable.new
@bus.get(:logger).info("#{self.class}: init")
end
def start
@running = true
@thread = Thread.new do
@bus.get(:logger).info("#{self.class}: start")
@bus.get(:logger).info("#{self.class}: getting properties")
props = @bus.get(:moaService).getProperties("test", "Genetic")
@bus.get(:logger).info("#{self.class}: received properties: #{props.inspect}")
props.each_key do |key|
@bus.get(:moaService).register(self, "Genetic", key)
end
@bus.stopped(self)
end
end
def update(observable, property, value)
@bus.get(:logger).info("#{self.class}: #{observable} has changed '#{property}' to #{value.inspect}")
end
end
class SimpleObserver < GeneticObserver
def start
@running = true
@thread = Thread.new do
@bus.get(:logger).info("#{self.class}: start")
@bus.get(:logger).info("#{self.class}: getting properties")
props = @bus.get(:moaService).getProperties("test", "Simple2")
@bus.get(:logger).info("#{self.class}: received properties: #{props.inspect}")
props.each_key do |key|
@bus.get(:moaService).register(self, "Simple2", key)
end
@bus.stopped(self)
end
end
end
class MutationStrategy
def initialize(agent)
@agent = agent
end
def run
for i in @agent.individuals
i.each_with_index do |element, idx|
i[idx] += rand(3) * (rand(2) == 0 ? 1 : -1)
end
end
end
end
class MyAggregate < Aggregate
after :init do
logger.info("#{self.address}: init")
end
end
class MySimpleAgent < SimpleAgent
attr_accessor :individuals
after :init do
logger.info("#{self.address}: init")
self['best_sum'] = 0
@individuals ||= []
end
after :step do
self['dummy'] = rand() if rand() < 0.1
for i in @individuals
i[0]=i[1]+i[2]
self['best_sum'] = i[0] if self['best_sum'] < i[0]
end
self.parent.addAction(Actions::UseStrategyAction.new(MutationStrategy.new(self))) if self.parent.respond_to?(:addAction)
end
end
CONFIG = <<EOF
agents_tree:
value:
- genetic
genetic:
class: GeneticWorkplace
setter-parameters:
iterations: 150
init_size: 1000
dimension: 2
gen_operators:
type: array
contents:
0..15: mutation
children:
- simple
- aggregate
aggregate:
class: MyAggregate
setter-parameters:
agents:
- simple2
- simple
simple:
class: MySimpleAgent
simple2:
class: MySimpleAgent
setter-parameters:
individuals:
-
- 0
- 2
- 3
-
- 0
- 7
- 1
mutation:
class: SimpleGeneticAlgorithm::MutationOperator
constructor-parameters:
- 1
crossover:
class: SimpleGeneticAlgorithm::CrossoverOperator
constructor-parameters:
- 1
EOF
class Bus < Node::Bus
def stopped(service)
super(service)
if service.is_a?(Service)
self.stop
self.wait
end
end
end
SERVICES = {
:logger => ExampleServices::LoggerService,
:configService => Configuration::Configuration,
:moaService => MOA::MOAService,
# :communicationService => CommunicationService,
:communicationService => CommunicationProtocol::Communicator,
:nameService => NameService,
:nameServiceProxy => NameServiceProxy,
:coreService => Service,
:simpleObserver => SimpleObserver,
:geneticObserver => GeneticObserver,
}
#bus = Bus.new(SERVICES)
#bus.get(:configService).set_configuration(YAML::load(config.gsub("\t", " ")))
#bus.start
#bus.wait
# -------
# FIXME: metoda readConfiguration() powinna rzucac bledem jesli nie ma pliku!
class MyApp < Wx::App
def on_init
@serwer = PluginSerwer.new(self)
@serviceData = Hash.new
@services = SERVICES.merge({:starter => self})
@bus = Node::Bus.new(@services)
@serviceData[:bus] = @bus
# @serwer.readConfiguration("config.yml") # TODO: parametryzacja ladowania zeby nie zmieniac kodu o zgrozo
@bus.get(:configService).set_configuration(YAML::load(CONFIG.gsub("\t", " ")))
(@frame = StarterFrame.new(self)).show
@window = @frame
end
end
begin
MyApp.new.main_loop
rescue Exception => e
puts e.message
puts e.backtrace
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment