From fb0c7a9aa20e5c46a4938995e009930c06193e0f Mon Sep 17 00:00:00 2001 From: Tom Gordon Date: Wed, 12 Jul 2017 15:07:26 +0200 Subject: [PATCH] fixed a new bug in the SWI Prolog implementation of the rule engine; added a maximum number of rule applications to assure termination, no matter which CHR implementation of CHR is used. --- src/engine/caes/caes.go | 2 +- src/engine/caes/inference.go | 5 ++++- src/engine/caes/swichr.go | 30 ++++++++++++++++++++++++----- src/engine/caes/swiplcmd_darwin.go | 1 - src/engine/caes/swiplcmd_linux.go | 1 - src/engine/caes/swiplcmd_windows.go | 1 - 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/engine/caes/caes.go b/src/engine/caes/caes.go index 6b55b07..c7c00bc 100644 --- a/src/engine/caes/caes.go +++ b/src/engine/caes/caes.go @@ -125,7 +125,7 @@ type Statement struct { type Rulebase interface { // Infer returns true if the goals succeed and false if they fail. // The list of strings returned represents the constraint store - Infer(goals []string) (bool, []string, error) + Infer(goals []string, max int) (bool, []string, error) // AddRule adds a constraint handling rule to the rulebase AddRule(name string, keep []string, delete []string, guard []string, body []string) error } diff --git a/src/engine/caes/inference.go b/src/engine/caes/inference.go index 22c22f8..542ad0f 100644 --- a/src/engine/caes/inference.go +++ b/src/engine/caes/inference.go @@ -16,6 +16,9 @@ import ( "github.com/carneades/carneades-4/src/engine/terms" ) +// maximum number of rule (scheme) applications when deriving arguments +const MAXRULEAPPS = 100000 + // ArgDesc: Structure describing an argument instantiating an // argument scheme. Represented in Prolog as argument(Scheme,Values) type ArgDesc struct { @@ -91,7 +94,7 @@ func (ag *ArgGraph) Infer() error { goals = append(goals, k) } - success, store, err := rb.Infer(goals) + success, store, err := rb.Infer(goals, MAXRULEAPPS) if err != nil { return err } diff --git a/src/engine/caes/swichr.go b/src/engine/caes/swichr.go index 77de6e8..94b1fd2 100644 --- a/src/engine/caes/swichr.go +++ b/src/engine/caes/swichr.go @@ -15,6 +15,7 @@ import ( "fmt" "io/ioutil" // "log" + "math" "os" "strings" "time" @@ -160,7 +161,11 @@ func writeCHR(t *SWIRulebase, goals []string, f *os.File) error { n := len(goals) for i := 0; i < n; i++ { _, err = f.WriteString(" " + goals[i]) - _, err = f.WriteString(".\n\n") + if i < n-1 { + _, err = f.WriteString(",\n") + } else { + _, err = f.WriteString(".\n\n") + } } if err != nil { @@ -170,11 +175,26 @@ func writeCHR(t *SWIRulebase, goals []string, f *os.File) error { } } -// Infer: Apply an SWIRulebase to a list of goals. Return true -// if the goals are successfully solved, and false if the goals fail. +// Infer: Apply an SWIRulebase to a list of goals. The +// maximum amount of time alloted the SWI Prolog process depends +// on the maximum number of rules (schemes) which may be applied, max. +// Return true if the goals are successfully solved, and false if the goals fail. // Return a list of the terms in the constraint store, if the goals // succeeded. Returns an error if the rulebase could not be applied to the goals. -func (rb *SWIRulebase) Infer(goals []string) (bool, []string, error) { +func (rb *SWIRulebase) Infer(goals []string, max int) (bool, []string, error) { + + // Assume that a rule or scheme application takes about 0.00015 seconds. + // If there is no limit to the number of rule applications (i.e. max==0), + // then limit the maximum amount of time alloted to the SWI Prolog to 15 seconds + // anyway, to assure termination. Otherwise limit the time to + // max * 0.00015 seconds, rounded up to the next second. + + secsPerRule := 0.00015 + timeLimit := 15 // seconds + if max > 0 { + timeLimit = int(math.Ceil(float64(max) * secsPerRule)) + } + f, err := ioutil.TempFile(os.TempDir(), "swirulebase") if err != nil { return false, nil, err @@ -207,7 +227,7 @@ func (rb *SWIRulebase) Infer(goals []string) (bool, []string, error) { done <- cmd.Wait() }() finished := false - timer := time.After(timeLimit * time.Second) + timer := time.After(time.Duration(timeLimit) * time.Second) scanner := bufio.NewScanner(stdout) store := []string{} diff --git a/src/engine/caes/swiplcmd_darwin.go b/src/engine/caes/swiplcmd_darwin.go index 76d95d6..2a7bef2 100644 --- a/src/engine/caes/swiplcmd_darwin.go +++ b/src/engine/caes/swiplcmd_darwin.go @@ -16,7 +16,6 @@ import ( // resource limits for Prolog processes const ( - timeLimit = 15 // Seconds stackLimit = "256m" // MB ) diff --git a/src/engine/caes/swiplcmd_linux.go b/src/engine/caes/swiplcmd_linux.go index 76d95d6..2a7bef2 100644 --- a/src/engine/caes/swiplcmd_linux.go +++ b/src/engine/caes/swiplcmd_linux.go @@ -16,7 +16,6 @@ import ( // resource limits for Prolog processes const ( - timeLimit = 15 // Seconds stackLimit = "256m" // MB ) diff --git a/src/engine/caes/swiplcmd_windows.go b/src/engine/caes/swiplcmd_windows.go index 231ade9..9e979f8 100644 --- a/src/engine/caes/swiplcmd_windows.go +++ b/src/engine/caes/swiplcmd_windows.go @@ -16,7 +16,6 @@ import ( // resource limits for Prolog processes const ( - timeLimit = 15 // Seconds stackLimit = "256m" // MB )