julia> using Test: @test
julia> arg_is42(x) = x == 42
arg_is42 (generic function with 1 method)
julia> @test arg_is42(41) == false
Test Passed
Expression: arg_is42(41) == false
Evaluated: false == false
\( F_S \) is the group formed by \(\left\{ \text{reduced words on } S \right\}\) together with concatenation, meaning:
Complete code available on GitHub.
struct Symb
symb::Symbol
inverse::Bool
end
function reduce end # Reduces a vector of `Symb`.
struct Word
w::Vector{Symb}
Word(w) = new(reduce(w))
end
function concat end # Concatenates `Word`s.
macro word end # Convenience macro to construct `Word`s.
function Base.one end # Empty `Word` (identity element).
As easy as overloading the generate
method!
import JCheck: generate
function generate(rng, ::Type{Word}, n)
lens = rand(rng, 1:100, n)
symbols_vec = map(lens) do len
Symbol.(rand(rng, 'a':'c', len))
end
inverses_vec = map(len -> rand(rng, Bool, len), lens)
map(symbols_vec, inverses_vec) do symbols, inverses
Word(map(Symb, symbols, inverses))
end
end
Is our implementation a group? Let's try to disprove it with JCheck.jl!
julia> using JCheck: Quickcheck
julia> qc = Quickcheck("Free Group Over {a, b, c}")
Free Group over {a, b, c}: 0 predicate and 0 free variable.
function Base.inv(word::Word)
ret_vec = map(inv, word.w)
Word(reverse!(ret_vec))
end
pred_inverse(word) =
concat(word, inv(word)) ==
concat(inv(word), word) ==
one(Word)
julia> using JCheck: @add_predicate
julia> @add_predicate(qc,
"Inverse",
word::Word -> pred_inverse(word))
Free Group over {a, b, c}: 1 predicate and 1 free variable.
word::Word
julia> using JCheck: @quickcheck
julia> @quickcheck qc
Test Summary: | Pass Total
Test Inverse | 1 1
Test
module
using Test: @test, @testset
@testset "Group Properties" begin
@test (@word a b) == (@word a b b-1 b)
@quickcheck qc
end
Test Summary: | Pass Total
Group Properties | 2 2
\( F_S \) is not commutative in general 😟
pred_commute(w1, w2) = concat(w1, w2) == concat(w2, w1)
julia> @add_predicate(qc,
"Commutativity",
(word::Word, word2::Word) ->
pred_commute(word, word2))
Free Group Over {a, b, c}: 2 predicates and 2 free variables:
word::Word
word2::Word
┌ Warning: Predicate "Commutativity" does not hold for
| valuation (word = a-1 b a-1 b c-1 , word2 = c b-1 a )
└ @ JCheck [...]/Quickcheck.jl:310
[...]
Some predicates do not hold for some valuations; they have been saved to
JCheck_YYY-MM-DD_HH-MM-SS.jchk. Use function load and macro @getcases to explore problematic cases.
Test Summary: | Pass Fail Total
Group Properties | 2 1 3
Test Inverse | 1 1
Test Commutativity | 1 1
ERROR: Some tests did not pass: 2 passed, 1 failed, 0 errored, 0 broken.
JCheck
, but might be more convenient (Cartesian product of inputs, ...)
using JCheck: specialcases
specialcases(::Type{Word}) = [one(Word)]
JCheck
until input is non-failing
import JCheck: shrink, shrinkable
shrinkable(word::Word) = length(word) > 2
function shrink(word::Word)
shrinkable(word) || return [word]
n = length(word) ÷ 2
[word[1:n], word[(n+1):end]]
end
┌ Warning: Predicate "Commutativity" does not hold for
| valuation (word = c-1 , word2 = a b-1 )
└ @ JCheck [...]/Quickcheck.jl:310
┌ Warning: Predicate "Commutativity" does not hold for
| valuation (word = a c-1 , word2 = a-1 )
└ @ JCheck ~/Projets/JCheck/src/Quickcheck.jl:310
[...]
Much easier to analyze 😀
P. Fournier (STATQAM — UQAM) Blue JuliaCon 2022 (Online) July 28th, 2022