1use super::guard::{self, GuardValue};
53use super::ir::{Assignment, ConstraintEvalError, Expr, StateId, Ty, VarId};
54use super::rng;
55use num_bigint::{BigInt, BigUint};
56use num_traits::{ToPrimitive, Zero};
57
58pub type RealVarId = u32;
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
64pub enum FloatKind {
65 F64,
67 F32,
69}
70
71impl FloatKind {
72 pub fn max_idx(self) -> i64 {
74 match self {
75 FloatKind::F64 => 0x7fef_ffff_ffff_ffff,
76 FloatKind::F32 => 0x7f7f_ffff,
77 }
78 }
79
80 pub fn max_val(self) -> f64 {
82 self.val(self.max_idx())
83 }
84
85 pub fn idx(self, x: f64) -> i64 {
88 match self {
89 FloatKind::F64 => {
90 let b = x.to_bits();
91 let m = (b & 0x7fff_ffff_ffff_ffff) as i64;
92 if b >> 63 == 1 { -m } else { m }
93 }
94 FloatKind::F32 => {
95 let b = (x as f32).to_bits();
96 let m = i64::from(b & 0x7fff_ffff);
97 if b >> 31 == 1 { -m } else { m }
98 }
99 }
100 }
101
102 pub fn val(self, i: i64) -> f64 {
104 match self {
105 FloatKind::F64 => {
106 if i >= 0 {
107 f64::from_bits(i as u64)
108 } else {
109 f64::from_bits(i.unsigned_abs() | (1 << 63))
110 }
111 }
112 FloatKind::F32 => {
113 if i >= 0 {
114 f64::from(f32::from_bits(i as u32))
115 } else {
116 f64::from(f32::from_bits(i.unsigned_abs() as u32 | (1 << 31)))
117 }
118 }
119 }
120 }
121
122 pub fn round(self, x: f64) -> f64 {
124 match self {
125 FloatKind::F64 => x,
126 FloatKind::F32 => f64::from(x as f32),
127 }
128 }
129
130 pub fn ceil_idx(self, x: f64) -> Option<i64> {
133 if x.is_nan() || x > self.max_val() {
134 return None;
135 }
136 if x <= -self.max_val() {
137 return Some(-self.max_idx());
138 }
139 let r = self.round(x);
140 let i = self.idx(r);
141 Some(if r < x { i + 1 } else { i })
142 }
143
144 pub fn floor_idx(self, x: f64) -> Option<i64> {
146 if x.is_nan() || x < -self.max_val() {
147 return None;
148 }
149 if x >= self.max_val() {
150 return Some(self.max_idx());
151 }
152 let r = self.round(x);
153 let i = self.idx(r);
154 Some(if r > x { i - 1 } else { i })
155 }
156
157 pub fn from_bits(self, w: &BigUint) -> f64 {
160 let b = w.to_u64().unwrap_or(0);
161 match self {
162 FloatKind::F64 => f64::from_bits(b),
163 FloatKind::F32 => f64::from(f32::from_bits(b as u32)),
164 }
165 }
166
167 pub fn to_bits(self, x: f64) -> u64 {
169 match self {
170 FloatKind::F64 => x.to_bits(),
171 FloatKind::F32 => u64::from((x as f32).to_bits()),
172 }
173 }
174
175 pub fn width(self) -> u32 {
177 match self {
178 FloatKind::F64 => 64,
179 FloatKind::F32 => 32,
180 }
181 }
182}
183
184#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
187pub struct RealSet {
188 kind: FloatKind,
189 iv: Vec<(i64, i64)>,
190}
191
192fn unit(seed: &mut u64) -> f64 {
194 (rng::draw(seed) >> 11) as f64 / (1u64 << 53) as f64
195}
196
197impl RealSet {
198 pub fn empty(kind: FloatKind) -> RealSet {
200 RealSet {
201 kind,
202 iv: Vec::new(),
203 }
204 }
205
206 pub fn full(kind: FloatKind) -> RealSet {
208 RealSet {
209 kind,
210 iv: vec![(-kind.max_idx(), kind.max_idx())],
211 }
212 }
213
214 pub fn from_idx(kind: FloatKind, lo: i64, hi: i64) -> RealSet {
217 let (lo, hi) = (lo.max(-kind.max_idx()), hi.min(kind.max_idx()));
218 RealSet {
219 kind,
220 iv: if lo <= hi { vec![(lo, hi)] } else { Vec::new() },
221 }
222 }
223
224 pub fn range(kind: FloatKind, lo: f64, hi: f64) -> RealSet {
227 match (kind.ceil_idx(lo), kind.floor_idx(hi)) {
228 (Some(a), Some(b)) => RealSet::from_idx(kind, a, b),
229 _ => RealSet::empty(kind),
230 }
231 }
232
233 pub fn point(kind: FloatKind, x: f64) -> RealSet {
236 if x.is_nan() || x.abs() > kind.max_val() || kind.round(x) != x {
237 return RealSet::empty(kind);
238 }
239 let i = kind.idx(x);
240 RealSet::from_idx(kind, i, i)
241 }
242
243 pub fn kind(&self) -> FloatKind {
245 self.kind
246 }
247
248 pub fn is_empty(&self) -> bool {
250 self.iv.is_empty()
251 }
252
253 pub fn contains(&self, x: f64) -> bool {
255 if x.is_nan() || x.abs() > self.kind.max_val() {
256 return false;
257 }
258 let i = self.kind.idx(x);
259 self.iv.iter().any(|&(a, b)| a <= i && i <= b)
260 }
261
262 pub fn intervals(&self) -> &[(i64, i64)] {
264 &self.iv
265 }
266
267 fn normalised(kind: FloatKind, mut iv: Vec<(i64, i64)>) -> RealSet {
268 iv.sort_unstable();
269 let mut out: Vec<(i64, i64)> = Vec::with_capacity(iv.len());
270 for (a, b) in iv {
271 match out.last_mut() {
272 Some(last) if a <= last.1 + 1 => last.1 = last.1.max(b),
273 _ => out.push((a, b)),
274 }
275 }
276 RealSet { kind, iv: out }
277 }
278
279 pub fn union(&self, o: &RealSet) -> RealSet {
281 let mut iv = self.iv.clone();
282 iv.extend_from_slice(&o.iv);
283 RealSet::normalised(self.kind, iv)
284 }
285
286 pub fn intersect(&self, o: &RealSet) -> RealSet {
288 let mut out = Vec::new();
289 let (mut i, mut j) = (0, 0);
290 while i < self.iv.len() && j < o.iv.len() {
291 let (a, b) = (self.iv[i], o.iv[j]);
292 let (lo, hi) = (a.0.max(b.0), a.1.min(b.1));
293 if lo <= hi {
294 out.push((lo, hi));
295 }
296 if a.1 < b.1 {
297 i += 1;
298 } else {
299 j += 1;
300 }
301 }
302 RealSet {
303 kind: self.kind,
304 iv: out,
305 }
306 }
307
308 pub fn complement(&self) -> RealSet {
310 let m = self.kind.max_idx();
311 let mut out = Vec::new();
312 let mut next = -m;
313 for &(a, b) in &self.iv {
314 if a > next {
315 out.push((next, a - 1));
316 }
317 next = b + 1;
318 }
319 if next <= m {
320 out.push((next, m));
321 }
322 RealSet {
323 kind: self.kind,
324 iv: out,
325 }
326 }
327
328 pub fn measure(&self) -> f64 {
332 self.iv
333 .iter()
334 .map(|&(a, b)| 0.5 * self.kind.val(b) - 0.5 * self.kind.val(a))
335 .sum()
336 }
337
338 fn points(&self) -> BigUint {
340 self.iv
341 .iter()
342 .map(|&(a, b)| BigUint::from((i128::from(b) - i128::from(a) + 1) as u128))
343 .sum()
344 }
345
346 pub fn sample(&self, seed: &mut u64) -> f64 {
349 debug_assert!(!self.is_empty(), "sampling an empty set");
350 let k = self.kind;
351 let total = self.measure();
352 if !total.is_finite() || total <= 0.0 {
353 let n = self.points();
356 let mut r = rng::below(seed, &n);
357 for &(a, b) in &self.iv {
358 let len = BigUint::from((i128::from(b) - i128::from(a) + 1) as u128);
359 if r < len {
360 let off = r.to_i64().unwrap_or(0);
361 return k.val(a + off);
362 }
363 r -= len;
364 }
365 return k.val(self.iv[self.iv.len() - 1].1);
366 }
367 let mut target = unit(seed) * total;
368 let mut chosen = self.iv[self.iv.len() - 1];
369 for &(a, b) in &self.iv {
370 let len = 0.5 * k.val(b) - 0.5 * k.val(a);
371 if target < len {
372 chosen = (a, b);
373 break;
374 }
375 target -= len;
376 }
377 let (lo, hi) = (k.val(chosen.0), k.val(chosen.1));
378 let (h_lo, h_hi) = (0.5 * lo, 0.5 * hi);
379 let x = 2.0 * (h_lo + unit(seed) * (h_hi - h_lo));
380 let x = k.round(x.clamp(lo, hi));
381 x.clamp(lo, hi)
382 }
383}
384
385#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
388pub struct Real(u64);
389
390impl Real {
391 pub fn new(x: f64) -> Real {
393 Real(x.to_bits())
394 }
395 pub fn get(self) -> f64 {
397 f64::from_bits(self.0)
398 }
399}
400
401impl PartialEq for Real {
402 fn eq(&self, o: &Real) -> bool {
403 self.0 == o.0
404 }
405}
406impl Eq for Real {}
407
408#[derive(Clone, Debug, PartialEq, Eq)]
410pub enum RealError {
411 Unsatisfiable,
413 GuardError,
415 StateUnreadable(StateId),
418 NonFinite(&'static str),
420 BadWeight,
422 Shape(String),
425}
426
427impl core::fmt::Display for RealError {
428 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
429 match self {
430 RealError::Unsatisfiable => f.write_str("the real constraints have no solution"),
431 RealError::GuardError => f.write_str("a real constraint guard evaluated to ERROR"),
432 RealError::StateUnreadable(s) => {
433 write!(f, "state variable #{s} of a real constraint cannot be read")
434 }
435 RealError::NonFinite(what) => write!(f, "real constraint evaluation: {what}"),
436 RealError::BadWeight => f.write_str("a real `dist` weight is negative"),
437 RealError::Shape(what) => write!(f, "unsupported real constraint: {what}"),
438 }
439 }
440}
441
442impl std::error::Error for RealError {}
443
444#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
448pub enum RealExpr {
449 Const(Real, FloatKind),
451 Var(RealVarId),
453 State(StateId, FloatKind),
455 FromInt(Box<Expr>),
458 Neg(Box<RealExpr>),
460 Add(Box<RealExpr>, Box<RealExpr>),
462 Sub(Box<RealExpr>, Box<RealExpr>),
464 Mul(Box<RealExpr>, Box<RealExpr>),
466 Div(Box<RealExpr>, Box<RealExpr>),
468 ToShort(Box<RealExpr>),
470}
471
472#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
474pub enum CmpOp {
475 Lt,
477 Le,
479 Gt,
481 Ge,
483 Eq,
485 Ne,
487}
488
489#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
491pub enum RealCond {
492 Const(bool),
494 Int(Expr),
496 Cmp(CmpOp, RealExpr, RealExpr),
498 Not(Box<RealCond>),
500 And(Vec<RealCond>),
502 Or(Vec<RealCond>),
504 Guard(Box<RealCond>, Box<RealCond>, Box<RealCond>),
507}
508
509#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
511pub enum RealSpan {
512 Point(RealExpr),
514 Range {
516 lo: Option<RealExpr>,
518 hi: Option<RealExpr>,
520 },
521}
522
523#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
525pub struct RealDistItem {
526 pub span: RealSpan,
528 pub weight: Expr,
532}
533
534#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
536pub struct RealDist {
537 pub var: RealVarId,
539 pub items: Vec<RealDistItem>,
541 pub default: Option<Expr>,
543}
544
545#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
547pub struct RealProblem {
548 pub kinds: Vec<FloatKind>,
550 pub conds: Vec<RealCond>,
552 pub dists: Vec<RealDist>,
554 pub before: Vec<(RealVarId, RealVarId)>,
556}
557
558impl RealExpr {
559 fn map_int(&self, f: &mut dyn FnMut(&Expr) -> Expr) -> RealExpr {
560 let mut b = |x: &RealExpr| Box::new(x.map_int(f));
561 match self {
562 RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) => self.clone(),
563 RealExpr::FromInt(x) => RealExpr::FromInt(Box::new(f(x))),
564 RealExpr::Neg(x) => RealExpr::Neg(b(x)),
565 RealExpr::ToShort(x) => RealExpr::ToShort(b(x)),
566 RealExpr::Add(x, y) => RealExpr::Add(b(x), b(y)),
567 RealExpr::Sub(x, y) => RealExpr::Sub(b(x), b(y)),
568 RealExpr::Mul(x, y) => RealExpr::Mul(b(x), b(y)),
569 RealExpr::Div(x, y) => RealExpr::Div(b(x), b(y)),
570 }
571 }
572
573 fn for_each_int(&self, f: &mut dyn FnMut(&Expr)) {
574 match self {
575 RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) => {}
576 RealExpr::FromInt(x) => f(x),
577 RealExpr::Neg(x) | RealExpr::ToShort(x) => x.for_each_int(f),
578 RealExpr::Add(x, y)
579 | RealExpr::Sub(x, y)
580 | RealExpr::Mul(x, y)
581 | RealExpr::Div(x, y) => {
582 x.for_each_int(f);
583 y.for_each_int(f);
584 }
585 }
586 }
587}
588
589impl RealCond {
590 fn map_int(&self, f: &mut dyn FnMut(&Expr) -> Expr) -> RealCond {
591 match self {
592 RealCond::Const(_) => self.clone(),
593 RealCond::Int(e) => RealCond::Int(f(e)),
594 RealCond::Cmp(op, l, r) => RealCond::Cmp(*op, l.map_int(f), r.map_int(f)),
595 RealCond::Not(x) => RealCond::Not(Box::new(x.map_int(f))),
596 RealCond::And(v) => RealCond::And(v.iter().map(|x| x.map_int(f)).collect()),
597 RealCond::Or(v) => RealCond::Or(v.iter().map(|x| x.map_int(f)).collect()),
598 RealCond::Guard(p, t, e) => RealCond::Guard(
599 Box::new(p.map_int(f)),
600 Box::new(t.map_int(f)),
601 Box::new(e.map_int(f)),
602 ),
603 }
604 }
605
606 fn for_each_int(&self, f: &mut dyn FnMut(&Expr)) {
607 match self {
608 RealCond::Const(_) => {}
609 RealCond::Int(e) => f(e),
610 RealCond::Cmp(_, l, r) => {
611 l.for_each_int(f);
612 r.for_each_int(f);
613 }
614 RealCond::Not(x) => x.for_each_int(f),
615 RealCond::And(v) | RealCond::Or(v) => v.iter().for_each(|x| x.for_each_int(f)),
616 RealCond::Guard(p, t, e) => {
617 p.for_each_int(f);
618 t.for_each_int(f);
619 e.for_each_int(f);
620 }
621 }
622 }
623}
624
625impl RealProblem {
626 pub fn reads_int_vars(&self) -> bool {
630 let mut any = false;
631 let mut f = |e: &Expr| e.for_each_var(&mut |_, _| any = true);
632 for c in &self.conds {
633 c.for_each_int(&mut f);
634 }
635 for d in &self.dists {
636 for it in &d.items {
637 match &it.span {
638 RealSpan::Point(x) => x.for_each_int(&mut f),
639 RealSpan::Range { lo, hi } => {
640 lo.iter().chain(hi).for_each(|x| x.for_each_int(&mut f));
641 }
642 }
643 }
644 }
645 any
646 }
647
648 pub fn has_integral_only_filter(&self) -> bool {
653 self.conds.iter().any(|c| {
654 let (mut int_var, mut real_var) = (false, false);
655 c.for_each_int(&mut |e| e.for_each_var(&mut |_, _| int_var = true));
656 c.for_each_var(&mut |_| real_var = true);
657 int_var && !real_var
658 })
659 }
660
661 pub fn with_int_vars(&self, mut f: impl FnMut(VarId, Ty) -> Expr) -> RealProblem {
665 let mut m = |e: &Expr| e.substitute_vars(&mut f);
666 let mut p = self.clone();
667 p.conds = self.conds.iter().map(|c| c.map_int(&mut m)).collect();
668 for d in &mut p.dists {
669 for it in &mut d.items {
670 it.span = match &it.span {
671 RealSpan::Point(x) => RealSpan::Point(x.map_int(&mut m)),
672 RealSpan::Range { lo, hi } => RealSpan::Range {
673 lo: lo.as_ref().map(|x| x.map_int(&mut m)),
674 hi: hi.as_ref().map(|x| x.map_int(&mut m)),
675 },
676 };
677 }
678 }
679 p
680 }
681}
682
683impl RealExpr {
684 pub fn kind(&self, kinds: &[FloatKind]) -> FloatKind {
686 match self {
687 RealExpr::Const(_, k) | RealExpr::State(_, k) => *k,
688 RealExpr::Var(v) => kinds.get(*v as usize).copied().unwrap_or(FloatKind::F64),
689 RealExpr::FromInt(_) => FloatKind::F64,
690 RealExpr::ToShort(_) => FloatKind::F32,
691 RealExpr::Neg(x) => x.kind(kinds),
692 RealExpr::Add(a, b)
693 | RealExpr::Sub(a, b)
694 | RealExpr::Mul(a, b)
695 | RealExpr::Div(a, b) => {
696 if a.kind(kinds) == FloatKind::F32 && b.kind(kinds) == FloatKind::F32 {
697 FloatKind::F32
698 } else {
699 FloatKind::F64
700 }
701 }
702 }
703 }
704
705 pub fn for_each_var(&self, f: &mut impl FnMut(RealVarId)) {
707 match self {
708 RealExpr::Var(v) => f(*v),
709 RealExpr::Const(..) | RealExpr::State(..) | RealExpr::FromInt(_) => {}
710 RealExpr::Neg(x) | RealExpr::ToShort(x) => x.for_each_var(f),
711 RealExpr::Add(a, b)
712 | RealExpr::Sub(a, b)
713 | RealExpr::Mul(a, b)
714 | RealExpr::Div(a, b) => {
715 a.for_each_var(f);
716 b.for_each_var(f);
717 }
718 }
719 }
720
721 pub fn for_each_state(&self, f: &mut impl FnMut(StateId)) {
724 match self {
725 RealExpr::State(s, _) => f(*s),
726 RealExpr::FromInt(e) => e.for_each_state(f),
727 RealExpr::Const(..) | RealExpr::Var(_) => {}
728 RealExpr::Neg(x) | RealExpr::ToShort(x) => x.for_each_state(f),
729 RealExpr::Add(a, b)
730 | RealExpr::Sub(a, b)
731 | RealExpr::Mul(a, b)
732 | RealExpr::Div(a, b) => {
733 a.for_each_state(f);
734 b.for_each_state(f);
735 }
736 }
737 }
738}
739
740impl RealCond {
741 pub fn for_each_var(&self, f: &mut impl FnMut(RealVarId)) {
743 match self {
744 RealCond::Const(_) | RealCond::Int(_) => {}
745 RealCond::Cmp(_, l, r) => {
746 l.for_each_var(f);
747 r.for_each_var(f);
748 }
749 RealCond::Not(x) => x.for_each_var(f),
750 RealCond::And(xs) | RealCond::Or(xs) => xs.iter().for_each(|x| x.for_each_var(f)),
751 RealCond::Guard(p, t, e) => {
752 p.for_each_var(f);
753 t.for_each_var(f);
754 e.for_each_var(f);
755 }
756 }
757 }
758
759 pub fn vars(&self) -> Vec<RealVarId> {
761 let mut v = Vec::new();
762 self.for_each_var(&mut |x| v.push(x));
763 v.sort_unstable();
764 v.dedup();
765 v
766 }
767}
768
769struct Ctx<'a> {
772 kinds: &'a [FloatKind],
773 vars: Vec<Option<f64>>,
775 states: &'a [Option<BigUint>],
776}
777
778fn int_value(e: &Expr, states: &[Option<BigUint>]) -> Result<BigUint, RealError> {
779 let a = Assignment {
780 vars: Vec::new(),
781 states: states.to_vec(),
782 };
783 super::ir::eval(e, &a).map_err(|err| match err {
784 ConstraintEvalError::StateUnknown(s) => RealError::StateUnreadable(s),
785 ConstraintEvalError::DivideByZero => RealError::NonFinite("integer division by zero"),
786 ConstraintEvalError::UnboundVar(_)
787 | ConstraintEvalError::UnboundState(_)
788 | ConstraintEvalError::Unexpanded => {
789 RealError::Shape("an integral sub-expression reads an unbound variable".into())
790 }
791 })
792}
793
794fn signed_number(v: &BigUint, e: &Expr) -> BigInt {
796 let t = e.ty();
797 if t.signed && v.bit(u64::from(t.width) - 1) {
798 BigInt::from(v.clone()) - (BigInt::from(1) << t.width)
799 } else {
800 BigInt::from(v.clone())
801 }
802}
803
804impl Ctx<'_> {
805 fn state(&self, s: StateId) -> Result<&BigUint, RealError> {
806 match self.states.get(s as usize) {
807 Some(Some(v)) => Ok(v),
808 _ => Err(RealError::StateUnreadable(s)),
809 }
810 }
811
812 fn eval(&self, e: &RealExpr) -> Result<f64, RealError> {
814 let finite = |x: f64, k: FloatKind| -> Result<f64, RealError> {
815 let x = k.round(x);
816 if x.is_nan() {
817 Err(RealError::NonFinite("a real expression is NaN"))
818 } else {
819 Ok(x)
820 }
821 };
822 Ok(match e {
823 RealExpr::Const(r, _) => r.get(),
824 RealExpr::Var(v) => self
825 .vars
826 .get(*v as usize)
827 .copied()
828 .flatten()
829 .ok_or_else(|| RealError::Shape(format!("real variable #{v} has no value here")))?,
830 RealExpr::State(s, k) => k.from_bits(self.state(*s)?),
831 RealExpr::FromInt(x) => {
832 let v = int_value(x, self.states)?;
833 signed_number(&v, x).to_f64().unwrap_or(f64::NAN)
834 }
835 RealExpr::Neg(x) => -self.eval(x)?,
836 RealExpr::ToShort(x) => finite(self.eval(x)?, FloatKind::F32)?,
837 RealExpr::Add(a, b)
838 | RealExpr::Sub(a, b)
839 | RealExpr::Mul(a, b)
840 | RealExpr::Div(a, b) => {
841 let (x, y) = (self.eval(a)?, self.eval(b)?);
842 let k = e.kind(self.kinds);
843 let r = match e {
844 RealExpr::Add(..) => x + y,
845 RealExpr::Sub(..) => x - y,
846 RealExpr::Mul(..) => x * y,
847 _ => {
848 if y == 0.0 {
849 return Err(RealError::NonFinite("division by zero"));
850 }
851 x / y
852 }
853 };
854 finite(r, k)?
855 }
856 })
857 }
858
859 fn cmp(&self, op: CmpOp, l: &RealExpr, r: &RealExpr) -> Result<bool, RealError> {
861 let (x, y) = (self.eval(l)?, self.eval(r)?);
862 Ok(match op {
863 CmpOp::Lt => x < y,
864 CmpOp::Le => x <= y,
865 CmpOp::Gt => x > y,
866 CmpOp::Ge => x >= y,
867 CmpOp::Eq => x == y,
868 CmpOp::Ne => x != y,
869 })
870 }
871
872 fn expr_reads_free(&self, e: &RealExpr) -> bool {
873 let mut any = false;
874 e.for_each_var(&mut |v| any |= self.vars.get(v as usize).copied().flatten().is_none());
875 any
876 }
877
878 fn pred_value(&self, p: &RealCond) -> GuardValue {
882 match p {
883 RealCond::Const(b) => {
884 if *b {
885 GuardValue::True
886 } else {
887 GuardValue::False
888 }
889 }
890 RealCond::Int(e) => guard::value(
891 e,
892 &Assignment {
893 vars: Vec::new(),
894 states: self.states.to_vec(),
895 },
896 ),
897 RealCond::Cmp(op, l, r) => {
898 let mut bad = false;
899 let mut note = |s: StateId| bad |= self.state(s).is_err();
900 l.for_each_state(&mut note);
901 r.for_each_state(&mut note);
902 if bad {
903 GuardValue::Error
904 } else if self.expr_reads_free(l) || self.expr_reads_free(r) {
905 GuardValue::Random
906 } else {
907 match self.cmp(*op, l, r) {
908 Ok(true) => GuardValue::True,
909 Ok(false) => GuardValue::False,
910 Err(_) => GuardValue::Error,
911 }
912 }
913 }
914 RealCond::Not(x) => self.pred_value(x).not(),
915 RealCond::And(xs) => xs
916 .iter()
917 .fold(GuardValue::True, |acc, x| acc.and(self.pred_value(x))),
918 RealCond::Or(xs) => xs
919 .iter()
920 .fold(GuardValue::False, |acc, x| acc.or(self.pred_value(x))),
921 RealCond::Guard(c, t, e) => match self.pred_value(c) {
922 GuardValue::True => self.pred_value(t),
923 GuardValue::False => self.pred_value(e),
924 GuardValue::Error => GuardValue::Error,
925 GuardValue::Random => GuardValue::Random,
926 },
927 }
928 }
929
930 fn truth(&self, c: &RealCond) -> Result<bool, RealError> {
932 Ok(match c {
933 RealCond::Const(b) => *b,
934 RealCond::Int(e) => !int_value(e, self.states)?.is_zero(),
935 RealCond::Cmp(op, l, r) => self.cmp(*op, l, r)?,
936 RealCond::Not(x) => !self.truth(x)?,
937 RealCond::And(xs) => {
938 let mut all = true;
939 for x in xs {
940 all &= self.truth(x)?;
941 }
942 all
943 }
944 RealCond::Or(xs) => {
945 let mut any = false;
946 for x in xs {
947 any |= self.truth(x)?;
948 }
949 any
950 }
951 RealCond::Guard(p, t, e) => match self.pred_value(p) {
952 GuardValue::True => self.truth(t)?,
953 GuardValue::False => self.truth(e)?,
954 GuardValue::Error => return Err(RealError::GuardError),
955 GuardValue::Random => {
956 return Err(RealError::Shape("a guard reads a free variable".into()));
957 }
958 },
959 })
960 }
961}
962
963fn monotone_set(
967 kind: FloatKind,
968 prefix_true: bool,
969 mut pred: impl FnMut(f64) -> Result<bool, RealError>,
970) -> Result<RealSet, RealError> {
971 let m = kind.max_idx();
972 let mut at = |i: i64| pred(kind.val(i));
973 let (lo_true, hi_true) = (at(-m)?, at(m)?);
974 let (first, last) = if prefix_true {
975 (lo_true, hi_true)
976 } else {
977 (hi_true, lo_true)
978 };
979 if !first {
981 return Ok(RealSet::empty(kind));
982 }
983 if last {
984 return Ok(RealSet::full(kind));
985 }
986 let (mut good, mut bad) = if prefix_true { (-m, m) } else { (m, -m) };
988 while good.abs_diff(bad) > 1 {
989 let mid = ((i128::from(good) + i128::from(bad)) / 2) as i64;
990 if at(mid)? {
991 good = mid;
992 } else {
993 bad = mid;
994 }
995 }
996 Ok(if prefix_true {
997 RealSet::from_idx(kind, -m, good)
998 } else {
999 RealSet::from_idx(kind, good, m)
1000 })
1001}
1002
1003impl<'a> Ctx<'a> {
1004 fn bind(&self, v: RealVarId, x: f64) -> Ctx<'a> {
1006 let mut vars = self.vars.clone();
1007 vars[v as usize] = Some(x);
1008 Ctx {
1009 kinds: self.kinds,
1010 vars,
1011 states: self.states,
1012 }
1013 }
1014
1015 fn free_vars(&self, c: &RealCond) -> Vec<RealVarId> {
1017 c.vars()
1018 .into_iter()
1019 .filter(|v| self.vars.get(*v as usize).copied().flatten().is_none())
1020 .collect()
1021 }
1022
1023 fn lin(&self, e: &RealExpr, v: RealVarId) -> Result<(f64, f64), RealError> {
1026 let non_affine =
1027 || RealError::Shape("a product or quotient is not affine in the variable".into());
1028 Ok(match e {
1029 RealExpr::Const(r, _) => (0.0, r.get()),
1030 RealExpr::Var(x) if *x == v => (1.0, 0.0),
1031 RealExpr::Var(_) | RealExpr::State(..) | RealExpr::FromInt(_) => (0.0, self.eval(e)?),
1032 RealExpr::Neg(x) => {
1033 let (c, d) = self.lin(x, v)?;
1034 (-c, -d)
1035 }
1036 RealExpr::ToShort(x) => self.lin(x, v)?,
1037 RealExpr::Add(a, b) => {
1038 let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1039 (c1 + c2, d1 + d2)
1040 }
1041 RealExpr::Sub(a, b) => {
1042 let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1043 (c1 - c2, d1 - d2)
1044 }
1045 RealExpr::Mul(a, b) => {
1046 let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1047 if c1 == 0.0 {
1048 (d1 * c2, d1 * d2)
1049 } else if c2 == 0.0 {
1050 (d2 * c1, d2 * d1)
1051 } else {
1052 return Err(non_affine());
1053 }
1054 }
1055 RealExpr::Div(a, b) => {
1056 let ((c1, d1), (c2, d2)) = (self.lin(a, v)?, self.lin(b, v)?);
1057 if c2 != 0.0 {
1058 return Err(non_affine());
1059 }
1060 if d2 == 0.0 {
1061 return Err(RealError::NonFinite("division by zero"));
1062 }
1063 (c1 / d2, d1 / d2)
1064 }
1065 })
1066 }
1067
1068 fn cmp_set(
1071 &self,
1072 op: CmpOp,
1073 l: &RealExpr,
1074 r: &RealExpr,
1075 v: RealVarId,
1076 ) -> Result<RealSet, RealError> {
1077 let kind = self.kinds[v as usize];
1078 let mut others = false;
1079 for e in [l, r] {
1080 e.for_each_var(&mut |x| {
1081 others |= x != v && self.vars.get(x as usize).copied().flatten().is_none();
1082 });
1083 }
1084 if others {
1085 return Err(RealError::Shape(
1086 "a comparison reads two random real variables".into(),
1087 ));
1088 }
1089 let (c, _) = {
1090 let ((c1, d1), (c2, d2)) = (self.lin(l, v)?, self.lin(r, v)?);
1091 (c1 - c2, d1 - d2)
1092 };
1093 if !c.is_finite() {
1094 return Err(RealError::NonFinite("a real coefficient is not finite"));
1095 }
1096 let (full, empty) = (RealSet::full(kind), RealSet::empty(kind));
1097 if c == 0.0 {
1098 return Ok(if self.bind(v, 0.0).cmp(op, l, r)? {
1099 full
1100 } else {
1101 empty
1102 });
1103 }
1104 let prefix = c > 0.0;
1105 let lt = || monotone_set(kind, prefix, |x| self.bind(v, x).cmp(CmpOp::Lt, l, r));
1106 let le = || monotone_set(kind, prefix, |x| self.bind(v, x).cmp(CmpOp::Le, l, r));
1107 Ok(match op {
1108 CmpOp::Lt => lt()?,
1109 CmpOp::Le => le()?,
1110 CmpOp::Gt => le()?.complement(),
1111 CmpOp::Ge => lt()?.complement(),
1112 CmpOp::Eq => le()?.intersect(<()?.complement()),
1113 CmpOp::Ne => le()?.intersect(<()?.complement()).complement(),
1114 })
1115 }
1116
1117 fn set_of(&self, c: &RealCond, v: RealVarId) -> Result<RealSet, RealError> {
1121 let kind = self.kinds[v as usize];
1122 let all = |b: bool| {
1123 if b {
1124 RealSet::full(kind)
1125 } else {
1126 RealSet::empty(kind)
1127 }
1128 };
1129 Ok(match c {
1130 RealCond::Const(b) => all(*b),
1131 RealCond::Int(_) => all(self.truth(c)?),
1132 RealCond::Cmp(op, l, r) => {
1133 if self.expr_reads_free(l) || self.expr_reads_free(r) {
1134 self.cmp_set(*op, l, r, v)?
1135 } else {
1136 all(self.cmp(*op, l, r)?)
1137 }
1138 }
1139 RealCond::Not(x) => self.set_of(x, v)?.complement(),
1140 RealCond::And(xs) => {
1141 let mut s = all(true);
1142 for x in xs {
1143 s = s.intersect(&self.set_of(x, v)?);
1144 }
1145 s
1146 }
1147 RealCond::Or(xs) => {
1148 let mut s = all(false);
1149 for x in xs {
1150 s = s.union(&self.set_of(x, v)?);
1151 }
1152 s
1153 }
1154 RealCond::Guard(p, t, e) => match self.pred_value(p) {
1155 GuardValue::True => self.set_of(t, v)?,
1156 GuardValue::False => self.set_of(e, v)?,
1157 GuardValue::Error => return Err(RealError::GuardError),
1158 GuardValue::Random => {
1159 let ps = self.pred_set(p, v)?;
1160 ps.intersect(&self.set_of(t, v)?)
1161 .union(&ps.complement().intersect(&self.set_of(e, v)?))
1162 }
1163 },
1164 })
1165 }
1166
1167 fn pred_set(&self, p: &RealCond, v: RealVarId) -> Result<RealSet, RealError> {
1170 let kind = self.kinds[v as usize];
1171 match self.pred_value(p) {
1172 GuardValue::True => Ok(RealSet::full(kind)),
1173 GuardValue::False => Ok(RealSet::empty(kind)),
1174 GuardValue::Error => Err(RealError::GuardError),
1175 GuardValue::Random => match p {
1176 RealCond::Not(x) => Ok(self.pred_set(x, v)?.complement()),
1177 RealCond::And(xs) => {
1178 let mut s = RealSet::full(kind);
1179 for x in xs {
1180 s = s.intersect(&self.pred_set(x, v)?);
1181 }
1182 Ok(s)
1183 }
1184 RealCond::Or(xs) => {
1185 let mut s = RealSet::empty(kind);
1186 for x in xs {
1187 s = s.union(&self.pred_set(x, v)?);
1188 }
1189 Ok(s)
1190 }
1191 RealCond::Cmp(op, l, r) => self.cmp_set(*op, l, r, v),
1192 RealCond::Guard(..) => self.set_of(p, v),
1193 RealCond::Const(_) | RealCond::Int(_) => {
1194 unreachable!("a state-only predicate is never RANDOM")
1195 }
1196 },
1197 }
1198 }
1199}
1200
1201fn weight_of(e: &Expr, states: &[Option<BigUint>]) -> Result<f64, RealError> {
1203 let n = signed_number(&int_value(e, states)?, e);
1204 if n < BigInt::zero() {
1205 return Err(RealError::BadWeight);
1206 }
1207 Ok(n.to_f64().unwrap_or(f64::INFINITY))
1208}
1209
1210struct DistPlan {
1212 items: Vec<(RealSet, f64)>,
1214 default: Option<(RealSet, f64)>,
1216 support: RealSet,
1218}
1219
1220fn dist_plan(ctx: &Ctx<'_>, d: &RealDist, kind: FloatKind) -> Result<DistPlan, RealError> {
1221 let mut items = Vec::with_capacity(d.items.len());
1222 let mut named = RealSet::empty(kind);
1223 let mut support = RealSet::empty(kind);
1224 for it in &d.items {
1225 let set = match &it.span {
1226 RealSpan::Point(e) => RealSet::point(kind, ctx.eval(e)?),
1227 RealSpan::Range { lo, hi } => {
1228 let lo = lo.as_ref().map_or(Ok(f64::NEG_INFINITY), |e| ctx.eval(e))?;
1229 let hi = hi.as_ref().map_or(Ok(f64::INFINITY), |e| ctx.eval(e))?;
1230 RealSet::range(kind, lo, hi)
1231 }
1232 };
1233 let w = weight_of(&it.weight, ctx.states)?;
1234 named = named.union(&set);
1235 if w > 0.0 {
1236 support = support.union(&set);
1237 }
1238 items.push((set, w));
1239 }
1240 let default = match &d.default {
1241 Some(e) => {
1242 let w = weight_of(e, ctx.states)?;
1243 let region = named.complement();
1244 if w > 0.0 {
1245 support = support.union(®ion);
1246 }
1247 Some((region, w))
1248 }
1249 None => None,
1250 };
1251 Ok(DistPlan {
1252 items,
1253 default,
1254 support,
1255 })
1256}
1257
1258fn share(item: &RealSet, f: &RealSet) -> f64 {
1261 let inter = item.intersect(f);
1262 if inter.is_empty() {
1263 return 0.0;
1264 }
1265 let m = item.measure();
1266 if m > 0.0 {
1267 (inter.measure() / m).min(1.0)
1268 } else {
1269 1.0
1270 }
1271}
1272
1273impl DistPlan {
1274 fn sample(&self, f: &RealSet, seed: &mut u64) -> Result<f64, RealError> {
1277 let all: Vec<(&RealSet, f64)> = self
1278 .items
1279 .iter()
1280 .map(|(s, w)| (s, *w))
1281 .chain(self.default.iter().map(|(s, w)| (s, *w)))
1282 .collect();
1283 let pick = |weights: &[f64], seed: &mut u64| -> Option<usize> {
1284 let total: f64 = weights.iter().sum();
1285 if !total.is_finite() || total <= 0.0 {
1286 return None;
1287 }
1288 let mut t = unit(seed) * total;
1289 let mut last = None;
1290 for (i, w) in weights.iter().enumerate() {
1291 if *w > 0.0 {
1292 last = Some(i);
1293 if t < *w {
1294 return Some(i);
1295 }
1296 t -= *w;
1297 }
1298 }
1299 last
1300 };
1301 let eff: Vec<f64> = all.iter().map(|(s, w)| w * share(s, f)).collect();
1302 let i = match pick(&eff, seed) {
1303 Some(i) => i,
1304 None => {
1305 let nominal: Vec<f64> = all
1308 .iter()
1309 .map(|(s, w)| if s.intersect(f).is_empty() { 0.0 } else { *w })
1310 .collect();
1311 pick(&nominal, seed).ok_or(RealError::Unsatisfiable)?
1312 }
1313 };
1314 Ok(all[i].0.intersect(f).sample(seed))
1315 }
1316}
1317
1318struct Coupled {
1320 a: RealVarId,
1321 pred: RealSet,
1322 then: Vec<(RealVarId, RealSet)>,
1323 other: Vec<(RealVarId, RealSet)>,
1324}
1325
1326impl Coupled {
1327 fn arm(&self, then: bool) -> &[(RealVarId, RealSet)] {
1328 if then { &self.then } else { &self.other }
1329 }
1330}
1331
1332#[derive(Default)]
1334struct Lowered {
1335 unary: Vec<(RealVarId, RealSet)>,
1336 coupled: Vec<Coupled>,
1337 joint: Vec<RealCond>,
1340 infeasible: bool,
1341}
1342
1343impl Ctx<'_> {
1344 fn lower(&self, c: &RealCond, out: &mut Lowered) -> Result<(), RealError> {
1346 let free = self.free_vars(c);
1347 match c {
1348 RealCond::Guard(p, t, e) => match self.pred_value(p) {
1349 GuardValue::True => self.lower(t, out),
1350 GuardValue::False => self.lower(e, out),
1351 GuardValue::Error => Err(RealError::GuardError),
1352 GuardValue::Random => {
1353 if free.len() == 1 {
1354 out.unary.push((free[0], self.set_of(c, free[0])?));
1355 return Ok(());
1356 }
1357 let pf = self.free_vars(p);
1358 let [a] = pf[..] else {
1359 out.joint.push(c.clone());
1360 return Ok(());
1361 };
1362 let pred = self.pred_set(p, a)?;
1363 let mut arms: [Vec<(RealVarId, RealSet)>; 2] = [Vec::new(), Vec::new()];
1364 for (k, arm) in [t, e].into_iter().enumerate() {
1365 let mut l = Lowered::default();
1366 self.lower(arm, &mut l)?;
1367 if !l.coupled.is_empty() {
1368 return Err(RealError::Shape(
1369 "a guard nested in a coupling guard couples two more variables"
1370 .into(),
1371 ));
1372 }
1373 let applies = if k == 0 {
1377 pred.clone()
1378 } else {
1379 pred.complement()
1380 };
1381 let off = applies.complement();
1382 if l.infeasible {
1383 out.unary.push((a, off.clone()));
1384 }
1385 for (v, s) in l.unary {
1386 if v == a {
1387 out.unary.push((a, off.union(&s)));
1388 } else {
1389 arms[k].push((v, s));
1390 }
1391 }
1392 }
1393 let [then, other] = arms;
1394 if !then.is_empty() || !other.is_empty() {
1395 out.coupled.push(Coupled {
1396 a,
1397 pred,
1398 then,
1399 other,
1400 });
1401 }
1402 Ok(())
1403 }
1404 },
1405 _ => match free.len() {
1406 0 => {
1407 if !self.truth(c)? {
1408 out.infeasible = true;
1409 }
1410 Ok(())
1411 }
1412 1 => {
1413 out.unary.push((free[0], self.set_of(c, free[0])?));
1414 Ok(())
1415 }
1416 _ => match c {
1417 RealCond::And(xs) => xs.iter().try_for_each(|x| self.lower(x, out)),
1418 _ => {
1419 out.joint.push(c.clone());
1420 Ok(())
1421 }
1422 },
1423 },
1424 }
1425 }
1426}
1427
1428const JOINT_TRIES: usize = 20_000;
1430
1431fn joint_draw(
1440 ctx: &Ctx<'_>,
1441 joint: &[RealCond],
1442 own: &[RealSet],
1443 plans: &[Option<DistPlan>],
1444 seed: &mut u64,
1445) -> Result<Vec<(usize, f64)>, RealError> {
1446 let mut vars: Vec<usize> = joint
1447 .iter()
1448 .flat_map(|c| ctx.free_vars(c))
1449 .map(|v| v as usize)
1450 .collect();
1451 vars.sort_unstable();
1452 vars.dedup();
1453 if vars.iter().any(|&v| own[v].is_empty()) {
1454 return Err(RealError::Unsatisfiable);
1455 }
1456 let mut probe = Ctx {
1457 kinds: ctx.kinds,
1458 vars: ctx.vars.clone(),
1459 states: ctx.states,
1460 };
1461 'tries: for _ in 0..JOINT_TRIES {
1462 for &v in &vars {
1463 let x = match &plans[v] {
1464 Some(plan) => plan.sample(&own[v], seed)?,
1465 None => own[v].sample(seed),
1466 };
1467 probe.vars[v] = Some(x);
1468 }
1469 for c in joint {
1470 match probe.truth(c) {
1471 Ok(true) => {}
1472 Ok(false) | Err(RealError::NonFinite(_)) => continue 'tries,
1475 Err(e) => return Err(e),
1476 }
1477 }
1478 return Ok(vars
1479 .iter()
1480 .map(|&v| (v, probe.vars[v].unwrap_or(0.0)))
1481 .collect());
1482 }
1483 Err(RealError::Shape(format!(
1484 "no draw satisfied the constraints that relate several random reals in {JOINT_TRIES} \
1485 tries (an equality between random reals, or a region too thin to sample)"
1486 )))
1487}
1488
1489fn ordered_before(before: &[(RealVarId, RealVarId)], a: RealVarId, b: RealVarId) -> bool {
1491 let mut seen = vec![a];
1492 let mut work = vec![a];
1493 while let Some(x) = work.pop() {
1494 for &(p, q) in before {
1495 if p == x && !seen.contains(&q) {
1496 if q == b {
1497 return true;
1498 }
1499 seen.push(q);
1500 work.push(q);
1501 }
1502 }
1503 }
1504 false
1505}
1506
1507pub fn solve(
1513 p: &RealProblem,
1514 known: &[Option<f64>],
1515 states: &[Option<BigUint>],
1516 seed: &mut u64,
1517) -> Result<Vec<Option<f64>>, RealError> {
1518 let n = p.kinds.len();
1519 if known.len() != n {
1520 return Err(RealError::Shape("one known value per real variable".into()));
1521 }
1522 let ctx = Ctx {
1523 kinds: &p.kinds,
1524 vars: known.to_vec(),
1525 states,
1526 };
1527 let mut low = Lowered::default();
1528 for c in &p.conds {
1529 ctx.lower(c, &mut low)?;
1530 }
1531 let mut plans: Vec<Option<DistPlan>> = (0..n).map(|_| None).collect();
1532 for d in &p.dists {
1533 let v = d.var as usize;
1534 if v >= n || plans[v].is_some() {
1535 return Err(RealError::Shape(
1536 "a `dist` names an unknown variable, or a variable has two".into(),
1537 ));
1538 }
1539 let plan = dist_plan(&ctx, d, p.kinds[v])?;
1540 if let Some(x) = known[v]
1541 && !plan.support.contains(x)
1542 {
1543 low.infeasible = true;
1544 }
1545 plans[v] = Some(plan);
1546 }
1547 if low.infeasible {
1548 return Err(RealError::Unsatisfiable);
1549 }
1550
1551 let active: Vec<usize> = (0..n).filter(|&v| known[v].is_none()).collect();
1552 let mut own: Vec<RealSet> = (0..n).map(|v| RealSet::full(p.kinds[v])).collect();
1553 for (v, s) in &low.unary {
1554 own[*v as usize] = own[*v as usize].intersect(s);
1555 }
1556 for &v in &active {
1557 if let Some(plan) = &plans[v] {
1558 own[v] = own[v].intersect(&plan.support);
1559 }
1560 }
1561
1562 let mut out: Vec<Option<f64>> = vec![None; n];
1565 if !low.joint.is_empty() {
1566 let drawn = joint_draw(&ctx, &low.joint, &own, &plans, seed)?;
1567 for g in &low.coupled {
1568 let mut touched = vec![g.a as usize];
1569 touched.extend(g.then.iter().chain(&g.other).map(|(b, _)| *b as usize));
1570 if touched.iter().any(|t| drawn.iter().any(|(v, _)| v == t)) {
1571 return Err(RealError::Shape(
1572 "a random real that a constraint relates to another random real also takes \
1573 part in a coupling guard"
1574 .into(),
1575 ));
1576 }
1577 }
1578 for (v, x) in drawn {
1579 out[v] = Some(x);
1580 }
1581 }
1582
1583 let mut parent: Vec<Option<RealVarId>> = vec![None; n];
1585 for g in &low.coupled {
1586 for arm in [&g.then, &g.other] {
1587 for (b, _) in arm {
1588 match parent[*b as usize] {
1589 Some(q) if q != g.a => {
1590 return Err(RealError::Shape(format!(
1591 "real variable #{b} is constrained by guards on two other variables"
1592 )));
1593 }
1594 _ => parent[*b as usize] = Some(g.a),
1595 }
1596 if !ordered_before(&p.before, g.a, *b) {
1597 return Err(RealError::Shape(format!(
1598 "a guard on real variable #{} constrains #{b}; add `solve` the first \
1599 `before` the second",
1600 g.a
1601 )));
1602 }
1603 }
1604 }
1605 }
1606 let depth = |mut v: usize| -> Result<usize, RealError> {
1607 let mut d = 0;
1608 while let Some(q) = parent[v] {
1609 d += 1;
1610 v = q as usize;
1611 if d > n {
1612 return Err(RealError::Shape("a cycle of coupled real guards".into()));
1613 }
1614 }
1615 Ok(d)
1616 };
1617 let mut order: Vec<(usize, usize)> = Vec::with_capacity(active.len());
1618 for &v in &active {
1619 order.push((depth(v)?, v));
1620 }
1621 order.sort_unstable();
1622 let order: Vec<usize> = order.into_iter().map(|(_, v)| v).collect();
1623
1624 let mut down: Vec<RealSet> = own.clone();
1626 for &v in order.iter().rev() {
1627 let guards: Vec<&Coupled> = low.coupled.iter().filter(|g| g.a as usize == v).collect();
1628 if guards.is_empty() {
1629 if down[v].is_empty() {
1630 return Err(RealError::Unsatisfiable);
1631 }
1632 continue;
1633 }
1634 let mut cells: Vec<(RealSet, Vec<bool>)> = vec![(own[v].clone(), Vec::new())];
1635 for g in &guards {
1636 let mut next = Vec::new();
1637 for (s, sig) in &cells {
1638 for (then, pset) in [(true, g.pred.clone()), (false, g.pred.complement())] {
1639 let t = s.intersect(&pset);
1640 if !t.is_empty() {
1641 let mut sg = sig.clone();
1642 sg.push(then);
1643 next.push((t, sg));
1644 }
1645 }
1646 }
1647 cells = next;
1648 }
1649 let mut children: Vec<usize> = guards
1650 .iter()
1651 .flat_map(|g| g.then.iter().chain(&g.other).map(|(b, _)| *b as usize))
1652 .collect();
1653 children.sort_unstable();
1654 children.dedup();
1655 let mut feasible = RealSet::empty(p.kinds[v]);
1656 for (s, sig) in &cells {
1657 let ok = children.iter().all(|&b| {
1658 let mut need = down[b].clone();
1659 for (g, &then) in guards.iter().zip(sig) {
1660 for (c, set) in g.arm(then) {
1661 if *c as usize == b {
1662 need = need.intersect(set);
1663 }
1664 }
1665 }
1666 !need.is_empty()
1667 });
1668 if ok {
1669 feasible = feasible.union(s);
1670 }
1671 }
1672 if feasible.is_empty() {
1673 return Err(RealError::Unsatisfiable);
1674 }
1675 down[v] = feasible;
1676 }
1677
1678 let mut extra: Vec<RealSet> = (0..n).map(|v| RealSet::full(p.kinds[v])).collect();
1680 for &v in &order {
1681 if out[v].is_some() {
1682 continue;
1683 }
1684 let f = down[v].intersect(&extra[v]);
1685 if f.is_empty() {
1686 return Err(RealError::Shape(
1687 "a real variable has no value left after its parents were drawn".into(),
1688 ));
1689 }
1690 let x = match &plans[v] {
1691 Some(plan) => plan.sample(&f, seed)?,
1692 None => f.sample(seed),
1693 };
1694 out[v] = Some(x);
1695 for g in low.coupled.iter().filter(|g| g.a as usize == v) {
1696 for (b, s) in g.arm(g.pred.contains(x)) {
1697 extra[*b as usize] = extra[*b as usize].intersect(s);
1698 }
1699 }
1700 }
1701 Ok(out)
1702}
1703
1704impl RealProblem {
1705 pub fn check(&self) -> Result<(), String> {
1710 let n = self.kinds.len() as u32;
1711 let mut edges: Vec<(RealVarId, RealVarId)> = Vec::new();
1712 let mut joint: Vec<RealVarId> = Vec::new();
1713 for c in &self.conds {
1714 check_cond(c, &mut edges, &mut joint)?;
1715 check_affine(c)?;
1716 }
1717 if let Some(&(a, b)) = edges
1718 .iter()
1719 .find(|&&(a, b)| joint.contains(&a) || joint.contains(&b))
1720 {
1721 return Err(format!(
1722 "real variable #{a} or #{b} is both related to another random real and part of \
1723 a coupling guard"
1724 ));
1725 }
1726 let mut seen = vec![false; self.kinds.len()];
1727 for d in &self.dists {
1728 if d.var >= n || std::mem::replace(&mut seen[d.var as usize], true) {
1729 return Err("a real variable has two `dist` constraints".into());
1730 }
1731 }
1732 if let Some(&(a, b)) = self.before.iter().find(|&&(a, b)| a >= n || b >= n) {
1733 return Err(format!(
1734 "`solve` names real variable #{a} or #{b} that does not exist"
1735 ));
1736 }
1737 let mut parent: Vec<Option<RealVarId>> = vec![None; self.kinds.len()];
1738 for &(a, b) in &edges {
1739 match parent[b as usize] {
1740 Some(q) if q != a => {
1741 return Err(format!(
1742 "real variable #{b} is constrained by guards on two other variables"
1743 ));
1744 }
1745 _ => parent[b as usize] = Some(a),
1746 }
1747 if !ordered_before(&self.before, a, b) {
1748 return Err(format!(
1749 "a guard on real variable #{a} constrains #{b}; add `solve` the first \
1750 `before` the second"
1751 ));
1752 }
1753 }
1754 Ok(())
1755 }
1756
1757 pub fn validate_ir(&self) -> Result<(), super::ir::IrError> {
1760 fn expr(e: &RealExpr) -> Result<(), super::ir::IrError> {
1761 match e {
1762 RealExpr::FromInt(x) => x.validate(),
1763 RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) => Ok(()),
1764 RealExpr::Neg(x) | RealExpr::ToShort(x) => expr(x),
1765 RealExpr::Add(a, b)
1766 | RealExpr::Sub(a, b)
1767 | RealExpr::Mul(a, b)
1768 | RealExpr::Div(a, b) => {
1769 expr(a)?;
1770 expr(b)
1771 }
1772 }
1773 }
1774 fn cond(c: &RealCond) -> Result<(), super::ir::IrError> {
1775 match c {
1776 RealCond::Const(_) => Ok(()),
1777 RealCond::Int(x) => x.validate(),
1778 RealCond::Cmp(_, l, r) => {
1779 expr(l)?;
1780 expr(r)
1781 }
1782 RealCond::Not(x) => cond(x),
1783 RealCond::And(xs) | RealCond::Or(xs) => xs.iter().try_for_each(cond),
1784 RealCond::Guard(p, t, e) => {
1785 cond(p)?;
1786 cond(t)?;
1787 cond(e)
1788 }
1789 }
1790 }
1791 self.conds.iter().try_for_each(cond)?;
1792 for d in &self.dists {
1793 for it in &d.items {
1794 it.weight.validate()?;
1795 match &it.span {
1796 RealSpan::Point(x) => expr(x)?,
1797 RealSpan::Range { lo, hi } => {
1798 lo.iter().chain(hi).try_for_each(expr)?;
1799 }
1800 }
1801 }
1802 if let Some(w) = &d.default {
1803 w.validate()?;
1804 }
1805 }
1806 Ok(())
1807 }
1808}
1809
1810fn check_cond(
1811 c: &RealCond,
1812 edges: &mut Vec<(RealVarId, RealVarId)>,
1813 joint: &mut Vec<RealVarId>,
1814) -> Result<(), String> {
1815 if c.vars().len() <= 1 {
1816 return Ok(());
1817 }
1818 match c {
1819 RealCond::And(xs) => xs.iter().try_for_each(|x| check_cond(x, edges, joint)),
1820 RealCond::Guard(p, t, e) => match p.vars()[..] {
1821 [] => {
1822 check_cond(t, edges, joint)?;
1823 check_cond(e, edges, joint)
1824 }
1825 [a] => {
1826 check_arm(t, a, edges)?;
1827 check_arm(e, a, edges)
1828 }
1829 _ => {
1830 joint.extend(c.vars());
1831 Ok(())
1832 }
1833 },
1834 _ => {
1835 joint.extend(c.vars());
1836 Ok(())
1837 }
1838 }
1839}
1840
1841fn check_affine(c: &RealCond) -> Result<(), String> {
1847 fn reads(e: &RealExpr, v: RealVarId) -> bool {
1848 let mut any = false;
1849 e.for_each_var(&mut |x| any |= x == v);
1850 any
1851 }
1852 fn affine(e: &RealExpr, v: RealVarId) -> Result<(), String> {
1853 match e {
1854 RealExpr::Const(..) | RealExpr::Var(_) | RealExpr::State(..) | RealExpr::FromInt(_) => {
1855 Ok(())
1856 }
1857 RealExpr::Neg(x) | RealExpr::ToShort(x) => affine(x, v),
1858 RealExpr::Add(a, b) | RealExpr::Sub(a, b) => {
1859 affine(a, v)?;
1860 affine(b, v)
1861 }
1862 RealExpr::Mul(a, b) => {
1863 if reads(a, v) && reads(b, v) {
1864 return Err(format!(
1865 "a product of two factors that both read real variable #{v} is not \
1866 affine in it"
1867 ));
1868 }
1869 affine(a, v)?;
1870 affine(b, v)
1871 }
1872 RealExpr::Div(a, b) => {
1873 if reads(b, v) {
1874 return Err(format!(
1875 "a quotient whose denominator reads real variable #{v} is not affine \
1876 in it"
1877 ));
1878 }
1879 affine(a, v)?;
1880 affine(b, v)
1881 }
1882 }
1883 }
1884 match c {
1885 RealCond::Cmp(_, l, r) => match c.vars()[..] {
1886 [v] => {
1887 affine(l, v)?;
1888 affine(r, v)
1889 }
1890 _ => Ok(()),
1891 },
1892 RealCond::Not(x) => check_affine(x),
1893 RealCond::And(xs) | RealCond::Or(xs) => xs.iter().try_for_each(check_affine),
1894 RealCond::Guard(p, _, _) => check_affine(p),
1895 RealCond::Const(_) | RealCond::Int(_) => Ok(()),
1896 }
1897}
1898
1899fn check_arm(
1900 c: &RealCond,
1901 a: RealVarId,
1902 edges: &mut Vec<(RealVarId, RealVarId)>,
1903) -> Result<(), String> {
1904 let vs = c.vars();
1905 if vs.len() <= 1 {
1906 if let [b] = vs[..]
1907 && b != a
1908 {
1909 edges.push((a, b));
1910 }
1911 return Ok(());
1912 }
1913 match c {
1914 RealCond::And(xs) => xs.iter().try_for_each(|x| check_arm(x, a, edges)),
1915 RealCond::Guard(p, t, e) if p.vars().is_empty() => {
1916 check_arm(t, a, edges)?;
1917 check_arm(e, a, edges)
1918 }
1919 RealCond::Guard(..) => {
1920 Err("a guard nested in a coupling guard couples two more variables".into())
1921 }
1922 _ => Err("a conjunct reads two random real variables".into()),
1923 }
1924}
1925
1926#[cfg(test)]
1927mod tests {
1928 use super::*;
1929
1930 const D: FloatKind = FloatKind::F64;
1931 const S: FloatKind = FloatKind::F32;
1932
1933 #[test]
1934 fn the_grid_is_order_preserving_and_round_trips() {
1935 for k in [D, S] {
1936 let xs = [
1937 -k.max_val(),
1938 -1.5,
1939 -1e-30,
1940 0.0,
1941 1e-30,
1942 1.0,
1943 2.5,
1944 k.max_val(),
1945 ]
1946 .map(|x| k.round(x));
1947 for w in xs.windows(2) {
1948 assert!(k.idx(w[0]) < k.idx(w[1]), "{k:?} {w:?}");
1949 }
1950 for x in xs {
1951 assert_eq!(k.val(k.idx(x)), x, "{k:?} {x}");
1952 }
1953 assert_eq!(k.idx(-0.0), 0);
1954 assert_eq!(k.val(k.idx(1.0)), 1.0);
1955 }
1956 assert_eq!(D.idx(f64::from_bits(1.0f64.to_bits() + 1)), D.idx(1.0) + 1);
1958 assert_eq!(
1959 S.val(S.idx(1.0) + 1),
1960 f64::from(f32::from_bits(1.0f32.to_bits() + 1))
1961 );
1962 }
1963
1964 #[test]
1965 fn ceil_and_floor_round_the_right_way_for_shortreal() {
1966 let up = f64::from(f32::from_bits(1.0f32.to_bits() + 1));
1967 let mid = (1.0 + up) / 2.0;
1968 assert_eq!(S.ceil_idx(mid), Some(S.idx(1.0) + 1));
1969 assert_eq!(S.floor_idx(mid), Some(S.idx(1.0)));
1970 assert_eq!(S.ceil_idx(1.0), Some(S.idx(1.0)));
1971 assert_eq!(S.floor_idx(1.0), Some(S.idx(1.0)));
1972 assert_eq!(S.ceil_idx(1e300), None);
1973 assert_eq!(S.floor_idx(1e300), Some(S.max_idx()));
1974 assert_eq!(S.ceil_idx(f64::NEG_INFINITY), Some(-S.max_idx()));
1975 assert_eq!(D.floor_idx(f64::NAN), None);
1976 }
1977
1978 #[test]
1979 fn set_algebra() {
1980 let a = RealSet::range(D, 0.0, 2.0);
1981 let b = RealSet::range(D, 1.0, 3.0);
1982 assert!(a.intersect(&b).contains(1.5) && !a.intersect(&b).contains(2.5));
1983 assert!(a.union(&b).contains(2.5) && !a.union(&b).contains(3.5));
1984 let c = a.complement();
1985 assert!(!c.contains(1.0) && c.contains(-1.0) && c.contains(2.5));
1986 assert_eq!(c.complement(), a);
1987 assert!(RealSet::full(D).complement().is_empty());
1988 assert_eq!(RealSet::empty(D).complement(), RealSet::full(D));
1989 let p = RealSet::point(D, 1.0);
1991 let q = RealSet::point(D, f64::from_bits(1.0f64.to_bits() + 1));
1992 assert_eq!(p.union(&q).intervals().len(), 1);
1993 let hole = RealSet::point(D, 1.0).complement();
1995 assert!(!hole.contains(1.0) && hole.contains(0.999) && hole.contains(1.001));
1996 assert!(RealSet::point(S, 0.1).is_empty());
1998 assert!(!RealSet::point(S, 0.5).is_empty());
1999 assert!(RealSet::range(D, 2.0, 1.0).is_empty());
2000 }
2001
2002 #[test]
2003 fn measure_does_not_overflow_on_the_full_range() {
2004 let m = RealSet::full(D).measure();
2005 assert!(m.is_finite() && m > 1e308);
2006 assert_eq!(RealSet::point(D, 3.0).measure(), 0.0);
2007 assert_eq!(RealSet::range(D, 1.0, 3.0).measure(), 1.0);
2008 }
2009
2010 #[test]
2011 fn sampling_stays_in_the_set_and_is_uniform_by_length() {
2012 let set = RealSet::range(D, 0.0, 2.0).union(&RealSet::range(D, 10.0, 12.0));
2013 let mut seed = 7u64;
2014 let (mut lo, mut hi, mut n_a) = (0u32, 0u32, 0u32);
2015 for _ in 0..4000 {
2016 let x = set.sample(&mut seed);
2017 assert!(set.contains(x), "{x}");
2018 if x < 5.0 {
2019 n_a += 1;
2020 }
2021 if x % 10.0 < 1.0 {
2022 lo += 1;
2023 } else {
2024 hi += 1;
2025 }
2026 }
2027 assert!((1850..=2150).contains(&n_a), "{n_a}");
2029 assert!((1850..=2150).contains(&lo), "{lo} vs {hi}");
2030 let full = RealSet::full(D);
2032 let xs: Vec<f64> = (0..200).map(|_| full.sample(&mut seed)).collect();
2033 assert!(xs.iter().all(|x| x.is_finite()));
2034 assert!(xs.iter().any(|&x| x < 0.0) && xs.iter().any(|&x| x > 0.0));
2035 }
2036
2037 #[test]
2038 fn a_point_set_is_sampled_over_its_points() {
2039 let set = RealSet::point(D, 3.0).union(&RealSet::point(D, 5.0));
2040 let mut seed = 1u64;
2041 let mut n3 = 0;
2042 for _ in 0..1000 {
2043 let x = set.sample(&mut seed);
2044 assert!(x == 3.0 || x == 5.0);
2045 n3 += usize::from(x == 3.0);
2046 }
2047 assert!((400..=600).contains(&n3), "{n3}");
2048 let sr = RealSet::range(S, 0.0, 1.0);
2050 for _ in 0..200 {
2051 let x = sr.sample(&mut seed);
2052 assert_eq!(f64::from(x as f32), x);
2053 assert!((0.0..=1.0).contains(&x));
2054 }
2055 }
2056
2057 fn v(i: u32) -> RealExpr {
2060 RealExpr::Var(i)
2061 }
2062 fn k(x: f64) -> RealExpr {
2063 RealExpr::Const(Real::new(x), D)
2064 }
2065 fn bx(e: RealExpr) -> Box<RealExpr> {
2066 Box::new(e)
2067 }
2068 fn cmp(op: CmpOp, l: RealExpr, r: RealExpr) -> RealCond {
2069 RealCond::Cmp(op, l, r)
2070 }
2071 fn imp(p: RealCond, t: RealCond) -> RealCond {
2072 RealCond::Guard(Box::new(p), Box::new(t), Box::new(RealCond::Const(true)))
2073 }
2074 fn problem(n: usize, conds: Vec<RealCond>) -> RealProblem {
2075 RealProblem {
2076 kinds: vec![D; n],
2077 conds,
2078 ..RealProblem::default()
2079 }
2080 }
2081 fn run(p: &RealProblem, seed: &mut u64) -> Result<Vec<f64>, RealError> {
2082 let known = vec![None; p.kinds.len()];
2083 solve(p, &known, &[], seed).map(|v| v.into_iter().map(|x| x.unwrap()).collect())
2084 }
2085 fn set_of(p: &RealProblem, c: &RealCond) -> RealSet {
2086 let ctx = Ctx {
2087 kinds: &p.kinds,
2088 vars: vec![None; p.kinds.len()],
2089 states: &[],
2090 };
2091 ctx.set_of(c, 0).unwrap()
2092 }
2093
2094 #[test]
2095 fn an_atom_is_exactly_the_floats_for_which_the_rounded_comparison_holds() {
2096 let p = problem(1, vec![]);
2099 let c = cmp(CmpOp::Lt, RealExpr::Div(bx(v(0)), bx(k(3.0))), k(1.0));
2100 let s = set_of(&p, &c);
2101 let (_, top) = *s.intervals().last().unwrap();
2102 assert!(D.val(top) / 3.0 < 1.0);
2103 assert!(D.val(top + 1) / 3.0 >= 1.0);
2104 assert_eq!(s.intervals().len(), 1);
2105 let c = cmp(CmpOp::Le, RealExpr::Sub(bx(k(5.0)), bx(v(0))), k(2.0));
2107 let s = set_of(&p, &c);
2108 assert!(s.contains(3.0) && !s.contains(f64::from_bits(3.0f64.to_bits() - 1)));
2109 let eq = set_of(&p, &cmp(CmpOp::Eq, v(0), k(2.5)));
2111 assert_eq!(eq, RealSet::point(D, 2.5));
2112 assert_eq!(set_of(&p, &cmp(CmpOp::Ne, v(0), k(2.5))), eq.complement());
2113 let gt = set_of(&p, &cmp(CmpOp::Gt, v(0), k(2.5)));
2114 let ge = set_of(&p, &cmp(CmpOp::Ge, v(0), k(2.5)));
2115 assert!(!gt.contains(2.5) && gt.contains(f64::from_bits(2.5f64.to_bits() + 1)));
2116 assert!(ge.contains(2.5) && !ge.contains(f64::from_bits(2.5f64.to_bits() - 1)));
2117 let c = cmp(CmpOp::Gt, RealExpr::Mul(bx(k(-2.0)), bx(v(0))), k(4.0));
2119 let s = set_of(&p, &c);
2120 assert!(s.contains(-2.0000000000000004) && !s.contains(-2.0));
2121 }
2122
2123 #[test]
2124 fn a_shortreal_atom_lives_on_the_binary32_grid() {
2125 let p = RealProblem {
2126 kinds: vec![S],
2127 ..RealProblem::default()
2128 };
2129 let ctx = Ctx {
2130 kinds: &p.kinds,
2131 vars: vec![None],
2132 states: &[],
2133 };
2134 let s = ctx.set_of(&cmp(CmpOp::Lt, v(0), k(1.0)), 0).unwrap();
2135 let below = f64::from(f32::from_bits(1.0f32.to_bits() - 1));
2136 assert!(s.contains(below) && !s.contains(1.0));
2137 let mut seed = 3;
2139 for _ in 0..50 {
2140 let x = s.sample(&mut seed);
2141 assert_eq!(f64::from(x as f32), x);
2142 }
2143 }
2144
2145 #[test]
2146 fn an_open_interval_is_uniform() {
2147 let p = problem(
2149 1,
2150 vec![RealCond::And(vec![
2151 cmp(CmpOp::Gt, v(0), k(0.0)),
2152 cmp(CmpOp::Lt, v(0), k(2.0)),
2153 ])],
2154 );
2155 let mut seed = 42u64;
2156 let mut bins = [0u32; 8];
2157 for _ in 0..8000 {
2158 let x = run(&p, &mut seed).unwrap()[0];
2159 assert!(x > 0.0 && x < 2.0, "{x}");
2160 bins[(x * 4.0) as usize] += 1;
2161 }
2162 for (i, b) in bins.iter().enumerate() {
2163 assert!((850..=1150).contains(b), "bin {i}: {bins:?}");
2164 }
2165 }
2166
2167 #[test]
2168 fn an_unsatisfiable_set_is_reported_and_draws_nothing() {
2169 let p = problem(
2170 1,
2171 vec![cmp(CmpOp::Gt, v(0), k(2.0)), cmp(CmpOp::Lt, v(0), k(1.0))],
2172 );
2173 let mut seed = 5;
2174 assert_eq!(run(&p, &mut seed), Err(RealError::Unsatisfiable));
2175 assert_eq!(seed, 5);
2176 let q = problem(1, vec![cmp(CmpOp::Lt, v(0), k(9.0))]);
2178 let r = solve(&q, &[Some(1.0)], &[], &mut seed).unwrap();
2179 assert_eq!(r, vec![None]);
2180 assert_eq!(seed, 5);
2181 assert_eq!(
2183 solve(&q, &[Some(10.0)], &[], &mut seed),
2184 Err(RealError::Unsatisfiable)
2185 );
2186 }
2187
2188 #[test]
2189 fn a_state_bound_is_read_per_call_and_an_unreadable_one_is_an_error() {
2190 let p = problem(1, vec![cmp(CmpOp::Lt, v(0), RealExpr::State(0, D))]);
2191 let mut seed = 1;
2192 let st = [Some(BigUint::from((-4.5f64).to_bits()))];
2193 let r = solve(&p, &[None], &st, &mut seed).unwrap();
2194 assert!(r[0].unwrap() < -4.5);
2195 assert_eq!(
2196 solve(&p, &[None], &[None], &mut seed),
2197 Err(RealError::StateUnreadable(0))
2198 );
2199 }
2200
2201 #[test]
2202 fn nan_and_division_by_zero_are_errors_not_silent_values() {
2203 let p = problem(
2204 1,
2205 vec![cmp(CmpOp::Lt, RealExpr::Div(bx(v(0)), bx(k(0.0))), k(1.0))],
2206 );
2207 let mut seed = 1;
2208 assert!(matches!(run(&p, &mut seed), Err(RealError::NonFinite(_))));
2209 }
2210
2211 #[test]
2212 fn a_non_affine_or_two_variable_conjunct_is_rejected() {
2213 let sq = cmp(CmpOp::Lt, RealExpr::Mul(bx(v(0)), bx(v(0))), k(4.0));
2214 let mut seed = 1;
2215 assert!(matches!(
2216 run(&problem(1, vec![sq.clone()]), &mut seed),
2217 Err(RealError::Shape(_))
2218 ));
2219 let why = problem(1, vec![sq]).check().unwrap_err();
2221 assert!(why.contains("not affine") && why.contains("#0"), "{why}");
2222 let recip = cmp(CmpOp::Gt, RealExpr::Div(bx(k(1.0)), bx(v(0))), k(2.0));
2223 assert!(
2224 problem(1, vec![recip])
2225 .check()
2226 .unwrap_err()
2227 .contains("denominator")
2228 );
2229 let scaled = cmp(CmpOp::Lt, RealExpr::Mul(bx(k(2.0)), bx(v(0))), k(4.0));
2232 assert!(problem(1, vec![scaled]).check().is_ok());
2233 }
2234
2235 #[test]
2236 fn conjuncts_relating_two_random_reals_are_solved_jointly() {
2237 let unit_range = |i: u32| [cmp(CmpOp::Ge, v(i), k(0.0)), cmp(CmpOp::Le, v(i), k(1.0))];
2241 let mut conds: Vec<RealCond> = unit_range(0).into_iter().chain(unit_range(1)).collect();
2242 conds.push(cmp(CmpOp::Lt, v(0), v(1)));
2243 conds.push(cmp(CmpOp::Lt, RealExpr::Add(bx(v(0)), bx(v(1))), k(1.0)));
2244 let p = problem(2, conds);
2245 assert!(p.check().is_ok());
2246 let mut seed = 5;
2247 let (mut lo_f, mut hi_g) = (f64::MAX, f64::MIN);
2248 for _ in 0..500 {
2249 let r = run(&p, &mut seed).unwrap();
2250 assert!(r[0] < r[1] && r[0] + r[1] < 1.0, "{r:?}");
2251 assert!((0.0..=1.0).contains(&r[0]) && (0.0..=1.0).contains(&r[1]));
2252 lo_f = lo_f.min(r[0]);
2253 hi_g = hi_g.max(r[1]);
2254 }
2255 assert!(lo_f < 0.05 && hi_g > 0.8, "{lo_f} {hi_g}");
2256 let eq = problem(2, vec![cmp(CmpOp::Eq, v(0), v(1))]);
2258 assert!(eq.check().is_ok());
2259 let mut seed = 1;
2260 assert!(matches!(run(&eq, &mut seed), Err(RealError::Shape(_))));
2261 let bad = problem(
2264 2,
2265 vec![cmp(CmpOp::Lt, v(0), v(1)), cmp(CmpOp::Gt, v(0), v(1))],
2266 );
2267 assert!(matches!(run(&bad, &mut seed), Err(RealError::Shape(_))));
2268 }
2269
2270 #[test]
2271 fn a_guard_with_a_state_predicate_follows_figure_18_3() {
2272 let g = |st: Option<u64>| {
2274 let p = problem(
2275 1,
2276 vec![imp(
2277 RealCond::Int(Expr::Binary(
2278 super::super::ir::BinOp::Eq,
2279 Box::new(Expr::State(0, super::super::ir::Ty::unsigned(8))),
2280 Box::new(Expr::constant(super::super::ir::Ty::unsigned(8), 1u64)),
2281 super::super::ir::Ty::BIT,
2282 )),
2283 cmp(CmpOp::Gt, v(0), k(5.0)),
2284 )],
2285 );
2286 let mut seed = 9;
2287 solve(&p, &[None], &[st.map(BigUint::from)], &mut seed)
2288 };
2289 assert!(g(Some(1)).unwrap()[0].unwrap() > 5.0);
2290 assert!(g(Some(2)).is_ok());
2291 assert_eq!(g(None), Err(RealError::GuardError));
2292 }
2293
2294 fn coupled() -> RealProblem {
2296 RealProblem {
2297 kinds: vec![D, D],
2298 conds: vec![
2299 RealCond::And(vec![
2300 cmp(CmpOp::Ge, v(0), k(0.0)),
2301 cmp(CmpOp::Le, v(0), k(10.0)),
2302 ]),
2303 imp(cmp(CmpOp::Lt, v(0), k(5.0)), cmp(CmpOp::Gt, v(1), k(20.0))),
2304 cmp(CmpOp::Lt, v(1), k(10.0)),
2305 ],
2306 dists: vec![],
2307 before: vec![(0, 1)],
2308 }
2309 }
2310
2311 #[test]
2312 fn a_coupled_guard_needs_its_order_and_is_solved_staged() {
2313 let mut p = coupled();
2314 assert!(p.check().is_ok());
2315 let mut seed = 11;
2316 for _ in 0..300 {
2319 let r = run(&p, &mut seed).unwrap();
2320 assert!((5.0..=10.0).contains(&r[0]), "{r:?}");
2321 assert!(r[1] < 10.0);
2322 }
2323 p.before.clear();
2324 let e = p.check().unwrap_err();
2325 assert!(e.contains("before"), "{e}");
2326 assert!(matches!(run(&p, &mut seed), Err(RealError::Shape(_))));
2327 }
2328
2329 #[test]
2330 fn a_coupled_guard_draws_the_child_from_the_arm_the_parent_chose() {
2331 let both = |lo: f64, hi: f64| {
2333 RealCond::And(vec![
2334 cmp(CmpOp::Ge, v(1), k(lo)),
2335 cmp(CmpOp::Le, v(1), k(hi)),
2336 ])
2337 };
2338 let p = RealProblem {
2339 kinds: vec![D, D],
2340 conds: vec![
2341 RealCond::And(vec![
2342 cmp(CmpOp::Ge, v(0), k(0.0)),
2343 cmp(CmpOp::Le, v(0), k(10.0)),
2344 ]),
2345 RealCond::Guard(
2346 Box::new(cmp(CmpOp::Lt, v(0), k(5.0))),
2347 Box::new(both(20.0, 30.0)),
2348 Box::new(both(-30.0, -20.0)),
2349 ),
2350 ],
2351 dists: vec![],
2352 before: vec![(0, 1)],
2353 };
2354 assert!(p.check().is_ok());
2355 let mut seed = 77;
2356 let mut low = 0;
2357 for _ in 0..2000 {
2358 let r = run(&p, &mut seed).unwrap();
2359 assert_eq!(r[0] < 5.0, r[1] > 0.0, "{r:?}");
2360 assert!(r[1].abs() >= 20.0 && r[1].abs() <= 30.0);
2361 low += usize::from(r[0] < 5.0);
2362 }
2363 assert!((900..=1100).contains(&low), "{low}");
2365 }
2366
2367 #[test]
2368 fn coupling_shapes_the_solver_cannot_do_are_named() {
2369 let p = RealProblem {
2371 kinds: vec![D, D, D],
2372 conds: vec![
2373 imp(cmp(CmpOp::Lt, v(0), k(5.0)), cmp(CmpOp::Gt, v(1), k(0.0))),
2374 imp(cmp(CmpOp::Lt, v(2), k(5.0)), cmp(CmpOp::Gt, v(1), k(1.0))),
2375 ],
2376 dists: vec![],
2377 before: vec![(0, 1), (2, 1)],
2378 };
2379 assert!(p.check().unwrap_err().contains("two other variables"));
2380 let q = problem(
2383 3,
2384 vec![imp(
2385 RealCond::And(vec![
2386 cmp(CmpOp::Lt, v(0), k(5.0)),
2387 cmp(CmpOp::Lt, v(1), k(5.0)),
2388 ]),
2389 cmp(CmpOp::Gt, v(2), k(0.0)),
2390 )],
2391 );
2392 assert!(q.check().is_ok());
2393 let mut seed = 3;
2394 for _ in 0..100 {
2395 let s = run(&q, &mut seed).unwrap();
2396 assert!(!(s[0] < 5.0 && s[1] < 5.0) || s[2] > 0.0, "{s:?}");
2397 }
2398 let r = RealProblem {
2400 before: vec![(0, 1), (1, 2)],
2401 ..problem(
2402 3,
2403 vec![imp(
2404 cmp(CmpOp::Lt, v(0), k(5.0)),
2405 imp(cmp(CmpOp::Lt, v(1), k(5.0)), cmp(CmpOp::Gt, v(2), k(0.0))),
2406 )],
2407 )
2408 };
2409 assert!(r.check().unwrap_err().contains("nested"));
2410 }
2411
2412 fn weight(n: u64) -> Expr {
2413 Expr::constant(super::super::ir::Ty::unsigned(8), n)
2414 }
2415
2416 #[test]
2417 fn a_real_dist_weights_points_and_spreads_ranges_over_their_length() {
2418 let dist = RealDist {
2421 var: 0,
2422 items: vec![
2423 RealDistItem {
2424 span: RealSpan::Point(k(1.0)),
2425 weight: weight(1),
2426 },
2427 RealDistItem {
2428 span: RealSpan::Range {
2429 lo: Some(k(2.0)),
2430 hi: Some(k(4.0)),
2431 },
2432 weight: weight(3),
2433 },
2434 ],
2435 default: None,
2436 };
2437 let mut p = problem(1, vec![cmp(CmpOp::Lt, v(0), k(3.0))]);
2438 p.dists.push(dist);
2439 let mut seed = 21;
2440 let (mut ones, n) = (0, 6000);
2441 for _ in 0..n {
2442 let x = run(&p, &mut seed).unwrap()[0];
2443 assert!(x == 1.0 || (2.0..3.0).contains(&x), "{x}");
2444 ones += usize::from(x == 1.0);
2445 }
2446 let want = f64::from(n as u32) * 0.4;
2447 assert!((ones as f64 - want).abs() < 150.0, "{ones} vs {want}");
2448 }
2449
2450 #[test]
2451 fn a_dist_default_covers_the_complement_and_zero_weights_are_excluded() {
2452 let dist = RealDist {
2454 var: 0,
2455 items: vec![RealDistItem {
2456 span: RealSpan::Range {
2457 lo: Some(k(0.0)),
2458 hi: Some(k(1.0)),
2459 },
2460 weight: weight(0),
2461 }],
2462 default: Some(weight(1)),
2463 };
2464 let mut p = problem(
2465 1,
2466 vec![RealCond::And(vec![
2467 cmp(CmpOp::Ge, v(0), k(0.0)),
2468 cmp(CmpOp::Le, v(0), k(2.0)),
2469 ])],
2470 );
2471 p.dists.push(dist);
2472 let mut seed = 2;
2473 for _ in 0..500 {
2474 let x = run(&p, &mut seed).unwrap()[0];
2475 assert!(x > 1.0 && x <= 2.0, "{x}");
2476 }
2477 let dollar = RealDist {
2479 var: 0,
2480 items: vec![RealDistItem {
2481 span: RealSpan::Range {
2482 lo: Some(k(5.0)),
2483 hi: None,
2484 },
2485 weight: weight(1),
2486 }],
2487 default: None,
2488 };
2489 let mut q = problem(1, vec![]);
2490 q.dists.push(dollar);
2491 for _ in 0..50 {
2492 assert!(run(&q, &mut seed).unwrap()[0] >= 5.0);
2493 }
2494 assert_eq!(
2496 solve(&q, &[Some(1.0)], &[], &mut seed),
2497 Err(RealError::Unsatisfiable)
2498 );
2499 }
2500
2501 #[test]
2502 fn a_point_in_a_range_item_falls_back_to_the_nominal_weights() {
2503 let dist = RealDist {
2506 var: 0,
2507 items: vec![RealDistItem {
2508 span: RealSpan::Range {
2509 lo: Some(k(0.0)),
2510 hi: Some(k(10.0)),
2511 },
2512 weight: weight(1),
2513 }],
2514 default: None,
2515 };
2516 let mut p = problem(1, vec![cmp(CmpOp::Eq, v(0), k(3.0))]);
2517 p.dists.push(dist);
2518 let mut seed = 4;
2519 assert_eq!(run(&p, &mut seed).unwrap(), vec![3.0]);
2520 }
2521
2522 #[test]
2523 fn a_negative_dist_weight_is_an_error() {
2524 let neg = Expr::constant(super::super::ir::Ty::signed(8), 255u64);
2525 let mut p = problem(1, vec![]);
2526 p.dists.push(RealDist {
2527 var: 0,
2528 items: vec![RealDistItem {
2529 span: RealSpan::Point(k(1.0)),
2530 weight: neg,
2531 }],
2532 default: None,
2533 });
2534 let mut seed = 1;
2535 assert_eq!(run(&p, &mut seed), Err(RealError::BadWeight));
2536 }
2537
2538 #[test]
2539 fn an_integral_state_converts_to_real_signed_or_unsigned() {
2540 use super::super::ir::Ty;
2541 let zstate = RealExpr::FromInt(Box::new(Expr::State(0, Ty::signed(32))));
2542 let ctx = Ctx {
2543 kinds: &[],
2544 vars: vec![],
2545 states: &[Some(BigUint::from(0xffff_ff9cu64))],
2546 };
2547 assert_eq!(ctx.eval(&zstate).unwrap(), -100.0);
2548 let unsigned = RealExpr::FromInt(Box::new(Expr::State(0, Ty::unsigned(32))));
2549 assert_eq!(ctx.eval(&unsigned).unwrap(), f64::from(0xffff_ff9cu32));
2550 }
2551
2552 #[test]
2553 fn the_lrm_real_constraint_example_is_solved_with_its_ordering() {
2554 let (zs, low, min, nom, max) = (-100.0, 0.7, 1.43, 3.3, 3.65);
2557 let range = |lo: f64, hi: f64, w: u64| RealDistItem {
2558 span: RealSpan::Range {
2559 lo: Some(k(lo)),
2560 hi: Some(k(hi)),
2561 },
2562 weight: weight(w),
2563 };
2564 let point = |x: f64, w: u64| RealDistItem {
2565 span: RealSpan::Point(k(x)),
2566 weight: weight(w),
2567 };
2568 let p = RealProblem {
2569 kinds: vec![D, D],
2570 conds: vec![imp(
2571 RealCond::And(vec![
2572 cmp(CmpOp::Ge, v(0), k(low)),
2573 cmp(CmpOp::Le, v(0), k(min)),
2574 ]),
2575 cmp(CmpOp::Eq, v(1), k(zs)),
2576 )],
2577 dists: vec![
2578 RealDist {
2579 var: 0,
2580 items: vec![
2581 point(zs, 5),
2582 range(low, min, 1),
2583 range(nom - nom * 0.01, nom + nom * 0.01, 13),
2584 range(min, max, 1),
2585 ],
2586 default: None,
2587 },
2588 RealDist {
2589 var: 1,
2590 items: vec![point(zs, 1), range(min, max, 20)],
2591 default: None,
2592 },
2593 ],
2594 before: vec![(0, 1)],
2595 };
2596 assert!(p.check().is_ok());
2597 let mut seed = 99;
2598 let n = 12_000;
2599 let (mut a_z, mut b_z_when_low) = (0, (0, 0));
2600 for _ in 0..n {
2601 let r = run(&p, &mut seed).unwrap();
2602 let (a, b) = (r[0], r[1]);
2603 assert!(a == zs || (low..=max).contains(&a), "{a}");
2604 assert!(b == zs || (min..=max).contains(&b), "{b}");
2605 if (low..=min).contains(&a) {
2606 assert_eq!(b, zs, "{a} {b}");
2607 b_z_when_low.0 += 1;
2608 } else if b == zs {
2609 b_z_when_low.1 += 1;
2610 }
2611 a_z += usize::from(a == zs);
2612 }
2613 let total = 5.0 + 1.0 + 13.0 + 1.0;
2616 let want = f64::from(n as u32) * 5.0 / total;
2617 assert!((a_z as f64 - want).abs() < 250.0, "{a_z} vs {want}");
2618 assert!(b_z_when_low.0 > 0 && b_z_when_low.1 > 0);
2620 }
2621
2622 #[test]
2623 fn check_rejects_a_second_dist_on_one_variable() {
2624 let d = |var| RealDist {
2625 var,
2626 items: vec![],
2627 default: None,
2628 };
2629 let mut p = problem(1, vec![]);
2630 p.dists = vec![d(0), d(0)];
2631 assert!(p.check().is_err());
2632 p.dists = vec![d(0)];
2633 assert!(p.check().is_ok());
2634 }
2635}