├── src ├── MiniHud.ts ├── Log.ts ├── decorators │ └── Register.ts ├── models │ ├── PopulationOperator.ts │ ├── IndividualOperator.ts │ ├── Individual.ts │ ├── fields │ │ ├── StrField.ts │ │ ├── JSCodeField.ts │ │ └── CSSField.ts │ ├── GroupOperator.ts │ ├── Population.ts │ ├── Operator.ts │ └── FieldDef.ts ├── Num.ts ├── operators │ ├── ranking │ │ ├── MinRank.ts │ │ └── LinearRanking.ts │ ├── misc │ │ ├── PopulationSizeControl.ts │ │ ├── RandomIndividualGenerator.ts │ │ ├── GrimReaper.ts │ │ └── Sort.ts │ ├── css │ │ ├── CSSRenderer.ts │ │ ├── CSSDescriptor.ts │ │ └── CSSGAOperator.ts │ ├── selection │ │ └── Roulette.ts │ ├── objective │ │ ├── Rastrigin.ts │ │ ├── Schwefel.ts │ │ └── Weierstrass.ts │ ├── renderers │ │ ├── TableRenderer.ts │ │ └── CanvasRenderer.ts │ ├── swarm │ │ └── PSOA.ts │ └── genetic │ │ ├── HauptGA.ts │ │ └── StringGA.ts ├── tests │ ├── SumFunction.ts │ ├── Test.ts │ └── ArrFunc.ts ├── hud │ └── GuiHud.ts └── Application.ts ├── package.json ├── tsconfig.json ├── .gitignore ├── LICENSE ├── samplests ├── tsd.json ├── Schwefel-PSOA.html ├── Weierstrass-PSOA.html ├── Rastrigin-PSOA.html ├── index.html ├── css-test.html ├── README.md └── dist └── bundle.js.map /src/MiniHud.ts: -------------------------------------------------------------------------------- 1 | import {Application} from "./Application"; 2 | export class MiniHud { 3 | 4 | constructor(application:Application) { 5 | 6 | } 7 | 8 | } -------------------------------------------------------------------------------- /src/Log.ts: -------------------------------------------------------------------------------- 1 | export class Log { 2 | 3 | static error(...args:any[]):void { 4 | console.error(args); 5 | } 6 | 7 | static info(...args:any[]):void { 8 | console.log(args); 9 | } 10 | 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/decorators/Register.ts: -------------------------------------------------------------------------------- 1 | function Register(constructor:Function) { 2 | 3 | if (window && window['ops'] === undefined) { 4 | window['ops'] = {} 5 | } 6 | var name = /function ([^(]*)/.exec(constructor + "")[1]; 7 | window['ops'][name] = { 8 | name: name, 9 | func: constructor 10 | }; 11 | 12 | } 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/models/PopulationOperator.ts: -------------------------------------------------------------------------------- 1 | import {Population} from "./Population"; 2 | import {Individual} from "./Individual"; 3 | import {Operator} from "./Operator"; 4 | export abstract class PopulationOperator extends Operator { 5 | 6 | doExecute(individual:Individual, population:Population):void { 7 | this.currentRunning = population; 8 | this.execute(population); 9 | } 10 | 11 | abstract execute(population:Population):void; 12 | 13 | 14 | } -------------------------------------------------------------------------------- /src/models/IndividualOperator.ts: -------------------------------------------------------------------------------- 1 | import {Operator} from "./Operator"; 2 | import {Individual} from "./Individual"; 3 | import {Population} from "./Population"; 4 | import {FieldDef} from "./FieldDef"; 5 | 6 | @Register 7 | export abstract class IndividualOperator extends Operator { 8 | 9 | 10 | 11 | doExecute(individual:Individual, population:Population):void { 12 | this.currentRunning = population; 13 | for (let ind of population.individuals) { 14 | this.execute(ind); 15 | } 16 | } 17 | 18 | abstract execute(individual:Individual):void; 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cambrian-web", 3 | "version": "0.1.0", 4 | "description": "Evolutionary Algorithm Optimisation Framework", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "concurrently \"tsc -w\" \"npm run lite\" ", 8 | "lite": "lite-server" 9 | }, 10 | "author": "", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "concurrently": "^2.0.0", 14 | "jquery": "^2.2.3", 15 | "lite-server": "^2.2.0", 16 | "react": "^15.0.1", 17 | "react-dom": "^15.0.1", 18 | "systemjs": "^0.19.26", 19 | "typescript": "^1.8.10", 20 | "typings": "^0.7.12" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "jsx": "react", 4 | "out": "dist/bundle.js", 5 | "target": "es5", 6 | "sourceMap": true, 7 | "module": "system", 8 | "moduleResolution": "node", 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "removeComments": false, 12 | "noImplicitAny": false 13 | }, 14 | "buildOnSave": true, 15 | "exclude": [ 16 | "ui", 17 | "node_modules", 18 | "typings/main", 19 | "typings/main.d.ts", 20 | "src/tests" 21 | ], 22 | "filesGlob": [ 23 | "./src/**/*.ts" 24 | ], 25 | "atom": { "rewriteTsconfig": false } 26 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | -------------------------------------------------------------------------------- /src/models/Individual.ts: -------------------------------------------------------------------------------- 1 | import {FieldDef} from "./FieldDef"; 2 | export class Individual { 3 | 4 | private fieldsMap:{ [key:string]:any; } = {}; 5 | private fieldsDefMap:{ [key:string]:FieldDef} = {}; 6 | 7 | getValue(name:string) { 8 | return this.fieldsMap[name]; 9 | } 10 | 11 | getFieldDefinition(name:string):FieldDef { 12 | return this.fieldsDefMap[name]; 13 | } 14 | 15 | setValue(name:string, value:any) { 16 | this.fieldsMap[name] = this.fieldsDefMap[name].filter(value); 17 | } 18 | 19 | registerField(field:FieldDef) { 20 | this.fieldsMap[field.name] = field.getInitialValue(); 21 | this.fieldsDefMap[field.name] = field; 22 | } 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/models/fields/StrField.ts: -------------------------------------------------------------------------------- 1 | import {FieldDef} from "../FieldDef"; 2 | import {Num} from "../../Num"; 3 | export class StrField extends FieldDef { 4 | 5 | static possible:string = " ;:'\"!@#$%^&*()_+-=\\|,./<>?[]{}ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 6 | length:number; 7 | 8 | constructor(name:string, min:number = 10, max:number = 100) { 9 | super(name); 10 | this.min = min; 11 | this.max = max; 12 | } 13 | 14 | 15 | getInitialValue():any { 16 | var text = ""; 17 | for (var i = 0; i < Num.getRandomNum(this.min, this.max); i++) 18 | text += StrField.getRandomChar(); 19 | 20 | return text; 21 | } 22 | 23 | static getRandomChar() { 24 | return StrField.possible.charAt(Math.floor(Math.random() * this.possible.length)); 25 | } 26 | 27 | 28 | } -------------------------------------------------------------------------------- /src/models/GroupOperator.ts: -------------------------------------------------------------------------------- 1 | import {Operator} from "./Operator"; 2 | import {FieldDef} from "./FieldDef"; 3 | import {Individual} from "./Individual"; 4 | import {Population} from "./Population"; 5 | 6 | @Register 7 | export class GroupOperator extends Operator { 8 | 9 | constructor(name, numExecutions:number = 1) { 10 | super(name); 11 | this.numExecutions = numExecutions; 12 | } 13 | 14 | getFieldDefinition():Array { 15 | return []; 16 | } 17 | 18 | doExecute(individual:Individual, population:Population):void { 19 | this.currentRunning = population; 20 | if (!this.operators) 21 | return; 22 | for (var i = 0; i < this.numExecutions; i++) { 23 | for (let operator of this.operators) { 24 | operator.doExecute(individual, population); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Num.ts: -------------------------------------------------------------------------------- 1 | export class Num { 2 | 3 | static getRandomNum(min:number = 0, max:number = 1, precision?:number):number { 4 | var result:number = min + (Math.random() * (max - min)); 5 | 6 | if (precision !== undefined) { 7 | var power = Math.pow(10, precision); 8 | result = Math.round(result * power) / power; 9 | } 10 | 11 | 12 | return result; 13 | } 14 | 15 | static randomInt(min:number = 0, max:number = 1) { 16 | return Num.getRandomNum(min, max, 0); 17 | } 18 | 19 | static randomArrPicker(arr:Array):any { 20 | return arr[Math.round(Math.random() * (arr.length - 1))]; 21 | } 22 | 23 | static roundToPrecision(value:number, precision:number):number { 24 | var pow = Math.pow(10, 6); 25 | var val = Math.round(value * pow) / pow; 26 | 27 | return val; 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/operators/ranking/MinRank.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {OutputField} from "../../models/FieldDef"; 5 | 6 | 7 | @Register 8 | export class MinRank extends PopulationOperator { 9 | 10 | static FIELD:string = 'rank'; 11 | 12 | private fieldSort:string; 13 | 14 | constructor(fieldSort:string) { 15 | super("MinRank"); 16 | this.fieldSort = fieldSort; 17 | } 18 | 19 | execute(population:Population):void { 20 | population.individuals = _.sortBy(population.individuals, ind => { 21 | return ind.getValue(this.fieldSort); 22 | }); 23 | 24 | _.each(population.individuals, (ind, index) => { 25 | ind.setValue(MinRank.FIELD, index); 26 | }); 27 | } 28 | 29 | getFieldDefinition():Array { 30 | return [new OutputField(MinRank.FIELD, 0)]; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/operators/misc/PopulationSizeControl.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | 5 | @Register 6 | export class PopulationSizeControl extends PopulationOperator { 7 | 8 | maxIndividuals:number; 9 | 10 | constructor(maxIndividuals:number) { 11 | super("PopualationSizeControl"); 12 | if (!maxIndividuals) { 13 | throw new Error("PopulationSizeControl needs maxIndividuals parameter to be passed as argument"); 14 | } 15 | 16 | this.maxIndividuals = maxIndividuals; 17 | } 18 | 19 | execute(population:Population):void { 20 | if (population.individuals.length > this.maxIndividuals) { 21 | population.individuals.splice(this.maxIndividuals, population.individuals.length - this.maxIndividuals); 22 | } 23 | } 24 | 25 | getFieldDefinition():Array { 26 | return undefined; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/models/Population.ts: -------------------------------------------------------------------------------- 1 | import {Individual} from "./Individual"; 2 | import {Num} from "../Num"; 3 | export class Population { 4 | 5 | individuals:Array; 6 | fields:Array; 7 | inputFields:Array; 8 | color:string; 9 | cache:any; 10 | rind:Function; 11 | index:number; 12 | 13 | constructor(numIndividuals?:number) { 14 | this.fields = []; 15 | this.inputFields = []; 16 | this.individuals = []; 17 | this.cache = {}; 18 | 19 | if (numIndividuals) { 20 | for (let i = 0; i < numIndividuals; i++) { 21 | this.individuals.unshift(new Individual()); 22 | } 23 | } 24 | } 25 | 26 | public requestIndividual():Individual { 27 | return this.rind(); 28 | } 29 | 30 | public removeIndividual(individual:Individual):void { 31 | let individuals = this.individuals; 32 | individuals.splice(individuals.indexOf(individual), 1); 33 | } 34 | 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/operators/css/CSSRenderer.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Num} from "../../Num"; 5 | import {Individual} from "../../models/Individual"; 6 | import {FieldType} from "../../models/FieldDef"; 7 | import {IndividualOperator} from "../../models/IndividualOperator"; 8 | import {OutputField} from "../../models/FieldDef"; 9 | import {CSSField} from "../../models/fields/CSSField"; 10 | 11 | @Register 12 | export class CSSRenderer extends PopulationOperator { 13 | 14 | private styleElement:HTMLElement; 15 | 16 | constructor() { 17 | super('CSSDescriptor'); 18 | this.styleElement = document.getElementById('cssstyle'); 19 | 20 | } 21 | 22 | execute(pop:Population):void { 23 | var ind = pop.individuals[0]; 24 | this.styleElement.innerHTML = CSSField.getFullCSSString(ind.getValue('css')); 25 | 26 | } 27 | 28 | 29 | getFieldDefinition():Array { 30 | return undefined; 31 | } 32 | 33 | 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Cornel Stefanache 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/operators/misc/RandomIndividualGenerator.ts: -------------------------------------------------------------------------------- 1 | import {IndividualOperator} from "../../models/IndividualOperator"; 2 | import {Individual} from "../../models/Individual"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {OutputField} from "../../models/FieldDef"; 5 | import {Num} from "../../Num"; 6 | import {Application} from "../../Application"; 7 | import {PopulationOperator} from "../../models/PopulationOperator"; 8 | import {Population} from "../../models/Population"; 9 | 10 | 11 | @Register 12 | export class RandomIndividualGenerator extends PopulationOperator { 13 | 14 | private num:number; 15 | private chance:number; 16 | 17 | constructor(generateNum:number = 5, chance:number = 0.1) { 18 | super("Random Individual Generator"); 19 | this.num = generateNum; 20 | this.chance = chance; 21 | } 22 | 23 | execute(population:Population):void { 24 | for (var i = 0; i < this.num; i++) { 25 | if (Num.getRandomNum() < this.chance) { 26 | population.requestIndividual(); 27 | } 28 | } 29 | } 30 | 31 | getFieldDefinition():Array { 32 | return undefined; 33 | } 34 | 35 | 36 | } -------------------------------------------------------------------------------- /src/operators/misc/GrimReaper.ts: -------------------------------------------------------------------------------- 1 | import {IndividualOperator} from "../../models/IndividualOperator"; 2 | import {Individual} from "../../models/Individual"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {OutputField} from "../../models/FieldDef"; 5 | import {Num} from "../../Num"; 6 | import {Application} from "../../Application"; 7 | 8 | @Register 9 | export class GrimReaper extends IndividualOperator { 10 | 11 | private avgAge:number; 12 | private regenerate:boolean; 13 | 14 | constructor(avgAge?:number, regenerate:boolean = true) { 15 | super('GrimReaper'); 16 | this.avgAge = avgAge || 100; 17 | this.regenerate = regenerate; 18 | } 19 | 20 | execute(individual:Individual):void { 21 | var age = individual.getValue("age"); 22 | var rand = Num.getRandomNum(); 23 | if (rand < age/this.avgAge) { 24 | this.getCurrentPopulation().removeIndividual(individual); 25 | if (this.regenerate) 26 | this.getCurrentPopulation().requestIndividual(); 27 | } 28 | 29 | individual.setValue("age", age+1); 30 | } 31 | 32 | 33 | getFieldDefinition():Array { 34 | return [new OutputField("age", 1)]; 35 | 36 | } 37 | 38 | } -------------------------------------------------------------------------------- /src/operators/ranking/LinearRanking.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {FieldDef} from "../../models/FieldDef"; 3 | import {OutputField} from "../../models/FieldDef"; 4 | import {Population} from "../../models/Population"; 5 | import {Individual} from "../../models/Individual"; 6 | import {Num} from "../../Num"; 7 | 8 | @Register 9 | export class LinearRanking extends PopulationOperator { 10 | 11 | private fieldSort:string; 12 | 13 | constructor(fieldSort:string) { 14 | super("LinearRanking"); 15 | this.fieldSort = fieldSort; 16 | } 17 | 18 | execute(population:Population):void { 19 | population.individuals = _.sortBy(population.individuals, ind => { 20 | return ind.getValue(this.fieldSort); 21 | }); 22 | 23 | var size:number = population.individuals.length; 24 | for (var i = 0; i < size; i++) { 25 | var individual:Individual = population.individuals[i]; 26 | individual.setValue("linearRanking", Num.roundToPrecision( (2 * size + 1 - 2 * i) / size / size, 2)); 27 | } 28 | } 29 | 30 | getFieldDefinition():Array { 31 | return [new OutputField("linearRanking", 0)]; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/tests/SumFunction.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | 4 | import {IndividualOperator} from "../models/IndividualOperator"; 5 | import {FieldDef} from "../models/FieldDef"; 6 | import {StrField} from "../models/fields/StrField"; 7 | import {OutputField} from "../models/FieldDef"; 8 | import {Individual} from "../models/Individual"; 9 | export class SumFunction extends IndividualOperator { 10 | 11 | constructor() { 12 | super('SumFunctionOperator'); 13 | } 14 | 15 | getFieldDefinition():Array { 16 | return [new StrField('content', 1, 10), new OutputField('obj')]; 17 | } 18 | 19 | execute(individual:Individual):void { 20 | var func = individual.getValue('content'); 21 | var value = 0; 22 | var x = 3; 23 | var y = 4; 24 | 25 | var parameters = ['x', 'y']; 26 | 27 | try { 28 | //var timestamp = new Date().getTime(); 29 | var result = eval(func); 30 | value = Math.abs(result - 7); 31 | } catch (err) { 32 | value += 1e3 - result.length; 33 | } 34 | 35 | for (let param of parameters) { 36 | if (func.indexOf(param) === -1) { 37 | value += 50; 38 | } 39 | } 40 | 41 | 42 | individual.setValue('obj', value); 43 | } 44 | 45 | } 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /samplests: -------------------------------------------------------------------------------- 1 | //Genetic algorithm TS 2 | 3 | var application = new Application(); // Initialize population 4 | 5 | if (window) { 6 | window['application'] = application; // Make them available on browser console 7 | window['num'] = Num; // Also random number generator 8 | } 9 | 10 | 11 | application.addPopulation(100); // Add a population of 30 individuals 12 | application.addPopulation(100); // Add a population of 30 individuals 13 | 14 | 15 | application.addOperator(new Schwefel()); // Add Schwefel optimisation problem 16 | application.addOperator(new LinearRanking('schwefel')); 17 | 18 | application.addOperator(new CanvasRenderer('x', 'y', {x: [-510, 510], y: [-510, 510]})); // Add population renderer (zoomed out) 19 | application.addOperator(new CanvasRenderer('x', 'y', {x: [-420.98, -420.96], y: [-420.98, -420.96]})); // Add population renderer (zoomed in) 20 | application.addOperator(new TableRenderer(10)); 21 | 22 | var selectionGroup = new GroupOperator("Selection Group", 50); 23 | application.addOperator(new Roulette('linearRanking'), selectionGroup); 24 | application.addOperator(new HauptGA(0.5, 0.2), selectionGroup); 25 | application.addOperator(selectionGroup); 26 | 27 | application.addOperator(new PopulationSizeControl(1000)); 28 | 29 | application.initializeHud(); //Initialize the Mini-HUD for easier algorithm interactions (start/stop/reset) 30 | application.tick(); 31 | 32 | -------------------------------------------------------------------------------- /src/operators/selection/Roulette.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Num} from "../../Num"; 5 | import {Individual} from "../../models/Individual"; 6 | 7 | @Register 8 | export class Roulette extends PopulationOperator { 9 | 10 | rankField:string; 11 | numSelected:number; 12 | 13 | constructor(rankField:string, numSelected:number = 2) { 14 | super("Roulette"); 15 | this.rankField = rankField; 16 | this.numSelected = numSelected; 17 | } 18 | 19 | execute(population:Population):void { 20 | var selection = []; 21 | var individual:Individual; 22 | 23 | 24 | for (var i = 0; i < this.numSelected; i++) { 25 | var rand = Num.getRandomNum(); 26 | var index = 0; 27 | while (rand > 0 && index < population.individuals.length) { 28 | individual = population.individuals[index]; 29 | var fitness = individual.getValue(this.rankField); 30 | rand -= fitness; 31 | 32 | index++; 33 | } 34 | 35 | selection.push(individual); 36 | } 37 | 38 | population.cache['selection'] = selection; 39 | } 40 | 41 | getFieldDefinition():Array { 42 | return undefined; 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /src/tests/Test.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | import {Application} from "../Application"; 4 | import {Num} from "../Num"; 5 | import {ArrFunction} from "./ArrFunc"; 6 | import {LinearRanking} from "../operators/ranking/LinearRanking"; 7 | import {TableRenderer} from "../operators/renderers/TableRenderer"; 8 | import {GroupOperator} from "../models/GroupOperator"; 9 | import {Roulette} from "../operators/selection/Roulette"; 10 | import {StringGA} from "../operators/genetic/StringGA"; 11 | import {PopulationSizeControl} from "../operators/misc/PopulationSizeControl"; 12 | var application = new Application(); // Initialize population 13 | 14 | if (window) { 15 | window['application'] = application; // Make them available on browser console 16 | window['num'] = Num; // Also random number generator 17 | } 18 | 19 | 20 | application.addPopulation(500); // Add a population of 30 individuals 21 | 22 | 23 | application.addOperator(new ArrFunction()); // Add Schwefel optimisation problem 24 | application.addOperator(new LinearRanking('obj')); 25 | application.addOperator(new TableRenderer(10)); 26 | 27 | var groupOperator = new GroupOperator("Group Operator", 50); 28 | groupOperator.addOperator(new Roulette('linearRanking')); 29 | groupOperator.addOperator(new StringGA('content', 0.1, 0.2)); 30 | application.addOperator(groupOperator); 31 | 32 | application.addOperator(new PopulationSizeControl(500)); 33 | application.initializeHud(); //Initialize the Mini-HUD for easier algorithm interactions (start/stop/reset) 34 | application.tick(); 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/operators/misc/Sort.ts: -------------------------------------------------------------------------------- 1 | import {IndividualOperator} from "../../models/IndividualOperator"; 2 | import {Individual} from "../../models/Individual"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {OutputField} from "../../models/FieldDef"; 5 | import {Num} from "../../Num"; 6 | import {Application} from "../../Application"; 7 | import {PopulationOperator} from "../../models/PopulationOperator"; 8 | import {Population} from "../../models/Population"; 9 | import {NumericField} from "../../models/FieldDef"; 10 | 11 | @Register 12 | export class Sort extends PopulationOperator { 13 | 14 | static FIELD:string = "sortOrder"; 15 | 16 | constructor(public field:string, public desc:boolean = false) { 17 | super("Sort"); 18 | } 19 | 20 | execute(population:Population):void { 21 | population.individuals.sort((a:Individual, b:Individual)=> { 22 | var value1 = a.getValue(this.field); 23 | var value2 = b.getValue(this.field); 24 | var val; 25 | if (value1 === value2) { 26 | val = 0; 27 | } else { 28 | val = value1 > value2 ? -1 : 1; 29 | } 30 | return this.desc ? val : -val; 31 | }); 32 | 33 | 34 | for (var i = 0; i < population.individuals.length; i++) { 35 | population.individuals[i].setValue(Sort.FIELD, i); 36 | } 37 | 38 | } 39 | 40 | getFieldDefinition():Array { 41 | return [new OutputField(Sort.FIELD, 0)]; 42 | } 43 | 44 | 45 | } -------------------------------------------------------------------------------- /src/operators/objective/Rastrigin.ts: -------------------------------------------------------------------------------- 1 | import {OutputField} from "../../models/FieldDef"; 2 | import {NumericField} from "../../models/FieldDef"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Individual} from "../../models/Individual"; 5 | import {IndividualOperator} from "../../models/IndividualOperator"; 6 | import {Num} from "../../Num"; 7 | 8 | @Register 9 | export class Rastrigin extends IndividualOperator { 10 | 11 | public static OBJ_FIELD_NAME:string = 'rastrigin'; 12 | public static PRECISION:number = 10; 13 | 14 | private X_FIELD_NAME:string = 'x'; 15 | private Y_FIELD_NAME:string = 'y'; 16 | 17 | 18 | constructor() { 19 | super('Rastrigin'); 20 | } 21 | 22 | 23 | execute(individual:Individual):void { 24 | var x = individual.getValue(this.X_FIELD_NAME); 25 | var y = individual.getValue(this.Y_FIELD_NAME); 26 | var sum:number = 0; 27 | var obj = 20; 28 | obj += x * x - (10 * Math.cos(2 * Math.PI * x)); 29 | obj += y * y - (10 * Math.cos(2 * Math.PI * y)); 30 | 31 | individual.setValue(Rastrigin.OBJ_FIELD_NAME, Num.roundToPrecision(obj, Rastrigin.PRECISION)); 32 | 33 | } 34 | 35 | getName():string { 36 | return "Rastrigin"; 37 | } 38 | 39 | getFieldDefinition():Array { 40 | var x = new NumericField(this.X_FIELD_NAME, -5.12, 5.12, Rastrigin.PRECISION); 41 | var y = new NumericField(this.Y_FIELD_NAME, -5.12, 5.12, Rastrigin.PRECISION); 42 | var value = new OutputField(Rastrigin.OBJ_FIELD_NAME); 43 | 44 | return [x, y, value]; 45 | } 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/operators/objective/Schwefel.ts: -------------------------------------------------------------------------------- 1 | import {OutputField} from "../../models/FieldDef"; 2 | import {NumericField} from "../../models/FieldDef"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Individual} from "../../models/Individual"; 5 | import {IndividualOperator} from "../../models/IndividualOperator"; 6 | import {Num} from "../../Num"; 7 | 8 | @Register 9 | export class Schwefel extends IndividualOperator { 10 | 11 | public static OBJ_FIELD_NAME:string = 'schwefel'; 12 | public static PRECISION:number = 10; 13 | 14 | private X_FIELD_NAME:string = 'x'; 15 | private Y_FIELD_NAME:string = 'y'; 16 | 17 | 18 | constructor() { 19 | super('Schwefel'); 20 | } 21 | 22 | 23 | execute(individual:Individual):void { 24 | var x = individual.getValue(this.X_FIELD_NAME); 25 | var y = individual.getValue(this.Y_FIELD_NAME); 26 | 27 | 28 | var sum:number = 0; 29 | 30 | sum += x * Math.sin(Math.sqrt(Math.abs(x))); 31 | sum += y * Math.sin(Math.sqrt(Math.abs(y))); 32 | 33 | var value:number = sum + 2 * 4.18982887272434686131e+02; 34 | individual.setValue(Schwefel.OBJ_FIELD_NAME, Num.roundToPrecision(value, Schwefel.PRECISION)); 35 | 36 | } 37 | 38 | getName():string { 39 | return "Schwefel"; 40 | } 41 | 42 | getFieldDefinition():Array { 43 | var x = new NumericField(this.X_FIELD_NAME, -500, 500, Schwefel.PRECISION); 44 | var y = new NumericField(this.Y_FIELD_NAME, -500, 500, Schwefel.PRECISION); 45 | var value = new OutputField(Schwefel.OBJ_FIELD_NAME); 46 | 47 | return [x, y, value]; 48 | } 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "underscore/underscore.d.ts": { 9 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 10 | }, 11 | "jquery/jquery.d.ts": { 12 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 13 | }, 14 | "react/react.d.ts": { 15 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 16 | }, 17 | "react/react-dom.d.ts": { 18 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 19 | }, 20 | "react/react-global.d.ts": { 21 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 22 | }, 23 | "react/react-addons-create-fragment.d.ts": { 24 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 25 | }, 26 | "react/react-addons-css-transition-group.d.ts": { 27 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 28 | }, 29 | "react/react-addons-transition-group.d.ts": { 30 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 31 | }, 32 | "react/react-addons-pure-render-mixin.d.ts": { 33 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 34 | }, 35 | "react/react-addons-perf.d.ts": { 36 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 37 | }, 38 | "react/react-addons-linked-state-mixin.d.ts": { 39 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 40 | }, 41 | "react/react-addons-test-utils.d.ts": { 42 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 43 | }, 44 | "react/react-addons-update.d.ts": { 45 | "commit": "544a35a10866b32afda9c7f029c0764558563f4f" 46 | }, 47 | "systemjs/systemjs.d.ts": { 48 | "commit": "a7b9b1adbfebf0d3725e81ca25fb8079f9d33f6b" 49 | }, 50 | "es6-shim/es6-shim.d.ts": { 51 | "commit": "a7b9b1adbfebf0d3725e81ca25fb8079f9d33f6b" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Schwefel-PSOA.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 48 | 49 | -------------------------------------------------------------------------------- /Weierstrass-PSOA.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 49 | 50 | -------------------------------------------------------------------------------- /Rastrigin-PSOA.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 50 | 51 | -------------------------------------------------------------------------------- /src/operators/renderers/TableRenderer.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | 5 | @Register 6 | export class TableRenderer extends PopulationOperator { 7 | 8 | private tableElement:JQuery; 9 | private maxRows:number; 10 | 11 | constructor(maxRows?:number, private cols?:Array, private executeOnClick?:string) { 12 | super("Table View"); 13 | this.maxRows = maxRows; 14 | if (window) { 15 | 16 | this.tableElement = $('
'); 17 | $(document.body).append(this.tableElement); 18 | } 19 | } 20 | 21 | execute(population:Population):void { 22 | 23 | if (population.index === 0) { 24 | var htmlContent = ' '; 25 | 26 | for (var field of this.cols ? this.cols : population.fields) { 27 | htmlContent += '' + field + ''; 28 | } 29 | 30 | htmlContent += ""; 31 | } 32 | var index = 0; 33 | for (var ind of population.individuals) { 34 | if (this.maxRows !== undefined && index++ > this.maxRows) 35 | break; 36 | 37 | htmlContent += ' '; 40 | for (var field of this.cols ? this.cols : population.fields) { 41 | htmlContent += '' + JSON.stringify(ind.getValue(field)) + ''; 42 | } 43 | htmlContent += ''; 44 | } 45 | 46 | htmlContent += ''; 47 | 48 | if (population.index === 0) { 49 | this.tableElement.html(htmlContent); 50 | } else { 51 | this.tableElement.find('tbody').append(htmlContent); 52 | } 53 | 54 | } 55 | 56 | getFieldDefinition():Array { 57 | return undefined; 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /src/models/Operator.ts: -------------------------------------------------------------------------------- 1 | import {Population} from "./Population"; 2 | import {Individual} from "./Individual"; 3 | import {FieldDef} from "./FieldDef"; 4 | import {OutputField} from "./FieldDef"; 5 | import {JSField} from "./FieldDef"; 6 | 7 | 8 | export abstract class Operator { 9 | 10 | protected currentRunning:Population; 11 | 12 | name:string; 13 | numExecutions:number = 1; 14 | operators:Array; 15 | 16 | constructor(name:string) { 17 | this.name = name; 18 | } 19 | 20 | abstract getFieldDefinition():Array; 21 | 22 | abstract doExecute(individual:Individual, population:Population):void; 23 | 24 | 25 | public addOperator(operator:Operator):void { 26 | if (this.operators === undefined) { 27 | this.operators = new Array(); 28 | } 29 | 30 | this.operators.push(operator); 31 | } 32 | 33 | public getCurrentPopulation():Population { 34 | return this.currentRunning; 35 | } 36 | 37 | 38 | } 39 | 40 | export class JSOperator extends Operator { 41 | 42 | private execute:Function; 43 | private isIndividual:boolean; 44 | private fieldsDefObj:Array; 45 | 46 | 47 | constructor(name:string, executeFunction:Function, isIndividual:boolean, fieldsDef:Array) { 48 | super(name); 49 | this.execute = executeFunction; 50 | this.isIndividual = isIndividual; 51 | this.fieldsDefObj = fieldsDef; 52 | } 53 | 54 | 55 | getFieldDefinition():Array { 56 | var arrFieldDef:Array; 57 | 58 | if (this.fieldsDefObj) { 59 | arrFieldDef = []; 60 | for (let fieldDef of this.fieldsDefObj) { 61 | arrFieldDef.push(JSField.get(fieldDef)); 62 | } 63 | } 64 | 65 | return arrFieldDef; 66 | } 67 | 68 | doExecute(individual:Individual, population:Population):void { 69 | 70 | this.currentRunning = population; 71 | if (this.isIndividual) { 72 | for (let ind of population.individuals) { 73 | this.execute(ind); 74 | } 75 | } else { 76 | this.execute(population); 77 | } 78 | } 79 | 80 | } -------------------------------------------------------------------------------- /src/operators/objective/Weierstrass.ts: -------------------------------------------------------------------------------- 1 | import {OutputField} from "../../models/FieldDef"; 2 | import {NumericField} from "../../models/FieldDef"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Individual} from "../../models/Individual"; 5 | import {IndividualOperator} from "../../models/IndividualOperator"; 6 | import {Num} from "../../Num"; 7 | 8 | @Register 9 | export class Weierstrass extends IndividualOperator { 10 | 11 | public static OBJ_FIELD_NAME:string = 'weierstrass'; 12 | public static PRECISION:number = 6; 13 | 14 | private X_FIELD_NAME:string = 'x'; 15 | private Y_FIELD_NAME:string = 'y'; 16 | private a:number; 17 | private b:number; 18 | private kMax:number; 19 | private constant:number; 20 | 21 | constructor() { 22 | super('Weierstrass'); 23 | this.a = 0.5; 24 | this.b = 3.0; 25 | this.kMax = 20; 26 | 27 | var tmp:number = 0.0; 28 | 29 | for (var k = 0; k <= this.kMax; k++) { 30 | tmp += Math.pow(this.a, k) * Math.cos(2 * Math.PI * Math.pow(this.b, k) * 0.5); 31 | } 32 | 33 | this.constant = tmp; 34 | 35 | } 36 | 37 | 38 | execute(individual:Individual):void { 39 | var x = individual.getValue(this.X_FIELD_NAME); 40 | var y = individual.getValue(this.Y_FIELD_NAME); 41 | 42 | 43 | var tmp:number = 0; 44 | 45 | 46 | for (let k = 0; k <= this.kMax; k++) { 47 | tmp += Math.pow(this.a, k) * Math.cos(2 * Math.PI * Math.pow(this.b, k) * (x + 0.5)); 48 | } 49 | 50 | for (let k = 0; k <= this.kMax; k++) { 51 | tmp += Math.pow(this.a, k) * Math.cos(2 * Math.PI * Math.pow(this.b, k) * (y + 0.5)); 52 | } 53 | 54 | 55 | var value:number = tmp - 2 * this.constant; 56 | individual.setValue(Weierstrass.OBJ_FIELD_NAME, Num.roundToPrecision(value, Weierstrass.PRECISION)); 57 | 58 | } 59 | 60 | getName():string { 61 | return "Weierstrass"; 62 | } 63 | 64 | getFieldDefinition():Array { 65 | var x = new NumericField(this.X_FIELD_NAME, 0, 2, Weierstrass.PRECISION); 66 | var y = new NumericField(this.Y_FIELD_NAME, 0, 2, Weierstrass.PRECISION); 67 | var value = new OutputField(Weierstrass.OBJ_FIELD_NAME); 68 | 69 | return [x, y, value]; 70 | } 71 | 72 | } 73 | 74 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 59 | 60 | -------------------------------------------------------------------------------- /src/operators/swarm/PSOA.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {OutputField} from "../../models/FieldDef"; 5 | import {Individual} from "../../models/Individual"; 6 | import {Num} from "../../Num"; 7 | 8 | @Register 9 | export class PSOA extends PopulationOperator { 10 | 11 | 12 | private fitness:string; 13 | 14 | constructor(fitness:string, private omega:number = 0.85, private c1:number = 0.1, private c2:number = 0.1) { 15 | super('PSOA'); 16 | this.fitness = fitness; 17 | } 18 | 19 | execute(population:Population):void { 20 | 21 | //assuming that is already ranked 22 | var best = population.individuals[0].getValue("PSOData"); 23 | 24 | var omega:number = this.omega; 25 | var c1:number = this.c1; 26 | var c2:number = this.c2; 27 | 28 | 29 | for (let i = 0; i < population.individuals.length; i++) { 30 | 31 | var currentIndividual:Individual = population.individuals[i]; 32 | var psoData = currentIndividual.getValue("PSOData"); 33 | 34 | 35 | for (let field of population.inputFields) { 36 | var pb = psoData[field]; 37 | 38 | if (!pb || currentIndividual.getValue(this.fitness) < psoData[this.fitness]) { 39 | var currentValue = currentIndividual.getValue(field); 40 | psoData[field] = pb = currentValue; 41 | } 42 | } 43 | psoData[this.fitness] = currentIndividual.getValue(this.fitness); 44 | 45 | for (let field of population.inputFields) { 46 | var pb = psoData[field]; 47 | var velocityKey = 'v.' + field; 48 | //x,y,z 49 | var currentValue = currentIndividual.getValue(field); 50 | var velocity = psoData[velocityKey]; 51 | 52 | if (velocity === undefined) { 53 | psoData[velocityKey] = velocity = 0; 54 | } 55 | 56 | psoData[velocityKey] = omega * velocity + (c1 * Num.getRandomNum() * (pb - currentValue)) + (c2 * Num.getRandomNum() * (best[field] - currentValue)); 57 | currentIndividual.setValue(field, currentValue + psoData[velocityKey]); 58 | } 59 | } 60 | } 61 | 62 | getFieldDefinition():Array { 63 | return [new OutputField("PSOData", {})]; 64 | } 65 | 66 | } -------------------------------------------------------------------------------- /src/operators/genetic/HauptGA.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Num} from "../../Num"; 5 | import {Individual} from "../../models/Individual"; 6 | import {FieldType} from "../../models/FieldDef"; 7 | 8 | @Register 9 | export class HauptGA extends PopulationOperator { 10 | 11 | 12 | private cp:number = 0.1; 13 | private mp:number = 0.1; 14 | 15 | constructor(crossoverProbability:number = 0.3, mutationProbabiliy:number = 0.1) { 16 | super('HauptGA'); 17 | this.cp = crossoverProbability; 18 | this.mp = mutationProbabiliy; 19 | } 20 | 21 | execute(population:Population):void { 22 | 23 | var selection:Array = population.cache['selection']; 24 | 25 | var child1:Individual = population.requestIndividual(); 26 | var child2:Individual = population.requestIndividual(); 27 | 28 | var parent1:Individual = selection[0]; 29 | var parent2:Individual = selection[1]; 30 | 31 | for (let field of population.inputFields) { 32 | 33 | var fieldDef:FieldDef = parent1.getFieldDefinition(field); 34 | 35 | //Apply only on numeric fields 36 | if (fieldDef.type !== FieldType.NUMERIC) { 37 | 38 | var parent1Value = parent1.getValue(field); 39 | var parent2Value = parent2.getValue(field); 40 | var crossoverValue = this.hauptCrossover(parent1Value, parent2Value); 41 | 42 | if (Num.getRandomNum() < this.cp) { 43 | child1.setValue(field, crossoverValue[0]); 44 | child2.setValue(field, crossoverValue[1]); 45 | } else { 46 | child1.setValue(field, parent1Value); 47 | child2.setValue(field, parent2Value); 48 | } 49 | 50 | if (Num.getRandomNum() < this.mp) { 51 | child1.setValue(field, this.hauptMutation(fieldDef.min, fieldDef.max)); 52 | } 53 | } 54 | 55 | } 56 | 57 | } 58 | 59 | getFieldDefinition():Array { 60 | return undefined; 61 | } 62 | 63 | private hauptCrossover(value1:number, value2:number):Array { 64 | if (_.isNumber(value1) && _.isNumber(value1)) { 65 | var beta = Num.getRandomNum(); 66 | return [beta * value1 + (1 - beta) * value2, (1 - beta) * value1 + beta * value2]; 67 | } else { 68 | return [value1, value2]; 69 | } 70 | 71 | } 72 | 73 | 74 | private hauptMutation(gmin:number, gmax:number):number { 75 | return gmin + Num.getRandomNum() * (gmax - gmin) 76 | } 77 | 78 | 79 | } -------------------------------------------------------------------------------- /src/tests/ArrFunc.ts: -------------------------------------------------------------------------------- 1 | 2 | import {IndividualOperator} from "../models/IndividualOperator"; 3 | import {FieldDef} from "../models/FieldDef"; 4 | import {JSCodeField} from "../models/fields/JSCodeField"; 5 | import {OutputField} from "../models/FieldDef"; 6 | import {Individual} from "../models/Individual"; 7 | export class ArrFunction extends IndividualOperator { 8 | 9 | keywords:Array = ["abstract", "arguments", "boolean", "break", "byte", "case", "catch", "char", "class*", "const", "continue", 10 | "debugger", "default", "delete", "do", "double", "else", "enum*", "eval", 11 | "export*", "extends*", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import*", 12 | "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", 13 | "protected", "public", "return", "short", "static", "super*", "switch", "synchronized", "this", "throw", "throws", "transient", 14 | "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield"]; 15 | 16 | definitions:Array = [/\\[.*\\]/, /\(.*\)/, /{.*}/]; 17 | 18 | constructor() { 19 | super('ArrFunction'); 20 | } 21 | 22 | getFieldDefinition():Array { 23 | return [new JSCodeField('content', 2, 6), new OutputField('obj', -1)]; 24 | } 25 | 26 | execute(individual:Individual):void { 27 | 28 | var obj = individual.getValue('obj'); 29 | if (obj !== -1) { 30 | return; 31 | } 32 | 33 | var func = individual.getValue('content'); 34 | var value = 0; 35 | 36 | 37 | try { 38 | var res = eval(func); 39 | if (!Array.isArray(res)) { 40 | value += 1e5; 41 | } else { 42 | value = 100; 43 | 44 | var last = res[0]; 45 | 46 | 47 | if (_.isNumber(last) && last < 10) { 48 | value--; 49 | } 50 | 51 | for (var i = 1; i < res.length; i++) { 52 | var val = res[i]; 53 | if (_.isNumber(val)) { 54 | value -= res[i] > last ? (res[i] - last === 1 ? -4 : -2) : -1; 55 | } else { 56 | value += 2; 57 | } 58 | last = res[i]; 59 | } 60 | } 61 | 62 | 63 | } catch (err) { 64 | value += 1e6 - func.length; 65 | 66 | 67 | for (let key in this.keywords) { 68 | if (func.indexOf(key) !== -1) { 69 | value--; 70 | } 71 | } 72 | 73 | for (let i = 0; i < this.definitions.length; i++) { 74 | if (func.match(this.definitions[i])) { 75 | value--; 76 | } 77 | } 78 | } 79 | 80 | 81 | individual.setValue('obj', value); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/models/fields/JSCodeField.ts: -------------------------------------------------------------------------------- 1 | import {FieldDef} from "../FieldDef"; 2 | import {Num} from "../../Num"; 3 | import {StrField} from "./StrField"; 4 | export class JSCodeField extends FieldDef { 5 | 6 | private keywords:Array = ["abstract", "arguments", "boolean", "break", "byte", "case", "catch", "char", "class*", "const", "continue", 7 | "debugger", "default", "delete", "do", "double", "else", "enum*", "eval", 8 | "export*", "extends*", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import*", 9 | "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", 10 | "protected", "public", "return", "short", "static", "super*", "switch", "synchronized", "this", "throw", "throws", "transient", 11 | "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield"]; 12 | private possible:string = " ;:'\"!@#$%^&*()_+-=\\|,./<>?[]{}ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 13 | 14 | private assignment:Array = ["+", "-", "*", "/", "%", "++", "--", "=", "=", "+=", "-=", "*=", "/=", "%="]; 15 | 16 | blocks:number; 17 | 18 | constructor(name:string, min:number = 10, max:number = 100) { 19 | super(name, ""); 20 | this.min = min; 21 | this.max = max; 22 | 23 | } 24 | 25 | getInitialValue():any { 26 | this.blocks = Num.getRandomNum(this.min, this.max); 27 | var result = ""; 28 | while (this.blocks > 0) { 29 | result += this.getBlock(); 30 | } 31 | 32 | return result; 33 | } 34 | 35 | getBlock():string { 36 | var value = ""; 37 | this.blocks--; 38 | if (this.blocks < 0) 39 | return value; 40 | 41 | var rand = Num.randomInt(0, 3); 42 | 43 | var str = this['case' + rand](); 44 | value += str; 45 | 46 | return value; 47 | 48 | } 49 | 50 | 51 | //assignment 52 | case0():string { 53 | return this.getBlock() + this.assignment[Num.randomInt(0, this.assignment.length)] + this.getBlock(); 54 | } 55 | 56 | //random 57 | case1():string { 58 | var num = Num.randomInt(1, 10); 59 | var str = ""; 60 | 61 | for (var i = 0; i < num; i++) { 62 | str += StrField.getRandomChar(); 63 | 64 | } 65 | 66 | return str; 67 | } 68 | 69 | //data-structure 70 | case2():string { 71 | var wrapperRand = Num.randomInt(0, 2); 72 | return wrapperRand === 0 ? 73 | '[' + this.getBlock() + ']' : 74 | wrapperRand === 1 ? 75 | '{' + this.getBlock() + '}' : 76 | wrapperRand === 2 ? 77 | '(' + this.getBlock() + ')' : 78 | '"' + this.getBlock() + '"'; 79 | } 80 | 81 | //data-structure 82 | case3():string { 83 | return " " + this.keywords[Num.randomInt(0, this.keywords.length - 1)] + " "; 84 | } 85 | 86 | 87 | } -------------------------------------------------------------------------------- /src/operators/renderers/CanvasRenderer.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | 5 | @Register 6 | export class CanvasRenderer extends PopulationOperator { 7 | 8 | private canvas:any; 9 | private xfield:string; 10 | private yfield:string; 11 | private scale:any; 12 | 13 | private width:number = 400; 14 | private height:number = 300; 15 | 16 | constructor(xfield:string, yfield:string, scale?:any, canvasWidth?:number, canvasHeight?:number) { 17 | super("Canvas View"); 18 | this.scale = scale; 19 | this.xfield = xfield; 20 | this.yfield = yfield; 21 | this.width = canvasWidth || this.width; 22 | this.height = canvasWidth || this.height; 23 | if (window) { 24 | this.canvas = $(''); 25 | var that = this; 26 | this.canvas.click(function(){ 27 | var scaleX = that.scale && that.scale[that.xfield] !== undefined ? that.scale[that.xfield] : undefined; 28 | var scaleY = that.scale && that.scale[that.yfield] !== undefined ? that.scale[that.yfield] : undefined; 29 | 30 | 31 | 32 | scaleX[0]+=0.001; 33 | scaleX[1]-=0.001; 34 | scaleY[0]+=0.001; 35 | scaleY[1]-=0.001; 36 | }); 37 | $(document.body).append(this.canvas); 38 | 39 | } 40 | 41 | 42 | } 43 | 44 | execute(population:Population):void { 45 | var canvas = this.canvas[0]; 46 | 47 | 48 | if (canvas.getContext) { 49 | var ctx = canvas.getContext('2d'); 50 | //ctx.clearRect(0, 0, this.width, this.height); 51 | ctx.globalAlpha=0.2; 52 | ctx.fillStyle="white"; 53 | ctx.fillRect(0, 0, this.width, this.height); 54 | 55 | ctx.globalAlpha = 1; 56 | ctx.fillStyle=population.color; 57 | var idx = 0; 58 | for (var ind of population.individuals) { 59 | idx++; 60 | 61 | var x = ind.getValue(this.xfield); 62 | var y = ind.getValue(this.yfield); 63 | 64 | var scaleX = this.scale && this.scale[this.xfield] !== undefined ? this.scale[this.xfield] : undefined; 65 | var scaleY = this.scale && this.scale[this.yfield] !== undefined ? this.scale[this.yfield] : undefined; 66 | 67 | if (scaleX) { 68 | x = (Math.abs(x - scaleX[0]) / Math.abs(scaleX[1] - scaleX[0])) * this.width; 69 | 70 | } 71 | 72 | if (scaleY) { 73 | y = (Math.abs(y - scaleY[0]) / Math.abs(scaleY[1] - scaleY[0])) * this.height; 74 | } 75 | 76 | 77 | // ctx.globalAlpha = (1-idx/population.individuals.length); 78 | // ctx.fillStyle=population.color; 79 | 80 | ctx.fillRect(x, y, 1, 1); 81 | 82 | } 83 | } 84 | 85 | 86 | } 87 | 88 | getFieldDefinition():Array { 89 | return undefined; 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /src/models/FieldDef.ts: -------------------------------------------------------------------------------- 1 | import {Num} from "../Num"; 2 | 3 | export class FieldType { 4 | static NUMERIC:number = 0; 5 | static ANY:number = 1; 6 | } 7 | 8 | export abstract class FieldDef { 9 | name:string; 10 | value:any; 11 | type:number = -1; 12 | defaultValue:any; 13 | min:number; 14 | max:number; 15 | precision:number; 16 | 17 | 18 | constructor(name:string, defaultValue?:any) { 19 | this.name = name; 20 | this.value = this.defaultValue = defaultValue; 21 | } 22 | 23 | filter(value:any):any { 24 | return value; 25 | } 26 | 27 | abstract getInitialValue():any; 28 | 29 | describe():string { 30 | var definition:string = "Declares: " + this.name; 31 | if (this instanceof OutputField) { 32 | definition += " as output field"; 33 | if (this.defaultValue !== undefined) { 34 | definition += " with " + this.defaultValue + " as default value"; 35 | } 36 | } else { 37 | definition += " as field with ("; 38 | definition += this.min !== undefined ? " min:" + this.min : ""; 39 | definition += this.max !== undefined ? " max:" + this.max : ""; 40 | definition += this.precision !== undefined ? " precision:" + this.precision : ""; 41 | definition += ")"; 42 | } 43 | 44 | return definition; 45 | } 46 | 47 | } 48 | 49 | 50 | export class OutputField extends FieldDef { 51 | 52 | getInitialValue():any { 53 | return _.clone(this.value); 54 | } 55 | 56 | constructor(name:string, defaultValue?:any) { 57 | super(name, defaultValue); 58 | this.type = FieldType.ANY; 59 | } 60 | } 61 | 62 | export class NumericField extends FieldDef { 63 | 64 | 65 | constructor(name:string, min:number, max:number, precision:number = 6, defaultValue?:any) { 66 | super(name, defaultValue); 67 | this.min = min; 68 | this.max = max; 69 | this.precision = precision; 70 | this.defaultValue = defaultValue; 71 | this.type = FieldType.NUMERIC; 72 | } 73 | 74 | filter(value:number):any { 75 | 76 | if (this.min !== undefined && value < this.min) { 77 | return this.min; 78 | } 79 | if (this.max !== undefined && value > this.max) { 80 | return this.max; 81 | } 82 | 83 | if (this.precision !== undefined) { 84 | return Num.roundToPrecision(value, this.precision); 85 | } 86 | 87 | return value; 88 | } 89 | 90 | getInitialValue():any { 91 | if (this.defaultValue === undefined) { 92 | return Num.getRandomNum(this.min, this.max, this.precision); 93 | } 94 | } 95 | } 96 | 97 | export class JSField { 98 | 99 | static get(config:any):FieldDef { 100 | 101 | if (!config.name) { 102 | throw new Error("JSField must be initialized with a name."); 103 | } 104 | 105 | var field:FieldDef; 106 | if (config.numeric) { 107 | var numData:any = config.numeric; 108 | if (!numData.min || !numData.max) { 109 | throw new Error("Please provide min and max value for numeric field [" + config.name + "]."); 110 | } 111 | 112 | field = new NumericField(config.name, numData.min, numData.max, numData.precision, config.defaultValue) 113 | } else { 114 | field = new OutputField(config.name, config.defaultValue); 115 | } 116 | 117 | return field; 118 | } 119 | 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/hud/GuiHud.ts: -------------------------------------------------------------------------------- 1 | import {Application} from "../Application"; 2 | export class GuiHud { 3 | 4 | app:Application; 5 | 6 | constructor(app:Application, populationDefault:number = 100) { 7 | 8 | if (!window) { 9 | throw new Error("Window object is missing. Not in a browser environment?"); 10 | } 11 | 12 | this.app = app; 13 | var style = document.createElement("style"); 14 | style.appendChild(document.createTextNode( 15 | ".cm-btn {" + 16 | " background-color: #FFFFFF; " + 17 | " border-radius: 2px; " + 18 | " padding: 6px; " + 19 | " margin: 2px;" + 20 | " box-shadow: 0px 0px 1px rgba(0,0,0,125);" + 21 | " -webkit-touch-callout: none; " + 22 | " -webkit-user-select: none; " + 23 | " -khtml-user-select: none; " + 24 | " -moz-user-select: none; " + 25 | " -ms-user-select: none; " + 26 | " user-select: none; " + 27 | 28 | "}" + 29 | ".cm-btn:hover {" + 30 | " background-color: #FFFFFF; " + 31 | " cursor:pointer; " + 32 | " margin: 0px;" + 33 | " padding: 8px;" + 34 | " box-shadow: 0px 0px 6px rgba(0,0,0,125);" + 35 | "}" + 36 | "")); 37 | 38 | 39 | document.head.appendChild(style); 40 | 41 | var root:HTMLElement = window.document.createElement('div'); 42 | root.style.position = "fixed"; 43 | root.style.backgroundColor = "rgba(0,0,0,0.2)"; 44 | root.style.padding = "10px"; 45 | root.style.right = "20px"; 46 | root.style.bottom = "20px"; 47 | 48 | var stop:HTMLElement; 49 | var start:HTMLElement; 50 | var tick:HTMLElement; 51 | 52 | 53 | start = this.addButton(root, "Start", function () { 54 | stop.style.display = "inline-block"; 55 | start.style.display = "none"; 56 | tick.style.display = "none"; 57 | app.start() 58 | }); 59 | 60 | 61 | stop = this.addButton(root, "Stop", function () { 62 | start.style.display = "inline-block"; 63 | tick.style.display = "inline-block"; 64 | stop.style.display = "none"; 65 | app.stop() 66 | }); 67 | 68 | tick = this.addButton(root, "Tick", function () { 69 | app.tick() 70 | }); 71 | 72 | 73 | stop.style.display = "none"; 74 | 75 | var populationNumber:HTMLInputElement = window.document.createElement('input'); 76 | populationNumber.value = populationDefault+""; 77 | populationNumber.style.width = "50px"; 78 | 79 | var addPop:HTMLElement = this.addButton(root, "+ Population", function () { 80 | app.addPopulation(parseInt(populationNumber.value)); 81 | }); 82 | 83 | addPop.appendChild(populationNumber); 84 | 85 | this.addButton(root, "Reset", function () { 86 | app.resetPopulations(); 87 | app.addPopulation(parseInt(populationNumber.value)); 88 | }); 89 | 90 | window.document.body.appendChild(root); 91 | 92 | } 93 | 94 | private addButton(root:HTMLElement, name:string, callback:Function):HTMLElement { 95 | var button:HTMLElement = window.document.createElement('span'); 96 | button.innerHTML = name; 97 | button.className = "cm-btn"; 98 | button.addEventListener('click', function () { 99 | callback(); 100 | }); 101 | 102 | root.appendChild(button); 103 | return button; 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /css-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 |
25 |
26 |
a
27 |
Col 1 Row 1
28 |
Col 2 Row 1
29 |
Col 3 Row 1
30 |
Col 4 Row 1
31 |
Col 5 Row 1
32 |
33 | 34 |
35 |
b
36 |
Col 1 Row 2
37 |
Col 2 Row 2
38 |
Col 3 Row 2
39 |
Col 4 Row 2
40 |
Col 5 Row 2
41 |
42 | 43 |
44 |
c
45 |
Col 1 Row 3
46 |
Col 2 Row 3
47 |
Col 3 Row 3
48 |
Col 4 Row 3
49 |
Col 5 Row 3
50 |
51 |
52 |
53 | 54 | 105 | 106 | -------------------------------------------------------------------------------- /src/operators/genetic/StringGA.ts: -------------------------------------------------------------------------------- 1 | import {FieldDef} from "../../models/FieldDef"; 2 | import {StrField} from "../../models/fields/StrField"; 3 | import {Num} from "../../Num"; 4 | import {PopulationOperator} from "../../models/PopulationOperator"; 5 | import {Population} from "../../models/Population"; 6 | import {Individual} from "../../models/Individual"; 7 | import {JSCodeField} from "../../models/fields/JSCodeField"; 8 | @Register 9 | export class StringGA extends PopulationOperator { 10 | 11 | 12 | constructor(public field, public crossoverChance = 0.2, public mutationChance = 0.5) { 13 | super('StringGA'); 14 | } 15 | 16 | execute(population:Population):void { 17 | 18 | 19 | var selection:Array = population.cache['selection']; 20 | 21 | 22 | var parent1:Individual = selection[0]; 23 | var parent2:Individual = selection[1]; 24 | 25 | 26 | var valueParent1 = parent1.getValue(this.field); 27 | var valueParent2 = parent2.getValue(this.field); 28 | 29 | if (valueParent1 === valueParent2) 30 | return; 31 | 32 | var crossoverPoint = Num.getRandomNum(0, Math.min(valueParent1.length, valueParent2.length), 0); 33 | 34 | var crossoverValue1:string = ""; 35 | var crossoverValue2:string = ""; 36 | 37 | var oneToOne = true; 38 | for (var i = 0; i < Math.max(valueParent1.length, valueParent2.length); i++) { 39 | 40 | if (i < valueParent1.length && i < valueParent2.length && Num.getRandomNum() < this.crossoverChance) 41 | oneToOne = !oneToOne; 42 | 43 | var vp1 = valueParent1.length <= i ? '' : valueParent1[i]; 44 | var vp2 = valueParent2.length <= i ? '' : valueParent2[i]; 45 | 46 | crossoverValue1 += oneToOne ? vp1 : vp2; 47 | crossoverValue2 += oneToOne ? vp2 : vp1; 48 | } 49 | 50 | 51 | //var crossoverValue1 = valueParent1.substr(0, crossoverPoint) + valueParent2.substr(crossoverPoint, valueParent2.length - crossoverPoint); 52 | //var crossoverValue2 = valueParent2.substr(0, crossoverPoint) + valueParent1.substr(crossoverPoint, valueParent1.length - crossoverPoint); 53 | 54 | crossoverValue1 = this.mutate(crossoverValue1); 55 | crossoverValue2 = this.mutate(crossoverValue2); 56 | 57 | 58 | if (crossoverValue1.trim() !== "" && crossoverValue1 != valueParent1 && crossoverValue1 != valueParent2) { 59 | var child1:Individual = population.requestIndividual(); 60 | child1.setValue(this.field, crossoverValue1); 61 | } 62 | 63 | if (crossoverValue2.trim() !== "" && crossoverValue1 != valueParent1 && crossoverValue1 != valueParent2) { 64 | var child2:Individual = population.requestIndividual(); 65 | child2.setValue(this.field, crossoverValue2); 66 | } 67 | 68 | } 69 | 70 | 71 | private mutate(str:string):string { 72 | var result = str; 73 | if (Num.getRandomNum() < this.mutationChance) { 74 | 75 | var res = Num.getRandomNum(0, 7, 0); 76 | var position = Num.getRandomNum(0, str.length, 0); 77 | var remainingLength = str.length - position; 78 | var randomLength = Num.randomInt(0, str.length - position); 79 | 80 | switch (res) { 81 | //remove one char 82 | case (0): 83 | result = str.slice(0, position) + str.slice(position + 1); 84 | break; 85 | 86 | //add one char 87 | case (1): 88 | result = str.slice(0, position) + str.slice(position + 1); 89 | break; 90 | 91 | //prefix 92 | case (2): 93 | result = str.slice(0, position); 94 | break; 95 | 96 | //suffix 97 | case (3): 98 | result = str.slice(position); 99 | break; 100 | 101 | //within 102 | case (4): 103 | result = str.substr(position, remainingLength); 104 | break; 105 | 106 | case (5): 107 | result = str.slice(0, position) + str.slice(position + randomLength); 108 | break; 109 | 110 | 111 | case (6): 112 | result = str.slice(0, position) + StrField.getRandomChar() + str.slice(position); 113 | break; 114 | 115 | case (7): 116 | result = str.slice(0, position) + new JSCodeField("test").getBlock() + str.slice(position); 117 | break; 118 | 119 | } 120 | 121 | } 122 | return result; 123 | } 124 | 125 | getFieldDefinition():Array { 126 | return undefined; 127 | } 128 | 129 | 130 | } -------------------------------------------------------------------------------- /src/operators/css/CSSDescriptor.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Num} from "../../Num"; 5 | import {Individual} from "../../models/Individual"; 6 | import {FieldType} from "../../models/FieldDef"; 7 | import {IndividualOperator} from "../../models/IndividualOperator"; 8 | import {OutputField} from "../../models/FieldDef"; 9 | import {CSSField} from "../../models/fields/CSSField"; 10 | 11 | @Register 12 | export class CSSDescriptor extends IndividualOperator { 13 | 14 | private styleElement:HTMLStyleElement; 15 | private htmlElement:HTMLElement; 16 | private elementId:string; 17 | 18 | constructor(id:string) { 19 | super('CSSDescriptor'); 20 | this.elementId = id; 21 | this.styleElement = document.createElement('style'); 22 | this.styleElement.id = "cssstyle"; 23 | document.getElementsByTagName('head')[0].appendChild(this.styleElement); 24 | this.htmlElement = document.getElementById(id); 25 | 26 | } 27 | 28 | execute(ind:Individual):void { 29 | if (ind.getValue('result') === undefined) { 30 | var val = CSSField.getFullCSSString(ind.getValue('css')); 31 | this.styleElement.innerHTML = val; 32 | var value = 0; 33 | 34 | value += this.alignment(document, '.content > div', true); 35 | value += this.noOverlapping(document, '.content > div'); 36 | value += this.noOffset(document, '.content > div'); 37 | value += this.fitInsideParent(document.getElementsByClassName('content')[0]); 38 | 39 | [].slice.call(document.querySelectorAll('.content > div')).forEach(elem=> { 40 | value += this.alignment(elem, '.elem', false); 41 | value += this.noOverlapping(elem, '.elem'); 42 | }); 43 | 44 | if (val.length === 0) { 45 | value = 1e10; 46 | } else { 47 | value += 1 - 1 / val.length; 48 | } 49 | 50 | 51 | ind.setValue("result", value); 52 | } 53 | } 54 | 55 | noOffset(root:any, query:String, increment:number = 0.1):number { 56 | var value = 0; 57 | [].slice.call(root.querySelectorAll(query)).forEach(elem=> { 58 | value += Math.abs(elem.offsetLeft); 59 | }); 60 | 61 | return value; 62 | } 63 | 64 | 65 | fitInsideParent(element:any):number { 66 | var value = 0; 67 | 68 | var parent = element.parentElement, 69 | width = parent.offsetWidth, 70 | height = parent.offsetHeight, 71 | actualWidth = element.offsetWidth + element.offsetLeft, 72 | actualHeight = element.offsetHeight + element.offsetTop; 73 | 74 | 75 | value += actualWidth > width ? actualWidth - width : 0; 76 | value += actualHeight > height ? actualHeight - height : 0; 77 | 78 | if (value > 0) { 79 | value = 1 - (1 / value); 80 | } 81 | 82 | return value; 83 | } 84 | 85 | noOverlapping(root:any, query:String, increment:number = 0.1):number { 86 | var value = 0, 87 | arr = [].slice.call(root.querySelectorAll(query)); 88 | for (var i = 0; i < arr.length - 1; i++) { 89 | for (var j = i + 1; j < arr.length; j++) { 90 | var rect1 = arr[i].getBoundingClientRect(); 91 | var rect2 = arr[j].getBoundingClientRect(); 92 | var overlap = !(rect1.right < rect2.left || 93 | rect1.left > rect2.right || 94 | rect1.bottom < rect2.top || 95 | rect1.top > rect2.bottom) 96 | 97 | if (overlap) { 98 | value += increment; 99 | } 100 | } 101 | } 102 | 103 | return value; 104 | } 105 | 106 | alignment(root:any, query:String, leftAlignment:boolean, increment:number = 0.1):number { 107 | var value = 0; 108 | var lastX = -1; 109 | var lastY = -1; 110 | 111 | [].slice.call(root.querySelectorAll(query)).forEach(elem=> { 112 | var clientRect = elem.getBoundingClientRect(); 113 | 114 | if (lastX == -1 && lastY == -1) { 115 | 116 | } else if (!leftAlignment && lastY !== clientRect.top) { 117 | value += Math.abs(clientRect.top - lastY); 118 | } else if (leftAlignment && lastX !== clientRect.left) { 119 | value += Math.abs(clientRect.left - lastX); 120 | } 121 | 122 | lastX = clientRect.left; 123 | lastY = clientRect.top; 124 | 125 | }); 126 | 127 | return value; 128 | } 129 | 130 | getFieldDefinition():Array { 131 | var css = new CSSField(this.elementId, 'css'); 132 | var value = new OutputField("result"); 133 | return [css, value]; 134 | } 135 | 136 | 137 | } -------------------------------------------------------------------------------- /src/operators/css/CSSGAOperator.ts: -------------------------------------------------------------------------------- 1 | import {PopulationOperator} from "../../models/PopulationOperator"; 2 | import {Population} from "../../models/Population"; 3 | import {FieldDef} from "../../models/FieldDef"; 4 | import {Num} from "../../Num"; 5 | import {Individual} from "../../models/Individual"; 6 | import {FieldType} from "../../models/FieldDef"; 7 | import {CSSField} from "../../models/fields/CSSField"; 8 | 9 | @Register 10 | export class CSSGAOperator extends PopulationOperator { 11 | tags:Array; 12 | 13 | constructor(id:string, 14 | private ruleAditionProbability:number = 0.6, 15 | private ruleRemovalProbability:number = 0.02, 16 | private propertyAlterProbability:number = 0.4, 17 | private propertyAdditionProbability:number = 0.4, 18 | private propertyRemovalProbability:number = 0.5) { 19 | super('CSSGAOperator'); 20 | this.tags = CSSField.getTagsList(id); 21 | } 22 | 23 | execute(population:Population):void { 24 | 25 | var selection:Array = population.cache['selection']; 26 | 27 | var child1:Individual = population.requestIndividual(); 28 | var child2:Individual = population.requestIndividual(); 29 | 30 | var parent1:Individual = selection[0]; 31 | var parent2:Individual = selection[1]; 32 | 33 | var parent1CSSObject:Array = parent1.getValue('css'); 34 | var parent2CSSObject:Array = parent2.getValue('css'); 35 | 36 | 37 | var location = Num.randomInt(1, Math.min(parent1CSSObject.length, parent2CSSObject.length) - 1); 38 | 39 | if (location > 0) { 40 | var end1 = parent1CSSObject.splice(location); 41 | var end2 = parent2CSSObject.splice(location); 42 | parent1CSSObject.concat(end2); 43 | parent2CSSObject.concat(end1); 44 | } 45 | 46 | 47 | //console.log(CSSField.getFullCSSString(parent1CSSObject)); 48 | this.mutate(parent1CSSObject); 49 | this.mutate(parent2CSSObject); 50 | //console.log(CSSField.getFullCSSString(parent1CSSObject)); 51 | 52 | child1.setValue('css', parent1CSSObject); 53 | child2.setValue('css', parent2CSSObject); 54 | 55 | //console.log(parent1.getValue('css')); 56 | //console.log(child1.getValue('css')); 57 | //console.log("------"); 58 | } 59 | 60 | mutate(properties:Array) { 61 | try { 62 | 63 | 64 | if (properties.length === 0 || Num.getRandomNum() < this.ruleAditionProbability) { 65 | properties.push(CSSField.buildSingleCSS(this.tags)); 66 | return; 67 | } 68 | 69 | var selectedRule = Num.randomInt(0, properties.length - 1); 70 | var defs = properties[selectedRule].def; 71 | var selectedProp = Num.randomInt(0, defs.length - 1); 72 | 73 | if (defs.length === 0 || Num.getRandomNum() < this.ruleRemovalProbability && properties.length > 1) { 74 | properties.splice(selectedRule, 1); 75 | } 76 | 77 | if (Num.getRandomNum() < this.propertyAlterProbability) { 78 | defs[selectedProp].value = CSSField.getCSSValueForProp(defs[selectedProp].property); 79 | } 80 | if (Num.getRandomNum() < this.propertyAdditionProbability) { 81 | var rule = CSSField.getSingleCSSRule(); 82 | var found; 83 | for (var i = 0; i < defs.length; i++) { 84 | if (defs[i].property === rule.property) { 85 | found = defs[i]; 86 | break; 87 | } 88 | } 89 | if (found) { 90 | found.value = rule.value; 91 | } else { 92 | defs.push(rule); 93 | } 94 | 95 | } 96 | if (Num.getRandomNum() < this.propertyRemovalProbability) { 97 | defs.splice(selectedProp, 1); 98 | } 99 | 100 | 101 | } catch (e) { 102 | console.log(properties, defs, selectedRule, selectedProp); 103 | } 104 | 105 | } 106 | 107 | getCSSString(arr:Array):string { 108 | var val = ""; 109 | arr.forEach(obj => { 110 | val += obj.def + "{" + obj.val.join(";") + "}" 111 | }); 112 | return val; 113 | } 114 | 115 | getCSSArray(cssString:string):Array { 116 | var cssObject = []; 117 | var m; 118 | var re:RegExp = /(.*?){(.*?)}/gm; 119 | while ((m = re.exec(cssString)) !== null) { 120 | if (m.index === re.lastIndex) { 121 | re.lastIndex++; 122 | } 123 | cssObject.push({def: m[1].trim(), val: m[2].trim().split(";")}); 124 | } 125 | 126 | return cssObject; 127 | 128 | } 129 | 130 | getFieldDefinition():Array { 131 | return undefined; 132 | } 133 | 134 | private hauptCrossover(value1:number, value2:number):Array { 135 | if (_.isNumber(value1) && _.isNumber(value1)) { 136 | var beta = Num.getRandomNum(); 137 | return [beta * value1 + (1 - beta) * value2, (1 - beta) * value1 + beta * value2]; 138 | } else { 139 | return [value1, value2]; 140 | } 141 | 142 | } 143 | 144 | 145 | private hauptMutation(gmin:number, gmax:number):number { 146 | return gmin + Num.getRandomNum() * (gmax - gmin) 147 | } 148 | 149 | 150 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Evolutionary Computation Benchmark 2 | 3 | The application is intended to be used to test evolutionary algorithms and find NP-Hard problem solutions. 4 | For the moment the library is using for solution finding only Particle Swarm but we intend to add Genetic, Memetic, Social etc. Algorithms soon. 5 | 6 | You can see it in action for optimising [Rastrigin](http://run.plnkr.co/plunks/UhXCUz/) and [Schwefel](http://run.plnkr.co/plunks/4lEiZy/) using Particle Swarm. 7 | 8 | # Changes 9 | ## 24.04 10 | * added Genetic Algorithm support and operators. Example: [Schwefel optimisation using GA](https://embed.plnkr.co/V7ABbK/) 11 | * added cache object to population in order to store volatile information 12 | * added option to GrimReaper to allow option to generate new individual when one is removed (by default this option is true) 13 | 14 | # Having fun with the benchmark: 15 | 16 | - for a plnkr 17 | 18 | OR 19 | 20 | - install NodeJS & clone/fork the repository and run: 21 | ``` 22 | npm install 23 | tsc -w 24 | ``` 25 | open index.html 26 | 27 | # The Library 28 | 29 | The library is intended to be used from from both Browser or Node environments. The library is implemented using Typescript but Javascript wrappers are created (see Plunker examples). 30 | 31 | Typescript Example: 32 | ``` 33 | var application = new Application(); // Initialize population 34 | 35 | if (window) { 36 | window['application'] = application; // Make them available on browser console 37 | window['num'] = Num; // Also random number generator 38 | } 39 | 40 | application.addPopulation(100); // Add a population of 100 individuals 41 | application.addPopulation(30); // Add a population of 30 individuals 42 | 43 | 44 | application.addOperator(new Schwefel()); // Add Schwefel optimisation problem 45 | application.addOperator(new MinRank(Schwefel.OBJ_FIELD_NAME)); // Add population sorter based on Schwefel obj result 46 | application.addOperator(new CanvasRenderer('x', 'y', {x: [-510, 510], y: [-510, 510]})); // Add population renderer (zoomed out) 47 | application.addOperator(new CanvasRenderer('x', 'y', {x: [-420.98, -420.96], y: [-420.98, -420.96]})); // Add population renderer (zoomed in) 48 | application.addOperator(new GrimReaper(100)); // Add operator that removes "old" individuals 49 | application.addOperator(new RandomIndividualGenerator(5, 0.2)); // Add random individual generators (trying to add 5 each iteration with a chance of 20%) 50 | application.addOperator(new PSOA(Schwefel.OBJ_FIELD_NAME)); // Calculate Particle Swarm vectors 51 | 52 | application.initializeHud(); //Initialize the Mini-HUD for easier algorithm interactions (start/stop/reset) 53 | ``` 54 | 55 | Javascript Example: 56 | ``` 57 | System.import('Application').then(function(module) { 58 | 59 | window.application = new module.Application(function() { 60 | application.addOperator(application.createOperator('Schwefel')); 61 | application.addOperator(application.createOperator('MinRank', ['schwefel'])); 62 | application.addOperator(application.createOperator('CanvasRenderer', ['x', 'y', { 63 | x: [-510, 510], 64 | y: [-510, 510] 65 | }])); 66 | application.addOperator(application.createOperator('CanvasRenderer', ['x', 'y', { 67 | x: [-420.98, -420.96], 68 | y: [-420.98, -420.96] 69 | }])); 70 | application.addOperator(application.createOperator('GrimReaper', [100])); 71 | application.addOperator(application.createOperator('RandomIndividualGenerator', [5, 0.2])); 72 | application.addOperator(application.createOperator('TableRenderer', [4, false])); 73 | application.addOperator(application.createOperator('PSOA', ['schwefel'])); 74 | 75 | application.addPopulation(100); 76 | application.addPopulation(30); 77 | 78 | application.tick(); 79 | application.initializeHud(); 80 | }) 81 | 82 | }, function(err) { 83 | console.log(err) 84 | }).catch(console.error.bind(console)); 85 | ``` 86 | 87 | # Details 88 | 89 | The Benchmark is developed around two concepts: 90 | - individuals -form populations and store state 91 | - Operators: pieces of logic that form the algorithm - they have the power to alter, generate or destroy individuals or population. 92 | 93 | ## Operator 94 | 95 | Each operator definition must define the execute function. Based on the type of definition, population or individual, the execute function will receive as parameter either the entire current population or each individual. 96 | The individual type operators are best for defining Objective functions that receive a set of parameters (x,y) and set another sed (output1, output2). The population operators are mostly used when one individual must be evaluated in relation with the rest of the population (ranking functions). 97 | The operator can also define a set of fields that will be stored as state for each individual in the population. 98 | 99 | Defining operators: 100 | 101 | Typescript: 102 | ``` 103 | @Register 104 | export class Schwefel extends IndividualOperator { 105 | 106 | //Extract output field as public constant 107 | public static OBJ_FIELD_NAME:string = 'schwefel'; 108 | public static PRECISION:number = 10; 109 | 110 | private X_FIELD_NAME:string = 'x'; 111 | private Y_FIELD_NAME:string = 'y'; 112 | 113 | //Construct operator with "Schwefel" name 114 | constructor() { 115 | super('Schwefel'); 116 | } 117 | 118 | //Define execution function with individual as input parameter 119 | execute(individual:Individual):void { 120 | var x = individual.getValue(this.X_FIELD_NAME); // read x value 121 | var y = individual.getValue(this.Y_FIELD_NAME); // read y value 122 | 123 | var sum:number = 0; 124 | 125 | sum += x * Math.sin(Math.sqrt(Math.abs(x))); 126 | sum += y * Math.sin(Math.sqrt(Math.abs(y))); 127 | 128 | var value:number = sum + 2 * 4.18982887272434686131e+02; 129 | 130 | //Set objective value (schwefel) field to result 131 | individual.setValue(Schwefel.OBJ_FIELD_NAME, Num.roundToPrecision(value, Schwefel.PRECISION)); 132 | } 133 | 134 | //@deprecated 135 | getName():string { 136 | return "Schwefel"; 137 | } 138 | 139 | //Return field definition needed by the algorithm 140 | getFieldDefinition():Array { 141 | var x = new NumericField(this.X_FIELD_NAME, -500, 500, Schwefel.PRECISION); 142 | var y = new NumericField(this.Y_FIELD_NAME, -500, 500, Schwefel.PRECISION); 143 | var value = new OutputField(Schwefel.OBJ_FIELD_NAME); 144 | return [x, y, value]; 145 | } 146 | 147 | } 148 | ``` 149 | 150 | Javascript: 151 | ``` 152 | application.newJSOperator('schwefel', function (individual) { 153 | var x = individual.getValue('x'); 154 | var y = individual.getValue('y'); 155 | 156 | var sum = x * Math.sin(Math.sqrt(Math.abs(x))); 157 | sum += y * Math.sin(Math.sqrt(Math.abs(y))); 158 | 159 | var value:number = sum + 2 * 4.18982887272434686131e+02; 160 | 161 | individual.setValue('schwefel', value, 6); 162 | }, true, [ 163 | {name: 'x', numeric: {min: -500, max: 500, precision: 4}}, 164 | {name: 'y', numeric: {min: -500, max: 500, precision: 4}}, 165 | {name: 'schwefel'}, 166 | ]) 167 | ``` 168 | 169 | 170 | # API 171 | 172 | The Benchmark is developed with extension in mind. The framework offers an API for testing algorithms. 173 | 174 | * addPopulation(*populationSize*) - adds a new population to the set with *populationSize* if defined, 100 otherwise 175 | * numPopulations() - returns the number of individuals 176 | * requestIndividual() - creates, register to the current population, prepare and return a new individual 177 | * removeIndividual(*individual*) - removes provided *individual* from the current population 178 | * resetPopulations() - clears populations 179 | * tick() - run an evolution cycle 180 | * start(*numTicks*) - start evolutionary process. If *numTicks* is provided then it will execute as many cycles as passed as parameter 181 | * stop() - stops evolutionary process 182 | * listOperators() - [JS] lists known operators 183 | * describeOperator(*name*) - [JS] describes (parameters and fields) operator found for *name* 184 | * createOperator(*operatorName*, *parameters*) - [JS] used to create an instance of the *operatorName* with the provided set of *parameters* 185 | * addOperator(*operator*, *parent*) - adds the *operator* to the current evolutionary algorithm. Since the operators can be grouped a *parent* can be passed. If there is no parent defined the operator will be added to the root group 186 | 187 | 188 | Contributions and suggestions are welcome. 189 | 190 | -------------------------------------------------------------------------------- /src/Application.ts: -------------------------------------------------------------------------------- 1 | import {Population} from "./models/Population"; 2 | import {Operator} from "./models/Operator"; 3 | import {GroupOperator} from "./models/GroupOperator"; 4 | import {OutputField} from "./models/FieldDef"; 5 | import {Individual} from "./models/Individual"; 6 | import {IndividualOperator} from "./models/IndividualOperator"; 7 | import {PopulationOperator} from "./models/PopulationOperator"; 8 | import {JSOperator} from "./models/Operator"; 9 | import {Log} from "./Log"; 10 | import {GuiHud} from "./hud/GuiHud"; 11 | import {FieldDef} from "./models/FieldDef"; 12 | import {Schwefel} from "./operators/objective/Schwefel"; 13 | import {Num} from "./Num"; 14 | import {MinRank} from "./operators/ranking/MinRank"; 15 | import {CanvasRenderer} from "./operators/renderers/CanvasRenderer"; 16 | import {GrimReaper} from "./operators/misc/GrimReaper"; 17 | import {RandomIndividualGenerator} from "./operators/misc/RandomIndividualGenerator"; 18 | import {PSOA} from "./operators/swarm/PSOA"; 19 | import {LinearRanking} from "./operators/ranking/LinearRanking"; 20 | import {TableRenderer} from "./operators/renderers/TableRenderer"; 21 | import {Roulette} from "./operators/selection/Roulette"; 22 | import {HauptGA} from "./operators/genetic/HauptGA"; 23 | import {PopulationSizeControl} from "./operators/misc/PopulationSizeControl"; 24 | 25 | export class Application { 26 | 27 | private colors:Array = ['green', 'blue', 'red', 'orange', 'black', 'teal', 'pink', 'magenta', 'fuchsia']; 28 | 29 | 30 | constructor(callback?:Function) { 31 | // Application.instance = this; 32 | 33 | if (callback) { 34 | Promise.all(Object.keys(System.defined).map((key) => { 35 | return System.import(key); 36 | })).then(() => { 37 | callback(); 38 | }); 39 | 40 | } 41 | } 42 | 43 | private populations:Array = []; 44 | private interval; 45 | private currentPopulation:Population; 46 | 47 | public rootOperator:Operator = new GroupOperator('root'); 48 | public delay:number = 50; 49 | public populationSize:number = 100; 50 | 51 | public numPopulations():number { 52 | return this.populations.length; 53 | } 54 | 55 | public addPopulation(populationSize?:number):void { 56 | if (!this.rootOperator) { 57 | this.rootOperator = new GroupOperator("root"); 58 | } 59 | 60 | 61 | var population:Population = new Population(populationSize || this.populationSize); 62 | population.index = this.populations.length; 63 | population.color = this.colors[this.populations.length]; 64 | var that = this; 65 | population.rind = function () { 66 | return that.requestIndividual() 67 | }; 68 | this.preparePopulation(this.rootOperator, population); 69 | this.populations.push(population); 70 | } 71 | 72 | public addOperator(operator:Operator, parent?:Operator):void { 73 | var host:Operator = parent !== undefined ? parent : this.rootOperator; 74 | host.addOperator(operator); 75 | 76 | console.log(operator); 77 | 78 | for (let population of this.populations) { 79 | this.preparePopulation(operator, population) 80 | } 81 | } 82 | 83 | public preparePopulation(operator:Operator, population:Population) { 84 | var defs = operator.getFieldDefinition(); 85 | if (defs) { 86 | for (let fieldDef of defs) { 87 | population.fields.push(fieldDef.name); 88 | if (!(fieldDef instanceof OutputField)) { 89 | population.inputFields.push(fieldDef.name); 90 | } 91 | 92 | for (let individual of population.individuals) { 93 | individual.registerField(fieldDef); 94 | } 95 | } 96 | } 97 | 98 | if (operator.operators !== undefined) { 99 | for (let op of operator.operators) { 100 | this.preparePopulation(op, population); 101 | } 102 | } 103 | } 104 | 105 | public requestIndividual():Individual { 106 | let individual:Individual = new Individual(); 107 | this.registerOperator(this.rootOperator, individual); 108 | this.currentPopulation.individuals.unshift(individual); 109 | return individual; 110 | } 111 | 112 | 113 | public resetPopulations():void { 114 | this.populations = []; 115 | } 116 | 117 | public tick():void { 118 | for (let pop of this.populations) { 119 | this.currentPopulation = pop; 120 | this.rootOperator.doExecute(undefined, pop); 121 | } 122 | 123 | } 124 | 125 | public start(numTicks?:number):void { 126 | 127 | if (this.interval) { 128 | return; 129 | } 130 | 131 | var ticks = 0; 132 | var that = this; 133 | this.interval = setInterval(function () { 134 | ticks++; 135 | that.tick(); 136 | if (numTicks && ticks > numTicks) { 137 | that.stop(); 138 | } 139 | }, this.delay); 140 | } 141 | 142 | public stop():void { 143 | if (this.interval) { 144 | clearInterval(this.interval); 145 | this.interval = undefined; 146 | } 147 | } 148 | 149 | //public getOperator(name:string):Operator { 150 | // var op:Operator; 151 | // import TestAction = require('./action/TestAction'); 152 | // var myAction = new TestAction(); 153 | // return op; 154 | //} 155 | 156 | public registerOperator(operator:Operator, individual:Individual) { 157 | var defs = operator.getFieldDefinition(); 158 | if (defs) { 159 | for (let fieldDef of defs) { 160 | individual.registerField(fieldDef); 161 | } 162 | } 163 | 164 | if (operator.operators) { 165 | for (let op of operator.operators) { 166 | this.registerOperator(op, individual); 167 | } 168 | } 169 | } 170 | 171 | public newJSOperator(name:string, callback:Function, isIndividual:boolean, fieldsDef?:Array) { 172 | console.log(name); 173 | return new JSOperator(name, callback, isIndividual, fieldsDef); 174 | } 175 | 176 | 177 | public listOperators():Array { 178 | var local:Array = []; 179 | 180 | if (window && window['ops']) { 181 | for (let op in window['ops']) { 182 | local.push(op); 183 | } 184 | } 185 | 186 | return local; 187 | } 188 | 189 | public describeOperator(name:string):string { 190 | 191 | var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; 192 | var ARGUMENT_NAMES = /([^\s,]+)/g; 193 | 194 | function getParamNames(func) { 195 | var fnStr = func.toString().replace(STRIP_COMMENTS, ''); 196 | var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES); 197 | if (result === null) 198 | result = []; 199 | return result; 200 | } 201 | 202 | var desc:string; 203 | 204 | if (window && window['ops']) { 205 | var operator = window['ops'][name]; 206 | 207 | if (!operator) { 208 | Log.error("Unable to find operator: " + name + ". Check if it has @Register as class decorator or use application.listOperators() to check the list of all operators"); 209 | } else { 210 | var funcStr = getParamNames(operator.func); 211 | console.log("Constructor parameters: ", funcStr); 212 | 213 | var instance = new operator.func(); 214 | for (let field of instance.getFieldDefinition()) { 215 | console.log(field.describe()); 216 | } 217 | 218 | 219 | } 220 | } else { 221 | Log.error("Window object not found or no operators are registered"); 222 | } 223 | 224 | return desc; 225 | } 226 | 227 | public createOperator(name:string, args:Array):Operator { 228 | var op:Operator; 229 | 230 | if (window && window['ops']) { 231 | 232 | var operator = window['ops'][name]; 233 | 234 | if (!operator) { 235 | Log.error("Unable to find operator: " + name + ". Check if it has @Register as class decorator or use application.listOperators() to check the list of all operators"); 236 | } else { 237 | var inst = Object.create(operator.func.prototype); 238 | operator.func.apply(inst, args); 239 | op = inst; 240 | } 241 | } else { 242 | Log.error("Window object not found or no operators are registered"); 243 | } 244 | 245 | return op; 246 | } 247 | 248 | initializeHud() { 249 | new GuiHud(this, this.populationSize); 250 | } 251 | 252 | 253 | } 254 | -------------------------------------------------------------------------------- /src/models/fields/CSSField.ts: -------------------------------------------------------------------------------- 1 | import {FieldDef} from "../FieldDef"; 2 | import {Num} from "../../Num"; 3 | 4 | /** 5 | * Fetch data 6 | * 7 | * 8 | //http://cssvalues.com/ 9 | var cssValues = {}; 10 | [].slice.call( 11 | document.getElementsByTagName('section')).forEach( 12 | (elem) => { 13 | var id = elem.id; 14 | var values = []; 15 | cssValues[id] = values; 16 | [].slice.call(elem.getElementsByTagName('code')).forEach((codeElem) => { 17 | console.log(codeElem); 18 | values.push(codeElem.innerText); 19 | }) 20 | } 21 | ) 22 | console.log(JSON.stringify(cssValues)); 23 | * 24 | */ 25 | export class CSSField extends FieldDef { 26 | 27 | tags:Array; 28 | 29 | constructor(id:string, name:string) { 30 | super(name); 31 | this.tags = CSSField.getTagsList(id); 32 | } 33 | 34 | getInitialValue():any { 35 | var val:Array = [], 36 | half = this.tags.length / 2; 37 | 38 | for (let i = 0; i < half + Num.randomInt(0, half); i++) { 39 | val.push(CSSField.buildSingleCSS(this.tags, 5, 30)); 40 | } 41 | 42 | return val; 43 | } 44 | 45 | static getFullCSSString(obj:Array):string { 46 | var value = ""; 47 | 48 | obj.forEach(def => { 49 | value += def.sel + "{"; 50 | def.def.forEach(props => { 51 | value += props.property + ":" + props.value + ";"; 52 | }); 53 | value += "}" 54 | }); 55 | 56 | return value; 57 | } 58 | 59 | 60 | static buildSingleCSS(selectors:Array, minRules:number = 1, maxRules:number = 10):string { 61 | var css:any = {}; 62 | var selector:string = Num.randomArrPicker(selectors); 63 | css.sel = selector; 64 | css.def = []; 65 | 66 | for (let i:number = 0; i < Math.round(Math.random() * (maxRules - minRules)) + 1; i++) { 67 | css.def.push(CSSField.getSingleCSSRule()); 68 | } 69 | 70 | return css; 71 | } 72 | 73 | static getSingleCSSRule():any { 74 | var keys = Object.keys(CSSField.CSSOptions); 75 | var cssRule:any = {}; 76 | var selectedKey = Num.randomArrPicker(keys); 77 | var tempValue:string = Num.randomArrPicker(CSSField.CSSOptions[selectedKey]); 78 | cssRule.property = selectedKey; 79 | cssRule.value = CSSField.parseValue(tempValue); 80 | return cssRule; 81 | } 82 | 83 | static getCSSValueForProp(prop:string):string { 84 | return CSSField.parseValue(CSSField.CSSOptions[prop]); 85 | } 86 | 87 | 88 | static parseValue(value:String):string { 89 | var final = "", 90 | isTag = false, 91 | lastIndex = 0; 92 | 93 | for (let i = 0; i < value.length; i++) { 94 | if (value[i] === "<" || value[i] === "[") { 95 | lastIndex = i + 1; 96 | isTag = true; 97 | } else if (value[i] === ">") { 98 | isTag = false; 99 | var tagName = value.substr(lastIndex, i - lastIndex); 100 | switch (tagName) { 101 | case "number": 102 | final += Num.randomInt(0, 100); 103 | break; 104 | case "percentage": 105 | final += Num.randomInt(0, 100) + "%"; 106 | break; 107 | case "length": 108 | case "integer": 109 | final += Num.randomInt(-1028, 1028) + "px"; 110 | break; 111 | case "time": 112 | final += Num.roundToPrecision(Num.getRandomNum(0, 2), 2) + "s"; 113 | break; 114 | case "currentColor": 115 | case 'color': 116 | final += "rgba(" 117 | + Num.randomInt(0, 255) + "," 118 | + Num.randomInt(0, 255) + "," 119 | + Num.randomInt(0, 255) + "," 120 | + Num.roundToPrecision(Num.getRandomNum(0, 1), 2) + ")"; 121 | break; 122 | default: 123 | console.log(tagName); 124 | 125 | } 126 | } else if (value[i] === "]") { 127 | isTag = false; 128 | var tagName = value.substr(lastIndex, i - lastIndex); 129 | //console.log(tagName); 130 | var parse = Num.randomArrPicker(CSSField.CSSOptions[tagName]); 131 | //console.log(parse); 132 | final += CSSField.parseValue(parse); 133 | } else if (!isTag) { 134 | final += value[i]; 135 | } 136 | 137 | 138 | } 139 | 140 | return final; 141 | } 142 | 143 | 144 | static getTagsList(id):Array { 145 | var tags:Set = new Set([]); 146 | this.iterateElement(document.getElementById(id), "#" + id, tags); 147 | return Array.from(tags); 148 | } 149 | 150 | static iterateElement(element:HTMLElement, ancestry:string, tags:Set) { 151 | [].slice.call(element.children).forEach((elem:HTMLElement) => { 152 | var tagName = elem.tagName.toLowerCase(); 153 | tags.add(ancestry + " " + tagName); 154 | 155 | if (elem.classList.length > 0) { 156 | [].slice.call(elem.classList).forEach(name => { 157 | tags.add(ancestry + " " + tagName + "." + name); 158 | tags.add(ancestry + " ." + name); 159 | }) 160 | } 161 | 162 | this.iterateElement(elem, ancestry, tags); 163 | }) 164 | } 165 | 166 | 167 | static CSSOptions:any = { 168 | "all": ["initial", "unset", "inherit"], 169 | "animation": ["[animation-name] [animation-duration] [animation-timing-function] [animation-delay] [animation-iteration-count] [animation-direction] [animation-fill-mode] [animation-play-state]"], 170 | "animation-delay": ["0s", "