Files
mercury/extras/quickcheck/tutes/T6.html
David Overton 07038626c1 The following change was made by Peter Moulder <pmoulder@csse.monash.edu.au>.
Estimated hours taken: 2

The following change was made by Peter Moulder <pmoulder@csse.monash.edu.au>.

extras/quickcheck/tutes/*.html:

	Many HTML fixes.  (Previously there were enough problems that
	it made more sense to read them as plain text files than with
	a web browser.)

	The files now pass through weblint (cleanly) and tidy (only
	`table lacks "summary" attribute' warnings remain).

	A small number of textual changes, and also a couple of
	<!-- XXX ... --> comments where I suspect the text should be
	changed.
2002-02-24 23:28:49 +00:00

413 lines
13 KiB
HTML

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
<html>
<head>
<title>QuickCheck</title>
</head>
<body>
<p>
Files:
<a href="use62.m">use62.m</a>
<br>
<a href="index.html">Back to main</a>
<h1>QuickCheck Tutorial 6</h1>
<h2>Generators: Discriminated Union &amp; Specific Frequency</h2>
<p>
Default generator is able to generate discriminated unions provided that all types
in the body of the definition have default/custom generators. In default frequency
mode, all branches at each level have the same chance of being selected.
<table border=0 width="100%" bgcolor="#eeeee0"><tr><td><pre>
:- func rand_union(type_desc, list(frequency), list({type_desc,
list(frequency)}), list(user_gen_type), rnd, rnd) = univ.
:- mode rand_union(in,in,in,list_skel_in(user_gen_inst),in,out) = out is det.
</pre></tr></table>
use61.m shows the randomly generated value for the type bullet, with default
frequency :
<table border=0 width="100%" bgcolor="#eeeee0"><tr><td><pre>
:- module use61.
:- interface.
:- use_module io.
:- pred main(io__state, io__state).
:- mode main(di, uo) is det.
%---------------------------------------------------------------------------%
:- implementation.
:- import_module list.
:- import_module qcheck.
%---------------------------------------------------------------------------%
% arbitrary user-defined types for testing purposes
%---------------------------------------------------------------------------%
:- type bullet
---&gt; good(color)
; inaccurate(color)
; defective(color).
:- type color
---&gt; black
; white.
%---------------------------------------------------------------------------%
main --&gt;
qcheck(qcheck__f(prop1), "even distribution", 1000, [], []).
:- func prop1(bullet) = property.
prop1(X) = X `&gt;&gt;&gt;` [yes].
</pre></tr></table>
Sample output shows the expected distribution :
<pre>
Test Description : even distribution
Number of test cases that succeeded : 1000
Number of trivial tests : 0
Number of tests cases which failed the pre-condition : 0
Distributions of selected argument(s) :
150 inaccurate(white)
153 defective(black)
165 inaccurate(black)
176 good(white)
178 defective(white)
178 good(black)
</pre>
<h2>Specific Frequency</h2>
<p>Specific Frequency changes a term's default frequency (which is evenly spread)
to one the user has provided. General Frequency changes a type's default frequency
to one the user has provided. An example :
<pre>
:- func Invariant_Function_X(bullet, bullet) = property.
</pre>
<p>
Different SF can be passed to the first and second bullet. For example, the first
bullet can have 80% chance of being black, while the second argument has 20% chance
of being black. However there can only be one GF for each type.
The key advantage of Specific Frequency over General Frequency is that it allows
different frequencies for the same type, where GF doesn't allow.
The draw back is that SF only goes as deep (down the branches) as the user
defines it, and the amount of work blows up as the depth of branches increases.
<p>
Suppose there are two bullet manufacturers.
Company_W's bullets are painted black; 50% are good, 10% inaccurate, 40% defective.
Company_B's bullets are painted white; 40% are good, 30% inaccurate, 30% defective.
A good bullet always hits its target, inaccurate one misses 50% of time, defective bullet
always misses. And color does affect performance.
<!-- XXX: The above is probably a typo; should probably say "colour itself doesn't affect performance". -->
<table border=0 width="100%" bgcolor="#eeeee0"><tr><td><pre>
:- type frequency
---&gt; {int, list(list(frequency))}.
</pre></tr></table>
frequency defines the relative chance of a branch being selected, and gives information
of that branch's sub-branches.
list(frequency) contains distribution information about 1 discrimated union, ie: the list
must contain a frequency for each possible branch.
list(list(frequency)) contains distribution information about a list of discrimated unions.
<p>
Let's try to describe Company_W's bullet, Bullet is discrimated union, so the list is 3 length long :
<pre>
list(frequency)
</pre>
There are 3 top level branches for Type Bullet, so the list is 3 length long :
<pre>
[frequency_good, frequency_inaccurate, frequency_defective]
:- type frequency = {int, list(list(frequency))}.
frequency_good = {50, ...something_good...}
frequency_inaccurate = {10, ...something_inaccurate...}
frequency_defective = {40, ...something_defective...}
</pre>
<p>
Any int is a valid 1st argument of frequency. (Negative numbers are treated
by qcheck as zeros.)
<pre>
chance of good-bullet is 50 / (50 + 10 + 40)
the chance of inaccurate is 10 / (50 + 10 + 40)
the chance of defective is 40 / (50 + 10 + 40)
</pre>
<p>
Another example (for type bullet):
<pre>
:- type frequency = {int, list(list(frequency))}.
frequency_good = {5, ...something_good...}
frequency_inaccurate = {1, ...something_inaccurate...}
frequency_defective = {4, ...something_defective...}
the chance of good-bullet is 5 / (5 + 1 + 4)
the chance of inaccurate is 1 / (5 + 1 + 4)
the chance of defective is 4 / (5 + 1 + 4)
</pre>
<p>
In both examples, the result distribution is the same (i.e. 50% good,
10% inaccurate, 40% defective).
<p>
...something_good... has format list(list(frequency)), and should describe the argument(s) of good/1.
good/1 only has 1 arguments, thus the list of 1 element,
<pre>
[ info_color ]
</pre>
<p>
info_color has format list(frequency), color has 2 branches, thus this list is of 2 elements.
<pre>
[ frequency_black, frequency_white ]
:- type frequency = {int, list(list(frequency))}.
frequency_black = {100, ...something_black...}
frequency_white = {0, ...something_white...}
</pre>
<p>
something_black has format list(list(frequency)), and should describe the argument(s) of black/0.
black/0 has no argument, thus the list is [], likewise for white/0.
If instead of black/0, it's black/3, eg:
<pre>
:- type color
---&gt; black(paint_manufacturer, warranty_type, warranty_provider)
; white(paint_manufacturer, warranty_type, warranty_provider)
</pre>
Then you can either use [] to use default frequeny for generating paint_manufacturer, warranty_type,
and warranty_provider. Or you can specify a list of 3 element ; each element describing the frequency
of paint_manufacturer, warranty_type or warranty_provider.
<pre>
So far: info_color = [ frequency_black, frequency_white ]
= [ {100, []}, {0, []} ]
Then: frequency_good = {50, ...something_good...}
= {50, [ info_color ] }
= {50, [ [ {100, []}, {0, []} ] ] }
</pre>
<p>
in this case ...something_good..., ...something_inaccurate... and ...something_defective are the same,
since they all describe a list which contains Color that has the same distribution.
<pre>
So: frequency_good = {50, [ [ {100, []}, {0, []} ] ] }
frequency_inaccurate = {10, [ [ {100, []}, {0, []} ] ] }
frequency_defective = {40, [ [ {100, []}, {0, []} ] ] }
Then: [frequency_good, frequency_inaccurate, frequency_defective]
= [ {50, [ [ {100, []}, {0, []} ] ] },
{10, [ [ {100, []}, {0, []} ] ] },
{40, [ [ {100, []}, {0, []} ] ] }
]
</pre>
<p>
For Company_W's bullet, its list(frequency) would be :
<pre>
[frequency_good, frequency_inaccurate, frequency_defective]
= [ {40, [ [ {0, []}, {100, []} ] ] },
{30, [ [ {0, []}, {100, []} ] ] },
{30, [ [ {0, []}, {100, []} ] ] }
]
</pre>
<p>
The complete code (use62.m):
<table border=0 width="100%" bgcolor="#eeeee0"><tr><td><pre>
:- module use62.
:- interface.
:- use_module io.
:- pred main(io__state, io__state).
:- mode main(di, uo) is det.
%---------------------------------------------------------------------------%
:- implementation.
:- import_module int, list, string.
:- import_module qcheck, rnd.
%---------------------------------------------------------------------------%
% arbitrary user-defined types for testing purposes
%---------------------------------------------------------------------------%
:- type bullet
---&gt; good(color)
; inaccurate(color)
; defective(color).
:- type color
---&gt; black
; white.
%---------------------------------------------------------------------------%
main --&gt;
{ freq_B(B) },
{ freq_W(W) },
qcheck(qcheck__f(prop2), "bullet fight", 10000, [[],B,W], []).
:- pred freq_B(list(frequency)).
:- mode freq_B(out) is det.
freq_B(Out) :-
Out = [ {50, [ [ {100, []}, {0, []} ] ] },
{10, [ [ {100, []}, {0, []} ] ] },
{40, [ [ {100, []}, {0, []} ] ] }
].
:- pred freq_W(list(frequency)).
:- mode freq_W(out) is det.
freq_W(Out) :-
Out = [ {40, [ [ {0, []}, {100, []} ] ] },
{30, [ [ {0, []}, {100, []} ] ] },
{30, [ [ {0, []}, {100, []} ] ] }
].
:- func prop2(int, bullet, bullet) = property.
prop2(Seed, B, W) = fight(Seed, B, W) `&gt;&gt;&gt;`
({"ComB",B} `&gt;&gt;&gt;`
({"ComW", W} `&gt;&gt;&gt;` [yes])
).
:- func fight(int, bullet, bullet) = string.
:- mode fight(in, in, in) = out is det.
fight(Seed, B, W) = String :-
rnd__init(Seed, RS0),
B_hit = is_hit(B, RS0, RS1),
W_hit = is_hit(W, RS1, _),
(if B_hit = W_hit
then
String = "draw"
else if B_hit &gt; W_hit
then
String = "B win"
else
String = "W win"
).
:- func is_hit(bullet, rnd, rnd) = int.
:- mode is_hit(in, in, out) = out is det.
is_hit(Bullet, RS0, RS) = Int :-
Temp = rand_allint(RS0, RS) rem 2,
(
Bullet = good(_),
Int = 1
;
Bullet = inaccurate(_),
(if Temp = 0
then
Int = 1
else
Int = 0
)
;
Bullet = defective(_),
Int = 0
).
</pre></tr></table>
In use62.m
<pre>
main --&gt;
{ freq_B(B) },
{ freq_W(W) },
qcheck(qcheck__f(prop2), "bullet fight", 10000, [[],B,W], []).
</pre>
The 4th argument of qcheck/7 is for passing Specific Frequency. Because the
invariant function has three input arguments, qcheck/7 's 4th argument must
be list of 3.
[[],B,W]
<p>
The first argument of prop2/3 is of type int, and I've passed [] as
it's SF. When qcheck is trying to generate that int, it will completely
ignore the [] since an int is not a discriminated union. In that sense,
one can replace that [] with anything, as long as it's the correct format ;
ie, a list(frequency). However the presence of [] will allow qcheck to
recognize that [] is for the first argument, B is for the second argument and
W is for the third argument.
<p>
A sample output:
<pre>
Test Description : bullet fight
Number of test cases that succeeded : 10000
Number of trivial tests : 0
Number of tests cases which failed the pre-condition : 0
Distributions of selected argument(s) :
909 {"ComB", inaccurate(black)}
2403 "B win"
2533 "W win"
2949 {"ComW", defective(white)}
3012 {"ComW", inaccurate(white)}
4017 {"ComB", defective(black)}
4039 {"ComW", good(white)}
5064 "draw"
5074 {"ComB", good(black)}
</pre>
Regroup the output to make comparison :
<pre>
5074 {"ComB", good(black)
909 {"ComB", inaccurate(black)}
4017 {"ComB", defective(black)}
4039 {"ComW", good(white)}
3012 {"ComW", inaccurate(white)}
2949 {"ComW", defective(white)}
</pre>
<p>
Note that ComB only makes black bullet; ComW only white. And their bullet quality is
what was expected of them.
<pre>
2403 "B win"
2533 "W win"
5064 "draw"
</pre>
<p>
Walk through in generating a Company_B 's bullet :
<ol>
<li> The program first enters the generator with
<pre>
SF = [ {50, [ [ {100, []}, {0, []} ] ] },
{10, [ [ {100, []}, {0, []} ] ] },
{40, [ [ {100, []}, {0, []} ] ] }
].
</pre>
<li>Suppose the 3rd branch is selected, then qcheck will extract
<tt>[ {100, []}, {0, []} ]</tt> from <tt>{40, [ [ {100, []}, {0, []} ] ] }</tt>.
<li> It then calls the generator with SF = [ {100, []}, {0, []} ]
<li> So qcheck enters generator for the sub-branch (for color) with
SF = [ {100, []}, {0, []} ]
<li> Suppose the 1st branch is selected, then qcheck will extract []
from {100, []}
<li> Since constructor black/0 has no argument, the program will stop
the recursive call.
</ol>
</body>
</html>