Skip to content

Commit

Permalink
Use a 64-bit USize as the sole integer type in the core (#376)
Browse files Browse the repository at this point in the history
Closes #356 .
  • Loading branch information
cqc-alec authored Aug 9, 2023
1 parent 9720b2f commit cb06bc1
Show file tree
Hide file tree
Showing 15 changed files with 97 additions and 189 deletions.
31 changes: 15 additions & 16 deletions specification/hugr.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ may be a `FuncDefn`, `TailLoop`, `DFG`, `Case` or `DFB` node.
#### `ErrorType`

- There is some type of errors, perhaps just a string, or
`Tuple(Int,String)` with some errorcode, that is returned along with
`Tuple(USize,String)` with some errorcode, that is returned along with
the fact that the graph/program panicked.

#### Catch
Expand Down Expand Up @@ -837,7 +837,7 @@ resources:
types:
- name: QubitVector
# Opaque types can take type arguments, with specified names
params: [["size", Int]]
params: [["size", USize]]
operations:
- name: measure
description: "measure a qubit"
Expand Down Expand Up @@ -865,9 +865,9 @@ resources:
- name: MatMul
description: "Multiply matrices of statically-known size"
params: # per-node values passed to type-scheme-interpreter and used in signature
- i: Int
- j: Int
- k: Int
- i: USize
- j: USize
- k: USize
signature:
inputs: [["a", Array<i>(Array<j>(F64))], ["b", Array<j>(Array<k>(F64))]]
outputs: [[null, Array<i>(Array<k>(F64))]]
Expand All @@ -876,7 +876,7 @@ resources:
- name: max_float
description: "Variable number of inputs"
params:
- n: Int
- n: USize
signature:
# Where an element of a signature has three subelements, the third is the number of repeats
inputs: [[null, F64, n]] # (defaulting to 1 if omitted)
Expand All @@ -885,8 +885,8 @@ resources:
description: "Concatenate two arrays. Resource provides a compute_signature implementation."
params:
- t: Type # Classic or Quantum
- i: Int
- j: Int
- i: USize
- j: USize
# inputs could be: Array<i>(t), Array<j>(t)
# outputs would be, in principle: Array<i+j>(t)
# - but default type scheme interpreter does not support such addition
Expand All @@ -896,8 +896,8 @@ resources:
params:
- r: ResourceSet
signature:
inputs: [[null, Graph[r](Int -> Int)], ["arg", Int]]
outputs: [[null, Int]]
inputs: [[null, Graph[r](USize -> USize)], ["arg", USize]]
outputs: [[null, USize]]
resources: r # Indicates that running this operation also invokes resources r
lowering:
file: "graph_op_hugr.bin"
Expand All @@ -908,7 +908,7 @@ The declaration of the `params` uses a language that is a distinct, simplified
form of the [Type System](#type-system) - writing terminals that appear in the YAML in quotes,
the value of each member of `params` is given by the following production:
```
TypeParam ::= "Type" | "ClassicType" | Int | "List"(TypeParam)
TypeParam ::= "Type" | "ClassicType" | USize | "List"(TypeParam)
```

**Implementation note** Reading this format into Rust is made easy by `serde` and
Expand Down Expand Up @@ -1000,7 +1000,7 @@ Container(T) ::= Tuple(#(T))
| Array<u64>(T)
| NewType(Name, T)
| Sum (#(T))
ClassicType ::= int<N>
ClassicType ::= USize
| Var(X)
| String
| Graph[R](#, #)
Expand All @@ -1019,15 +1019,14 @@ sent down Static edges.
Function signatures are made up of *rows* (\#), which consist of an
arbitrary number of SimpleTypes, plus a resource spec.

ClassicTypes such as `int<N>` (where `N` is the bit-width) are fixed-size, as is
Qubit.
The `USize` type represents 64-bit unsigned integers.

ClassicTypes such as `USize` are fixed-size, as is Qubit.
`Sum` is a disjoint union tagged by unsigned int; `Tuple`s have
statically-known number and type of elements, as does `Array<N>` (where
N is a static constant). These types are also fixed-size if their
components are.

For integer types, the width is provided in the type, and signedness is left unspecified to be interpreted by operations. The width is allowed to be 2^i for i in the range [0,7], so the allowed integer types are [I1, I2, I4, ... , I128].

Container types are defined in terms of statically-known element types.
Besides `Array<N>`, `Sum` and `Tuple`, these also include the variable-sized
types: `Graph`. `NewType`
Expand Down
2 changes: 1 addition & 1 deletion src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ mod test {
use super::{DataflowSubContainer, HugrBuilder};

pub(super) const NAT: SimpleType = SimpleType::Classic(ClassicType::i64());
pub(super) const BIT: SimpleType = SimpleType::Classic(ClassicType::bit());
pub(super) const BIT: SimpleType = SimpleType::Classic(ClassicType::usize());
pub(super) const QB: SimpleType = SimpleType::Qubit;

/// Wire up inputs of a Dataflow container to the outputs.
Expand Down
8 changes: 4 additions & 4 deletions src/builder/tail_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ mod test {
#[test]
fn basic_loop() -> Result<(), BuildError> {
let build_result: Result<Hugr, ValidationError> = {
let mut loop_b = TailLoopBuilder::new(vec![], vec![BIT], vec![ClassicType::i64()])?;
let mut loop_b = TailLoopBuilder::new(vec![], vec![BIT], vec![ClassicType::usize()])?;
let [i1] = loop_b.input_wires_arr();
let const_wire = loop_b.add_load_const(Const::i64(1)?)?;
let const_wire = loop_b.add_load_const(Const::usize(1)?)?;

let break_wire = loop_b.make_break(loop_b.loop_signature()?.clone(), [const_wire])?;
loop_b.set_outputs(break_wire, [i1])?;
Expand All @@ -135,7 +135,7 @@ mod test {
let [b1] = fbuild.input_wires_arr();
let loop_id = {
let mut loop_b = fbuild.tail_loop_builder(
vec![(ClassicType::bit(), b1)],
vec![(ClassicType::usize(), b1)],
vec![],
classic_row![ClassicType::i64()],
)?;
Expand All @@ -160,7 +160,7 @@ mod test {
let mut branch_1 = conditional_b.case_builder(1)?;
let [_b1] = branch_1.input_wires_arr();

let wire = branch_1.add_load_const(Const::i64(2)?)?;
let wire = branch_1.add_load_const(Const::usize(2)?)?;
let break_wire = branch_1.make_break(signature, [wire])?;
branch_1.finish_with_outputs([break_wire])?;

Expand Down
23 changes: 23 additions & 0 deletions src/extensions/arithmetic/int_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ pub fn resource() -> Resource {

#[cfg(test)]
mod test {
use cool_asserts::assert_matches;

use super::*;

#[test]
Expand All @@ -79,4 +81,25 @@ mod test {
assert_eq!(r.types().count(), 1);
assert_eq!(r.operations().count(), 0);
}

#[test]
fn test_int_widths() {
let type_arg_32 = TypeArg::USize(32);
assert_matches!(get_width(&type_arg_32), Ok(32));

let type_arg_33 = TypeArg::USize(33);
assert_matches!(
get_width(&type_arg_33),
Err(SignatureError::TypeArgMismatch(_))
);

let type_arg_128 = TypeArg::USize(128);
assert_matches!(get_width(&type_arg_128), Ok(128));

let type_arg_256 = TypeArg::USize(256);
assert_matches!(
get_width(&type_arg_256),
Err(SignatureError::TypeArgMismatch(_))
);
}
}
5 changes: 3 additions & 2 deletions src/hugr/rewrite/simple_replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,8 +509,9 @@ mod test {

#[test]
fn test_replace_after_copy() {
let one_bit: Vec<SimpleType> = vec![ClassicType::bit().into()];
let two_bit: Vec<SimpleType> = vec![ClassicType::bit().into(), ClassicType::bit().into()];
let one_bit: Vec<SimpleType> = vec![ClassicType::usize().into()];
let two_bit: Vec<SimpleType> =
vec![ClassicType::usize().into(), ClassicType::usize().into()];

let mut builder =
DFGBuilder::new(AbstractSignature::new_df(one_bit.clone(), one_bit.clone())).unwrap();
Expand Down
10 changes: 5 additions & 5 deletions src/hugr/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,13 +303,13 @@ pub mod test {
match (inputs == 0, outputs == 0) {
(false, false) => DFG {
signature: AbstractSignature::new_df(
vec![ClassicType::bit().into(); inputs - 1],
vec![ClassicType::bit().into(); outputs - 1],
vec![ClassicType::usize().into(); inputs - 1],
vec![ClassicType::usize().into(); outputs - 1],
),
}
.into(),
(true, false) => Input::new(vec![ClassicType::bit().into(); outputs - 1]).into(),
(false, true) => Output::new(vec![ClassicType::bit().into(); inputs - 1]).into(),
(true, false) => Input::new(vec![ClassicType::usize().into(); outputs - 1]).into(),
(false, true) => Output::new(vec![ClassicType::usize().into(); inputs - 1]).into(),
(true, true) => Module.into(),
}
}
Expand Down Expand Up @@ -433,7 +433,7 @@ pub mod test {

#[test]
fn dfg_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
let tp: Vec<SimpleType> = vec![ClassicType::bit().into(); 2];
let tp: Vec<SimpleType> = vec![ClassicType::usize().into(); 2];
let mut dfg = DFGBuilder::new(AbstractSignature::new_df(tp.clone(), tp))?;
let mut params: [_; 2] = dfg.input_wires_arr();
for p in params.iter_mut() {
Expand Down
12 changes: 6 additions & 6 deletions src/hugr/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ mod test {
use crate::{type_row, Node};

const NAT: SimpleType = SimpleType::Classic(ClassicType::i64());
const B: SimpleType = SimpleType::Classic(ClassicType::bit());
const B: SimpleType = SimpleType::Classic(ClassicType::usize());
const Q: SimpleType = SimpleType::Qubit;

/// Creates a hugr with a single function definition that copies a bit `copies` times.
Expand Down Expand Up @@ -791,7 +791,7 @@ mod test {
.add_op_with_parent(
parent,
LeafOp::Noop {
ty: ClassicType::bit().into(),
ty: ClassicType::usize().into(),
},
)
.unwrap();
Expand Down Expand Up @@ -878,7 +878,7 @@ mod test {
#[test]
fn leaf_root() {
let leaf_op: OpType = LeafOp::Noop {
ty: HashableType::Int(32).into(),
ty: HashableType::USize.into(),
}
.into();

Expand Down Expand Up @@ -968,7 +968,7 @@ mod test {
b.replace_op(
output,
NodeType::pure(LeafOp::Noop {
ty: ClassicType::bit().into(),
ty: ClassicType::usize().into(),
}),
);
assert_matches!(
Expand Down Expand Up @@ -1147,11 +1147,11 @@ mod test {
})
);
// Second input of Xor from a constant
let cst = h.add_op_with_parent(h.root(), ops::Const::int::<1>(1).unwrap())?;
let cst = h.add_op_with_parent(h.root(), ops::Const::usize(1).unwrap())?;
let lcst = h.add_op_with_parent(
h.root(),
ops::LoadConstant {
datatype: ClassicType::int::<1>(),
datatype: ClassicType::usize(),
},
)?;
h.connect(cst, 0, lcst, 0)?;
Expand Down
6 changes: 3 additions & 3 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub(crate) use impl_box_clone;
/// ```
/// # use hugr::macros::type_row;
/// # use hugr::types::{AbstractSignature, ClassicType, SimpleType, SimpleRow};
/// const B: SimpleType = SimpleType::Classic(ClassicType::bit());
/// const B: SimpleType = SimpleType::Classic(ClassicType::usize());
/// const QB: SimpleType = SimpleType::Qubit;
/// let static_row: SimpleRow = type_row![B, QB];
/// let dynamic_row: SimpleRow = vec![B, B, B].into();
Expand Down Expand Up @@ -93,8 +93,8 @@ macro_rules! type_row {
/// ```
/// # use hugr::macros::classic_row;
/// # use hugr::types::{ClassicType, Signature, ClassicRow};
/// const B: ClassicType = ClassicType::bit();
/// const I: ClassicType = ClassicType::int::<2>();
/// const B: ClassicType = ClassicType::usize();
/// const I: ClassicType = ClassicType::usize();
/// let static_row: ClassicRow = classic_row![B, B];
/// let dynamic_row: ClassicRow = vec![B, B, I].into();
///
Expand Down
24 changes: 5 additions & 19 deletions src/ops/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,14 @@ impl Const {
Self::simple_predicate(0, 2)
}

/// Fixed width integer
pub fn int<const N: u8>(value: HugrIntValueStore) -> Result<Self, ConstTypeError> {
/// Size
pub fn usize(value: u64) -> Result<Self, ConstTypeError> {
Self::new(
ConstValue::Hashable(HashableValue::Int(value)),
ClassicType::int::<N>(),
ClassicType::usize(),
)
}

/// 64-bit integer
pub fn i64(value: i64) -> Result<Self, ConstTypeError> {
Self::int::<64>(value as HugrIntValueStore)
}

/// Tuple of values
pub fn new_tuple(items: impl IntoIterator<Item = Const>) -> Self {
let (values, types): (Vec<ConstValue>, Vec<ClassicType>) = items
Expand Down Expand Up @@ -123,11 +118,6 @@ impl OpTrait for Const {
}
}

pub(crate) type HugrIntValueStore = u128;
pub(crate) type HugrIntWidthStore = u8;
pub(crate) const HUGR_MAX_INT_WIDTH: HugrIntWidthStore =
HugrIntValueStore::BITS as HugrIntWidthStore;

/// Value constants. (This could be "ClassicValue" to parallel [HashableValue])
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
#[non_exhaustive]
Expand Down Expand Up @@ -335,7 +325,7 @@ mod test {
types::custom::test::{CLASSIC_CUST, CLASSIC_T},
types::{simple::Container, type_param::TypeArg},
types::{AbstractSignature, ClassicType, CustomType, SimpleRow, SimpleType, TypeTag},
values::{ConstIntError, ConstTypeError, CustomCheckFail, HashableValue, ValueOfType},
values::{ConstTypeError, CustomCheckFail, HashableValue, ValueOfType},
};

fn custom_value(f: f64) -> ConstValue {
Expand Down Expand Up @@ -387,13 +377,9 @@ mod test {

#[test]
fn test_constant_values() {
const T_INT: ClassicType = ClassicType::int::<64>();
const T_INT: ClassicType = ClassicType::usize();
const V_INT: ConstValue = ConstValue::Hashable(HashableValue::Int(257));
V_INT.check_type(&T_INT).unwrap();
assert_eq!(
V_INT.check_type(&ClassicType::int::<8>()),
Err(ConstTypeError::Int(ConstIntError::IntTooLarge(8, 257)))
);
custom_value(17.4).check_type(&CLASSIC_T).unwrap();
assert_matches!(
V_INT.check_type(&CLASSIC_T),
Expand Down
4 changes: 2 additions & 2 deletions src/ops/custom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,12 +265,12 @@ mod test {
"res".into(),
"op",
"desc".into(),
vec![TypeArg::Type(HashableType::Int(1).into())],
vec![TypeArg::Type(HashableType::USize.into())],
None,
);
let op: ExternalOp = op.into();
assert_eq!(op.name(), "res.op");
assert_eq!(op.description(), "desc");
assert_eq!(op.args(), &[TypeArg::Type(HashableType::Int(1).into())]);
assert_eq!(op.args(), &[TypeArg::Type(HashableType::USize.into())]);
}
}
2 changes: 1 addition & 1 deletion src/ops/leaf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl OpTrait for LeafOp {
// Static signatures. The `TypeRow`s in the `AbstractSignature` use a
// copy-on-write strategy, so we can avoid unnecessary allocations.
const Q: SimpleType = SimpleType::Qubit;
const B: SimpleType = SimpleType::Classic(ClassicType::bit());
const B: SimpleType = SimpleType::Classic(ClassicType::usize());

match self {
LeafOp::Noop { ty: typ } => {
Expand Down
4 changes: 2 additions & 2 deletions src/ops/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,15 +464,15 @@ mod test {

#[test]
fn test_validate_io_nodes() {
const B: SimpleType = SimpleType::Classic(ClassicType::bit());
const B: SimpleType = SimpleType::Classic(ClassicType::usize());

let in_types = type_row![B];
let out_types = type_row![B, B];

let input_node: OpType = ops::Input::new(in_types.clone()).into();
let output_node = ops::Output::new(out_types.clone()).into();
let leaf_node = LeafOp::Noop {
ty: ClassicType::bit().into(),
ty: ClassicType::usize().into(),
}
.into();

Expand Down
Loading

0 comments on commit cb06bc1

Please sign in to comment.