Skip to content

Commit

Permalink
add alternative reduction execution order
Browse files Browse the repository at this point in the history
  • Loading branch information
mandoway committed Sep 21, 2024
1 parent 990eaa9 commit f27b0da
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 30 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ SeRu currently supports:
2. `-t <test>` _Required_
Test script checking if the reduced file still kept the required property
A test script **must return 0** when the property was kept and 1 (or any code) if the property was lost
3. `-l <language>`
Programming language of the input file. Will be inferred from the file extension if omitted.
4. `-s`
Use strategy isolation.
This mode will apply only one semantic strategy and try to reduce all returned candidates using the syntactic reducer.
Default mode: strategy combination
In strategy combination, all strategies are applied and combined to one "best candidate". Then this one candidate will be reduced by the syntactic reducer.

# Future plans

Expand Down
43 changes: 24 additions & 19 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,32 @@ import (
"os"
)

var (
inputFile, testScript, givenLanguage string
rootCmd = &cobra.Command{
Use: "seru",
Short: "A tool to reduce a program while maintaining a property",
// TODO
Long: `TODO`,
Run: func(cmd *cobra.Command, args []string) {
err := reduction.StartReductionProcess(inputFile, testScript, givenLanguage)
if err != nil {
log.Fatal(err)
}
},
Version: version.Version,
}
)
type Flags struct {
InputFile, TestScript, GivenLanguage string
UseStrategyIsolation bool
}

var flags Flags

var rootCmd = &cobra.Command{
Use: "seru",
Short: "A tool to reduce a program while maintaining a property",
// TODO
Long: `TODO`,
Run: func(cmd *cobra.Command, args []string) {
err := reduction.StartReductionProcess(flags.InputFile, flags.TestScript, flags.GivenLanguage, flags.UseStrategyIsolation)
if err != nil {
log.Fatal(err)
}
},
Version: version.Version,
}

func init() {
rootCmd.PersistentFlags().StringVarP(&inputFile, "input", "i", "", "-i <path to file>")
rootCmd.PersistentFlags().StringVarP(&testScript, "test", "t", "", "-i <path to testscript>")
rootCmd.PersistentFlags().StringVarP(&givenLanguage, "lang", "l", "", "-l <language of file>")
rootCmd.PersistentFlags().StringVarP(&flags.InputFile, "input", "i", "", "-i <path to file>")
rootCmd.PersistentFlags().StringVarP(&flags.TestScript, "test", "t", "", "-i <path to testscript>")
rootCmd.PersistentFlags().StringVarP(&flags.GivenLanguage, "lang", "l", "", "-l <language of file>")
rootCmd.PersistentFlags().BoolVarP(&flags.UseStrategyIsolation, "strategy-isolation", "s", false, "")

_ = rootCmd.MarkPersistentFlagRequired("input")
_ = rootCmd.MarkPersistentFlagRequired("test")
Expand Down
11 changes: 11 additions & 0 deletions collection/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package collection

func FilterSlice[T any](slice []T, predicate func(it T) bool) []T {
var result []T
for _, v := range slice {
if predicate(v) {
result = append(result, v)
}
}
return result
}
14 changes: 13 additions & 1 deletion reduction/candidate/util.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
package candidate

import "slices"
import (
"github.com/mandoway/seru/collection"
"slices"
)

func MinCandidate(candidates []CandidateWithSize) CandidateWithSize {
return slices.MinFunc(candidates, func(a, b CandidateWithSize) int {
return b.Size - a.Size
})
}

func MinCandidateP(candidates []*CandidateWithSize) *CandidateWithSize {
nonNilCandidates := collection.FilterSlice(candidates, func(it *CandidateWithSize) bool {
return it != nil
})
return slices.MinFunc(nonNilCandidates, func(a, b *CandidateWithSize) int {
return b.Size - a.Size
})
}
23 changes: 23 additions & 0 deletions reduction/context/alg_context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package context

type SemanticApplicationMethod string

const (
ApplyFirstOnly SemanticApplicationMethod = "ApplyFirstOnly"
ApplyAllCombined SemanticApplicationMethod = "ApplyAllCombined"
)

type AlgorithmContext struct {
applicationMethod SemanticApplicationMethod
}

func NewAlgorithmContext(useIsolation bool) *AlgorithmContext {
var application SemanticApplicationMethod
if useIsolation {
application = ApplyFirstOnly
} else {
application = ApplyAllCombined
}

return &AlgorithmContext{applicationMethod: application}
}
38 changes: 37 additions & 1 deletion reduction/context/run_context.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package context

import (
"errors"
"fmt"
"github.com/mandoway/seru/files"
"github.com/mandoway/seru/reduction/candidate"
Expand All @@ -27,6 +28,9 @@ type RunContext struct {
currentSemanticStrategy int
semanticStrategiesTotal int

algorithmContext AlgorithmContext
forceExhaustedSemanticStrategies bool

bestResult *candidate.CandidateWithSize

countTokens plugin.TokenCountFunction
Expand Down Expand Up @@ -56,6 +60,28 @@ func (ctx *RunContext) SemanticStrategiesTotal() int {
return ctx.semanticStrategiesTotal
}

func (ctx *RunContext) SemanticApplicationMethod() SemanticApplicationMethod {
return ctx.algorithmContext.applicationMethod
}

func (ctx *RunContext) SetExhaustedSemanticStrategies() {
if ctx.algorithmContext.applicationMethod == ApplyFirstOnly {
panic("Forcing strategy exhaustion is only supported with combined strategies")
}
ctx.forceExhaustedSemanticStrategies = true
}

func (ctx *RunContext) ExhaustedSemanticStrategies() bool {
switch ctx.algorithmContext.applicationMethod {
case ApplyFirstOnly:
return ctx.currentSemanticStrategy >= ctx.semanticStrategiesTotal
case ApplyAllCombined:
return ctx.forceExhaustedSemanticStrategies
}

panic(fmt.Sprintf("unknown algorithm method: %s", ctx.algorithmContext.applicationMethod))
}

func (ctx *RunContext) CountTokens(bytes []byte) int {
return ctx.countTokens(bytes)
}
Expand All @@ -68,6 +94,14 @@ func (ctx *RunContext) SemanticReduce(bytes []byte) ([][]byte, error) {
return ctx.semanticReducer(bytes, ctx.currentSemanticStrategy)
}

func (ctx *RunContext) SemanticReduceWithStrategy(bytes []byte, strategyIndex int) ([][]byte, error) {
if strategyIndex >= ctx.semanticStrategiesTotal {
return [][]byte{}, errors.New(fmt.Sprintf("no strategy available at index %d", strategyIndex))
}

return ctx.semanticReducer(bytes, strategyIndex)
}

func (ctx *RunContext) Language() string {
return ctx.language
}
Expand Down Expand Up @@ -123,7 +157,7 @@ func (ctx *RunContext) saveCurrent() error {
return nil
}

func NewRunContext(givenLanguage, inputFilePath, testScriptPath string) (*RunContext, error) {
func NewRunContext(givenLanguage, inputFilePath, testScriptPath string, algoContext AlgorithmContext) (*RunContext, error) {
// Copy input files
reductionDir := fmt.Sprintf("%s%s", RunContextFolderPrefix, time.Now().Format(time.RFC3339))
err := os.Mkdir(reductionDir, 0750)
Expand Down Expand Up @@ -181,6 +215,8 @@ func NewRunContext(givenLanguage, inputFilePath, testScriptPath string) (*RunCon
semanticStrategiesTotal: semanticStrategiesSize,
currentSemanticStrategy: 0,

algorithmContext: algoContext,

bestResult: bestCandidate,

syntacticReducer: syntacticFunctions,
Expand Down
6 changes: 4 additions & 2 deletions reduction/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import (
"log"
)

func StartReductionProcess(inputFile, testScript, givenLanguage string) error {
func StartReductionProcess(inputFile, testScript, givenLanguage string, isolation bool) error {
log.Println("SeRu - Syntactic & Semantic Reduction")
log.Println()
log.Printf("Creating new run context with (input=%s, test=%s, lang=%s)\n", inputFile, testScript, givenLanguage)
algorithmContext := context.NewAlgorithmContext(isolation)
log.Printf("Running algorithm with context %v\n", algorithmContext)

runCtx, err := context.NewRunContext(givenLanguage, inputFile, testScript)
runCtx, err := context.NewRunContext(givenLanguage, inputFile, testScript, *algorithmContext)
if err != nil {
return err
}
Expand Down
66 changes: 59 additions & 7 deletions reduction/reduction_loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ func RunMainReductionLoop(ctx *context.RunContext) error {
// TODO wrap errors

candidates := []*candidate.CandidateWithSize{ctx.BestResult()}
for ctx.CurrentSemanticStrategy() < ctx.SemanticStrategiesTotal() {
for !ctx.ExhaustedSemanticStrategies() {

err := reduceSyntacticallyAndSaveResultIfBetter(ctx, candidates)
if err != nil {
return err
}

// TODO apply all semantic strategies and combine the results before feeding the output to the syntactic reducer
// just keep the best result per strategy as it will be processed again and there is a chance to modify the next instance in the next iteration
candidates, err = getCandidatesFromSemanticReduction(ctx)
if err != nil {
return err
Expand Down Expand Up @@ -88,7 +86,7 @@ func reduceSyntacticallyAndSaveResultIfBetter(ctx *context.RunContext, reduction
}

func getCandidatesFromSemanticReduction(ctx *context.RunContext) ([]*candidate.CandidateWithSize, error) {
if ctx.CurrentSemanticStrategy() >= ctx.SemanticStrategiesTotal() {
if ctx.ExhaustedSemanticStrategies() {
logging.LogSemantic("Exhausted all semantic strategies, aborting")
return nil, nil
}
Expand All @@ -99,7 +97,17 @@ func getCandidatesFromSemanticReduction(ctx *context.RunContext) ([]*candidate.C
return nil, err
}

validCandidates, err := trySemanticStrategiesToFindValidCandidates(ctx, currentBytes)
var validCandidates []*candidate.CandidateWithSize
switch ctx.SemanticApplicationMethod() {
case context.ApplyAllCombined:
validCandidates, err = applySemanticStrategiesCombined(ctx, currentBytes)
break
case context.ApplyFirstOnly:
validCandidates, err = applyFirstSemanticStrategy(ctx, currentBytes)
break
default:
panic("Unknown application method")
}
if err != nil {
return nil, err
}
Expand All @@ -111,9 +119,10 @@ func getCandidatesFromSemanticReduction(ctx *context.RunContext) ([]*candidate.C
return validCandidates, nil
}

func trySemanticStrategiesToFindValidCandidates(ctx *context.RunContext, currentBytes []byte) ([]*candidate.CandidateWithSize, error) {
func applyFirstSemanticStrategy(ctx *context.RunContext, currentBytes []byte) ([]*candidate.CandidateWithSize, error) {
logging.LogSemantic("Trying strategies one by one")
var validCandidates []*candidate.CandidateWithSize
for len(validCandidates) == 0 && ctx.CurrentSemanticStrategy() < ctx.SemanticStrategiesTotal() {
for len(validCandidates) == 0 && !ctx.ExhaustedSemanticStrategies() {
logging.LogSemantic("Trying strategy", ctx.CurrentSemanticStrategy()+1, "of", ctx.SemanticStrategiesTotal())
candidates, err := ctx.SemanticReduce(currentBytes)
if err != nil {
Expand All @@ -132,3 +141,46 @@ func trySemanticStrategiesToFindValidCandidates(ctx *context.RunContext, current
}
return validCandidates, nil
}

func applySemanticStrategiesCombined(ctx *context.RunContext, currentBytes []byte) ([]*candidate.CandidateWithSize, error) {
logging.LogSemantic("Trying strategies and combine results")
var bestCandidate *candidate.CandidateWithSize
currentStrategy := 0

for currentStrategy < ctx.SemanticStrategiesTotal() {
logging.LogSemantic("Trying strategy", currentStrategy+1, "of", ctx.SemanticStrategiesTotal())
var bytesToReduce []byte
var err error
if bestCandidate == nil {
bytesToReduce = currentBytes
} else {
bytesToReduce, err = os.ReadFile(bestCandidate.InputPath)
if err != nil {
return nil, err
}
}
candidates, err := ctx.SemanticReduceWithStrategy(bytesToReduce, currentStrategy)
if err != nil {
return nil, err
}
logging.LogSemantic("Found candidates:", len(candidates))

validCandidates := persistance.CheckAndKeepValidCandidates(candidates, ctx)

if len(validCandidates) > 0 {
logging.LogSemantic("Valid candidates:", len(validCandidates))
logging.LogSemantic("Setting minimum as new intermediate best")
bestCandidate = candidate.MinCandidateP(validCandidates)
} else {
logging.LogSemantic("No valid candidates left after check, try next strategy")
}
currentStrategy++
}

if bestCandidate == nil {
ctx.SetExhaustedSemanticStrategies()
return []*candidate.CandidateWithSize{}, nil
}

return []*candidate.CandidateWithSize{bestCandidate}, nil
}

0 comments on commit f27b0da

Please sign in to comment.