9 changed files with 364 additions and 138 deletions
+5 -5
View File
@@ -3,16 +3,16 @@
:url "https://github.com/jarradh/narjure"
:license {:name "GNU General Public License 2.0"
:url "http://www.gnu.org/licenses/old-licenses/gpl-2.0.html"}
:dependencies [[org.clojure/clojure "1.7.0"]
:dependencies [[org.clojure/clojure "1.8.0"]
[org.clojure/core.logic "0.8.10"]
[instaparse "1.4.1"]
[com.rpl/specter "0.9.1"]
[org.clojure/tools.nrepl "0.2.12"]]
[org.clojure/tools.nrepl "0.2.12"]
[org.clojure/data.priority-map "0.0.7"]]
:main ^:skip-aot narjure.core
:plugins [[lein-cloverage "1.0.6"]
[cider/cider-nrepl "0.11.0-SNAPSHOT"]]
:target-path "target/%s"
:repl-options {:init-ns narjure.repl
:nrepl-middleware
[narjure.repl/narsese-handler]}
:repl-options {:init-ns narjure.repl
:nrepl-middleware [narjure.repl/narsese-handler]}
:profiles {:uberjar {:aot :all}})
+68 -67
View File
@@ -1,80 +1,81 @@
(* Narsese Grammar - https://github.com/opennars/opennars/wiki/Input-Output-Format *)
task ::= [budget] sentence (* task to be processed *)
task ::= [budget] sentence (* task to be processed *)
sentence ::= statement"." [tense] [truth] (* judgement to be remembered *)
| statement"?" [tense] [truth] (* question to be answered, tense added in OpenNARS 1.7 *)
| statement"@" [tense] [truth] (* question on desire value to be answered, tense added in OpenNARS 1.7 *)
| statement"!" [tense] [truth] (* goal to be realized, tense added in OpenNARS 1.7 *)
sentence ::= statement"." [tense] [truth] (* judgement to be remembered *)
| statement"?" [tense] [truth] (* question to be answered, tense added in OpenNARS 1.7 *)
| statement"@" [tense] [truth] (* question on desire value to be answered, tense added in OpenNARS 1.7 *)
| statement"!" [tense] [truth] (* goal to be realized, tense added in OpenNARS 1.7 *)
statement ::= <"<">term copula term<">"> (* two terms related to each other *)
| <"(">term copula term<")"> (* two terms related to each other, new notation *)
| term (* a term can name a statement *)
| "(^"word {","term} ")" (* an operation to be executed *)
| word"("term {","term} ")" (* an operation to be executed, new notation *)
statement ::= <"<">term copula term<">"> (* two terms related to each other *)
| <"(">term copula term<")"> (* two terms related to each other, new notation *)
| term (* a term can name a statement *)
| "(^"word {","term} ")" (* an operation to be executed *)
| word"("term {","term} ")" (* an operation to be executed, new notation *)
copula ::= "-->" (* inheritance *)
| "<->" (* similarity *)
| "{--" (* instance *)
| "--]" (* property *)
| "{-]" (* instance-property *)
| "==>" (* implication *)
| "=/>" (* predictive implication *)
| "=|>" (* concurrent implication *)
| "=\\>" (* =\> retrospective implication *)
| "<=>" (* equivalence *)
| "</>" (* predictive equivalence *)
| "<|>" (* concurrent equivalence *)
copula ::= "-->" (* inheritance *)
| "<->" (* similarity *)
| "{--" (* instance *)
| "--]" (* property *)
| "{-]" (* instance-property *)
| "==>" (* implication *)
| "=/>" (* predictive implication *)
| "=|>" (* concurrent implication *)
| "=\\>" (* =\> retrospective implication *)
| "<=>" (* equivalence *)
| "</>" (* predictive equivalence *)
| "<|>" (* concurrent equivalence *)
term ::= word (* an atomic constant term *)
| variable (* an atomic variable term *)
| compound-term (* a term with internal structure *)
| statement (* a statement can serve as a term *)
| interval (* time measure between events *)
term ::= word (* an atomic constant term *)
| variable (* an atomic variable term *)
| compound-term (* a term with internal structure *)
| statement (* a statement can serve as a term *)
| interval (* time measure between events *)
compound-term ::= "{" term {","term} "}" (* extensional set *)
| "[" term {","term} "]" (* intensional set *)
| "("op-multi","term{","term} ")" (* compound term with infix operator *)
| "("op-single","term"," term ")" (* compound term with infix operator *)
| "(" term {","term} ")" (* product, new notation *)
| "(/," term {","term} ")" (* extensional image *)
| "(\\," term {","term} ")" (* \ intensional image *)
| "(--," term ")" (* negation *)
| "--"term (* negation, new notation *)
compound-term ::= op-ext-set term {"," term} "}" (* extensional set *)
| op-int-set term {"," term} "]" (* intensional set *)
| "("op-multi"," term {"," term} ")" (* with prefix operator *)
| "("op-single"," term "," term ")" (* with prefix operator *)
| "(" term {op-multi term} ")" (* with infix operator *)
| "(" term op-single term ")" (* with infix operator *)
| "(" term {","term} ")" (* product, new notation *)
| "(" op-ext-image "," term {"," term} ")"(* special case, extensional image *)
| "(" op-int-image "," term {"," term} ")"(* special case, \ intensional image *)
| "(" op-negation "," term ")" (* negation *)
| op-negation term (* negation, new notation *)
(* new compound-term notation *)
op-int-set::= "[" (* extensional set *)
op-ext-set::= "{" (* intensional set *)
op-negation::= "--" (* negation *)
op-int-image::= "\\" (* \ intensional image *)
op-ext-image::= "/" (* extensional image *)
op-multi ::= "&&" (* conjunction *)
| "*" (* product *)
| "||" (* disjunction *)
| "&|" (* parallel events *)
| "&/" (* sequential events *)
| "|" (* intensional intersection *)
| "&" (* extensional intersection *)
op-single ::= "-" (* extensional difference *)
| "~" (* intensional difference *)
| "(" term {op-multi term} ")" (* compound term with infix operator *)
| "(" term op-single term ")" (* compound term with infix operator *)
variable ::= "$"word (* independent variable *)
| "#"word (* dependent variable *)
| "?"word (* query variable in question *)
op-multi ::= "&&" (* conjunction *)
| "*" (* product *)
| "||" (* disjunction *)
| "&|" (* parallel events *)
| "&/" (* sequential events *)
| "|" (* intensional intersection *)
| "&" (* extensional intersection *)
tense ::= ":/:" (* future event *)
| ":|:" (* present event *)
| ":\\:" (* :\: past event *)
| <":">#"\d+"<":"> (* defined event, output only *)
op-single ::= "-" (* extensional difference *)
| "~" (* intensional difference *)
interval ::= <"/">#"\d+" (* integer *)
variable ::= "$"word (* independent variable *)
| "#"[word] (* dependent variable *)
| "?"[word] (* query variable in question *)
truth ::= <"%">frequency[<";">confidence]<"%"> (* two numbers in [0,1]x(0,1) *)
budget ::= <"$">priority[<";">durability][<";">quality]<"$"> (* three numbers in [0,1]x(0,1)x[0,1] *)
tense ::= ":/:" (* future event *)
| ":|:" (* present event *)
| ":\\:" (* :\: past event *)
| <":">#"\d+"<":"> (* defined event, output only *)
interval ::= <"/">#"\d+" (* integer *)
truth ::= <"%">frequency[<";">confidence]<"%"> (* two numbers in [0,1]x(0,1) *)
budget ::= <"$">priority[<";">durability][<";">quality]<"$"> (* three numbers in [0,1]x(0,1)x[0,1] *)
word : #"\w+" (* unicode string *)
priority : #"([0]?\.[0-9]+|1|0)" (* 0 <= x <= 1 *)
durability : #"[0]?\.[0]*[1-9]{1}[0-9]*" (* 0 < x < 1 *)
quality : #"([0]?\.[0-9]+|1|0)" (* 0 <= x <= 1 *)
frequency : #"([0]?\.[0-9]+|1|0)" (* 0 <= x <= 1 *)
confidence : #"[0]?\.[0]*[1-9]{1}[0-9]*" (* 0 < x < 1 *)
word : #"\w+" (* unicode string *)
priority : #"([0]?\.[0-9]+|1\.[0]*|1|0)" (* 0 <= x <= 1 *)
durability : #"[0]?\.[0]*[1-9]{1}[0-9]*" (* 0 < x < 1 *)
quality : #"([0]?\.[0-9]+|1\.[0]*|1|0)" (* 0 <= x <= 1 *)
frequency : #"([0]?\.[0-9]+|1\.[0]*|1|0)" (* 0 <= x <= 1 *)
confidence : #"[0]?\.[0]*[1-9]{1}[0-9]*" (* 0 < x < 1 *)
+1 -1
View File
@@ -14,7 +14,7 @@
instance property ext-set int-set product negation
inst-prop ext-difference revision inference equivalence
inference2 inference3 equivalence-list reduceo replace-var
replace-all implication)
replace-all implication choice)
;===============================================================================
;revision
+45
View File
@@ -0,0 +1,45 @@
(ns narjure.bag
(:require [clojure.data.priority-map :refer [priority-map-keyfn-by]]))
;TODO take/get semantic and names to be decided
(defprotocol Bag
(put-el [this item])
(get-el [this] [this k])
(remove-el [this k])
(take-el [this] [this k])
(count-els [this]))
;TODO must be discussed
(defn randomize-priority [priority]
(* (rand) priority))
(defn assoc-to-bag
"Set some random priority for a new item and slice map."
[col {:keys [key priority] :as v} capacity]
(let [ncol (->> (randomize-priority priority)
(assoc v :rand-priority)
(assoc col key))]
(if (> (count ncol) capacity)
(let [[k] (last ncol)]
(dissoc ncol k))
ncol)))
(defrecord DefaultBag [capacity queue]
Bag
(put-el [_ item]
(DefaultBag. capacity (assoc-to-bag queue item capacity)))
(get-el [_] (second (peek queue)))
(get-el [_ key] (when-let [el (queue key)] el))
(remove-el [_ key] (DefaultBag. capacity (dissoc queue key)))
(take-el [bag]
(when-let [el (get-el bag)]
[el (remove-el bag (:key el))]))
(take-el [bag k]
(when-let [el (get-el bag k)]
[el (remove-el bag k)]))
(count-els [_] (count queue)))
(defn default-bag
([] (default-bag 100))
([capacity]
(DefaultBag. capacity (priority-map-keyfn-by :rand-priority >))))
+121
View File
@@ -0,0 +1,121 @@
(ns narjure.cycle
(:require [narjure.bag :refer :all]
[narjure.narsese :refer [parse]]))
(declare task->buffer)
;TODO create record for memory abstraction, but only after
;api will become more/less stable
(defn memory [buffer concepts]
{:concepts concepts
:cycles-cnt 0
:tasks-cnt 0
:buffer buffer})
(defn default-memory
([] (default-memory 100 100))
([buffer-capacity concepts-capacity]
(memory (default-bag buffer-capacity)
(default-bag concepts-capacity))))
(defn default-concept [term]
{:key term
:priority 1
:tasks (default-bag 100)
:beliefs (default-bag 100)})
(defn get-concept [concepts term]
(if-let [concept (get-el concepts term)]
concept
(default-concept term)))
(defn local-revision
[concept {:keys [statement] :as task}]
#_(if-let [old-beleife ()]
(if (overlaps?))))
;TODO local inefernce should be somewhere here
(defn task->concept
[task {:keys [concepts] :as m} term]
(->> task
(update (get-concept concepts term) :tasks put-el)
(update m :concepts put-el)))
(defn task->concepts
[m {:keys [terms] :as task}]
(reduce (partial task->concept task) m terms))
(def tasks-to-fetch 1)
(defn buffer->tasks
[{:keys [buffer] :as m}]
(let [[buffer tasks]
(reduce (fn [[buf tasks] _]
(let [[task buf] (take-el buf)]
[buf (conj tasks task)])) [buffer []]
(range tasks-to-fetch))]
(assoc m :buffer buffer
:tasks tasks)))
(defn filling-tasks
"1. Select tasks in the buffer to insert into the corresponding concepts,
which may include the creation of new concepts (I'm not sure about the rest)
and beliefs, as well as direct processing on the tasks."
;TODO Should revision/choice be implemented here?
[{:keys [tasks] :as m}]
(dissoc (reduce task->concepts m tasks) :tasks))
(defn do-inference [task beleife]
[])
(defn inference
"2. Select a concept from the memory, then select a task and a belief
from the concept.
3. Feed the task and the belief to the inference engine
to produce derived tasks."
[{:keys [concepts] :as m}]
(let [[{:keys [tasks beliefs] :as concept} concepts] (take-el concepts)
[task tasks] (take-el tasks)
[beleife beleifs] (take-el beliefs)]
(if (and task beleife)
(let [new-tasks (do-inference task beleife)
upd-beleifs (put-el beleifs beleife)
uod-concept (assoc concept :beliefs upd-beleifs)
upd-concepts (put-el concepts uod-concept)
upd-m (assoc m :tasks tasks)]
(-> (reduce task->buffer upd-m new-tasks)
(assoc :concepts upd-concepts)))
m)))
(defn do-cycle
"The cycle of NARS."
[memory]
(-> memory
(update :cycles-cnt inc)
buffer->tasks
filling-tasks
inference
;save-results
))
;TODO
(defn task-priority [_] 0.8)
(defn pack-task [task cycle n]
(merge task
{:key (hash task) ;TODO to be replaced
:priority (task-priority task)
:cycle cycle
:evidental-base [n]}))
(defn task->buffer [{:keys [cycles-cnt tasks-cnt] :as m} t]
(let [n-task (inc tasks-cnt)]
(assoc (->> (pack-task t cycles-cnt n-task)
(update m :buffer put-el))
:tasks-cnt n-task)))
(comment
(let [task (parse "<a --> b>.")]
(def t-m (task->buffer (default-memory) task)))
(do-cycle t-m))
+67 -45
View File
@@ -23,32 +23,21 @@
"<|>" 'concurrent-equivalence})
(def compound-terms
{"{" 'ext-set
"[" 'int-set
"(&," 'ext-intersection
"&" 'ext-intersection
"(|," 'int-intersection
"|" 'int-intersection
"(-," 'ext-difference
"-" 'ext-difference
"(~," 'int-difference
"~" 'int-difference
"(*," 'product
"*" 'product
"(" 'product
"(/," 'ext-image
"(\\," 'int-image
"(--," 'negation
"--" 'negation
"(||," 'disjunction
"||" 'disjunction
"(&&," 'conjunction
"&&" 'conjunction
"(&/," 'sequential-events
{"{" 'ext-set
"[" 'int-set
"&" 'ext-intersection
"|" 'int-intersection
"-" 'ext-difference
"~" 'int-difference
"*" 'product
"(" 'product
"/" 'ext-image
"\\" 'int-image
"--" 'negation
"||" 'disjunction
"&&" 'conjunction
"&/" 'sequential-events
"(&|," 'parallel-events
"&|" 'parallel-events
})
"&|" 'parallel-events})
(defn get-compound-term [[_ operator-srt]]
(compound-terms operator-srt))
@@ -59,6 +48,7 @@
(def ^:dynamic *action* (atom nil))
(def ^:dynamic *lvars* (atom []))
(def ^:dynamic *truth* (atom []))
(def ^:dynamic *budget* (atom []))
(defn keep-cat [fun col]
(into [] (comp (mapcat fun) (filter (complement nil?))) col))
@@ -74,7 +64,11 @@
(defmethod element :sentence [[_ & data]]
(let [filtered (group-by string? data)]
(reset! *action* (actions (first (filtered true))))
(keep element (filtered false))))
(let [cols (filtered false)
last-el (last cols)]
(when (= :truth (first last-el))
(element last-el))
(element (first cols)))))
(defmethod element :statement [[_ & data]]
(if-let [copula (get-copula data)]
@@ -84,18 +78,30 @@
(defmethod element :task [[_ & data]]
`[~@(keep-cat element data)])
(defmethod element :compound-term [[_ _ & data]]
(let [first-el-type (get-in (vec data) [0 0])
comp-operator ((if (= :term first-el-type) second first) data)]
;looks strange but it is because of special syntax for negation --bird.
(defn get-comp-operator [second-el data]
(let [first-el-type (get-in (vec data) [0 0])]
(if (some #{(first second-el)} [:op-negation :op-int-set :op-ext-set])
second-el
((if (= :term first-el-type) second first) data))))
(defmethod element :compound-term [[_ second-el & data]]
(let [comp-operator (get-comp-operator second-el data)]
`[~(get-compound-term comp-operator)
~@(keep-cat element (remove string? data))]))
(defmethod element :copula [_])
(defmethod element :op-multi [_])
(defmethod element :op-single [_])
(defmethod element :op-negation [_])
(defmethod element :op-ext-set [_])
(defmethod element :op-int-set [_])
(defmethod element :op-ext-image [_])
(defmethod element :op-int-image [_])
(defmethod element :variable [[_ _ [_ v]]]
(let [v (symbol v)]
(def var-prefixes {"#" "d_" "?" "q_"})
(defmethod element :variable [[_ type [_ v]]]
(let [v (symbol (str (var-prefixes type) v))]
(swap! *lvars* conj v)
v))
@@ -109,19 +115,32 @@
[[_ & data]]
`[[~@(mapv element data)]])
(defmethod element :frequency [[_ d]]
(let [d (Double/parseDouble d)]
(swap! *truth* conj d) d))
(defmethod element :confidence [[_ d]]
(let [d (Double/parseDouble d)]
(swap! *truth* conj d) d))
(defmethod element :priority [[_ d]] (Double/parseDouble d))
(defmethod element :durability [[_ d]] (Double/parseDouble d))
(defmacro double-element [n a]
`(defmethod element ~n [[t# d#]]
(let [d# (Double/parseDouble d#)]
(swap! ~a conj d#) d#)))
(double-element :frequency *truth*)
(double-element :confidence *truth*)
(double-element :priority *budget*)
(double-element :durability *budget*)
(double-element :quality *budget*)
(defmethod element :task [[_ & data]]
(when (= :budget (first (first data)))
(element (first data)))
(element (last data)))
(defmethod element :default [[_ & data]]
(when (seq? data)
(keep element data)))
;TODO check for variables in statemnts, ignore subterm if it contains variable
(defn terms
"Fetch terms from task."
[statement]
(into #{statement} (rest statement)))
(defn parse
"Parses a Narsese string into task ready for inference"
[narsese-str]
@@ -129,10 +148,13 @@
(if-not (i/failure? data)
(binding [*action* (atom nil)
*lvars* (atom [])
*truth* (atom [])]
(let [parsed-code (element data)]
{:action @*action*
:lvars @*lvars*
:truth @*truth*
:data parsed-code}))
*truth* (atom [])
*budget* (atom [])]
(let [statement (element data)]
{:action @*action*
:lvars @*lvars*
:truth @*truth*
:budget @*budget*
:statement statement
:terms (terms statement)}))
data)))
+19 -14
View File
@@ -4,6 +4,8 @@
[instaparse.core :as i]
[nal.core :as c]
[clojure.core.logic :as l]
[clojure.string :refer [trim]]
[clojure.pprint :as p]
[clojure.tools.nrepl.middleware :refer [set-descriptor!]]))
(defonce narsese-repl-mode (atom false))
@@ -35,9 +37,8 @@
(defn inference [n st1 st2]
(l/run n [q] (c/inference st1 st2 q)))
(defmethod collect! :judgement [{:keys [truth data]}]
(defmethod collect! :judgement [{:keys [truth statement]}]
(let [truth (if (empty? truth) [1 0.9] truth)
statement (first data)
known-truth (@db statement)]
(cond (nil? known-truth) (do (swap! db assoc statement truth) [statement truth])
(not= known-truth truth) (revision! statement known-truth truth)
@@ -51,9 +52,8 @@
(defn- wrap-code [code]
(str "(narjure.repl/handle-narsese \"" code "\")"))
(defn- sentence [{:keys [data]}]
(let [statement (first data)]
[statement (@db statement)]))
(defn- sentence [{:keys [statement]}]
[statement (@db statement)])
(defn run [n]
(let [last-two (map sentence (take-last 2 @buffer))
@@ -63,16 +63,21 @@
[])]
(into forward backward)))
(defn- get-result [code]
(let [result (parse code)]
(if (and (not (i/failure? result)))
(do (swap! buffer conj result)
(collect! result))
result)))
(defn handle-narsese [code]
(if-let [n (parse-int (clojure.string/trim code))]
(run n)
(if (= "stop!" (clojure.string/trim code))
(stop-narsese-repl!)
(let [result (parse code)]
(if (and (not (i/failure? result)))
(do (swap! buffer conj result)
(collect! result))
result)))))
(let [n (parse-int (trim code))]
(cond
(integer? n) (p/pprint (run n))
(= "stop!" (trim code)) (stop-narsese-repl!)
(= \* (first code)) (reset! buffer [])
(= \/ (first code)) ""
:default (get-result code))))
(defn narsese-handler [handler]
(fn [args & tail]
+25
View File
@@ -0,0 +1,25 @@
(ns narjure.test.bag
(:require [clojure.test :refer :all]
[narjure.bag :refer :all]))
(defn abc-bag
([] (abc-bag 3))
([cap] (-> (default-bag cap)
(put-el {:key :a :priority 0.7})
(put-el {:key :b :priority 0.7})
(put-el {:key :c :priority 0.7}))))
(def a-bag (put-el (default-bag) {:key :a :priority 0.7}))
(deftest test-bag
(is (= 3 (count-els (abc-bag))))
(is (= 2 (count (-> (default-bag)
(put-el {:key :a :priority 0.7})
(put-el {:key :b :priority 0.7})
(put-el {:key :a :priority 0.7})
:queue))))
(is (= 2 (count-els (abc-bag 2))))
(is (= :a (-> a-bag get-el first)))
(is (= :a (-> a-bag (get-el :a) first)))
(is (nil? (get-el a-bag :b))))
+13 -6
View File
@@ -3,19 +3,24 @@
[narjure.narsese :refer :all]
[instaparse.core :refer [failure?]]))
(defn narsese->clj [s] (:data (parse s)))
(defn narsese->clj [s] (:statement (parse s)))
(defn get-truth [s] (:truth (parse s)))
(deftest test-parser
(is (= '([[conjunction [inheritance tim fish] [inheritance tom fish]]])
(is (= '[[conjunction [inheritance tim fish] [inheritance tom fish]]]
(narsese->clj "(&&, <tim --> fish>, <tom --> fish>).")))
(is (= '([[conjunction [inheritance tim fish] [inheritance tom fish]]])
(is (= '[[conjunction [inheritance tim fish] [inheritance tom fish]]]
(narsese->clj "(<tim --> fish> && <tom --> fish>).")))
(is (= '([[conjunction [inheritance tim fish] [inheritance tom fish]]])
(is (= '[[conjunction [inheritance tim fish] [inheritance tom fish]]]
(narsese->clj "((tim --> fish) && (tom --> fish)).")))
(is (= '([inheritance bird swimmer])
(is (= '[inheritance bird swimmer]
(narsese->clj "<bird --> swimmer>.")))
(is (= [1.0 0.9] (get-truth "<bird --> swimmer>. %1;0.9%"))))
(is (= [1.0 0.9] (get-truth "<bird --> swimmer>. %1;0.9%")))
(is (= '[[negation bird]] (narsese->clj "--bird.")))
(is (= '[[negation bird]] (narsese->clj "(--,bird).")))
(is (= '[[int-image bird animal]] (narsese->clj "(\\,bird,animal).")))
(is (= '[[conjunction [inheritance d_1 [int-set red]] [inheritance d_1 apple]]]
(narsese->clj "(&&,<#1 --> [red]>,<#1 --> apple>)."))))
(deftest test-numbers-validation
(is (not (failure? (parse "<bird --> swimmer>. %1;0.9%"))))
@@ -30,5 +35,7 @@
(is (not (failure? (parse "<bird --> swimmer>. %0.1;0.9%"))))
(is (not (failure? (parse "<bird --> swimmer>. %0.0;0.9%"))))
(is (not (failure? (parse "<bird --> swimmer>. %0;0.9%"))))
(is (not (failure? (parse "<bird --> swimmer>. %1.00;0.9%"))))
(is (not (failure? (parse "<bird --> swimmer>. %1.;0.9%"))))
(is (failure? (parse "<bird --> swimmer>. %5;0.9%")))
(is (failure? (parse "<bird --> swimmer>. %-1;0.9%"))))