Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
doug-q committed May 14, 2024
1 parent 8ee0273 commit 9d9e560
Show file tree
Hide file tree
Showing 10 changed files with 97 additions and 41 deletions.
2 changes: 1 addition & 1 deletion hugr/src/hugr/serialize/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ pub fn check_hugr_roundtrip(hugr: &Hugr, check_schema: bool) -> Hugr {

// for now this is only used in property testing, so otherwise configured out to
// avoid unused warnings.
#[cfg(feature="proptest")]
#[cfg(feature = "proptest")]
fn check_testing_roundtrip(t: impl Into<TestingModel>) {
let before = Versioned::new(t.into());
let after_strict = ser_roundtrip_validate(&before, Some(&TESTING_SCHEMA_STRICT));
Expand Down
3 changes: 1 addition & 2 deletions hugr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,10 @@
// Unstable check, may cause false positives.
// https://github.com/rust-lang/rust-clippy/issues/5112
#![warn(clippy::debug_assert_with_mut_call)]

// proptest-derive generates many of these warnings.
// https://github.com/rust-lang/rust/issues/120363
// https://github.com/proptest-rs/proptest/issues/447
#![cfg_attr(all(test,feature = "proptest"), allow(non_local_definitions))]
#![cfg_attr(all(test, feature = "proptest"), allow(non_local_definitions))]

pub mod algorithm;
pub mod builder;
Expand Down
2 changes: 1 addition & 1 deletion hugr/src/ops/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ mod test {
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
use proptest::collection::vec;
let signed_strat = (..=LOG_WIDTH_MAX).prop_flat_map(|log_width| {
use std::i64;
use i64;
let max_val = (2u64.pow(log_width as u32) / 2) as i64;
let min_val = -max_val - 1;
(min_val..=max_val).prop_map(move |v| {
Expand Down
47 changes: 38 additions & 9 deletions hugr/src/proptest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,32 @@ use smol_str::SmolStr;

use crate::Hugr;

#[derive(Clone, Copy, Debug)]
pub struct TypeDepth(usize);

impl TypeDepth {
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
/// The types [Type], [TypeEnum], [SumType], [FunctionType], [TypeArg],
/// [TypeParam], as well as several others, form a mutually recursive heirarchy.
///
/// The proptest `Strategy::prop_recursive` is inadequate to generate values for
/// these types. Instead, ther Arbitrary instances take a `RecursionDepth` as
/// their (or part of their) [Arbitrary::Parameters]. We then use that parameter
/// to generate children of that value. Usually we forward it unchanged, but in
/// crucial locations we instead forward the `descend` of it.
///
/// Consider the tree of values generated. Each node is labelled with a
/// [RecursionDepth].
///
/// Consider a path between two nodes of the same type(e.g. two [Type]s, or two
/// [FunctionType]s). The path must be decreasing in [RecursionDepth] because
/// each child's [RecursionDepth] is derived from it's parents.
///
/// We must maintain the invariant that the [RecursionDepth] of the start of the
/// path is strictly greater than the [RecursionDepth] of the end of the path.
///
/// With this invariant in place we are guaranteed to terminate in producing a
/// value, because there are only finitely many different types a node can take.
pub struct RecursionDepth(usize);

impl RecursionDepth {
const DEFAULT_RECURSION_DEPTH: usize = 4;
pub fn descend(&self) -> Self {
if self.leaf() {
*self
Expand All @@ -20,17 +42,24 @@ impl TypeDepth {
pub fn leaf(&self) -> bool {
self.0 == 0
}

pub fn new() -> Self {
Self(Self::DEFAULT_RECURSION_DEPTH)
}
}

impl Default for TypeDepth {
impl Default for RecursionDepth {
fn default() -> Self {
Self(3)
Self::new()
}
}

impl From<usize> for TypeDepth {
fn from(s: usize) -> Self {
Self(s)
impl<I: TryInto<usize>> From<I> for RecursionDepth
where
<I as TryInto<usize>>::Error: std::fmt::Debug,
{
fn from(s: I) -> Self {
Self(s.try_into().unwrap())
}
}

Expand Down
12 changes: 6 additions & 6 deletions hugr/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl EdgeKind {
#[derive(
Copy, Default, Clone, PartialEq, Eq, Hash, Debug, derive_more::Display, Serialize, Deserialize,
)]
#[cfg_attr(all(test,feature = "proptest"), derive(proptest_derive::Arbitrary))]
#[cfg_attr(all(test, feature = "proptest"), derive(proptest_derive::Arbitrary))]
/// Bounds on the valid operations on a type in a HUGR program.
pub enum TypeBound {
/// The equality operation is valid on this type.
Expand Down Expand Up @@ -451,7 +451,6 @@ pub(crate) mod test {

use crate::extension::prelude::USIZE_T;


#[test]
fn construct() {
let t: Type = Type::new_tuple(vec![
Expand Down Expand Up @@ -485,12 +484,12 @@ pub(crate) mod test {
#[cfg(feature = "proptest")]
mod proptest {
use crate::ops::AliasDecl;
use crate::proptest::TypeDepth;
use crate::proptest::RecursionDepth;
use crate::types::custom::test::proptest::CustomTypeArbitraryParameters;
use crate::types::{CustomType, FunctionType, SumType, TypeBound, TypeEnum, TypeRow};
use ::proptest::prelude::*;
impl Arbitrary for super::TypeEnum {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
prop_oneof![
Expand All @@ -506,7 +505,7 @@ pub(crate) mod test {
}

impl Arbitrary for super::SumType {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
use proptest::collection::vec;
Expand All @@ -521,9 +520,10 @@ pub(crate) mod test {
}

impl Arbitrary for super::Type {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
// We descend here, because a TypeEnum may contain a Type
any_with::<TypeEnum>(depth.descend())
.prop_map(Self::new)
.boxed()
Expand Down
15 changes: 8 additions & 7 deletions hugr/src/types/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,24 +144,24 @@ pub mod test {
#[cfg(feature = "proptest")]
pub mod proptest {
use crate::extension::ExtensionId;
use crate::proptest::TypeDepth;
use crate::proptest::any_nonempty_string;
use crate::proptest::RecursionDepth;
use crate::types::type_param::TypeArg;
use crate::types::{CustomType, TypeBound};
use ::proptest::prelude::*;
use ::proptest::collection::vec;
use crate::proptest::any_nonempty_string;
use ::proptest::prelude::*;

#[derive(Default)]
pub struct CustomTypeArbitraryParameters(TypeDepth, Option<TypeBound>);
pub struct CustomTypeArbitraryParameters(RecursionDepth, Option<TypeBound>);

impl From<TypeDepth> for CustomTypeArbitraryParameters {
fn from(v: TypeDepth) -> Self {
impl From<RecursionDepth> for CustomTypeArbitraryParameters {
fn from(v: RecursionDepth) -> Self {
Self::new(v)
}
}

impl CustomTypeArbitraryParameters {
pub fn new(depth: TypeDepth) -> Self {
pub fn new(depth: RecursionDepth) -> Self {
Self(depth, None)
}

Expand All @@ -181,6 +181,7 @@ pub mod test {
let args = if depth.leaf() {
Just(vec![]).boxed()
} else {
// a TypeArg may contain a CustomType, so we descend here
vec(any_with::<TypeArg>(depth.descend()), 0..3).boxed()
};
(any_nonempty_string(), args, any::<ExtensionId>(), bound)
Expand Down
19 changes: 15 additions & 4 deletions hugr/src/types/poly_func.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ use super::{FunctionType, Substitution};
#[derive(
Clone, PartialEq, Debug, Default, Eq, derive_more::Display, serde::Serialize, serde::Deserialize,
)]
#[cfg_attr(all(test,feature = "proptest"), derive(proptest_derive::Arbitrary), proptest(params = "crate::proptest::TypeDepth"))]
#[cfg_attr(
all(test, feature = "proptest"),
derive(proptest_derive::Arbitrary),
proptest(params = "crate::proptest::RecursionDepth")
)]
#[display(
fmt = "forall {}. {}",
"params.iter().map(ToString::to_string).join(\" \")",
Expand All @@ -25,10 +29,18 @@ pub struct PolyFuncType {
/// The declared type parameters, i.e., these must be instantiated with
/// the same number of [TypeArg]s before the function can be called. This
/// defines the indices used by variables inside the body.
#[cfg_attr(all(test,feature = "proptest"), proptest(strategy = "::proptest::collection::vec(::proptest::prelude::any_with::<TypeParam>(params), 0..3)"))]
#[cfg_attr(
all(test, feature = "proptest"),
proptest(
strategy = "::proptest::collection::vec(::proptest::prelude::any_with::<TypeParam>(params), 0..3)"
)
)]
params: Vec<TypeParam>,
/// Template for the function. May contain variables up to length of [Self::params]
#[cfg_attr(all(test,feature = "proptest"), proptest(strategy = "::proptest::prelude::any_with::<FunctionType>(params)"))]
#[cfg_attr(
all(test, feature = "proptest"),
proptest(strategy = "::proptest::prelude::any_with::<FunctionType>(params)")
)]
body: FunctionType,
}

Expand Down Expand Up @@ -335,5 +347,4 @@ pub(crate) mod test {
)?;
Ok(())
}

}
18 changes: 14 additions & 4 deletions hugr/src/types/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,32 @@ use super::{subst_row, Substitution, Type, TypeRow};
use crate::extension::{ExtensionRegistry, ExtensionSet, SignatureError};
use crate::{Direction, IncomingPort, OutgoingPort, Port};

#[cfg(all(test,feature = "proptest"))]
#[cfg(all(test, feature = "proptest"))]
use ::proptest::prelude::*;

#[derive(Clone, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[cfg_attr(all(test,feature = "proptest"), derive(proptest_derive::Arbitrary), proptest(params = "crate::proptest::TypeDepth"))]
#[cfg_attr(
all(test, feature = "proptest"),
derive(proptest_derive::Arbitrary),
proptest(params = "crate::proptest::RecursionDepth")
)]
/// Describes the edges required to/from a node, and thus, also the type of a [Graph].
/// This includes both the concept of "signature" in the spec,
/// and also the target (value) of a call (static).
///
/// [Graph]: crate::ops::constant::Value::Function
pub struct FunctionType {
/// Value inputs of the function.
#[cfg_attr(all(test,feature = "proptest"), proptest(strategy = "any_with::<TypeRow>(params)"))]
#[cfg_attr(
all(test, feature = "proptest"),
proptest(strategy = "any_with::<TypeRow>(params)")
)]
pub input: TypeRow,
/// Value outputs of the function.
#[cfg_attr(all(test,feature = "proptest"), proptest(strategy = "any_with::<TypeRow>(params)"))]
#[cfg_attr(
all(test, feature = "proptest"),
proptest(strategy = "any_with::<TypeRow>(params)")
)]
pub output: TypeRow,
/// The extension requirements which are added by the operation
pub extension_reqs: ExtensionSet,
Expand Down
16 changes: 11 additions & 5 deletions hugr/src/types/type_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,11 +408,11 @@ mod test {

use super::super::{CustomTypeArg, TypeArg, TypeArgVariable, TypeParam, UpperBound};
use crate::extension::ExtensionSet;
use crate::proptest::{any_serde_yaml_value, TypeDepth};
use crate::proptest::{any_serde_yaml_value, RecursionDepth};
use crate::types::{CustomType, Type, TypeBound};

impl Arbitrary for CustomTypeArg {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
(
Expand All @@ -427,7 +427,7 @@ mod test {
}

impl Arbitrary for TypeArgVariable {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
(any::<usize>(), any_with::<TypeParam>(depth))
Expand All @@ -437,7 +437,7 @@ mod test {
}

impl Arbitrary for TypeParam {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
use proptest::collection::vec;
Expand All @@ -453,6 +453,7 @@ mod test {
.boxed(),
]);
if !depth.leaf() {
// we descend here because we these constructors contain TypeParams
strat = strat
.or(any_with::<Self>(depth.descend())
.prop_map(|x| Self::List { param: Box::new(x) })
Expand All @@ -467,7 +468,7 @@ mod test {
}

impl Arbitrary for TypeArg {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
use proptest::collection::vec;
Expand All @@ -483,11 +484,16 @@ mod test {
any_with::<CustomTypeArg>(depth)
.prop_map(|arg| Self::Opaque { arg })
.boxed(),
// TODO this is a bit dodgy, TypeArgVariables are supposed
// to be constructed from TypeArg::new_var_use. We are only
// using this instance for serialisation now, but if we want
// to generate valid TypeArgs this will need to change.
any_with::<TypeArgVariable>(depth)
.prop_map(|v| Self::Variable { v })
.boxed(),
]);
if !depth.leaf() {
// We descend here because this constructor contains TypeArg>
strat = strat.or(vec(any_with::<Self>(depth.descend()), 0..3)
.prop_map(|elems| Self::Sequence { elems })
.boxed());
Expand Down
4 changes: 2 additions & 2 deletions hugr/src/types/type_row.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@ impl DerefMut for TypeRow {
mod test {
#[cfg(feature = "proptest")]
mod proptest {
use crate::proptest::TypeDepth;
use crate::proptest::RecursionDepth;
use crate::{type_row, types::Type};
use ::proptest::prelude::*;

impl Arbitrary for super::super::TypeRow {
type Parameters = TypeDepth;
type Parameters = RecursionDepth;
type Strategy = BoxedStrategy<Self>;
fn arbitrary_with(depth: Self::Parameters) -> Self::Strategy {
use proptest::collection::vec;
Expand Down

0 comments on commit 9d9e560

Please sign in to comment.