Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a 64-bit USize as the sole integer type in the core #376

Merged
merged 7 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
6 changes: 3 additions & 3 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::int()])?;
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::int(1)?)?;

let break_wire = loop_b.make_break(loop_b.loop_signature()?.clone(), [const_wire])?;
loop_b.set_outputs(break_wire, [i1])?;
Expand Down Expand Up @@ -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::int(2)?)?;
let break_wire = branch_1.make_break(signature, [wire])?;
branch_1.finish_with_outputs([break_wire])?;

Expand Down
4 changes: 2 additions & 2 deletions src/hugr/validate.rs
Original file line number Diff line number Diff line change
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::int(1).unwrap())?;
let lcst = h.add_op_with_parent(
h.root(),
ops::LoadConstant {
datatype: ClassicType::int::<1>(),
datatype: ClassicType::int(),
},
)?;
h.connect(cst, 0, lcst, 0)?;
Expand Down
2 changes: 1 addition & 1 deletion src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ 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 I: ClassicType = ClassicType::int();
/// 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> {
/// Integer
pub fn int(value: u64) -> Result<Self, ConstTypeError> {
Self::new(
ConstValue::Hashable(HashableValue::Int(value)),
ClassicType::int::<N>(),
ClassicType::int(),
)
}

/// 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 @@ -343,7 +333,7 @@ mod test {
types::simple::Container,
types::type_param::TypeArg,
types::{AbstractSignature, ClassicType, CustomType, SimpleRow, SimpleType, TypeTag},
values::{ConstIntError, ConstTypeError, CustomCheckFail, HashableValue, ValueOfType},
values::{ConstTypeError, CustomCheckFail, HashableValue, ValueOfType},
};

#[test]
Expand Down Expand Up @@ -394,13 +384,9 @@ mod test {

#[test]
fn test_constant_values() {
const T_INT: ClassicType = ClassicType::int::<64>();
const T_INT: ClassicType = ClassicType::int();
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)))
);
ConstValue::F64(17.4).check_type(&ClassicType::F64).unwrap();
assert_matches!(
V_INT.check_type(&ClassicType::F64),
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())]);
}
}
27 changes: 12 additions & 15 deletions src/types/simple.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
//! Dataflow types

use std::fmt::{self, Display, Formatter, Write};
use std::fmt::{self, Display, Formatter};

use super::type_row::{TypeRow, TypeRowElem};
use super::{custom::CustomType, AbstractSignature};
use crate::{classic_row, ops::constant::HugrIntWidthStore};
use crate::classic_row;
use itertools::Itertools;
use serde_repr::{Deserialize_repr, Serialize_repr};
use smol_str::SmolStr;
Expand Down Expand Up @@ -193,8 +193,8 @@ pub enum HashableType {
/// TODO of course this is not necessarily hashable, or even classic,
/// depending on how it is instantiated...
Variable(SmolStr),
/// An arbitrary size integer.
Int(HugrIntWidthStore),
/// A 64-bit unsigned integer.
USize,
/// An arbitrary length string.
String,
/// A container (all of whose elements can be hashed)
Expand All @@ -216,22 +216,22 @@ impl ClassicType {
ClassicType::Graph(Box::new(signature))
}

/// Returns a new integer type with the given number of bits.
/// Returns a new integer type.
#[inline]
pub const fn int<const N: HugrIntWidthStore>() -> Self {
Self::Hashable(HashableType::Int(N))
pub const fn int() -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably worth renaming this method at this point

Self::Hashable(HashableType::USize)
}

/// Returns a new 64-bit integer type.
#[inline]
pub const fn i64() -> Self {
Self::int::<64>()
Self::int()
}

/// Returns a new 1-bit integer type.
#[inline]
pub const fn bit() -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this method and use int() wherever this is used I think

Self::int::<1>()
Self::int()
}

/// New unit type, defined as an empty Tuple.
Expand Down Expand Up @@ -294,10 +294,7 @@ impl Display for HashableType {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
HashableType::Variable(x) => f.write_str(x),
HashableType::Int(i) => {
f.write_char('I')?;
f.write_str(&i.to_string())
}
HashableType::USize => f.write_str("I64"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
HashableType::USize => f.write_str("I64"),
HashableType::USize => f.write_str("USize"),

HashableType::String => f.write_str("String"),
HashableType::Container(c) => c.fmt(f),
}
Expand Down Expand Up @@ -496,7 +493,7 @@ mod test {
);

let hash = vec![
SimpleType::Classic(ClassicType::Hashable(HashableType::Int(8))),
SimpleType::Classic(ClassicType::Hashable(HashableType::USize)),
SimpleType::Classic(ClassicType::Hashable(HashableType::String)),
];
let ty = SimpleType::new_tuple(hash);
Expand All @@ -523,7 +520,7 @@ mod test {
SimpleType::Classic(ClassicType::Container(Container::Sum(_)))
);

let hash: TypeRow<HashableType> = vec![HashableType::Int(4), HashableType::String].into();
let hash: TypeRow<HashableType> = vec![HashableType::USize, HashableType::String].into();
let ty = SimpleType::new_sum(hash.map_into());
assert_matches!(
ty,
Expand Down
16 changes: 6 additions & 10 deletions src/types/simple/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,11 @@ use super::SimpleType;

use super::super::AbstractSignature;

use crate::ops::constant::HugrIntWidthStore;

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[serde(tag = "t")]
pub(crate) enum SerSimpleType {
Q,
I {
width: HugrIntWidthStore,
},
I,
F,
S,
G {
Expand Down Expand Up @@ -102,7 +98,7 @@ impl From<HashableType> for SerSimpleType {
fn from(value: HashableType) -> Self {
match value {
HashableType::Variable(s) => SerSimpleType::Var { name: s },
HashableType::Int(w) => SerSimpleType::I { width: w },
HashableType::USize => SerSimpleType::I,
HashableType::String => SerSimpleType::S,
HashableType::Container(c) => c.into(),
}
Expand Down Expand Up @@ -161,7 +157,7 @@ impl From<SerSimpleType> for SimpleType {
fn from(value: SerSimpleType) -> Self {
match value {
SerSimpleType::Q => SimpleType::Qubit,
SerSimpleType::I { width } => HashableType::Int(width).into(),
SerSimpleType::I => HashableType::USize.into(),
SerSimpleType::F => ClassicType::F64.into(),
SerSimpleType::S => HashableType::String.into(),
SerSimpleType::G { signature } => ClassicType::Graph(Box::new(*signature)).into(),
Expand Down Expand Up @@ -224,14 +220,14 @@ mod test {

// A Classic sum
let t = SimpleType::new_sum(vec![
SimpleType::Classic(ClassicType::Hashable(HashableType::Int(4))),
SimpleType::Classic(ClassicType::Hashable(HashableType::USize)),
SimpleType::Classic(ClassicType::F64),
]);
assert_eq!(ser_roundtrip(&t), t);

// A Hashable list
let t = SimpleType::Classic(ClassicType::Hashable(HashableType::Container(
Container::Array(Box::new(HashableType::Int(8)), 3),
Container::Array(Box::new(HashableType::USize), 3),
)));
assert_eq!(ser_roundtrip(&t), t);
}
Expand All @@ -241,7 +237,7 @@ mod test {
// This list should be represented as a HashableType::Container.
let malformed = SimpleType::Qontainer(Container::Array(
Box::new(SimpleType::Classic(ClassicType::Hashable(
HashableType::Int(8),
HashableType::USize,
))),
6,
));
Expand Down
Loading