Hi Nico

Thanks for your advice.

* The `size` param in `gen-hierarchy` is being passed as the first param to 
> `run`, which limits the count of results returned by core.logic. What's the 
> intention there? I think it's most common to grab one result from run, or 
> all of them (to check if it always generates correct solutions, for 
> example), but not something in the middle, nevertheless based on the size 
> of the test.check iteration.


Agreed. I should allow 'run' to return all results and rather let 
test.check reduce the returned set.

* I think that your generator should generate a graph and a starting point. 
> The traversal could be generated in the body of the `for-all`, and some 
> relation between the graph, the starting point and the traversal should be 
> asserted there. Here's a draft example:


I was generating the starting point independently with a,

> (gen/not-empty (gen/int))


I'm not sure what the size means with regard to the graph and the starting 
> point. This is important to think about so as to get a better shrinking 
> when there's a failure. The graph generator should implement the shrinking 
> strategy, I think... But all of that should not be strictly necessary to 
> have something working.


Worrying about shrinking may be a bit past my current skill level with 
test.check, maybe as the next stage. :-)

I will refactor my solution to take into account some of your points above 
and see if I have any more success.

Thanks again for your help Nico. 

On Wednesday, 10 December 2014 06:07:26 UTC+2, Nicolás Berger wrote:
>
> Hey Cliff,
>
> A couple comments:
>
> * The `size` param in `gen-hierarchy` is being passed as the first param 
> to `run`, which limits the count of results returned by core.logic. What's 
> the intention there? I think it's most common to grab one result from run, 
> or all of them (to check if it always generates correct solutions, for 
> example), but not something in the middle, nevertheless based on the size 
> of the test.check iteration.
>
> * I think that your generator should generate a graph and a starting 
> point. The traversal could be generated in the body of the `for-all`, and 
> some relation between the graph, the starting point and the traversal 
> should be asserted there. Here's a draft example:
>
> ```
> (defn gen-graph
>   "Should return a real graph. Node count could be given by size"
>   [size]
>   {1 [2 3 4]
>    2 [5 6 7]
>    6 [8 9]
>    10 [11 12 13]})
>
> (defn traverse-graph
>   [graph start]
>   #{3 4 5 7 8 9})
>
> (defn gen-graph-and-starting-point
>  [size]
>  (gen/bind gen-graph
>            #(gen/tuple (gen/return %) (gen/elements (keys %)))))
>
> (defspec graph-traversal
>   (prop/for-all [[graph start] gen-graph-and-starting-point]
>     (let [traversal (traverse-graph graph start)]
>       ; every element in traversal is a node in the graph 
>       (is (every? #(in-graph? graph %) traversal))
>       ; start is not part of traversal 
>       (is (not (traversal start)))
>       ; some other property?
>       (is (some-other-property? graph start traversal)))))
> ```
>
> I'm not sure what the size means with regard to the graph and the starting 
> point. This is important to think about so as to get a better shrinking 
> when there's a failure. The graph generator should implement the shrinking 
> strategy, I think... But all of that should not be strictly necessary to 
> have something working.
>
> Hope it helps.
>
> Nico
>
> On Tue, Dec 9, 2014 at 3:50 AM, cliff <clifford...@gmail.com <javascript:>
> > wrote:
>
>> Hi Gary
>>
>> I have tried your suggestion but I fear there is a deeper problem.
>>
>> Thanks
>> Cliff
>>
>> On Monday, 8 December 2014 12:03:47 UTC+2, Gary Verhaegen wrote:
>>>
>>> I haven't touched test.check yet, si this might be completely off the 
>>> mark, but based on my limited understanding, here's what I think happens.
>>>
>>> for-all probably removes one level of nesting from your generator, which 
>>> means that bindings is bound to a seq with one element, which is a map. 
>>> Then, keys in the (let [ks (into #{} (keys bindings))]... Form turns its 
>>> argument into a seq, which is easy as it is already one, and then tries to 
>>> map the key function on the result. The key function expects a Map.Entry, 
>>> but here the elements of the seq are (or actually is, as there is only one) 
>>> maps, so it blows up. Easiest way to test (I'm on my phone, emse I'd have 
>>> done it) this hypothesis would be to turn the into for into (into #{} (keys 
>>> (first bindings))).
>>>
>>> Once confirmed, I would suggest getting ris of that extra level of 
>>> nesting earlier, for example inside your ->maps fn (mapcat identity should 
>>> do the trick).
>>>
>>> On Monday, 8 December 2014, cig <clifford...@gmail.com> wrote:
>>>
>>>> Hi
>>>>
>>>> I would like to test a function which recursively traverses the nodes 
>>>> in a graph and collects them. For example,
>>>>
>>>> (def graph {1 [2 3 4]
>>>>                    2 [5 6 7]
>>>>                    6 [8 9]
>>>>                    10 [11 12 13]}
>>>>
>>>> my function is given a starting point say, 1 and should then traverse 
>>>> each node which is reachable and return the set. In this case the result 
>>>> should be:
>>>> #{3, 4, 5, 7, 8, 9}
>>>>
>>>> note: it does not return any elements reachable by 10.
>>>>
>>>> I would like to test this using test.check, but I would like to 
>>>> generate test data which will exercise the traversal of the graph.
>>>>
>>>> I found a similar thread here: https://groups.google.com/
>>>> forum/#!topic/clojure/YWeT8BFc8k4
>>>>
>>>> But, I don't think the proposed solution would suit this use case. So, 
>>>> I tried generating a graph with relations using core.logic
>>>>
>>>> (defn ->maps
>>>>>
>>>>>   "take the output of run* and convert it into sequence of maps"
>>>>>   [q]
>>>>>   (let [r (->> q
>>>>>                (partition 2)
>>>>>                (map (fn [[k v]] {k (apply vector v)}))
>>>>>                (apply merge))]
>>>>>    r ))
>>>>> (defn gen-hierarchy
>>>>>   "generate a related hierarchy"
>>>>>   [size]
>>>>>   (let [vars1 (->> (repeatedly 7 lvar) (into []))
>>>>>         vars2 (->> (repeatedly 7 lvar) (into []))
>>>>>         vars3 (->> (repeatedly 7 lvar) (into []))]
>>>>>     (->>
>>>>>      (run size [q]
>>>>>           (fresh [?k1 ?k2 ?k3 ?v1 ?v2 ?v3 ?a]
>>>>>                  (fd/distinct vars1)
>>>>>                  (everyg #(fd/in % (fd/interval 1 9)) vars1)
>>>>>                  (fd/in ?k1 (fd/interval 1 9))
>>>>>                  (rembero ?k1 vars1 ?v1)
>>>>>                  (membero ?k2 ?v1)
>>>>>                  (fd/distinct vars2)
>>>>>                  (everyg #(fd/in % (fd/interval 1 9)) vars2)
>>>>>                  (rembero ?k2 vars2 ?v2)
>>>>>                  (membero ?k3 ?v2)
>>>>>                  (fd/distinct vars3)
>>>>>                  (everyg #(fd/in % (fd/interval 1 9)) vars3)
>>>>>                  (rembero ?k3 vars3 ?v3)
>>>>>                  (appendo [?k1 ?v1] [?k2 ?v2] ?a)
>>>>>                  (appendo ?a [?k3 ?v3] q)))
>>>>>      (map ->maps)))) 
>>>>>
>>>>>
>>>> Hooking this into test.check. I tried the following: 
>>>>
>>>> (defn gen-port-hierarchy []
>>>>>   (gen/sized (fn [size]
>>>>>                (gen/fmap #(gen-hierarchy %) (gen/return size)))))
>>>>> (gen/sample (gen/not-empty (gen-port-hierarchy)) 1)
>>>>
>>>>
>>>> Which does produce more or less what I'm after:
>>>>
>>>> (({6 [2 3 4 5 7 1], 3 [6 7 1 2 4 5], 1 [3 2 4 5 6 7]})
>>>>  ({5 [1 2 3 4 6 7], 7 [5 3 4 6 1 2], 1 [7 2 3 4 5 6]})) 
>>>>
>>>> However, when I try use this in a spec:
>>>>
>>>> (prop/for-all [bindings (gen/not-empty (gen-port-hierarchy))] 
>>>>
>>>>   (let [ ks (into #{} (keys bindings))] ...)
>>>>
>>>>
>>>> I seem to be getting back a LazySeq which then leads to a 
>>>>  ClassCastException:
>>>>
>>>> java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot 
>>>> be cast to java.util.Map$Entry
>>>>
>>>> Am I on the completely wrong path here? 
>>>> Or have I incorrectly hooked this generator up with test.check?
>>>>
>>>> Any help would be very appreciated.
>>>>
>>>>  -- 
>>>> You received this message because you are subscribed to the Google
>>>> Groups "Clojure" group.
>>>> To post to this group, send email to clojure@googlegroups.com
>>>> Note that posts from new members are moderated - please be patient with 
>>>> your first post.
>>>> To unsubscribe from this group, send email to
>>>> clojure+unsubscr...@googlegroups.com
>>>> For more options, visit this group at
>>>> http://groups.google.com/group/clojure?hl=en
>>>> --- 
>>>> You received this message because you are subscribed to the Google 
>>>> Groups "Clojure" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send 
>>>> an email to clojure+unsubscr...@googlegroups.com.
>>>> For more options, visit https://groups.google.com/d/optout.
>>>>
>>>  -- 
>> You received this message because you are subscribed to the Google
>> Groups "Clojure" group.
>> To post to this group, send email to clo...@googlegroups.com 
>> <javascript:>
>> Note that posts from new members are moderated - please be patient with 
>> your first post.
>> To unsubscribe from this group, send email to
>> clojure+u...@googlegroups.com <javascript:>
>> For more options, visit this group at
>> http://groups.google.com/group/clojure?hl=en
>> --- 
>> You received this message because you are subscribed to the Google Groups 
>> "Clojure" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to clojure+u...@googlegroups.com <javascript:>.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to