Skip to content

Commit 1f86089

Browse files
Add functions for metrics
1 parent 9a6b36b commit 1f86089

5 files changed

Lines changed: 159 additions & 1 deletion

File tree

public/automate-core.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,9 @@ function evaluerFitness(univers, poidsObj = {}) {
725725
}
726726

727727
function evaluerPopulation(population, poidsObj = {}) {
728+
if (window.AutomaginariumUniversVivant?.evaluer_population) {
729+
return window.AutomaginariumUniversVivant.evaluer_population(population, poidsObj);
730+
}
728731
return population.map((config) => {
729732
const univers = genererUnivers(config);
730733
const evaluation = evaluerFitness(univers, poidsObj);

scripts/build_wasm_bundle.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def build_french_core() -> None:
7575
"etat_formulaire_vers_configuration,configuration_vers_etat_formulaire,"
7676
"valider_configuration,etiquette_espace_regles,etiquette_regle_hud,"
7777
"generer_univers_detaille,population_initiale,nouvelle_generation,"
78-
"preset_poids_genetique,appliquer_perturbation"
78+
"preset_poids_genetique,evaluer_population,appliquer_perturbation"
7979
),
8080
"--out",
8181
str(RICH_BROWSER / "browser_module.mjs"),

src/automate_universel.multi

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
importer json
2+
importer math
23
importer random
34
importer dégradés_couleur
45

@@ -676,6 +677,151 @@ def nouvelle_generation(population, scores, taux_mutation_sur_1000, graine):
676677
retour nouvelle
677678

678679

680+
def cellule_vivante(valeur, valeur_vide):
681+
retour str(valeur) != str(valeur_vide)
682+
683+
684+
def calculer_densite_ligne(ligne, valeur_vide):
685+
si len(ligne) == 0:
686+
retour 0.0
687+
soit vivantes = [valeur pour valeur dans ligne si cellule_vivante(valeur, valeur_vide)]
688+
retour len(vivantes) / len(ligne)
689+
690+
691+
def calculer_symetrie_ligne(ligne):
692+
si len(ligne) <= 1:
693+
retour 1.0
694+
soit accords = 0
695+
soit paires = len(ligne) // 2
696+
pour indice dans range(paires):
697+
si str(ligne[indice]) == str(ligne[len(ligne) - 1 - indice]):
698+
accords = accords + 1
699+
retour accords / paires si paires > 0 sinon 1.0
700+
701+
702+
def calculer_compacite_ligne(ligne, valeur_vide):
703+
soit indices_vivants = [indice pour indice dans range(len(ligne)) si cellule_vivante(ligne[indice], valeur_vide)]
704+
si len(indices_vivants) == 0:
705+
retour 0.0
706+
soit debut = indices_vivants[0]
707+
soit fin = indices_vivants[len(indices_vivants) - 1]
708+
retour len(indices_vivants) / max(1, fin - debut + 1)
709+
710+
711+
def metriques_univers(univers):
712+
soit lignes = univers.get("lignes", [])
713+
soit configuration = univers.get("configuration", {})
714+
soit valeur_vide = configuration.get("alphabet_entree", [0])[0]
715+
si len(lignes) == 0:
716+
retour {
717+
"entropie": 0.0,
718+
"compacite": 0.0,
719+
"symetrie": 0.5,
720+
"tauxCroissance": 0.0,
721+
"densite": 0.0,
722+
"oscillation": 0.0,
723+
"complexite": 0.0,
724+
"varianceDensite": 0.0,
725+
"periodeDetectee": 0,
726+
}
727+
728+
soit densites = [calculer_densite_ligne(ligne, valeur_vide) pour ligne dans lignes]
729+
soit compacites = [calculer_compacite_ligne(ligne, valeur_vide) pour ligne dans lignes]
730+
soit symetries = [calculer_symetrie_ligne(ligne) pour ligne dans lignes]
731+
soit toutes_valeurs = []
732+
pour ligne dans lignes:
733+
pour valeur dans ligne:
734+
toutes_valeurs.append(str(valeur))
735+
736+
soit comptes = {}
737+
pour cle_valeur dans toutes_valeurs:
738+
comptes[cle_valeur] = comptes.get(cle_valeur, 0) + 1
739+
740+
soit total = max(1, len(toutes_valeurs))
741+
soit entropie = 0.0
742+
pour cle_compte dans comptes:
743+
soit probabilite = comptes[cle_compte] / total
744+
si probabilite > 0:
745+
entropie = entropie - (probabilite * math.log2(probabilite))
746+
747+
soit densite = sum(densites) / len(densites)
748+
soit compacite = sum(compacites) / len(compacites)
749+
soit symetrie = sum(symetries) / len(symetries)
750+
soit tranche = max(1, len(densites) // 5)
751+
soit densite_debut = sum(densites[:tranche]) / tranche
752+
soit densite_fin = sum(densites[-tranche:]) / tranche
753+
soit taux_croissance = max(-1.0, min(1.0, densite_fin - densite_debut))
754+
soit variance = max(0.0, min(1.0, variance_densite(densites) * 4.0))
755+
soit periode = detecter_periode(densites, min(24, len(densites)))
756+
soit score_variance = 1.0 - min(1.0, abs(variance - 0.5) * 2.0)
757+
soit score_periode = 1.0 si periode > 0 sinon 0.0
758+
soit oscillation = max(0.0, min(1.0, (score_variance * 0.7) + (score_periode * 0.3)))
759+
soit complexite = max(0.0, min(1.0, entropie * (1.0 - compacite)))
760+
761+
retour {
762+
"entropie": entropie,
763+
"compacite": compacite,
764+
"symetrie": symetrie,
765+
"tauxCroissance": taux_croissance,
766+
"densite": densite,
767+
"oscillation": oscillation,
768+
"complexite": complexite,
769+
"varianceDensite": variance,
770+
"periodeDetectee": periode,
771+
}
772+
773+
774+
def evaluer_fitness(univers, poids_obj={}):
775+
soit metriques = metriques_univers(univers)
776+
soit poids = [
777+
poids_obj.get("symetrie", 5),
778+
poids_obj.get("densite", 5),
779+
poids_obj.get("stabilite", 5),
780+
poids_obj.get("oscillation", 5),
781+
poids_obj.get("complexite", 5),
782+
poids_obj.get("croissance", 5),
783+
]
784+
soit f_sym = metriques["symetrie"]
785+
soit f_den = 0.8 si abs(metriques["densite"] - 0.45) < 0.2 sinon 0.2
786+
soit f_sta = ((1.0 - abs(metriques["tauxCroissance"])) * 0.5) + (metriques["compacite"] * 0.5)
787+
soit f_cmp = metriques["complexite"]
788+
soit f_cro = max(0.0, metriques["tauxCroissance"])
789+
soit total_poids = max(1, sum(poids))
790+
soit score = ((f_sym * poids[0]) + (f_den * poids[1]) + (f_sta * poids[2]) + (metriques["oscillation"] * poids[3]) + (f_cmp * poids[4]) + (f_cro * poids[5])) / total_poids
791+
soit score_borne = max(0.0, min(1.0, score))
792+
soit configuration = univers.get("configuration", {})
793+
soit canaux = max(1, int(configuration.get("nombre_canaux_sortie", 1)))
794+
soit scores_par_canal = [score_borne pour _ dans range(canaux)]
795+
retour {
796+
"score": score_borne,
797+
"metriques": {
798+
"entropie": metriques["entropie"],
799+
"densite": metriques["densite"],
800+
"compacite": metriques["compacite"],
801+
"symetrie": metriques["symetrie"],
802+
"oscillation": metriques["oscillation"],
803+
"complexite": metriques["complexite"],
804+
"croissance": max(0.0, metriques["tauxCroissance"]),
805+
"tauxCroissance": metriques["tauxCroissance"],
806+
"scores_par_canal": scores_par_canal,
807+
},
808+
}
809+
810+
811+
def evaluer_population(population, poids_obj={}):
812+
soit evaluations = []
813+
pour config dans population:
814+
soit univers = generer_univers_detaille(config)
815+
soit evaluation = evaluer_fitness(univers, poids_obj)
816+
evaluations.append({
817+
"config": config,
818+
"univers": univers,
819+
"score": evaluation["score"],
820+
"metriques": evaluation["metriques"],
821+
})
822+
retour evaluations
823+
824+
679825
# ============================================================================
680826
# FONCTIONS DE FITNESS: Scoring criteria for genetic evolution
681827
# ============================================================================

tests/core_smoke.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ function testGeneratedLivingUniverseModulePrecedence() {
372372
preset_poids_genetique(nom) {
373373
return { generatedPreset: nom };
374374
},
375+
evaluer_population(population) {
376+
return population.map((config, index) => ({ config, score: index + 1, generatedEvaluation: true }));
377+
},
375378
appliquer_perturbation(univers, evenement) {
376379
return { ...univers, generatedPerturbation: evenement.type };
377380
},
@@ -413,6 +416,8 @@ function testGeneratedLivingUniverseModulePrecedence() {
413416
assert.equal(AutomaginariumCore.populationInitiale(config, 3, 1).length, 3);
414417
assert.equal(AutomaginariumCore.nouvelleGeneration([{ a: 1 }, { b: 2 }], [1, 2], 50, 1)[0].generatedNextPopulation, 2);
415418
assert.equal(AutomaginariumCore.presetPoidsGenetique("stable").generatedPreset, "stable");
419+
assert.equal(AutomaginariumCore.evaluerPopulation([config], {}).length, 1);
420+
assert.equal(AutomaginariumCore.evaluerPopulation([config], {})[0].generatedEvaluation, true);
416421
assert.equal(AutomaginariumCore.appliquerPerturbation({ configuration: config, lignes: [], sorties: [] }, { type: "pulse" }).generatedPerturbation, "pulse");
417422
} finally {
418423
global.AutomaginariumUniversVivant = previousGenerated;

tests/generated_browser_module_smoke.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const exportsList = [
2929
"population_initiale",
3030
"nouvelle_generation",
3131
"preset_poids_genetique",
32+
"evaluer_population",
3233
"appliquer_perturbation",
3334
].join(",");
3435

@@ -100,6 +101,9 @@ async function testGeneratedModule() {
100101
assert.equal(module.population_initiale(config, 3, 11).length, 3);
101102
assert.equal(module.nouvelle_generation([config, config], [0.1, 0.9], 50, 11).length, 2);
102103
assert.equal(module.preset_poids_genetique("stable").stabilite, 9);
104+
const evaluated = module.evaluer_population([config], module.preset_poids_genetique("beau"));
105+
assert.equal(evaluated.length, 1);
106+
assert.equal(evaluated[0].metriques.scores_par_canal.length, 1);
103107
const perturbed = module.appliquer_perturbation(module.generer_univers_detaille(config), {
104108
cx: 4,
105109
cy: 2,

0 commit comments

Comments
 (0)