hypothesis-specs 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e8eb07704e06280a79469b76b6b7267cf356797
4
- data.tar.gz: edd8bb7476a3a84dedf2751016385dbe2d6afcac
3
+ metadata.gz: 2e49a69e84d999619c8b73873bc8dc92f4e9e534
4
+ data.tar.gz: d51bc24f3c230ec37a4a19ddcbd43ca5532646ee
5
5
  SHA512:
6
- metadata.gz: 8e72ef276034266146155092d14827e75f5f20ec3986dd57245f94694e1f7da09d5cc62b74fced3829e0d8e5dbff522ad44d6fc88e2e49d274979eaae3f06e0e
7
- data.tar.gz: b6917a3a24f98febffaab647ecec0393827e3f116390762d3163cd9d2fe7e1cac49cfd79bc2db1e448beb044e862b0c96c79a5e3e8d7aed986ff718eef755d40
6
+ metadata.gz: 1fc1e55701ebf1b24184f243ee232eb4528d85d6306fbe83e95bfd37a0f6f1328077181496620214f3674a2e3f8063bd628178539cbf3eca5af28a7dea86e1a1
7
+ data.tar.gz: 87772f4660cdef5a8a93df5eb81d2322896c58536d3aff5f2ea9a93ec973b83c0ee99c26b4e08467a91af7948dc46ec73a556cbdc3af3a725f075bcdd2b1e148
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## Hypothesis for Ruby 0.0.10 (2018-04-26)
2
+
3
+ This release is another update to shrinking:
4
+
5
+ * Cases where the value may be simplified without necessarily
6
+ becoming smaller will have better results.
7
+ * Duplicated values can now sometimes be simultaneously shrunk.
8
+
1
9
  ## Hypothesis for Ruby 0.0.9 (2018-04-20)
2
10
 
3
11
  This improves Hypothesis for Ruby's shrinking to be much closer
data/Rakefile CHANGED
@@ -16,7 +16,11 @@ begin
16
16
  t.verbose = true
17
17
  end
18
18
 
19
- task test: %i[build spec minitests]
19
+ task :rust_tests do
20
+ sh 'cargo test'
21
+ end
22
+
23
+ task test: %i[build spec minitests rust_tests]
20
24
  rescue LoadError
21
25
  end
22
26
 
data/src/data.rs CHANGED
@@ -39,6 +39,7 @@ pub struct Draw {
39
39
  pub struct DataSource {
40
40
  bitgenerator: BitGenerator,
41
41
  record: DataStream,
42
+ sizes: Vec<u64>,
42
43
  draws: Vec<DrawInProgress>,
43
44
  draw_stack: Vec<usize>,
44
45
  written_indices: HashSet<usize>,
@@ -49,6 +50,7 @@ impl DataSource {
49
50
  return DataSource {
50
51
  bitgenerator: generator,
51
52
  record: DataStream::new(),
53
+ sizes: Vec::new(),
52
54
  draws: Vec::new(),
53
55
  draw_stack: Vec::new(),
54
56
  written_indices: HashSet::new(),
@@ -88,6 +90,7 @@ impl DataSource {
88
90
  match self.bitgenerator {
89
91
  BitGenerator::Recorded(ref mut v) if self.record.len() >= v.len() => Err(FailedDraw),
90
92
  _ => {
93
+ self.sizes.push(0);
91
94
  self.record.push(value);
92
95
  Ok(())
93
96
  }
@@ -95,6 +98,7 @@ impl DataSource {
95
98
  }
96
99
 
97
100
  pub fn bits(&mut self, n_bits: u64) -> Result<u64, FailedDraw> {
101
+ self.sizes.push(n_bits);
98
102
  let mut result = match self.bitgenerator {
99
103
  BitGenerator::Random(ref mut random) => random.next_u64(),
100
104
  BitGenerator::Recorded(ref mut v) => if self.record.len() >= v.len() {
@@ -119,6 +123,7 @@ impl DataSource {
119
123
  record: self.record,
120
124
  status: status,
121
125
  written_indices: self.written_indices,
126
+ sizes: self.sizes,
122
127
  draws: self.draws
123
128
  .drain(..)
124
129
  .filter_map(|d| match d {
@@ -174,5 +179,6 @@ pub struct TestResult {
174
179
  pub record: DataStream,
175
180
  pub status: Status,
176
181
  pub draws: Vec<Draw>,
182
+ pub sizes: Vec<u64>,
177
183
  pub written_indices: HashSet<usize>,
178
184
  }
data/src/distributions.rs CHANGED
@@ -1,8 +1,8 @@
1
1
  use data::{DataSource, FailedDraw};
2
2
 
3
+ use std::cmp::{Ord, Ordering, PartialOrd, Reverse};
3
4
  use std::collections::BinaryHeap;
4
5
  use std::mem;
5
- use std::cmp::{Ord, Ordering, PartialOrd, Reverse};
6
6
 
7
7
  use std::u64::MAX as MAX64;
8
8
 
@@ -216,16 +216,71 @@ impl Sampler {
216
216
  }
217
217
 
218
218
  pub fn good_bitlengths() -> Sampler {
219
- let weights = vec!(
220
- 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, // 1 byte
221
- 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, // 2 bytes
222
- 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 3 bytes
223
- 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, // 4 bytes
224
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 5 bytes
225
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 6 bytes
226
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 7 bytes
227
- 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 8 bytes (last bit spare for sign)
228
- );
219
+ let weights = vec![
220
+ 4.0,
221
+ 4.0,
222
+ 4.0,
223
+ 4.0,
224
+ 4.0,
225
+ 4.0,
226
+ 4.0,
227
+ 4.0, // 1 byte
228
+ 2.0,
229
+ 2.0,
230
+ 2.0,
231
+ 2.0,
232
+ 2.0,
233
+ 2.0,
234
+ 2.0,
235
+ 2.0, // 2 bytes
236
+ 1.0,
237
+ 1.0,
238
+ 1.0,
239
+ 1.0,
240
+ 1.0,
241
+ 1.0,
242
+ 1.0,
243
+ 1.0, // 3 bytes
244
+ 0.5,
245
+ 0.5,
246
+ 0.5,
247
+ 0.5,
248
+ 0.5,
249
+ 0.5,
250
+ 0.5,
251
+ 0.5, // 4 bytes
252
+ 0.1,
253
+ 0.1,
254
+ 0.1,
255
+ 0.1,
256
+ 0.1,
257
+ 0.1,
258
+ 0.1,
259
+ 0.1, // 5 bytes
260
+ 0.1,
261
+ 0.1,
262
+ 0.1,
263
+ 0.1,
264
+ 0.1,
265
+ 0.1,
266
+ 0.1,
267
+ 0.1, // 6 bytes
268
+ 0.1,
269
+ 0.1,
270
+ 0.1,
271
+ 0.1,
272
+ 0.1,
273
+ 0.1,
274
+ 0.1,
275
+ 0.1, // 7 bytes
276
+ 0.1,
277
+ 0.1,
278
+ 0.1,
279
+ 0.1,
280
+ 0.1,
281
+ 0.1,
282
+ 0.1, // 8 bytes (last bit spare for sign)
283
+ ];
229
284
  assert!(weights.len() == 63);
230
285
  Sampler::new(weights)
231
286
  }
data/src/engine.rs CHANGED
@@ -3,11 +3,14 @@
3
3
 
4
4
  use rand::{ChaChaRng, Rng, SeedableRng};
5
5
 
6
+ use std::cmp::Reverse;
7
+ use std::collections::HashMap;
8
+ use std::mem;
6
9
  use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
7
10
  use std::thread;
8
- use std::mem;
9
11
 
10
12
  use data::{DataSource, DataStream, Status, TestResult};
13
+ use intminimize::minimize_integer;
11
14
 
12
15
  #[derive(Debug, Clone)]
13
16
  enum LoopExitReason {
@@ -153,7 +156,8 @@ where
153
156
  while prev != self.changes {
154
157
  prev = self.changes;
155
158
  self.adaptive_delete()?;
156
- self.binary_search_blocks()?;
159
+ self.minimize_individual_blocks()?;
160
+ self.minimize_duplicated_blocks()?;
157
161
  if prev == self.changes {
158
162
  self.expensive_passes_enabled = true;
159
163
  }
@@ -354,27 +358,14 @@ where
354
358
  }
355
359
  }
356
360
 
357
- fn binary_search_blocks(&mut self) -> StepResult {
361
+ fn minimize_individual_blocks(&mut self) -> StepResult {
358
362
  let mut i = 0;
359
363
 
360
364
  while i < self.shrink_target.record.len() {
361
- let mut hi = self.shrink_target.record[i];
362
-
363
- if hi > 0 && !self.shrink_target.written_indices.contains(&i) {
364
- let zeroed = self.try_lowering_value(i, 0)?;
365
- if !zeroed {
366
- let mut lo = 0;
367
- // Binary search to find the smallest value we can
368
- // replace this with.
369
- while lo + 1 < hi {
370
- let mid = lo + (hi - lo) / 2;
371
- if self.try_lowering_value(i, mid)? {
372
- hi = mid;
373
- } else {
374
- lo = mid;
375
- }
376
- }
377
- }
365
+ if !self.shrink_target.written_indices.contains(&i) {
366
+ minimize_integer(self.shrink_target.record[i], |v| {
367
+ self.try_lowering_value(i, v)
368
+ })?;
378
369
  }
379
370
 
380
371
  i += 1;
@@ -383,6 +374,61 @@ where
383
374
  Ok(())
384
375
  }
385
376
 
377
+ fn calc_duplicates(&self) -> Vec<Vec<usize>> {
378
+ assert!(self.shrink_target.record.len() == self.shrink_target.sizes.len());
379
+ let mut duplicates: HashMap<(u64, u64), Vec<usize>> = HashMap::new();
380
+ for (i, (u, v)) in self.shrink_target
381
+ .record
382
+ .iter()
383
+ .zip(self.shrink_target.sizes.iter())
384
+ .enumerate()
385
+ {
386
+ if !self.shrink_target.written_indices.contains(&i) {
387
+ duplicates
388
+ .entry((*u, *v))
389
+ .or_insert_with(|| Vec::new())
390
+ .push(i);
391
+ }
392
+ }
393
+
394
+ let mut result: Vec<Vec<usize>> = duplicates
395
+ .drain()
396
+ .filter_map(|(_, elements)| {
397
+ if elements.len() > 1 {
398
+ Some(elements)
399
+ } else {
400
+ None
401
+ }
402
+ })
403
+ .collect();
404
+ result.sort_by_key(|v| Reverse(v.len()));
405
+ result
406
+ }
407
+
408
+ fn minimize_duplicated_blocks(&mut self) -> StepResult {
409
+ let mut i = 0;
410
+ let mut targets = self.calc_duplicates();
411
+ while i < targets.len() {
412
+ let target = mem::replace(&mut targets[i], Vec::new());
413
+ i += 1;
414
+ assert!(target.len() > 0);
415
+ let v = self.shrink_target.record[target[0]];
416
+ let base = self.shrink_target.record.clone();
417
+
418
+ let w = minimize_integer(v, |t| {
419
+ let mut attempt = base.clone();
420
+ for i in &target {
421
+ attempt[*i] = t
422
+ }
423
+ self.incorporate(&attempt)
424
+ })?;
425
+ if w != v {
426
+ targets = self.calc_duplicates();
427
+ }
428
+ }
429
+ Ok(())
430
+ }
431
+
386
432
  fn execute(&mut self, buf: &DataStream) -> Result<(bool, TestResult), LoopExitReason> {
387
433
  // TODO: Later there will be caching here
388
434
  let result = self.main_loop.execute(DataSource::from_vec(buf.clone()))?;
@@ -0,0 +1,149 @@
1
+ use std::cmp::min;
2
+
3
+ const SMALL: u64 = 5;
4
+
5
+ struct Minimizer<'a, F: 'a> {
6
+ criterion: &'a mut F,
7
+ best: u64,
8
+ }
9
+
10
+ impl<'a, F, T> Minimizer<'a, F>
11
+ where
12
+ F: 'a + FnMut(u64) -> Result<bool, T>,
13
+ {
14
+ fn test(&mut self, candidate: u64) -> Result<bool, T> {
15
+ if candidate == self.best {
16
+ return Ok(true);
17
+ }
18
+ if candidate > self.best {
19
+ return Ok(false);
20
+ }
21
+ let result = (self.criterion)(candidate)?;
22
+ if result {
23
+ self.best = candidate;
24
+ }
25
+ Ok(result)
26
+ }
27
+
28
+ fn modify<G>(&mut self, g: G) -> Result<bool, T>
29
+ where
30
+ G: Fn(u64) -> u64,
31
+ {
32
+ let x = g(self.best);
33
+ self.test(x)
34
+ }
35
+ }
36
+
37
+ pub fn minimize_integer<F, T>(start: u64, mut criterion: F) -> Result<u64, T>
38
+ where
39
+ F: FnMut(u64) -> Result<bool, T>,
40
+ {
41
+ if start == 0 {
42
+ return Ok(start);
43
+ }
44
+
45
+ for i in 0..min(start, SMALL) {
46
+ if criterion(i)? {
47
+ return Ok(i);
48
+ }
49
+ }
50
+ if start <= SMALL {
51
+ return Ok(start);
52
+ }
53
+
54
+ let mut minimizer = Minimizer {
55
+ best: start,
56
+ criterion: &mut criterion,
57
+ };
58
+
59
+ loop {
60
+ if !minimizer.modify(|x| x >> 1)? {
61
+ break;
62
+ }
63
+ }
64
+
65
+ for i in 0..64 {
66
+ minimizer.modify(|x| x ^ (1 << i))?;
67
+ }
68
+
69
+ assert!(minimizer.best >= SMALL);
70
+
71
+ for i in 0..64 {
72
+ let left_mask = 1 << i;
73
+ let mut right_mask = left_mask >> 1;
74
+ while right_mask != 0 {
75
+ minimizer.modify(|x| {
76
+ if x & left_mask == 0 || x & right_mask != 0 {
77
+ x
78
+ } else {
79
+ x ^ (right_mask | left_mask)
80
+ }
81
+ })?;
82
+ right_mask >>= 1;
83
+ }
84
+ }
85
+
86
+ if !minimizer.modify(|x| x - 1)? {
87
+ return Ok(minimizer.best);
88
+ }
89
+
90
+ let mut lo = 0;
91
+ let mut hi = minimizer.best;
92
+ while lo + 1 < hi {
93
+ let mid = lo + (hi - lo) / 2;
94
+ if minimizer.test(mid)? {
95
+ hi = mid;
96
+ } else {
97
+ lo = mid;
98
+ }
99
+ }
100
+
101
+ Ok(minimizer.best)
102
+ }
103
+
104
+ #[cfg(test)]
105
+ mod tests {
106
+ use super::*;
107
+
108
+ fn non_failing_minimize<F>(start: u64, criterion: F) -> u64
109
+ where
110
+ F: Fn(u64) -> bool,
111
+ {
112
+ let mut best = start;
113
+
114
+ loop {
115
+ let ran: Result<u64, ()> = minimize_integer(best, |x| Ok(criterion(x)));
116
+ let result = ran.unwrap();
117
+ assert!(result <= best);
118
+ if result == best {
119
+ return best;
120
+ }
121
+ best = result;
122
+ }
123
+ }
124
+
125
+ #[test]
126
+ fn minimize_down_to() {
127
+ let n = non_failing_minimize(100, |x| x >= 10);
128
+ assert_eq!(n, 10);
129
+ }
130
+
131
+ #[test]
132
+ fn unset_relevant_bits() {
133
+ let x = 0b101010101010;
134
+ let y = 0b111111111111;
135
+ let n = non_failing_minimize(y, |k| k & x == x);
136
+ assert_eq!(n, x);
137
+ }
138
+
139
+ #[test]
140
+ fn sort_bits() {
141
+ let x: u64 = 0b1011011011000111;
142
+ let y: u64 = 0b0000001111111111;
143
+ let c = x.count_ones();
144
+ assert_eq!(c, y.count_ones());
145
+
146
+ let n = non_failing_minimize(x, |k| k.count_ones() == c);
147
+ assert_eq!(y, n);
148
+ }
149
+ }
data/src/lib.rs CHANGED
@@ -12,15 +12,16 @@ extern crate core;
12
12
  extern crate helix;
13
13
  extern crate rand;
14
14
 
15
- mod engine;
16
15
  mod data;
17
16
  mod distributions;
17
+ mod engine;
18
+ mod intminimize;
18
19
 
19
20
  use std::mem;
20
21
 
21
- use engine::Engine;
22
22
  use data::{DataSource, Status};
23
23
  use distributions::Repeat;
24
+ use engine::Engine;
24
25
 
25
26
  ruby! {
26
27
  class HypothesisCoreDataSource {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hypothesis-specs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - David R. Maciver
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-20 00:00:00.000000000 Z
11
+ date: 2018-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: helix_runtime
@@ -70,6 +70,7 @@ files:
70
70
  - src/data.rs
71
71
  - src/distributions.rs
72
72
  - src/engine.rs
73
+ - src/intminimize.rs
73
74
  - src/lib.rs
74
75
  homepage: http://github.com/HypothesisWorks/hypothesis-ruby
75
76
  licenses: