|
|
import numpy as np |
|
|
from .artifacts import AbstractArtifact |
|
|
from .utils import _softmax |
|
|
class Cluster: |
|
|
""" An implementation of a Cluster of an island. This code is an implementation of Funsearch (https://www.nature.com/articles/s41586-023-06924-6) and is heavily inspired by the original code (https://github.com/google-deepmind/funsearch) |
|
|
|
|
|
**Citation**: |
|
|
|
|
|
@Article{FunSearch2023, |
|
|
author = {Romera-Paredes, Bernardino and Barekatain, Mohammadamin and Novikov, Alexander and Balog, Matej and Kumar, M. Pawan and Dupont, Emilien and Ruiz, Francisco J. R. and Ellenberg, Jordan and Wang, Pengming and Fawzi, Omar and Kohli, Pushmeet and Fawzi, Alhussein}, |
|
|
journal = {Nature}, |
|
|
title = {Mathematical discoveries from program search with large language models}, |
|
|
year = {2023}, |
|
|
doi = {10.1038/s41586-023-06924-6} |
|
|
} |
|
|
""" |
|
|
def __init__(self,score: float,first_program: AbstractArtifact,epsilon=1e-6,sample_with_replacement=False, default_program_temperature=0.1): |
|
|
self.score: float = score |
|
|
self.programs: list[AbstractArtifact] = [first_program] |
|
|
self.lengths = np.array([len(str(first_program))],dtype=np.float32) |
|
|
self.epsilon = epsilon |
|
|
self.sample_with_replacement = sample_with_replacement |
|
|
self.default_program_temperature = default_program_temperature |
|
|
|
|
|
|
|
|
def compute_length_probs(self,program_temperature: float): |
|
|
""" Compute the probability of each program given the length of the program. The probability is computed as the softmax of the negative length of the program. The temperature of the softmax is controlled by the program_temperature parameter. |
|
|
|
|
|
:param program_temperature: The temperature of the softmax |
|
|
:type program_temperature: float |
|
|
:return: The probability of each program given the length of the program |
|
|
:rtype: np.array |
|
|
""" |
|
|
min_length = np.min(self.lengths) |
|
|
max_length = np.max(self.lengths) |
|
|
|
|
|
|
|
|
length_logits = (self.lengths - min_length)/(max_length + self.epsilon) |
|
|
|
|
|
probs = _softmax(-length_logits,program_temperature) |
|
|
return probs |
|
|
|
|
|
def register_program(self,program: str): |
|
|
""" Register a program on the cluster. |
|
|
|
|
|
:param program: The program to register |
|
|
:type program: str |
|
|
""" |
|
|
self.programs.append(program) |
|
|
self.lengths = np.append(self.lengths,len(str(program))) |
|
|
|
|
|
def sample_program(self,program_temperature=None): |
|
|
""" Sample a program from the cluster given the program temperature. |
|
|
|
|
|
:param program_temperature: The temperature of the program |
|
|
:type program_temperature: float, optional |
|
|
:return: The sampled program |
|
|
:rtype: str |
|
|
""" |
|
|
if program_temperature is None: |
|
|
program_temperature = self.default_program_temperature |
|
|
|
|
|
|
|
|
probs = self.compute_length_probs(program_temperature) |
|
|
|
|
|
index = np.random.choice(len(probs),p=probs,replace=self.sample_with_replacement) |
|
|
|
|
|
return self.programs[index] |