ztellman / automat Goto Github PK
View Code? Open in Web Editor NEWbetter automata through combinators
better automata through combinators
Seeing a warning in cljs:
WARNING: cljs.core/bit-or, all arguments must be numbers, got [clj-nil number] inste
ad. at line 601 /Users/zalan/.boot/cache/tmp/Users/zalan/src/semion/onty/2ao/-bj0ipr
/main.out/automat/fsm.cljc
Simple test case:
(ns simple.core
(:require [automat.core :as a]))
(a/compile [1])
Tested with:
It doesn't seem to have any affect in my simple use cases. Is this a known issue? Is this something to worry about?
Hello,
As FSM's can be represented by both a state diagram and a state transition table, I think it would be really nice if the lib could generate such tables. As an image or as HTML. HTML could be easy to implement, but I'm not sure since I haven't dig into the code yet.
What do you think ?
This will allow for consistent order of actions
I am looking for a way to dump the state into EDN.
I can achieve the opposite using (a/->automaton-state fsm m)
though.
Please advise.
(require '[automat.core :as a]
(-> (a/or (a/interpose-$ :x [1 2 3])
(a/interpose-$ :y [1 2 9]))
(a/compile {:reducers {:x #(update-in %1 [:x] conj %2)
:y #(update-in %1 [:y] conj %2)}})
(a/find {:x [] :y []} [1 2 3])
:value)
;=> {:x [3] :y [1 2]}
I would expect the reduction value to be {:x [1 2 3] :y [1 2]}
.
It looks like only actions attached to the last union are triggered when the unions overlap.
(-> (a/or (a/interpose-$ :y [1 2 9]) ; :y actions will be replaced by overlapping :x actions
(a/interpose-$ :x [1 2 3]))
(a/compile {:reducers {:x #(update-in %1 [:x] conj %2)
:y #(update-in %1 [:y] conj %2)}})
(a/find {:x [] :y []} [1 2 3])
:value)
;=> {:x [1 2 3], :y []}
; no :y actions trigger
I'm trying to express the concept of valid transitions between states for one of the domain entities in my system.
I have an fsm that does this nicely: [1 (a/* [2 3]) 4]
.
Requests to move the domain entity from its current state (any of 1, 2, 3, or 4) to another state (say from 1 to 4 or 1 to 2 or 2 to 3 or 3 to 2) come in via http requests and I'd like to return a 409 in the event that the transition would not be allowable given the above fsm.
However, to do that, it appears that I have to construct an entire state map with knowledge of the internal state-indexes used by the fsm once it's been compiled. There doesn't appear to be any way to turn the compiled fsm into, say, a state transition table like {1 #{2 4} 2 #{3} 3 #{2 4}}
which could then be used in turn to implement a possible?
or permissible?
predicate.
So, am I missing something or could something like permissible?
be implemented directly in automat? Ideally, it would be something like (a/permissible? fsm (things-current-state-as-named-in-fsm-data thing) proposed-state)
I will say that I may be fundamentally misunderstanding this library and maybe even FSMs in general so forgive me if that's the case and maybe point me in the direction of some reading I could do/a different library I could use.
Thanks in advance!
The destination link currently reads "No such thing". I think this was working a few days ago.
The or
function in core may run into infinite loop at the minimization step. In particular, when it happens, the infinite loop will appear in the root
function in the merge-pairs
function. The occurrence of the infinite loop seems to be random, however, it will always appear if one runs or
enough times.
To reproduce, run the following code:
(dotimes [i 100]
(println (str "No." i))
(a/or 1 2 3 4))
It may take a couple of runs of the above to generate an infinite loop. The process will have to be killed to stop it.
If we only or
two states together, this bug doesn't appear.
Could you add more details on when the reduce actions are being executed to the documentation, and show the actions in the view
graph?
In the documentation it reads like the actions might be associated with the transitions, but they actually seem to be collected on each state, i.e. for :x :w
input action :F
is still executed because it's associated with state 1, not the transition :z
.
(defn states
[arg]
(let [f (a/compile
[(a/$ :A)
(a/or
[(a/$ :B) :x (a/$ :C)]
[(a/$ :D) :y (a/$ :E) :z (a/$ :F)])
(a/$ :G)
:w
(a/$ :H)]
(apply merge
(map (fn [t] {t #(conj %1 [t %2])})
[:A :B :C :D :E :F :G :H])))])
(:value (a/greedy-find f (a/start f []) arg)))
(states [:x :w])
=> [[:A :x] [:D :x] [:B :x] [:F :x] [:C :x] [:G :x] [:H :w]]
(states [:y :z :w])
=> [[:A :y] [:D :y] [:B :y] [:E :y] [:F :z] [:C :z] [:G :z] [:H :w]]
(require '[automat.core :as a])
(-> [:a \b]
a/compile
(a/find nil [:a :b]))
;=> java.lang.ClassCastException: clojure.lang.Keyword cannot be cast to java.lang.Number
Looks like chars and 1-character strings are coerced to ints: https://github.com/ztellman/automat/blob/8bd3989e0c9d987caafc3b5f0d64e7750d2a3d49/src/automat/core.clj#L19-24
Oddly, this works just fine:
(-> (a/or [:a \b] [:a :b])
a/compile
(a/find nil [:a :b]))
;=> #automat.core.CompiledAutomatonState{:accepted? true, :checkpoint nil, :state-index 2, :start-index 0, :stream-index 2, :value nil}
Hi @ztellman just updating this problem in a clean and separate issue
org.clojure/clojurescript
dependency and :aot :all
with ztellman/automat
throws error when executing standalone jar Caused by: java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to clojure.lang.MultiFn
We discussed this on Slack briefly. I got this using a plain clojure.main repl. I've generated a few data sets using test.check, I've used 1055 as the length of input.
user=> (require '[automat.core :as a]
'[criterium.core :as c])
user=> (let [data "7Z0YzI6qmKUR4QNOq81l409AC2Ng3c00tb9J4VOz5kBuGTqCAWmYTx4E7Xckp6662k0jd14ds9G1jt0ldShcf37POcfYqsPcqliwlcM0l6GYVF3850650biK89QL3N68Z19k57Eu3dLSukaWZCM32J3Dz99l4KF53JvkWraBwlX3FpU3TWM3X992a1tU2qL6q6Y0g4r4ay5QMuc1twL9xRJMn3WoeA7g8Oh3FoYMX3VD02FhaB76TwMm5kxf2mxK5HUmDu88gYsO4CS6paOqEf7s17hJ712y3xaCT9a3r71M4E2467jr0i4uGt267HMKQO7dqHywM8CFy72AIUq7OZ4K5Nn2t9JuL5Kqv79Y8ZE3aAS4tNeG8ckh201AfB11okTh5bT9oann21A6wN67vzq3Q31SyF5wg2Xx1885z56X4WLgkRfq5A3kFgmqAJdfmlcTp5Dr4404P2nWGmDBJ62dR100RQtLLjKN25a42Kebk1pWFbu6z56y5jHE0txpN9V87cb66Exp50Cg42Jl37Mbi2MqkW218LjNMTnRU7365RT8Uf3523X1cCZFWq7z2GSkc6uL2ai2d88B7a7i1k0h0d9K63765p3w4obwd32kIlQu5xy4yFQ3R91XP353c8J7H3Ep3Oo45p8F70y3649hqM310Gnv13gBXHdLyXs25od8tmwE5fHB3G06OaG877ymRI5J89KI1XylAx47o18H17CHC6P5fm6KC71n40WjK01P4rt06K06F8TM6OF81eq3wE4W0AO702PxEO042I4r9VUt8g0jp3057Sbju25U15HCP1742NEVTM1niDjb0S89885Nf93X97Cgsw5x3eu13ZU8cGbj246qt5t7lzzW08ozl1yykx73d7a74VNPYdZE4498XTgnzhv3R44p6iulMm9Vauv9sXG2Hb4TH4Kx9G6hK1X2MJEsHFm680ZueWNu98G3oVnA435SfwSA6umdt26g9hBoTGMXZ7K64yIs8kYxgwKi0Jqhvm3wdTJdn86u9HX5TeQh45Vc6TSZeCW2xLxNagu"
fsm (a/compile [\f \o \o])]
(c/bench
(reduce
#(a/advance fsm %1 %2 false)
nil
data)))
Evaluation count : 339480 in 60 samples of 5658 calls.
Execution time mean : 178.169999 µs
Execution time std-deviation : 2.128803 µs
Execution time lower quantile : 176.386367 µs ( 2.5%)
Execution time upper quantile : 182.867510 µs (97.5%)
Overhead used : 1.727016 ns
Found 4 outliers in 60 samples (6.6667 %)
low-severe 1 (1.6667 %)
low-mild 3 (5.0000 %)
Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
If I'm not being daft, which is common enough, (/ 178.16 1055)
is 0.169µs = 169ns per advancement.
Numbers have a similar result:
user=> (let [data [-3 25 1 -14 -2 15 -18 -11 30 3 -30 17 24 -20 27 13 -1 -17 -15 -23 -24 -25 22 11 12 -8 -24 10 11 14 18 -10 -10 19 -28 27 -26 -8 1 -22 20 4 -12 -13 -15 13 27 -8 30 -7 21 -5 25 -28 10 4 -18 24 3 -16 -20 -22 22 -28 -8 25 -29 -13 -16 -24 -9 1 1 7 -9 7 -26 -5 5 -2 19 25 29 25 -4 20 1 21 -3 21 -13 -11 -15 2 -3 -17 28 -12 -10 15 -7 -28 14 2 13 18 17 -17 7 1 17 11 23 -16 -8 22 -28 21 28 1 18 7 26 27 5 -14 7 16 -7 -21 -7 10 5 5 20 24 30 30 17 27 16 -17 -18 21 -9 -3 -30 3 -25 -24 7 25 22 -18 -3 -12 -8 14 -20 -4 15 7 -19 23 -29 2 1 29 -12 24 19 18 16 -17 20 27 -26 18 -26 16 -17 13 24 29 -23 21 -7 1 -2 -15 -6 17 19 5 -18 30 -7 5 2 -1 3 6 -24 3 -13 -18 5 4 6 -5 7 10 22 7 30 -9 6 -16 -27 22 26 -15 17 14 -10 24 -22 17 20 30 5 14 17 -26 -6 17 23 18 6 1 -25 -15 -29 -7 -20 -23 3 1 23 -1 22 -15 -11 2 -22 12 16 28 24 4 12 -11 -8 1 23 10 0 20 25 -9 -17 21 -21 10 7 -7 5 11 -8 -11 -15 29 8 -29 -24 -14 17 -19 -17 -18 16 16 3 13 -19 7 27 -29 15 -21 -26 24 2 10 -27 -26 13 17 29 -14 0 -26 -2 10 18 0 25 23 8 21 6 -6 -18 5 16 19 23 26 24 -27 -26 -11 -3 -17 -12 23 21 12 23 22 20 -27 -3 -3 15 -1 -20 5 -13 -21 30 -6 -18 -24 -4 20 -30 -15 -28 23 10 27 3 -21 18 -12 1 23 16 4 3 29 -27 -2 15 -26 2 -9 -14 28 18 -25 9 -17 7 -23 -29 -14 -23 -7 -25 16 3 -1 -27 -14 7 -25 28 -11 -27 -15 -27 -6 19 19 -15 -5 26 -9 -28 -4 7 -20 -26 7 8 28 0 14 -21 -16 6 30 10 -21 0 -8 -2 -25 -18 -21 20 -27 24 8 -22 18 -20 8 -10 -29 6 9 -7 4 -11 -17 -21 -4 -16 15 3 10 -23 8 -26 3 20 28 -3 25 20 -28 -28 5 -11 24 -3 -29 -21 27 -20 -12 2 3 22 25 22 25 29 3 26 27 -16 -10 30 25 -28 26 -13 -24 -16 -2 10 11 11 -2 -24 18 -22 14 -30 -25 -5 -23 -28 18 28 10 -16 6 7 -8 -22 23 -13 11 21 -3 -24 -4 22 -13 -8 27 -13 19 -29 10 -6 -7 -5 9 13 -19 -1 -9 30 -2 2 21 -13 9 -12 -5 -26 12 7 -23 20 -29 2 27 5 -14 14 2 14 2 -9 17 -13 -24 -9 -6 11 -6 -26 -27 -13 -1 28 -23 -19 22 3 -29 2 -13 9 -1 11 30 3 5 -17 -18 28 26 28 -9 -20 15 -4 -19 -14 -18 -8 -17 -22 -29 -24 20 12 12 16 7 -25 -6 14 0 -15 14 20 -25 23 10 -29 8 14 -19 -20 -3 25 -3 -7 -19 -11 0 -29 24 27 -28 -28 22 -17 27 12 -24 -26 -19 14 17 -25 -7 -13 18 28 21 24 22 -25 25 -18 -15 -2 25 26 -17 20 15 -7 30 18 20 -14 -12 -16 10 23 -27 -7 -14 -8 -8 -12 24 6 -4 30 -16 14 4 -15 27 -17 -14 -24 29 29 18 -7 -15 28 -11 7 6 18 30 -25 22 -2 -20 1 -23 -13 -28 -4 26 18 25 22 -18 -5 -25 -7 5 30 -1 -7 -8 -30 24 -13 19 18 26 1 -16 1 -1 25 16 10 9 1 12 -28 5 -27 -16 14 24 18 -1 -12 25 -19 30 24 -21 27 18 -10 -14 -7 -12 -26 -9 13 23 10 -6 1 -7 -16 25 20 -22 11 12 20 4 14 -6 18 19 5 -6 14 -29 -26 21 11 30 19 -24 -12 -6 -27 25 14 -1 25 -12 18 -17 0 -29 -24 -30 5 -20 29 24 -17 28 -11 -10 7 11 7 -22 2 21 14 -15 -29 8 16 16 10 9 -10 16 30 12 -2 -18 9 5 -20 13 10 -29 17 11 22 22 -27 -3 -9 -19 22 11 15 -22 -15 11 -27 15 15 -26 6 -2 2 16 16 -7 -1 -7 -2 -17 20 -12 -13 18 8 7 -3 9 30 13 -19 -4 -13 7 -12 24 -7 8 10 -5 14 6 26 -18 -21 -1 -4 28 4 2 -13 21 15 -4 12 6 -20 -6 23 -21 8 22 22 5 23 -12 -23 -16 -11 7 -12 20 -24 10 -23 16 7 20 3 -1 26 -24 23 -21 -9 27 -3 -23 -30 22 16 12 3 10 -14 -17 -6 16 -16 -17 -2 5 13 -6 23 27 -26 15 -7 21 -14 14 12 -8 -13 23 -5 -27 30 27 -5 26 29 29 28 1 -15 -26 20 30 17 1 2 -1 -3 13 13 -10 -13 13 -7 -15 -14 -26 -11 -3 1 21 -16 11 23 -1 6 17 19 2 2 -14 3 -27 9 2 14 -18 -30 22 3 -2 19 -26 30 29 -24 -16 -2 5 -6 -13 -21 -20 -17 23 15 30 15 -12 -5 -18 8 -2 -9 -13 -24 -20 -26 -27 -9 21 -30 -10 4 -27 22 25 18 8 -28 -16 6 30 -2]
fsm (a/compile [50 20 10])]
(c/bench
(reduce
#(a/advance fsm %1 %2 false)
nil
data)))
Evaluation count : 446280 in 60 samples of 7438 calls.
Execution time mean : 139.068076 µs
Execution time std-deviation : 12.810401 µs
Execution time lower quantile : 134.080157 µs ( 2.5%)
Execution time upper quantile : 168.040733 µs (97.5%)
Overhead used : 1.727016 ns
Found 9 outliers in 60 samples (15.0000 %)
low-severe 1 (1.6667 %)
low-mild 8 (13.3333 %)
Variance from outliers : 65.3027 % Variance is severely inflated by outliers
nil
This is with this java:
❯ java -version
openjdk version "1.8.0_152"
OpenJDK Runtime Environment (build 1.8.0_152-b03)
OpenJDK 64-Bit Server VM (build 25.152-b03, mixed mode)
and clojure version 1.8.
I also tried with Oracle JDK:
user=> (let [data [-3 25 1 -14 -2 15 -18 -11 30 3 -30 17 24 -20 27 13 -1 -17 -15 -23 -24 -25 22 11 12 -8 -24 10 11 14 18 -10 -10 19 -28 27 -26 -8 1 -22 20 4 -12 -13 -15 13 27 -8 30 -7 21 -5 25 -28 10 4 -18 24 3 -16 -20 -22 22 -28 -8 25 -29 -13 -16 -24 -9 1 1 7 -9 7 -26 -5 5 -2 19 25 29 25 -4 20 1 21 -3 21 -13 -11 -15 2 -3 -17 28 -12 -10 15 -7 -28 14 2 13 18 17 -17 7 1 17 11 23 -16 -8 22 -28 21 28 1 18 7 26 27 5 -14 7 16 -7 -21 -7 10 5 5 20 24 30 30 17 27 16 -17 -18 21 -9 -3 -30 3 -25 -24 7 25 22 -18 -3 -12 -8 14 -20 -4 15 7 -19 23 -29 2 1 29 -12 24 19 18 16 -17 20 27 -26 18 -26 16 -17 13 24 29 -23 21 -7 1 -2 -15 -6 17 19 5 -18 30 -7 5 2 -1 3 6 -24 3 -13 -18 5 4 6 -5 7 10 22 7 30 -9 6 -16 -27 22 26 -15 17 14 -10 24 -22 17 20 30 5 14 17 -26 -6 17 23 18 6 1 -25 -15 -29 -7 -20 -23 3 1 23 -1 22 -15 -11 2 -22 12 16 28 24 4 12 -11 -8 1 23 10 0 20 25 -9 -17 21 -21 10 7 -7 5 11 -8 -11 -15 29 8 -29 -24 -14 17 -19 -17 -18 16 16 3 13 -19 7 27 -29 15 -21 -26 24 2 10 -27 -26 13 17 29 -14 0 -26 -2 10 18 0 25 23 8 21 6 -6 -18 5 16 19 23 26 24 -27 -26 -11 -3 -17 -12 23 21 12 23 22 20 -27 -3 -3 15 -1 -20 5 -13 -21 30 -6 -18 -24 -4 20 -30 -15 -28 23 10 27 3 -21 18 -12 1 23 16 4 3 29 -27 -2 15 -26 2 -9 -14 28 18 -25 9 -17 7 -23 -29 -14 -23 -7 -25 16 3 -1 -27 -14 7 -25 28 -11 -27 -15 -27 -6 19 19 -15 -5 26 -9 -28 -4 7 -20 -26 7 8 28 0 14 -21 -16 6 30 10 -21 0 -8 -2 -25 -18 -21 20 -27 24 8 -22 18 -20 8 -10 -29 6 9 -7 4 -11 -17 -21 -4 -16 15 3 10 -23 8 -26 3 20 28 -3 25 20 -28 -28 5 -11 24 -3 -29 -21 27 -20 -12 2 3 22 25 22 25 29 3 26 27 -16 -10 30 25 -28 26 -13 -24 -16 -2 10 11 11 -2 -24 18 -22 14 -30 -25 -5 -23 -28 18 28 10 -16 6 7 -8 -22 23 -13 11 21 -3 -24 -4 22 -13 -8 27 -13 19 -29 10 -6 -7 -5 9 13 -19 -1 -9 30 -2 2 21 -13 9 -12 -5 -26 12 7 -23 20 -29 2 27 5 -14 14 2 14 2 -9 17 -13 -24 -9 -6 11 -6 -26 -27 -13 -1 28 -23 -19 22 3 -29 2 -13 9 -1 11 30 3 5 -17 -18 28 26 28 -9 -20 15 -4 -19 -14 -18 -8 -17 -22 -29 -24 20 12 12 16 7 -25 -6 14 0 -15 14 20 -25 23 10 -29 8 14 -19 -20 -3 25 -3 -7 -19 -11 0 -29 24 27 -28 -28 22 -17 27 12 -24 -26 -19 14 17 -25 -7 -13 18 28 21 24 22 -25 25 -18 -15 -2 25 26 -17 20 15 -7 30 18 20 -14 -12 -16 10 23 -27 -7 -14 -8 -8 -12 24 6 -4 30 -16 14 4 -15 27 -17 -14 -24 29 29 18 -7 -15 28 -11 7 6 18 30 -25 22 -2 -20 1 -23 -13 -28 -4 26 18 25 22 -18 -5 -25 -7 5 30 -1 -7 -8 -30 24 -13 19 18 26 1 -16 1 -1 25 16 10 9 1 12 -28 5 -27 -16 14 24 18 -1 -12 25 -19 30 24 -21 27 18 -10 -14 -7 -12 -26 -9 13 23 10 -6 1 -7 -16 25 20 -22 11 12 20 4 14 -6 18 19 5 -6 14 -29 -26 21 11 30 19 -24 -12 -6 -27 25 14 -1 25 -12 18 -17 0 -29 -24 -30 5 -20 29 24 -17 28 -11 -10 7 11 7 -22 2 21 14 -15 -29 8 16 16 10 9 -10 16 30 12 -2 -18 9 5 -20 13 10 -29 17 11 22 22 -27 -3 -9 -19 22 11 15 -22 -15 11 -27 15 15 -26 6 -2 2 16 16 -7 -1 -7 -2 -17 20 -12 -13 18 8 7 -3 9 30 13 -19 -4 -13 7 -12 24 -7 8 10 -5 14 6 26 -18 -21 -1 -4 28 4 2 -13 21 15 -4 12 6 -20 -6 23 -21 8 22 22 5 23 -12 -23 -16 -11 7 -12 20 -24 10 -23 16 7 20 3 -1 26 -24 23 -21 -9 27 -3 -23 -30 22 16 12 3 10 -14 -17 -6 16 -16 -17 -2 5 13 -6 23 27 -26 15 -7 21 -14 14 12 -8 -13 23 -5 -27 30 27 -5 26 29 29 28 1 -15 -26 20 30 17 1 2 -1 -3 13 13 -10 -13 13 -7 -15 -14 -26 -11 -3 1 21 -16 11 23 -1 6 17 19 2 2 -14 3 -27 9 2 14 -18 -30 22 3 -2 19 -26 30 29 -24 -16 -2 5 -6 -13 -21 -20 -17 23 15 30 15 -12 -5 -18 8 -2 -9 -13 -24 -20 -26 -27 -9 21 -30 -10 4 -27 22 25 18 8 -28 -16 6 30 -2]
fsm (a/compile [50 20 10])]
(c/bench
(reduce
#(a/advance fsm %1 %2 false)
nil
data)))
Evaluation count : 428820 in 60 samples of 7147 calls.
Execution time mean : 140.780361 µs
Execution time std-deviation : 748.460193 ns
Execution time lower quantile : 139.914201 µs ( 2.5%)
Execution time upper quantile : 142.457726 µs (97.5%)
Overhead used : 1.727470 ns
Found 6 outliers in 60 samples (10.0000 %)
low-severe 6 (10.0000 %)
Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
nil
❯ java -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
Is this enough detail? Anything else I can do?
I'm trying to use both aleph and automat in a project together. In :require
ing libraries from these projects I end up with the error:
CompilerException java.lang.IllegalAccessError: vector does not exist, compiling:(byte_streams/graph.clj:1:1)
I've tried to track this down myself and see where byte_streams/graph.clj does the following so that it can refer to clj-tuple's vector. Beyond that I'm lost.
(:refer-clojure :exclude [vector type byte-array])
Here are the dependencies from project.clj:
:dependencies [[org.clojure/clojure "1.6.0"] [automat "0.1.3"] [aleph "0.4.0"]])
Here is the ns
:
(ns nettink.core (:require [automat.core :as a] [aleph.tcp :as tcp]))
Not sure if there is a different way to :require
these two projects together or not. Am hoping that you have combined the two before and can help me out.
The automata ["1"]
won't accept the input "1"
:
avi.repl=> (a/advance (a/compile [:1]) nil :1 :error)
#automat.core.CompiledAutomatonState{:accepted? true, :checkpoint nil, :state-index 1, :start-index 0, :stream-index 1, :value nil}
avi.repl=> (a/advance (a/compile ["1"]) nil "1" :error)
ClassCastException java.lang.String cannot be cast to java.lang.Number automat.stream/to-stream/reify--20992 (stream.clj:70)
avi.repl=> (a/advance (a/compile ["x1"]) nil "x1" :error)
#automat.core.CompiledAutomatonState{:accepted? true, :checkpoint nil, :state-index 1, :start-index 0, :stream-index 1, :value nil}
avi.repl=>
[automat "0.1.3"]
in project.clj
's dependencies
. When I lein repl
and try to require view
, I see:
user=> (require '[automat.viz :refer (view)])
CompilerException java.lang.NoClassDefFoundError: IllegalName: compile__stub.automat.fsm.automat.fsm/State, compiling:(automat/fsm.clj:18:1)
Really sorry if this is a known problem, I tried googling and poking around github, but can't find anything, and am super-excited to try it!
It is desirable for the signal function to return multiple values (a seq of candidates, for example), and the FSM would try and take the first value that can advance to the next state. This way, the signal become context sensitive, giving different interpretations depending on the state.
Would it be possible to add something like this? I looked at the code, it seems to be easy to add this functionality to the base compiler, but I do not seem to grok the eval compiler to be able to add it myself.
When the result of automat's fsm->dot is run through rhizome's dot->svg, the resulting
svg fragment doesn't display the entire automata. Specifically, the svg's width and height
are larger than the viewBox dimensions:
(-> (automat/fsm->dot [1] {}) rhizome/dot->svg)
;=> ... <svg width="242pt" height="72pt" viewBox="0.00 0.00 174.00 52.00" ...
I need to call {:dpi 72} to get a correct rendering. I'm on ubuntu 12.04.
(-> (automat/fsm->dot [1] {:dpi 72}) rhizome/dot->svg)
;=> ... <svg width="174pt" height="52pt" viewBox="0.00 0.00 174.00 52.00" ...
I'm using the svg fragments to render the automata in gorilla-repl.
(a/or [1 2 3 (a/$ :bar)] [1 4 3 (a/$ :foo)])
should not have the two 3
states minimized into each other
I'm a bit lost having this problem only when i tried my app using the uberjar generated using lein uberjar
The error says ...
Caused by: java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to clojure.lang.MultiFn
(perhaps) Could be related with my AOT project.clj
config???
:main roc.platform.main
:aot [roc.platform.main
#"roc\.platform\.*"
#"roc\.[a-z]+$"]
$ java -jar target/roc-0.1.0-SNAPSHOT-standalone.jar
Exception in thread "main" java.lang.ExceptionInInitializerError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:344)
at clojure.lang.RT.classForName(RT.java:2168)
at clojure.lang.RT.classForName(RT.java:2177)
at clojure.lang.RT.loadClassForName(RT.java:2196)
at clojure.lang.RT.load(RT.java:443)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:421)
at automat.compiler.core$loading__5569__auto____14606.invoke(core.cljc:1)
at automat.compiler.core__init.load(Unknown Source)
at automat.compiler.core__init.<clinit>(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:344)
at clojure.lang.RT.classForName(RT.java:2168)
at clojure.lang.RT.classForName(RT.java:2177)
at clojure.lang.RT.loadClassForName(RT.java:2196)
at clojure.lang.RT.load(RT.java:443)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:482)
at automat.viz$loading__5569__auto____14604.invoke(viz.clj:1)
at automat.viz__init.load(Unknown Source)
at automat.viz__init.<clinit>(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:344)
at clojure.lang.RT.classForName(RT.java:2168)
at clojure.lang.RT.classForName(RT.java:2177)
at clojure.lang.RT.loadClassForName(RT.java:2196)
at clojure.lang.RT.load(RT.java:443)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:619)
at roc.auto$loading__5569__auto____14602.invoke(auto.clj:1)
at roc.auto__init.load(Unknown Source)
at roc.auto__init.<clinit>(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:344)
at clojure.lang.RT.classForName(RT.java:2168)
at clojure.lang.RT.classForName(RT.java:2177)
at clojure.lang.RT.loadClassForName(RT.java:2196)
at clojure.lang.RT.load(RT.java:443)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:1289)
at roc.platform.system$loading__5569__auto____24062.invoke(system.clj:1)
at roc.platform.system__init.load(Unknown Source)
at roc.platform.system__init.<clinit>(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:344)
at clojure.lang.RT.classForName(RT.java:2168)
at clojure.lang.RT.classForName(RT.java:2177)
at clojure.lang.RT.loadClassForName(RT.java:2196)
at clojure.lang.RT.load(RT.java:443)
at clojure.lang.RT.load(RT.java:419)
at clojure.core$load$fn__5677.invoke(core.clj:5893)
at clojure.core$load.invokeStatic(core.clj:5892)
at clojure.core$load.doInvoke(core.clj:5876)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$load_one.invokeStatic(core.clj:5697)
at clojure.core$load_one.invoke(core.clj:5692)
at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
at clojure.core$load_lib.invokeStatic(core.clj:5736)
at clojure.core$load_lib.doInvoke(core.clj:5717)
at clojure.lang.RestFn.applyTo(RestFn.java:142)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$load_libs.invokeStatic(core.clj:5774)
at clojure.core$load_libs.doInvoke(core.clj:5758)
at clojure.lang.RestFn.applyTo(RestFn.java:137)
at clojure.core$apply.invokeStatic(core.clj:648)
at clojure.core$require.invokeStatic(core.clj:5796)
at clojure.core$require.doInvoke(core.clj:5796)
at clojure.lang.RestFn.invoke(RestFn.java:408)
at clojure.core$eval145.invokeStatic(NO_SOURCE_FILE:7)
at clojure.core$eval145.invoke(NO_SOURCE_FILE:7)
at clojure.lang.Compiler.eval(Compiler.java:6927)
at clojure.lang.Compiler.eval(Compiler.java:6916)
at clojure.lang.Compiler.eval(Compiler.java:6890)
at clojure.core$eval.invokeStatic(core.clj:3105)
at clojure.core$eval.invoke(core.clj:3101)
at roc.platform.main$_main.invokeStatic(main.clj:6)
at roc.platform.main$_main.doInvoke(main.clj:4)
at clojure.lang.RestFn.invoke(RestFn.java:397)
at clojure.lang.AFn.applyToHelper(AFn.java:152)
at clojure.lang.RestFn.applyTo(RestFn.java:132)
at roc.platform.main.main(Unknown Source)
Caused by: java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to clojure.lang.MultiFn
at automat.fsm__init.load(Unknown Source)
at automat.fsm__init.<clinit>(Unknown Source)
... 150 more
Thanks in advance for this great lib!
The README says to go to http://ideolalia.com/automat/
for the complete documentation, but that link only contains the docs for the viz namespace.
lynaghk/cljx says cljx is deprecated now that Clojure supports reader conditionals ... This ClojureScript wiki page Using cljc links to two example pull requests for porting. A port would enable current efforts to use automat in ClojureScript
I'd expect character ranges (e.g. the way regexes allow [A-Z]
) to work, but they're not supported:
boot.user=> (view (a/range \A \Z))
java.lang.IllegalArgumentException: Don't know how to create a range from '\A' to '\Z'
https://github.com/ztellman/automat/blob/master/src/automat/compiler/base.clj#L89
Found using Eastwood lint tool
Related to #23, the other question I'm interested in asking the fsm is, given the current state, what inputs would be valid. This would allow me to return to the user in the 409
the statement: you asked me to do this, which is wrong, but you could've asked me to do these, which would have been ok.
Again, given the tools available in fsm
I'm unclear how to accomplish that goal. Any help there?
Compiling a complex FSM throws java.lang.RuntimeException: Method code too large!
(require '[automat.core :as a])
(let [coll (range 20)]
(->> coll
(map #(interpose % coll))
(apply a/or)
a/compile))
;=> clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Method code too large!
Again, apologies if I'm misunderstanding the docs - from the main docs "The not operator is equivalent to the regex ^ flag for negating character classes". Running viz/view on trivial (one-node) automata this fits my understanding, however the following example of a word-count automaton fails:
(defn word+ [v _] (inc v))
(def word [\a])
(def !word (a/not word))
(def word-count-with-not
(a/compile
(a/*
(a/or
[(a/+ !word)]
[[(a/+ word) !word] (a/$ :word+)]))
{:reducers {:word+ word+}}))
(:value (reduce #(a/advance word-count-with-not %1 %2) 0 "aaa aa aaaaaa.")) ;=> 6?!
If I preprocess the machines with a signal instead, the logic seems to be correct:
(def word-count-with-signal
(a/compile
(a/*
(a/or
[(a/+ false)]
[[(a/+ true) false] (a/$ :word+)]))
{:signal (partial = \a)
:reducers {:word+ word+}}))
(:value (reduce #(a/advance word-count-with-signal %1 %2) 0 "aaa aa aaaaaa.")) ;=> 3
Comparing the state machines with viz.view, it looks like the :word+
event is triggered, unexpectedly, after the 2nd (a/+ word)
iteration.
Using automat together with midje causes exception on startup.
How to reproduce:
Create a new project, add dependencies:
[automat "0.1.3"]
[midje "1.8.2"]
Launch REPL, load midje:
=> (require '[midje.repl])
Run `(doc midje)` for Midje usage.
Run `(doc midje-repl)` for descriptions of Midje repl functions.
CompilerException java.lang.RuntimeException: Unable to resolve symbol: _wrapping-target_ in this context, compiling:(midje/repl.clj:32:3)
It can be worked around by adding a third dependency:
[potemkin "0.4.2"]
Seems a minor issue, but it took me a while to figure out, eventually found this: marick/Midje#340
I believe it can be fixed by changing potemkin version for automat to 0.4.2. I could make a pull request, but there is no branch for 0.1.3 hotfixes :)
While I think I know how I would do that (via reducer functions), I am looking for a recommendation how to achieve timeout transitions.
First of all, thanks for the wonderful library! I'm trying to study FSM applications and doing some exercises. Suddenly I bumped into an issue:
This works:
(view (a/interpose-$ 42 [1]))
While this doesn't:
(view [1 (a/$ 42)])
If I replace 42
with :foo
, it works both ways and produces identical FSMs.
My use case is that I want to dispatch reducers with a function, not with a map:
(defn reducers [v]
(fn [state _] (+ state v)))
...
(a/compile [ ... ]
{:reducers reducers}))
Therefore I need to pass more than just a keyword, possibly a nested data structure.
Is this a bug or a feature? It would be great to have it working consistently, and also just $
is much shorter than interpose-$
.
> (a/advance (a/compile [(int \f)]) nil \f ::fail)
ClassCastException java.lang.Character cannot be cast to java.lang.Number automat.stream/to-stream/reify--91907 (stream.cljc:73)
to-stream
's prior attempt to cover the narrow case of char input when expecting a number caused other issues (#34), but the char/int mismatch is just the most obvious/convenient/common case. Any FSM that requires a numeric input to advance, but encounters one that can't be coerced to a number will fail with a ClassCast instead of rejecting.
A straightforward way to address this would be to signal rejection in the reified nextNumericInput
impl if the return isn't actually a number. That would cover the immediate problem.
One interesting/useful case that this wouldn't cover is where one has an FSM expecting a numeric input, it encounters something non-numeric, but you'd like to account for this via the :signal
mechanism. Right now, that's not possible at all, but in such a case, it could "back off" to use nextInput
, get the non-numeric result, apply the :signal
fn, and then attempt to continue. This might belong in a separate enhancement issue, but I think it would nicely shave off a rough corner and make :signal
much more general-purpose. At the moment, I'm "preprocessing" inputs to my FSM because some never get to :signal
in the first place.
The following example under "Searching over streams" has some errors:
> (def f
(a/compile
[($:clear) (a/interpose-$ :conj (range 5))]
{:clear (constantly [])
:conj conj}))
I believe that should be (a/$ :clear)
on line 3 and a missing {:reducers ...}
wrapper on lines 4-5.
Thanks for this amazing library Zach.
You may want to mention in the README.md that automat uses rhizome which in turn leverages Graphviz and that you need to have it installed first. If not for that fact that I suspected you were using other libraries of your own, I was stuck looking at a java error on UnixProcesses and no idea why!
Thanks again
the $ function has a precondition of keyword?
, and that limits reduction operations to fsm with alphabets that are keywords.
The linked docs are nearly empty and only provide the latest snapshot rather than 0.1.3. http://ideolalia.com/automat
If you mis-spell the name of a reducer function, it will be ignored silently, e.g.:
(let [fsm (a/compile [:foo :bar (a/$ :baar)]
{:reducers {:bar (fn [state _] (assoc state :bar true))}})]
(reduce (partial a/advance fsm)
{}
[:foo :bar]))
I believe it's because of this line in the eval
version, and this one in the base version, throwing away any nils after looking up the reducer in the reducers map. Wouldn't it be better to throw some sort of error if a requested reducer does not exist?
As you know, the current release doesn't work with clojure 1.8.
If a release is impossible, an update to the readme would be nice.
Thanks.
How to reproduce:
=> (a/advance (a/compile [\A]) nil \A)
ClassCastException java.lang.Character cannot be cast to java.lang.Number automat.stream/to-stream/reify--19385 (stream.clj:70)
While this of course works:
=> (a/advance (a/compile [1]) nil 1)
And even this:
=> (a/advance (a/compile [\A]) nil 65)
Is there any fundamental reason for chars are mandatorily converted to ints when compilation happens?
For example:
(view [:x (a/* (a/* :y) :z)])
Should be generated:
(view [:x (a/* (a/or [(a/+ :y) :z] :z))])
Hi, I'm new to clojure and when I try to do the example I'm unsuccessful.
$ lein repl
2016-12-21 09:00:52,663 [main] DEBUG org.jboss.logging - Logging Provider: org.jboss.logging.Slf4jLoggerProvider
nREPL server started on port 62218 on host 127.0.0.1 - nrepl://127.0.0.1:62218
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_25-b17
Docs: (doc function-name-here)
(find-doc "part-of-name-here")
Source: (source function-name-here)
Javadoc: (javadoc java-object-or-class-here)
Exit: Control+D or (exit) or (quit)
Results: Stored in vars *1, *2, *3, an exception in *e
user=> (require '[automat.viz :refer (view)])
nil
user=> (require '[automat.core :as a])
nil
user=> (view [1 2 3])
IOException error=2, No such file or directory java.lang.UNIXProcess.forkAndExec (UNIXProcess.java:-2)
user=>
I'm on mac os x. What do I need to do to allow lein access to x11?
The construction of the complement of an automaton (automat.fsm/complement) is wrong, as it appears by inspecting the svg of the complement of a, or of the complement of a*. Currently the function builds the complement by "switching" final and nonfinal states. This works only for complete DFAs but not for incomplete DFAs or NFAs (see https://en.wikipedia.com/wiki/Deterministic_finite_automaton for definitions). In the general case the construction should be as follows:
1- first, if the automaton is nondeterministic, make it deterministic; call d the obtained deterministic automaton;
2- then, if d is incomplete, make it complete by adding a "refuse all" nonfinal state S, and transitions s -[DEF]->S from all the states s to S; call c the resulting automaton;
3- finally, the final and nonfinal states of c are switched. Note that this way S becomes an "accept all" state.
In the current implementation both step 1 and step 2 are missing.
I'm generating FSMs that use integers as inputs/signals, but they're actually character codes. (Doing this so range
and such are available.) I'd like to get characters on the visualizations' edges, but I don't see an easy way to do so. Looks like I could do unwise things to #'pprint-inputs
as a workaround, but maybe I'm missing something more straightforward?
I'm not sure if this is a bug, or just a confusion on my part about greedy-find vs find: The documentation is a little unclear, but it seems to suggest that the :checkpoint
value would be set by greedy-find
if a greedy match is found which skipped a non-greedy match.
For example with this automaton:
(def foo (a/compile
(a/or [:d :d (a/$ :first)]
[:d :d :g (a/$ :second)])
{:reducers {:first (fn [s t] :first)
:second (fn [s t] :second)}}))
I run e.g.:
boot.user=> (require '[automat.core :as a])
nil
boot.user=> (def foo (a/compile
#_=> (a/or [:d :d (a/$ :first)]
#_=> [:d :d :g (a/$ :second)])
#_=> {:reducers {:first (fn [s t] :first)
#_=> :second (fn [s t] :second)}}))
#'boot.user/foo
boot.user=> (a/find foo nil [:d :d])
#automat.compiler.core.CompiledAutomatonState{
:accepted? true, :checkpoint nil,
:state-index 2, :start-index 0,
:stream-index 2, :value :first}
boot.user=> (a/greedy-find foo nil [:d :d])
#automat.compiler.core.CompiledAutomatonState{
:accepted? true, :checkpoint nil,
:state-index 2, :start-index 0,
:stream-index 2, :value :first}
boot.user=> (a/find foo nil [:d :d :g])
#automat.compiler.core.CompiledAutomatonState{
:accepted? true, :checkpoint nil,
:state-index 2, :start-index 0,
:stream-index 2, :value :first}
boot.user=> (a/greedy-find foo nil [:d :d :g])
#automat.compiler.core.CompiledAutomatonState{
:accepted? true, :checkpoint -->nil<---,
:state-index 3, :start-index 0,
:stream-index 3, :value :second}
The first 3 calls are as expected. I'm not finding the full sequence [:d :d :g]
because either I didn't pass in enough tokens, or because I did a non-greedy find.
In the 4th example, I find the full pattern, and get the expected :value
. But should the :checkpoint
not have been set to the previous value? (I'm not sure what to expect here - perhaps the :first
symbol, or the state-index 2
, but certainly something to indicate that the most recent non-greedy find.)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.