def absorb(self): """Absorb an energy from neighbors.""" # absorb incoming energy and calculate 'birthing' status incoming_energy = core.IntegerVariable() num_full = core.IntegerVariable() treshold = self.meta.full_treshold for i in range(len(self.buffers)): incoming_energy += self.neighbors[i].buffer.energy is_full = self.neighbors[i].main.new_energy >= treshold num_full += is_full self.main.energy = self.main.new_energy + incoming_energy self.main.birthing = self.main.rule.is_born(num_full) self.main.birthing *= self.main.energy < self.meta.full_treshold # neighbor's genomes crossover genomes = [] for i in range(len(self.buffers)): genomes.append(core.IntegerVariable(name="genome%d" % i)) for i in range(len(self.buffers)): is_fit = self.neighbors[i].buffer.energy > 0 is_fit = is_fit * (self.neighbors[i].buffer.rule > 0) genomes[i] += self.neighbors[i].buffer.rule * is_fit num_genes = self.main.rule.bit_width new_genome = genome_crossover( self.main, num_genes, *genomes, max_genes=self.meta.max_genes, mutation_prob=self.meta.mutation_prob) * (self.main.energy < self.meta.full_treshold) self.main.rule = new_genome + self.main.rule * (new_genome == 0)
def absorb(self): """Apply EvoLife dynamics.""" # test if cell is sustained num_neighbors = core.IntegerVariable() for i in range(len(self.buffers)): nbr_energy = self.neighbors[i].buffer.energy nbr_rule = self.neighbors[i].buffer.rule num_neighbors += xmath.min(1, (nbr_energy + nbr_rule)) is_sustained = core.IntegerVariable() is_sustained += self.main.rule.is_sustained(num_neighbors) # test if cell is born fitnesses = [] for i in range(len(self.buffers)): fitnesses.append(core.IntegerVariable(name="fit%d" % i)) num_parents = core.IntegerVariable() for gene in range(len(self.buffers)): num_parents *= 0 # hack for re-init variable for i in range(len(self.buffers)): nbr_energy = self.neighbors[i].buffer.energy nbr_rule = self.neighbors[i].buffer.rule is_alive = xmath.min(1, (nbr_energy + nbr_rule)) is_fit = self.neighbors[i].buffer.rule.is_born(gene + 1) num_parents += is_alive * is_fit fitnesses[gene] += num_parents * (num_parents == (gene + 1)) num_fit = core.IntegerVariable() num_fit += xmath.max(*fitnesses) # neighbor's genomes crossover genomes = [] for i in range(len(self.buffers)): genomes.append(core.IntegerVariable(name="genome%d" % i)) for i in range(len(self.buffers)): is_fit = self.neighbors[i].buffer.rule.is_born(num_fit) genomes[i] += self.neighbors[i].buffer.rule * is_fit num_genes = self.main.rule.bit_width old_rule = core.IntegerVariable() old_rule += self.main.rule old_energy = core.IntegerVariable() old_energy += self.main.energy new_genome = genome_crossover( self.main, num_genes, *genomes, max_genes=self.meta.max_genes, mutation_prob=self.meta.mutation_prob ) self.main.rule = new_genome + self.main.rule * (new_genome == 0) # new energy value self.main.energy *= 0 is_live = core.IntegerVariable() old_live = (old_energy + old_rule) == 0 is_live += (old_energy < 0xff) & (old_live | is_sustained) old_dead = (old_energy + old_rule) != 0 new_energy = old_energy + self.meta.death_speed * old_dead self.main.energy = new_energy * (self.main.rule == old_rule) + \ self.main.energy * (self.main.rule != old_rule) self.main.rule = old_rule * (self.main.rule == old_rule) + \ self.main.rule * (self.main.rule != old_rule) self.main.energy *= is_live self.main.rule *= is_live
def emit(self): """Apply ConcervedLife dynamics.""" self.main.new_energy = 1 * self.main.energy # calculate number of full neighbors num_full = core.IntegerVariable() for i in range(len(self.buffers)): is_full = self.neighbors[i].main.energy >= self.meta.full_treshold num_full += is_full # decide, do cell need to spread an energy me_full = self.main.energy >= self.meta.full_treshold me_empty = self.main.energy < self.meta.full_treshold me_dying = xmath.int(self.main.rule.is_sustained(num_full)) == 0 me_dying = xmath.int(me_dying) me_birthing = self.main.rule.is_born(num_full) has_free_energy = self.main.energy > self.meta.full_treshold has_free_energy = xmath.int(has_free_energy) need_spread_full = 1 * me_full * (has_free_energy | me_dying) need_spread_empty = 1 * me_empty * (xmath.int(me_birthing) == 0) need_spread = need_spread_full + need_spread_empty # search the direction to spread energy denergy = core.IntegerVariable() energy_passed = core.IntegerVariable() energy_passed *= 1 gate = core.IntegerVariable() gate += xmath.int(self.main.rng.uniform * 8) gate_final = core.IntegerVariable() treshold = self.meta.full_treshold for i in range(len(self.buffers) * 3): i_valid = (i >= gate) * (i < gate + len(self.buffers) * 2) is_birthing = self.neighbors[i % 8].main.birthing * (i < 8 + gate) is_empty = self.neighbors[i % 8].main.energy < treshold is_empty = is_empty * (i >= 8 + gate) is_fit = is_empty + is_birthing denergy *= 0 denergy += xmath.min(self.main.new_energy, self.meta.death_speed) denergy *= need_spread * is_fit * (energy_passed == 0) denergy *= i_valid gate_final *= (energy_passed != 0) gate_final += (i % 8) * (energy_passed == 0) energy_passed += denergy # spread the energy in chosen direction for i in range(len(self.buffers)): gate_fit = i == gate_final self.buffers[i].energy = energy_passed * gate_fit self.buffers[i].rule = self.main.rule * gate_fit self.main.new_energy -= energy_passed
def absorb(self): """ Implement the logic of absorb phase. Each cell just sums incoming energy and gate values. """ incoming_energy = core.IntegerVariable() incoming_gate = core.IntegerVariable() for i in range(len(self.buffers)): incoming_energy += self.neighbors[i].buffer.energy incoming_gate += self.neighbors[i].buffer.gate self.main.energy += incoming_energy self.main.gate += incoming_gate % 8
def emit(self): """ Implement the logic of emit phase. Each cell is a Poisson process that fires an energy in the gate direction, when Poisson event occurs. The rate of events depends on cell's energy level: more energy, higher the rate. The amount of energy depends on the cell's own "valency" and its gated neighbour's "valency" (energy % 8). When neighbour's valency is 0, cell spreads the amount of energy, equal to its own valency. Otherwise, it spreads some default amount of energy. Cells are also spreading their gate values when event occurs, so they are "syncing" with neighbours. """ shade = xmath.min(255, self.main.energy) lmb = xmath.float(self.main.interval) / xmath.float(256 - shade) prob = 1 - xmath.exp(-lmb) fired = core.IntegerVariable() fired += xmath.int(self.main.rng.uniform < prob) fired *= self.main.energy > 0 denergy = xmath.min(self.default_denergy, shade) gate = self.main.gate mutated = core.IntegerVariable() mutated += xmath.int(self.main.rng.uniform < 0.0001) denergy = denergy - 1 * (denergy > 0) * mutated energy_passed = core.IntegerVariable() for i in range(len(self.buffers)): valency1 = self.neighbors[i].main.energy % 8 valency2 = self.main.energy % 8 gate_fit = (i == gate) full_transition = denergy * (valency1 != 0 | valency2 == 0) potent_transition = valency2 * (valency1 == 0) denergy_fin = full_transition + potent_transition energy_passed += denergy_fin * fired * gate_fit self.buffers[i].energy = energy_passed * fired * gate_fit self.buffers[i].gate = (self.main.gate + 7) * fired * gate_fit self.main.interval = (self.main.interval + 1) * (energy_passed == 0) self.main.energy -= energy_passed * (energy_passed > 0) self.main.gate = (self.main.gate + 1) % 8
def absorb(self): """Implement parent's clone with a rule as a parameter.""" neighbors_alive = core.IntegerVariable() for i in range(len(self.buffers)): neighbors_alive += self.neighbors[i].buffer.state is_born = (self.rule >> neighbors_alive) & 1 is_sustain = (self.rule >> 9 >> neighbors_alive) & 1 self.main.state = is_born | is_sustain & self.main.state
def absorb(self): """ Calculate RGB as neighbors sum for living cell only. Note, parent ``absorb`` method should be called using direct class access, not via ``super``. """ GameOfLife.absorb(self) red_sum = core.IntegerVariable() green_sum = core.IntegerVariable() blue_sum = core.IntegerVariable() for i in range(len(self.buffers)): red_sum += self.neighbors[i].buffer.red + 1 green_sum += self.neighbors[i].buffer.green + 1 blue_sum += self.neighbors[i].buffer.blue + 1 self.main.red = red_sum * self.main.state self.main.green = green_sum * self.main.state self.main.blue = blue_sum * self.main.state
def hsv2rgb(hue, saturation, value): """ Convert HSV color to RGB format. :param hue: Hue value [0, 1] :param saturation: Saturation value [0, 1] :param value: Brightness value [0, 1] :returns: tuple (red, green, blue) """ if isinstance(hue, DeferredExpression): hue_f = core.FloatVariable() hue_f += hue * 6 hue_i = core.IntegerVariable() hue_i += xmath.int(hue_f) hue_f -= hue_i sat = core.FloatVariable() sat += saturation val = core.FloatVariable() val += value grad_p = core.FloatVariable() grad_p += val * (1 - sat) grad_q = core.FloatVariable() grad_q += val * (1 - sat * hue_f) grad_t = core.FloatVariable() grad_t += val * (1 - sat * (1 - hue_f)) else: hue_f = hue * 6 hue_i = int(hue_f) hue_f -= hue_i sat = saturation val = value grad_p = val * (1 - sat) grad_q = val * (1 - sat * hue_f) grad_t = val * (1 - sat * (1 - hue_f)) red = ((hue_i == 0) | (hue_i >= 5)) * val red = red + (hue_i == 1) * grad_q red = red + ((hue_i == 2) | (hue_i == 3)) * grad_p red = red + (hue_i == 4) * grad_t green = ((hue_i == 0) | (hue_i >= 6)) * grad_t green = green + ((hue_i == 1) | (hue_i == 2)) * val green = green + (hue_i == 3) * grad_q green = green + ((hue_i == 4) | (hue_i == 5)) * grad_p blue = ((hue_i == 0) | (hue_i == 1) | (hue_i >= 6)) * grad_p blue = blue + (hue_i == 2) * grad_t blue = blue + ((hue_i == 3) | (hue_i == 4)) * val blue = blue + (hue_i == 5) * grad_q return (red, green, blue)
def absorb(self): """ Implement the logic of absorb phase. Statements below will be translated into C code as well. Here, we sum all neigbors buffered states and apply Conway rule to modify cell's own state. """ neighbors_alive = core.IntegerVariable() for i in range(len(self.buffers)): neighbors_alive += self.neighbors[i].buffer.state is_born = (8 >> neighbors_alive) & 1 is_sustain = (12 >> neighbors_alive) & 1 self.main.state = is_born | is_sustain & self.main.state
def genome_crossover(state, num_genes, *genomes, max_genes=None, mutation_prob=0, rng_name="rng"): """ Crossover given genomes in stochastic way. :param state: A container holding model's properties. :param num_genes: Genome length, assuming all genomes has the same number of genes. :param genomes: A list of genomes (integers) to crossover :param max_genes: Upper limit for '1' genes in the resulting genome. :param mutation_prob: Probability of a single gene's mutation. :param rng_name: Name of ``RandomProperty``. :returns: Single integer, a resulting genome. """ max_genes = max_genes or num_genes gene_choose = core.IntegerVariable() new_genome = core.IntegerVariable() num_genomes = core.IntegerVariable() num_active = core.IntegerVariable() new_gene = core.IntegerVariable() rand_val = getattr(state, rng_name).uniform start_gene = core.IntegerVariable() start_gene *= 0 start_gene += xmath.int(rand_val * num_genes) % len(genomes) for gene in range(num_genes): gene_choose *= 0 num_genomes *= 0 gene = (gene + start_gene) % num_genes for genome in genomes: gene_choose += ((genome >> gene) & 1 & (genome > 0)) << num_genomes num_genomes += (genome > 0) rand_val = getattr(state, rng_name).uniform winner_gene = xmath.int(rand_val * num_genomes) new_gene *= 0 new_gene += ((gene_choose >> winner_gene) & 1) num_active += new_gene new_gene *= num_active <= max_genes is_mutated = 0 if mutation_prob > 0: is_mutated = getattr(state, rng_name).uniform < mutation_prob is_mutated = is_mutated * (gene_choose > 0) new_genome += (new_gene ^ is_mutated) << gene return new_genome
def absorb(self): """Absorb surrounding values by summing them.""" new_val = core.IntegerVariable() for i in range(len(self.buffers)): new_val += self.neighbors[i].buffer.state self.main.state = new_val