UNPKG

@specs-feup/kadabra

Version:

A Java source-to-source compiler written in Typescript

337 lines (301 loc) 9.02 kB
import { Field } from "../../Joinpoints.js"; import { primitive2Class } from "../Utils.js"; /** * Class defining the structure of an autotuner */ export class Configs { /////////////////////////////////////////////////// //////////////// USEFUL CONSTANTS ///////////////// /////////////////////////////////////////////////// public static readonly PACKAGE = "autotuner.configs."; public static readonly FACTORY = Configs.PACKAGE + "autotuner.configs."; public static readonly RANGED = Configs.PACKAGE + "knobs.number.ranged."; public static readonly Tuple = "tdrc.tuple.Tuple"; public static readonly order = { normal: Configs.FACTORY + "::normal", random: Configs.FACTORY + "::random", }; /** A list of values to test in the order they are given. **/ static default(knobs: Field | Field[], values: string[], type: string) { return list2Config(knobs, "normal", values, false, type); } /** A list of values to test in random order. **/ static random(knobs: Field | Field[], values: string[], type: string) { return list2Config(knobs, "random", values, false, type); } static combine(knobs: Field | Field[], values: string[][], type: string) { if (!Array.isArray(values) || values.length < 2) { throw new Error( "At least two values must be provided to combine values" ); } const args = values.map((arg) => { return "java.util.Arrays.asList(" + arg.join(",") + ")"; }); return new Configuration( knobs, Configs.FACTORY + ".normal(tdrc.utils.ListUtils.createTuplesFromList(" + args.join(",") + "))", type ); } /* This configuration uses Ranged knobs, which are defined in the lower part of this file (e.g. IntegerRange) */ static randomOf( knobs: Field | Field[], ranges: PrimitiveRange | PrimitiveRange[] ) { return rangedConfig(knobs, ranges, "random", true); } static range( knobs: Field | Field[], ranges: PrimitiveRange | PrimitiveRange[] ) { return rangedConfig(knobs, ranges, "range", true); } static around( knobs: Field | Field[], ranges: PrimitiveRange | PrimitiveRange[] ) { return rangedConfig(knobs, ranges, "around", true); } static linear( knobs: Field | Field[], ranges: PrimitiveRange | PrimitiveRange[] ) { return rangedConfig(knobs, ranges, "linear", true); } static custom(knobs: Field | Field[], configCode: string, type: string) { return new Configuration(knobs, configCode, type); } } export class Configuration { applier: string; configCode: string; type: string; constructor(knobs: Field | Field[], configCode: string, type: string) { if (Array.isArray(knobs)) { let applierCode = "(knobs) -> {\n"; for (let i = 0; i < knobs.length; i++) { const k = knobs[i]; applierCode += "\t" + k.staticAccess + " = (" + k.type + ")knobs.get(" + i + ");\n"; } this.applier = applierCode + "}"; } else { this.applier = `(knob)-> ${knobs.staticAccess} = knob`; } this.type = type; this.configCode = configCode; } declare(name: string) { return ( Configs.PACKAGE + "Configuration<" + this.type + "> " + name + " = " + this.get() ); } declareProvider(name: string) { return ( "java.util.function.Supplier< " + Configs.PACKAGE + "Configuration<" + this.type + ">> " + name + " = " + this.provider() ); } provider() { return "()-> " + this.configCode; } get() { return this.configCode; } toString() { return this.get(); } } ////////////////////////////////////////////////////////////// //////////////// GENERIC CONFIGS USING LISTS ///////////////// ////////////////////////////////////////////////////////////// function list2Config( knobs: Field | Field[], constructor: string, values: string[], acceptsSingle: boolean, type: string ) { if (values.length == 0) { throw new Error( "At least one value must be provided to create a " + constructor + " configuration" ); } if (values.length == 1 && acceptsSingle) { return new Configuration( knobs, Configs.FACTORY + "." + constructor + "(" + values.join(",") + ")", type ); } return new Configuration( knobs, Configs.FACTORY + "." + constructor + "(" + values.join(",") + ")", type ); } /////////////////////////////////////////////////////// //////////////// CONFIGS USING RANGES ///////////////// /////////////////////////////////////////////////////// export function rangedConfig( knobs: Field | Field[], ranges: PrimitiveRange | PrimitiveRange[], type: string, acceptsSingle: boolean ) { let args; if (Array.isArray(ranges)) { if (ranges.length == 0) { throw new Error( "At least one value must be provided to create a(n) " + type + " configuration" ); } args = ranges.map(function (range) { return range.instance(); }); } else { args = [ranges.instance()]; } return list2Config(knobs, "", args, acceptsSingle, type); } /* Ranged knob is used in the configurations, use this if you intend to create knobs containing multiple parameters (one RangedKnob per knob) and combine them in a configuration */ export class PrimitiveRange { type: string; lowerLimit: number; upperLimit: number; step: number | undefined; value: number | undefined; descend: string | undefined; ascend: string | undefined; constructor( type: string, lowerLimit: number, upperLimit: number, step: number = 1, value?: number ) { this.type = primitive2Class(type); this.lowerLimit = lowerLimit; this.upperLimit = upperLimit; this.step = step; this.value = value; this.descend = undefined; this.ascend = undefined; } setClimbers(descend: string, ascend: string) { this.descend = descend; this.ascend = ascend; this.step = undefined; return this; } initValue(value: number) { this.value = value; return this; } toConfig() { const instanceCode = this.instance(); return Configs.FACTORY + ".range(" + instanceCode + ")"; } instance() { if (this.ascend != undefined) { let newKnob = "new " + Configs.RANGED + "CustomStep" + this.type + "Knob(" + this.lowerLimit + "," + this.upperLimit + ","; if (this.value !== undefined) { newKnob += this.value + ","; } if (this.descend === undefined) { throw new Error("Expected descend to be defined."); } return newKnob + this.descend + "," + this.ascend + ")"; } let newKnob = "new " + Configs.RANGED + this.type + "Step(" + this.lowerLimit + "," + this.upperLimit + ","; if (this.value !== undefined) { newKnob += this.value + ","; } if (this.step === undefined) { throw new Error("Expected step to be defined."); } return newKnob + this.step + ")"; } declare(name: string) { return ( Configs.RANGED + "RangedKnob<" + this.type + ">" + name + " = " + this.instance() ); } } export class IntegerRange extends PrimitiveRange { constructor( lowerLimit: number, upperLimit: number, step: number, value: number ) { super("Integer", lowerLimit, upperLimit, step, value); } } export class FloatRange extends PrimitiveRange { constructor( lowerLimit: number, upperLimit: number, step: number, value: number ) { super("Float", lowerLimit, upperLimit, step, value); } }