Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into fix/funcdefn-inference
Browse files Browse the repository at this point in the history
  • Loading branch information
croyzor committed Nov 16, 2023
2 parents 417ffc1 + e7473f2 commit cfd51f8
Show file tree
Hide file tree
Showing 36 changed files with 905 additions and 358 deletions.
93 changes: 82 additions & 11 deletions .github/workflows/notify-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,43 +15,115 @@ jobs:
runs-on: ubuntu-latest
outputs:
msg: ${{ steps.make_msg.outputs.msg }}
should_notify: ${{ steps.get_coverage.outputs.should_notify }}
steps:
- name: Download commit sha of the most recent successful run
uses: dawidd6/action-download-artifact@v2
with:
# Downloads the artifact from the most recent successful run
workflow: 'notify-coverage.yml'
name: head-sha.txt
if_no_artifact_found: ignore
- name: Get today's and yesterday's coverage trends from codecov
id: get_coverage
# API reference: https://docs.codecov.com/reference/repos_totals_retrieve
run: |
YESTERDAY=$( date -u +%Y-%m-%dT%H:%M:%SZ -d 'yesterday' )
# Get the previous commit coverage, if the last sha is available
if [ ! -f head-sha.txt ]
then
echo "No previous coverage found."
# Update the head-sha.txt file with the current sha,
# so next time we campare against the current coverage.
echo ${{ github.sha }} > head-sha.txt
echo "should_notify=false" >> "$GITHUB_OUTPUT"
exit 0
fi
PREV_SHA=$( cat head-sha.txt )
echo "Previous sha: \"$PREV_SHA\""
# Check if the sha has changed
if [ "$PREV_SHA" == "${{ github.sha }}" ]
then
echo "No new commits since last run."
echo "should_notify=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Query the previous coverage from codecov
curl --request GET \
--url "https://api.codecov.io/api/v2/github/${{ github.repository_owner }}/repos/${{ github.event.repository.name }}/coverage/?interval=1d&start_date=$YESTERDAY" \
--url "https://api.codecov.io/api/v2/github/${{ github.repository_owner }}/repos/${{ github.event.repository.name }}/totals/?sha=$PREV_SHA" \
--header 'accept: application/json' \
--header "authorization: Bearer ${{ secrets.CODECOV_GET_TOKEN }}" \
> coverage.json
echo "Coverage JSON:"
cat coverage.json
> coverage-prev.json
cat coverage-prev.json | jq ".totals.coverage" > coverage-prev.txt
echo "Previous coverage query result:"
cat coverage-prev.json | jq "del(.files)"
echo
cat coverage.json | jq ".results[0].max" > coverage-prev.txt
cat coverage.json | jq ".results[-1].max" > coverage.txt
# Query the current coverage from codecov
curl --request GET \
--url "https://api.codecov.io/api/v2/github/${{ github.repository_owner }}/repos/${{ github.event.repository.name }}/totals/?sha=${{ github.sha }}" \
--header 'accept: application/json' \
--header "authorization: Bearer ${{ secrets.CODECOV_GET_TOKEN }}" \
> coverage.json
cat coverage.json | jq ".totals.coverage" > coverage.txt
echo "Current coverage query result:"
cat coverage.json | jq "del(.files)"
echo
echo
echo "Previous coverage: `cat coverage-prev.txt`%"
echo "Current coverage: `cat coverage.txt`%"
# A `null` in either coverage means that the coverage is not available,
# so we don't want to notify about that.
if [ "$( cat coverage-prev.txt )" == "null" ]
then
echo "Previous coverage not available."
echo ${{ github.sha }} > head-sha.txt
echo "should_notify=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ "$( cat coverage.txt )" == "null" ]
then
echo "Current coverage not available."
# Note that we don't update the head-sha.txt file here,
# so next time we compare against the one that had coverage data.
echo "should_notify=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo ${{ github.sha }} > head-sha.txt
echo "should_notify=true" >> "$GITHUB_OUTPUT"
- name: Compare with previous summary and make message
id: make_msg
if: steps.get_coverage.outputs.should_notify == 'true'
run: |
change="`cat coverage-prev.txt`% --> `cat coverage.txt`%"
prev=`cat coverage-prev.txt`
current=`cat coverage-prev.txt`
change=`printf "%.2f%% --> %.2f%%" $prev $current`
codecov="https://codecov.io/gh/${{ github.repository }}?search=&trend=7%20days"
if (( $(echo "`cat coverage-prev.txt` < `cat coverage.txt` + 0.04" | bc -l) ))
if (( $(echo "$prev < $current + 0.04" | bc -l) ))
then
MSG="msg=Coverage check for hugr shows no regression (${change}). ✅ ${codecov}"
else
MSG="msg=Coverage check for hugr shows regression (${change}). ❌ ${codecov}"
fi
echo $MSG
echo $MSG >> "$GITHUB_OUTPUT"
- name: Upload current HEAD sha
uses: actions/upload-artifact@v3
with:
name: head-sha.txt
path: head-sha.txt

notify-slack:
needs: check-coverage
runs-on: ubuntu-latest
if: needs.check-coverage.outputs.should_notify == 'true'
steps:
- name: Send notification
uses: slackapi/[email protected]
Expand All @@ -60,4 +132,3 @@ jobs:
slack-message: ${{ needs.check-coverage.outputs.msg }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ petgraph = { version = "0.6.3", default-features = false }
context-iterators = "0.2.0"
serde_json = "1.0.97"
delegate = "0.10.0"
rustversion = "1.0.14"
paste = "1.0"

[features]
pyo3 = ["dep:pyo3"]
Expand All @@ -61,7 +63,6 @@ rmp-serde = "1.1.1"
webbrowser = "0.8.10"
urlencoding = "2.1.2"
cool_asserts = "2.0.3"
paste = "1.0"
insta = { version = "1.34.0", features = ["yaml"] }

[[bench]]
Expand All @@ -71,4 +72,4 @@ harness = false

[profile.dev.package]
insta.opt-level = 3
similar.opt-level = 3
similar.opt-level = 3
66 changes: 0 additions & 66 deletions specification/hugr.md
Original file line number Diff line number Diff line change
Expand Up @@ -1719,72 +1719,6 @@ Conversions between integers and floats:
| `convert_u<N>` | `int<N>` | `float64` | unsigned int to float |
| `convert_s<N>` | `int<N>` | `float64` | signed int to float |

### Quantum Extension

This is the extension that is designed to be natively understood by
TKET2. Besides a range of quantum operations (like Hadamard, CX, etc.)
that take and return `Qubit`, we note the following operations for
allocating/deallocating `Qubit`s:

```
qalloc: () -> Qubit
qfree: Qubit -> ()
```

`qalloc` allocates a fresh, 0 state Qubit - if none is available at
runtime it panics. `qfree` loses a handle to a Qubit (may be reallocated
in future). The point at which an allocated qubit is reset may be
target/compiler specific.

Note there are also `measurez: Qubit -> (i1, Qubit)` and on supported
targets `reset: Qubit -> Qubit` operations to measure or reset a qubit
without losing a handle to it.

#### Dynamic vs static allocation

With these operations the programmer/front-end can request dynamic qubit
allocation, and the compiler can add/remove/move these operations to use
more or fewer qubits. In some use cases, that may not be desirable, and
we may instead want to guarantee only a certain number of qubits are
used by the program. For this purpose TKET2 places additional
constraints on the HUGR that are in line with TKET1 backwards
compatibility:

1. The `main` function takes one `Array<N, Qubit>`
input and has one output of the same type (the same statically known
size).
2. All Operations that have a `FunctionType` involving `Qubit` have as
many `Qubit` input wires as output.


With these constraints, we can treat all `Qubit` operations as returning all qubits they take
in. The implicit bijection from input `Qubit` to output allows register
allocation for all `Qubit` wires.
If further the program does not contain any `qalloc` or `qfree`
operations we can state the program only uses `N` qubits.

#### Angles

The Quantum extension also defines a specialized `angle<N>` type which is used
to express parameters of rotation gates. The type is parametrized by the
_log-denominator_, which is an integer $N \in [0, 53]$; angles with
log-denominator $N$ are multiples of $2 \pi / 2^N$, where the multiplier is an
unsigned `int<N>` in the range $[0, 2^N]$. The maximum log-denominator $53$
effectively gives the resolution of a `float64` value; but note that unlike
`float64` all angle values are equatable and hashable; and two `angle<N>` that
differ by a multiple of $2 \pi$ are _equal_.

The following operations are defined:

| Name | Inputs | Outputs | Meaning |
| -------------- | ---------- | ---------- | ------- |
| `aconst<N, x>` | none | `angle<N>` | const node producing angle $2 \pi x / 2^N$ (where $0 \leq x \lt 2^N$) |
| `atrunc<M,N>` | `angle<M>` | `angle<N>` | round `angle<M>` to `angle<N>`, where $M \geq N$, rounding down in $[0, 2\pi)$ if necessary |
| `aconvert<M,N>` | `angle<M>` | `Sum(angle<N>, ErrorType)` | convert `angle<M>` to `angle<N>`, returning an error if $M \gt N$ and exact conversion is impossible |
| `aadd<M,N>` | `angle<M>`, `angle<N>` | `angle<max(M,N)>` | add two angles |
| `asub<M,N>` | `angle<M>`, `angle<N>` | `angle<max(M,N)>` | subtract the second angle from the first |
| `aneg<N>` | `angle<N>` | `angle<N>` | negate an angle |

### Higher-order (Tierkreis) Extension

In **some** contexts, notably the Tierkreis runtime, higher-order
Expand Down
15 changes: 8 additions & 7 deletions src/builder/build_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ pub trait Dataflow: Container {
hugr: Hugr,
input_wires: impl IntoIterator<Item = Wire>,
) -> Result<BuildHandle<DataflowOpID>, BuildError> {
let num_outputs = hugr.get_optype(hugr.root()).signature().output_count();
let num_outputs = hugr.get_optype(hugr.root()).value_output_count();
let node = self.add_hugr(hugr)?.new_root;

let inputs = input_wires.into_iter().collect();
Expand All @@ -257,8 +257,8 @@ pub trait Dataflow: Container {
hugr: &impl HugrView,
input_wires: impl IntoIterator<Item = Wire>,
) -> Result<BuildHandle<DataflowOpID>, BuildError> {
let num_outputs = hugr.get_optype(hugr.root()).signature().output_count();
let node = self.add_hugr_view(hugr)?.new_root;
let num_outputs = hugr.get_optype(hugr.root()).value_output_count();

let inputs = input_wires.into_iter().collect();
wire_up_inputs(inputs, node, self)?;
Expand Down Expand Up @@ -612,8 +612,9 @@ pub trait Dataflow: Container {
})
}
};
let const_in_port = signature.output.len();
let op_id = self.add_dataflow_op(ops::Call { signature }, input_wires)?;
let op: OpType = ops::Call { signature }.into();
let const_in_port = op.static_input_port().unwrap();
let op_id = self.add_dataflow_op(op, input_wires)?;
let src_port = self.hugr_mut().num_outputs(function.node()) - 1;

self.hugr_mut()
Expand All @@ -633,13 +634,13 @@ fn add_node_with_wires<T: Dataflow + ?Sized>(
nodetype: impl Into<NodeType>,
inputs: Vec<Wire>,
) -> Result<(Node, usize), BuildError> {
let nodetype = nodetype.into();
let sig = nodetype.op_signature();
let nodetype: NodeType = nodetype.into();
let num_outputs = nodetype.op().value_output_count();
let op_node = data_builder.add_child_node(nodetype)?;

wire_up_inputs(inputs, op_node, data_builder)?;

Ok((op_node, sig.output().len()))
Ok((op_node, num_outputs))
}

fn wire_up_inputs<T: Dataflow + ?Sized>(
Expand Down
5 changes: 3 additions & 2 deletions src/builder/conditional.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::extension::ExtensionRegistry;
use crate::hugr::views::HugrView;
use crate::ops::dataflow::DataflowOpTrait;
use crate::types::{FunctionType, TypeRow};

use crate::ops;
use crate::ops::handle::CaseID;
use crate::ops::{self, OpTrait};

use super::build_traits::SubContainer;
use super::handle::BuildHandle;
Expand Down Expand Up @@ -104,12 +105,12 @@ impl<B: AsMut<Hugr> + AsRef<Hugr>> ConditionalBuilder<B> {
pub fn case_builder(&mut self, case: usize) -> Result<CaseBuilder<&mut Hugr>, BuildError> {
let conditional = self.conditional_node;
let control_op = self.hugr().get_optype(self.conditional_node);
let extension_delta = control_op.signature().extension_reqs;

let cond: ops::Conditional = control_op
.clone()
.try_into()
.expect("Parent node does not have Conditional optype.");
let extension_delta = cond.signature().extension_reqs;
let inputs = cond
.case_input_row(case)
.ok_or(ConditionalBuildError::NotCase { conditional, case })?;
Expand Down
37 changes: 18 additions & 19 deletions src/builder/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use crate::{
};

use crate::ops::handle::{AliasID, FuncID, NodeHandle};
use crate::ops::OpType;

use crate::types::Signature;

Expand Down Expand Up @@ -72,22 +71,22 @@ impl<T: AsMut<Hugr> + AsRef<Hugr>> ModuleBuilder<T> {
/// # Errors
///
/// This function will return an error if there is an error in adding the
/// [`OpType::FuncDefn`] node.
/// [`crate::ops::OpType::FuncDefn`] node.
pub fn define_declaration(
&mut self,
f_id: &FuncID<false>,
) -> Result<FunctionBuilder<&mut Hugr>, BuildError> {
let f_node = f_id.node();
let (signature, name) = if let OpType::FuncDecl(ops::FuncDecl { signature, name }) =
self.hugr().get_optype(f_node)
{
(signature.clone(), name.clone())
} else {
return Err(BuildError::UnexpectedType {
let ops::FuncDecl { signature, name } = self
.hugr()
.get_optype(f_node)
.as_func_decl()
.ok_or(BuildError::UnexpectedType {
node: f_node,
op_desc: "OpType::FuncDecl",
});
};
op_desc: "crate::ops::OpType::FuncDecl",
})?
.clone();

self.hugr_mut().replace_op(
f_node,
NodeType::new_pure(ops::FuncDefn {
Expand All @@ -105,7 +104,7 @@ impl<T: AsMut<Hugr> + AsRef<Hugr>> ModuleBuilder<T> {
/// # Errors
///
/// This function will return an error if there is an error in adding the
/// [`OpType::FuncDecl`] node.
/// [`crate::ops::OpType::FuncDecl`] node.
pub fn declare(
&mut self,
name: impl Into<String>,
Expand All @@ -124,11 +123,11 @@ impl<T: AsMut<Hugr> + AsRef<Hugr>> ModuleBuilder<T> {
Ok(declare_n.into())
}

/// Add a [`OpType::AliasDefn`] node and return a handle to the Alias.
/// Add a [`crate::ops::OpType::AliasDefn`] node and return a handle to the Alias.
///
/// # Errors
///
/// Error in adding [`OpType::AliasDefn`] child node.
/// Error in adding [`crate::ops::OpType::AliasDefn`] child node.
pub fn add_alias_def(
&mut self,
name: impl Into<SmolStr>,
Expand All @@ -149,10 +148,10 @@ impl<T: AsMut<Hugr> + AsRef<Hugr>> ModuleBuilder<T> {
Ok(AliasID::new(node, name, bound))
}

/// Add a [`OpType::AliasDecl`] node and return a handle to the Alias.
/// Add a [`crate::ops::OpType::AliasDecl`] node and return a handle to the Alias.
/// # Errors
///
/// Error in adding [`OpType::AliasDecl`] child node.
/// Error in adding [`crate::ops::OpType::AliasDecl`] child node.
pub fn add_alias_declare(
&mut self,
name: impl Into<SmolStr>,
Expand Down Expand Up @@ -233,14 +232,14 @@ mod test {

let mut f_build = module_builder.define_function(
"main",
FunctionType::new(type_row![NAT], type_row![NAT]).pure(),
FunctionType::new(type_row![NAT], type_row![NAT, NAT]).pure(),
)?;
let local_build = f_build.define_function(
"local",
FunctionType::new(type_row![NAT], type_row![NAT]).pure(),
FunctionType::new(type_row![NAT], type_row![NAT, NAT]).pure(),
)?;
let [wire] = local_build.input_wires_arr();
let f_id = local_build.finish_with_outputs([wire])?;
let f_id = local_build.finish_with_outputs([wire, wire])?;

let call = f_build.call(f_id.handle(), f_build.input_wires())?;

Expand Down
Loading

0 comments on commit cfd51f8

Please sign in to comment.