diff --git a/safety-related-profile/README.md b/safety-related-profile/README.md
index 749a7309..c8300d86 100644
--- a/safety-related-profile/README.md
+++ b/safety-related-profile/README.md
@@ -2,9 +2,7 @@
# ONNX Safety-Related Profile Working Group (WG)
-This repository is where ONNX Safety-Related Profile WG will capture various artifacts and deliverables.
-
-The purpose of this WG is to elaborate an ONNX profile dedicated to safety-related systems.
+The purpose of this WG is to elaborate an ONNX profile dedicated to "safety-related" systems.
An ONNX model is the representation of an ML/AI model to be implemented. In other words, it is the specification for the implementation activity.
The model may be either interpreted by a tool or translated into some lower level equivalent representation (e.g., some source code) by a tool or a human. In both cases, to be able to interpret the model according to the intent of its designer, its syntax and semantics must be clear and non-ambiguous.
@@ -13,29 +11,39 @@ This is for instance the case in the aeronautical domain for which evidences sha
We consider that the current ONNX standard does not fully satisfy these requirements and that there is a need (i) to clarify the industrial needs in that matter, (ii) to identify and address the weaknesses of the current standard in a systematic manner in order to produce an "safety-related ONNX profile" that would to fulfil these needs.
We also consider that these needs are specific and that the proposed changes and clarifications of the syntax, semantics, and documentation shall not prevent the use of ONNX in domains where constraints are relaxed. For instance, introducing restrictions on the parameters values for operators of the safety-related profile must not affect the usage of the same operators out of the profile.
-## Working Group Status
+# Contents
+
+The main elements of this repository are:
+- [Slides of the Kick-Off Meeting](./meetings/general/2024-09-25%20-%20KOM/2024-09-25%20-%20SONNX%20KOM.pdf)
+- [Recent presentation of the projects results](./meetings/Other_meetings/SONNX%20-%20WG114-%20oct-2025.pdf)
+- [Minutes of the WG meetings](./meetings/minutes.md)
+- Deliverables (draft)
+ - [Industrial needs](./deliverables/needs/needs.md) (What are the needs of the industrial partners?)
+ - [Use cases](./deliverables/scope/scope.md) (What are the first models to be implemented using SONNX?)
+ - [Requirements](./deliverables/reqs/reqs.md) (How do the needs translate to constraints on the SONNX profile?)
+ - [Issues](./deliverables/issues/issues.md) (What are the first issues identified for the usage of ONNX in a safety-related system?) (obsolete)
+ - [Informal specification of operators](./sonnx/ops/spec/informal/)
+ - [Formal specification of operators](./sonnx/ops/spec/formal/)
+ Formal specification of operators is done using the [Why3](https://www.why3.org/).
+ - [Formal specification of graphs](./sonnx/graph/README.md) (tbc)
+Note that this is a **work in progress**.
+
+
+# Working Group Status
**ACTIVE**
-# Slack channel
-Please sign up at https://slack.lfai.foundation/ and join [onnx-safety-related-profile-wg](https://app.slack.com/client/TPUCV58TG/CPS6Y3N21) channel.
# WG Lead(s)
-* Eric JENN (IRT Saint-Exupery) and Jean SOUYRIS (Airbus) (July 22, 2024 - Current)
-
-# Logistics
+* Eric JENN (IRT Saint-Exupery, France) and Jean SOUYRIS (Airbus, France) (July 22, 2024 - Current)
-* WG leads will drive the meeting.
-* Meeting annoucement will be posted in our Slack channel: https://lfaifoundation.slack.com/archives/C02AANGFBJB
-* Feedbacks and topic request are welcome by all.
-* Documents and artifacts: https://github.com/onnx/working-groups/tree/main/safety-related-profile
# WG Meeting Info
-* Meeting (to be defined).
-* TEAMS Meeting link: (to be defined)
-* Meeting ID: (to be defined)
-
-# Meeting notes
+* Working group meetings take place every other Wednesday, 4 p.m. to 6 p.m. (Paris time)
+* To join, please subscribe to the SONNX mailing list (see below); we will come back to you.
+* The meeting notes can be found [here](./meetings/minutes.md).
-The meeting notes can be found [here](https://github.com/onnx/working-groups/tree/main/safety-related-profile/meetings)
+# Mailing list
+* To subscribe to the mailing list, send an email to onnx-sonnx-workgroup+subscribe@lists.lfaidata.foundation
+
diff --git a/safety-related-profile/analyses/certification/SONNX_ED324_interest.docx b/safety-related-profile/analyses/certification/SONNX_ED324_interest.docx
new file mode 100644
index 00000000..8225acbb
Binary files /dev/null and b/safety-related-profile/analyses/certification/SONNX_ED324_interest.docx differ
diff --git a/safety-related-profile/attic/certification/SONNX_ED324_interest.docx b/safety-related-profile/attic/certification/SONNX_ED324_interest.docx
new file mode 100644
index 00000000..9938fffe
Binary files /dev/null and b/safety-related-profile/attic/certification/SONNX_ED324_interest.docx differ
diff --git a/safety-related-profile/documents/mngt/README.md b/safety-related-profile/attic/mngt/README.md
similarity index 100%
rename from safety-related-profile/documents/mngt/README.md
rename to safety-related-profile/attic/mngt/README.md
diff --git a/safety-related-profile/documents/mngt/WG_objectives_and_organisation.docx b/safety-related-profile/attic/mngt/WG_objectives_and_organisation.docx
similarity index 100%
rename from safety-related-profile/documents/mngt/WG_objectives_and_organisation.docx
rename to safety-related-profile/attic/mngt/WG_objectives_and_organisation.docx
diff --git a/safety-related-profile/documents/mngt/Workplan.xlsx b/safety-related-profile/attic/mngt/Workplan.xlsx
similarity index 100%
rename from safety-related-profile/documents/mngt/Workplan.xlsx
rename to safety-related-profile/attic/mngt/Workplan.xlsx
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/2025-11-07-work_session.md b/safety-related-profile/attic/operator_spec_sub_wg/2025-11-07-work_session.md
new file mode 100644
index 00000000..a9cead1a
--- /dev/null
+++ b/safety-related-profile/attic/operator_spec_sub_wg/2025-11-07-work_session.md
@@ -0,0 +1,38 @@
+### Notations and other conventions to be added to the *guidelines*
+- Change the notation of variadic arguments from $X_i$ to $Xi$ .
+- All elements are indexed from $0$ to $L$ , L being the index of the **L**ast element.
+- In the spec of all operator, add an hyperlink to the ONNX operator
+- To designate a specific element of a tensor $T$, use $T[i,j,..]$, i.e., square bracket rather than parentheses.
+- Comply with the ONNX naming conventions: $add$ becomes $Add$.
+- Use ONNX type lists (without the "tensor())
+- Provide a template of the informal spec that will serve as a basis for the writing of new operators (besides the *guidelines*)
+- [ ] (all) Modify the already specified operators to ensure compliance with these new conventions.
+
+### Review of the $\text{Slice}$ operator
+- See replies to Ricardo and Jõao's questions in [joao-ricardo-doubts.md](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/slice/reviews/joao-ricardo-doubts.md)
+- Those questions raised very interesting discussions about empty tensors
+- [ ] (Eric) Create a side note about "empty tensors" (To be placed in "doc"). [project::[[SONNX]]]
+- Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.)
+
+### Review of the $\text{Clip}$ operator
+- Jõao and Ricardo have questions about the handling of NaN.
+ - The spec. should only refer to $\min(M, \max(X[i],L))$ and define what $\min(x,NaN)$ and $\max(x,NaN)$ means.
+ - Consider other special values (+Inf, -Inf, $0^+$ +, $0^-$ )
+ - Check what the IEEE says about min and max with these values.
+- [ ] (Mariem) Give R&J a pointer to Why3 where NaN are handled.
+- [ ] (Mariem) Give R&J a feedback ont the formal spec (in particular: recall a few principles to be followed).
+
+### Review of $\text{Max}$ and broadcasting
+- See Eric's comments [here](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/max/reviews/max-ejn.md)
+- [ ] (Jean-loup) Separate the spec broadcasting and the max operator
+ - Broadcasting is specified as $(Z1,Z2,ZL)=\text{bc}(X1,X2,...,XL)$
+ - The spec is to be placed in "common"
+ - "Properties" will be useful to support the verification (formal, test) activities but they have to be removed from the informal spec.
+ - They should be specified in the most simple way so has to be traceable to some simple implementation using primitive operations (loops, simple mathematical operations). In particular, we should stick to two-step process : (i) construction of the broadcasted tensors and (ii) computation of the maxes on those tensors must be explicit.
+
+### Other topics
+- [ ] (Eric) Provide explanations about the new way to manage modifications (using Pull Requests).
+- [ ] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$)
+- [ ] (Eric) During next meeting ask participants if they know other issues similar to those raised by empty tensors.
+
+
diff --git a/safety-related-profile/meetings/operator_spec_sub_wg/README.md b/safety-related-profile/attic/operator_spec_sub_wg/README.md
similarity index 100%
rename from safety-related-profile/meetings/operator_spec_sub_wg/README.md
rename to safety-related-profile/attic/operator_spec_sub_wg/README.md
diff --git a/safety-related-profile/meetings/operator_spec_sub_wg/SONNX_Operator_List.xlsx b/safety-related-profile/attic/operator_spec_sub_wg/SONNX_Operator_List.xlsx
similarity index 100%
rename from safety-related-profile/meetings/operator_spec_sub_wg/SONNX_Operator_List.xlsx
rename to safety-related-profile/attic/operator_spec_sub_wg/SONNX_Operator_List.xlsx
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/conv_netron.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/conv_netron.png
new file mode 100644
index 00000000..071c598a
Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/conv_netron.png differ
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.drawio.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.drawio.png
new file mode 100644
index 00000000..4244ad2f
Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.drawio.png differ
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.md b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.md
new file mode 100644
index 00000000..f584ad00
--- /dev/null
+++ b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.md
@@ -0,0 +1,333 @@
+# Introduction
+
+The idea is to build up all operators of our selection on the basis of a small corpus of primitive operators. The objective is to simplify the specification (esp. the formal specification) thanks to a modular approach.
+
+In some cases, the decomposition is fairly trivial and the resulting description remains understandable describes in a fairly understanable way what the operator is doing. However, care shall be taken not to make the description of the operators overly complicated. A good example is the one of convolution (`conv`). We will show how it can be decomposed using simpler operators, but at the expense of making it much more difficult to understand than when using the usual "triple-sum" (for a 2D convolution).
+
+
+# Core operators
+## Core Arithmetic Operators:
+ - Add, (Sub)
+ - Mul, Div
+ - Exp
+ - Sine, (Cos, Tan)
+ - Log
+ - Sqrt
+
+## Matrix Multiplication:
+- MatMul
+
+## Data Manipulation/Shaping Operators:
+- Reshape,
+- Concat,
+- Slice,
+- Gather,
+- Where,
+- Shape
+
+## Comparison/Logical Operators
+- Max, (Min),
+- Greater, (Less).
+
+## Reduction Operators
+- ReduceSum,
+- ReduceMean,
+- ReduceMax.
+
+## Other operators (non ONNX)
+Those operators do not belong to the ONNX operator set. However, they are useful to describe the are not ONNX operators, but opera
+- Broadcast, used to describe the implicit broadcasting done by some operators
+- Dilation, used to describe the dilation done by the `conv` operator.
+
+# Synthesis
+
+The following table indicates is an operator is primitive or not.
+When the answer is "YES (*)", it means that we consider it primitive even though they could actually be described using more primitive operators. This is for instance the case of `Sub(x,y)` which could be in principle encoded as `Add(x, -y)`.
+
+An empty entry means that the operator has not been analyzed yet.
+
+| Operator | Primitive |
+|------------------------------|-----------|
+| Abs | NO |
+| Add | YES |
+| Cast | |
+| Clip | |
+| Concat | YES |
+| Constant | YES |
+| ConstantOfShape | |
+| Conv | NO |
+| ConvTranspose | NO |
+| Div | YES (*) |
+| Equal | YES |
+| Erf | YES |
+| Exp | YES |
+| Expand | |
+| Flatten | NO |
+| Gather | |
+| Gemm | NO |
+| GlobalAveragePool | NO |
+| GRU | |
+| HardSwish | NO |
+| Identity | |
+| LeakyRelu | NO |
+| Less | |
+| Log | YES |
+| LSTM | |
+| MatMul | YES |
+| Max | YES |
+| MaxPool | |
+| Min | YES (*) |
+| Mod | YES |
+| Mul | YES |
+| Neg | NO |
+| Not | YES |
+| Pad | YES |
+| Pow | YES |
+| Range | |
+| ReduceMean | YES |
+| ReduceSum | YES |
+| Relu | NO |
+| Reshape | YES |
+| Resize | |
+| ScatterND | |
+| Shape | YES |
+| Sigmoid | NO |
+| Slice | |
+| Softmax | NO |
+| SoftPlus | NO |
+| Split | |
+| Sqrt | YES |
+| Squeeze | |
+| Sub | YES (*) |
+| Tanh | NO |
+| Transpose | NO |
+| ConvTranspose | |
+| Unsqueeze | |
+| Where | |
+
+
+# Examples
+
+# Abs
+Operator Àbs`could be defined using operator `Max`:
+
+$$\texttt{Abs}(x) = \texttt{Max}(x,-x)$$
+
+or
+
+$$\texttt{Abs}(x) = x \times \texttt{Sign}(x)$$
+
+
+## Neg
+
+$$Neg(x)= x \times -1$$
+
+## ReLU
+
+$$Relu(x) = max(1,x)$$
+
+## Convolution
+ - Conv = (Slice + Reshape = img2col) + MatMul
+
+A convolution can be implemented using a matrix multiplication by "flattening" the kernel and reorganizing the tensor, as illustrated below for a (2x2) kernel and (3x3) matrix.
+
+
+
+The Slice operator is used to extract the four "patches". Then each patch is reshaped into a (1x4) vector. The four vectors are then concatenated into a 4x4 matrix.
+
+The kernel is reshaped (`reshape`) into a 1x4 vector.
+
+The flattened kernel is then multiplied with the 4x4 matrix. The (1x4) vector result is finally reshaped into a 2x2 matrix. The Python code can be found in [Google Collab](https://colab.research.google.com/drive/1CpJv2-zwHV_ZmC7FH9VD0UffFDahHI3s#scrollTo=vWPbx5Br7RJZ).
+
+The resulting graph is given below:
+
+
+
+*WARNING: describing convolution this way makes the actual operation done by the operator much more difficult to understand... In fact, this scheme will necessarily be explained with a reference to the classical way to compute convolutions...*
+
+The `Conv` operator also does padding and dilation. Padding can be expressed using the ONNX `Pad` operator. since there is no Dilation operator, we have to describe it using simpler operators.
+
+Dilation can be implemented using `Reshape` and `Concat`. For instance, for a 2D tensor:
+- First, we insert zeroes between columns by
+ - reshaping the tensor to add a dimension. This means that the scalar items of the matrix becomes vectors. The initial shape 2x2 becomes 2x2x1 :
+ ```
+ [
+ [1,2],
+ [3,4]
+ ]
+ ```
+ becomes
+ ```
+ [
+ [ [1],[2]],
+ [ [3],[4]]
+ ]
+ ```
+ - we construct a zero tensor with the same shape 2x2x1:
+ ```
+ [
+ [ [0],[0]],
+ [ [0],[0]]
+ ]
+ ```
+ - Then we concat the two tensors along dimension 2. The resulting tensor is now 2x2x2
+ ```
+ [
+ [[1,0],[2,0]],
+ [[3,0],[4,0]]
+ ]
+ ```
+ - Then we reshape the tensor to 2x4 which is the size of the dilated tensor along the second dimension (columns):
+ ```
+ [
+ [1,0,2,0],
+ [3,0,4,0]
+ ]
+ ```
+ - Second, the same process is applied on the rows.
+
+We could also use `pad` to insert the zeroes. For instance, if we consider the dilation on the column dimension:
+- First, as before, we reshape the tensor from 2x2 to 2x2x1
+- We pad the tensor with zeroes (tensor is now 2x2x2):
+ ```
+ [
+ [[1,0],[2,0]],
+ [[3,0],[4,0]]
+ ]
+ ```
+- and we reshape it to 2x4
+ ```
+ [
+ [1,0,2,0],
+ [3,0,4,0]
+ ]
+ ```
+
+We could also use `Reshape` to flatten the tensor, then use `ScatterElement`to insert zeroes and use `Reshape` again to set final tensor shape.
+
+## MaxPool
+
+ - MaxPool = Slice + ReduceMax + Concat
+
+## LayerNormalization
+ - LayerNormalization = ReduceMean, Sub, Pow, Sqrt, Add, Div, Mul
+
+## GlobalAveragePool
+
+*Defined with respect to AveragePool.*
+
+## AveragePool
+
+`AveragePool` can be defined using a channel-wise convolution. Average pooling consists to apply a kernel which values are all equal to $1/\texttt{kernel size}$.
+
+
+It can also be described using `Slice`, `Reshape` and `ReduceMean`
+
+## HardSwish
+
+Operator `HardSwish` can be defined using the `max` and `min` operators:
+
+$$ hardswish(x) = x * max(0, min(1, \alpha * x + \beta))$$
+
+with $\alpha=1/6$ and $\beta=0.5$.
+
+It could also be expressed using `Relu` or `HardSigmoid`.
+
+## LeakyRelu
+
+The mathematical definition of `LeakyRelu` is
+
+$$ LeakyRelu(x) = \begin{cases}
+ x & \text{if}\ x \ge 0 \\
+ \alpha x & \text{if}\ x \lt 0
+ \end{cases} $$
+
+which can be written as
+
+$$LeakyRelu(x) = max(0,x)+min(0, \alpha x)$$
+
+## SoftMax
+
+Operator `SoftMax` is defined as follows:
+$$\texttt{SoftMax}(x_i) = { e^{x_i} \over {\sum_{j=1}^{K} e^{x_j}}}$$
+
+The numerator can be computed using operator `Exp`. The denominator can be computed using the `ReduceSum` operator that computes the sum of the input tensor’s elements along the provided axes.
+
+In order to ensure numerical stability, the actual formula should be
+
+$$\texttt{SoftMax}(x_i) = { e^{x_i-max(x)} \over {\sum_{j=1}^{K} e^{x_j-max(x)}}}$$
+
+which means that we have to use the `ReduceMax` operator.
+
+The netron representation of the `SoftMax` operator is given below:
+
+
+
+## SoftPlus
+
+Operator `SoftPlus` is defined as follows:
+$$\texttt{SoftPlus}(x) = \ln(e^{x} +1 )$$
+
+*Note that in the ONNX documenation, $\ln$ and $\log$ are used. There is a unique logarithm operator, `Log` that computes the natural (neperian) logarithm, i.e, $\log(x)=\ln(x)=\log_e(x)$.*
+
+This operator can be described using `Log` and `Add`.
+
+## Sigmoid
+
+The mathematical expression for operator `Sigmoid` is :
+
+$$ sigmoid(x)= { 1 \over 1+e^{-x} } $$
+
+This operator can be described using `Div`, `Exp` and `Add`.
+
+## Tanh
+
+The mathematical expression for operator `Tanh` is :
+
+$$ tanh(x)= { e^x-e^{-x} \over e^x+e^{-x} } $$
+or
+$$ tanh(x)= { 1-e^{-2x} \over 1+e^{-2x} } $$
+
+
+This operator can be described using `Div`, `Exp`, `Add`, `Sub`, `Mul`
+
+## Transpose
+
+Transpose could be expressed using
+- `Reshape` (or `Flatten`), to flatten the tensor,
+- a computation of the new indices
+- `Gather` to collect the elements of the flattened tensor
+- `Reshape` to set back the dimension of the tensor
+
+## Flatten
+
+Operator `Flatten` flattens a tensor starting from a given axis. If input tensor has shape $(d_0, d_1, … d_n)$ then the output will have shape $(d_0 \times d_1 \times ... \times d_{{axis}-1}, d_{axis} \times d_{{axis}+1} \times ... \times d_n)$.
+
+This operator can be represented using:
+- `Shape` to get the shape of the tensor
+- `Slice`, `ReduceProd`, and `Concat` to compute the flattened dimensions
+- `Reshape`
+
+The following picture gives an example of such transformation applied to a 2D input tensor (but it can be generalized).
+
+Values `dim0` and `dim1` correspond to the previous products:
+- ${dim_0} = d_0 \times d_1 \times ... \times d_{{axis}-1}$
+- ${dim_1} = d_{axis} \times d_{{axis}+1} \times ... \times d_n$
+
+
+
+
+Operator `Slice` uses the starts, ends, axes and steps inputs to select a sub-tensor of its input data tensor. (see the ONNX and numpy documenration: it is a pretty complicated operator...)
+
+## Gemm
+
+Operator `Gemm` is defined as follows:
+$$\texttt{Gemm}(A,B,C) = \alpha \times A' \times B’ + \beta \times C$$
+
+where $A'$ and $B'$ are the transposed version of $A$ and $B$ depending on attributes `transA` and `transB`.
+
+This operator can be described using operators `Transpose`, `MatMul`, `Add`, `Mul`.
+
+The booleans that control the transposition of $A$ and $B$ are attributes, not input tensors. Therefore, the description of the operator should probably (?) use a textual condition, (i.e., "if transA is true then...") rather than other operators. (To be discussed).
+
+
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/flatten_netron.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/flatten_netron.png
new file mode 100644
index 00000000..d26673cc
Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/flatten_netron.png differ
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/softmax_netron.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/softmax_netron.png
new file mode 100644
index 00000000..07653a59
Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/softmax_netron.png differ
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/dumitru-2025-07-21.md b/safety-related-profile/attic/operator_spec_sub_wg/dumitru-2025-07-21.md
new file mode 100644
index 00000000..4fb89111
--- /dev/null
+++ b/safety-related-profile/attic/operator_spec_sub_wg/dumitru-2025-07-21.md
@@ -0,0 +1,54 @@
+**Dumitru's proposal concerning broadcasting**
+
+ONNX has pretty complex broadcasting rules, described
+here:
+ https://onnx.ai/onnx/repo-docs/Broadcasting.html
+They claim they follow Numpy's rules on most operators,
+as defined here:
+
+ https://numpy.org/doc/stable/user/basics.broadcasting.html#general-broadcasting-rules
+
+and a different rule on Gemm and on Prelu.
+
+But both rules are pretty complex.
+
+> (Eric) They are not *** that *** complex but, for sure, adding broadcasting will make our life more complicated.
+>> (Dumitru) It's arguably more complex than the operators that use it, like "add".
+
+Thus, it draws attention away from the core functionality of the operator to the broadcasting.
+
+The question is: how to define the *semantics* of operators that perform broadcast? Add the (explicit) description of broadcasting to all operators performing it, or defined the operator semantics separately, and the broadcast separately?
+
+One first remark here is that actually decomposing inside ONNX for implementation purposes does not seem to be possible, as no explicit broadcast operator exists. This is unlike in numpy, where functions exist both for the broadcast of a tensor to a shape:
+
+ https://numpy.org/doc/stable/reference/generated/numpy.broadcast_to.html#numpy.broadcast_to
+
+and for computing the broadcast shape.
+
+ https://numpy.org/doc/stable/reference/generated/numpy.broadcast_shapes.html#numpy.broadcast_shapes
+
+However, for semantics definition purposes, my suggestion would be to decompose by introducing a "virtual" broadcast operator (e.g. why3 functions corresponding to numpy.broadcast_to, numpy.broadcast_shapes) and then defining the full semantics of the operator by composing this with a simple definition of the base operator.
+
+> (eric) As you write above, the other solution is to specify the operation including the broadcasting, as it will >be actually done in the implementation. This will make the mathematical formulation and the formal spec significantly more complicated because we will have to play with mods whenever we compute an index.
+
+>> (Dumitru) Exactly.
+
+Something like:
+```
+x = onnx.add(y,z)
+```
+would become:
+```
+ x_shape = broadcast_shapes(shape(y),shape(z))
+ y_full = broadcast_to(y,x_shape)
+ z_full = broadcast_to(z,x_shape)
+ x = add_homogenous(y_full,z_full)
+```
+
+> (Eric) I fully agree with the approach.
+> We may possibly add a predicate "need broadcasting" so that we can clearly express in which condition broadcasting is nedeed and possible. This will anyway be necessary to express the usage constraints.
+So, I propose that we keep the capability of broadcasting (for the moment we can keep our spec as is and we will add broadcasting in a second phase).
+
+> (Eric) Note that concerning `x_shape = broadcast_shapes(shape(y),shape(z))`, this raises another question : we have also forbidden shape inference. Normally, the shape of x should be known in advance...
+
+>> (Dumitru) Oh, then it's even easier, requiring in the operation properties that the constant x_shape provided by the programmer satisfies "x_shape = broadcast_shapes(shape(y),shape(z))"
diff --git a/safety-related-profile/attic/operator_spec_sub_wg/error_conditions.md b/safety-related-profile/attic/operator_spec_sub_wg/error_conditions.md
new file mode 100644
index 00000000..4ca15554
--- /dev/null
+++ b/safety-related-profile/attic/operator_spec_sub_wg/error_conditions.md
@@ -0,0 +1,140 @@
+# Introduction
+
+This document is a followup to our July 2nd discussion about the way to specify the behaviour of operators in the presence of errors conditions.
+
+# The issue
+Some operator are not defined for some input values. Typical examples are division by zero, overflows, etc.
+
+For simple operators, those conditions can be specified in the definition of the input domain. For instance `y = Div (a , b)` is not defined for $b=0$, and the domain of the inputs in $\mathbb R$ or $\mathbb Z$ is $\mathbb R\times \mathbb R^*$ or $\mathbb Z\times \mathbb Z^*$, respectively.
+
+For complex operators, the correct domain (where the function is defined) can't be defined easily due to the complexity of the relation between the error condition (e.g., some denominator equal to 0 or some overflow) and the inputs.
+
+**How can we address this in our informal specification?**
+
+For instance, let's take the `Add` operator whose signature is:
+
+`y : int32 = Add(a: int32, b: int32)`
+
+It can be specified as
+
+$y = a+b$ where operator $+$ is the usual mathematical addition defined in $\mathbb R$, $\mathbb Z$, etc.
+
+Several options for the specification can be proposed...
+
+# The options
+
+#### Option 0
+**Don't say anything...**
+
+The operator `Add` is simply specified as
+$$y = a + b $$
+
+Problem: this specification is incorrect since, as soon as $a+b > 2^{32}$, the value $a+b$ does not belong to the set of integers `INT32`
+.
+
+The user may hopefully be in the case where the condition $a+b\leq 2^{32}$ holds, but the specification remains incorrect or inconsistent (i.e., the "user" of the operator cannot expect condition $y=a+b$ to be satisfied for all values in the domain).
+
+#### Option 1
+**Specify the exact conditions (the domain) for which the specification is applicable.**
+
+Here, the condition is $a+b \leq 2^{32}-1$ (for the right bound of the domain) where, again, operator "$+$" is the usual addition.
+
+The specification becomes
+$$y = a + b $$
+for any $a$ and $b$ in $\mathbb Z \times \mathbb Z$ such that
+$-2^{32} \leq a+b \leq 2^{32}-1$
+
+This solution makes sense but it is not really practical since to check the condition *in practice*, we have to compute the result in $\mathbb Z$)...
+
+In practice, we would be happier with some condition - possibly more conservative - easier to check.
+
+For instance, for a simple addition, this could be : $-2^{31}+1\leq a \leq 2^{31}-1$ and $-2^{31}+1 \leq b \leq 2^{31}-1$ .
+
+However, this approach is not easy to implement for more complex operators. It **may** work for instance if we know that the input values are in a certain range (e.g., because they have been normalized) so that not overflow can occur.
+
+#### Option 2
+**Specify the operator in the set `INT32`, i.e., using signed, two's complement arithmetic.**
+
+Expressed using the usual $+$ operator, the specification becomes
+$$
+y =
+\begin{cases}
+(a + b) \bmod 2^n & \text{if } (a + b) \bmod 2^n < 2^{n-1} \\
+(a + b) \bmod 2^n - 2^n & \text{otherwise}
+\end{cases}
+$$
+
+Advantage: the operation is fully and precisely defined in `INT32`. It matches exactly what happens in an implementation where our `INT32` isa 32 bit machine value.
+
+Problem: This approach could be applied to simple operators but for more complex operator such as, for instance, a matrix multiplication, all intermediate operations used to describe the matrix multiplication would actually refer to operation in `INT32`. The usual $+$ will be replaced by $(+)$, etc. This approach is cumbersome and does not really express what we expect. In practice, we would probably prefer all intermediate "computations" to be carried out in $\mathbb Z$ and the final result to be mapped to `INT32` (to be compared with the case where all intermediate operations are carried out in $\mathbb Z$).
+
+Another solution would be to do all computations using standard arithmetic in $\mathbb Z$ (i.e., use $+$ in the usual addition in $Z$) and require that no "intermediate value" ever gets out of the `INT32`domain.
+
+In that case, it'll be the responsibility of the implementer or the user to ensure that all these "intermediate values" remain in the appropriate domain:
+- The implementer could perform actual (2's arithmetic) intermediate computations in a larger domain (tahn `INT32`) in order to prevent overflows
+- The user could perform some verification (by testing, formal verification, etc.) to ensure that all intermediate values actual remain in the `INT32` domain.
+
+Note that the concept of "intermediate values" is strange in a specification. In a specification, when we write $y=a+b+c+d$, we do not say anything about the order in which the operations shall be actually performed (thanks to commutativity and associativity). We just express that the result $y$ must be equal to $a+b+c+d$ which, for the usual operator $+$ and values in $\mathbb Z$, can be done in any possible way ($a+(b+c+d))$, $((a+b)+(c+d)$), etc.
+
+### Option 3
+**Specify that the semantics is the one defined using the usual $+$ operator considering that no overflow shall occur (some would write "no runtime error occurs").**
+
+Such specification is a bit of a chicken-egg problem in the sense that saying the "no overflow shall occur" is not a complete specification: what are those "overflows"? what is the actual conditions to be checked?
+
+Stated differently, the specification is something abstract and we don't really know "when" those "overflow" may occur.
+
+If we were to be more explicit, we could (for instance) require that, in the case of operation $y=a+b+c+d$ :
+- $a+b$ is in the domain of `INT32`
+- $b+c$ is in the domain of `INT32`
+- $c+d$ is in the domain of `INT32`
+- $a+c$ is in the domain of `INT32`
+- etc.
+
+because we simply don't know in which order the operations will be carried out.
+
+### Option 4
+**Simply state that "some error condition may occur" (e.g., an overflow).
+**
+
+This is strange to write such a thing in a specification... because (i) it is unclear and (ii) brings no useful information (besides the one of being "careful").
+
+### Option 5
+**Chose the most accurate option among the previous ones that can be implemented in practice for a given operator.**
+
+In the example of an operator that can lead to a division by zero, we would either say that "some division by zero could occur" (option 4) or "division by zero will occur for such or such input value" (option 1).
+
+# An illustration
+
+Let's consider the `MatMulInteger` operator.
+This operator computes a matrix multiplication with input tensors in `int8` and output tensor in `int32`.
+
+This operator does not overflow "most of the time" thanks to the width of the accumulator. However, it **can** overflow if the **sizes** of the input tensors are sufficiently large.
+
+Consider the multiplication of two tensors `A` and `B` of shape `[1,N]` and `[N,1]`. The smallest $N$ for which overflow can occur is such that
+$$
+127 \times 127 \times N > 2^{31} - 1
+$$
+
+this gives
+$$
+N > \frac{2^{32}-1}{127^2} \approx 133141.5
+$$
+
+Since N is integer, the minimal value for an error to trigger is
+$$
+N = 133142
+$$
+
+Strangely (?), overflow occurs for $N=133145$.
+
+As shown below, both python reference implementation and ORT give the same (incorrect) result:
+
+```
+ONNX python reference Output: [-2147471591]
+ONNX Runtime Output (int32): -2147471591
+Expected value: 2147495705
+```
+
+The specification of `MatMulInteger` could state a condition on the shapes of `A` and `B` to prevent overflows. The condition only concerns the dimension of accumulation: if the dimension is smaller than 133145, no overflow can occur.
+
+As stated above, we may also specify the multiplication using 2's complement arithmetic. This specification would give the exact (but unexpected) result.
\ No newline at end of file
diff --git a/safety-related-profile/meetings/operator_spec_sub_wg/worksharing.md b/safety-related-profile/attic/operator_spec_sub_wg/worksharing.md
similarity index 56%
rename from safety-related-profile/meetings/operator_spec_sub_wg/worksharing.md
rename to safety-related-profile/attic/operator_spec_sub_wg/worksharing.md
index 8c85bbae..225c3f65 100644
--- a/safety-related-profile/meetings/operator_spec_sub_wg/worksharing.md
+++ b/safety-related-profile/attic/operator_spec_sub_wg/worksharing.md
@@ -1,4 +1,18 @@
-|Bigram | Actual name |
+This document gives
+- the list of operators to be covered by the SONNX working group
+- the list of people involved in the **writing** and **review** processes
+- the status of each operator
+
+If you want to contribute, please:
+- add your name + initials in the "list of contributors"
+- add your initial in the list of "writers" and/or "local reviewers".
+
+*Thanks!*
+
+
+### List of contributors
+
+|Initials | Actual name |
|------|-------------|
| ej | Eric |
| mt | Mariem |
@@ -7,20 +21,38 @@
| tb | Thiziri |
| hb | Henri |
| nv | Nicolas |
+| sml| Salomé |
+| js | Jean |
+
+### Definition of statuses
+
+|Status | Meaning|
+|-------|--------|
+| DR | Draft |
+| RLR-i | Ready for local (*) review #i |
+| RGR-i | Ready for general (**) review #i |
+| RER-i | Ready for external review #i |
+| FI | Finalized |
+
+- (*) A "local" review involves a limited set of people.
+- (**) A "general review" involves the complete working group.
+- (***) An "external" review involves people outside of the working group.
+
+### Status of operators
-| Operator | Writers | Reviewers |
-|------------------------------|--------------------|-------------------|
-| Abs | | |
-| Add |hb?, |hb |
+| Operator | Writers | Local reviewers | Status (WR, RW, FI)
+|------------------------------|--------------------|-------------------|-------------------
+| Abs |hb, | | DR
+| Add |hb | sml | DR
| Cast | | |
| Clip | | |
-| Concat | | |
-| Constant | |hb |
+| Concat |sml | | RGR-1
+| Constant |hb, | | DR
| ConstantOfShape | | |
-| Conv |ej,mt |jlf,sb,tb,hb |
+| Conv |ej,mt |jlf,sb,tb,hb | RGR-1
| ConvTranspose | | |
| Dense | | |
-| Div |hb?, |hb |
+| Div |hb, | | DR
| Equal | | |
| Erf | | |
| Exp | | |
@@ -28,46 +60,45 @@
| Flatten | | |
| FullyConnected | | |
| Gather | | |
-| Gemm |nv | |
+| Gemm |nv | | DR
| GlobalAveragePool | | |
| GRU | | |
| HardSwish | | |
| Identity | | |
| LeakyRelu | | |
-| Less |hb?, |hb |
-| Log | | |
-| LSTM | | |
-| MatMul |nv | |
-| Max | | |
-| MaxPool | | |
+| Less |hb, | | DR
+| Log |hb, | | DR
+| LSTM |nv | | DR
+| MatMul |nv | | DR
+| Max |jlf | |
+| MaxPool |sml, js | |
| Min | | |
| Mod | | |
-| Mul |hb?, |hb |
-| Neg | | |
+| Mul |hb, | | DR
+| Neg |hb, | | DR
| Not | | |
| Pad | | |
-| Padding | | |
-| Pow | | |
+| Pow |hb, | | DR
| Range | | |
| ReduceMean | | |
| ReduceSum | | |
-| Relu | | |
+| Relu |sml, js | |
| Reshape | | |
-| Resize | | |
+| Resize |sml | |
| ScatterND | | |
| Shape | | |
-| Sigmoid | | |
+| Sigmoid | | | DR
| Slice | | |
| Softmax | | |
| SoftPlus | | |
| Split | | |
-| Sqrt | | |
+| Sqrt |hb, | | WR
| Squeeze | | |
-| Sub |hb?, |hb |
-| Tanh | | |
+| Sub |hb, | | WR
+| Tanh | | | WR
| Transpose | | |
| ConvTransposeDeconvolution | | |
| Unsqueeze | | |
-| Where |hb?, |hb |
+| Where |hb, | | WR
diff --git a/safety-related-profile/attic/profile_ir/IR.md b/safety-related-profile/attic/profile_ir/IR.md
new file mode 100644
index 00000000..4b9979bf
--- /dev/null
+++ b/safety-related-profile/attic/profile_ir/IR.md
@@ -0,0 +1,549 @@
+
+
+# Open Neural Network Exchange Intermediate Representation (ONNX IR) Specification
+
+__Purpose__
+
+This document contains the normative specification of the semantics of ONNX.
+
+The `.proto` and `.proto3` files found under the [onnx folder](/onnx/) form the normative specification of its syntax authored in the [Protocol Buffers](https://developers.google.com/protocol-buffers) definition language. Commentary found in the `.proto` and `.proto3` files are intended to improve readability of those files, but are not normative if they conflict with this document. Such conflicts should be reported as documentation bugs.
+
+__Notes on model validation__
+
+A [tool](../onnx/checker.py) is available to perform general validation of models against this specification. It is implemented in C++ with a Python command-line wrapper.
+
+__Notes on language in this and all related documents__:
+
+1. The use of SHOULD, MUST, MAY and so on in this document is consistent with [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt).
+
+2. The use of 'list' shall denote an ordered collection of items, 'set' shall denote an unordered collection of unique elements, and 'bag' an unordered collection of possibly non-unique elements.
+
+## Components
+
+ONNX is an open specification that consists of the following components:
+
+1) A definition of an extensible computation graph model.
+
+2) Definitions of standard data types.
+
+3) Definitions of built-in operators.
+
+#1 and #2 together make up the ONNX Intermediate Representation, or 'IR', specification which is covered herein; the built-in operators are covered in documents listed at the end. Specifically, built-in operators are divided into a set of primitive operators and functions. A function is an operator whose semantics is formally expressed via expansion into a sub-graph (called the function body) using other operators (and functions). Functionality-wise, an ONNX compatible framework or runtime may inline a function body to execute it if it does not have corresponding implementation of the function.
+
+There are two official ONNX variants; the main distinction between the two is found in the default operator sets. __ONNX-ML__ extends the __ONNX__ operator set with ML algorithms that are not based on neural networks.
+
+Up to IR version 6, the ONNX specification and model format addressed only inference (also known as scoring). Starting from IR version 7, the ONNX specification and model format also support training. An ONNX training model is an extension of the inference-model. An inference-only runtime can consume a training model ignoring the training-related extensions. However, an inference-only model may enable a representation that is more optimal for inference purposes than a training model.
+
+## Runtime Agnostic
+
+ONNX does not pre-suppose or imply any particular method of runtime implementation.
+
+For example, an implementation may consist of a rich runtime which interprets the model; it may be a code generator that translates the model in its entirety to executable code for some target programming language; it may be a hardware implementation; it may be a combination of two or three of those.
+
+Nothing in this specification should be construed as advocating one implementation approach over any other; any comments on the inner workings of concrete implementations are to be interpreted as examples.
+
+## ONNX Versioning
+
+The IR specification, individual models, and operator sets are all versioned. Furthermore, each individual operator indicates which version of its containing operator set it was introduced or stabilized in.
+
+Version numbers can be used as a simple number, or used to encode [semantic versions](https://semver.org/)(AKA SemVer). If using semantic versions, the convention is to use the two most significant bytes for the major number, the next two bytes for the minor number, and the least significant four bytes for the patch/build/bugfix number. When using semantic versioning, at least one of the major/minor numbers MUST be non-zero.
+
+The IR specification uses simple monotonically increasing numbers for its versions. The valid IR versions are defined by the `onnx.Version` enumeration in [onnx.proto](/onnx/onnx.proto).
+
+Operator sets use a simple version number. Each operator set version represents a snapshot of the set of operators, and their semantics at a particular point in time.
+
+This specification does not provide guidance on what versioning scheme model producers should be using.
+
+More details on conventions and best practices for versioning of IR, operator sets, and models can be found in [Versioning](Versioning.md).
+
+## Extensible computation graph model
+
+ONNX specifies the portable, serialized format of a computation graph. It does not have to be the form a framework chooses to use internally. For example, an implementation may represent the model differently in memory if it is more efficient to manipulate during optimization passes.
+
+An implementation MAY extend ONNX by adding operators expressing semantics beyond the standard set of operators that all implementations MUST support. The mechanism for this is adding operator sets to the `opset_import` property in a model that depends on the extension operators.
+
+### Models
+
+The top-level ONNX construct is a ‘Model.’, and is represented in protocol buffers as the type `onnx.ModelProto`
+
+The main purpose of the model structure is to associate metadata with a graph which contains all the executable elements. The metadata is used when first reading the model file, giving an implementation the information it needs in order to determine whether it will be able to execute the model, generate logging messages, error reports, etc. Further, the metadata is useful to tools, such as IDEs and model galleries, which need it for informing humans about a given model’s purpose and characteristics.
+
+Each model has the following components:
+
+|Name|Type|Description|
+|---|---|---|
+|ir_version|int64|The ONNX version assumed by the model.|
+|opset_import|OperatorSetId|A collection of operator set identifiers made available to the model. An implementation must support all operators in the set or reject the model.|
+|producer_name|string|The name of the tool used to generate the model.|
+|producer_version|string|The version of the generating tool.|
+|domain|string|A reverse-DNS name to indicate the model namespace or domain, for example, 'org.onnx'|
+|model_version|int64|The version of the model itself, encoded in an integer.|
+|doc_string|string|Human-readable documentation for this model. Markdown is allowed.|
+|graph|Graph|The parameterized graph that is evaluated to execute the model.|
+|metadata_props|map|Named metadata values; keys should be distinct.|
+|training_info|TrainingInfoProto[]|An optional extension that contains information for training.|
+|functions|FunctionProto[]|An optional list of functions local to the model.|
+
+ Models MUST specify a domain and use reverse domain names based on the responsible organization's identity, the same convention that is used for [naming Java packages](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html).
+
+__Note: Exploring an ONNX file__
+
+You can use the `protoc` tool that is part of the Protocol Buffers distribution to examine the contents of an ONNX file, you do so like this:
+
+```
+$ protoc --decode=onnx.ModelProto onnx.proto < yourfile.onnx
+```
+
+Where [onnx.proto](/onnx/onnx.proto) is the file that is part of this repository.
+
+Alternatively, you can use a tool like [Netron](https://github.com/lutzroeder/netron) to explore the ONNX file.
+
+### Model Semantics
+
+The semantics of an inference-model is a _stateless function_ (except possibly for the state used for random-number generation). Thus, whenever an inference-model (without random-generator operations) is used to perform inference on the same input, it is expected to produce the same output.
+
+The semantics of a training model is that of a _stateful object_, with the state consisting of the current values of trained-weights (and any other auxiliary state required, such as momentum, for example, used by the learning algorithm). Specifically, its semantics is captured via three methods: an initialization method (which is used to initialize or reset the values of state variables), a training step method (to train using a batch of input-output pairs), and an inference method to perform inference using the current values of the learned weights. The first two methods update the state of the object, while the third method is side-effect-free.
+
+### Optional Metadata
+
+The 'metadata_props' field in the model is available for any kind of optional metadata that a tool or model developer chooses to place there. The following are the defined “standard” optional metadata properties of a model.
+
+Name|Type|Format|Description
+|---|---|---|---|
+model_author|string|A comma-separated list of names.|The personal name of the author(s) of the model, and/or their organizations.
+model_license|string|Name or URL.|The well-known name or URL of the license under which the model is made available.
+
+### Operator Set Identifiers
+
+Each operator set is uniquely identified by a (domain, version) pair.
+
+Name|Type|Description
+|---|---|---|
+domain|string|The domain of the operator set being identified.
+version|int64|The version of the operator set being identified. Same as 'opset_version' in the operator set.
+
+The operator set version is a simple integer value that is monotonically increased as new versions of the operator set are published.
+
+Operator sets other than the default operator set MUST specify a domain and SHOULD use reverse domain names based on the responsible organization's identity, the same convention that is used for [naming Java packages](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html).
+
+### Operator Sets
+
+Each model MUST explicitly name the operator sets that it relies on for its functionality. Operator sets define the available operators and their version. Each model defines the imported operator sets by their domains. All models implicitly import the default ONNX operator set.
+
+Each operator set SHALL be defined in a separate document, also using protobuf as the serialization format. How operator set documents are found at runtime is implementation-dependent.
+
+__Note: As of the publication of this document, no ONNX implementation is known to process operator set documents.__
+
+The properties of an operator set are:
+
+Name|Type|Description
+|---|---|---|
+magic|string|The value ‘ONNXOPSET’
+ir_version|int32|The ONNX version corresponding to the operators.
+ir_version_prerelease|string|The prerelease component of the SemVer of the IR.
+ir_build_metadata|string|The build metadata of this version of the operator set.
+domain|string|The domain of the operator set. Must be unique among all sets.
+opset_version|int64|The version of the operator set.
+doc_string|string|Human-readable documentation for this operator set. Markdown is allowed.
+operator|Operator[]|The operators contained in this operator set.
+
+The operator set version is a simple integer value that is monotonically increased as new versions of the operator set are published.
+
+Operator sets other than the default operator set MUST specify a domain and SHOULD use reverse domain names based on the responsible organization's identity, the same convention that is used for [naming Java packages](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html).
+
+### Operators
+
+Each operator used within a graph MUST be explicitly declared by one of the operator sets imported by the model.
+
+The properties of an operator definition are:
+
+Name|Type|Description
+|---|---|---|
+op_type|string|The name of the operator (case sensitive), as used in graph nodes. MUST be unique within the operator set’s domain.
+since_version|int64|The version of the operator set when this operator was introduced.
+status|OperatorStatus|One of ‘EXPERIMENTAL’ or ‘STABLE.’
+doc_string|string|A human-readable documentation string for this operator. Markdown is allowed.
+
+The version value MUST be the same value as the operator set version when the operator was first published. Subsequent versions of the operator set MUST NOT alter the signature or semantics of the operator once published as STABLE.
+
+The ‘status’ property indicates whether the syntax, semantics, or presence of the operator is in an experimental or stable stage. Once an operator is published as STABLE, it’s syntax and semantics MUST NOT change in subsequent versions of the operator set.
+
+There are two distinct ways to pass information to operators – inputs and attributes. Inputs represent graph inputs or values computed elsewhere in the graph, while attributes are used for values that are constants in the graph. This distinction may be highly relevant to achieving good performance for some implementations, while completely irrelevant to others.
+
+### Functions
+
+A _function_ may be thought of as an operator combined with an implementation of the operator using
+other, more primitive, ops, referred to as the _function body_. The function body consists of a
+topologically sorted list of nodes that form a graph. Thus, a function combines aspects of both
+an operator as well a graph (described below).
+
+Each function contained in a Model (also referred to as a model-local function) serves
+as a default or fallback implementation of the corresponding operator. A runtime, however,
+may choose to use an alternative implementation of the operator (usually as an optimized kernel).
+As such, the unique name of a function is significant as it is implicitly associated with a
+semantic specification.
+
+A serialized function (a _FunctionProto_) has the following properties:
+
+|Name|Type|Description|
+|---|---|---|
+name|string|The name of the function
+domain|string|The domain to which this function belongs
+overload|string|Part of unique id of function (added in IR version 10)
+doc_string|string|Human-readable documentation for this function. Markdown is allowed.
+attribute|string[]|The attribute parameters of the function
+attribute_proto|Attribute[]| (IR version 9+) The attribute parameters with default values of the function. A function attribute shall be represented either as a string attribute or an Attribute, not both.
+input|string[]|The input parameters of the function
+output|string[]|The output parameters of the function.
+node|Node[]|A list of nodes, forming a partially ordered computation graph. It must be in topological order.
+|opset_import|OperatorSetId|A collection of operator set identifiers used by the function implementation.
+|value_info|ValueInfo[]| (IR version >= 10) Used to store the type and shape information of values used in the function.
+|metadata_props|map|(IR version >= 10) Named metadata values; keys should be distinct.
+
+The name and domain serve to identify the operator uniquely in IR versions upto 9. IR version 10 adds the
+field overload, and the triple (name, domain, overload) acts as a unique-id across functions stored in
+a model. This is intended to support cases where distinct function-bodies are required for distinct
+calls to the function within the model.
+An opset version is not explicitly identified in a FunctionProto, but it is implicitly determined by
+the opset version of the domain included in the model.
+
+The input, output, attribute, and attribute_proto (added in IR version 9) constitute the signature part of the operator. No type information
+is explicitly included in the signature. The attribute_proto field describes attribute parameters of the function along with their default-value (when not specified by an call-site node), while the attribute field lists attribute parameters without a default-value. The names in these two lists must be distinct. When an attribute-parameter of the function is used in a node within the function, it is replaced by the actual parameter value specified for the attribute at a call-site node (of the function) when such a attribute is specified, and it is replaced by the default-value if the attribute has a default-value specified, and it is omitted otherwise.
+
+The opset_import and node fields describe the implementation of the function.
+
+The value_info field (added in IR version 10) allows a model to store type and shape information about the values used in a function, including its inputs and outputs. Note that this is optional, and ONNX allows functions to be polymorphic.
+
+### Graphs
+
+A graph is used to describe a side-effect-free computation (function).
+A serialized graph is comprised of a set of metadata fields, a list of model parameters, and a list of computation nodes.
+
+Each computation dataflow graph is structured as a topologically sorted list of nodes that form a graph, which MUST be free of cycles. Each node represents a call to an operator or a model local function. Each node has zero or more inputs and one or more outputs.
+
+Graphs have the following properties:
+
+|Name|Type|Description|
+|---|---|---|
+name|string|The name of the model graph.
+node|Node[]|A list of nodes, forming a partially ordered computation graph based on input/output data dependencies. It is in topological order.
+initializer|Tensor[]|A list of named tensor values. When an initializer has the same name as a graph input, it specifies a default value for that input. When an initializer has a name different from all graph inputs, it specifies a constant value. The order of the list is unspecified.
+doc_string|string|Human-readable documentation for this model. Markdown is allowed.
+input|ValueInfo[]|The input parameters of the graph, possibly initialized by a default value found in ‘initializer.’
+output|ValueInfo[]|The output parameters of the graph. Once all output parameters have been written to by a graph execution, the execution is complete.
+value_info|ValueInfo[]|Used to store the type and shape information of values that are not inputs or outputs.
+|metadata_props|map|(IR version >= 10) Named metadata values; keys should be distinct.
+
+ValueInfo has the following properties:
+
+Name|Type|Description
+|---|---|---|
+name|string|The name of the value/parameter.
+type|Type|The type of the value **including shape information**.
+doc_string|string|Human-readable documentation for this value. Markdown is allowed.
+
+Each main (top-level) graph MUST define the names, types and shapes of its inputs and outputs, which are specified as ‘value info’ structures. The main graph inputs and outputs are required to have a shape, indicating the rank, even though the exact dimensions need not be specified.
+
+Nested subgraphs (specified as attribute values) MUST define the names of its inputs and outputs
+and MAY define the types of its inputs and outputs.
+
+Each graph MUST specify a name.
+
+The graph MUST adhere to single static assignment (SSA) for all node outputs; this means that all node output names MUST be unique within a graph.
+
+Graphs SHOULD be populated with documentation strings, which MAY be interpreted using GitHub-style markdown syntax. HTML and other text-markup languages MAY NOT be used in documentation strings.
+
+### Names Within a Graph
+
+All names SHOULD adhere to [C90 identifier syntax rules](https://en.cppreference.com/w/c/language/identifier).
+
+Names of nodes, inputs, outputs, initializers, and attributes are organized into several namespaces. Within a namespace, each name MUST be unique for each given graph. Please see below for further clarification in the case where a graph contains nested subgraphs (as attribute values).
+
+The namespaces are:
+
+Namespace|Description
+|---|---|
+Attribute|The names of attributes of an operator. Unique for each operator.
+Value|The names of values – node inputs & outputs, tensor values (if named), graph inputs, outputs.
+Node|The names of graph nodes.
+Graph|The names of graphs within a domain, unique within the model domain.
+Operator|The names of operators within a domain.
+Shape|The names of tensor shape variables – scoped to the value information records of a graph, which is where shape variables occur.
+
+
+### Nodes
+
+Computation nodes are comprised of a name, the name of an operator that it invokes, a list of named inputs, a list of named outputs, and a list of attributes.
+
+Input and outputs are positionally associated with operator inputs and outputs. Attributes are associated with operator attributes by name.
+
+They have the following properties:
+
+Name|Type|Description
+|---|---|---|
+name|string|An optional name of the node, used for diagnostic purposes only.
+input|string[]|Names of the values used by the node to propagate input values to the node operator. It must refer to either a graph input, a graph initializer or a node output.
+output|string[]|Names of the outputs used by the node to capture data from the operator invoked by the node. It either introduces a value in the graph or refers to a graph output.
+op_type|string|The symbolic identifier of the operator to invoke.
+domain|string|The domain of the operator set that contains the operator named by the op_type.
+attribute|Attribute[]|Named attributes, another form of operator parameterization, used for constant values rather than propagated values.
+doc_string|string|Human-readable documentation for this value. Markdown is allowed.
+overload|string|Part of unique id of function (added in IR version 10)
+|metadata_props|map|(IR version >= 10) Named metadata values; keys should be distinct.
+
+A name belonging to the Value namespace may appear in multiple places, namely as a graph input, a graph initializer, a graph output, a node input, or a node output. The occurrence of a name as a graph input, a graph initializer, or as a node output is said to be a definition and the occurrence of a name as a node input or as a graph output is said to be a use.
+
+A value name used in a graph must have a unique definition, with the exception that the same name MAY appear in both the graph input list and graph initializer list. (Further exceptions apply in the presence of nested subgraphs, as described later.)
+
+When a name appears in both the initializer list and the graph input list, a runtime MAY allow a caller to specify a value for this (input) name overriding the value specified in the initializer and a runtime MAY allow users to omit specifying a value for this (input) name, choosing the value specified in the initializer. Names of constants that are not meant to be overridden by the caller should appear only in the initializer list and not in the graph input list. In models with IR version >= 4, in nested subgraphs used as attribute values, users MUST NOT use the same name as both a subgraph initializer and subgraph input unless the corresponding op's specification explicitly allows it. In models with IR version <= 3, users MAY use the same name as both a subgraph initializer and subgraph input, but this is restricted to support constants via initializers that are not intended to correspond to any actual inputs passed from the node into the subgraph. In particular, the control-flow operator semantics determines the set of inputs supplied to the execution of the subgraph, and these input names MUST NOT appear as subgraph initializers. Subgraph initializer names must appear in the graph input list _after_ the actual inputs. This allows the actual inputs and formal inputs to be matched positionally.
+
+Edges in the computation graph are established by outputs of one node being referenced by name in the inputs of a subsequent node.
+
+The outputs of a given node introduce new names into the graph. The values of node outputs are computed by the node's operator. Node inputs MAY refer to node outputs, graph inputs, and graph initializers. When the name of a node output coincides with the name of a graph output, the graph output's value is the corresponding output value computed by that node. A node input in a nested subgraph MAY refer to names introduced in outer graphs (as node outputs, graph inputs, or graph initializers).
+
+The graph MUST use single static assignment for all node outputs, which means that all node output names MUST be unique within a graph. In the case of a nested subgraph, a node output name MUST be distinct from the names from the outer scopes that are visible in the nested subgraph.
+
+Node dependencies MUST NOT create cycles in the computation graph.
+
+The number of inputs and outputs in a node, their types, the set of attributes specified in a node and their types MUST satisfy the constraints imposed by the signature of the node’s operator.
+
+The list of nodes defining the top-level computation graph MUST be ordered topologically; that is, if node K follows node N in the graph, none of the data inputs of N may refer to outputs of K.
+
+Node attributes are used to pass literal (static) values to operators.
+
+#### Input and Output Values
+
+The representation distinguishes between two kinds of values: attribute values, which are statically known, and input/output values. The types of values permitted in the two cases are different.
+
+Input and output values are found as graph inputs, outputs, and initializers, and as node inputs and outputs. Their values are determined at runtime, either by the code that initiates model execution, or by operators computing output values.
+
+#### Attributes
+
+Attribute values are only found in nodes, passed to operators by name association. Attribute values are runtime constants, in that their values are determined when a model graph is constructed and therefore not computed at runtime. A common use for attributes is to represent coefficients established during model training.
+
+Attributes have the following properties:
+
+Name|Type|Description
+|---|---|---|
+name|string|The name of the attribute. Must be unique among attributes, inputs, and outputs for any given operator and node.
+doc_string|string|Human-readable documentation for this value. Markdown is allowed.
+type|AttributeType|The type of the attribute, determining which of the remaining fields is used to hold the value of the attribute.
+f|float|A 32-bit floating-point value.
+i|int64|A 64-bit integer value.
+s|byte[]|UTF-8 string.
+t|Tensor|A tensor value.
+g|Graph|A graph.
+floats|float[]|A list of 32-bit floating-point values.
+ints|int64[]|A list of 64-bit integer values.
+strings|byte[][]|A list of UTF-8 strings.
+tensors|Tensor[]|A list of tensor values.
+graphs|Graph[]|A list of graphs.
+ref_attr_name|string|The name of a parent function's attribute.
+
+The properties ‘name’ and ‘type’ are required on all attributes, and ‘doc_string’ SHOULD be used on all attributes. An attribute MUST have only one of the value-carrying properties.
+
+In case ‘ref_attr_name’ is set, this attribute does not contain data, and instead it's a reference to the parent function's attribute of the given name. Can only be used within the function body.
+
+
+#### Variadic Inputs and Outputs
+
+The last input or output of an operator MAY be marked as variadic. For example, the operator 'Max()' can be used to compute the maximum of a varying number of input values. A variadic operator has a minimum arity, which specifies the minimum number of operands that must be specified.
+
+For each variadic operator input, N or more node inputs must be specified where N is the minimum arity of the operator. For each variadic operator output, N or more node outputs must be specified where N is the minimum arity of the operator.
+
+#### Optional Inputs and Outputs
+
+##### Static Optional
+
+Some operators have inputs that are marked as optional, which means that a referring node MAY forgo providing values for such inputs.
+
+Some operators have outputs that are optional. When an actual output parameter of an operator is not specified, the operator implementation MAY forgo computing values for such outputs.
+
+There are two ways to leave an optional input or output unspecified: the first, available only for trailing inputs and outputs, is to simply not provide that input or output; the second method is to use an empty string in place of an input or output name.
+
+Each node referring to an operator with optional outputs MUST provide a name for each output that is computed and MUST NOT provide names for outputs that are not computed.
+
+Optional inputs and outputs of the above kind are referred to as _static-optional_.
+
+##### Dynamic Optional (since IR-8)
+
+**IR-8 Version** introduced a new type-constructor to represent _dynamic-optional_ inputs and outputs,
+in addition to the earlier static-optional version described above. A dynamic-optional INT64
+tensor is a distinct type from an INT64 tensor type. In contrast, a static-optional INT64
+tensor does not have a distinct type, it has the same type as a INT64 tensor.
+The operators `Optional` and `OptionalGetElement` MUST be explicitly used to convert between
+the dynamic-optional type and the underlying non-optional type.
+The dynamic-optional allows for more expressiveness than static-optional.
+
+#### External Tensor Data
+
+The raw data for large constant tensors, such as initializers, MAY be serialised in a separate file. In such a case, the tensor MUST provide the filename relative to the model file and MUST NOT use the value fields. It MAY provide a byte offset and length within that file. It MAY also specify a SHA1 digest of the file. One file MAY contain the data for multiple tensors.
+
+More details can be found in [External Data](ExternalData.md).
+
+## Standard data types
+
+There are two official ONNX variants; the main distinction between the two is found in the supported types and the supported operators.
+
+With respect to supported types, both __ONNX__ and __ONNX-ML__ definition recognize tensors, sparse tensors, sequences, maps, and optionals as input and output types. Sequences and maps were supported from the IR version 6 (ONNX 1.6.0 release). Optional type was supported from IR vesion 8 (ONNX 1.10.0 release).
+
+The following data types are supported by ONNX for inputs and outputs of graphs and nodes as well as the initializers of a graph.
+
+Primitive numeric, string, and Boolean types MUST be used as elements of tensors.
+
+### Tensor Definition
+
+Tensors are a generalization of vectors and matrices; whereas vectors have one dimension, and matrices two, tensors can have any number of dimensions, including zero. A zero-dimensional tensor is logically equivalent to a scalar value.
+
+Mathematically, a tensor can be defined as a pair of sequences/lists (V, S) where S is the shape of the tensor (a list of non-negative integers) and V is a list of values with length equal to the product of the dimensions in S. Two tensors (V, S) and (V', S') are equal if and only if V = V' and S = S'. The length of S is referred to as the rank.
+
+ - If S has length 0, V must have length 1, since the empty product is defined to be 1. In this case, the tensor represents a scalar.
+ - S can contain dimensions of value 0. If any dimensions are 0, V must have length 0.
+ - If S has length 1, V has length equal to the single dimension in S. In this case, the tensor represents a vector.
+ - A tensor representing a vector of length 1 has shape [1], while a tensor representing a scalar has shape []. They both have a single element, but scalars are _not_ vectors of length 1.
+
+A tensor's shape S is a list but can be represented as a tensor with values S and shape [R] where R is the rank of the tensor.
+
+ - For a tensor (V, S), the tensor representing its shape is (S, [R]).
+ - The shape of a scalar is []. Represented as a tensor, [] has shape [0].
+
+#### Representation
+
+It is common to represent a tensor as a nested list. This generally works fine, but is problematic when zero dimensions are involved. A tensor of shape (5, 0) can be represented as [[], [], [], [], []], but (0, 5) is represented as [] which loses the information that the second dimension is 5.
+
+ - A nested list is not a complete representation of a tensor with dimensions of value zero.
+
+### Tensor Element Types
+
+|Group|Types|Description|
+|---|---|---|
+Floating Point Types|float16, float32, float64, bfloat16, float8e4m3fn, float8e5m2, float8e4m3fnuz, float8e5m2fnuz, float4e2m1|Values adhering to the IEEE 754-2008 standard representation of floating-point data or defined in papers [FP8 Formats for Deep Learning](https://arxiv.org/abs/2209.05433), [8-bit Numerical Formats for Deep Neural Networks](https://arxiv.org/abs/2206.02915), and the [Open Compute Project](https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf)
+Signed Integer Types|int4, int8, int16, int32, int64|Signed integers are supported for 4-64 bit widths.
+Unsigned Integer Types|uint4, uint8, uint16, uint32, uint64|Unsigned integers are supported for 4-64 bit widths.
+Complex Types|complex64, complex128|A complex number with either 32- or 64-bit real and imaginary parts.
+Other|string|Strings represent textual data. All strings are encoded using UTF-8.
+Other|bool|Boolean values represent data with only two values, typically true and false.
+
+### Input / Output Data Types
+
+The following types are used to define the types of graph and node inputs and outputs.
+
+|Variant | Type | Description |
+|---|---|---|
+ONNX|dense tensors|Represents a Tensor. See definition above.
+ONNX|sequence|Sequences are dense, ordered, collections of elements that are of homogeneous types.
+ONNX|map|Maps are associative tables, defined by a key type and a value type.
+ONNX|optional|Optionals are wrappers that may contain an element of tensor, sequence, or map type, or may be empty (containing none). [Details](ONNXTypes.md)
+
+#### Static tensor shapes
+
+In addition to element type, tensor types have a **static** shape. The static shape of a tensor variable is related to, but different from, the runtime (dynamic) shape of a tensor value. A static tensor shape is a list of records that indicates whether the tensor is a vector, a matrix, or a higher-dimensional value. For example, a 100x100 matrix has the shape [100,100].
+
+The static shape is defined by 'TensorShapeProto':
+
+```
+message TensorShapeProto {
+ message Dimension {
+ oneof value {
+ int64 dim_value = 1;
+ string dim_param = 2;
+ };
+ };
+ repeated Dimension dim = 1;
+}
+```
+Which is referenced by the Tensor type message:
+
+```
+ message Tensor {
+ optional TensorProto.DataType elem_type = 1;
+ optional TensorShapeProto shape = 2;
+ }
+```
+
+The empty list of dimension sizes, [], is a valid tensor shape, denoting a zero-dimension (scalar) value. A zero-dimension tensor is distinct from a tensor of unknown dimensionality, which is indicated by an absent 'shape' property in the Tensor message. When the shape property is absent in the type of a value (including node input),
+it indicates that the corresponding runtime value may have any shape. This sub-section describes how to interpret a missing-shape or a shape with missing dimensions etc. However, specific usage contexts may impose further constraints on a type and shape.
+For example, the inputs and outputs of a model (top-level graph) are required to *have* a shape, indicating the rank of inputs and outputs,
+even though the exact dimensions need not be specified.
+
+Each size in the list MAY be expressed as an integral value or as a "dimension variable," a string denoting that the actual size of the dimension is not statically constrained to a particular number. This is useful for declaring interfaces that care about the number of dimensions, but not the exact size of each dimension. A dimension MAY have neither dim_value nor dim_param set. Such a dimension represents an unknown dimension unrelated to other unknown dimensions.
+
+For example, a NxM matrix would have the shape list [N,M].
+
+The name of each dimension variable SHOULD adhere to [C90 identifier syntax rules](https://en.cppreference.com/w/c/language/identifier).
+
+Currently, dimension variables are not scoped. A dimension variable "N" represents the same value across the entire graph in a model. For example, if the graph has two inputs X and Y each with shape ["N"], then at runtime the values passed in for X and Y MUST be tensors of rank 1 with the same dimension. Nested sub-graphs currently share the same scope for dimension variables as the main-graph. This allows a model to relate the dimensions of tensors inside the subgraph to the dimensions of tensors in the outer graph.
+
+ONNX supports types such as Sequences of Tensors. The global scoping of dimension variables means that a variable with type "Sequence" represents a sequence of tensors that *all have the same shape*. The dimension variables M or N must be omitted from the above type if that dimension does not have a fixed size across all tensors in the sequence. The entire shape must be omitted from the type if different tensors in the sequence may have different ranks.
+
+For example, a graph that performs matrix cross-product may be defined as taking two inputs of shape [K,M] and [M,N], and producing an output of shape [K,N].
+
+Shapes MAY be defined using a combination of integers and variables.
+
+_Historical Notes_: The following extensions were considered early on, but were never implemented or supported.
+* The use of an empty string (as a dimension variable) to denote an unknown dimension not related to any other dimension. This was discarded in favor of using a Dimension with neither dim_value nor dim_param set.
+* The use of the string "\*" (as a dimension variable) to denote a sequence of zero or more dimensions of unknown cardinality. This is not supported. In the current implementation, the number of dimensions in a shape MUST represent the rank of the tensor. A tensor of unknown rank is represented using a TypeProto::Tensor object with no shape, which is legal.
+* A scoping mechanism to allow dimension variables that are local to a sub-graph (such as the body of a loop) may be useful, but is not currently supported.
+* ONNX supports types such as Sequences of Tensors. A scoping mechanism for the dimension variables local to a type may be useful to distinguish between the following two types: a sequence of square matrices (of differing sizes) vs a sequence of square matrices (all of same size). This is not currently supported.
+
+### Attribute Types
+
+The type system used for attributes is related to but slightly different from that used for of inputs and outputs. Attribute values may be a dense tensor, sparse tensor, a scalar numerical value, a string, a graph, or repeated values of one of the above mentioned types.
+
+## Other Metadata
+
+The ModelProto structure, and in IR versions >= 10, various other structures (GraphProto, FunctionProto, NodeProto)
+contain a metadata_props field allowing users to store other metadata in the form of key-value pairs.
+It is recommended that users use key names qualified with a reverse-DNS name as prefix
+(such as "ai.onnxruntime.key1") to avoid conflicts between different uses.
+Unqualified names may be used in the future by the ONNX standard.
+
+## Training Related Information
+
+Training related information is described by one or more instances of _TrainingInfoProto_ contained in a model. Each TrainingInfoProto contains information describing both an initialization step and a training step.
+
+The initialization step is described using a Graph (TrainingInfoProto.initialization) and an initialization-binding map (TrainingInfoProto.initialization_binding). The initialization step is performed by evaluating the Graph, and assigning the outputs produced by the Graph to the _state variables_ of the training model as specified in the initialization-binding. The initialization-binding is conceptually a map, specified as a list of key-value pairs, where each key is the name of a state variable, and the value is the name of an output of the (initialization) Graph. Each name specified as a key in the binding MUST be the name of an initializer that appears in the main inference graph (i.e., in ModelProto.graph.initializer) or the name of an initializer that appears in TrainingInfoProto.algorithm.initializer. Each name specified as a value in the binding MUST be the name of an output of the TrainingInfoProto.initialization graph. Key values specified in the repeated initialization_binding field MUST be unique.
+
+The training step is similarly described using a Graph (TrainingInfoProto.algorithm) and an update-binding map (TrainingInfoProto.update_binding). The training step is performed by evaluating the Graph and assigning the outputs produced by the Graph to the state variables as specified in the update-binding. The constraints and description presented above for the initialization apply to the training step as well.
+
+Thus, the state variables of the training model consist of a subset of the initializers of the main inference graph (i.e., ModelProto.graph.initializer) and the training-algorithm graph (TrainingInfoProto.algorithm.initializer) as identified by the keys of the bindings (in TrainingInfoProto.initialization_binding and TrainingInfoProto.update_binding). Note that the state variables are not constant values in the context of training. They represent mutable variables shared by multiple graphs (implicitly declared in the top-level training model scope). This implicit declaration of shared mutable variables is used instead of an explicit declaration for purposes of backward compatibility with the inference graph representation.
+
+All state variables are pre-initialized to the value specified in the corresponding initializer. A subsequent call to perform the initialization step (using the appropriate API exposed by a runtime) updates the values of the state variables as described above. If the training model has more than one instance of TrainingInfoProto, the initialization step corresponding to each is performed in order. A TrainingInfoProto.initialization MAY be omitted (only if there are no initialization_bindings). For the training step, a runtime MAY allow users to invoke any one of the TrainingInfoProto.algorithm, allowing the training process to interleave the different algorithms as desired. The order in which the different TrainingProto.algorithms are called affects the training result, and it is the callers responsibility to call them in the correct order.
+
+## Other Specification Documents
+
+The ONNX specification is comprised of this document, which defines the semantics of the IR and the standard data types, and the following documents defining standard operator semantics and the IR syntax. The latter is specified as Protobuf v2 and v3 schema files.
+
+See the [metadata category documentation](MetadataProps.md) for more details.
+
+### Operators
+
+[Neural Network Operators](Operators.md)
+
+[Classical Machine Learning operators](Operators-ml.md)
+
+### Syntax
+
+[ONNX Models and Graphs - protobuf v2](../onnx/onnx.proto)
+
+[ONNX Models and Graphs - protobuf v3](../onnx/onnx.proto3)
+
+[ONNX-ML Models and Graphs - protobuf v2](../onnx/onnx-ml.proto)
+
+[ONNX-ML Models and Graphs - protobuf v3](../onnx/onnx-ml.proto3)
+
+[ONNX Operator Sets - protobuf v2](../onnx/onnx-operators.proto)
+
+[ONNX Operator Sets - protobuf v3](../onnx/onnx-operators.proto3)
+
+[ONNX-ML Operator Sets - protobuf v2](../onnx/onnx-operators-ml.proto)
+
+[ONNX-ML Operator Sets - protobuf v3](../onnx/onnx-operators-ml.proto3)
+
+### Versioning Conventions and Best Practices
+
+[Versioning](Versioning.md)
\ No newline at end of file
diff --git a/safety-related-profile/attic/profile_opset/README.md b/safety-related-profile/attic/profile_opset/README.md
new file mode 100644
index 00000000..27869bff
--- /dev/null
+++ b/safety-related-profile/attic/profile_opset/README.md
@@ -0,0 +1,5 @@
+## Operators specification
+
+- **Informal Specification:** [Link](../../sonnx/ops/spec/informal)
+- **Formal Specification:** [Link](../../sonnx/ops/spec/formal)
+- **Guidelines for Informal Specification:** [Link](../../sonnx/ops/docs/guidelines/informal.md)
\ No newline at end of file
diff --git a/safety-related-profile/documents/conv_specification_example/reviews/README.md b/safety-related-profile/attic/profile_opset/conv/reviews/README.md
similarity index 91%
rename from safety-related-profile/documents/conv_specification_example/reviews/README.md
rename to safety-related-profile/attic/profile_opset/conv/reviews/README.md
index 76d3bc06..0e9ee04f 100644
--- a/safety-related-profile/documents/conv_specification_example/reviews/README.md
+++ b/safety-related-profile/attic/profile_opset/conv/reviews/README.md
@@ -1,4 +1,4 @@
-This directory contains the reviews of the **CONV2D operator** description.
+This directory contains the reviews of the **CONV2D operator** description and tests.
| Id | Who |
|----------|---------------------------|
diff --git a/safety-related-profile/meetings/reqs_sub_wg/README.md b/safety-related-profile/attic/reqs_sub_wg/README.md
similarity index 99%
rename from safety-related-profile/meetings/reqs_sub_wg/README.md
rename to safety-related-profile/attic/reqs_sub_wg/README.md
index 7fcfe4d1..f63e53ce 100644
--- a/safety-related-profile/meetings/reqs_sub_wg/README.md
+++ b/safety-related-profile/attic/reqs_sub_wg/README.md
@@ -1,2 +1,2 @@
-This is the work area for the "requirement definition sub workgroup":
-- list of operators needed to cover the [Use Cases](../../documents/usecases.md), in this [Excel sheet](./ONNX_operators_for_Use_Cases.xlsx).
+This is the work area for the "requirement definition sub workgroup":
+- list of operators needed to cover the [Use Cases](../../documents/usecases.md), in this [Excel sheet](./ONNX_operators_for_Use_Cases.xlsx).
diff --git a/safety-related-profile/meetings/analysis_sub_wg/minutes.md b/safety-related-profile/attic/reqs_sub_wg/minutes.md
similarity index 100%
rename from safety-related-profile/meetings/analysis_sub_wg/minutes.md
rename to safety-related-profile/attic/reqs_sub_wg/minutes.md
diff --git a/safety-related-profile/meetings/reqs_sub_wg/slides-18-12.md b/safety-related-profile/attic/reqs_sub_wg/slides-18-12.md
similarity index 100%
rename from safety-related-profile/meetings/reqs_sub_wg/slides-18-12.md
rename to safety-related-profile/attic/reqs_sub_wg/slides-18-12.md
diff --git a/safety-related-profile/meetings/team.md b/safety-related-profile/attic/team.md
similarity index 100%
rename from safety-related-profile/meetings/team.md
rename to safety-related-profile/attic/team.md
diff --git a/safety-related-profile/deliverables/issues/issues.md b/safety-related-profile/deliverables/issues/issues.md
index 1fc8dfff..179090bd 100644
--- a/safety-related-profile/deliverables/issues/issues.md
+++ b/safety-related-profile/deliverables/issues/issues.md
@@ -5,7 +5,8 @@
- CAT: _category in {__OPERATOR__ , __GRAPH__ , __FORMAT__}_
- CRIT: _criticality in { __LOW__ , __HIGH__ }_
- REQ: _Identification of the SONNX requirement that can't be satisfied due to this issue_
-- LOC: _Location in the standard, possibly using an hyperlink__
+- LOC: _Location in the standard, possibly using an hyperlink_
+- AUT: _author_ (optional)
### Issue
_Description of the issue (in a synthetic way)_
@@ -19,38 +20,14 @@ _Proposal to solve the issue or mitigate its consequences_
### Remarks (opt)
_Additional remarks_
-# Issues - Non operators
-## Issue #1.1: Execution order of graph nodes
-- CAT: GRAPH
-- CRI: LOW
-- REQ: (TBC)
-- LOC: [Open Neural Network Exchange Intermediate Representation (ONNX IR) Specification](https://github.com/onnx/onnx/blob/main/docs/IR.md)
-
-### Issue
-The ONNX specification states that "Each computation dataflow graph is structured as a topologically sorted list of nodes that form a graph, which MUST be free of cycles. [...] ". The topological order is a partial order.
-
-### Consequence
-Different implementations may execute nodes in different orders, leading to different results when computations are done using floating poit numbers.
-
-### Proposal
-The SONNX standard should provide a means to impose a total ordering on the nodes.
-
-### Remarks
-This constraint will prevent optimisations.
-Note that nothing prevents a model to be ill-formed. Compliance with the syntax and semantics of the ONNX standard must be checked (it is certainly checked, but nothing is said about what is checked or not and whether these checkers are complete / correct or not).
-
-Other constraints are given in the [onnx-ml.proto3](https://github.com/onnx/onnx/blob/main/onnx/onnx-ml.proto3). E.g.:
-
- // One FunctionProto can reference other FunctionProto in the model, however, recursive reference
- // is not allowed.
-
-## Issue #1.2: Overloading
+## Issue #1.1: Overloading
- CAT: (to be completed)
- CRIT: (to be completed)
-- REQ: (to be completed)
-- LOC: ONNX file format definition ([onnx-ml.proto3](https://github.com/onnx/onnx/blob/main/onnx/onnx-ml.proto3))
+- REQ: (to be completed)
+- LOC: ONNX file format definition ([onnx-ml.proto3](https://github.com/onnx/onnx/blob/main/onnx/onnx-ml.proto3))
+- AUT: (to be completed)
### Issue
A [function in ONNX](https://onnx.ai/onnx/intro/concepts.html) is a way to reuse the same combination of operators at different locations in a model. A function may refer to operators that are in a different opset than the model itself. In that case, the standard leaves the runtimes the freedom to chose whether the local
@@ -165,7 +142,6 @@ IR version 10 introduces overloading, i.e. the capability to have several defini
### Issue
Variadic operators can accept any number of inputs.
-
### Consequence
(TBC)
@@ -233,158 +209,167 @@ Do we need to specify our own encoding format?
ONNX supports __Quantization__ operators. Quantization data types are not consistent accross operators.
[QuantizeLinear](https://onnx.ai/onnx/operators/onnx__QuantizeLinear.html) is able to output int16, uint16, but [QLinearMatMul](https://onnx.ai/onnx/operators/onnx__QLinearMatMul.html), [QLinearConv](https://onnx.ai/onnx/operators/onnx__QLinearConv.html), [MatMulInteger](https://onnx.ai/onnx/operators/onnx__MatMulInteger.html) and [ConvInteger](https://onnx.ai/onnx/operators/onnx__ConvInteger.html#l-onnx-doc-convinteger) do not support these types.
-# Operators
-## Issue #2.1: Incomplete specification of SPLIT operator
+## Issue #1.11: Axis attributes
-- CAT: Operator
-- CRIT: High
-- REQ: (TBC)
+- CAT: Format
+- CRI: LOW
+- REQ: (TBC)
- LOC: https://onnx.ai/onnx/operators/onnx__Split.html
-
+ https://onnx.ai/onnx/operators/onnx__Concat.html
+ https://onnx.ai/onnx/operators/onnx__Softmax.html
+ https://onnx.ai/onnx/operators/onnx__Slice.html
+
### Issue
-1. The "axis" attribute gives an integer to define the axis along which split the input tensor into a list of tensors. The onnx documentation does not specify the correspondance between the integer and the axis of the tensor. Is the following interpretation the correct representation ? :
+Several operators identified in CNNs present the « axis » attribute (ex : Split, Concat, Softmax, Slice). This attribute gives an integer to define the axis of the input tensor on which the operation applies. But the onnx documentation does not specify the correspondance between the integer and the axis of the tensor. Is the following interpretation the correct representation ?
- axis = '0' <=> batch ?
- axis = '1' <=> channels ?
- axis = '2' <=> rows ?
- axis = '3' <=> columns ?
-
-2. Moreover, the "split" input gives a tensor which indicate the length of the 'axis' specified for each output tensor. For example, if the shape of the input tensor is [1,32,320,320] (assuming 32=channels, 320=lines and 320=columns) and the "split" input is [16,16] with the "axis" attribute = 1, then the operator splits the input tensor into two output tensors [1,16,320,320] and [1,16,320,320]. In general, the next layer of the network applies its operation on one of the two output tensors and the other one is kept for use in a deeper layer of the network. But the documentation does not specify which 16 channels are used in the next layer and which 16 channels are set aside. Is it the first 16 or the last 16 channels of the input tensor ?
+If the « axis » attribute is set to ‘1’, does this mean that the channels of the input tensors are operated (concatenated or splitted or sliced for example) ?
### Consequence
-1. When implementing a neural network containing a "split" operator, the split operation on an input tensor can be performed on the wrong axis et so it would give an incorrect result.
-2. After the split of tensor's channels, the next layer of the neural network could receive the wrong feature maps and not those expected from the correct channels.
+If the operation was performed along the wrong axis of the input tensors, the values of elements in the feature maps of the output tensor could be incorrect and then the next layer of the CNN would give an incorrect result by applying its operation on the wrong data.
### Proposal
Define in the documentation a dictionary associating integers with axis of tensors.
-The onnx description should specify exactly how the axis of the input tensor is splited and indicate precisely where each of the outputs in the following layers of the network are used.
-## Issue #2.2: Incomplete specification of CONCAT operator
-
-- CAT: Operator
-- CRIT: High
-- REQ: (TBC)
-- LOC: https://onnx.ai/onnx/operators/onnx__Concat.html
+## Issue #1.12: Associate dimensions of input tensors with reshaping tensors
+- CAT: Format
+- CRI: LOW
+- REQ: (TBC)
+- LOC: https://onnx.ai/onnx/operators/onnx__Resize.html
+ https://onnx.ai/onnx/operators/onnx__Reshape.html
+ https://onnx.ai/onnx/operators/onnx__Shape.html
+
### Issue
-1. The "axis" attribute gives an integer which defines the axis along which the n input tensors should be concatenated. The onnx documentation does not specify the correspondance between the integer and the axis of the tensor. In general, the "axis" attribute of the concat operator is set to 1, but which axis corresponds to 1 ? Does this mean that we must concatenate the channels of the input tensors, if "axis=1" corresponds to the channels ?
-2. Moreover, the onnx description does not specify the order in which the input tensors are concatenated.
+This issue is linked to the previous issue (1.11). Some operators take as input a tensor defining the dimensions of the output tensor based on the input data tensor(s). But the onnx documentation does not specify exactly the correspondance between this reshaping tensor and the axis of the input data tensor to be reshaped.
+- The input named 'scales' of the RESIZE operator (in CNNs) gives a tensor which indicate the resize of each dimension of the input data tensor. Each element of 'scales' corresponds to an axis of the input data tensor. But the documentation does not specify the correspondance between the position of the element on the 'scales' tensor and the axe of the input tensor. Does the first element of the 'scales' tensor correspond to batch of the input tensor ? Does the second element of the 'scales' tensor correspond to the channels of the input tensor ? Does the third element of the 'scales' tensor correspond to the rows of the input tensor ? ...
+- The input named 'shape' of the RESHAPE operator is a shape tensor which specifies the output shape. But the documentation does not specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor. Is it the value of the third element of the 'shape' tensor that determines the size of the ouput tensor rows (in case of CNNs) ?
+- The SHAPE operator outputs an 1D tensor containing the shape of the input tensor. But the documentation does not specify the correspondance between the dimension of the input data tensor and the position of the element on the 'shape' output tensor. In case of CNNs, if the input was a tensor containing 16 channels of feature maps with size 80x80, what would the output tensor look like ? Would it be : 'shape' = [16,80,80] ? Or the sizes of each dimension of the input tensor should be written to the output tensor in another order ?
### Consequence
-If the concatenation was done along the wrong axis of the input tensors or in the wrong order depending on the different input tensors, then the next layer of the network would give an incorrect result by applying its operation on the wrong feature maps.
+- For RESIZE operator : The ouput tensor could be the incorrect shape with incorrect elements in the feature maps if the rescaling of the dimension was misunderstood. The operations of the next layers would be distorted.
+- For RESHAPE operator : The output tensor may be incorrect if the input tensor was reshaped based on the wrong axes.
+- For SHAPE operator : The output tensor could be incorrect if the implementation of the 'shape' operator did not list the dimensions of the input tensor in the correct order.
### Proposal
-Define in the documentation a dictionary associating integers with axis of tensors.
-The onnx description should specify the order in which the input tensors are concatenated.
+- For RESIZE operator : Specify the correspondance between the elements of the "scales" input and the axis of the input tensor to be resized.
+- For RESHAPE operator : Specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor.
+- For SHAPE operator : Specify in the documentation the correspondance between the dimension of the input tensor and the position of the value on the 'shape' output tensor.
-## Issue #2.3: Incomplete specification of RESIZE operator
+# Issues - Operators
+
+## Issue #2.1: Incomplete specification of SPLIT operator
- CAT: Operator
- CRIT: High
- REQ: (TBC)
-- LOC: https://onnx.ai/onnx/operators/onnx__Resize.html
+- LOC: https://onnx.ai/onnx/operators/onnx__Split.html
### Issue
-1. The "scales" input gives a tensor which indicate the resize of each dimension. Each element of 'scales' corresponds to an axe of the input tensor. The onnx documentation indicates that the "scales" tensor takes value greater than 0. If it’s less than 1, it’s sampling down, otherwise, it’s upsampling. But the documentation does not specify the correspondance between the position of the element on the 'scales' tensor and the axe of the input tensor. Does the first element of the 'scales' tensor correspond to batch of the input tensor ? Does the second element of the 'scales' tensor correspond to the channels of the input tensor ? Does the third element of the 'scales' tensor correspond to the rows of the input tensor ? ... Moreover, if it's the value '2', we understand that the dimension of the corresponding axis is upsampling but by how much ? Does it mean that it is multiplied by 2 ?
-2. The "nearest_mode" attribute indicates how to get “nearest” pixel in input tensor from x_original. There are 4 modes : "round_prefer_floor", "round_prefer_ceil", "floor", "ceil" but for no mode the documentation explains which operation applies to the tensor. What difference applies depending on the mode?
+The input named "split" gives a tensor which indicate the length of the 'axis' specified for each output tensor. For example, if the shape of the input tensor is [1,32,320,320] and the "split" input is [16,16] with the "axis" attribute = 1, then the operator splits the input tensor into two output tensors [1,16,320,320] and [1,16,320,320]. These two output tensors are each an input tensor of a following layer of the CNN. However, the onnx documentation does not specify exactly which are the 16 feature maps of the input tensor which go into the first output and which are the 16 which generate the other output.
### Consequence
-The ouput tensor could be the incorrect shape with incorrect elements in the feature maps if the rescaling of the dimension was misunderstood. The operations of the next layers would be distorted.
+The next layers of the neural network could receive the wrong feature maps and not those expected from the correct channels.
### Proposal
-1. Specify the correspondance between the elements of the "scales" input and the axis of the input tensor to be resized.
-2. Specify exactly the transformation applied to the input tensor depending on the assigned upsampling mode. Give an example of what the output tensor looks like from an input tensor for each of the modes
+The onnx description should specify exactly how the axis of the input tensor is splited by giving a correspondence between the input tensor data and the output tensors of the operator.
-
-## Issue #2.4: Incomplete specification of RESHAPE operator
+## Issue #2.2: Incomplete specification of CONCAT operator
- CAT: Operator
- CRIT: High
- REQ: (TBC)
-- LOC: https://onnx.ai/onnx/operators/onnx__Reshape.html
+- LOC: https://onnx.ai/onnx/operators/onnx__Concat.html
### Issue
-The "shape" input is a shape tensor which specifies the output shape. If one dimension of the new shape is -1, the value of the output dimension is inferred from the size of the input tensor and the remaining dimensions. Let's suppose an input tensor with size [1,c,l,w] and 'shape'=[1,c,-1], in this case where the shape of the output tensor is inferior to the shape of the input tensor, does it mean that we have to reorganize the matrix lxw of feature map into an unique row of size l*w in order to obtain an output tensor with the shape [1,c,l*w] ? And how are the rows of w columns organized on a single line ? Are they concatenated one after the other ? Is an order to be respected ? The onnx documentation does not specify exactly how the dimension '-1' transform the tensor to be reshaped. And vice versa, if shape of the output tensor > shape of the input tensor (input tensor's size = [1,c,L] and 'shape'=[1,c,l,w]), then how is the data from a row organized into matrices (L=l*w) ? The onnx documentation does not specify exactly how the data from the input tensor is reorganized. Moreover, the documentation does not specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor.
+The onnx description does not specify the order in which the input tensors are concatenated. A list of tensors is given as input for concatenation along the attributed axis but it is not specify in which order the concatenated output tensor contains the input tensors in the specified axis.
### Consequence
-1. The output tensor may be incorrect if the input tensor was reshaped based on the wrong axes.
-2. The output tensor may be incorrect if the reordering data in one dimension was done in the wrong order --> the operations of the next layers would be distorted.
+If the concatenation was done in the wrong order depending on the different input tensors, then the next layer of the network would give an incorrect result by applying its operation on the wrong feature maps.
### Proposal
-1. Specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor.
-2. Specify exactly how the dimension '-1' transform the tensor to be reshaped.
-3. Specify exactly how the data from the tensor is reorganized.
-
+The onnx description should specify the order in which the input tensors are concatenated (by giving a number to each input tensor ?).
-## Issue #2.5: Incomplete specification of SOFTMAX operator
+## Issue #2.3: Incomplete specification of RESIZE operator
- CAT: Operator
- CRIT: High
- REQ: (TBC)
-- LOC: https://onnx.ai/onnx/operators/onnx__Softmax.html
+- LOC: https://onnx.ai/onnx/operators/onnx__Resize.html
### Issue
-Softmax(input, axis) = Exp(input) / ReduceSum(Exp(input), axis=axis, keepdims=1)
-The “axis” attribute indicates the dimension along which Softmax will be performed. But the onnx documentation does not specify the correspondance between the integer given to the "axis" attribute and the axe of the input tensor. If 'axis=1', does it mean that Softmax is performed along the channels of the input tensor ?
+1. The input named "scales" gives a tensor which indicate the resize of each dimension. Each element of 'scales' corresponds to an axe of the input tensor. The onnx documentation indicates that the "scales" tensor takes value greater than 0. If it’s less than 1, it’s sampling down, otherwise, it’s upsampling. If it's the value '2', we understand that the dimension of the corresponding axis is upsampling but by how much ? Does it mean that it is multiplied by 2 ?
+2. The "nearest_mode" attribute indicates how to get “nearest” pixel in input tensor from x_original. There are 4 modes : "round_prefer_floor", "round_prefer_ceil", "floor", "ceil" but for no mode the documentation explains which operation applies to the tensor. What difference applies depending on the mode ?
### Consequence
-The values of elements in the feature maps of the output tensor could be incorrect if the softmax was performed along the wrong axe of the input tensor.
+The ouput tensor could be the incorrect shape with incorrect elements in the feature maps if the rescaling of the dimension was misunderstood. The operations of the next layers would be distorted.
### Proposal
-Specify in the documentation the correspondance between the integer given to the "axis" attribute and the axe of the input tensor.
-
+Specify exactly the transformation applied to the input tensor depending on the assigned upsampling mode. Give an example of what the output tensor looks like from an input tensor for each of the modes
-## Issue #2.6 : Incomplete specification of SHAPE operator
+## Issue #2.4: Incomplete specification of RESHAPE operator
- CAT: Operator
- CRIT: High
- REQ: (TBC)
-- LOC: https://onnx.ai/onnx/operators/onnx__Shape.html
+- LOC: https://onnx.ai/onnx/operators/onnx__Reshape.html
### Issue
-The "shape" operator outputs an 1D tensor containing the shape of the input tensor. But the onnx documentation does not specify the correspondance between the dimension of the input tensor and the position of the element on the 'shape' output tensor. If the input was a tensor containing 16 channels of feature maps with size 80x80, what would the output tensor look like ? 'shape' = [16,80,80] ? Or the sizes of each dimension of the input tensor should be written to the output tensor in another order ?
+The input named "shape" is a tensor which specifies the output shape. If one dimension of the new shape is -1, the value of the output dimension is inferred from the size of the input tensor and the remaining dimensions. Let's suppose an input tensor with size [1,c,l,w] and 'shape'=[1,c,-1], in this case where the shape of the output tensor is inferior to the shape of the input tensor, does it mean that we have to reorganize the matrix lxw of feature map into an unique row of size l*w in order to obtain an output tensor with the shape [1,c,l*w] ? And how are the rows of w columns organized on a single line ? Are they concatenated one after the other ? Is an order to be respected ? The onnx documentation does not specify exactly how the dimension '-1' transform the tensor to be reshaped. And vice versa, if shape of the output tensor > shape of the input tensor (input tensor's size = [1,c,L] and 'shape'=[1,c,l,w]), then how is the data from a row organized into matrices (L=l*w) ? The onnx documentation does not specify exactly how the data from the input tensor is reorganized.
### Consequence
-The output tensor could be incorrect if the implementation of the 'shape' operator did not list the dimensions of the input tensor in the correct order.
+The output tensor may be incorrect if the reordering data in one dimension was done in the wrong order --> the operations of the next layers would be distorted.
### Proposal
-1. Specify in the documentation the correspondance between the dimension of the input tensor and the position of the value on the 'shape' output tensor.
-2. Note in the onnx file of description of the neural network the shape of the layer input as well as the layer output.
+1. Specify exactly how the dimension '-1' transform the tensor to be reshaped.
+2. Specify exactly how the data from the input tensor is reorganized.
-
-## Issue #2.7 : Incomplete specification of SLICE operator
-
-- CAT: Operator
-- CRIT: High
-- REQ: (TBC)
-- LOC: https://onnx.ai/onnx/operators/onnx__Slice.html
+## Issue #2.5: incomplete specification of CONV operator
+- CAT: Operator
+- CRI: HIGH
+- REQ: (TBC)
+- LOC: [CONV operator](https://onnx.ai/onnx/operators/onnx__Conv.html), but this issue appear in other operators
### Issue
-The "axes" attribute gives an integer which defines the axis along which the input tensor should be sliced. The onnx documentation does not specify the correspondance between the integer and the axis of the tensor.
+- The description of the CONV operator is very abstract: "The convolution operator consumes an input tensor and a filter, and computes the output.".
+- The value of the padding is not defined (it is actually 0).
+- Presentation of attributes makes it difficult to check if all dependencies are expressed.
### Consequence
-When implementing a neural network containing a "slice" operator, the slice operation on an input tensor can be performed on the wrong axis et so it would give an incorrect result.
+- Implementer needs to check the referece implementation (or other doc.) to understand what needs to be implemented. Different implementations may lead to different results.
### Proposal
-Define in the documentation a dictionary associating integers with axis of tensors.
-
+See [example](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/documents/conv_specification_example)
-## Issue #2.8: incomplete specification of CONV operator
+## Issue #2.6: Batches
- CAT: Operator
- CRI: HIGH
- REQ: (TBC)
-- LOC: [CONV operator](https://onnx.ai/onnx/operators/onnx__Conv.html), but this issue appear in other operators
+- LOC: [CONV operator](https://onnx.ai/onnx/operators/onnx__MatMul.html),
+- AUT: Nicolas
+
+In the ONNX tensor semantics, there is no notion of a batch dimension.
-### Issue
-The description of the CONV operator is very abstract: "The convolution operator consumes an input tensor and a filter, and computes the output.".
+In principle, inconsistencies are detected at runtime through shape inference when convolutions are involved, because it is convolutions that introduce batch semantics, which then propagate to the preceding/following operators.
-The value of the padding is not defined (it is actually 0).
+However, if we consider a network without convolutions, there is no longer any batch semantics.
-Presentation of attributes makes it difficult to check if all dependencies are expressed.
+So, if we take a MatMul that receives an input tensor with T.shape = [2,3], it's unclear whether this is a 1D batch of size 2, or a 2D tensor with no batch dimension.
-### Consequence
-Implementer needs to check the referece implementation (or other doc.) to understand what needs to be implemented. Different implementation may lead to different results.
+The ONNX specification for MatMul refers to NumPy:
+> If both arguments are 2-D they are multiplied like conventional matrices.
+> If either argument is N-D, N > 2, it is treated as a stack of matrices residing in the last two indexes and broadcast accordingly.
-### Proposal
-See [example](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/documents/conv_specification_example)
+Some frameworks (such as https://github.com/Verified-Intelligence/auto_LiRPA) assume that the first dimension is always the batch, but without stating it explicitly — which can lead to hours of debugging to figure out. In this case, in my opinion, it is a misinterpretation (bug) in the framework.
+
+Since we more or less agreed to be as explicit as possible in SONNX,
+I suggest that for MatMul and Gemm we explain the following:
+- [3] is a 1D tensor of dimension 3
+- [1,3] is a 2D tensor of dimensions 1 and 3
+- [1,1,3] is a batch of 1 tensor, each of shape [1,3]
+=> There is no way to specify a 1D tensor with batch size > 1
+Workaround: [2,1,3] is a batch of 2 tensors, each of shape [1,3]
diff --git a/safety-related-profile/deliverables/reqs/reqs.md b/safety-related-profile/deliverables/reqs/reqs.md
index 200209a9..cf20ade9 100644
--- a/safety-related-profile/deliverables/reqs/reqs.md
+++ b/safety-related-profile/deliverables/reqs/reqs.md
@@ -3,462 +3,472 @@
This document captures the requirements applicable to the SONNX profile.
The specification is organized as follows:
-
- General requirements
- Requirements about the operators
- Requirements about the graph
- Requirement about the serialization format
-# Operators
-
+## Lexicon
+- **inference**:The phase where the model applies what it has learned to make predictions on new, unseen data, without updating its parameters.
+- **training**: The phase where the model learns from data by adjusting its internal parameters to minimize error.
+
## General requirements
-### REQ `OP 000`: `Compliance with the ONNX standard`
-
-#### Description
-The SONNX standard shall not modify the structure (inputs, outputs, attributes) of the ONNX operators ). However, it is allowed to restrict the input and parameter domains if deemed necessary.
-
-#### Rationale
-Compatibility with the ONNX standard.
-
-#### Related need
-[TBC]
-
-### REQ `OP 001`: `Operators set`
+### Documentation
-#### Description
-The SONNX profile shall include the following operators:
-> [TBC]: list to be established from the [list of use cases](./usecases.md). See also [Excel file](../meetings/reqs_sub_wg/ONNX_operators_for_Use_Cases.xlsx)
-
-#### Rationale
-The set of operators included in the SONNX profile shall allow the implementation of simple industral use cases by the end of 2025.
+The documentation of the SONNX profile is composed of two parts:
+- a documentary part, used to document the operator semantics
+- a formal part, used to specify the exact behaviour of the operator.
-#### Related need
-[TBC]
+The documentary part shall not be considered to be a formal specification. For an exact and precise specification of an operator, the user shall refer to the formal specification.
-### REQ `OP 002`: `I/O specification`
+### REQ-DO-010: Documentation structure
#### Description
-For each operator in the SONNX operator set, the SONNX profile shall completely and precisely determine the expected values of the operator's outputs for any valid value of its inputs and attributes.\
-A *valid* input (resp. attribute) is an input (resp. attribute) whose value comply with all preconditions (see [REQ XXX](#XXXX)).\
-Depending on the operator, the expected value may be unique or not.\
-In the latter case, the expected value may be a range of values, or a set of values satisfying some properties (post-condition).
-
-The expected value will preferably be defined by a mathematical relation between inputs, outputs, and attributes.
-Usual mathematical notations (i.e., notations that are commonly used by engineers) must be used preferably.
+The SONNX profile shall describe each operator according to the following structure:
+- a summary of the restrictions applicable to the operator
+- the signature of the operator
+- the inputs, attributes and output of the operator
+- a documentation about the semantics of the operator, which may include illustrations, code samples, etc.
+- a formal specification of the operator semantics written in Why3
#### Rationale
-There shall be no room for interpretation or non determinism.
+Homogeneity, readability.
#### Related need
[TBC]
-
-### REQ `OP 003`: `Accuracy`
+### REQ-DO-020: Notation consistency
#### Description
-The specification shall provide
-- the maximal acceptable error (upper bound) on the outputs
-- or the inputs leading to the maximal error (the measurement of errors being left to the implementer).
-
-The error may be expressed for each scalar or, on the matrice, vector, or tensor. In any case, the error metric must be clearly defined.
+The SONNX profile shall use consistent notations for the description of all operators.
#### Rationale
-If we say nothing about the accuracy of an operator, it basically mean that any output is correct.
+Homogeneity, readability.
+
+#### Notes
+SONNX may provide a set of standard notations to be used in the documentation.
-Note: this can be considered as a specific case of requirement [req-op-002](#req-op-002-io-specification) about the operator's I/O relation.
+For instance, the following conventions may be applied:
+- Tensors are represented in uppercase (e.g., `X`,`B`,...)
+- Attributes are represented in lowercase (`auto_pad`, `group`,...)
+- The number of lines and columns of a 2-dimension tensor `T` are respectively denoted by $nl(T)$ and $nc(T)$
+- The number of channels of a tensor `T` is denoted by $nch(T)$.
#### Related need
[TBC]
-## Informal specification
-
-### REQ `OP 010`: `Informal specification - structure`
+### REQ-DO-030: Naming consistency
#### Description
-The SONNX profile shall describe each operator according to the same structure including:
-- The operator's inputs and outputs
-- The operator's attributes
-- The semantics of the operator using a mathematical notation possibly illustrated by figures when applicable.
-- Any intermediate variable introduced for the sake of the specification must be defined explicitly.
-
-The description shall follow the example given for the `conv` operator.
+The SONNX profile shall use consistent terms for the description of all operators.
#### Rationale
-Homogeneity, readability.
+Consistency, readability.
+
+#### Notes
+SONNX may provide a set of standard terms to be used in the documentation.
+
+For instance:
+- "kernel" (not "filter"), as in "convolution *kernel*"
+- "spatial dimensions" (not "spatial *axes*")
+- etc.
#### Related need
[TBC]
-### REQ `OP 011`: `Consistency of notations`
+### RECO-DO-001: Simplicity
#### Description
-The SONNX profile shall use consistent notations for the description of all operators.
-
-The following conventions apply:
-- Tensors are represented in uppercase (e.g., `X`,`B`,...)
-- Attributes are represented in lowercase (`auto_pad`, `group`,...)
-- The number of lines and columns of a 2-dimension tensor `T` are respectively denoted by $nl(T)$ and $nc(T)$
-- The number of channels of a tensor `T` is denoted by $nch(T)$.
+In the documentation part, the SONNX profile should describe the operator semantics in the simplest and most intuitive way.\
+It shall be as close as possible to the standard mathematical description of the operator, without optimization (those are left to the implementer).
#### Rationale
-Homogeneity, readability.
+The documentation shall be easy to understand and shall facilitate validation and verification activities.
#### Related need
[TBC]
-### REQ `OP 012`: `Consistency of naming`
+### REQ-DO-050: No implementation prescription
#### Description
-The SONNX profile shall use consistent terms for the description of all operators:
-- "kernel" (not "filter"), as in "convolution *kernel*"
-- "spatial dimensions" (not "spatial *axes*")
-
-*This list is to be completed.*
+The SONNX profile shall not mandate specific implementation solutions. However, if a particular implementation is to be preferred in practice, the specification shall define the properties that such an implementation satisfies.
#### Rationale
-Consistency, radability.
+SONNX is a specification and shall not prescribe implementation. Should the model designer need to impose a specific implementation solution, this information will be expressed using specific meta-data (see [derived requirements](#derived_reqs)).
+
+#### Notes
+The relation between inputs and outputs may also be expressed by algorithm describing
+how inputs are processed to generate produce the outputs according to the attributes.
+In that case, the algorithm shall not be considered as a requirement on the implementation,
+but only as one possible way to compute the expected result.
#### Related need
[TBC]
-### REQ `OP 013`: `Simplicity`
+### RECO-DO-001: Conditions stated once
#### Description
-The SONNX profile shall describe the operator semantics in the simplest and most intuitive way.\
-In an algorithmic description is used, it shall be as straightforward as possibe with no
-fancy optimization (that are left to the implementer).
+The SONNX profile should ensure that, when a condition involves several inputs, outputs or attributes, it is only expressed once in the section dedicated to one of the inputs, outputs or attributes. Should the condition involve multiple inputs, outputs or attributes, references (hyperlinks) to the unique condition shall be used in all other sections.
-#### Rationale
-The specification shall be easy to understand and shall facilitate validation and verification activities.
+#### Rationale
+For instance, if two inputs `A` and `B` are constrained by some relation C, this relation must appear in the section concerning `A` *and* in the section concerning `B`. Duplicating the constraint raises a risk of inconsistency.
#### Related need
[TBC]
-### REQ `OP 014`: `No implementation prescription`
+## Formal specification
+
+### REQ-FS-000: Formal specification
#### Description
-The SONNX profile shall not prescribe implementation solutions.\
-The relation between inputs and outputs may also be expressed by algorithm describing
-how inputs are processed to generate produce the outputs according to the attributes.
-In that case, the algorithm shall not be considered as a requirement on the implementation,
-but only as one possible way to compute the expected result.
+The SONNX profile shall provide a formal specification of each operator.
-#### Rationale
-SONX is a specification and shall not prescribe implementation. Should the model designer need to
-impose a specific implementation solution, this information shall be expressed in a specific meta-data (see [derived requirements](#derived_reqs)).
+#### Rationale
+The formal specification uses a formal language with a well-defined and sound semantics that shows none of the potential ambiguities of the informal specification operators used in the documentary part. In particular, the formal specification can be used to prove the correctness of an implementation or be used as a test oracle.
#### Related need
[TBC]
-### REQ `OP 015`: `Restrictions wrt ONNX`
+### REQ-FS-010: Formal specification traceability
#### Description
-The SONNX profile shall clearly indicate when a condition on the inputs, outputs, and attributes is a restriction
-with respect to the ONNX standard.
+The formal specification must be traceable to the documentation, i.e., the elements of the formal specification shall refer to the sections and or paragraphs of the documentary part.
-In that case, the condition must be maked with tag `[restrict]`.
-
-#### Rationale
-Clarity.
+#### Rationale
+The formal specification of an operator perfectly defines it, but the semantics so expressed may not reflect the operator designer intent. Having an explicit link between the formal specification and its informal counterpart is one way (i) to prevent errors (both specifications are redundant to some extent) and (ii) to enforce the formal specification to remain "simple".
#### Related need
[TBC]
-### REQ `OP 016`: `No default values`
+### REQ-FO-020: Operator versions
#### Description
-The SONNX profile shall forbid the use of defaut values.
+The model shall indicate precisely the version of each operator used in the model.
#### Rationale
-The ONNX standard defines default value for attributes that are left without values.
-
-The objective is to ensure that the model designer and model implementer has a clear knowledge of the values involved in computations.
+Operators may have several versions (opset) and, during graph execution, the shall be no ambiguity about the version to be used.
#### Related need
-[TBC]
+[need-thav-001](needs.md#need-thav-001-version-index-in-the-onnx-model)
-### REQ `OP 017`: `Datatypes`
+### REQ-FO-030: Representation of parameters
#### Description
-The SONNX standard shall
-- specify operators for values in the domain of real numbers ($R$), systematically
-- specify operators for all domain necesary to support the industrrial use cases (e.g, `float32`and `int32`).
-
-For instance, the `conv` operator shall be specified for values in $R$, `float32` and `int32` datatypes.
+The model shall shall specify all parameters down to the least significant bit and in a non-ambiguous way. For instance, the IEEE hexadecimal binary representation may be used to represent floating point parameters ([-]0x1.abcdefp[+-]n) .
#### Rationale
-The semantics of the operator may depend on the types (float, integers), accurracy (float32, float64), range (int16, int32)of numbers.
+The serialization of floating point number must not degrade the accuracy of the source model parameters.
#### Related need
[TBC]
-### REQ `OP 018`: `Input domain definition`
+### REQ-FO-040: Documentation of input and output tensors
#### Description
-The SONNX profile shall specify all conditions on inputs, outputs, and attributes that must be satisfied for the operator to be applicable. The behaviour of the operator for any value out of the valid input domain must be described.
+The SONNX file format shall have the capability to describe
+- the semantics of the input and output tensors,
+- the semantics of the dimensions of the tensors.
#### Rationale
-The semantics of operators is only defined in the inputs, outputs, and attribute validity domain.
+[TBC]
#### Related need
-[TBC]
+[need-arcys-001](needs.md#need-arcys-001-final-prediction)
-### REQ `OP 019`: `Behaviour in case of errors`
+### REQ-FO-050: Derived requirements - implementation
#### Description
-The SONNX profile shall specify the expected behaviour of an operator should
-- the input values be out of the valid input domain
-- over/under flow occur
+The SONNX profile shall have the capability to attach meta-data on model elements. Those meta-data may be used to describe e.g.,
+- the exact order in which the graph operators must be executed
+- the target hardware on which the model or part of the model must be deployed
+- the semantics of tensors
+- etc.
#### Rationale
[TBC]
#### Related need
-[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
+[need-ai-003](needs.md#need-ai-003-expression-of-implementation-requirements)
+
-### REQ `OP 020`: `Overflow and underflow`
+# Operators
+
+## Operator set
+
+### REQ-OP-010: Deterministic operators
#### Description
-The SONNX profile shall specify the conditions leading to overflows and underflows.
+The profile shall only include functional operators, or restriction shall be expressed to ensure that the operator will behave as a function during inference.
+
+A functional operator is an operator that maps one input value to one and only one output value. For instance, the ONNX ``dropout`` operator with ``training_mode`` set to ``true`` performs a random dropout that is not functional. A functional behaviour may be achieved by restricting the use of the operator, for instance, by enforcing input ``training_mode`` to be set to false. Operator ``RandomUniform`` is another example: if the seed input is set to some fixed value, the operator becomes functional. Checking that the input value may be done by a dedicated tool.
#### Rationale
-[TBC]
+A graph must be deterministic.
#### Related need
[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
+### REQ OP 000: Operator set
+
+#### Description
+The SONNX profile shall include at least the following operators:
+
+
+| Operator |
+|------------------------------|
+| Abs |
+| Add |
+| Cast |
+| Clip |
+| Concat |
+| Constant |
+| ConstantOfShape |
+| Conv |
+| ConvTranspose |
+| Dense |
+| Div |
+| Equal |
+| Erf |
+| Exp |
+| Expand |
+| Flatten |
+| FullyConnected |
+| Gather |
+| Gemm |
+| GlobalAveragePool |
+| GRU |
+| HardSwish |
+| Identity |
+| LeakyRelu |
+| Less |
+| Log |
+| LSTM |
+| MatMul |
+| Max |
+| MaxPool |
+| Min |
+| Mod |
+| Mul |
+| Neg |
+| Not |
+| Pad |
+| Padding |
+| Pow |
+| Range |
+| ReduceMean |
+| ReduceSum |
+| Relu |
+| Reshape |
+| Resize |
+| ScatterND |
+| Shape |
+| Sigmoid |
+| Slice |
+| Softmax |
+| SoftPlus |
+| Split |
+| Sqrt |
+| Squeeze |
+| Sub |
+| Tanh |
+| Transpose |
+| ConvTransposeDeconvolution |
+| Unsqueeze |
+| Where |
-### REQ `OP 021`: `Unique conditions`
-#### Description
-The SONNX profile shall ensure that, when a condition involves several inputs, outputs or attributes,
-the condition is only expressed once in the section dedicate to one of the inputs, outputs or attributes.
-Should the condition involve multiple inputs, outputs or attributes, references to the unique condition
-shall be ued in all other sections.
+#### Rationale
+The SONNX profile does not cover the complete set of ONNX operators. It is limited to operators (i) used during inference, (ii) that do not undermine determinism and predictability, (iii) used in a first set of industrial [use cases](../../scope/scope.md). This set may be later extended depending on the needs.
-#### Rationale
-Prevention of inconsistencies.
#### Related need
[TBC]
-### REQ `OP 022`: `Stability of operators`
+### REQ-OP-020: Inference operators
#### Description
-The SONNX profile shall preclude the use of instable operators or document this phenomenon if it occurs.
+The profile shall only include operators useful during inference. For instance, `SoftmaxCrossEntropyLoss`, `NegativeLogLikelihoodLoss`or gradient operators such as `AddGrad` are forbidden.
-#### Rationale
-[TBC]
+#### Rationale
+Operators only used during training will not contribute to the computation of the graph output and represent "dead operators".
#### Related need
-[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
-
-
-### REQ `OP 023`: `Determinism of resource usage`
-
-#### Description
-The SONNX profile shall preclude the use operators which memory usage varies dynamically (i.e, depends on the inputs).
-
-#### Rationale
[TBC]
#### Related need
[need-ai-009](needs.md#need-ai-009-resource-usage-determinism-and-predictability)
-### REQ `OP 024`: `Determinism of execution times`
+### REQ-OP-030: Compliance with the ONNX standard
#### Description
-The SONNX profile shall preclude the use operators which memory usage varies dynamically (i.e, depends on the inputs).
-
-#### Rationale
-[TBC]
-
-#### Related need
-[need-ai-010](needs.md#need-ai-010-execution-time-determinism-and-predictability)
-
-
-## Formal specification
+The SONNX version of an operator `op` shall have the same inputs, outputs, and attributes than the ONNX `op` operator.
-### REQ `OP 030`: `Formal specification in ACSL`
+However, it is allowed to restrict the inputs and parameters value domains if deemed necessary.
-#### Description
-The SONNX profile shall provide an ACSL specification for each operator.
-#### Rationale
-The formal specification will be used to verify the correctness of the reference implementation.
+#### Rationale
+Compatibility with the ONNX standard.
#### Related need
[TBC]
-### REQ `OP 031`: `Generic properties`
+### REQ-OP-060: Restrictions wrt ONNX
#### Description
-"As far as possible", the formal specification must also include high-level mathematical properties that the operator must satify (e.g., symmetry, reflexivity, existence of a neutral element, etc.). For instance, `sub(T,T)=0`, `sub(T,0)=T`,`transpose(tranpose(T))=T`...
-
- Whenever possible, the formal specification may rely on other operator, e.g., `sub(add(T1,T2), T2)=T1`, . This schema may be applied on more complex operators such as convolution. For instance: `ConvTranspose(Conv(X))=X and `Conv(Conv`Transpose(X))=X" for specific values of attributes.
+The SONNX profile shall clearly indicate when a condition on the inputs, outputs, and attributes is a restriction with respect to the ONNX standard.
-See example of the `conv` operator.
+In that case, the condition must be marked with tag `[restrict]`.
-#### Rationale
-If an operator is described "algorithmically", it will be very close to the actual (e.g.) C implementation. Additional properties may be useful to detect errors in the formal specification.
+#### Rationale
+Clarity.
#### Related need
[TBC]
-## Profile contents
-### REQ `OP 032`: `Deterministic operators`
+## Operator specification
+
+### REQ-OP-030: Input domain specification
#### Description
-The profile shall only contain deterministic operators. The profile shall forbib any operator whose behaviour is intrinsically non-deterministic or for which some inputs determining the behaviour of the operators would not be identified (e.g., an operator that would use a random generator with no control on the random generator seed).
+For each operator in the SONNX operator set, the SONNX profile shall specify the validity domain of inputs and attributes.
#### Rationale
-[TBC]
-
+There shall be no room for interpretation or non determinism.
#### Related need
[TBC]
+### REQ-OP-030: Operator specification
-# Requirements on graph interpretation
+#### Description
+For each operator in the SONNX operator set, the SONNX profile shall specify the expected output values for any input values and attributes in their validity domain.
-### REQ `GR 000`: `Graph interpretation`
+The SONNX standard shall specify operators for values in the domain of real numbers ($R$) and in all domains necessary to support the industrial use cases (e.g, `float32`and `int32`).
-#### Description
-The profile shall specify how graphs are interpreted.
+For instance, the `conv` operator shall be specified for values in $R$, `float32` and `int32` datatypes.
#### Rationale
+The semantics of the operator may depend on the types (float, integers), accuracy (float32, float64) and range (int16, int32) of numbers.
#### Related need
[TBC]
-# Requirements on file format
-
-## General requirements
-
-### REQ `FO 000`: `Operator versions``
-
-#### Description
-The model shall indicate precisely the version of each operator used in the model. There shal be no ambiguity about the version to be used.
#### Rationale
-Operators may have several versions (opset)
+There shall be no room for interpretation or non determinism.
#### Related need
-[need-thav-001](needs.md#need-thav-001-version-index-in-the-onnx-model)
+[TBC]
-### REQ `FO 001`: `Representation of parameters`
+### REQ-OP-040: Out of domain errors
#### Description
-The representation of parameters (weights, biases) in the serialized representation of the model shall not degrade the accuracy of parameters. For instance, the IEEE hexadecimal binary representation ([-]0x1.abcdefp[+-]n) may be used to represent floating point parameters.
+The SONNX profile shall specify the behaviour of an operator should some input value be out of its validity domain.
#### Rationale
-The serialization of floating point number must not introduce degrade the accuracy of the source model parameters.
+Out of domain conditions may not be prevented by design, checked statically or detected at runtime.
+For attributes, a static verification of the model is expected to be carried out in order to prevent such error conditions.
#### Related need
-[TBC]
-
-## User documentation
+[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
-### REQ `FO 010`: `Documentation of input and output tensors`
+### REQ-OP-050: Overflow and underflow conditions
#### Description
-The SONNX profile must provide the capability to give the semantics of the input and ouptut tensors, including the semantics of the dimensions of the tensors.
+The SONNX profile shall specify the conditions leading to overflows, underflows, wrap-around.
#### Rationale
[TBC]
#### Related need
-[need-arcys-001](needs.md#need-arcys-001-final-prediction)
+[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
-### REQ `FO 011`: `Derived requirements - implementatiobn`
-#### Description
-The SONNX profile shall provide capability (metadata) to describe how the model must be deployed on a specific target, i.e.,
-- the exact order in which the graph operators must be executed
-- the target hardware on which the model or part of the model must be deployed.
+### REQ-OP-070: No default value
-> To be discussed. Shall it be part of the MLMD or be part of another document?
-> What kind of derived requirements do we want to express?
+ #### Description
+The SONNX profile shall forbid the use of default values.
#### Rationale
-[TBC]
+The ONNX standard defines default value for attributes that are left without values. The objective is to ensure that the model designer and model implementer has a clear knowledge of the values involved in computations.
#### Related need
-[need-ai-003](needs.md#need-ai-003-expression-of-implementation-requirements)
+[TBC]
+
+# Requirements on graph
-### REQ `FO 012`: `Traceability to training model`
+### REQ-GR-000: Graph specification
#### Description
-The SONNX profile must provide the capability to trace the ONNX model to the training model from which it has been generated.
+The profile shall specify the graph execution semantics.
#### Rationale
-[TBC]
+A model is a graph of operators. The semantics of the graph defines how operators are applied to generate the graph outputs out of its inputs.
#### Related need
-[need-ai-004](needs.md#need-ai-004-support-for-traceability)
-
+[TBC]
-### REQ `FO 013`: `Traceability to training environment`
+### REQ-GR-000: Explicit types and shapes
#### Description
-The SONNX profile must provide the capability to trace the environment used for training.
+All numerical types must be indicated explicitly (no type inference).\
+All shape conversion must be done explicitly (using the `reshape`) operator. (no [shape inference](https://onnx.ai/onnx/api/shape_inference.html))
#### Rationale
+[TBC]
#### Related need
-[need-ai-004](needs.md#need-ai-004-support-for-traceability)
-
+[need-thav-002](needs.md#need-thav-002-typing-of-data-handled-by-implementation-operators)
-# Model validity
+# Requirements on file format
-### REQ `VA 000`: `Valid operator set`
+### REQ-FO-000: Compatibility with ONNX
#### Description
-A model shall only use operators in the SONNX set.
+Any model compliant with the SONNX file format shall be a correct ONNX model.
#### Rationale
-[TBC]
+Compliance with ONNX.
#### Related need
[TBC]
-### REQ `VA 001`: `Explicit types and shapes`
+### REQ-FO-010: Format completeness
#### Description
-All datatypes must be indicated explicity (no type inference).\
-All shape conversion must be peformed explicity (using the `reshape`) operator. (no [shape inference](https://onnx.ai/onnx/api/shape_inference.html))
+The SONNX description of a model shall fully specify the function computed by the model.
#### Rationale
-[TBC]
+A SONNX model shall leave no room to interpretation.
#### Related need
-[need-thav-002](needs.md#need-thav-002-typing-of-data-handled-by-implementation-operators)
-
+[TBC]
# Reference implementation
-### REQ `RI 000`: `Reference implementation`
+### REQ-RI-000: Reference implementation
#### Description
The SONNX profile shall provide a reference implementation covering
-- the SONNX operator set
+- the operator set
- the graph execution
-- the graph import (deszerialization of a SONNX model)
+- the graph import (deserialization of a SONNX model)
#### Rationale
-In order to verify the correctness of his/her implementation, the user will need to compare the results computed by this implementation with some reference. Since we nned to compare computation **results**, this means that the specification must be executable. One possible way could be to use an executable formal specification (e.g., `why3`). Another way consists to provide (i) a formal specification and (ii) a "reference" implementation demonstrated to comply with the formal specification.
+In order to verify the correctness of his/her implementation, the user will need to compare the results computed by this implementation with some reference. Since we need to compare computation **results**, this means that the specification must be executable. One possible way could be to use an executable formal specification (e.g., `why3`). Another way consists to provide (i) a formal specification and (ii) a "reference" implementation demonstrated to comply with the formal specification.
#### Related need
[need-ai-011](needs.md#need-ai-011-support-for-verification-activities)
-### REQ `RI 001`: `Relation with specification`
+### RECO-RI-010: Relation with specification
#### Description
-The relation between the specification and the implementation must be as straightforward as possible. In particular, the reference implementation must reproduce the structure of the mathematical specification, without introducing implementation optimizations. See example of the `CONV2D`` operator.
+The reference implementation satisfies all the properties of the SONNX specification, i.e. it is compliant to SONNX, and it is written in the most plain and clear style possible (no optimisations).
+
#### Rationale
Verifying the reference implementation must be as easy as possible.
@@ -468,18 +478,85 @@ Verifying the reference implementation must be as easy as possible.
# Tooling
-### REQ `TO 000`: `Verification tool`
+### REQ-TO-000: Verification tool
#### Description
-The SONNX profile shall come with a model verification tool.\
+The SONNX profile shall provide a model verification tool.\
This tool aims at verifying that all validity conditions are satisfied, including:
-- the graph structure is well-formed,
-- all required metadata are present
-- operators are allowed
-- all conditions involving operators inputs, outputs and parameters are satisfied
+- The model complies with the serialization format
+- the graph described by the model is acyclic,
+- the required metadata are present
+- all operators used in the model belong to the SONNX profile
+- conditions that can be checked statically are satisfied
+- etc.
+
+#### Rationale
+Ensure compliance of the of a model to the SONNX profile.
+
+#### Related need
+[need-ai-007](needs.md#need-ai-007-support-for-model-verification)
+
+
+# TO BE DISCUSSED
+
+
+### REQ-OP-022: Stability of operators
+
+#### Description
+The SONNX profile shall not include numerically unstable operators. If such an unstable operator is required, the instability phenomenon shall be fully characterized
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
+
+
+# TO BE SUPPRESSED
+
+### REQ-OP-023: Determinism of resource usage
+
+#### Description
+The SONNX profile shall only include operators which memory usage does not depend on input values.
+
+> To be suppressed: in most cases, this requirement concerns the implementation of the operator, not the operator itself. There might be some exceptions (operators whose semantics requires some kind of dynamic memory allocation). Check if such operators exist before adding this req. because it might lead to endless discussions...
+
+#### Rationale
+[TBC]
+
+### REQ-OP-030: Determinism of execution times
+
+#### Description
+The SONNX profile shall not include operators which execution time depend on input values.
#### Rationale
[TBC]
#### Related need
-[need-ai-007](needs.md#need-ai-007-support-for-model-verification)
\ No newline at end of file
+[need-ai-010](needs.md#need-ai-010-execution-time-determinism-and-predictability)
+
+### REQ-FO-012: Traceability to training model
+
+[TBDis]
+
+#### Description
+The SONNX profile must provide the capability to trace the ONNX model to the training model from which it has been generated.
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-004](needs.md#need-ai-004-support-for-traceability)
+
+### REQ-FO-013: Traceability to training environment
+
+[TBDis]
+
+#### Description
+The SONNX profile must provide the capability to trace the environment used for training.
+
+#### Rationale
+
+#### Related need
+[need-ai-004](needs.md#need-ai-004-support-for-traceability)
+
diff --git a/safety-related-profile/deliverables/reqs/reviews/edoardo.md b/safety-related-profile/deliverables/reqs/reviews/edoardo.md
new file mode 100644
index 00000000..e78f9025
--- /dev/null
+++ b/safety-related-profile/deliverables/reqs/reviews/edoardo.md
@@ -0,0 +1,644 @@
+# Introduction
+
+This document captures the requirements applicable to the SONNX profile.
+
+The specification is organized as follows:
+
+- General requirements
+- Requirements about the operators
+- Requirements about the graph
+- Requirement about the serialization format
+
+> **_NOTE:_** General requirements are presented later in the document. Move them here at the beginning. Perhaps create also a list by collating general operator, graph and serialization-specific requirements?
+> OK: moved.
+
+## General requirements
+
+> **_NOTE:_** Move the general requirements section to the top. It will act as a good introduction to the requirements. \
+> OK: Done
+
+### Documentation
+
+The documentation of the SONNX profile is composed of two parts:
+- a documentary part, used to document the operator semantics
+- a formal part, used to specify the exact behaviour of the operator.
+
+The documentary part shall not be considered to be a formal specification. For an exact and precise specification of an operator, the user shall refer to the formal specification.
+
+> **_NOTE:_** This section is a bit "meta". Shall we move it to the top of the document? It would work great under "general requirements".\
+> OK: moved
+
+### REQ-DO-010: Documentation structure
+
+#### Description
+The SONNX profile shall describe each operator according to the following structure:
+- a summary of the restrictions applicable to the operator
+- the signature of the operator
+- the inputs, attributes and output of the operator
+- a documentation about the semantics of the operator, which may include illustrations, code samples, etc.
+- a formal specification of the operator semantics written in Why3
+
+#### Rationale
+Homogeneity, readability.
+
+#### Related need
+[TBC]
+
+### REQ-DO-020: Notation consistency
+
+#### Description
+The SONNX profile shall use consistent notations for the description of all operators.
+
+#### Rationale
+Homogeneity, readability.
+
+#### Notes
+SONNX may provide a set of standard notations to be used in the documentation.
+
+For instance, the following conventions may be applied:
+- Tensors are represented in uppercase (e.g., `X`,`B`,...)
+- Attributes are represented in lowercase (`auto_pad`, `group`,...)
+- The number of lines and columns of a 2-dimension tensor `T` are respectively denoted by $nl(T)$ and $nc(T)$
+- The number of channels of a tensor `T` is denoted by $nch(T)$.
+
+#### Related need
+[TBC]
+
+### REQ-DO-030: Naming consistency
+
+#### Description
+The SONNX profile shall use consistent terms for the description of all operators.
+
+#### Rationale
+Consistency, readability.
+
+#### Notes
+SONNX may provide a set of standard terms to be used in the documentation.
+
+For instance:
+- "kernel" (not "filter"), as in "convolution *kernel*"
+- "spatial dimensions" (not "spatial *axes*")
+- etc.
+
+#### Related need
+[TBC]
+
+### REQ-DO-040: Simplicity
+
+#### Description
+In the documentation part, the SONNX profile shall describe the operator semantics in the simplest and most intuitive way.\
+It shall be as close as possible to the standard mathematical description of the operator, without optimization (those are left to the implementer).
+
+> **_NOTE:_** This is very important, yet very difficult to define. I can already see some future discussion about what "simplicity" means... Ideas to improve: add a reference style such as "understandable by anyone with a STEM undergraduate/anyone who can implement a neural network in PyTorch", "in a similar style of the MatLab/TensorFlow documentation", etc.\
+> ??: I fully agree with you. This is not really a "requirement", but we don need to say something about it. I don't really like the reference to "STEM undergraduate or someone who can implement...". Another way can be to define a set of basic constructs / math operations and require all specs to be build using these elements. This would not prevent specifications to be written in a convoluted way (for instance, an optimized way)... Rather than a req, this could simply be a recommendation. It will be up to the reviewer of the operator spec to determine whether or not it must be rewritten or not...
+> Final: Change this "req" a "reco"
+
+#### Rationale
+The documentation shall be easy to understand and shall facilitate validation and verification activities.
+
+#### Related need
+[TBC]
+
+### REQ-DO-050: No implementation prescription
+
+#### Description
+The SONNX profile shall not mandate specific implementation solutions. However, if a particular implementation is to be preferred in practice, the specification shall define the properties that such an implementation satisfies.
+
+#### Rationale
+SONNX is a specification and shall not prescribe implementation. Should the model designer need to impose a specific implementation solution, this information will be expressed using specific meta-data (see [derived requirements](#derived_reqs)).
+
+#### Notes
+The relation between inputs and outputs may also be expressed by algorithm describing
+how inputs are processed to generate produce the outputs according to the attributes.
+In that case, the algorithm shall not be considered as a requirement on the implementation,
+but only as one possible way to compute the expected result.
+
+#### Related need
+[TBC]
+
+
+### REQ-DO-060: Conditions stated once
+
+#### Description
+The SONNX profile shall ensure that, when a condition involves several inputs, outputs or attributes, it is only expressed once in the section dedicated to one of the inputs, outputs or attributes. Should the condition involve multiple inputs, outputs or attributes, references (hyperlinks) to the unique condition shall be used in all other sections.
+
+For instance, if the values of two scalar inputs `A` and `B` are such that `A` > `B` , there shall be one constraint C applicable to `A` stating that `A` > `B`, and one constraint applicable to `B` referencing constraint C.
+
+
+> **_NOTE:_** I can guess what this mean (and I agree with it), but an example would help clarify.\
+> OK: Done
+> => change the example in a "negative" way.
+
+#### Rationale
+Prevention of inconsistencies.
+
+#### Related need
+[TBC]
+
+## Formal specification
+
+### REQ-FS-000: Formal specification
+
+#### Description
+The SONNX profile shall provide a formal specification of each operator.
+
+
+#### Rationale
+The formal specification uses a formal language with a well-defined and sound semantics that shows none of the potential ambiguities of the informal specification operators used in the documentary part. In particular, the formal specification can be used to prove the correctness of an implementation or be used as a test oracle.
+
+> **_NOTE:_** Potential inconsistency with the requirement that we do not enforce a specific implementation. Maybe clarify that we only enforce a number of _properties_ of the implementation (invariants?), which are listed in the formal specification.\
+> OK: I have reworded the rationale. I see no reference to the implementation...
+> => OK
+
+#### Related need
+[TBC]
+
+### REQ-FS-010: Formal specification traceability
+
+#### Description
+The formal specification must be traceable to the documentation, i.e., the elements of the formal specification shall refer to the sections and or paragraphs of the documentary part.
+
+
+> **_NOTE:_** Define exact meaning of "traceability"\
+> > OK: defined.
+> => OK
+
+#### Rationale
+The formal specification of an operator perfectly defines it, but the semantics so expressed may not reflect the operator designer intent. Having an explicit link between the formal specification and its informal counterpart is one way (i) to prevent errors (both specifications are redundant to some extent) and (ii) to enforce the formal specification to remain "simple".
+
+#### Related need
+[TBC]
+
+
+### REQ-FO-020: Operator versions
+
+#### Description
+The model shall indicate precisely the version of each operator used in the model.
+
+#### Rationale
+Operators may have several versions (opset) and, during graph execution, the shall be no ambiguity about the version to be used.
+
+#### Related need
+[need-thav-001](needs.md#need-thav-001-version-index-in-the-onnx-model)
+
+
+### REQ-FO-030: Representation of parameters
+
+#### Description
+The model shall shall specify all parameters down to the least significant bit and in a non-ambiguous way. For instance, the IEEE hexadecimal binary representation may be used to represent floating point parameters ([-]0x1.abcdefp[+-]n) .
+
+> **_NOTE:_** Rephrase in positive: e.g. "the model shall specify all parameters down to the least significant bit and in a non-ambiguous way". I like the hexadecimal representation for IEEE754 floats.\
+> OK: done
+> => OK
+
+#### Rationale
+The serialization of floating point number must not degrade the accuracy of the source model parameters.
+
+#### Related need
+[TBC]
+
+### REQ-FO-040: Documentation of input and output tensors
+
+#### Description
+The SONNX file format shall have the capability to describe
+- the semantics of the input and output tensors,
+- the semantics of the dimensions of the tensors.
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-arcys-001](needs.md#need-arcys-001-final-prediction)
+
+### REQ-FO-050: Derived requirements - implementation
+
+#### Description
+The SONNX profile shall have the capability to describe how the model must be deployed on a specific target, i.e.,
+- the exact order in which the graph operators must be executed
+- the target hardware on which the model or part of the model must be deployed.
+
+> **_NOTE:_** This is ambitious. I get how it may be useful to guarantee deterministic behaviour. But "target hardware" may be split into different levels of abstraction, e.g. "any GPU" vs "that specific model of GPU running that specific version of CUDA".\
+> ??: I am really wondering if this req has to be kept... Note that the req makes no assumption about the target, it only states that there shall be a way to specify the exact order in which operations (as defined in the model) are to be computed. The main question is: is it useful? is it necessary?
+> => Simply provide the capability to add meta-data in the model. Check whether they exist or not.
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-003](needs.md#need-ai-003-expression-of-implementation-requirements)
+
+
+# Operators
+
+
+## Operator set
+
+
+### REQ-OP-010: Deterministic operators
+
+#### Description
+The profile shall only include functional operators, or restriction shall be expressed to ensure that the operator will behave as a function during inference.
+
+A functional operator is an operator that maps one input value to one and only one output value. For instance, the ONNX ``dropout`` operator with ``training_mode`` set to ``true`` performs a random dropout that is not functional. A functional behaviour may be achieved by restricting the use of the operator, for instance, by enforcing input ``training_mode`` to be set to false. Operator ``RandomUniform`` is another example: if the seed input is set to some fixed value, the operator becomes functional. Checking that the input value may be done by a dedicated tool.
+
+> **_NOTE:_** Add examples to clarify? E.g. drop-out layers. Clarify whether the requirement extends to the _implementation_ of the operator: parallel computing in floating-point is non deterministic.
+> ??: I have rewritten the req in a positive way. I have added examples. I have used the term "functional" rather than "deterministic".
+=> OK
+
+#### Rationale
+A graph must be deterministic.
+
+#### Related need
+[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
+
+### REQ OP 000: Operator set
+
+#### Description
+The SONNX profile shall include at least the following operators:
+
+
+| Operator |
+|------------------------------|
+| Abs |
+| Add |
+| Cast |
+| Clip |
+| Concat |
+| Constant |
+| ConstantOfShape |
+| Conv |
+| ConvTranspose |
+| Dense |
+| Div |
+| Equal |
+| Erf |
+| Exp |
+| Expand |
+| Flatten |
+| FullyConnected |
+| Gather |
+| Gemm |
+| GlobalAveragePool |
+| GRU |
+| HardSwish |
+| Identity |
+| LeakyRelu |
+| Less |
+| Log |
+| LSTM |
+| MatMul |
+| Max |
+| MaxPool |
+| Min |
+| Mod |
+| Mul |
+| Neg |
+| Not |
+| Pad |
+| Padding |
+| Pow |
+| Range |
+| ReduceMean |
+| ReduceSum |
+| Relu |
+| Reshape |
+| Resize |
+| ScatterND |
+| Shape |
+| Sigmoid |
+| Slice |
+| Softmax |
+| SoftPlus |
+| Split |
+| Sqrt |
+| Squeeze |
+| Sub |
+| Tanh |
+| Transpose |
+| ConvTransposeDeconvolution |
+| Unsqueeze |
+| Where |
+
+
+#### Rationale
+The SONNX profile does not cover the complete set of ONNX operators. It is limited to operators (i) used during inference, (ii) that do not undermine determinism and predictability, (iii) used in a first set of industrial [use cases](../../scope/scope.md). This set may be later extended depending on the needs.
+
+> **_NOTE:_** "inference", "determinism", "predictability", "use cases" should be defined before being used as a justification.
+> ??: Note that this text is not part of the specification. I have introduced the requirement about determinism before. The concept of "inference" is well-known and I don't think that it deserves a definition (to be discussed).
+> => Inference to be defined (lexicon to be added)
+
+> **_NOTE:_** We should list the use cases (with references) if possible. That will give the reader a better idea of the scope.
+
+#### Related need
+[TBC]
+
+
+
+### REQ-OP-020: Inference operators
+
+#### Description
+The profile shall only include operators used during inference.
+
+#### Rationale
+SONNX is only concerned with inference.
+
+> **_NOTE:_** Is "inference" clear enough? Can we define it as "all numerical parameters in the graph are constant" or something like that?
+> OK/ modified as proposed.
+> => Modify the rationale ("dead code").
+
+#### Related need
+[TBC]
+
+#### Related need
+[need-ai-009](needs.md#need-ai-009-resource-usage-determinism-and-predictability)
+
+
+### REQ-OP-030: Compliance with the ONNX standard
+
+#### Description
+The SONNX version of an operator `op` shall have the same inputs, outputs, and attributes than the ONNX `op` operator.
+
+However, it is allowed to restrict the inputs and parameters value domains if deemed necessary.
+
+> **_NOTE:_** CHange "ranges" to "values"?\
+> ?? : "ranges" => "domains"
+> => OK
+
+#### Rationale
+Compatibility with the ONNX standard.
+
+#### Related need
+[TBC]
+
+### REQ-OP-060: Restrictions wrt ONNX
+
+#### Description
+The SONNX profile shall clearly indicate when a condition on the inputs, outputs, and attributes is a restriction with respect to the ONNX standard.
+
+In that case, the condition must be marked with tag `[restrict]`.
+
+> **_NOTE:_** Move this requirement closer to requirement "compliance with the ONNX standard" as they are related?\
+> OK: moved
+> => OK
+
+#### Rationale
+Clarity.
+
+#### Related need
+[TBC]
+
+
+## Operator specification
+
+### REQ-OP-030: Input domain specification
+
+#### Description
+For each operator in the SONNX operator set, the SONNX profile shall specify the validity domain of inputs and attributes.
+
+#### Rationale
+There shall be no room for interpretation or non determinism.
+
+#### Related need
+[TBC]
+
+### REQ-OP-030: Operator specification
+
+#### Description
+For each operator in the SONNX operator set, the SONNX profile shall specify the expected output values for any input values and attributes in their validity domain.
+
+The SONNX standard shall specify operators for values in the domain of real numbers ($R$) and in all domains necessary to support the industrial use cases (e.g, `float32`and `int32`).
+
+For instance, the `conv` operator shall be specified for values in $R$, `float32` and `int32` datatypes.
+
+> **_NOTE:_** Again, I'm not against the use of qualifiers such as "systematically", but we should give a definition of their meaning at the beginning of the document.\
+> OK: I have simply removed "systematically that was not required here. I have also suppressed the dedicated req and merged it with the one concerning ht operator specification
+> => OK.
+
+#### Rationale
+The semantics of the operator may depend on the types (float, integers), accuracy (float32, float64) and range (int16, int32) of numbers.
+
+#### Related need
+[TBC]
+
+> **_NOTE:_** Very nice! Do we want to introduce some specific language at the beginning of the document, like in legal documents? E.g. by "completely and specific" we mean... Also, there are multiple requirements named REQ-OP-030. \
+> ??: (i) I think that we can remove "completely" and "precisely" that brings no useful information > - requirements will be renumbered in a second phase.
+> => OK
+
+#### Rationale
+There shall be no room for interpretation or non determinism.
+
+#### Related need
+[TBC]
+
+### REQ-OP-040: Out of domain errors
+
+#### Description
+The SONNX profile shall specify the behaviour of an operator should some input value be out of its validity domain.
+
+#### Rationale
+Out of domain conditions may not be prevented by design, checked statically or detected at runtime.
+For attributes, a static verification of the model is expected to be carried out in order to prevent such error conditions.
+
+#### Related need
+[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
+
+### REQ-OP-050: Overflow and underflow conditions
+
+#### Description
+The SONNX profile shall specify the conditions leading to overflows, underflows, wrap-around.
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
+
+
+### REQ-OP-070: No default value
+
+ #### Description
+The SONNX profile shall forbid the use of default values.
+
+#### Rationale
+The ONNX standard defines default value for attributes that are left without values. The objective is to ensure that the model designer and model implementer has a clear knowledge of the values involved in computations.
+
+#### Related need
+[TBC]
+
+
+
+# Requirements on graph
+
+### REQ-GR-000: Graph specification
+
+#### Description
+The profile shall specify the graph execution semantics.
+
+#### Rationale
+A model is a graph of operators. The semantics of the graph defines how operators are applied to generate the graph outputs out of its inputs.
+
+> **_NOTE:_** Missing: what is a node, what is an edge, is it a DAG, etc.
+> => See if we put it before ops.
+
+#### Related need
+[TBC]
+
+### REQ-GR-000: Explicit types and shapes
+
+#### Description
+All numerical types must be indicated explicitly (no type inference).\
+All shape conversion must be done explicitly (using the `reshape`) operator. (no [shape inference](https://onnx.ai/onnx/api/shape_inference.html))
+
+> **_NOTE:_** If we enforce consistent input/output shapes (types), then the latter requirement is redundant. We could keep it as an example?
+> OK: chenged "datatypes" to numerical types (because in formal "theory" a "type" also covers the structure...)
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-thav-002](needs.md#need-thav-002-typing-of-data-handled-by-implementation-operators)
+
+
+# Requirements on file format
+
+### REQ-FO-000: Compatibility with ONNX
+
+#### Description
+Any model compliant with the SONNX file format shall be a correct ONNX model.
+
+#### Rationale
+Compliance with ONNX.
+
+#### Related need
+[TBC]
+
+### REQ-FO-010: Format completeness
+
+#### Description
+The SONNX file format specification shall ensure that a model expressed in this format can be implemented without requiring any additional information.
+
+#### Rationale
+A SONNX model must be completely determined, leaving no room to interpretation.
+
+> **_NOTE:_** Slight ambiguity here. We do not mandate a specific implementation, thus a single SONNX model accepts multiple implementations, i.e. there _is_ room for interpretation. Can we reformulate this in a better way? "sufficient number of properties"?
+> => To be rephrased...
+
+#### Related need
+[TBC]
+
+
+# Reference implementation
+
+### REQ-RI-000: Reference implementation
+
+#### Description
+The SONNX profile shall provide a reference implementation covering
+- the operator set
+- the graph execution
+- the graph import (deserialization of a SONNX model)
+
+#### Rationale
+In order to verify the correctness of his/her implementation, the user will need to compare the results computed by this implementation with some reference. Since we need to compare computation **results**, this means that the specification must be executable. One possible way could be to use an executable formal specification (e.g., `why3`). Another way consists to provide (i) a formal specification and (ii) a "reference" implementation demonstrated to comply with the formal specification.
+
+#### Related need
+[need-ai-011](needs.md#need-ai-011-support-for-verification-activities)
+
+### REQ-RI-010: Relation with specification
+
+#### Description
+The reference implementation satisfies all the properties of the SONNX specification, i.e. it is compliant to SONNX, and it is written in the most plain and clear style possible (no optimisations).
+
+> **_NOTE:_** Again, I understand the spirit of the requirement -> make the implementation as "vanilla" as possible. However, I wonder whether we can say something as "the reference implementation satisfies all the properties of the SONNX specification, i.e. it is compliant to SONNX, and it is written in the most plain and clear style possible (no optimisations)". Just to stress that there are two separate requirements: (1) correctness/adherence to SONNX and (2) simple and easy-to-read style.
+> => change to recommandation and use Edoardo's sentence.
+
+#### Rationale
+Verifying the reference implementation must be as easy as possible.
+
+#### Related need
+[TBC]
+
+
+# Tooling
+
+### REQ-TO-000: Verification tool
+
+#### Description
+The SONNX profile shall provide a model verification tool.\
+This tool aims at verifying that all validity conditions are satisfied, including:
+- the graph structure is well-formed,
+- all required metadata are present
+- operators are allowed
+- all conditions involving attributes are satisfied
+
+> **_NOTE:_** Do we need to add "is executable" as one of the conditions (well-formedness)? Also, the latter condition needs to be split into (1) conditions related to input/output/parameter _types_ are satisfied and (2) conditions related to input/output/parameter _values_ are satisfied. The conditions on values may not be trivial to verify at all, especially if they depend on very specific execution traces (e.g. proving an operator input is always positive is an NP-Hard problem).
+> => Check if conditions that can be verified only cobncer attributes (and not values). Check also the case of dimensions...
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-007](needs.md#need-ai-007-support-for-model-verification)
+
+
+# TO BE DISCUSSED
+
+> **_NOTE:_** These are not bad ideas, but they are very ambiguous.
+> => Ceck if we keep some of them as recos.
+
+### REQ-OP-022: Stability of operators
+
+#### Description
+The SONNX profile shall not include numerically unstable operators. If such an unstable operator is required, the instability phenomenon shall be fully characterized
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability)
+
+
+### REQ-OP-023: Determinism of resource usage
+
+#### Description
+The SONNX profile shall only include operators which memory usage does not depend on input values.
+
+#### Rationale
+[TBC]
+
+### REQ-OP-030: Determinism of execution times
+
+#### Description
+The SONNX profile shall not include operators which execution time depend on input values.
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-010](needs.md#need-ai-010-execution-time-determinism-and-predictability)
+
+### REQ-FO-012: Traceability to training model
+
+[TBDis]
+
+#### Description
+The SONNX profile must provide the capability to trace the ONNX model to the training model from which it has been generated.
+
+#### Rationale
+[TBC]
+
+#### Related need
+[need-ai-004](needs.md#need-ai-004-support-for-traceability)
+
+
+### REQ-FO-013: Traceability to training environment
+
+[TBDis]
+
+#### Description
+The SONNX profile must provide the capability to trace the environment used for training.
+
+#### Rationale
+
+#### Related need
+[need-ai-004](needs.md#need-ai-004-support-for-traceability)
+
diff --git a/safety-related-profile/deliverables/scope/scope.md b/safety-related-profile/deliverables/scope/scope.md
index 8d5eaaa2..28374ef1 100644
--- a/safety-related-profile/deliverables/scope/scope.md
+++ b/safety-related-profile/deliverables/scope/scope.md
@@ -130,28 +130,28 @@ YoloNAS
## Operators
### Priority 1 list:
-1) Conv2D (no grouping nor depthwise)
-2) ConvTranspose/Deconvolution
+1) Conv (no grouping nor depthwise)
+2) ConvInteger
3) MaxPool
-4) ReLU
-5) Add
-6) Mul
-7) Concat
+4) Resize
+5) QuantizeLinear
+6) DequantizeLinear
+7) Add
+8) Mul
+9) Relu
+10) Concat
### Priority 2 list:
-8) FullyConnected
-9) Conv2D (grouping & depthwise)
-10) Sub
-11) Abs
-12) ReduceSum
-13) Transpose
-14) Split
-15) Slice
-16) Gather
-17) Squeeze
-18) Unsqueeze
-19) Reshape
-20) Flatten
+11) Conv (grouping & depthwise)
+12) Gemm
+13) MatMul
+14) Abs
+15) Sub
+16) ReduceSum
+17) Gelu
+18) LeakyRelu
+19) LayerNormalization
+20) SoftMax
# Use case `Helicopters` - `Valot Nicolas`
diff --git a/safety-related-profile/documents/Attic/call.md b/safety-related-profile/documents/Attic/call.md
new file mode 100644
index 00000000..da3e94c8
--- /dev/null
+++ b/safety-related-profile/documents/Attic/call.md
@@ -0,0 +1,34 @@
+Our work on defining ONNX is progressing—albeit slowly—thanks to the dedication of a **small group** of contributors.
+
+Initially, our meetings had around 15 participants, but attendance has gradually declined to a critically low level, with only seven attendees in our last meeting.
+
+**This situation is hardly sustainable if we aim to deliver the first SONNX profile by the end of the year.**
+
+So, we urgently need to expand our team to:
+
+- Continue specifying SONNX operators,
+- Finalize the format specification (primarily verification and potential completion),
+- Develop the "SONNX checker" tool,
+- Alleviate the burden on the core team.
+
+So far, we have refrained from broadening our audience, assuming the initial group was already sufficiently large and that it would be more effective to engage a wider audience once we had a solid set of preliminary results. While we have not fully reached this point, we are getting close and we plan to get talk during ONNX next meetup in June.
+
+The key problem is that most of the people have no dedicated funding for this activity, which obviously strongly limit the commitment… **In this situation, having a clear view of what effort you can spend on SONNX would really help sharing the work. Please tell us.**
+
+If your capability is more of less the time we spend in our meetings, o**ne possibility could be to shorten the meetings and use this time to do actual work on smaller and more focused tasks.** Would you agree?
+
+Another solution could be to setup some form of collaborative project (funded by the participants and/ secure external funding to allocate dedicated resources for key tasks, thereby lightening the workload of the working group.
+
+If you have any ideas about this modality, please tell us.
+
+Finally, we also have to address the root causes for this situation:
+
+- A general lack of interest in the topic,
+- Diverging views on the objectives,
+- Differences in the way the working group is managed.
+
+While there may be little we can do about (i), we can certainly improve (ii) and (iii).
+
+We welcome your feedback—please let us know what you think could or should be improved.
+
+Thank you for your cooperation.
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/.gitignore b/safety-related-profile/documents/c_code_generation/conv_op/.gitignore
new file mode 100644
index 00000000..f12c3561
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/.gitignore
@@ -0,0 +1,5 @@
+.why3find
+why3session.xml
+why3shapes.gz
+/lib
+/html
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/Makefile b/safety-related-profile/documents/c_code_generation/conv_op/Makefile
new file mode 100644
index 00000000..ed847a19
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/Makefile
@@ -0,0 +1,37 @@
+# Makefile for libvector
+
+.PHONY: all prove update doc lib cc clean
+
+all: prove doc lib
+ @echo "------------------------------------------"
+
+prove:
+ @echo "------------------------------------------"
+ @echo "--- Proofs"
+ @echo "------------------------------------------"
+ @why3find prove *.mlw -l -s -x
+
+update:
+ why3find prove *.mlw -l -s -x -m
+
+doc:
+ @echo "------------------------------------------"
+ @echo "--- Documentation"
+ @echo "------------------------------------------"
+ @rm -fr html
+ @why3find doc *.mlw -t "Tensor Library"
+
+EXTRACT=-D c -D cdriver.drv -L . --debug-all --modular --interface -o lib
+
+lib:
+ @echo "------------------------------------------"
+ @echo "--- Extraction"
+ @echo "------------------------------------------"
+ @rm -fr lib
+ @why3 extract $(EXTRACT) libvector.CIndex
+ @why3 extract $(EXTRACT) libtensor.CTensor
+ @cd lib && gcc -c *.c
+ @find lib -type f | sort
+
+clean:
+ rm -fr lib html
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/README.md b/safety-related-profile/documents/c_code_generation/conv_op/README.md
new file mode 100644
index 00000000..cb07f3ad
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/README.md
@@ -0,0 +1,93 @@
+# ONNX-C
+
+
+
+## Getting started
+
+To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+
+Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+
+## Add your files
+
+- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
+- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
+
+```
+cd existing_repo
+git remote add origin https://git.frama-c.com/lcorrenson/onnx-c.git
+git branch -M main
+git push -uf origin main
+```
+
+## Integrate with your tools
+
+- [ ] [Set up project integrations](https://git.frama-c.com/lcorrenson/onnx-c/-/settings/integrations)
+
+## Collaborate with your team
+
+- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
+- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
+- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
+- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
+- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
+
+## Test and Deploy
+
+Use the built-in continuous integration in GitLab.
+
+- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
+- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
+- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
+- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
+- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+
+***
+
+# Editing this README
+
+When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
+
+## Suggestions for a good README
+
+Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+
+## Name
+Choose a self-explaining name for your project.
+
+## Description
+Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
+
+## Badges
+On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
+
+## Visuals
+Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
+
+## Installation
+Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
+
+## Usage
+Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+
+## Support
+Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+
+## Roadmap
+If you have ideas for releases in the future, it is a good idea to list them in the README.
+
+## Contributing
+State if you are open to contributions and what your requirements are for accepting them.
+
+For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+
+You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+
+## Authors and acknowledgment
+Show your appreciation to those who have contributed to the project.
+
+## License
+For open source projects, say how it is licensed.
+
+## Project status
+If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/cdriver.drv b/safety-related-profile/documents/c_code_generation/conv_op/cdriver.drv
new file mode 100644
index 00000000..38cc585f
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/cdriver.drv
@@ -0,0 +1,87 @@
+module std.Clib
+ interface export "#include "
+ interface export "#include "
+ syntax val to_uint32 "(uint32_t) %1"
+ syntax val is_null "!%1" prec 0
+ syntax val ([]) "%1[%2]" prec 1 1 15
+ syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14
+end
+
+
+
+module std.Cint
+ interface export "#include "
+ interface export "#include "
+ syntax type int "int32_t"
+ syntax val of_int "(int32_t) %1"
+ syntax val ( .+ ) "%1 + %2" prec 8 8 7
+ syntax val ( .- ) "%1 - %2" prec 8 8 7
+ syntax val ( .* ) "%1 * %2" prec 7 7 6
+ syntax val ( ./ ) "%1 / %2" prec 7 7 6
+ syntax val malloc_int "malloc(%1 * sizeof(int))"
+ syntax val malloc_int32 "malloc(%1 * sizeof(int32_t))"
+ syntax val get "%1[%2]"
+ syntax val ([]) "%1[%2]" prec 1 1 15
+end
+
+module std.Clib
+ syntax val malloc_int32 "malloc(%1 * sizeof(int32_t))"
+end
+
+module mach.int.Int32
+ syntax type int32 "int32_t"
+ syntax val of_int "(int32_t) %1"
+ syntax val to_int "(int) %1"
+ syntax val (>=) "%1 >= %2" prec 11 11 10
+ syntax val (<=) "%1 <= %2" prec 11 11 10
+ syntax val (<) "%1 < %2" prec 11 11 10
+ syntax val (>) "%1 > %2" prec 11 11 10
+ syntax val (=) "%1 == %2" prec 11 11 10
+end
+
+module mach.int.UInt32
+ syntax type uint32 "uint32_t"
+ syntax val to_int "(int) %1"
+end
+
+module std.Cfloat
+ syntax type float "double"
+ syntax val zero "0.0"
+ syntax val one "1.0"
+ syntax val f32 "(double) %1"
+ syntax val f64 "(double) %1"
+ syntax val ( .+ ) "%1 + %2" prec 8 8 7
+ syntax val ( .- ) "%1 - %2" prec 8 8 7
+ syntax val (.-_) "- %1" prec 5 4
+ syntax val ( .* ) "%1 * %2" prec 7 7 6
+ syntax val ( ./ ) "%1 / %2" prec 7 7 6
+ syntax val ( .= ) "%1 = %2" prec 11 11 10
+ syntax val ( .< ) "%1 < %2" prec 11 11 10
+ syntax val ( .<= ) "%1 <= %2" prec 11 11 10
+end
+
+module ref.Ref
+ syntax type ref "%1*"
+ syntax val ref "&%1"
+ syntax val (!) "*%1"
+ syntax val (:=) "*%1 = %2"
+end
+
+module mach.c.C
+ syntax type ptr "%1*"
+end
+
+
+module libvector.CIndex
+ interface "#include "
+ interface "#include "
+end
+
+module libtensor.CTensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+
+
+end
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout.mlw b/safety-related-profile/documents/c_code_generation/conv_op/layout.mlw
new file mode 100644
index 00000000..fd7bcf7b
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/layout.mlw
@@ -0,0 +1,91 @@
+module CFlat
+ use std.Int
+ use std.List
+ use tensor.Range
+
+ function offset (ks ds : list int) : int =
+ match ks , ds with
+ | Cons k ks , Cons _ ds -> k * size ds + offset ks ds
+ | _ -> 0
+ end
+
+ function index (p : int) (ds : list int) : list int =
+ match ds with
+ | Nil -> Nil
+ | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds)
+ end
+
+ let rec lemma offset_size (ks ds : list int)
+ requires { valid ks ds }
+ ensures { 0 <= offset ks ds < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k krs , Cons d drs ->
+ offset_size krs drs ;
+ let p = offset ks ds in
+ let q = offset krs drs in
+ assert { p = size drs * k + q } ;
+ assert {
+ size drs * k
+ <= size drs * (d - 1)
+ = d * size drs - size drs
+ } ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma index_range (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { valid (index p ds) ds }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds -> index_range (mod p (size rds)) rds
+ end
+ (*qed*)
+
+ let rec lemma index_of_offset (ks ds : list int)
+ requires { valid ks ds }
+ ensures { index (offset ks ds) ds = ks }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k rks , Cons _ rds ->
+ index_of_offset rks rds ;
+ let p = offset ks ds in
+ let n = size rds in
+ let q = offset rks rds in
+ euclide p k n q ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma offset_of_index (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { offset (index p ds) ds = p }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds ->
+ let n = size rds in
+ offset_of_index (mod p n) rds
+ end
+ (*qed*)
+
+ let rec lemma offset_push (ks ds : list int) (k d : int)
+ requires { valid ks ds }
+ ensures { offset (push ks k) (push ds d) = offset ks ds * d + k }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Cons _ krs , Cons _ drs -> offset_push krs drs k d
+ | _ -> ()
+ end
+ (*qed*)
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/layout/proof.json
new file mode 100644
index 00000000..0b07d2c9
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/layout/proof.json
@@ -0,0 +1,39 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "CFlat": {
+ "index_of_offset": { "prover": "cvc5@1.2.1", "time": 0.055 },
+ "index_range": { "prover": "alt-ergo@2.5.4", "time": 0.088 },
+ "offset_of_index": { "prover": "alt-ergo@2.5.4", "time": 0.16 },
+ "offset_push": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.075 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.01 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 1.6 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.049 }
+ ]
+ }
+ ]
+ },
+ "offset_size": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.01 },
+ { "prover": "z3@4.13.4", "time": 0.01 },
+ { "prover": "cvc5@1.2.1", "time": 0.137 }
+ ]
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3session.xml.bak
new file mode 100644
index 00000000..cf0e4c72
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3session.xml.bak
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3shapes.gz.bak
new file mode 100644
index 00000000..46419040
Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3shapes.gz.bak differ
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor.mlw b/safety-related-profile/documents/c_code_generation/conv_op/libtensor.mlw
new file mode 100644
index 00000000..89c0865d
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/libtensor.mlw
@@ -0,0 +1,313 @@
+module CTensor
+ use int.Int
+ use std.List
+ use std.Clib
+ use std.Cfloat
+ use mach.int.Int32
+ use tensor.Range
+ use tensor.Tensor
+ use tensor.OPWhere
+ use layout.CFlat
+ use libvector.CIndex
+ use ref.Ref
+ use tensor.OPAdd
+ use tensor.OPConv2d
+
+
+
+
+ type farray = ptr float
+
+ type ctensor = {
+ t_rank : int32 ;
+ t_dims : iarray ;
+ t_data : farray ;
+ }
+
+ function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank
+ function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank
+ predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t)
+ predicate empty_tensor (t : ctensor) = t.t_rank = 0
+
+ predicate valid_tensor (t : ctensor) =
+ dimension t.t_dims t.t_rank /\
+ valid_range t.t_data 0 (tensor_size t) /\
+ writable t.t_data
+
+ function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t)
+
+ function tensor_value_at (k : list int) (t : ctensor) : real =
+ if valid_index k t then
+ value_at t.t_data (tensor_offset k t)
+ else 0.0
+
+ function tensor_value (t : ctensor) : list int -> real =
+ fun k -> tensor_value_at k t
+
+ function tensor_valueb (t : ctensor) : list int -> bool =
+ fun k -> Real.(tensor_value_at k t > 0.0)
+
+ let ghost function tensor (t : ctensor) : tensor real
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_value t }
+ ensures { result.background = 0.0 }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_value t } ;
+ background = 0.0 ;
+ }
+
+ let ghost function tensorb (t : ctensor) : tensor bool
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_valueb t }
+ ensures { result.background = false }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_valueb t } ;
+ background = false ;
+ }
+
+ let ctensor_create (ds : iarray) (n : int32) : ctensor =
+ (*proof*)
+ requires { dimension ds n }
+ ensures { empty_tensor result \/ valid_tensor result }
+ ensures { empty_tensor result \/ result.t_rank = n }
+ ensures { empty_tensor result \/ result.t_dims = ds }
+ (*qed*)
+ let m = cdim_size ds n in
+ let vs = malloc (to_uint32 m) in
+ {
+ t_rank = if is_null vs then 0 else n ;
+ t_dims = ds ;
+ t_data = vs ;
+ }
+
+ let ctensor_clear (r : ctensor) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 }
+ (*qed*)
+ r.t_data[i] <- f32 0.0
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_reset (r : ctensor) (v : float) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = v }
+ (*qed*)
+ r.t_data[i] <- v
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_where (cond a b r : ctensor) =
+ (*proof*)
+ requires { valid_tensor cond }
+ requires { valid_tensor a }
+ requires { valid_tensor b }
+ requires { valid_tensor r }
+ requires { tensor a ~= tensor b ~= tensor r }
+ (*qed*)
+ requires { tensorb cond ~ tensor a ~ tensor b }
+ ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant {
+ forall k. 0 <= k < i ->
+ value_at r.t_data k =
+ if Real.(0.0 < value_at cond.t_data k)
+ then a.t_data[k] else b.t_data[k]
+ }
+ (*qed*)
+ r.t_data[i] <-
+ if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i]
+ done
+ (*proof*)
+ ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) }
+ (*qed*)
+
+let ctensor_add (a b r : ctensor) =
+ requires { valid_tensor a }
+ requires { valid_tensor b }
+ requires { valid_tensor r }
+ requires { tensor a ~= tensor b ~= tensor r }
+ ensures { tensor r = opadd (tensor a) (tensor b) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ invariant { forall k.
+ 0 <= k < i ->
+ value_at r.t_data k =
+ value_at a.t_data k .+ value_at b.t_data k
+ }
+ r.t_data[i] <- a.t_data[i] .+ b.t_data[i]
+ done
+ ;
+ assert { tensor r == opadd (tensor a) (tensor b) }
+
+(* ===== SECTION 1: Function Definition and Pre/Postconditions ===== *)
+
+ (* Goal: Implements the imperative 2D convolution operation (Conv2D) by iterating over all output pixels,
+ * calculating the weighted sum, and storing the result in the 'output' tensor.
+ * Inputs:
+ * x, w, b: ctensor - Input, Kernel (weights), and Bias tensors.
+ * pad_top...str_w: int32 - Convolution attributes (Padding, Dilation, Stride).
+ * output: ctensor - The tensor where the result will be stored (modified in-place).
+ * Outputs: unit (returns nothing, modifies 'output' in-place). *)
+let ctensor_conv2d (x : ctensor) (w : ctensor) (b : ctensor)
+ (pad_top pad_bottom pad_left pad_right : int32)
+ (dil_h dil_w : int32) (str_h str_w : int32)
+ (output : ctensor): unit =
+ requires { valid_tensor x }
+ requires { valid_tensor w }
+ requires { valid_tensor b }
+ requires { valid_tensor output }
+ requires { x.t_rank = 4 } (* Input tensor [N, C, H, W] *)
+ requires { w.t_rank = 4 } (* Kernel tensor [M, C, kH, kW] *)
+ requires { b.t_rank = 1 } (* Bias tensor [M] *)
+ requires { output.t_rank = 4 } (* Output tensor [N, M, oH, oW] *)
+
+
+ (* Attribute constraints: Stride and Dilation must be positive, Padding must be non-negative. *)
+ requires { str_h > 0 /\ str_w > 0 }
+ requires { dil_h > 0 /\ dil_w > 0 }
+ requires { pad_top >= 0 /\ pad_bottom >= 0 /\ pad_left >= 0 /\ pad_right >= 0 }
+ (* Postcondition: The concrete tensor 'output' must be equal to the ghost specification of Conv2D. *)
+ ensures { tensor output = OPConv2d.opconv2d (tensor x) (tensor w) (tensor b)
+ (Int32.to_int pad_top) (Int32.to_int pad_bottom)
+ (Int32.to_int pad_left) (Int32.to_int pad_right)
+ (Int32.to_int dil_h) (Int32.to_int dil_w)
+ (Int32.to_int str_h) (Int32.to_int str_w) }
+
+
+ (* ===== SECTION 2: Dimension Extraction and Variable Initialization ===== *)
+
+ (* Extraction des dimensions de chaque tenseur pour les limites des boucles *)
+ let n_batches = x.t_dims[0] in (* Batch size (N) *)
+ let c_in = x.t_dims[1] in (* Input Channels (C_in) *)
+ let h_in = x.t_dims[2] in (* Input Height (H_in) *)
+ let w_in = x.t_dims[3] in (* Input Width (W_in) *)
+ let m_out = w.t_dims[0] in (* Output Channels / Kernel count (M) *)
+ let kh = w.t_dims[2] in (* Kernel Height (kH) *)
+ let kw = w.t_dims[3] in (* Kernel Width (kW) *)
+
+ let h_out = output.t_dims[2] in (* Output Height (oH) *)
+ let w_out = output.t_dims[3] in (* Output Width (oW) *)
+
+
+ (* Conversion des paramètres de convolution en variables locales (int32 -> int32) *)
+ let pad_top_i = pad_top in
+ let pad_left_i = pad_left in
+ let dil_h_i = dil_h in
+ let dil_w_i = dil_w in
+ let str_h_i = str_h in
+ let str_w_i = str_w in
+
+
+ (* Allocation de tableaux temporaires pour stocker les coordonnées 4D (X, W, Output) ou 1D (B)
+ * Ces tableaux sont utilisés par la fonction 'coffset' pour calculer l'indice 1D dans t_data. *)
+ let x_coords = malloc_int32 (to_uint32 4) in
+ let w_coords = malloc_int32 (to_uint32 4) in
+ let b_coords = malloc_int32 (to_uint32 1) in
+ let output_coords = malloc_int32 (to_uint32 4) in
+
+ if is_not_null x_coords && is_not_null w_coords && is_not_null b_coords && is_not_null output_coords then begin
+ (* Boucles imbriquées pour calculer la convolution *)
+ (* ===== SECTION 3: Core Iteration Loops (Output Tensor) ===== *)
+ for n = 0 to n_batches - 1 do (* Loop over Batch dimension (N) *)
+ invariant { true }
+ for m = 0 to m_out - 1 do (* Loop over Output Channels (M) *)
+ invariant { true }
+ for oh = 0 to h_out - 1 do (* Loop over Output Height (oH) *)
+ invariant { true }
+ for ow = 0 to w_out - 1 do (* Loop over Output Width (oW) *)
+ invariant { true }
+
+ (* Initialisation de l'accumulateur pour le produit scalaire (dot product) *)
+ let conv_sum = ref (f64 0.0) in
+
+ (* ===== SECTION 4: Convolution and Summation Loops (Kernel) ===== *)
+
+ for c = 0 to c_in - 1 do (* Loop over Input Channels (C) *)
+ invariant { true }
+ for k_h = 0 to kh - 1 do (* Loop over Kernel Height (kH) *)
+ invariant { true }
+ for k_w = 0 to kw - 1 do (* Loop over Kernel Width (kW) *)
+ invariant { true }
+
+ (* 1. Calculate the indices in the input space *with padding* (ih, iw) *)
+ let ih = oh * str_h_i + k_h * dil_h_i in
+ let iw = ow * str_w_i + k_w * dil_w_i in
+
+ (* 2. Convert to *original* input indices (after subtracting padding) *)
+ let padded_ih = ih - pad_top_i in
+ let padded_iw = iw - pad_left_i in
+ (* 3. Retrieve the input value (X) with zero-padding handling *)
+ let x_val =
+ if 0 <= padded_ih && padded_ih < h_in && 0 <= padded_iw && padded_iw < w_in then begin
+ (* Calculate the X offset if the index is within the original bounds *)
+ set_ofs x_coords 0 n;
+ set_ofs x_coords 1 c;
+ set_ofs x_coords 2 padded_ih;
+ set_ofs x_coords 3 padded_iw;
+ let x_offset = coffset x_coords x.t_dims 4 in
+ if x_offset >= 0 then x.t_data[x_offset] else f64 0.0 (* Value of X *)
+ end else f64 0.0 (* Value 0.0 for padding *)
+ in
+
+ (* 4. Retrieve the kernel value (W) and calculate its offset *)
+ set_ofs w_coords 0 m;
+ set_ofs w_coords 1 c;
+ set_ofs w_coords 2 k_h;
+ set_ofs w_coords 3 k_w;
+ let w_offset = coffset w_coords w.t_dims 4 in
+ let w_val = if w_offset >= 0 then w.t_data[w_offset] else f64 0.0 in
+
+ (* 5. Accumulate the product X_val * W_val into the convolution sum *)
+ conv_sum := !conv_sum .+ (x_val .* w_val)
+ done
+ done
+ done;
+
+ (* ===== SECTION 5: Final Result Calculation and Assignment ===== *)
+
+ (* 6. Add the bias (B) (corresponding to the output channel m) *)
+ set_ofs b_coords 0 m;
+ let b_offset = coffset b_coords b.t_dims 1 in
+ let bias_val = if b_offset >= 0 then b.t_data[b_offset] else f64 0.0 in
+ let final_val = !conv_sum .+ bias_val in
+
+ (* 7. Calculate the final offset and store the result in the 'output' data array *)
+ set_ofs output_coords 0 n;
+ set_ofs output_coords 1 m;
+ set_ofs output_coords 2 oh;
+ set_ofs output_coords 3 ow;
+ let output_offset = coffset output_coords output.t_dims 4 in
+ if output_offset >= 0 then output.t_data[output_offset] <- final_val
+ done
+ done
+ done
+ done
+ end
+
+
+
+
+end
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/proof.json
new file mode 100644
index 00000000..25f011d6
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/proof.json
@@ -0,0 +1,2595 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 30, "time": 0.14 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.215 }
+ ],
+ "proofs": {
+ "CITensor": {
+ "citensor_clear": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.039 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.056 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.076 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.036 }
+ ]
+ },
+ "citensor_create": { "prover": "alt-ergo@2.5.4", "time": 0.054 },
+ "citensor_reset": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.039 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.058 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.087 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ "citensor_to_tensor": { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ "gto_int32": {
+ "tactic": "split_vc",
+ "children": [ null, { "prover": "alt-ergo@2.5.4", "time": 0.023 } ]
+ },
+ "to_int32": { "prover": "alt-ergo@2.5.4", "time": 0.021 }
+ },
+ "CTensor": {
+ "ctensor_add": { "prover": "alt-ergo@2.5.4", "time": 0.87 },
+ "ctensor_clear": { "prover": "alt-ergo@2.5.4", "time": 0.157 },
+ "ctensor_conv2d": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.062 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.069 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.063 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.068 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.059 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.059 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.06 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.062 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.069 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.115 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.173 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.258 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.042 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.404 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.048 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.631 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.062 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.924 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.068 },
+ { "prover": "cvc5@1.2.1", "time": 0.594 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.064 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.035 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.636 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.625 },
+ { "prover": "cvc5@1.2.1", "time": 0.166 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.089 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.084 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.079 }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.177 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.089 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.085 }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.187 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.085 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.084 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.662 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.089 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.085 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.113 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.768 },
+ { "prover": "cvc5@1.2.1", "time": 0.104 },
+ { "prover": "cvc5@1.2.1", "time": 0.323 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.72 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.089 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.096 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.088 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.087 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.787 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.088 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.09 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.089 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.817 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.092 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.087 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.092 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.088 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.09 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.79 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.09 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.097 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.098 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.15 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.942 },
+ { "prover": "cvc5@1.2.1", "time": 0.107 },
+ { "prover": "cvc5@1.2.1", "time": 0.411 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.155 },
+ { "prover": "cvc5@1.2.1", "time": 0.174 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.173 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.037 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.194 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.182 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.116 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.734 },
+ { "prover": "cvc5@1.2.1", "time": 0.103 },
+ { "prover": "cvc5@1.2.1", "time": 0.366 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.116 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.075 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.067 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.063 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.061 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.122 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.063 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.155 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.098 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.759 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.125 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.127 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.134 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.134 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.074 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.178 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.121 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.124 },
+ { "prover": "cvc5@1.2.1", "time": 0.308 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.087 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.081 },
+ { "prover": "z3@4.13.4", "time": 0.118 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.056 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.05 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.044 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.043 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.039 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.119 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }, null
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ null
+ ]
+ }
+ ]
+ },
+ "ctensor_create": { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ "ctensor_reset": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.055 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ ]
+ },
+ "ctensor_where": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.061 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.062 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.062 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.044 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.063 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.22 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 }
+ ]
+ },
+ "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ "tensorb": { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ },
+ "CTensorConv": {
+ "ctensor_conv2d": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.066 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.065 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.069 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.064 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.067 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.071 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.069 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.068 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.065 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ { "prover": "z3@4.13.4", "time": 0.313 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.539 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.654 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.042 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.854 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.056 },
+ { "prover": "z3@4.13.4", "time": 0.927 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.065 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 1.8 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.07 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 1.9 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.074 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.036 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.036 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.569 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.572 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.397 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.092 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.089 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.097 }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.393 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.099 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "cvc5@1.2.1", "time": 0.4 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.101 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.11 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.097 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.106 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.102 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.105 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "cvc5@1.2.1", "time": 0.427 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.106 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.103 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.038 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.101 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.109 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.102 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.148 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.047 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.097 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.108 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.111 }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.099 },
+ { "prover": "cvc5@1.2.1", "time": 0.422 },
+ { "prover": "cvc5@1.2.1", "time": 0.478 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.103 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.088 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.092 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.096 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.858 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.089 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.096 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.091 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "cvc5@1.2.1", "time": 0.938 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.099 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.096 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.096 }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.103 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.098 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.097 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "cvc5@1.2.1", "time": 1.9 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.1 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "cvc5@1.2.1", "time": 1.9 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.092 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.098 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.098 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.1 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.103 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.092 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.096 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.156 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.107 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.092 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.099 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.094 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.097 }
+ ]
+ }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.086 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.101 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.096 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.098 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.088 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.095 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.101 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.093 }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.161 },
+ { "prover": "cvc5@1.2.1", "time": 0.326 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.316 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.036 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "cvc5@1.2.1", "time": 0.305 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null,
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "cvc5@1.2.1", "time": 0.468 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null,
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.035 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.133 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.089 },
+ { "prover": "cvc5@1.2.1", "time": 0.456 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.128 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.078 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.079 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.07 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.069 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.638 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.036 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.073 },
+ { "prover": "z3@4.13.4", "time": 0.31 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.116 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.763 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.712 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.776 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.835 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.023 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.029 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.734 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.035 }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.034 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.039 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.085 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.039 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.037 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.127 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.127 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.988 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.08 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.089 },
+ { "prover": "z3@4.13.4", "time": 0.911 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.063 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.055 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.054 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.041 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.042 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ },
+ { "prover": "z3@4.13.4", "time": 0.206 },
+ null
+ ]
+ }
+ },
+ "CTensor_Int": {
+ "create_coords_array": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.034 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.04 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ "create_coords_array_int": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.042 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ "ctensor_int_clear": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.045 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 }
+ ]
+ },
+ "ctensor_int_create": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 }, null, null
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ "ctensor_int_get": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }, null
+ ]
+ }
+ ]
+ },
+ "ctensor_int_set": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.036 },
+ { "prover": "cvc5@1.2.1", "time": 0.372 },
+ null
+ ]
+ },
+ "tensor_int": { "prover": "alt-ergo@2.5.4", "time": 0.042 }
+ },
+ "CtensorConv2D": {
+ "conv_partial_sum_dilated": {
+ "tactic": "split_vc",
+ "children": [
+ null,
+ null,
+ null,
+ null,
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 }, null
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 }, null
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 }, null
+ ]
+ },
+ null
+ ]
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3session.xml.bak
new file mode 100644
index 00000000..80c59c98
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3session.xml.bak
@@ -0,0 +1,1534 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3shapes.gz.bak
new file mode 100644
index 00000000..5369bfbf
Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector.mlw b/safety-related-profile/documents/c_code_generation/conv_op/libvector.mlw
new file mode 100644
index 00000000..2cc06a87
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/libvector.mlw
@@ -0,0 +1,136 @@
+(** C-Library to compute Offsets & Dimensions *)
+
+module CIndex
+ use std.Int
+ use std.List
+ use std.Clib
+ use mach.int.Int32
+ use tensor.Range
+ use layout.CFlat
+
+
+ type iarray = ptr int32
+
+ function ivector (u : iarray) (n : int) : list int
+ = map Int32.to_int (vector u n)
+
+ function islice (u : iarray) (p q : int) : list int
+ = map Int32.to_int (slice u p q)
+
+ (** ## Dimensions *)
+
+ function vdim (u : iarray) (n : int) : int = size (ivector u n)
+ function sdim (u : iarray) (p q : int) : int = size (islice u p q)
+
+ predicate pdim (u : iarray) (p q : int) =
+ forall k. p <= k < q -> 0 < value_at u k
+
+ predicate dimension (u : iarray) (n : int) =
+ 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32
+
+ (** Equivalence betwen {pdim} and {tensor.Range.positive}. *)
+ let rec lemma positive_pdim (u : iarray) (p q : int)
+ (*proof*)
+ ensures { pdim u p q <-> positive (islice u p q) }
+ variant { q - p }
+ = if p < q then positive_pdim u (p+1) q
+ (*qed*)
+
+ let ghost sdim_split (u : iarray) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { pdim u p q }
+ ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q }
+ = assert { islice u p k ++ islice u k q = islice u p q }
+ (*qed*)
+
+ (** Extracted Library *)
+
+ let cdim_size (u : iarray) (n : int32): int32 =
+ ensures { result = vdim u n }
+ (*proof*)
+ requires { dimension u n }
+ (*qed*)
+ begin
+ let ref p = 1 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { p = vdim u i }
+ ghost
+ begin
+ ensures { 1 <= p * u[i] <= max_int32 }
+ let ghost dl = pure { sdim u 0 i } in
+ let ghost di = Int32.to_int u[i] in
+ let ghost dr = pure { sdim u (i+1) n } in
+ let ghost dn = pure { sdim u 0 n } in
+ sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * u[i] ;
+ done ; p
+ end
+
+ let cdim_create_1 (n : int32) : iarray =
+ requires { 0 < n }
+ ensures { is_not_null result -> vdim result 1 = n }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 1 }
+ ensures { is_not_null result -> value_at result 0 = n }
+ (*qed*)
+ let cd = malloc 1 in
+ if is_not_null cd then cd[0] <- n ; cd
+
+ let cdim_create_2 (p q : int32) : iarray =
+ requires { 0 < p /\ 0 < q /\ p * q <= max_int32 }
+ ensures { is_not_null result -> vdim result 2 = p * q }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 2 }
+ ensures { is_not_null result -> value_at result 0 = p }
+ ensures { is_not_null result -> value_at result 1 = q }
+ (*qed*)
+ let cd = malloc 2 in
+ if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd
+
+ (** ## Coordinates *)
+
+ let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 =
+ (*proof*)
+ requires { 0 <= n }
+ requires { valid_range ks 0 n }
+ requires { dimension ds n }
+ ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) }
+ ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) }
+ (*qed*)
+ begin
+ let ref p = 0 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { valid (ivector ks i) (ivector ds i) }
+ invariant { p = offset (ivector ks i) (ivector ds i) }
+ (*qed*)
+ let d = ds[i] in
+ let k = ks[i] in
+ if 0 <= k && k < d then
+ begin
+ (*proof*)
+ assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ;
+ ghost
+ begin
+ ensures { Int32.in_bounds (p * d) }
+ ensures { Int32.in_bounds (p * d + k) }
+ let ghost dl = pure { sdim ds 0 i } in
+ let ghost di = Int32.to_int ds[i] in
+ let ghost dr = pure { sdim ds (i+1) n } in
+ let ghost dn = pure { sdim ds 0 n } in
+ sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * d + k ;
+ end
+ else return (-1)
+ done ; p
+ end
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/libvector/proof.json
new file mode 100644
index 00000000..8bfef621
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/libvector/proof.json
@@ -0,0 +1,103 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "CIndex": {
+ "cdim_create_1": { "prover": "alt-ergo@2.5.4", "time": 0.194 },
+ "cdim_create_2": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.224 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 }
+ ]
+ },
+ "cdim_size": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.066 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.935 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.193 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 }
+ ]
+ },
+ "coffset": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.813 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.057 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.191 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 }, null
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.93 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 }
+ ]
+ },
+ "positive_pdim": { "prover": "alt-ergo@2.5.4", "time": 0.074 },
+ "sdim_split": { "prover": "alt-ergo@2.5.4", "time": 0.206 }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3session.xml.bak
new file mode 100644
index 00000000..fbd37417
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3session.xml.bak
@@ -0,0 +1,213 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3shapes.gz.bak
new file mode 100644
index 00000000..03a81a28
Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3shapes.gz.bak differ
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std.mlw b/safety-related-profile/documents/c_code_generation/conv_op/std.mlw
new file mode 100644
index 00000000..5d88c4c9
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/std.mlw
@@ -0,0 +1,231 @@
+(* Extensions to Standard Library *)
+
+module Int
+ use export int.Int
+ use export int.Abs
+ use export int.ComputerDivision
+ use list.List
+ use list.Length
+
+ (** Unicity of euclidian division decomposition *)
+ let ghost euclide (a b q r : int)
+ requires { 0 <= a /\ 0 <= r < q }
+ requires { a = b * q + r }
+ ensures { b = div a q }
+ ensures { r = mod a q }
+ (*proof*)
+ = let rec kernel (b r : int)
+ requires { b * q + r = 0 }
+ requires { abs r < q }
+ ensures { b = r = 0 }
+ variant { abs b }
+ = if b < 0 then kernel (b + 1) (r - q) else
+ if b > 0 then kernel (b - 1) (r + q) else ()
+ in kernel (b - div a q) (r - mod a q)
+ (*qed*)
+
+ (** Multiplication upper-bound *)
+ let ghost mult_bound (a b c : int)
+ requires { 0 < a * b <= c }
+ ensures { abs a <= c }
+ ensures { abs b <= c }
+ = ()
+(** Helper function to extract element from list at given position *)
+ let rec function get_dim (dims : list int) (idx : int) : int
+ requires { 0 <= idx < length dims }
+ variant { dims }
+ = match dims with
+ | Nil -> 0 (* should not happen due to precondition *)
+ | Cons h t -> if idx = 0 then h else get_dim t (idx - 1)
+ end
+
+end
+
+module List
+ use export list.List
+ use export list.Map
+ use export list.Append
+
+ (** Appends an element to the end of the list *)
+ function push (xs : list 'a) (x : 'a) : list 'a =
+ xs ++ Cons x Nil
+
+ let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a)
+ ensures { map f (xs ++ ys) = map f xs ++ map f ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end
+ (*qed*)
+
+end
+
+module Clib
+ use map.Map
+ use int.Int
+ use mach.int.Int32
+ use export mach.c.C
+
+ type int32 = Int32.int32
+ type uint32 = UInt32.uint32
+
+ val function to_uint32 (v : int32) : uint32
+ requires { 0 <= v }
+ ensures { Int32.to_int v = UInt32.to_int result }
+
+ (** Null-Pointer *)
+ val predicate is_null (vp : ptr 'a) : bool
+ ensures { result <-> vp.zone = null_zone }
+
+ (** Array-range validity *)
+ predicate valid_range (vp : ptr 'a) (p q : int) =
+ q <= p \/
+ ( 0 <= vp.min <= vp.offset /\
+ 0 <= p <= q <= max_int32 /\
+ 0 <= vp.min <= vp.offset + p /\
+ vp.offset + q <= vp.max <= vp.plength )
+
+ let lemma valid_in_range (vp : ptr 'a) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { valid_range vp p q }
+ ensures { valid_ptr_shift vp k }
+ = ()
+ (*qed*)
+
+ (** Array as map *)
+ let ghost function value_at (vp : ptr 'a) : map int 'a =
+ pure { fun k -> vp.data.Array.elts (vp.offset + k) }
+
+ (** Array access (ghost) *)
+ function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k
+
+ (** Array access (C, inlined) *)
+ let ([]) (vp : ptr 'a) (k : int32) : 'a
+ (*proof*)
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { result = vp[k] }
+ = get_ofs vp k
+ (*qed*)
+
+ (** Array update (C, inlined) *)
+ let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit
+ requires { writable vp }
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v }
+ = set_ofs vp k v
+
+ (** Array as list *)
+ use List
+
+ let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a =
+ (*proof*)
+ variant { q - p }
+ (*qed*)
+ if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q)
+
+ let rec lemma slice_append (u : ptr 'a) (p q r : int)
+ requires { p <= q <= r }
+ ensures { slice u p r = slice u p q ++ slice u q r }
+ (*proof*)
+ variant { q - p }
+ = if p < q then slice_append u (p+1) q r
+ (*qed*)
+
+ let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n
+
+ let lemma vector_push (u : ptr 'a) (n : int)
+ requires { 0 <= n }
+ ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+ (*proof*)
+ = slice_append u 0 n (n+1)
+ (*qed*)
+
+ val function malloc_int32 (s: uint32) : ptr int32
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+end
+
+module Cint
+ use int.Int
+ use mach.int.Int32
+ use mach.int.Int32 as Int32
+ use mach.int.UInt32
+ use mach.int.UInt32 as UInt32
+ use int.ComputerDivision
+ use Clib
+
+ val function ( .+ ) (a b : int) : int
+ ensures { result = a + b }
+
+ val function ( .- ) (a b : int) : int
+ ensures { result = a - b }
+
+ val function ( .* ) (a b : int) : int
+ ensures { result = a * b }
+
+ val function ( ./ ) (a b : int) : int
+ requires { b <> 0 }
+ ensures { result = ComputerDivision.div (a) ( b) }
+
+ val function of_int (v : int) : Int32.int32
+ ensures { Int32.to_int result = v }
+
+ val function malloc_int (s: uint32) : ptr int
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+ val function get (p: ptr int) (k: Int32.int32) : int
+ ensures { result = p[k] }
+
+ val function malloc_int32 (s: uint32) : ptr int32
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+ let ([]) (p : ptr int) (k :Int32.int32) : int = get p k
+
+end
+
+
+module Cfloat
+ use int.Int
+ use real.Real
+
+ type float = abstract { to_real : real }
+ meta coercion function to_real
+
+ type f32 = < float 8 24 > (* single precision literals *)
+ type f64 = < float 11 53 > (* double precision literals *)
+
+ val function f32 (k : f32) : float
+ ensures { result = f32'real k }
+
+ val function f64 (k : f64) : float
+ ensures { result = f64'real k }
+
+ let constant zero = f64 0.0
+ let constant one = f64 1.0
+
+ val function (.+) (a b : float) : float
+ ensures { Real.( result = a + b ) }
+
+ val function (.-) (a b : float) : float
+ ensures { Real.( result = a - b ) }
+
+ val function (.-_) (a : float) : float
+ ensures { Real.( result = (- a) ) }
+
+ val function ( .* ) (a b : float) : float
+ ensures { Real.( result = a * b ) }
+
+ val function ( ./ ) (a b : float) : float
+ requires { b <> 0.0 }
+ ensures { Real.( result = a / b ) }
+
+ val predicate ( .= ) (a b : float) : bool
+ ensures { result <-> Real.( a = b ) }
+
+ val predicate ( .< ) (a b : float) : bool
+ ensures { result <-> Real.( a < b ) }
+
+ val predicate ( .<= ) (a b : float) : bool
+ ensures { result <-> Real.( a <= b ) }
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/std/proof.json
new file mode 100644
index 00000000..5cdde76b
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/std/proof.json
@@ -0,0 +1,28 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "Cfloat": {
+ "one": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 }
+ },
+ "Cint": { "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.026 } },
+ "Clib": {
+ "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "mixfix []<-": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "slice": { "prover": "alt-ergo@2.5.4", "time": 0.01 },
+ "slice_append": { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ "valid_in_range": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "vector_push": { "prover": "z3@4.13.4", "time": 0.027 }
+ },
+ "Int": {
+ "euclide": { "prover": "z3@4.13.4", "time": 0.02 },
+ "get_dim": { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ "mult_bound": { "prover": "z3@4.13.4", "time": 0.012 }
+ },
+ "List": { "map_concat": { "prover": "alt-ergo@2.5.4", "time": 0.017 } }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/std/why3session.xml.bak
new file mode 100644
index 00000000..562e22e5
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/std/why3session.xml.bak
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/std/why3shapes.gz.bak
new file mode 100644
index 00000000..cc61e1dc
Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/std/why3shapes.gz.bak differ
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor.mlw b/safety-related-profile/documents/c_code_generation/conv_op/tensor.mlw
new file mode 100644
index 00000000..e137cdaf
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/tensor.mlw
@@ -0,0 +1,318 @@
+(** Formalization of coordinates and dimensions *)
+
+module Range
+ use int.Int
+ use std.List
+
+ function size (ds : list int) : int =
+ match ds with
+ | Nil -> 1
+ | Cons d ds -> d * size ds
+ end
+
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+
+ let rec lemma valid_push (ks ds : list int) (k d : int)
+ requires { 0 <= k < d }
+ requires { valid ks ds }
+ ensures { valid (push ks k) (push ds d) }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Nil , Nil -> ()
+ | Cons _ ks , Cons _ ds -> valid_push ks ds k d
+ | _ -> absurd
+ end
+ (*qed*)
+
+ let rec lemma size_append (xs ys : list int)
+ ensures { size (xs ++ ys) = size xs * size ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end
+ (*qed*)
+
+ lemma size_push: forall xs x. size (push xs x) = size xs * x
+
+ let rec lemma positive_size (ds : list int)
+ requires { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ds with Nil -> () | Cons _ ds -> positive_size ds end
+ (*qed*)
+
+ let rec lemma positive_valid (ks ds : list int)
+ requires { valid ks ds }
+ ensures { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons _ ks , Cons _ ds -> positive_valid ks ds
+ | _ -> ()
+ end
+ (*qed*)
+
+end
+
+(** Formalization of Tensor *)
+module Tensor
+ use int.Int
+ use map.Map
+ use list.List
+ use Range
+
+ type data 'a = map (list int) 'a
+
+ type tensor 'a = {
+ dims : list int ;
+ data : data 'a ;
+ background : 'a ; (* default value, or value for 0-dimensions tensor *)
+ }
+ invariant { positive dims }
+ invariant { forall k. valid k dims \/ data k = background }
+ (*proof*)
+ by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+ (*qed*)
+
+ meta coercion function dims
+ meta coercion function data
+
+ (** Tensor with the same dimensions *)
+ predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims
+
+ (** Tensor with the same dimensions and background value *)
+ predicate (~=) (a : tensor 'a) (b : tensor 'a) =
+ a ~ b /\ a.background = b.background
+
+ (** Equal tensors *)
+ predicate (==) (a b : tensor 'a) =
+ a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k
+
+ (** Extensionality *)
+ lemma exteq: forall a b : tensor 'a. a == b <-> a = b
+ (*proof*) by tensor'eq a b (*qed*)
+
+ (** Scalar Tensor *)
+ let ghost function scalar (v : 'a) : tensor 'a
+ ensures { result.dims = Nil }
+ ensures { result.background = v }
+ ensures { forall k. result k = v }
+ (*proof*)
+ = { dims = Nil ; data = (fun _ -> v) ; background = v }
+ (*qed*)
+
+ (** Null Tensor *)
+ let ghost function zero (e : 'a) (ds : list int) : tensor 'a
+ requires { positive ds }
+ ensures { result.dims = ds }
+ ensures { result.background = e }
+ ensures { forall k. result k = e }
+ (*proof*)
+ = { dims = ds ; data = (fun _ -> e) ; background = e }
+ (*qed*)
+
+
+ (** Constant Tensor *)
+ let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a
+ ensures { result.dims = ds }
+ requires { positive ds }
+ ensures { result.background = bg }
+ ensures { forall k. valid k ds -> result k = v }
+ (*proof*)
+ = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg }
+ (*qed*)
+
+ (** Constant & Null *)
+ goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds
+
+end
+
+(** OP-Where Tensor Operation *)
+module OPWhere
+ use Tensor
+
+ let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+
+end
+
+module OPAdd
+ use Tensor
+ use int.Int
+ use real.Real
+ use std.Cfloat
+
+ let ghost function dadd (a b : data real) : data real
+ = fun ks -> a ks + b ks
+
+
+ let ghost function opadd (a b : tensor real) : tensor real
+ requires { a ~= b }
+ ensures { result ~= a }
+ ensures { result.data = dadd a.data b.data }
+ = { dims = a.dims ;
+ data = dadd a.data b.data ;
+ background = a.background + b.background }
+
+end
+
+module OPConv2d
+ use Tensor
+ use Range
+ use int.Int
+ use real.Real
+ use std.List
+ use std.Int
+ use std.Cint
+ use list.List
+ use list.Length
+ use list.Nth
+ use std.Cfloat
+ use ref.Ref
+
+
+
+(* ===== SECTION 1: Helper function for padding (Zero-Padding) ===== *)
+
+(** A helper function to access the padded input tensor value. *)
+(* Goal: Retrieves a value from the input tensor X, accounting for zero-padding.
+ * If the effective coordinates (x_h, x_w), after removing padding, fall outside the original tensor bounds, it returns 0.0.
+ * Inputs:
+ * x: data real - The input tensor data function.
+ * x_dims: list int - The dimensions of the input tensor X [N, C, H, W].
+ * pad_top, pad_left: int - The padding applied to the top/left of the height/width dimensions.
+ * n, c: int - Batch and input channel indices.
+ * x_h, x_w: int - The coordinates in the conceptually padded input space (derived from output position, stride, and dilation).
+ * Outputs: real - The value from the input tensor, or 0.0 if padded. *)
+ let ghost function padded_value (x : data real) (x_dims : list int)
+ (pad_top pad_left : int) (n c x_h x_w : int) : real =
+ requires { length x_dims = 4 }
+ let padded_h = x_h - pad_top in (* Convert padded index to original index *)
+ let padded_w = x_w - pad_left in (* Convert padded index to original index *)
+ let padded_ks = Cons n (Cons c (Cons padded_h (Cons padded_w Nil))) in
+ if valid padded_ks x_dims then
+ x padded_ks (* Return value if coordinates are within bounds *)
+ else
+ 0.0 (* Return 0.0 if coordinates are out of bounds *)
+
+(* --- *)
+
+(* ===== SECTION 2: Data-level Convolution (Weighted Sum) ===== *)
+
+(** The main 2D convolution data operation. *)
+(* Goal: Defines the function that computes the value of a single output pixel Y[n, m, y_h, y_w].
+ * This is achieved by iterating over all input channels (c), kernel height (i), and kernel width (j), performing the dot product, and adding the bias.
+ * Inputs: Tensors (data functions) X, W, B, their dimensions, and all convolution attributes.
+ * Outputs: data real - The data function for the output tensor Y. *)
+ let ghost function dconv2d (x : data real) (x_dims : list int)
+ (w : data real) (w_dims : list int) (b : data real) (b_dims : list int)
+ (pad_top pad_bottom pad_left pad_right : int) (dil_h dil_w : int)
+ (str_h str_w : int) : data real
+ requires { length x_dims = 4 } (* X shape: [N, C, H, W] *)
+ requires { length w_dims = 4 } (* W shape: [M, C, kH, kW] where M=C_out *)
+ requires { length b_dims = 1 } (* [M, C, kH, kW] *)
+ requires { get_dim x_dims 1 = get_dim w_dims 1 } (* C_in = C_kernel *)
+ requires { get_dim w_dims 0 = get_dim b_dims 0 } (* M = M_bias (Output Channels must match Bias size) *)
+ requires { str_h > 0 /\ str_w > 0 }
+ requires { dil_h > 0 /\ dil_w > 0 }
+ = fun yks -> (* Input 'yks' is the output coordinate list: [n, m, y_h, y_w] *)
+ if length yks <> 4 then 0.0 else
+ let Cons n (Cons m (Cons y_h (Cons y_w Nil))) = yks in (* Destructure output coordinates *)
+ let c_in = get_dim w_dims 1 in (* Number of input channels *)
+ let kh = get_dim w_dims 2 in (* Kernel height *)
+ let kw = get_dim w_dims 3 in (* Kernel width *)
+
+ let sum_value = ref 0.0 in (* Accumulator for the convolution sum *)
+ for c = 0 to c_in - 1 do (* Loop over input channels *)
+ invariant { true }
+ for i = 0 to kh - 1 do (* Loop over kernel height (i) *)
+ invariant { true }
+ for j = 0 to kw - 1 do (* Loop over kernel width (j) *)
+ invariant { true }
+ (* Calculate corresponding H/W coordinate in the padded input X *)
+ let x_h = y_h * str_h + i * dil_h in
+ let x_w = y_w * str_w + j * dil_w in
+ let x_val = padded_value x x_dims pad_top pad_left n c x_h x_w in (* Get padded input value *)
+ let w_coords = Cons m (Cons c (Cons i (Cons j Nil))) in
+ let w_val = w w_coords in (* Get kernel weight value *)
+ sum_value := !sum_value + (x_val * w_val) (* Accumulate the product *)
+ done
+ done
+ done;
+ let bias_coords = Cons m Nil in
+ let bias_val = b bias_coords in (* Get the bias value for output channel m *)
+ !sum_value + bias_val (* Final output value = Weighted Sum + Bias *)
+
+(* --- *)
+
+(* ===== SECTION 3: Tensor-level Convolution Operator and Shape Calculation ===== *)
+(** The tensor-level 2D convolution operator. *)
+(* Goal: Constructs the final output tensor Y, calculating its dimensions and assigning the dconv2d data function.
+ * Inputs: The three tensors X, W, B, and all convolution attributes.
+ * Outputs: tensor real - The resulting output tensor Y [N_out, C_out, H_out, W_out]. *)
+ let ghost function opconv2d (x w b : tensor real)
+ (pad_top pad_bottom pad_left pad_right : int) (dil_h dil_w : int)
+ (str_h str_w : int) : tensor real
+ requires { length x.dims = 4 } (* [N, C, H, W] *)
+ requires { length w.dims = 4 } (* [M, C, kH, kW] *)
+ requires { length b.dims = 1 } (* [M] *)
+ requires { get_dim x.dims 1 = get_dim w.dims 1 } (* C_in = C_kernel *)
+ requires { get_dim w.dims 0 = get_dim b.dims 0 } (* M = M_bias *)
+ requires { str_h > 0 /\ str_w > 0 }
+ requires { dil_h > 0 /\ dil_w > 0 }
+ requires { pad_top >= 0 /\ pad_bottom >= 0 /\ pad_left >= 0 /\ pad_right >= 0 }
+ ensures { length result.dims = 4 }
+ ensures { get_dim result.dims 0 = get_dim x.dims 0 } (* N_out = N_in *)
+ ensures { get_dim result.dims 1 = get_dim w.dims 0 } (* C_out = M *)
+ (* Main ensures clause: the output data must be exactly the function dconv2d *)
+ ensures {result.data = dconv2d x.data x.dims w.data w.dims b.data b.dims
+ pad_top pad_bottom pad_left pad_right dil_h dil_w str_h str_w}
+ (*proof*)
+ = let n = get_dim x.dims 0 in
+ let c_in = get_dim x.dims 1 in
+ let h_in = get_dim x.dims 2 in
+ let w_in = get_dim x.dims 3 in
+ let m_out = get_dim w.dims 0 in
+ let kh = get_dim w.dims 2 in
+ let kw = get_dim w.dims 3 in
+
+ (* Calculate Effective Kernel Size (with dilation) *)
+ let effective_kh = (kh - 1) * dil_h + 1 in
+ let effective_kw = (kw - 1) * dil_w + 1 in
+ (* Calculate Output Height (H_out) using the convolution formula:
+ * H_out = floor((H_in + P_top + P_bottom - effective_kH) / S_h) + 1 *)
+ let out_h = (h_in + pad_top + pad_bottom - effective_kh) ./ (str_h + 1) in
+ (* Calculate Output Width (W_out) *)
+ let out_w = (w_in + pad_left + pad_right - effective_kw) ./ (str_w + 1) in
+ let output_dims = Cons n (Cons m_out (Cons out_h (Cons out_w Nil))) in
+ (* Return the final output tensor record *)
+ { dims = output_dims ;
+ data = dconv2d x.data x.dims w.data w.dims b.data b.dims
+ pad_top pad_bottom pad_left pad_right dil_h dil_w str_h str_w ;
+
+ background = 0.0 }
+
+end
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/tensor/proof.json
new file mode 100644
index 00000000..0c02b926
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/tensor/proof.json
@@ -0,0 +1,256 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 30, "time": 0.14 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "OPAdd": {
+ "dadd": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "opadd": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ null,
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.01 }
+ ]
+ }
+ },
+ "OPConv2d": {
+ "dconv2d": { "prover": "alt-ergo@2.5.4", "time": 0.113 },
+ "opconv2d": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ null,
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.197 },
+ { "prover": "z3@4.13.4", "time": 0.047 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ ]
+ },
+ "padded_value": { "prover": "alt-ergo@2.5.4", "time": 0.02 }
+ },
+ "OPWhere": {
+ "dwhere": { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ },
+ "OpConv2D": {
+ "conv2d": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ null,
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ null,
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.2 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.606 }
+ ]
+ },
+ "conv_at_position_dilated": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 }, null
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 }, null
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 }, null
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 }
+ ]
+ },
+ "conv_sum_dilated": {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 }, null
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 }, null
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.015 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 }
+ ]
+ },
+ { "prover": "cvc5@1.2.1", "time": 0.029 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.014 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ null, { "prover": "alt-ergo@2.5.4", "time": 0.018 }
+ ]
+ }
+ ]
+ },
+ null
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ ]
+ }
+ },
+ "Range": {
+ "positive_size": { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ "positive_valid": { "prover": "alt-ergo@2.5.4", "time": 0.033 },
+ "size_append": { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ "size_push": { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ "valid_push": { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ },
+ "Tensor": {
+ "const": { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ "exteq": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 }
+ ]
+ },
+ "scalar": { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "zero_is_const": { "prover": "alt-ergo@2.5.4", "time": 0.014 }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3session.xml.bak
new file mode 100644
index 00000000..6b8f7fcb
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3session.xml.bak
@@ -0,0 +1,182 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3shapes.gz.bak
new file mode 100644
index 00000000..1f0f73ed
Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/documents/c_code_generation/conv_op/why3find.json b/safety-related-profile/documents/c_code_generation/conv_op/why3find.json
new file mode 100644
index 00000000..fb0612dc
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/conv_op/why3find.json
@@ -0,0 +1,10 @@
+{
+ "fast": 0.2,
+ "time": 1.0,
+ "depth": 4,
+ "packages": [],
+ "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ],
+ "tactics": [ "split_vc", "inline_goal" ],
+ "drivers": [],
+ "warnoff": []
+}
diff --git a/safety-related-profile/documents/c_code_generation/sources.md b/safety-related-profile/documents/c_code_generation/sources.md
new file mode 100644
index 00000000..94c56df6
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/sources.md
@@ -0,0 +1,151 @@
+# Converting Why3 Specifications to C Code
+
+This document outlines how to extract C code from Why3 (`.mlw`) specifications using the Why3 extraction mechanism and a C driver. It includes an example, supported features, and key constraints.
+
+## Compiling Why3 to C language
+### Example
+
+This example defines a function `locate_max` that returns the index of the maximum element in an array `a` of size `n`.
+
+```whyml
+use int.Int
+use map.Map as Map
+use mach.c.C
+use mach.int.Int32
+use mach.int.Int64
+
+function ([]) (a: ptr 'a) (i: int): 'a = Map.get a.data.Array.elts (a.offset + i)
+
+let locate_max (a: ptr int64) (n: int32): int32
+ requires { 0 < n }
+ requires { valid a n }
+ ensures { 0 <= result < n }
+ ensures { forall i. 0 <= i < n -> a[i] <= a[result] }
+= let ref idx = 0 in
+ for j = 1 to n - 1 do
+ invariant { 0 <= idx < n }
+ invariant { forall i. 0 <= i < j -> a[i] <= a[idx] }
+ if get_ofs a idx < get_ofs a j then idx <- j
+ done;
+ idx
+ ```
+
+### Extraction Command
+```bash
+why3 extract -D c locate_max.mlw
+```
+With debug details:
+```bash
+why3 extract -D c --debug-all locate_max.mlw -o locate_max.c
+```
+### Output C code
+```c
+#include
+
+int32_t locate_max(int64_t * a, int32_t n) {
+ int32_t idx;
+ int32_t j, o;
+ idx = 0;
+ o = n - 1;
+ if (1 <= o) {
+ for (j = 1; ; ++j) {
+ if (a[idx] < a[j]) {
+ idx = j;
+ }
+ if (j == o) break;
+ }
+ }
+ return idx;
+}
+```
+
+## Supporte Features and Rules
+### Basic Types
+
+| WhyML Type | Description | C Equivalent |
+|----------------------|------------------------------------|------------------------|
+| `int32`, `uint64`, etc. | From `mach.int` | `int32_t`, `uint64_t`, etc. |
+| `bool` | Translated from `bool.Bool` | `int` |
+| `char`, `string` | Partial support via `mach.c.String`| `char`, `char *` |
+| `int` (mathematical) | ❌ Not supported | — |
+| `float` | ❌ Not supported | — |
+---
+
+### Compound Types
+#### Immutable Records
+
+**WhyML:**
+```whyml
+type r = { x : int32; y : int32 }
+let swap (a : r) : r = { x = a.y ; y = a.x }
+```
+**C:**
+```c
+struct r {
+ int32_t x;
+ int32_t y;
+};
+
+struct r swap(struct r a) {
+ struct r r;
+ r.x = a.y;
+ r.y = a.x;
+ return r;
+}
+```
+
+#### Mutable Records
+**WhyML:**
+```whyml
+type r = { mutable x : int32; mutable y : int32 }
+let swap (a : r) : unit =
+ let tmp = a.y in a.y <- a.x; a.x <- tmp
+```
+**C:**
+```c
+struct r {
+ int32_t x;
+ int32_t y;
+};
+
+void swap(struct r * a) {
+ int32_t tmp;
+ tmp = a->y;
+ a->y = a->x;
+ a->x = tmp;
+}
+```
+## Unsupported Features
+- WhyML arrays
+
+- Algebraic data types (including enumerations)
+
+- Pattern matching
+
+- Exception raising and catching
+
+- Floating-point types
+
+## Control Stuctures
+
+| Feature | Support |
+|----------------------------|---------|
+| `if` / `else` | ✅ |
+| `while` loops | ✅ |
+| `for` loops | ✅ |
+| Sequence (`;`) | ✅ |
+| `break`, `continue`, `return` | ✅ |
+| Pattern matching | ❌ |
+| Exceptions | ❌ |
+
+## Notes
+- **Example of driver file used for C extraction**: [`c.drv`](https://gitlab.inria.fr/why3/why3/-/blob/master/drivers/c.drv)
+- This driver defines how WhyML types and functions are translated into equivalent C constructs.
+
+## References
+
+- [Why3 Documentation](https://why3.lri.fr/doc/)
+- [Why3 GitLab Repository](https://gitlab.inria.fr/why3/why3)
+
+
+
diff --git a/safety-related-profile/documents/c_code_generation/tensor/.gitignore b/safety-related-profile/documents/c_code_generation/tensor/.gitignore
new file mode 100644
index 00000000..f12c3561
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/.gitignore
@@ -0,0 +1,5 @@
+.why3find
+why3session.xml
+why3shapes.gz
+/lib
+/html
diff --git a/safety-related-profile/documents/c_code_generation/tensor/Makefile b/safety-related-profile/documents/c_code_generation/tensor/Makefile
new file mode 100644
index 00000000..4ca6970b
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/Makefile
@@ -0,0 +1,37 @@
+# Makefile for libvector
+
+.PHONY: all prove update doc lib cc clean
+
+all: prove doc lib
+ @echo "------------------------------------------"
+
+prove:
+ @echo "------------------------------------------"
+ @echo "--- Proofs"
+ @echo "------------------------------------------"
+ @why3find prove *.mlw -l -s -x
+
+update:
+ why3find prove *.mlw -l -s -x -m
+
+doc:
+ @echo "------------------------------------------"
+ @echo "--- Documentation"
+ @echo "------------------------------------------"
+ @rm -fr html
+ @why3find doc *.mlw -t "Tensor Library"
+
+EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib
+
+lib:
+ @echo "------------------------------------------"
+ @echo "--- Extraction"
+ @echo "------------------------------------------"
+ @rm -fr lib
+ @why3 extract $(EXTRACT) libvector.CIndex
+ @why3 extract $(EXTRACT) libtensor.CTensor
+ @cd lib && gcc -c *.c
+ @find lib -type f | sort
+
+clean:
+ rm -fr lib html
diff --git a/safety-related-profile/documents/c_code_generation/tensor/README.md b/safety-related-profile/documents/c_code_generation/tensor/README.md
new file mode 100644
index 00000000..cb07f3ad
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/README.md
@@ -0,0 +1,93 @@
+# ONNX-C
+
+
+
+## Getting started
+
+To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+
+Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+
+## Add your files
+
+- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
+- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
+
+```
+cd existing_repo
+git remote add origin https://git.frama-c.com/lcorrenson/onnx-c.git
+git branch -M main
+git push -uf origin main
+```
+
+## Integrate with your tools
+
+- [ ] [Set up project integrations](https://git.frama-c.com/lcorrenson/onnx-c/-/settings/integrations)
+
+## Collaborate with your team
+
+- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
+- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
+- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
+- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
+- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
+
+## Test and Deploy
+
+Use the built-in continuous integration in GitLab.
+
+- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
+- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
+- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
+- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
+- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+
+***
+
+# Editing this README
+
+When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
+
+## Suggestions for a good README
+
+Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+
+## Name
+Choose a self-explaining name for your project.
+
+## Description
+Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
+
+## Badges
+On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
+
+## Visuals
+Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
+
+## Installation
+Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
+
+## Usage
+Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+
+## Support
+Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+
+## Roadmap
+If you have ideas for releases in the future, it is a good idea to list them in the README.
+
+## Contributing
+State if you are open to contributions and what your requirements are for accepting them.
+
+For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+
+You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+
+## Authors and acknowledgment
+Show your appreciation to those who have contributed to the project.
+
+## License
+For open source projects, say how it is licensed.
+
+## Project status
+If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
diff --git a/safety-related-profile/documents/c_code_generation/tensor/cdriver.drv b/safety-related-profile/documents/c_code_generation/tensor/cdriver.drv
new file mode 100644
index 00000000..b2ac835a
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/cdriver.drv
@@ -0,0 +1,35 @@
+module std.Clib
+ interface export "#include "
+ interface export "#include "
+ syntax val to_uint32 "(uint32_t) %1"
+ syntax val is_null "!%1" prec 0
+ syntax val ([]) "%1[%2]" prec 1 1 15
+ syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14
+end
+
+module std.Cfloat
+ syntax type float "double"
+ syntax val zero "0.0"
+ syntax val one "1.0"
+ syntax val f32 "(double) %1"
+ syntax val f64 "(double) %1"
+ syntax val ( .+ ) "%1 + %2" prec 8 8 7
+ syntax val ( .- ) "%1 - %2" prec 8 8 7
+ syntax val (.-_) "- %1" prec 5 4
+ syntax val ( .* ) "%1 * %2" prec 7 7 6
+ syntax val ( ./ ) "%1 / %2" prec 7 7 6
+ syntax val ( .= ) "%1 = %2" prec 11 11 10
+ syntax val ( .< ) "%1 < %2" prec 11 11 10
+ syntax val ( .<= ) "%1 <= %2" prec 11 11 10
+end
+
+module libvector.CIndex
+ interface "#include "
+ interface "#include "
+end
+
+module libtensor.CTensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+end
diff --git a/safety-related-profile/documents/c_code_generation/tensor/documentation.md b/safety-related-profile/documents/c_code_generation/tensor/documentation.md
new file mode 100644
index 00000000..59d987cc
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/documentation.md
@@ -0,0 +1,128 @@
+# Formally Verified C Tensor Library
+
+This project is a provably correct tensor library written in C, with its correctness formally verified using **Why3** and its **WhyML language**.
+The primary goal is to demonstrate how formal verification can be applied to ensure that low-level C code for numerical operations behaves exactly as intended by a high-level mathematical model.
+
+---
+
+## Project Structure
+
+These files provide a **formal verification system** for a tensor library, primarily using the **WhyML language** with **Why3**.
+The goal is to **prove the correctness of C-like tensor operations**.
+This repository contains:
+ - Formal specification files (`.mlw`)
+ - The C driver file (`.drv`)
+ - A `Makefile` to automate the process
+
+### File Breakdown
+
+#### `tensor.mlw`
+This file is the **core of the formalization**, defining the fundamental mathematical concepts and data structures for tensors.
+It serves as a **foundational library** for other modules.
+
+- **Range module**
+ - `size`: Calculates the total number of elements in a tensor (recursive multiplication of dimensions).
+ - `positive`: Predicate that checks all dimensions are positive integers.
+ - `valid`: Verifies coordinates are within the valid range for given dimensions.
+
+- **Tensor module**
+ - `data`: Represents tensor elements as a map from coordinates to values.
+ - `tensor`: Record with dimensions (`dims`), data (`data`), and background value.
+ - **Predicates**: `~`, `~=`, `==` define equality and similarity notions for tensors.
+ - **Ghost Functions**: they are for verification only and won't be compiled to C
+ - `scalar`: Single-valued tensor
+ - `zero`: Tensor of all zeros
+ - `const`: Tensor with a constant value
+
+---
+
+#### `layout.mlw`
+This file (module **CFlat**) focuses on the **memory layout of tensors**.
+
+- **Functions**
+ - `offset`: Maps multidimensional coordinates to a flat memory address (generalized row-major offset).
+ - `index`: Inverse of `offset`, mapping an address back to coordinates.
+
+- **Lemmas**
+ - `index_of_offset`: Proves correctness and reversibility of `offset` and `index`.
+
+---
+
+#### `libvector.mlw`
+This module bridges **abstract concepts** with **concrete C arrays**.
+
+- **Types and Functions**
+ - `iarray`: Pointer to a C array of 32-bit integers.
+ - `ivector`, `islice`: Convert `iarray` to WhyML lists.
+ - `cdim_size`: C implementation of total size calculation, proven equivalent to the abstract definition.
+ - `coffset`: C implementation of memory offset, proven equivalent to `layout.mlw`’s `offset`.
+
+---
+
+#### `libtensor.mlw`
+Provides a **C-like representation of tensors** and proofs linking them to abstract models.
+
+- **Types**
+ - `ctensor`: Concrete C representation (rank, dimensions array, data array).
+
+- **Ghost Functions**
+ - `tensor`, `tensorb`: Convert `ctensor` into abstract `tensor`.
+
+- **C Functions (with proofs)**
+ - `ctensor_clear`: Fills tensor with zeros → proven equivalent to `Tensor.zero`.
+ - `ctensor_reset`: Fills tensor with constant → proven equivalent to `Tensor.const`.
+ - `ctensor_where`: Conditional element assignment → proven equivalent to `opwhere`.
+
+---
+
+#### `cdriver.drv`
+A **Why3 driver file** that specifies mappings from WhyML to C.
+
+- **std.Clib**: Maps standard C library functions (`stdlib.h`, `stdint.h`).
+- **std.Cfloat**: Maps floating-point operations (`+`, `-`, `*`, `/`) to C equivalents.
+- **Other modules**: Headers like `cindex.h` for extracted code.
+
+---
+
+#### `Makefile`
+Automates the **build and verification process**.
+
+- `prove`: Runs Why3 proofs on all `.mlw` files.
+- `doc`: Generates HTML documentation.
+- `lib`: Extracts verified WhyML to C and compiles into a library.
+- `clean`: Removes generated files.
+
+---
+### Dependency Diagram
+
+
+
+---
+
+## Building and Verification
+
+The `Makefile` simplifies the entire process with a few commands:
+
+### Prove all properties
+```bash
+make prove
+```
+This command runs the Why3 proof engine on all the .mlw files to check that all logical properties and correctness conditions hold.
+### Generate the C library:
+```bash
+make lib
+```
+This command first extracts the verified WhyML code into C source files and then compiles them into a library.
+
+### Generate documentation
+```bash
+make doc
+```
+This command creates an HTML documentation from the formal specifications in the WhyML files.
+
+### Clean the project
+```bash
+make clean
+```
+Removes all generated files, including the C library and documentation. and insert the figure in the right place
+
diff --git a/safety-related-profile/documents/c_code_generation/tensor/images/diagram.png b/safety-related-profile/documents/c_code_generation/tensor/images/diagram.png
new file mode 100644
index 00000000..d2b2d284
Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/tensor/images/diagram.png differ
diff --git a/safety-related-profile/documents/c_code_generation/tensor/layout.mlw b/safety-related-profile/documents/c_code_generation/tensor/layout.mlw
new file mode 100644
index 00000000..fd7bcf7b
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/layout.mlw
@@ -0,0 +1,91 @@
+module CFlat
+ use std.Int
+ use std.List
+ use tensor.Range
+
+ function offset (ks ds : list int) : int =
+ match ks , ds with
+ | Cons k ks , Cons _ ds -> k * size ds + offset ks ds
+ | _ -> 0
+ end
+
+ function index (p : int) (ds : list int) : list int =
+ match ds with
+ | Nil -> Nil
+ | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds)
+ end
+
+ let rec lemma offset_size (ks ds : list int)
+ requires { valid ks ds }
+ ensures { 0 <= offset ks ds < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k krs , Cons d drs ->
+ offset_size krs drs ;
+ let p = offset ks ds in
+ let q = offset krs drs in
+ assert { p = size drs * k + q } ;
+ assert {
+ size drs * k
+ <= size drs * (d - 1)
+ = d * size drs - size drs
+ } ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma index_range (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { valid (index p ds) ds }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds -> index_range (mod p (size rds)) rds
+ end
+ (*qed*)
+
+ let rec lemma index_of_offset (ks ds : list int)
+ requires { valid ks ds }
+ ensures { index (offset ks ds) ds = ks }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k rks , Cons _ rds ->
+ index_of_offset rks rds ;
+ let p = offset ks ds in
+ let n = size rds in
+ let q = offset rks rds in
+ euclide p k n q ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma offset_of_index (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { offset (index p ds) ds = p }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds ->
+ let n = size rds in
+ offset_of_index (mod p n) rds
+ end
+ (*qed*)
+
+ let rec lemma offset_push (ks ds : list int) (k d : int)
+ requires { valid ks ds }
+ ensures { offset (push ks k) (push ds d) = offset ks ds * d + k }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Cons _ krs , Cons _ drs -> offset_push krs drs k d
+ | _ -> ()
+ end
+ (*qed*)
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/tensor/layout/proof.json b/safety-related-profile/documents/c_code_generation/tensor/layout/proof.json
new file mode 100644
index 00000000..0b07d2c9
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/layout/proof.json
@@ -0,0 +1,39 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "CFlat": {
+ "index_of_offset": { "prover": "cvc5@1.2.1", "time": 0.055 },
+ "index_range": { "prover": "alt-ergo@2.5.4", "time": 0.088 },
+ "offset_of_index": { "prover": "alt-ergo@2.5.4", "time": 0.16 },
+ "offset_push": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.075 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.01 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 1.6 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.049 }
+ ]
+ }
+ ]
+ },
+ "offset_size": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.01 },
+ { "prover": "z3@4.13.4", "time": 0.01 },
+ { "prover": "cvc5@1.2.1", "time": 0.137 }
+ ]
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/tensor/libtensor.mlw b/safety-related-profile/documents/c_code_generation/tensor/libtensor.mlw
new file mode 100644
index 00000000..c150fcb5
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/libtensor.mlw
@@ -0,0 +1,140 @@
+module CTensor
+ use std.Int
+ use std.List
+ use std.Clib
+ use std.Cfloat
+ use mach.int.Int32
+ use tensor.Range
+ use tensor.Tensor
+ use tensor.OPWhere
+ use layout.CFlat
+ use libvector.CIndex
+
+ type farray = ptr float
+
+ type ctensor = {
+ t_rank : int32 ;
+ t_dims : iarray ;
+ t_data : farray ;
+ }
+
+ function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank
+ function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank
+ predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t)
+ predicate empty_tensor (t : ctensor) = t.t_rank = 0
+
+ predicate valid_tensor (t : ctensor) =
+ dimension t.t_dims t.t_rank /\
+ valid_range t.t_data 0 (tensor_size t) /\
+ writable t.t_data
+
+ function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t)
+
+ function tensor_value_at (k : list int) (t : ctensor) : real =
+ if valid_index k t then
+ value_at t.t_data (tensor_offset k t)
+ else 0.0
+
+ function tensor_value (t : ctensor) : list int -> real =
+ fun k -> tensor_value_at k t
+
+ function tensor_valueb (t : ctensor) : list int -> bool =
+ fun k -> Real.(tensor_value_at k t > 0.0)
+
+ let ghost function tensor (t : ctensor) : tensor real
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_value t }
+ ensures { result.background = 0.0 }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_value t } ;
+ background = 0.0 ;
+ }
+
+ let ghost function tensorb (t : ctensor) : tensor bool
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_valueb t }
+ ensures { result.background = false }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_valueb t } ;
+ background = false ;
+ }
+
+ let ctensor_create (ds : iarray) (n : int32) : ctensor =
+ (*proof*)
+ requires { dimension ds n }
+ ensures { empty_tensor result \/ valid_tensor result }
+ ensures { empty_tensor result \/ result.t_rank = n }
+ ensures { empty_tensor result \/ result.t_dims = ds }
+ (*qed*)
+ let m = cdim_size ds n in
+ let vs = malloc (to_uint32 m) in
+ {
+ t_rank = if is_null vs then 0 else n ;
+ t_dims = ds ;
+ t_data = vs ;
+ }
+
+ let ctensor_clear (r : ctensor) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 }
+ (*qed*)
+ r.t_data[i] <- f32 0.0
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_reset (r : ctensor) (v : float) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = v }
+ (*qed*)
+ r.t_data[i] <- v
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_where (cond a b r : ctensor) =
+ (*proof*)
+ requires { valid_tensor cond }
+ requires { valid_tensor a }
+ requires { valid_tensor b }
+ requires { valid_tensor r }
+ requires { tensor a ~= tensor b ~= tensor r }
+ (*qed*)
+ requires { tensorb cond ~ tensor a ~ tensor b }
+ ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant {
+ forall k. 0 <= k < i ->
+ value_at r.t_data k =
+ if Real.(0.0 < value_at cond.t_data k)
+ then a.t_data[k] else b.t_data[k]
+ }
+ (*qed*)
+ r.t_data[i] <-
+ if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i]
+ done
+ (*proof*)
+ ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) }
+ (*qed*)
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/tensor/libtensor/proof.json b/safety-related-profile/documents/c_code_generation/tensor/libtensor/proof.json
new file mode 100644
index 00000000..94f77db9
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/libtensor/proof.json
@@ -0,0 +1,46 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 30, "time": 0.14 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.215 }
+ ],
+ "proofs": {
+ "CTensor": {
+ "ctensor_clear": { "prover": "alt-ergo@2.5.4", "time": 0.157 },
+ "ctensor_create": { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ "ctensor_reset": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.055 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ ]
+ },
+ "ctensor_where": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.061 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.062 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.062 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.044 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.063 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.22 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 }
+ ]
+ },
+ "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ "tensorb": { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/tensor/libvector.mlw b/safety-related-profile/documents/c_code_generation/tensor/libvector.mlw
new file mode 100644
index 00000000..79ff9b42
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/libvector.mlw
@@ -0,0 +1,135 @@
+(** C-Library to compute Offsets & Dimensions *)
+
+module CIndex
+ use std.Int
+ use std.List
+ use std.Clib
+ use mach.int.Int32
+ use tensor.Range
+ use layout.CFlat
+
+ type iarray = ptr int32
+
+ function ivector (u : iarray) (n : int) : list int
+ = map Int32.to_int (vector u n)
+
+ function islice (u : iarray) (p q : int) : list int
+ = map Int32.to_int (slice u p q)
+
+ (** ## Dimensions *)
+
+ function vdim (u : iarray) (n : int) : int = size (ivector u n)
+ function sdim (u : iarray) (p q : int) : int = size (islice u p q)
+
+ predicate pdim (u : iarray) (p q : int) =
+ forall k. p <= k < q -> 0 < value_at u k
+
+ predicate dimension (u : iarray) (n : int) =
+ 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32
+
+ (** Equivalence betwen {pdim} and {tensor.Range.positive}. *)
+ let rec lemma positive_pdim (u : iarray) (p q : int)
+ (*proof*)
+ ensures { pdim u p q <-> positive (islice u p q) }
+ variant { q - p }
+ = if p < q then positive_pdim u (p+1) q
+ (*qed*)
+
+ let ghost sdim_split (u : iarray) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { pdim u p q }
+ ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q }
+ = assert { islice u p k ++ islice u k q = islice u p q }
+ (*qed*)
+
+ (** Extracted Library *)
+
+ let cdim_size (u : iarray) (n : int32): int32 =
+ ensures { result = vdim u n }
+ (*proof*)
+ requires { dimension u n }
+ (*qed*)
+ begin
+ let ref p = 1 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { p = vdim u i }
+ ghost
+ begin
+ ensures { 1 <= p * u[i] <= max_int32 }
+ let ghost dl = pure { sdim u 0 i } in
+ let ghost di = Int32.to_int u[i] in
+ let ghost dr = pure { sdim u (i+1) n } in
+ let ghost dn = pure { sdim u 0 n } in
+ sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * u[i] ;
+ done ; p
+ end
+
+ let cdim_create_1 (n : int32) : iarray =
+ requires { 0 < n }
+ ensures { is_not_null result -> vdim result 1 = n }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 1 }
+ ensures { is_not_null result -> value_at result 0 = n }
+ (*qed*)
+ let cd = malloc 1 in
+ if is_not_null cd then cd[0] <- n ; cd
+
+ let cdim_create_2 (p q : int32) : iarray =
+ requires { 0 < p /\ 0 < q /\ p * q <= max_int32 }
+ ensures { is_not_null result -> vdim result 2 = p * q }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 2 }
+ ensures { is_not_null result -> value_at result 0 = p }
+ ensures { is_not_null result -> value_at result 1 = q }
+ (*qed*)
+ let cd = malloc 2 in
+ if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd
+
+ (** ## Coordinates *)
+
+ let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 =
+ (*proof*)
+ requires { 0 <= n }
+ requires { valid_range ks 0 n }
+ requires { dimension ds n }
+ ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) }
+ ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) }
+ (*qed*)
+ begin
+ let ref p = 0 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { valid (ivector ks i) (ivector ds i) }
+ invariant { p = offset (ivector ks i) (ivector ds i) }
+ (*qed*)
+ let d = ds[i] in
+ let k = ks[i] in
+ if 0 <= k && k < d then
+ begin
+ (*proof*)
+ assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ;
+ ghost
+ begin
+ ensures { Int32.in_bounds (p * d) }
+ ensures { Int32.in_bounds (p * d + k) }
+ let ghost dl = pure { sdim ds 0 i } in
+ let ghost di = Int32.to_int ds[i] in
+ let ghost dr = pure { sdim ds (i+1) n } in
+ let ghost dn = pure { sdim ds 0 n } in
+ sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * d + k ;
+ end
+ else return (-1)
+ done ; p
+ end
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/tensor/libvector/proof.json b/safety-related-profile/documents/c_code_generation/tensor/libvector/proof.json
new file mode 100644
index 00000000..be619b08
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/libvector/proof.json
@@ -0,0 +1,104 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "CIndex": {
+ "cdim_create_1": { "prover": "alt-ergo@2.5.4", "time": 0.194 },
+ "cdim_create_2": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.224 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 }
+ ]
+ },
+ "cdim_size": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.039 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.066 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.935 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.193 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 }
+ ]
+ },
+ "coffset": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.016 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.024 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.813 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.026 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.057 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.191 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 1.9 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.93 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.011 }
+ ]
+ },
+ "positive_pdim": { "prover": "alt-ergo@2.5.4", "time": 0.074 },
+ "sdim_split": { "prover": "alt-ergo@2.5.4", "time": 0.206 }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/tensor/std.mlw b/safety-related-profile/documents/c_code_generation/tensor/std.mlw
new file mode 100644
index 00000000..0a25aefd
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/std.mlw
@@ -0,0 +1,179 @@
+(* Extensions to Standard Library *)
+
+module Int
+ use export int.Int
+ use export int.Abs
+ use export int.ComputerDivision
+
+ (** Unicity of euclidian division decomposition *)
+ let ghost euclide (a b q r : int)
+ requires { 0 <= a /\ 0 <= r < q }
+ requires { a = b * q + r }
+ ensures { b = div a q }
+ ensures { r = mod a q }
+ (*proof*)
+ = let rec kernel (b r : int)
+ requires { b * q + r = 0 }
+ requires { abs r < q }
+ ensures { b = r = 0 }
+ variant { abs b }
+ = if b < 0 then kernel (b + 1) (r - q) else
+ if b > 0 then kernel (b - 1) (r + q) else ()
+ in kernel (b - div a q) (r - mod a q)
+ (*qed*)
+
+ (** Multiplication upper-bound *)
+ let ghost mult_bound (a b c : int)
+ requires { 0 < a * b <= c }
+ ensures { abs a <= c }
+ ensures { abs b <= c }
+ = ()
+
+end
+
+module List
+ use export list.List
+ use export list.Map
+ use export list.Append
+
+ (** Appends an element to the end of the list *)
+ function push (xs : list 'a) (x : 'a) : list 'a =
+ xs ++ Cons x Nil
+
+ let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a)
+ ensures { map f (xs ++ ys) = map f xs ++ map f ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end
+ (*qed*)
+
+end
+
+module Clib
+ use map.Map
+ use int.Int
+ use mach.int.Int32
+ use export mach.c.C
+
+ type int32 = Int32.int32
+ type uint32 = UInt32.uint32
+
+ val function to_uint32 (v : int32) : uint32
+ requires { 0 <= v }
+ ensures { Int32.to_int v = UInt32.to_int result }
+
+ (** Null-Pointer *)
+ val predicate is_null (vp : ptr 'a) : bool
+ ensures { result <-> vp.zone = null_zone }
+
+ (** Array-range validity *)
+ predicate valid_range (vp : ptr 'a) (p q : int) =
+ q <= p \/
+ ( 0 <= vp.min <= vp.offset /\
+ 0 <= p <= q <= max_int32 /\
+ 0 <= vp.min <= vp.offset + p /\
+ vp.offset + q <= vp.max <= vp.plength )
+
+ let lemma valid_in_range (vp : ptr 'a) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { valid_range vp p q }
+ ensures { valid_ptr_shift vp k }
+ = ()
+ (*qed*)
+
+ (** Array as map *)
+ let ghost function value_at (vp : ptr 'a) : map int 'a =
+ pure { fun k -> vp.data.Array.elts (vp.offset + k) }
+
+ (** Array access (ghost) *)
+ function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k
+
+ (** Array access (C, inlined) *)
+ let ([]) (vp : ptr 'a) (k : int32) : 'a
+ (*proof*)
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { result = vp[k] }
+ = get_ofs vp k
+ (*qed*)
+
+ (** Array update (C, inlined) *)
+ let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit
+ requires { writable vp }
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v }
+ = set_ofs vp k v
+
+ (** Array as list *)
+ use List
+
+ let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a =
+ (*proof*)
+ variant { q - p }
+ (*qed*)
+ if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q)
+
+ let rec lemma slice_append (u : ptr 'a) (p q r : int)
+ requires { p <= q <= r }
+ ensures { slice u p r = slice u p q ++ slice u q r }
+ (*proof*)
+ variant { q - p }
+ = if p < q then slice_append u (p+1) q r
+ (*qed*)
+
+ let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n
+
+ let lemma vector_push (u : ptr 'a) (n : int)
+ requires { 0 <= n }
+ ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+ (*proof*)
+ = slice_append u 0 n (n+1)
+ (*qed*)
+
+end
+
+module Cfloat
+ use int.Int
+ use real.Real
+
+ type float = abstract { to_real : real }
+ meta coercion function to_real
+
+ type f32 = < float 8 24 > (* single precision literals *)
+ type f64 = < float 11 53 > (* double precision literals *)
+
+ val function f32 (k : f32) : float
+ ensures { result = f32'real k }
+
+ val function f64 (k : f64) : float
+ ensures { result = f64'real k }
+
+ let constant zero = f64 0.0
+ let constant one = f64 1.0
+
+ val function (.+) (a b : float) : float
+ ensures { Real.( result = a + b ) }
+
+ val function (.-) (a b : float) : float
+ ensures { Real.( result = a - b ) }
+
+ val function (.-_) (a : float) : float
+ ensures { Real.( result = (- a) ) }
+
+ val function ( .* ) (a b : float) : float
+ ensures { Real.( result = a * b ) }
+
+ val function ( ./ ) (a b : float) : float
+ requires { b <> 0.0 }
+ ensures { Real.( result = a / b ) }
+
+ val predicate ( .= ) (a b : float) : bool
+ ensures { result <-> Real.( a = b ) }
+
+ val predicate ( .< ) (a b : float) : bool
+ ensures { result <-> Real.( a < b ) }
+
+ val predicate ( .<= ) (a b : float) : bool
+ ensures { result <-> Real.( a <= b ) }
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/tensor/std/proof.json b/safety-related-profile/documents/c_code_generation/tensor/std/proof.json
new file mode 100644
index 00000000..e12bdd27
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/std/proof.json
@@ -0,0 +1,26 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "Cfloat": {
+ "one": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 }
+ },
+ "Clib": {
+ "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "mixfix []<-": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "slice": { "prover": "alt-ergo@2.5.4", "time": 0.01 },
+ "slice_append": { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ "valid_in_range": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "vector_push": { "prover": "z3@4.13.4", "time": 0.027 }
+ },
+ "Int": {
+ "euclide": { "prover": "z3@4.13.4", "time": 0.02 },
+ "mult_bound": { "prover": "z3@4.13.4", "time": 0.012 }
+ },
+ "List": { "map_concat": { "prover": "alt-ergo@2.5.4", "time": 0.017 } }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/tensor/tensor.mlw b/safety-related-profile/documents/c_code_generation/tensor/tensor.mlw
new file mode 100644
index 00000000..abf3ce30
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/tensor.mlw
@@ -0,0 +1,159 @@
+(** Formalization of coordinates and dimensions *)
+
+module Range
+ use int.Int
+ use std.List
+
+ function size (ds : list int) : int =
+ match ds with
+ | Nil -> 1
+ | Cons d ds -> d * size ds
+ end
+
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+
+ let rec lemma valid_push (ks ds : list int) (k d : int)
+ requires { 0 <= k < d }
+ requires { valid ks ds }
+ ensures { valid (push ks k) (push ds d) }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Nil , Nil -> ()
+ | Cons _ ks , Cons _ ds -> valid_push ks ds k d
+ | _ -> absurd
+ end
+ (*qed*)
+
+ let rec lemma size_append (xs ys : list int)
+ ensures { size (xs ++ ys) = size xs * size ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end
+ (*qed*)
+
+ lemma size_push: forall xs x. size (push xs x) = size xs * x
+
+ let rec lemma positive_size (ds : list int)
+ requires { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ds with Nil -> () | Cons _ ds -> positive_size ds end
+ (*qed*)
+
+ let rec lemma positive_valid (ks ds : list int)
+ requires { valid ks ds }
+ ensures { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons _ ks , Cons _ ds -> positive_valid ks ds
+ | _ -> ()
+ end
+ (*qed*)
+
+end
+
+(** Formalization of Tensor *)
+module Tensor
+ use int.Int
+ use map.Map
+ use list.List
+ use Range
+
+ type data 'a = map (list int) 'a
+
+ type tensor 'a = {
+ dims : list int ;
+ data : data 'a ;
+ background : 'a ; (* default value, or value for 0-dimensions tensor *)
+ }
+ invariant { positive dims }
+ invariant { forall k. valid k dims \/ data k = background }
+ (*proof*)
+ by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+ (*qed*)
+
+ meta coercion function dims
+ meta coercion function data
+
+ (** Tensor with the same dimensions *)
+ predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims
+
+ (** Tensor with the same dimensions and background value *)
+ predicate (~=) (a : tensor 'a) (b : tensor 'a) =
+ a ~ b /\ a.background = b.background
+
+ (** Equal tensors *)
+ predicate (==) (a b : tensor 'a) =
+ a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k
+
+ (** Extensionality *)
+ lemma exteq: forall a b : tensor 'a. a == b <-> a = b
+ (*proof*) by tensor'eq a b (*qed*)
+
+ (** Scalar Tensor *)
+ let ghost function scalar (v : 'a) : tensor 'a
+ ensures { result.dims = Nil }
+ ensures { result.background = v }
+ ensures { forall k. result k = v }
+ (*proof*)
+ = { dims = Nil ; data = (fun _ -> v) ; background = v }
+ (*qed*)
+
+ (** Null Tensor *)
+ let ghost function zero (e : 'a) (ds : list int) : tensor 'a
+ requires { positive ds }
+ ensures { result.dims = ds }
+ ensures { result.background = e }
+ ensures { forall k. result k = e }
+ (*proof*)
+ = { dims = ds ; data = (fun _ -> e) ; background = e }
+ (*qed*)
+
+
+ (** Constant Tensor *)
+ let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a
+ ensures { result.dims = ds }
+ requires { positive ds }
+ ensures { result.background = bg }
+ ensures { forall k. valid k ds -> result k = v }
+ (*proof*)
+ = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg }
+ (*qed*)
+
+ (** Constant & Null *)
+ goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds
+
+end
+
+(** OP-Where Tensor Operation *)
+module OPWhere
+ use Tensor
+
+ let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/tensor/tensor/proof.json b/safety-related-profile/documents/c_code_generation/tensor/tensor/proof.json
new file mode 100644
index 00000000..aa543a48
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/tensor/proof.json
@@ -0,0 +1,35 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 30, "time": 0.14 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "OPWhere": {
+ "dwhere": { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ },
+ "Range": {
+ "positive_size": { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ "positive_valid": { "prover": "alt-ergo@2.5.4", "time": 0.033 },
+ "size_append": { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ "size_push": { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ "valid_push": { "prover": "alt-ergo@2.5.4", "time": 0.033 }
+ },
+ "Tensor": {
+ "const": { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ "exteq": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 }
+ ]
+ },
+ "scalar": { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "zero_is_const": { "prover": "alt-ergo@2.5.4", "time": 0.014 }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/tensor/why3find.json b/safety-related-profile/documents/c_code_generation/tensor/why3find.json
new file mode 100644
index 00000000..fb0612dc
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/tensor/why3find.json
@@ -0,0 +1,10 @@
+{
+ "fast": 0.2,
+ "time": 1.0,
+ "depth": 4,
+ "packages": [],
+ "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ],
+ "tactics": [ "split_vc", "inline_goal" ],
+ "drivers": [],
+ "warnoff": []
+}
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/.gitignore b/safety-related-profile/documents/c_code_generation/where_op/main/.gitignore
new file mode 100644
index 00000000..f12c3561
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/.gitignore
@@ -0,0 +1,5 @@
+.why3find
+why3session.xml
+why3shapes.gz
+/lib
+/html
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/Makefile b/safety-related-profile/documents/c_code_generation/where_op/main/Makefile
new file mode 100644
index 00000000..09a822a5
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/Makefile
@@ -0,0 +1,36 @@
+# Makefile for libvector
+
+.PHONY: all prove update doc lib clean
+
+all: prove doc lib
+ @echo "------------------------------------------"
+
+prove:
+ @echo "------------------------------------------"
+ @echo "--- Proofs"
+ @echo "------------------------------------------"
+ @why3find prove *.mlw -l
+
+update:
+ why3find prove *.mlw -l -s -m -x
+
+doc:
+ @echo "------------------------------------------"
+ @echo "--- Documentation"
+ @echo "------------------------------------------"
+ @rm -fr html
+ @why3find doc *.mlw -t "Tensor Library"
+
+EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib
+
+lib:
+ @echo "------------------------------------------"
+ @echo "--- Extraction"
+ @echo "------------------------------------------"
+ @rm -fr lib
+ @why3 extract $(EXTRACT) libvector.CIndex
+ @cd lib && gcc -c *.c
+ @find lib -type f
+
+clean:
+ rm -fr lib html
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/README.md b/safety-related-profile/documents/c_code_generation/where_op/main/README.md
new file mode 100644
index 00000000..cb07f3ad
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/README.md
@@ -0,0 +1,93 @@
+# ONNX-C
+
+
+
+## Getting started
+
+To make it easy for you to get started with GitLab, here's a list of recommended next steps.
+
+Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
+
+## Add your files
+
+- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
+- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command:
+
+```
+cd existing_repo
+git remote add origin https://git.frama-c.com/lcorrenson/onnx-c.git
+git branch -M main
+git push -uf origin main
+```
+
+## Integrate with your tools
+
+- [ ] [Set up project integrations](https://git.frama-c.com/lcorrenson/onnx-c/-/settings/integrations)
+
+## Collaborate with your team
+
+- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
+- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
+- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
+- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
+- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/)
+
+## Test and Deploy
+
+Use the built-in continuous integration in GitLab.
+
+- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
+- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
+- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
+- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
+- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
+
+***
+
+# Editing this README
+
+When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template.
+
+## Suggestions for a good README
+
+Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
+
+## Name
+Choose a self-explaining name for your project.
+
+## Description
+Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
+
+## Badges
+On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
+
+## Visuals
+Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
+
+## Installation
+Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
+
+## Usage
+Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
+
+## Support
+Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
+
+## Roadmap
+If you have ideas for releases in the future, it is a good idea to list them in the README.
+
+## Contributing
+State if you are open to contributions and what your requirements are for accepting them.
+
+For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
+
+You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
+
+## Authors and acknowledgment
+Show your appreciation to those who have contributed to the project.
+
+## License
+For open source projects, say how it is licensed.
+
+## Project status
+If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/cdriver.drv b/safety-related-profile/documents/c_code_generation/where_op/main/cdriver.drv
new file mode 100644
index 00000000..01aec189
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/cdriver.drv
@@ -0,0 +1,11 @@
+module std.Clib
+ interface export "#include "
+ interface export "#include "
+ syntax val ([]) "%1[%2]" prec 1 1 15
+ syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14
+end
+
+module libvector.CIndex
+ interface "#include "
+ interface "#include "
+end
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/layout.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/layout.mlw
new file mode 100644
index 00000000..fd7bcf7b
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/layout.mlw
@@ -0,0 +1,91 @@
+module CFlat
+ use std.Int
+ use std.List
+ use tensor.Range
+
+ function offset (ks ds : list int) : int =
+ match ks , ds with
+ | Cons k ks , Cons _ ds -> k * size ds + offset ks ds
+ | _ -> 0
+ end
+
+ function index (p : int) (ds : list int) : list int =
+ match ds with
+ | Nil -> Nil
+ | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds)
+ end
+
+ let rec lemma offset_size (ks ds : list int)
+ requires { valid ks ds }
+ ensures { 0 <= offset ks ds < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k krs , Cons d drs ->
+ offset_size krs drs ;
+ let p = offset ks ds in
+ let q = offset krs drs in
+ assert { p = size drs * k + q } ;
+ assert {
+ size drs * k
+ <= size drs * (d - 1)
+ = d * size drs - size drs
+ } ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma index_range (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { valid (index p ds) ds }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds -> index_range (mod p (size rds)) rds
+ end
+ (*qed*)
+
+ let rec lemma index_of_offset (ks ds : list int)
+ requires { valid ks ds }
+ ensures { index (offset ks ds) ds = ks }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k rks , Cons _ rds ->
+ index_of_offset rks rds ;
+ let p = offset ks ds in
+ let n = size rds in
+ let q = offset rks rds in
+ euclide p k n q ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma offset_of_index (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { offset (index p ds) ds = p }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds ->
+ let n = size rds in
+ offset_of_index (mod p n) rds
+ end
+ (*qed*)
+
+ let rec lemma offset_push (ks ds : list int) (k d : int)
+ requires { valid ks ds }
+ ensures { offset (push ks k) (push ds d) = offset ks ds * d + k }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Cons _ krs , Cons _ drs -> offset_push krs drs k d
+ | _ -> ()
+ end
+ (*qed*)
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/layout/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/layout/proof.json
new file mode 100644
index 00000000..e35cde60
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/layout/proof.json
@@ -0,0 +1,39 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "CFlat": {
+ "index_of_offset": { "prover": "cvc5@1.2.1", "time": 0.055 },
+ "index_range": { "prover": "alt-ergo@2.5.4", "time": 0.197 },
+ "offset_of_index": { "prover": "alt-ergo@2.5.4", "time": 0.343 },
+ "offset_push": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.075 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 1.6 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.049 }
+ ]
+ }
+ ]
+ },
+ "offset_size": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "z3@4.13.4", "time": 0.02 },
+ { "prover": "cvc5@1.2.1", "time": 0.137 }
+ ]
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/libvector.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/libvector.mlw
new file mode 100644
index 00000000..f2f0c591
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/libvector.mlw
@@ -0,0 +1,118 @@
+
+(** Offset & Dimensions *)
+module CIndex
+ use std.Int
+ use std.List
+ use std.Clib
+ use mach.int.Int32
+ use tensor.Range
+ use layout.CFlat
+
+ type iarray = ptr int32
+
+ function ivector (u : iarray) (n : int) : list int
+ = map Int32.to_int (vector u n)
+
+ function islice (u : iarray) (p q : int) : list int
+ = map Int32.to_int (slice u p q)
+
+ (** ## Dimensions *)
+
+ function vdim (u : iarray) (n : int) : int = size (ivector u n)
+ function sdim (u : iarray) (p q : int) : int = size (islice u p q)
+
+ predicate pdim (u : iarray) (p q : int) =
+ forall k. p <= k < q -> 0 < value_at u k
+
+ predicate dimension (u : iarray) (n : int) =
+ 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ vdim u n <= max_int32
+
+ (** Equivalence betwen {pdim} and {tensor.Range.positive}. *)
+ let rec lemma positive_pdim (u : iarray) (p q : int)
+ (*proof*)
+ ensures { pdim u p q <-> positive (islice u p q) }
+ variant { q - p }
+ = if p < q then positive_pdim u (p+1) q
+ (*qed*)
+
+ let ghost sdim_split (u : iarray) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { pdim u p q }
+ ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q }
+ = assert { islice u p k ++ islice u k q = islice u p q }
+ (*qed*)
+
+ (** Extracted Library *)
+
+ let cdim_size (u : iarray) (n : int32): int32 =
+ ensures { result = vdim u n }
+ (*proof*)
+ requires { dimension u n }
+ (*qed*)
+ begin
+ let ref p = 1 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { p = vdim u i }
+ ghost
+ begin
+ ensures { 1 <= p * u[i] <= max_int32 }
+ let ghost dl = pure { sdim u 0 i } in
+ let ghost di = Int32.to_int u[i] in
+ let ghost dr = pure { sdim u (i+1) n } in
+ let ghost dn = pure { sdim u 0 n } in
+ sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * u[i] ;
+ done ; p
+ end
+
+ let cdim_create_1 (n : int32) : iarray =
+ requires { 0 < n }
+ ensures { is_not_null result -> vdim result 1 = n }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 1 }
+ ensures { is_not_null result -> value_at result 0 = n }
+ (*qed*)
+ let cd = malloc 1 in
+ if is_not_null cd then cd[0] <- n ; cd
+
+ let cdim_create_2 (p q : int32) : iarray =
+ requires { 0 < p /\ 0 < q /\ p * q <= max_int32 }
+ ensures { is_not_null result -> vdim result 2 = p * q }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 2 }
+ ensures { is_not_null result -> value_at result 0 = p }
+ ensures { is_not_null result -> value_at result 1 = q }
+ (*qed*)
+ let cd = malloc 2 in
+ if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd
+
+ (** ## Coordinates *)
+
+ let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 =
+ (*proof*)
+ requires { 0 <= n }
+ requires { valid_range ks 0 n }
+ requires { valid_range ds 0 n }
+ requires { vdim ds n < max_int32 }
+ ensures { 0 <= result -> dimension ds n }
+ ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) }
+ ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) }
+ (*qed*)
+ begin
+ assume { false } ;
+ let ref p = 0 in
+ for i = 0 to n - 1 do
+ let d = ds[i] in
+ let k = ks[i] in
+ if 0 <= k && k < d then
+ p <- p * d + k
+ else return (-1)
+ done ; p
+ end
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/libvector/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/libvector/proof.json
new file mode 100644
index 00000000..6505f2d5
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/libvector/proof.json
@@ -0,0 +1,72 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "CIndex": {
+ "cdim_create_1": { "prover": "alt-ergo@2.5.4", "time": 0.194 },
+ "cdim_create_2": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.461 },
+ {
+ "tactic": "inline_goal",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.042 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.03 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ "cdim_size": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.031 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.039 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.066 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "inline_goal",
+ "children": [ { "prover": "alt-ergo@2.5.4", "time": 1.4 } ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.097 }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.037 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.193 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ },
+ "coffset": { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ "positive_pdim": { "prover": "alt-ergo@2.5.4", "time": 0.074 },
+ "sdim_split": { "prover": "alt-ergo@2.5.4", "time": 0.206 }
+ }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/std.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/std.mlw
new file mode 100644
index 00000000..8889dc17
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/std.mlw
@@ -0,0 +1,124 @@
+(* Extensions to Standard Library *)
+
+module Int
+ use export int.Int
+ use export int.Abs
+ use export int.ComputerDivision
+
+ (** Unicity of euclidian division decomposition *)
+ let ghost euclide (a b q r : int)
+ requires { 0 <= a /\ 0 <= r < q }
+ requires { a = b * q + r }
+ ensures { b = div a q }
+ ensures { r = mod a q }
+ (*proof*)
+ = let rec kernel (b r : int)
+ requires { b * q + r = 0 }
+ requires { abs r < q }
+ ensures { b = r = 0 }
+ variant { abs b }
+ = if b < 0 then kernel (b + 1) (r - q) else
+ if b > 0 then kernel (b - 1) (r + q) else ()
+ in kernel (b - div a q) (r - mod a q)
+ (*qed*)
+
+ (** Multiplication upper-bound *)
+ let ghost mult_bound (a b c : int)
+ requires { 0 < a * b <= c }
+ ensures { abs a <= c }
+ ensures { abs b <= c }
+ = ()
+
+end
+
+module List
+ use export list.List
+ use export list.Map
+ use export list.Append
+
+ (** Appends an element to the end of the list *)
+ function push (xs : list 'a) (x : 'a) : list 'a =
+ xs ++ Cons x Nil
+
+ let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a)
+ ensures { map f (xs ++ ys) = map f xs ++ map f ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end
+ (*qed*)
+
+end
+
+module Clib
+ use map.Map
+ use int.Int
+ use mach.int.Int32
+ use export mach.c.C
+
+ type int32 = Int32.int32
+
+ (** Array-range validity *)
+ predicate valid_range (vp : ptr 'a) (p q : int) =
+ q <= p \/
+ ( 0 <= vp.min <= vp.offset /\
+ 0 <= p <= q <= max_int32 /\
+ 0 <= vp.min <= vp.offset /\
+ vp.offset + q <= vp.max <= vp.plength )
+
+ let lemma valid_in_range (vp : ptr 'a) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { valid_range vp p q }
+ ensures { valid_ptr_shift vp k }
+ = ()
+ (*qed*)
+
+ (** Array as map *)
+ let ghost function value_at (vp : ptr 'a) : map int 'a =
+ pure { fun k -> vp.data.Array.elts (vp.offset + k) }
+
+ (** Array access (ghost) *)
+ function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k
+
+ (** Array access (C, inlined) *)
+ let ([]) (vp : ptr 'a) (k : int32) : 'a
+ (*proof*)
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { result = vp[k] }
+ = get_ofs vp k
+ (*qed*)
+
+ (** Array update (C, inlined) *)
+ let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit
+ requires { writable vp }
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v }
+ = set_ofs vp k v
+
+ (** Array as list *)
+ use List
+
+ let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a =
+ (*proof*)
+ variant { q - p }
+ (*qed*)
+ if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q)
+
+ let rec lemma slice_append (u : ptr 'a) (p q r : int)
+ requires { p <= q <= r }
+ ensures { slice u p r = slice u p q ++ slice u q r }
+ (*proof*)
+ variant { q - p }
+ = if p < q then slice_append u (p+1) q r
+ (*qed*)
+
+ let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n
+
+ let lemma vector_push (u : ptr 'a) (n : int)
+ requires { 0 <= n }
+ ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+ (*proof*)
+ = slice_append u 0 n (n+1)
+ (*qed*)
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/std/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/std/proof.json
new file mode 100644
index 00000000..d2bc32ea
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/std/proof.json
@@ -0,0 +1,22 @@
+{
+ "profile": [
+ { "prover": "z3@4.13.4", "size": 33, "time": 0.865 },
+ { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 },
+ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 }
+ ],
+ "proofs": {
+ "Clib": {
+ "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "mixfix []<-": { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ "slice": { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ "slice_append": { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ "valid_in_range": { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ "vector_push": { "prover": "z3@4.13.4", "time": 0.027 }
+ },
+ "Int": {
+ "euclide": { "prover": "z3@4.13.4", "time": 0.02 },
+ "mult_bound": { "prover": "z3@4.13.4", "time": 0.012 }
+ },
+ "List": { "map_concat": { "prover": "alt-ergo@2.5.4", "time": 0.035 } }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/tensor.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/tensor.mlw
new file mode 100644
index 00000000..b25db8ce
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/tensor.mlw
@@ -0,0 +1,88 @@
+module Range
+ use int.Int
+ use std.List
+
+ function size (ds : list int) : int =
+ match ds with
+ | Nil -> 1
+ | Cons d ds -> d * size ds
+ end
+
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+
+ let rec lemma size_append (xs ys : list int)
+ ensures { size (xs ++ ys) = size xs * size ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end
+ (*qed*)
+
+ lemma size_push: forall xs x. size (push xs x) = size xs * x
+
+ let rec lemma positive_size (ds : list int)
+ requires { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ds with Nil -> () | Cons _ ds -> positive_size ds end
+ (*qed*)
+
+ let rec lemma positive_valid (ks ds : list int)
+ requires { valid ks ds }
+ ensures { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons _ ks , Cons _ ds -> positive_valid ks ds
+ | _ -> ()
+ end
+ (*qed*)
+
+end
+
+module Tensor
+ use int.Int
+ use map.Map
+ use list.List
+ use Range
+
+ type data 'a = map (list int) 'a
+
+ type tensor 'a = {
+ dims : list int ;
+ data : data 'a ;
+ } invariant { positive dims }
+ meta coercion function data
+
+ (** Tensor with the same dimensions *)
+ predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims
+
+end
+
+module OPWhere
+ use Tensor
+
+ let function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { c ~ a ~ b }
+ ensures { result ~ c ~ a ~ b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data }
+ (*qed*)
+
+end
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/tensor/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/tensor/proof.json
new file mode 100644
index 00000000..a2f9be46
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/tensor/proof.json
@@ -0,0 +1,16 @@
+{
+ "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } ],
+ "proofs": {
+ "OPWhere": {
+ "dwhere": { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ },
+ "Range": {
+ "positive_size": { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ "positive_valid": { "prover": "alt-ergo@2.5.4", "time": 0.033 },
+ "size_append": { "prover": "alt-ergo@2.5.4", "time": 0.029 },
+ "size_push": { "prover": "alt-ergo@2.5.4", "time": 0.015 }
+ },
+ "Tensor": { "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.015 } }
+ }
+}
diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/why3find.json b/safety-related-profile/documents/c_code_generation/where_op/main/why3find.json
new file mode 100644
index 00000000..fb0612dc
--- /dev/null
+++ b/safety-related-profile/documents/c_code_generation/where_op/main/why3find.json
@@ -0,0 +1,10 @@
+{
+ "fast": 0.2,
+ "time": 1.0,
+ "depth": 4,
+ "packages": [],
+ "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ],
+ "tactics": [ "split_vc", "inline_goal" ],
+ "drivers": [],
+ "warnoff": []
+}
diff --git a/safety-related-profile/documents/conv_specification_example/README.md b/safety-related-profile/documents/conv_specification_example/README.md
deleted file mode 100644
index e78e70c5..00000000
--- a/safety-related-profile/documents/conv_specification_example/README.md
+++ /dev/null
@@ -1,708 +0,0 @@
-
-# /!\ WARNING /!\
-THIS VERSION IS NOW OBSOLETE.
-PLEASE DO NOT MAKE ANY MODIFICATIONS TO THIS VERSION.\
-INSTEAD, USE THE VERSION LOCATED [HERE](../profile_opset/conv/conv.md)
-
-
-# Conventions
-## Notations
-- Notations $h(X)$ and $w(X)$ respectively denote the _height_ and the _width_ of tensor $X$. If the tensor represents an image, $h(X)$ and $w(X)$ represent the _height_ and the _width_ of the image.
-## Usage of fonts
-- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "pads" attribute is represented by `pads`.
-## Tags
-- Restrictions with respect to the ONNX standard are indicated in the text with the tag `[Ri]` where `i` is a number.\
-A synthesis of all restrictions is given in section "Restrictions".
-## Types
-- Operators are first described for values in the domain of real numbers. A specific description is given for the other types (floats, integers).
-
-# `conv` operator (real)
-
-### Restrictions
-The following restrictions apply to the `conv` operator for the SONNX profile:
-- The number of spatial axes of the tensors is restricted to 2 `[R1]`
-- Attribute `auto_pad` is restricted to `NOTSET` `[R2]`
-- Attribute `group` is restricted to 1 (standard convolution) or to the number of channels of the input tensor (depthwise convolution) `[R3]`
-- All attributes shall be be given explicit values (i.e., default values for attributes are not supported) `[R4]`
-- The number of elements in the `strides` list is equal to 2. `[R5]`
-
-### Signature
-`Y = conv(X,W,[B])`
-where
-- `X`: input tensor
-- `W`: convolution kernel
-- `B`: optional bias
-- `Y`: output tensor
-
-#### Informal specification
-
-Operator `conv` computes the convolution of the input tensor `X` with the kernel `W` and adds bias `B` to the result. Two types of convolutions are supported: _standard convolution_ and _depthwise convolution_ `[R3]`. The SONNX profile limits the number of spatial axes to 2 `[R1]`.
-
-##### Standard convolution
-A _standard convolution_ applies a kernel (also called "filter") to the input tensor, aggregating information accross both spatial axes and channels. For a given output channel, the kernel operates accross all input channels and all contributions are summed to produce the output. This corresponds to the case where `group`= 1.
-
-The mathematical definition of the operator without padding is given hereafter.
-The formal specification is given in Section Formal specification. When considering padding, the same formula applies, in which `X` represents the padded version of the actual input `X`.
-
-$$\begin{gathered}
- Y[b, c, m, n] = \sum_{i=0}^{fm(W)-1} \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1} \\ (X[b,i,m \cdot strides[0]+ j \cdot dilations[0], n \cdot strides[1]+ z \cdot dilations[1]] \cdot W[c, i, j, z]) \\ + B[c]
-\end{gathered}$$
-
-Where
-- $b$ is the batch index, $b \in [0,b(Y)-1]$, $b(Y)$ is the batch size of output `Y`
-- $c$ is the data channel, $c \in [0,c(Y)-1]$, $c(Y)$ is the number of data channels of output `Y`
-- $m \in [0,h(Y)-1]$ is the index of the first spatial axis of output `Y`
-- $n \in [0,w(Y)-1]$ is the index of the second spatial axis of output `Y`
-- $fm(W)$ is the number of feature maps of kernel `W`
-- $h(W)$ is the size of the first spatial axis of kernel `W`
-- $w(W)$ is the sizes of the second spatial axis of kernel `W`
-
-`strides` and `dilations` are attributes of the operator. They are described later in this section.
-
-The effect of the operator is illustrated on the following figure. In this example
-- shape of `Y` is $1\times 1 \times 4 \times 4$ (batch size is 1, number of data channels is 1)
-- shape of `X` is $1 \times 1 \times 8 \times 8$ (batch size is 1, number of data channels is 1)
-- shape of `W` is $1 \times 1 \times 3 \times 2$ (number of data channels is 1)
-- shape of `B` is $1$
-- `pads` is set to (1,2,2,2) (1 column on the left, 2 columns on the right, 2 rows on the top, 2 rows on the bottom)
-- `dilations` is set to (2,2)
-- `strides` is set to (2,3)
-
-
-
-The following figure shows the case where the number of channels of `X` is 3. In this example:
-- shape of `Y` is $1 \times 1 \times 4 \times 4$
-- shape of `X` is $1 \times 3 \times 8 \times 8$
-- shape of `W` is $1 \times 3 \times 3 \times 2$
-- shape of `B` is $1$
-- `groups` is set to 1
-- the other attributes have the same values as in the previous figure.
-
-
-
-
-##### Depthwise convolution
-A _depthwise convolution_ applies a specific kernel (or "filter") to each input channels. The number of output channels is equal to the number of input channels. This corresponds to the case where `group`= $c(X)$.
-
-The mathematical definition is given hereafter:
-
-$$\begin{gathered}
- Y[b, c, m, n] = \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1}\\ (X[b, c, m \cdot strides[0] + j \cdot dilations[0], n \cdot strides[1] + z \cdot dilations[1]] \cdot W[c, 0, j , z] ) + B[c]
-\end{gathered}$$
-
-Variables are defined as for the standard convolution.
-The effect of the operator is illustrated on the following figure. In this example,
-- shape of `Y` is $1\times 3 \times 4 \times 4$
-- shape of `X` is $1 \times 3 \times 8 \times 8$
-- shape of `W` is $3 \times 1 \times 3 \times 2$
-- shape of `B` is $3$
-- `groups` is set to 3
-- the other attributes have the same values as in the previous figure.
-
-
-
-#### Inputs and outputs
-
-##### `X`
-
-Tensor `X` is the input tensor on which convolution with kernel `W` is computed.
-
-The shape of tensor `X` is $b(X) \times c(X) \times h(X) \times w(X)$.
-
-###### Constraints
-
-- (C1) Number of spatial axes of tensor `X`
- - Statement: The number of spatial axes of tensor `X` is 2. `[R1]`
- - Rationale: This restriction is intoduced to simplify the implementation considering the actual industrial use cases.
-- (C2) Consistency between the number of channels of `X` and `W`
- - Statement: $c(X)=fm(W)$
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`
-
- - Statement:
- * $$\left\lfloor{\frac{alpha-(dilations[0] \cdot h(W)-1)}{strides[0]}} \right\rfloor +1 = h(Y) \mbox{ with } alpha=h(X)+pads[0]+pads[2]$$
-
- and
-
- * $$\left\lfloor{\frac{beta-(dilations[1] \cdot w(W)-1)}{strides[1]}} \right\rfloor +1 = w(Y) \mbox{ with } beta=w(X)+pads[1]+pads[3]$$
- - Rationale: The size of the output is determined by the number of times the kernel can be applied on a given spatial axis.
-- (C4) Axis denotations
- - Statement: If axis denotation is in effect, the operation expects input data tensor to have axis denotation \[`DATA_BATCH`, `DATA_CHANNEL`, `DATA_FEATURE`, `DATA_FEATURE`\].
- - Rationale: Denotation convention
-
-##### `W`
-
-Tensor `W` is the convolution kernel.
-
-The shape of tensor `W`is $(c(W) \times fm(W) \times h(W) \times w(W))$, where
-- $c(W)$ is the number of output channels or number of feature maps
-- $fm(W)$ is the number of input channels
-- $h(W)$ and $w(W)$ are the sizes of the kernel for the two spatial axes.
-
-###### Constraints
-- (C1) Consistency between the number of channels of `X` and `W`
- - Statement: [See constraint (C2) of X](#channel_consist).
-- (C2) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist).
-- (C3) Consistency between `W` and `kernel_shape`
-
- - Statement: The size of `W` for an axis must bve equal to the value of `kernel_shape` for that axis
- - Rationale: `kernel_shape` represents the shape of `W`, where `kernel_shape[0]` = $w(W)$ and `kernel_shape[1]` = $h(W)$.
-- (C4) Axis denotations
- - Statement: If axis denotation is in effect, the operation expects the weight tensor to have axis denotation \[`FILTER_OUT_CHANNEL`, `FILTER_IN_CHANNEL`, `FILTER_SPATIAL`, `FILTER_SPATIAL`\].
- - Rationale: Denotation convention
-
-##### `B`
-
-Tensor `B` is the bias.
-
-The shape of tensor `B`is $c(B)$.
-
-###### Constraints
-- (C1) Consistency between the number of channels of `B` and `W`
- - Statement: $c(B) = fm(W)$.
-
-#### Attributes
-
-##### `strides`: list of int
-
-Attribute `strides` determines how the kernel is applied on tensor `X` during the convolution.
-
-For instance, with $\mbox{\texttt{stride}}[0]=2$ and $\mbox{\texttt{stride}}[1]=3$, the kernel is applied to data 2 units on right in the first spatial axis and to data 3 units down in the second spatial axis at each step of the convolution.
-
-> The previous sentence is not clear...
-
-The effect of the `strides` attribute is illustrated on the following figure. In this example, `strides`=(2,3).
-
-
-
-###### Constraints
-- (C1) Size of `strides`
- - Statement: the number of elements in the `strides` list is equal to 2. `[R5]`
- - Rationale: The SONNX profile only supports 2 spatial axes.
-- (C2) Value domain
- - Statement: `strides` is a list of strictly positive integers.
- - Rationale: Stride values are used in the denominator of expression in [constraint (C3) of X](#shape_consist)
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist)
-
-##### `auto_pad` : string
-
-The `auto_pad` attribute determines if and how automatic padding is done for the input tensor X.
-
-###### Constraints
-- (C1) Explicit padding
- - Statement: `auto_pad` shall be set to `NOTSET` `[R3]`
- - Rationale: The SONNX profile imposes explicit padding.
-
-##### `pads`: list of int
-
-Attribute `pads` determines the padding at the beginning and end along each spatial axis of the input tensor `X`.
-
-`pads` is a list of the form (`x1_begin`, `x2_begin`,..., `x1_end`, `x2_end`,...), where `xi_begin` is the number of elements (possibly zero) added at the beginning of axis $i$ and `xi_end` is the number of elements added at the end of axis $i$.
-
-The padding value is 0.
-
-The effect of the `pads` attribute is illustrated on the following figure. In this example, `pads`=(1,3,2,2).
-
-
-
-###### Constraints
-- (C1) Value domain
- - Statement: `pads` is a list of positive or null integers.
- - Rationale: A padding value gives a number of elements to be added to some spatial axis. This is positive[^2].
-- (C2) Consistency between the shape of `X` and the length of `pads`
- - Statement: The length of the `pads` list is two times the number of spatial axes of `X`
- - Rationale: Padding shall be given for all spatial axes, and a beggining value and an end value must be given for each axis.
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist)
-
-##### `dilations`: list of int
-
-Attribute `dilations` specifies the spacing between the kernel elements for each spatial axis of the filter `W`. It is a list of non-null integer values where each value gives the dilation factor for spatial axis $i$. If the dilation factor is greater than 1 for axis $i$, then the kernel points are spaced out by the dilation factor for that axis.
-
-The spacing value is 0.
-
-The effect of the `dilations` attribute for a tensor with two spatial axes is depicted on the following figure. In this example, `dilations`=(2,2).
-
-
-
-
-###### Constraints
-- (C1) Value domain
- - Statement: `dilations` is a list of strictly positive integers
-- (C2) Relation between `dilations` and `W`
- - Statement: The length of the `dilations` list is equal to number of spatial axes of `W`.
- - Rationale: Dilation is defined for all spatial axes of `W`.
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist)
-
-##### `group`: int
-
-This attribute specifies the number of groups the input channels and output channels are divided into. When `group`=1, a standard convolution is performed.
-When group is greater than 1, convolution is computed for each group separately with a specific set of filters.
-
-The effect of the `group` attribute for a tensor with two spatial axes is depicted on the following figure. In this example `group`=3.
-
-
-
-(Taken from https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning)
-
-In the example, with `group` set to 3 and an input `X` and an output `Y` with 3 channels, the input and output channels will be divided into 3 groups of 1 channel.
-
-###### Constraints
-- (C1) Support for standard and depthwise convolutions
- - Statement: `group`=1 (standard convolution) or `group`$=c(X)$ (depthwise convolution)
- - Rationale: SONNX only supports standard and depthwise convolutions
-
-##### `kernel_shape`: list of int
-
-This parameter specifies the shape of the convolution kernel `W`.
-
-###### Constraints.
-
-- (C1) Value domain
- - Statement: `kernel_shape` is a list of strictly positive integers
- - Rationale: A dimension is always positive and cannot be null.
-- (C2) Consistency between `W` and `kernel_shape`
- - Statement: [See constraint (C3) of W](#kernel_shape_w)
-
-#### Outputs
-
-##### `Y`
-
-The size of the output `Y` will be $(b(Y) \times c(Y) \times h(Y) \times w(Y))$ where
-- $b(Y)$ is the number of batches
-- $c(Y)$ is the number of channels
-- $h(Y)$ and $w(Y)$ are the sizes of the output for the two spatial axes
-
-###### Constraints.
-- (C1) Consistency between the number of channels of `B` and `Y`
- - Statement: HYPERLIEN VERS W / constraint kernel shape
-- (C2) Consistency between the shape of tensors `X`, `W`, `Y`, attributes `pads` and `strides`,
- - Statement: [see constraint (C3) of X](#shape_consist)
-
-#### Formal specification
-
-The following code specifies the `conv` operator using the Why3 language[^3].
-
-###### Nota: the specification does not cover all attributes values. Currently, there is no padding (`pads` is not set and `auto_pad = NOTSET`) and `dilations` is not set.
-
-``` ocaml
-module Conv
- use int.Int
- use real.RealInfix
- use array.Array
- use int.ComputerDivision
- use real.Truncate
- use real.FromInt
-
- type input_tensor = {
- x: array real;
- x_l: int;
- x_c: int;
- x_b: int;
- x_ch: int;
- }
-
- type convolution_kernel = {
- w: array real;
- w_l: int;
- w_c: int;
- w_ch_in: int;
- w_ch_out: int;
- }
-
- type bias_tensor = {
- b: array real;
- b_c: int;
- }
-
- type attributes = {
- stride: array int;
- pads: array int;
- auto_pad: int;
- dilation: array int;
- }
-
- type output_tensor = {
-
- y_b: int;
- y_ch: int;
- y_l: int;
- y_c: int;
-
- }
-
- function conv_size (out: output_tensor) : int =
- out.y_b * out.y_ch * out.y_l * out.y_c
-
- predicate conv_result
- (inp: input_tensor)
- (kernel: convolution_kernel)
- (bias: bias_tensor)
- (attr: attributes)
- (out: output_tensor)
- (res: array real)
- (bi ci hi wi: int)
- (ci_in ki_h ki_w: int) =
- let y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi in
- let x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] in
- let x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] in
-
- (0 <= x_l_idx < inp.x_l /\ 0 <= x_c_idx < inp.x_c) ->
- let x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx in
- let w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w in
- res.elts (y_idx) = bias.b[ci] +. (inp.x[x_idx] *. kernel.w[w_idx])
-
- val conv (inp: input_tensor)(kernel: convolution_kernel)(bias: bias_tensor)(attr: attributes)(out: output_tensor): array real
- requires{inp.x_ch = out.y_ch = kernel.w_ch_in = bias.b_c}
- requires{out.y_l = (div (inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l)) attr.stride[0]) + 1}
- requires{out.y_c = (div (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c)) attr.stride[1]) +1}
- requires { inp.x_l > 0 /\ inp.x_c > 0 /\ inp.x_ch > 0 /\ inp.x_b > 0}
- requires{kernel.w_l > 0 /\ kernel.w_c > 0 /\ kernel.w_ch_in > 0 /\ kernel.w_ch_out > 0}
- requires { out.y_b > 0 /\ out.y_ch > 0 /\ out.y_l > 0 /\ out.y_c > 0}
- requires { length inp.x = inp.x_l * inp.x_c * inp.x_ch * inp.x_b}
- requires{length kernel.w = kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out}
- requires{inp.x_l >= kernel.w_l}
- requires{inp.x_c >= kernel.w_c}
- requires{length bias.b = bias.b_c}
- requires{length attr.stride = 2}
- requires{length attr.dilation = 2}
- requires{length attr.pads = 4}
- requires{forall i. 0 <= i < length attr.pads -> attr.pads[i] = 0}
- requires{forall j. 0 <= j < length attr.dilation -> attr.dilation[j] >= 1}
- requires{forall k. 0 <= k < length attr.stride -> attr.stride[k] >= 1}
- ensures { length result = conv_size out }
- ensures { forall bi ci hi wi ci_in ki_h ki_w: int.
- 0 <= bi < out.y_b ->
- 0 <= ci < out.y_ch ->
- 0 <= hi < out.y_l ->
- 0 <= wi < out.y_c ->
- 0 <= ci_in < kernel.w_ch_in ->
- 0 <= ki_h < kernel.w_l ->
- 0 <= ki_w < kernel.w_c -> conv_result inp kernel bias attr out result bi ci hi wi ci_in ki_h ki_w }
-
-end
-
-
-module Test_conv
- use int.Int
- use real.RealInfix
- use array.Array
- use int.ComputerDivision
- use real.Truncate
- use real.FromInt
- use Conv
-
-let test_conv () =
- let inp_x = Array.make 9 1.0 in
- let inp = { x = inp_x; x_l = 3; x_c = 3; x_b = 1; x_ch = 1 } in
-
- let kernel_w = Array.make 4 0.0 in
- let kernel = { w = kernel_w; w_l = 2; w_c = 2; w_ch_in = 1; w_ch_out = 1 } in
-
- let bias_b = Array.make 1 0.5 in
- let bias = { b = bias_b; b_c = 1 } in
- let stride = Array.make 2 1 in (* Stride of 1 *)
- let pads = Array.make 4 0 in (* No padding *)
- let dilation = Array.make 2 1 in (* Dilation of 1 *)
- let attr = { stride = stride; pads = pads; auto_pad = 0; dilation = dilation } in
- let out_h = (div (inp.x_l + pads[0] + pads[2] - (dilation[0] * kernel.w_l)) stride[0]) + 1 in
- let out_w = (div (inp.x_c + pads[1] + pads[3] - (dilation[1] * kernel.w_c)) stride[1]) + 1 in
- let out = { y_b = 1; y_ch = 1; y_l = out_h ; y_c = out_w } in
- (* Call the conv function *)
- let result = conv inp kernel bias attr out in
- let actual_result = result.elts 0 in
- assert { conv_result inp kernel bias attr out result 0 0 0 0 0 0 0} ;
- assert { actual_result = 0.5 } ;
- ()
-
-end
-```
-
-Another formal specification of the `conv` operator using Frama-C
-language[^4] is presented below.
-
-``` objectivec
-#include
-#include
-#include
-
-/* Data Structures */
-typedef struct {
- float *x;
- int x_l, x_c, x_b, x_ch;
-} input_tensor;
-
-typedef struct {
- float *w;
- int w_l, w_c, w_ch_in, w_ch_out;
-} convolution_kernel;
-
-typedef struct {
- float *b;
- int b_c;
-} bias_tensor;
-
-typedef struct {
- int *stride;
- int *pads;
- int *dilation;
-} attributes;
-
-typedef struct {
- float *y;
- int y_b, y_ch, y_l, y_c;
-} output_tensor;
-/*@
- requires \valid_read(pads + (0 .. 3));
- requires \valid_read(stride + (0 .. 1));
- requires \valid_read(result + (0 .. 3));
- requires x_l > 0 && x_c > 0 && w_l > 0 && w_c > 0 && y_l > 0 && y_c > 0;
- assigns result[0 .. 3];
- behavior empty_or_notset:
- assumes (auto_pad == "") || (auto_pad == "NOTSET");
- ensures \forall integer i; 0 <= i < 4 ==> result[i] == pads[i];
-
-behavior valid:
- assumes (auto_pad == "VALID") ;
- ensures \forall integer i; 0 <= i < 4 ==> result[i] == 0;
-
-behavior same_upper:
- assumes (auto_pad == "SAME_UPPER") ;
- ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- \let pad_c = (y_c - 1) * stride[1] + w_c - x_c;
- ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==>
- (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2)) &&
- (pad_l % 2 != 0) && (pad_c % 2 != 0) ==>
- (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == ((pad_c / 2) + 1) && result[3] == ((pad_l / 2) + 1));
-
-behavior same_lower:
- assumes (auto_pad == "SAME_LOWER");
- ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- \let pad_c = (y_c - 1) * stride[1] + w_c - x_c;
- ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==>
- (result[0] == (pad_c / 2) && result[2] == (pad_c / 2) && result[1] == (pad_l / 2) && result[3] == (pad_l == 2)) &&
- ((pad_l % 2 != 0) && (pad_c % 2 != 0)) ==>
- (result[0] == ((pad_c / 2) + 1) && result[1] == ((pad_l / 2) + 1) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2));
-complete behaviors empty_or_notset, valid, same_upper, same_lower;
-disjoint behaviors empty_or_notset, valid, same_upper, same_lower;
-*/
-void compute_pad(const char* auto_pad, int pads[4], int stride[2], int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int result[4]) {
- int pad_l, pad_c;
-
- if ((auto_pad == "") || (auto_pad == "NOTSET")) {
- for (int i = 0; i < 4; i++) {
- result[i] = pads[i];
- }
- } else if ((auto_pad == "VALID")) {
- for (int i = 0; i < 4; i++) {
- result[i] = 0;
- }
- } else if ((auto_pad == "SAME_UPPER")) {
- pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- pad_c = (y_c - 1) * stride[1] + w_c - x_c;
-
- if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) {
- result[0] = result[2] = pad_c / 2;
- result[1] = result[3] = pad_l / 2;
- } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) {
- result[0] = pad_c / 2;
- result[1] = pad_l / 2;
- result[2] = (pad_c / 2) + 1;
- result[3] = (pad_l / 2) + 1;
- }
- } else if ((auto_pad == "SAME_LOWER")) {
- pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- pad_c = (y_c - 1) * stride[1] + w_c - x_c;
-
- if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) {
- result[0] = result[2] = pad_c / 2;
- result[1] = result[3] = pad_l / 2;
- } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) {
- result[0] = (pad_c / 2) + 1;
- result[1] = (pad_l / 2) + 1;
- result[2] = pad_c / 2;
- result[3] = pad_l / 2;
- }
- }
-}
-
-/* Function to compute the convolution */
-//void compute_pad(int auto_pad, int *pads, int *stride, int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int *result);
-
-/*@
- requires \valid_read(inp.x + (0..(inp.x_l*inp.x_c*inp.x_b*inp.x_ch)-1));
- requires \valid_read(kernel.w + (0..(kernel.w_l*kernel.w_c*kernel.w_ch_in*kernel.w_ch_out)-1));
- requires \valid_read(bias.b + (0..bias.b_c-1));
- requires \valid_read(attr.stride+(0..1));
- requires \valid_read(attr.pads+(0..3));
- requires \valid_read(attr.dilation+(0..1));
- requires inp.x_ch == out.y_ch;
- requires out.y_ch == kernel.w_ch_in;
- requires kernel.w_ch_in == bias.b_c;
- requires out.y_l == ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1;
- requires out.y_c == ((inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) + 1;
- requires inp.x_l > 0 && inp.x_c > 0 && inp.x_ch > 0 && inp.x_b > 0;
- requires kernel.w_l > 0 && kernel.w_c > 0 && kernel.w_ch_in > 0 && kernel.w_ch_out > 0;
- requires bias.b_c > 0;
- requires out.y_l > 0 && out.y_c > 0 && out.y_ch > 0 && out.y_b > 0;
- requires inp.x_l >= kernel.w_l;
- requires inp.x_c >= kernel.w_c;
- requires \forall integer i; 0 <= i < 4 ==> attr.pads[i] >= 0;
- requires \forall integer i; 0 <= i < 2 ==> attr.dilation[i] >= 1;
- requires \forall integer i; 0 <= i < 2 ==> attr.stride[i] >= 1;
-
-
- assigns out.y[0..(out.y_b * out.y_ch * out.y_l * out.y_c)-1];
-
- ensures \forall integer bi, ci, hi, wi, ci_in, ki_h, ki_w;
- 0 <= bi < out.y_b ==>
- 0 <= ci < out.y_ch ==>
- 0 <= hi < out.y_l ==>
- 0 <= wi < out.y_c ==>
- 0 <= ci_in
- 0 <= ki_h < kernel.w_l ==>
- 0 <= ki_w < kernel.w_c ==>
- (0 <= hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) &&
- (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] < inp.x_l) &&
- (0 <= wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1]) &&
- (wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] < inp.x_c) ==>
- out.y[bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi] == inp.x[bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) * inp.x_c + ( wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1])] * kernel.w[ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w] + bias.b[ci];
-
-
-
-
-
-
-
- */
-float* conv(input_tensor inp, convolution_kernel kernel, bias_tensor bias, attributes attr, output_tensor out) {
-
- out.y_l = ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1;
- out.y_c = ( (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) +1;
- int y_size = out.y_b * out.y_ch * out.y_l * out.y_c;
- out.y = (float *)malloc(y_size * sizeof(float));
-
- if (out.y == NULL) {
- fprintf(stderr, "Memory allocation failed\n");
- exit(EXIT_FAILURE);
- }
-
- // Compute padding
- // compute_pad(attr.auto_pad, attr.pads, attr.stride, inp.x_l, inp.x_c, kernel.w_l, kernel.w_c, out.y_l, out.y_c, attr.pads);
-
- // Initialize result tensor to bias values
- for (int bi = 0; bi < out.y_b; ++bi) {
- for (int ci = 0; ci < out.y_ch; ++ci) {
- for (int hi = 0; hi < out.y_l; ++hi) {
- for (int wi = 0; wi < out.y_c; ++wi) {
- int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi;
- out.y[y_idx] = bias.b[ci];
- }
- }
- }
- }
-
- // Convolution computation
- for (int bi = 0; bi < out.y_b; ++bi) {
- for (int ci = 0; ci < out.y_ch; ++ci) {
- for (int hi = 0; hi < out.y_l; ++hi) {
- for (int wi = 0; wi < out.y_c; ++wi) {
- int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi;
-
- for (int ci_in = 0; ci_in < kernel.w_ch_in; ++ci_in) {
- for (int ki_h = 0; ki_h < kernel.w_l; ++ki_h) {
- for (int ki_w = 0; ki_w < kernel.w_c; ++ki_w) {
- int x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0];
- int x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1];
-
- if (x_l_idx >= 0 && x_l_idx < inp.x_l && x_c_idx >= 0 && x_c_idx < inp.x_c) {
- int x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx;
- int w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w;
-
- if (x_idx < inp.x_l * inp.x_c * inp.x_ch * inp.x_b && w_idx < kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out) {
- out.y[y_idx] += inp.x[x_idx] * kernel.w[w_idx];
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- return out.y;
-}
-```
-
-# Graph execution semantics
-
-
-
-Elements of the execution semantics is given on the [IR (Intermediate
-Representation) page](https://onnx.ai/onnx/repo-docs/IR.html) of the
-ONNX web site. In addition, a Python “reference implementation” is also
-provided (see ). The source
-code of this implementation can be found at
-.
-
-Very informally, the semantics is pretty simple: each operator (or
-function) is called according to its position in the topological sorting
-of the operators. The topological order is a partial order that ensures
-that an operator is executed only when its inputs are available. Being a
-partial order, it means that several valid orders exist for a given
-graph. Normally (?) each order should generate the same result, even in
-the presence of floating point operations.
-
-The Python code to execute a graph is given in class
-[`ReferenceEvaluator`](https://github.com/onnx/onnx/blob/main/onnx/reference/reference_evaluator.py)).
-After having processed the inputs and the initializers (i.e., fed the
-`results` dictorionary with these data), the nodes are executed in
-sequence. For each operator, the interpretor checks that its inputs are
-in the `results` dictionary. If they are not, an error is raised (if the
-operators are listed in topological order, this situation should not
-occur). Otherwise, the operator is simply executed (method `run`) with
-or without a context (composed of the current results) depending on the
-type of operators. (Check that this does not create a dependency to the
-total order of operators.)
-
-
-
-### Informal specification
-
-
-
-The semantics of an ONNX model is given in Section "Model Semantics" of
-the [Intermediate
-Representation](https://github.com/onnx/onnx/blob/main/docs/IR.md) page.
-Basically, an inference-model is a stateless function (except possibly
-for some specific nodes such as a random-generation node) represented by
-an acyclic `graph` of nodes. The `graph` is mainly represented by a set
-of inputs and outputs and a topologically sorted list of nodes. Each
-node represents a call to an operator or a function. A `function` is
-itself a graph.
-
-Note that the types of inputs and outputs are not systematically
-required because they can be inferred. In our case, I guess that we will
-forbib shape inference and rely on static tensor shapes (or, at least,
-shape inference can be bone before serializing the model). The proecss
-of shape inference is described in Section [ONNX Shape
-Inference](https://onnx.ai/onnx/repo-docs/ShapeInference.html).
-
-
-
-### Formal specification
-
-*To be completed.*
-
-[^1]: At least in a first phase...
-
-[^2]: Note: in the ONNX runtime implementation, the padding value may be
- negative, which corresponds to reducing the size of the tensor.
-
-[^3]: See [Why3 documentation](https://www(W)hy3.org/)
-
-[^4]: See [Frama-C
- documentation](https://www.frama-c.com/html/documentation.html)
diff --git a/safety-related-profile/documents/issues.md b/safety-related-profile/documents/issues.md
deleted file mode 100644
index 516c7dfd..00000000
--- a/safety-related-profile/documents/issues.md
+++ /dev/null
@@ -1 +0,0 @@
-File moved [here](../deliverables/issues/issues.md).
\ No newline at end of file
diff --git a/safety-related-profile/documents/needs.md b/safety-related-profile/documents/needs.md
deleted file mode 100644
index 08d9779d..00000000
--- a/safety-related-profile/documents/needs.md
+++ /dev/null
@@ -1 +0,0 @@
-File moved [here](../deliverables/needs/needs.md).
\ No newline at end of file
diff --git a/safety-related-profile/documents/profile_formal/README.md b/safety-related-profile/documents/profile_formal/README.md
new file mode 100644
index 00000000..d635b2ce
--- /dev/null
+++ b/safety-related-profile/documents/profile_formal/README.md
@@ -0,0 +1 @@
+This directory contains the formal specification of operators, expressed using the Why3 formal language.
\ No newline at end of file
diff --git a/safety-related-profile/documents/profile_formal/onnxgraph.mlw b/safety-related-profile/documents/profile_formal/onnxgraph.mlw
new file mode 100644
index 00000000..642920dc
--- /dev/null
+++ b/safety-related-profile/documents/profile_formal/onnxgraph.mlw
@@ -0,0 +1,604 @@
+module Graph
+
+
+use int.Int
+use list.List
+use list.Map
+use option.Option
+use map.Map
+use list.Mem
+use list.FoldLeft
+use list.Length
+use list.NthNoOpt
+use map.Const
+use bool.Bool
+
+
+(* A tensor value is just an int [preliminary]*)
+type value = int
+
+(* A tensor is designated by an integer id [preliminary] *)
+(* In the actual implementation, a tensor is designted by a string *)
+(* [TXX] *)
+type tensor = int
+
+(* The state of a graph is a mapping from the set of tensors to a set optional values *)
+(* [TXX] *)
+type graph_state = Map.map tensor (option value)
+
+(* An operator *)
+(* [TODO] The inputs / outputs of an operator are typed. *)
+(* [TODO] The operator may have parameters, e.g., a convolution kernel size *)
+(* [TODO] The operator may have attributes, e.g., a relu activation function *)
+(* [TODO] The operator may have a type, e.g., a convolution, an addition, etc. *)
+(* [TODO] The operator may have a set of attributes, e.g., a relu activation function *)
+
+(* The binding proposed by a node must comply with these typing constraints *)
+type shape = list int (* dimensions of the tensor *)
+
+(* An operator is a function that takes a list of input tensors and produces a list of output tensors *)
+(* [TXX] The operator is defined by its name, its input shapes, and its output shapes *)
+type operator = {
+ name: string; (* name of the operator *)
+ opi: list shape; (* input shapes *)
+ opo: list shape; (* output shapes *)
+}
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] An operator has at least one output *)
+(* ------------------------------------------------------------------------- *)
+predicate operator_one_output (op: operator) =
+ length op.opo > 0
+
+
+goal operator_one_output_ok :
+ let op : operator = {
+ name="Add" ;
+ opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ;
+ opo= (Cons (Cons 1 Nil) Nil)
+ } in
+ operator_one_output op
+
+goal operator_one_output_ko :
+ let op : operator = {
+ name="Add" ;
+ opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ;
+ opo= Nil
+ } in
+ not operator_one_output op
+
+
+(* A node is an application of an operator *)
+(* [TXX] *)
+type node = {
+ ope: operator; (* The operator referred to by the node *)
+ oi: list tensor; (* Input tensors, position-wise *)
+ ou: list tensor; (* Output tensors, position-wise *)
+}
+
+
+(* A graph is a list of tensors and nodes + a list of input and output tensors *)
+(* [TXX] *)
+type graph = {
+ gi: list tensor; (* graph inputs *)
+ go: list tensor; (* graph outputs *)
+ gt: list tensor; (* graph tensors *)
+ gn: list node; (* graph nodes *)
+}
+
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] In a graph, a node provide all operators inputs and ouputs *)
+(* ------------------------------------------------------------------------- *)
+predicate all_operator_ins_and_outs (g: graph) =
+ forall n: node. mem n g.gn ->
+ length n.oi = length n.ope.opi /\ length n.ou = length n.ope.opo
+
+(* Specification tests *)
+goal all_operator_ins_and_outs_ok :
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons {
+ ope={
+ name="Add" ;
+ opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ;
+ opo= (Cons (Cons 1 Nil) Nil) };
+ oi=Cons 1 (Cons 2 Nil);
+ ou= (Cons 4 Nil)} Nil;
+ } in
+ all_operator_ins_and_outs g
+
+goal all_operator_ins_and_outs_ko :
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons {
+ ope={
+ name="Add" ;
+ opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ;
+ opo= (Cons (Cons 1 Nil) Nil) };
+ oi=Cons 1 (Cons 2 Nil);
+ ou= Nil} Nil; (* <= the is no output *)
+ } in
+ not all_operator_ins_and_outs g
+
+
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] In a graph, a node has at least one output tensor*)
+(* ------------------------------------------------------------------------- *)
+predicate all_node_with_one_output (g: graph) =
+ forall n: node. mem n g.gn -> n.ou <> Nil
+
+(* Specification tests *)
+goal all_node_with_one_output_ok:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)}
+ (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 8 Nil)} Nil);
+ } in
+ all_node_with_one_output g
+
+(* Specification tests *)
+goal all_node_with_one_output_ko:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou= Nil} (* This operator has no output *)
+ (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 8 Nil)} Nil);
+ } in
+ not all_node_with_one_output g
+
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] In a graph, a tensor is either an input or the output of at most one node *)
+(* ------------------------------------------------------------------------- *)
+predicate tensor_is_unique_output (g: graph) =
+ forall t: tensor.
+ mem t g.gt ->
+ (mem t g.gi \/
+ (exists n: node.
+ mem n g.gn /\ mem t n.ou /\
+ (forall n': node.
+ (mem n' g.gn /\ mem t n'.ou) -> n = n')))
+
+
+(* Specification tests *)
+goal tensor_is_unique_output_test_ok:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)}
+ (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 8 Nil)} Nil);
+ } in
+ tensor_is_unique_output g
+
+(* [TODO] This test and its negation are not valid!! *)
+goal tensor_is_unique_output_test_ko:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)}
+ (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 4 Nil)} Nil);
+ } in
+ tensor_is_unique_output g
+
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] A graph input is the input of at least one node *)
+(* (No useless input) *)
+(* ------------------------------------------------------------------------- *)
+predicate graph_ins_are_node_ins (g: graph) =
+forall t: tensor.
+ mem t g.gi ->
+ exists op. mem op g.gn /\ mem t op.oi
+
+(* Specification tests *)
+goal graph_ins_are_node_ins_test_ok:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} Nil;
+ } in
+ graph_ins_are_node_ins g
+
+goal graph_ins_are_node_ins_test_ko:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 2 Nil); ou= Cons 3 Nil } Nil;
+ } in
+ not graph_ins_are_node_ins g
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] A graph output is the output of one node *)
+(* ------------------------------------------------------------------------- *)
+predicate graph_outs_are_node_outs (g: graph) =
+ forall t: tensor. mem t g.go ->
+ exists n. mem n g.gn /\ mem t n.ou
+
+(* Specification tests *)
+goal graph_outs_are_node_outs_test_ok:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} Nil;
+ } in
+ graph_outs_are_node_outs g
+
+goal graph_outs_are_node_outs_test_ko:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou= Cons 3 Nil } Nil;
+ } in
+ not graph_outs_are_node_outs g
+
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] Graph inputs and ouputs belong to the set of tensors *)
+(* ------------------------------------------------------------------------- *)
+predicate graph_ins_outs_are_tensors (g: graph) =
+ forall t: tensor. (mem t g.gi \/ mem t g.go) -> mem t g.gt
+
+(* Specification tests *)
+goal graph_ins_outs_are_tensors_test_ok:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil);
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} Nil;
+ } in
+ graph_ins_outs_are_tensors g
+
+goal graph_ins_outs_are_tensors_test_ko:
+ let g : graph = {
+ gi = Cons 1 (Cons 2 Nil);
+ go = Cons 3 (Cons 4 Nil); (* <= Output 4 is not in the tensor set *)
+ gt = Cons 1 (Cons 2 (Cons 3 (Cons 5 Nil)));
+ gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou= Cons 3 Nil } Nil;
+ } in
+ not graph_ins_outs_are_tensors g
+
+
+(* ------------------------------------------------------------------------- *)
+(* [TXX] A tensor is either a graph input or a node output *)
+(* ------------------------------------------------------------------------- *)
+predicate no_free_tensor (g: graph) =
+ forall t: tensor. mem t g.gt ->
+ mem t g.gi \/
+ exists n. mem n g.gn \/ mem t n.ou
+
+
+
+(* =========================================================================== *)
+(* Utilities *)
+(* =========================================================================== *)
+
+(* Create t list of n elements with the same value x *)
+let rec make_list (x: 'a) (n: int) : list 'a
+requires { n >= 0 }
+ensures{ length result = n}
+ensures{ forall v. mem v result -> v = x }
+variant { n }
+=
+ if n = 0
+ then
+ Nil
+ else
+ Cons x (make_list x (n - 1))
+
+(* Fold implementation with one parameters *)
+(*[TODO] Ensures is missing... *)
+let rec fold_left (f: 'acc -> 'a -> 'acc) (acc: 'acc) (l: list 'a) : 'acc
+ variant { l }
+= match l with
+ | Nil -> acc
+ | Cons x xs -> fold_left f (f acc x) xs
+ end
+
+(* Fold implementation with two parameters *)
+(* [TODO] See fold_product in List*)
+let rec fold_left2 (f: 'acc -> 'a -> 'b -> 'acc)
+ (acc: 'acc)
+ (l1: list 'a)
+ (l2: list 'b) : 'acc
+ requires { length l1 = length l2 }
+ variant { l1 }
+ = match l1, l2 with
+ | Nil, Nil -> acc
+ | Cons x xs, Cons y ys -> fold_left2 f (f acc x y) xs ys
+ | _, _ -> absurd
+ end
+
+(* Simple map function *)
+let rec map (f: 'a -> 'b) (l: list 'a) : list 'b
+ensures { length result = length l }
+variant { l }
+= match l with
+ | Nil -> Nil
+ | Cons x xs -> Cons (f x) (map f xs)
+end
+
+
+(* =========================================================================== *)
+(* Simple implementation of a map (using a list) *)
+(* Used to associate a value to a tensor *)
+(* =========================================================================== *)
+
+type fmap = list (tensor, option value)
+
+(* Get an item from the map (spec)*)
+(* Logic functions must be total, so we return None if the tensor is not in the map. *)
+ (* /\ I wuld like to specify this function as if exists k,v . mem (k,v) m then v else None *)
+function fget_logic (m: fmap) (k: tensor) : option value =
+ match m with
+ | Nil -> None
+ | Cons (k', v) xs -> if k = k' then v else fget_logic xs k
+ end
+
+(* Get an item from the map (imp)*)
+let rec fget (m: fmap) (k: tensor) : option value =
+ requires{ exists v. mem (k,v) m } (* The tensor must be in the map (absurd will never be reached) *)
+ requires{ forall v1, v2. mem (k,v1) m /\ mem (k,v2) m -> v1=v2}
+ (* The tensor must be in the map once and only once*)
+ (* Relaxed : it may appear several times, but with theesam value *)
+ ensures { result = fget_logic m k}
+ variant { m }
+ match m with
+ | Nil -> absurd
+ | Cons (k', v) xs ->
+ if k = k' then v
+ else fget xs k
+ end
+
+(* Set an item in the map (spec) *)
+function fset_logic (m: fmap) (k: tensor) (v: option value) : fmap =
+ match m with
+ | Nil -> Cons (k, v) Nil
+ | Cons (k', v') xs ->
+ if k = k' then Cons (k, v) xs
+ else Cons (k', v') (fset_logic xs k v)
+ end
+
+(* Set an item in the map (imp) *)
+let rec fset (m: fmap) (k: tensor) (v: option value) : fmap =
+ ensures { result = fset_logic m k v } (* The value is correctly set *)
+ ensures{ forall k', v'. mem (k', v') m /\ k' <> k ->
+ exists k'', v''. mem (k'',v'') result /\ v'=v''} (* The other values are not modified *)
+ variant { m }
+ match m with
+ | Nil -> Cons (k, v) Nil
+ | Cons (k', v') tl ->
+ if k = k' then Cons (k, v) tl
+ else Cons (k', v') (fset tl k v)
+ end
+
+(* If I set (k,v) in the map, then get(k) shall return v *)
+lemma get_set_eq:
+ forall m: fmap, k: tensor, v: option value.
+ fget_logic (fset_logic m k v) k = v
+
+(* When I set (k,v) in the map, then the rest of the map must not change,
+i.e., for any other key k', the value associated with k' shall not change *)
+lemma get_set_neq:
+ forall m: fmap, k1 k2: tensor, v: option value.
+ k1 <> k2 ->
+ fget_logic (fset_logic m k2 v) k1 = fget_logic m k1
+
+(* ------------------------------------------------------------------------- *)
+(* Link ("collage") between the abstract map and the implementation map *)
+(* ------------------------------------------------------------------------- *)
+
+(* Logical projection from the concrete fmap to the abstract map *)
+function fmap_to_map (m: fmap): Map.map tensor (option value) =
+ fun k -> fget_logic m k
+
+(* [TODO] The inverse projection is not defined, as the fmap is not a total function *)
+(* It is not possible to get a fmap from a map, as the map may contain keys that are not in the fmap *)
+
+(* Logical projection from the abstract map to the concrete fmap *)
+function map_to_fmap (m: Map.map tensor (option value)) : fmap
+
+(* get for fmap and Map are equivalent *)
+lemma fmap_get_complies_with_map_get:
+ forall m: fmap, k: tensor.
+ match fget_logic m k with
+ | Some v -> Map.get (fmap_to_map m) k = Some(v)
+ | None -> true
+ end
+
+(* set for fmap and Map are equivalent *)
+lemma fmap_set_complies_with_map_set:
+ forall m: fmap, k: tensor, v: option value.
+ fmap_to_map (fset_logic m k v) = Map.set (fmap_to_map m) k v
+
+
+(* =========================================================================== *)
+(* Operators *)
+(* =========================================================================== *)
+
+(* Computes the outputs of a node *)
+(* Currently, this is a dummy implementation that returns a list of identical values *)
+function eval_operator_log (op: operator) (inputs: list (option value)) : list (option value)
+
+(* Function modeling the behavior of an operator *)
+(* [TODO] Replace with actual operators *)
+let eval_operator (op: operator) (inputs: list (option value)) : list (option value)
+requires { length inputs = length op.opi } (* The node provides as any iputs as needed by the operator *)
+requires { forall i. mem i inputs -> i <> None } (* All inputs are initialized before execution *)
+ensures { forall i. mem i result -> i <> None } (* All outputs are initialized after execution*)
+ensures { length result = length op.opo } (* There is one value per output tensor *)
+=
+ (* This is a dummy implementation that returns the appropriate number of identical values *)
+ make_list (Some 0) (length op.opo)
+
+
+(* ------------------------------------------------------------------------- *)
+(* True if a tensor t is initialized in state s. *)
+(* ------------------------------------------------------------------------- *)
+predicate tensor_is_initialized (s: graph_state) (t: tensor) =
+ match Map.get s t with
+ | Some _ -> true
+ | None -> false
+end
+
+(* ------------------------------------------------------------------------- *)
+(* True if a tensor t is initialized in state s. *)
+(* ------------------------------------------------------------------------- *)
+let is_initialized (s: graph_state) (t: tensor) : bool
+ensures { result = tensor_is_initialized s t }
+=
+ match Map.get s t with
+ | Some _ -> true
+ | None -> false
+ end
+
+let rec are_initialized (s: graph_state) (l: list tensor) : bool =
+ensures { result = forall t. mem t l -> tensor_is_initialized s t }
+variant {l}
+match l with
+| Nil -> true
+| Cons x xs -> (is_initialized s x) && (are_initialized s xs)
+end
+
+(* ------------------------------------------------------------------------- *)
+(* True if node op is executable in state s *)
+(* ------------------------------------------------------------------------- *)
+predicate node_is_ready (s: graph_state) (op: node) =
+ (* A node is ready if all its inputs tensors are initialized *)
+ forall t: tensor.
+ mem t op.oi -> tensor_is_initialized s t
+
+let node_ready (s: graph_state) (op: node) : bool
+ensures { result = true <-> node_is_ready s op }
+=
+ are_initialized s op.oi
+
+
+(* =========================================================================== *)
+(* Graph execution *)
+(* =========================================================================== *)
+
+(* --------------------------------------------------------------------------- *)
+(* Execute one node *)
+(* --------------------------------------------------------------------------- *)
+let ghost exec_node (s: graph_state) (n: node) : graph_state
+requires { node_is_ready s n } (* The node must be ready to be executed *)
+requires { length n.oi = length n.ope.opi } (* The number of inputs must match the number of operator's inputs *)
+requires { length n.ou = length n.ope.opo } (* The number of outputs must match the number of operator's outputs *)
+ensures { forall t: tensor. mem t n.ou -> tensor_is_initialized result t } (* All output tensors are set *)
+=
+ (* Function that sets the value of a tensor in the graph state *)
+ let f (s: graph_state)(t: tensor)(v: (option value) ) : graph_state
+ (* The value of the tensor is set to v in the state st *)
+ requires { v <> None } (* We never assign a None value during the execution of a node *)
+ ensures { tensor_is_initialized result t }
+ =
+ fun k -> fget (fset (map_to_fmap s) t v) k
+ in
+ (* the values of tensors that are inputs to a node *)
+ let inputs = map (fun t -> Map.get s t) n.oi in
+ assert { forall v. mem v inputs -> v <> None };
+ (* the values of all outputs after evaluation *)
+ let outputs = eval_operator n.ope inputs in
+ assert { forall v. mem v outputs -> v <> None };
+ (* the updated state *)
+ fold_left2 (f) s n.ou outputs
+
+(* --------------------------------------------------------------------------- *)
+(* Execute all nodes.
+ Returns the graph state after executing all nodes in the graph
+ The next node to be executed is chosen non deterministically among the
+ executable nodes *)
+(* --------------------------------------------------------------------------- *)
+
+let rec ghost exec_nodes (s: graph_state) (ns: list node) : graph_state =
+ requires { forall n. mem n ns -> node_is_ready s n } (* All nodes in the list are ready to be executed *)
+ ensures { forall n : node. mem n ns ->
+ forall t: tensor. mem t n.ou ->
+ tensor_is_initialized result t } (* All outputs of the nodes are initialized *)
+ variant { ns }
+ match ns with
+ | Nil -> s
+ | Cons n ns ->
+ let s' = exec_node s n in
+ exec_nodes s' ns
+ end
+
+
+let rec filter ( b: node -> bool) (l: list node) : list node =
+ ensures { forall n. mem n result <-> (mem n l /\ b n) } (* The result contains only nodes that satisfy the predicate b *)
+ variant {l}
+ match l with
+ | Nil -> Nil
+ | Cons x xs ->
+ if b x then Cons x (filter b xs)
+ else filter b xs
+ end
+
+
+let rec ghost exec_nodes_until_completion (s: graph_state) (ns: list node) : graph_state =
+ (* All the output tensors of the nodes in ns are initialized *)
+ requires { forall n. mem n ns -> node_is_ready s n } (* All nodes in ns are ready *)
+ ensures { forall n: node. mem n ns ->
+ forall t: tensor. mem t n.ou ->
+ tensor_is_initialized result t } (* All outputs of the nodes are initialized *)
+ variant {ns}
+ let en = filter (fun n -> node_ready s n) ns
+ in
+ let dn = filter (fun n -> not node_ready s n) ns
+ in
+ match en with
+ | Nil -> s (* No node is ready, return the current state *)
+ | Cons n _ ->
+ let s' = exec_node s n in
+ exec_nodes_until_completion s' dn (* Execute the node and continue with the remaining nodes *)
+ end
+
+let ghost exec_graph (s: graph_state) (g: graph) : graph_state
+ requires { forall t. mem t g.gi -> tensor_is_initialized s t } (* The graph can only be executed if its inputs are initialized *)
+ ensures { forall t. mem t g.go -> tensor_is_initialized result t } (* After execution, all outputs are initialized *)
+ =
+ exec_nodes_until_completion s g.gn
+
+
+(* ------------------------------------------------------------------------- *)
+(* Initial tensor state: all tensors are undefined *)
+(* ------------------------------------------------------------------------- *)
+let ghost set_initial_state (g: graph) : graph_state =
+ fold_left (fun s t -> Map.set s t None) (Const.const None) g.gt
+
+
+(* ------------------------------------------------------------------------- *)
+(* /\ !Simple test: a fold-left and on list l is true iff all elements of
+ l are true. Why does the proof fail? *)
+(* ------------------------------------------------------------------------- *)
+predicate all_true (l: list bool) =
+ forall b: bool. mem b l -> b = true
+
+lemma and_true_true:
+ forall a: bool, b: bool. Bool.andb a b <-> a /\ b
+
+goal fold_left_and_equiv_all_true:
+ forall l: list bool.
+ FoldLeft.fold_left Bool.andb true l = true <-> all_true l
+(* ------------------------------------------------------------------------- *)
+
+
+end
+
+
+
diff --git a/safety-related-profile/documents/profile_graph/graph.md b/safety-related-profile/documents/profile_graph/graph.md
index 6a1a0eda..7987c3fe 100644
--- a/safety-related-profile/documents/profile_graph/graph.md
+++ b/safety-related-profile/documents/profile_graph/graph.md
@@ -1 +1,556 @@
-(Specification of the graph semantics and constraints. *To be completed*)
\ No newline at end of file
+## Restrictions
+The following restrictions apply to graphs in the SONNX profile:
+
+| Restriction | Statement | Origin |
+| -------- | ------- | ------- |
+| `[R1]`| There shall be a 1-to-1 mapping between the inputs and outputs of a node and the inputs and outputs of its associated operator. | TBC |
+| `[R2]` | All computation nodes of a graph must contribute to the computation of the graph outputs | No dead computation nodes |
+| `[R3]` | A graph must only contain computation nodes referring to operators in the SONNX subset | TBC |
+
+> (eric) Add a reference to ONNX documentation, e.g., [ONNX concepts](https://onnx.ai/onnx/intro/concepts.html).
+
+# Informal specification
+
+## Definitions
+### Graph
+- `[T01a]` A graph is an acyclic graph composed of *nodes* and *edges*
+- `[T01b]` A node is either a *tensor node* or a *computation node*
+
+> The fact that the graph is acyclic may not be necessary since there is also the SSA constraint...
+
+In the following example[^1], written using the `onnxruntime` API, the graph is composed of four computation nodes (`add_node`, `cons_node`, `mul_node1`, `mul_node2`) and 4 tensors nodes (`g_i1` and `g_i2`, `op1_o`, `op3_o`). The complete example in given in Section [example](#example) below).
+
+```python
+# Create nodes
+add_node = onnx.helper.make_node(op_type="Add", inputs=[g_i1_id, g_i2_id], outputs=[op1_o_id])
+cons_node = onnx.helper.make_node("Constant", inputs=[], outputs=[op2_o_id], value=const_value)
+mul_node1 = onnx.helper.make_node("Mul", inputs=[op1_o_id, op2_o_id], outputs=[op3_o_id])
+mul_node2 = onnx.helper.make_node("Mul", inputs=[g_i1_id, g_i2_id], outputs=[op4_o_id])
+[...]
+# Create graph
+graph = onnx.helper.make_graph(
+ nodes=[add_node, cons_node, mul_node1, mul_node2],
+ name="Example",
+ inputs=[g_i1, g_i2],
+ outputs=[op1_o, op3_o]
+)
+[...]
+```
+[^1]: The complete example in given in Section [example](#example) below.
+
+
+### Tensor nodes
+- `[T02a]` A tensor node is an object that can have a value or no value
+- `[T02b]` A tensor node is identified by a unique identifier within a graph
+
+`[RX]` A tensor node that is not the input of a computation node (i.e., it does not belong to any edges to a computation node input) must be an output node of the graph.
+
+In the following example[^1], written using the `onnxruntime` API, a tensor node is created using the `onnx.helper.make_tensor_value_info` function. In this example, `op1_o` is a tensor of rank 2 with no given dimensions. Its identifier is `op1_o` is `OP1_O`.
+
+```python
+op1_o_id = "OP1_O"
+op1_o = onnx.helper.make_tensor_value_info(op1_o_id, onnx.TensorProto.FLOAT, [None, None])
+```
+
+### Data flow computation nodes
+
+- `[T03a]` A computation node specifies some relation between its inputs and its outputs.
+- `[T03b]` The relation is defined by the *operator* that is associated with the node. The semantics of operators is defined in the SONNX profile opset (see, e.g., [add](../profile_opset/add/add.md)).
+ - Note that multiple computation nodes can refer to the same operator.
+- `[T03c]` There is a 1-to-1 mapping between the computation node's inputs and outputs and those of its associated operator .
+
+- `[RX]` A computation node must belong to at least one path from an input tensor to an output tensor of the graph (i.e., there shall be no "dead" node)
+- `[Rx]` A computation node must refer to an operator in the SONNX profile.
+
+In the following example[^1], nodes `mul_node1` and `mul_node2` refer to the same operator [`Mul`](../profile_opset/mul/mul.md) that has 2 inputs and 1 output.
+
+```python
+mul_node1 = onnx.helper.make_node("Mul", [op1_o_id, op2_o_id], [op3_o_id])
+mul_node2 = onnx.helper.make_node("Mul", [g_i1_id, g_i2_id], [op4_o_id])
+```
+
+### Control flow computation nodes
+
+> See later.
+
+
+### Edges
+- `[T04a]` An edge is a relation between a computation node, one input or output of this computation node, and a tensor node. A tensor that is related to some computation node input (resp. output) is said to be an input (resp. output) of the computation node.
+
+- `[Rx]` All inputs and outputs of all computation nodes must belong to an edge.
+ - Note that is is a restriction with respect to the ONNX standard that allows fewer inputs or outputs when the omitted input or output is optional
+
+In the following example[^1], edges are defined by the `inputs` and `outputs` arguments of the `make_node` and `make_graph` functions. For instance, the `add_node` computation node has 2 inputs (one for each element of the `inputs` list, noted $i_1$, $i_2$ hereafter) and 1 output (one for each element of the `outputs` list, noted $o$). The corresponding graph edges are:
+- e1: (add_node, $i_1$, g_i1_id)
+- e2: (add_node, $i_2$, g_i2_id)
+
+```python
+# Create nodes
+add_node = onnx.helper.make_node(op_type="Add", inputs=[g_i1_id, g_i2_id], outputs=[op1_o_id])
+cons_node = onnx.helper.make_node("Constant", inputs=[], outputs=[op2_o_id], value=const_value)
+mul_node1 = onnx.helper.make_node("Mul", inputs=[op1_o_id, op2_o_id], outputs=[op3_o_id])
+mul_node2 = onnx.helper.make_node("Mul", inputs=[g_i1_id, g_i2_id], outputs=[op4_o_id])
+[...]
+# Create graph
+graph = onnx.helper.make_graph(
+ nodes=[add_node, cons_node, mul_node1, mul_node2],
+ name="Example",
+ inputs=[g_i1, g_i2],
+ outputs=[op1_o, op3_o]
+)
+[...]
+```
+
+### Operators
+- `[T05a]` An operator specifies a function, i.e., a relation between the values of some input variables (the arguments of the function) and some output variables. For instance operation $z=\text{\bf add}(x,y)$ specifies a relation between variables $x$, $y$, and $z$ such that $z=x+y$.
+ - The set of input variables may be empty (case of a constant function).
+
+
+> (eric) Can a node have an internal state? In that case, it is not functional.
+
+### Example
+The following figure gives an example of a simple graph composed of 4 nodes. The graph has 2 input and 2 output tensors. Note that output of one of the nodes, `mul_node2`, is not used: this is actually forbidden in SONNX.
+
+
+
+The `onnxruntime`used to create the graph is given hereafter:
+
+```python
+import onnx
+import onnxruntime as ort
+import numpy as np
+
+# Define tensor identifiers
+g_i1_id = "G_I1"
+g_i2_id = "G_I2"
+op1_o_id = "OP1_O"
+op2_o_id = "OP2_O"
+op3_o_id = "OP3_O"
+op4_o_id = "OP4_O"
+
+# Create 2-rank input tensors (two inputs for division)
+g_i1 = onnx.helper.make_tensor_value_info(g_i1_id, onnx.TensorProto.FLOAT, [None, None])
+g_i2 = onnx.helper.make_tensor_value_info(g_i2_id, onnx.TensorProto.FLOAT, [None, None])
+
+# Create output tensors
+op1_o = onnx.helper.make_tensor_value_info(op1_o_id, onnx.TensorProto.FLOAT, [None, None])
+op2_o = onnx.helper.make_tensor_value_info(op2_o_id, onnx.TensorProto.FLOAT, [None, None])
+op3_o = onnx.helper.make_tensor_value_info(op3_o_id, onnx.TensorProto.FLOAT, [None, None])
+op4_o = onnx.helper.make_tensor_value_info(op4_o_id, onnx.TensorProto.FLOAT, [None, None])
+
+# Create a constant value
+const_value = onnx.helper.make_tensor(
+ name='const_tensor',
+ data_type=onnx.TensorProto.FLOAT,
+ dims=[2, 2],
+ vals=np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32).flatten() # Example values, adjust as needed
+)
+
+# Create nodes
+add_node = onnx.helper.make_node("Add", [g_i1_id, g_i2_id], [op1_o_id])
+cons_node = onnx.helper.make_node("Constant", [], [op2_o_id], value=const_value)
+mul_node1 = onnx.helper.make_node("Mul", [op1_o_id, op2_o_id], [op3_o_id])
+mul_node2 = onnx.helper.make_node("Mul", [g_i1_id, g_i2_id], [op4_o_id])
+
+# Create the ONNX graph
+graph = onnx.helper.make_graph(
+ nodes=[add_node, cons_node, mul_node1, mul_node2],
+ name="Example",
+ inputs=[g_i1, g_i2],
+ outputs=[op1_o, op3_o]
+)
+
+# Create the ONNX model
+model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid("", 13)], ir_version=10) # Explicitly set opset to 13 and ir_version to 10
+onnx.checker.check_model(model)
+print(onnx.helper.printable_graph(model.graph))
+
+# Save the model
+onnx.save(model, "graph.onnx")
+
+# Load and run the model using ONNX Runtime
+session = ort.InferenceSession("graph.onnx")
+
+# Do inference
+i1 = np.array([[1.0, 2.0],[3.0, 4.0]], dtype=np.float32)
+i2 = np.array([[3.0, 4.0],[5.0, 6.0]], dtype=np.float32)
+output = session.run(None, {g_i1_id: i1, g_i2_id: i2})
+
+# Display results
+i1_f=(np.array2string(i1, separator=',', max_line_width=np.inf).replace('\n', ''))
+i2_f=(np.array2string(i2, separator=',', max_line_width=np.inf).replace('\n', ''))
+o1_f=(np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\n', ''))
+o2_f=(np.array2string(output[1], separator=',', max_line_width=np.inf).replace('\n', ''))
+
+# Display results
+np.set_printoptions(precision=None, floatmode='fixed')
+print(f"I1={i1_f}, I2={i2_f}")
+print(f"Result={o1_f}{o2_f}")
+```
+
+## Execution Semantics
+Executing a graph means evaluating the output tensors of the graph according to the following rules:
+- `[T06a]` A computation node is executable if all the tensors connected to its inputs (i.e., belonging to an edge) are initialized
+
+> Only valid for data flow graphs. To be completed to account for control flow nodes.
+
+- `[T06b]` Executing a computation node means assigning values to the tensors connected to its outputs (i.e., belonging to an edge) so that the relation specified by the operator between its inputs and outputs holds
+- `[T06c]` All executable computation nodes shall be executed
+- `[T06e]` A tensor shall be assigned a value at most once (Single Assignment)
+
+
+## Special nodes
+
+ONNX provides two other categories of special nodes:
+- function nodes
+- control flow nodes.
+
+*This section is very preliminary*
+
+The way graphs are described and executed is independent from the operators used in the graph. In other terms, the semantics of the graph (how the operators are called) and the semantics of the operators (what the operators do) are defined separately. This modularity applies to the standard operators (convolution, relu, etc.) and to the special function and control flow nodes.
+
+### Functions nodes
+
+A "function" node is defined using primitive ONNX operators or other function operators. Function nodes are introduced [here](https://onnx.ai/onnx/intro/concepts.html), Section "Functions".
+
+Some ONNX operators are defined as functions, e.g.:
+- Softplus
+- Softsign
+- - Celu
+- HardSwish
+- Gelu
+- Selu
+
+
+For instance, the `SoftwaPlus`operator is defined as follows:
+```
+<
+ domain: "",
+ opset_import: ["" : 18]
+>
+Softplus (X) => (Y)
+{
+ exp_x = Exp (X)
+ one = Constant ()
+ one_cast = CastLike (one, X)
+ exp_x_add_one = Add (exp_x, one_cast)
+ Y = Log (exp_x_add_one)
+}
+```
+
+which is a readable description directly generated from the implementation model which is located in file [onnx/defs/math/defs.cc](https://github.com/onnx/onnx/blob/main/onnx/defs/math/defs.cc)
+
+A function operator has a `.FunctionBody()` call that describes the subgraph:
+
+```
+ONNX_OPERATOR_SET_SCHEMA(
+ Softplus,
+ 22,
+ OpSchema()
+ .SetDoc(Softplus_ver22_doc)
+ .Input(0, "X", "Input tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
+ .Output(0, "Y", "Output tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
+ .TypeConstraint("T", OpSchema::all_float_types_ir4(), "Constrain input and output types to float tensors.")
+ .TypeAndShapeInferenceFunction(propagateShapeAndTypeFromFirstInput)
+ .FunctionBody(
+ R"ONNX(
+ {
+ exp_x = Exp (X)
+ one = Constant ()
+ one_cast = CastLike (one, X)
+ exp_x_add_one = Add (exp_x, one_cast)
+ Y = Log (exp_x_add_one)
+ }
+ )ONNX",
+ 18));
+```
+
+The function body can be obtained using the Python API:
+```Python
+schema = defs.get_schema("Softplus", domain="", max_inclusive_version=21)
+fproto = schema.function_body
+print(str(fproto))
+```
+
+that returns:
+```
+name: "Softplus"
+input: "X"
+output: "Y"
+node {
+ input: "X"
+ output: "exp_x"
+ op_type: "Exp"
+ domain: ""
+}
+node {
+ output: "one"
+ op_type: "Constant"
+ attribute {
+ name: "value"
+ t {
+ data_type: 1
+ float_data: 1
+ name: ""
+ }
+ type: TENSOR
+ }
+ domain: ""
+}
+node {
+ input: "one"
+ input: "X"
+ output: "one_cast"
+ op_type: "CastLike"
+ domain: ""
+}
+node {
+ input: "exp_x"
+ input: "one_cast"
+ output: "exp_x_add_one"
+ op_type: "Add"
+ domain: ""
+}
+node {
+ input: "exp_x_add_one"
+ output: "Y"
+ op_type: "Log"
+ domain: ""
+}
+doc_string: "\nSoftplus takes one input data (Tensor) and produces one output data\n(Tensor) where the softplus function, y = ln(exp(x) + 1), is applied to\nthe tensor elementwise.\n"
+opset_import {
+ domain: ""
+ version: 18
+}
+domain: ""
+```
+
+Only function operators have the `.FunctionBody` description. For instance, for the `Abs` operator, we find, in the source tree:
+
+```
+ONNX_OPERATOR_SET_SCHEMA(
+ Abs,
+ 13,
+ OpSchema()
+ .SetDoc(Abs_ver13_doc)
+ .Input(0, "X", "Input tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
+ .Output(0, "Y", "Output tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable)
+ .TypeConstraint(
+ "T",
+ OpSchema::all_numeric_types_ir4(),
+ "Constrain input and output types to all numeric tensors.")
+ .TypeAndShapeInferenceFunction(propagateShapeAndTypeFromFirstInput));
+```
+
+To check whether an operator is primitive or not, one can use the ONNX `get_schema()`function:
+```
+from onnx import defs
+schema = defs.get_schema("Softmax", domain="", max_inclusive_version=21)
+print(schema.has_function)
+schema = defs.get_schema("Softplus", domain="", max_inclusive_version=21)
+print(schema.has_function)
+False
+True
+``
+
+The complete list of function operators can be optained using:
+```Python
+from onnx import defs
+function_ops = [
+ s.name for s in defs.get_all_schemas_with_history() if s.has_function
+]
+print(function_ops)
+```
+
+
+
+The representation of functions is models defined in[onnx.proto](https://github.com/onnx/onnx/blob/main/onnx/onnx.proto), item `message Functionproto`.
+
+
+- A `function` operator encapsulates a graph.
+- Executing a function operator means executing the encapsulated graph according to the graph execution semantics described before.
+- An encapsulated graph may itself use `function` nodes, in a hierarchical manner.
+- A valid ONNX graph with function nodes must be always actually converted to an equivalent ONNX graph without any function nodes. This forbids any direct or indirect recursion.
+
+### Control-flow operators
+- ONNX provides three control flow operators:
+ - [If](https://onnx.ai/onnx/operators/onnx__If.html)
+ - [Loop](https://onnx.ai/onnx/operators/onnx__Loop.html)
+ - [Scan](https://onnx.ai/onnx/operators/onnx__Scan.html)
+
+As stated in the [documentation]() The structures are slow and complex. It is better to avoid them if possible". We define their semantics for completeness, but the SONNX profile does not contain any control flow operator.
+
+Those nodes take either one (e.g, operators `loop`, `scan`,...) or two graphs (`if`) as attributes and execute them graphs according to their specific semantics.
+
+The `if` operator is functional: each tensor is at most assigned once (SSA).
+
+The `loop`or `scan` operators are functional: each tensor is assigned at most once, each iteration producing a different tensor. Semantically, a `loop` or `scan` operator can be replaced by its unrolled version (looping $N$ times over graph $g$ on input $x$ is conceptually equivalent to computing $g \circ g \circ ... \circ (x)$.
+```
+f_body : (iter, cond, state) → (cond', state')
+Loop(N, True, state0)
+ = let (cond1, state1) = f_body(0, True, state0)
+ (cond2, state2) = f_body(1, cond1, state1)
+ ...
+ (condN, stateN) = f_body(N-1, condN-1, stateN-1)
+ in stateN
+```
+
+#### The `loop` operator
+
+- A `loop` operator executes some graph (`body`) according to some condition whihc can be a number of iterations or a certain condition.
+> The graph run each iteration. It has 2+N inputs: (iteration_num, condition, loop carried dependencies…). It has 1+N+K outputs: (condition, loop carried dependencies…, scan_outputs…). Each scan_output is created by concatenating the value of the specified output value at the end of each iteration of the loop. It is an error if the dimensions or data type of these scan_outputs change across loop iterations.
+
+
+
+#### The `if` operator
+
+- An `if` operator, takes one boolean input and two attributes (`then_branch` and `else_branch`). When the input is true, the graph designated by the `then_branch` is executed, otherwise, the graph designed by the `else_branch`is executed.
+- The `if` operator has one variadic output that is composed of the outputs of the two sub-graphs.
+- As for any other operator, the `if` node has a single list of outputs (e.g., `[Y1, Y2, ..., Yn]`).
+ - The `then_branch` and the `else_branch` subgraphs must produce the same number and types of outputs, and those are mapped to the outputs of the `if` node.
+ - For a given output, the shape of the corresponding output tensor of the then and else branches can be different, but the shape of the `if` outputs must be compatible with both of them.
+- The then and else subgraphs have their own input tensors. Only the subgraph that is selected by the boolean input is executed. This means that if the tensors of the
+
+
+> This means that in the definition of the graph semantics, some execution nodes may ot be executed.
+
+```
+import onnx
+from onnx import helper, TensorProto, numpy_helper
+
+cond_input = helper.make_tensor_value_info("cond", TensorProto.BOOL, [])
+
+if_output = helper.make_tensor_value_info("if_out", TensorProto.FLOAT, [])
+
+# Create a constant node for the then branch
+then_const = helper.make_node("Constant", [], ["then_out"],
+ value=helper.make_tensor(name="const_tensor_1", data_type=TensorProto.FLOAT, dims=[], vals=[1.0])
+)
+then_output = helper.make_tensor_value_info("then_out", TensorProto.FLOAT, [])
+
+# The "then" subgraph contains a unique "constant" node.
+then_branch = helper.make_graph([then_const], "then_branch", [], [then_output])
+
+# Create a constant node for the else branch
+else_const = helper.make_node("Constant", [], ["else_out"],
+ value=helper.make_tensor(name="const_tensor_0", data_type=TensorProto.FLOAT, dims=[], vals=[0.0])
+)
+else_output = helper.make_tensor_value_info("else_out", TensorProto.FLOAT, [])
+
+# The "else" subgraph contains a unique "constant" node.
+else_branch = helper.make_graph([else_const], "else_branch", [], [else_output])
+
+# Create the "if" node that contains the "then" and "else" subgraphs.
+if_node = helper.make_node(
+ "If",
+ inputs=["cond"],
+ outputs=["if_out"],
+ then_branch=then_branch,
+ else_branch=else_branch
+)
+
+# Ceate the toplevel graph.
+graph = helper.make_graph(
+ nodes=[if_node],
+ name="if_example",
+ inputs=[cond_input],
+ outputs=[if_output]
+)
+
+model = helper.make_model(graph, producer_name="onnx-if-minimal")
+onnx.checker.check_model(model)
+onnx.save(model, "if_example.onnx")
+
+```
+
+- In addition, they may use tensors directly available in the scope of the `if` operator. In the following example, the `then_branch` and the `else_branch` subgraphs "capture" tensor `X` that is declared in the top-level graph.
+
+
+> **DO WE ACCEPT SUCH CAPTURE OR DO WE IMPOSE ALL FLOWS TO BE DECLARED IN THE IF NODE**?
+
+```
+import onnx
+from onnx import helper, TensorProto
+
+
+cond_input = helper.make_tensor_value_info("cond", TensorProto.BOOL, [])
+
+# X is an input tensor of the graph
+x_input = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1])
+
+if_output = helper.make_tensor_value_info("if_out", TensorProto.FLOAT, [1])
+
+# Create constant node with value 2
+two_const = helper.make_node(
+ "Constant",
+ inputs=[],
+ outputs=["two"],
+ value=helper.make_tensor("two_val", TensorProto.FLOAT, [1], [2.0])
+)
+
+# Create node X*2
+mul_node = helper.make_node("Mul", ["X", "two"], ["then_out"])
+then_output = helper.make_tensor_value_info("then_out", TensorProto.FLOAT, [1])
+then_branch = helper.make_graph(
+ [two_const, mul_node],
+ "then_branch",
+ inputs=[],
+ outputs=[then_output]
+)
+
+# Create constant node with value 3
+three_const = helper.make_node(
+ "Constant",
+ inputs=[],
+ outputs=["three"],
+ value=helper.make_tensor("three_val", TensorProto.FLOAT, [1], [3.0])
+)
+
+# Create node X+3
+add_node = helper.make_node("Add", ["X", "three"], ["else_out"])
+else_output = helper.make_tensor_value_info("else_out", TensorProto.FLOAT, [1])
+else_branch = helper.make_graph(
+ [three_const, add_node],
+ "else_branch",
+ inputs=[],
+ outputs=[else_output]
+)
+
+# Create IF node with no input (but the condition)
+if_node = helper.make_node(
+ "If",
+ inputs=["cond"],
+ outputs=["if_out"],
+ then_branch=then_branch,
+ else_branch=else_branch
+)
+
+# Create the main graph
+main_graph = helper.make_graph(
+ [if_node],
+ "if_capture_outer_scope",
+ inputs=[cond_input, x_input],
+ outputs=[if_output]
+)
+
+# Model
+model = helper.make_model(main_graph, producer_name="onnx-if-outer-scope")
+onnx.checker.check_model(model)
+onnx.save(model, "if_capture_outer_scope.onnx")
+```
+
+
+## Additional remarks
+
+### Properties of a graph
+- If all operators are purely functional (stateless), a graph is also purely functional, i.e., the values of its outputs only depends on the values of its inputs and the values of the attributes of its nodes.
+- If all operators are deterministic, a graph is also deterministic, i.e., for a given set of input values, the execution of the graph always gives the same output values.
+- A graph has no side effect, i.e., the only visible effects of a graph are via its outputs.
+
+
+
+
+
+# Formal specification
+
+See draft [here](../profile_formal/onnxgraph.mlw).
diff --git a/safety-related-profile/documents/profile_graph/imgs/div.onnx b/safety-related-profile/documents/profile_graph/imgs/div.onnx
new file mode 100644
index 00000000..8d5ef93a
Binary files /dev/null and b/safety-related-profile/documents/profile_graph/imgs/div.onnx differ
diff --git a/safety-related-profile/documents/profile_graph/imgs/ex1_netron.png b/safety-related-profile/documents/profile_graph/imgs/ex1_netron.png
new file mode 100644
index 00000000..2d3324eb
Binary files /dev/null and b/safety-related-profile/documents/profile_graph/imgs/ex1_netron.png differ
diff --git a/safety-related-profile/documents/profile_graph/imgs/graph.png b/safety-related-profile/documents/profile_graph/imgs/graph.png
new file mode 100644
index 00000000..6295388a
Binary files /dev/null and b/safety-related-profile/documents/profile_graph/imgs/graph.png differ
diff --git a/safety-related-profile/documents/profile_graph/reviews/README.md b/safety-related-profile/documents/profile_graph/reviews/README.md
new file mode 100644
index 00000000..800d6736
--- /dev/null
+++ b/safety-related-profile/documents/profile_graph/reviews/README.md
@@ -0,0 +1 @@
+Review of the "graph" specification.
\ No newline at end of file
diff --git a/safety-related-profile/documents/profile_graph/reviews/edoardo.md b/safety-related-profile/documents/profile_graph/reviews/edoardo.md
new file mode 100644
index 00000000..2b0554ad
--- /dev/null
+++ b/safety-related-profile/documents/profile_graph/reviews/edoardo.md
@@ -0,0 +1,176 @@
+> **_NOTE:_** Copy of "graph.md" with notes by Edoardo Manino
+
+# Preamble
+
+This document gives a high level description of how the execution execution of an ONNX graph. How the graph graph elements are described in the ONNX file is given in the Intermediate Representation (IR) specification document.
+
+In the context of SONNX, the specification of the semantics of an ONNX graph is limited to the inference phase.
+
+> **_NOTE 1:_** Typos: "execution" and "graph" are repeated twice. The first sentence is broken: maybe it is meant to say "how the execution of an ONNX graph _takes place_"?
+
+> **_REPLY 1:_** OK: Corrected.
+
+# Informal specification
+
+> **_NOTE 2:_** The document does not contain a _formal_ specification (yet?). If it exists in another document, mention that. Also, it may be worth having a little table of content listing the main sections: i.e. structure, behaviour, special nodes, additional remarks.
+
+> **_REPLY 2:_** OK: There is no formal specification yet. As for the operators, I add a "Formal specification" section that will point to the Why3 spec (which does not exist yet). The document does not contain a _formal_ specification (yet?). If it exists in another document, mention that. Also, it may be worth having a little table of content listing the main sections: i.e. structure, behaviour, special nodes, additional remarks.
+
+
+## Structure
+
+### Graph
+- In ONNX, a **model** is represented by a **graph**. Evaluating a model means evaluating the graph.
+- A **graph**
+ - is composed of a set of **nodes** and **edges**
+ - has zero or more inputs and at least one output.
+
+> **_NOTE 3:_** Later we state that nodes in the graph may have unused outputs. I can see how that could be helpful, e.g. use a complicated operator for just a few of its outputs, or pad a computational graph with irrelevant operators to avoid side-channel attacks. However, we are losing the nice recursive property that any subgraph of an ONNX graph is also a valid ONNX graph, because an arbitrary subgraph _may not have any outputs_. For an example, see the illustration below: the subgraph containing only the "Sub" node is not a valid ONNX graph. Do we care about this recursive definition? Should we change the definition of ONNX graphs to include graphs without any outputs?
+
+> **_REPLY 3:_** This has to be discussed... One point : a graph is a particular object... In particular, the output of a graph may be "driven" by outputs of multiple node, but outputs of different nodes cannot be connected together (due to the SSA constraint).
+
+
+- An **input** or an **output** is either undefined or has a value.
+
+> **_NOTE 4:_** Maybe it is worth clarifying the last sentence. By "undefined" do we mean we have not computed its value yet, its value does not matter, or that its value is non-deterministic? When an input/output has a value, can the value change during this specific execution or is it constant once it is computed?
+
+> **_REPLY 4:_** KO: I don't see clearly the ambiguity. In computer science, "undefined" generally (often?) means "not initialized", "not assigned". I don't like the term "initialized" because it refers to a specific phase of "initialization". "Not assigned" would be fine. I have changed to : "An **input** or an **output** is either defined (i.e. it has a value) or undefined (it has has no value)."
+
+- A **node**
+ - refers to a fully qualified and configured ONNX **operator** (the version of the operator is defined, the attributes of the operators are set).
+ - has inputs and outputs corresponding to the inputs and outputs of the referenced operator.
+- An **edge**
+ - is a connection between an input and an output of two different nodes, or a connection between an input (resp. output) of the graph and an input (resp. output) of a node so that,
+
+> **_NOTE 4:_** I would complete the sentence with "...so that the following constraints hold." and rename the next section as "Graph Constraints".
+
+> **_REPLY 5:_** OK: Agree, but I keep the section title ("constraints") to be consistent with the operators specification.
+
+> ** NOTE (Eric) ** In fact, I am not convinced that the use of nodes and edges is a good idea. A better model would be that nodes have inputs and output consuming and producing tensors. Connections between nodes would be done by referring to the same tensor. Thi sis how I have started modeling formally a graph.
+
+### Constraints
+- (C1) Each input of the graph is connected to one or more inputs of its nodes.
+- (C2) Each output of the graph is connected to the output of one of its nodes.
+- (C3) A graph is acyclic.
+
+> **_NOTE 6:_** Should we mention that the graph is also directed? We clearly imply it, since all edges connect the output of a node to the input of another (C5-6).
+
+> **_REPLY 6:_** KO: I don't think that we have to say that it is directed because, as you write, this is pretty clear from the existence of input and outputs and the way there are connected.
+
+- (C4) A node has zero or more inputs and at least one output.
+- (C5) A node's input is either connected to the output of another node or to an input of the graph.
+- (C6) A node's output is either connected to the input of another node, to an output of the graph, or remains unconnected.
+
+> **_NOTE 7:_** Typo: "node" is inanimate so we cannot use the Saxon genitive. Say "The input/output of a node" instead of "A node's input/output".
+
+> **_REPLY 7:_** OK: Corrected, even though the rule you mentioned is not really applied any more... See e.g., [here](https://english.stackexchange.com/questions/1031/is-using-the-possessive-apostrophe-correct-in-the-cars-antenna/100073#100073).
+
+### Illustration
+The following picture gives a simple example of a graph composed of 4 nodes. In this example, the inputs of the graph are connected to the inputs of two nodes (`add` and `sub`) and the output of the `sub` node is not used.
+
+
+
+
+
+Here is a textual export of same model using `onnx.helper.printable_graph(model.graph)`.
+
+```
+graph Test (
+ %I1[FLOAT, ?x?]
+ %I2[FLOAT, ?x?]
+) {
+ %O1 = Add(%I1, %I2)
+ %op2_out = Constant[value = ]()
+ %O2 = Mul(%O1, %op2_out)
+ %op4_out = Sub(%I1, %I2)
+ return %O1, %O2
+}
+```
+
+*(Note that ONNX also defines another textual serialization scheme (only available in C++)*
+
+## Behaviour
+- Evaluating a graph's output requires evaluating the output of the node it is connected to.
+- Evaluating a node's output is done by executing the node.
+
+> **_NOTE 8:_** Typos: "Evaluating an output of a graph/node"
+> **_REPLY 8:_** OK. Corrected. (but see my previous reply)
+
+- Executing a node means computing its outputs based on the specification of the referenced operator.
+- A node can only be executed if all its input values are defined.
+- Initially, all input values are defined
+
+> **_NOTE 9:_** I presume we are referring to the graph inputs, rather than all node inputs? I would also say "before execution", as it is more precise than "initially".
+
+> **_REPLY 9:_** (a) KO : but there is an error: "all inputs are undefined." (b) OK
+
+- Initially, all output values are undefined
+- All inputs and outputs connected by an edge are either both undefined or have the same value.
+
+
+## Special nodes
+
+### Functions nodes
+
+> **_NOTE 10:_** In this section we are using "encapsulate" and "embed" as synonym. I would pick only one for consistency.
+> **_REPLY 10:_** OK : use "encapsulate"
+>
+- A `function` operator encapsulates a graph.
+- Executing a function operator means executing the embedded graph according to the graph execution semantics described before.
+- An embedded graph may itself use `function` nodes, in a hierarchical manner.
+
+> **_NOTE 11:_** We are not saying explicitly whether the same function definition can be reused (called) in more than one node of the graph.
+> **_REPLY 11:_** As we are not saying that this is forbidden, it is authorized... I wouldn't say anything more.
+
+- In ONNX, a `function` node is conceptually expanded ("inlined") at the place where it is used. This forbids any direct or indirect recursion (incl. infinite recursion).
+
+> **_NOTE 12:_** (a) The last requirement is crucial, but I feel it is expressed in a potentially ambiguous way. Possible alternative: "A valid ONNX graph with function nodes must be always convertible to an equivalent ONNX graph without any function nodes. This forbids any direct..." (b) On this note, we might need to say explicitly (in the graph definition) that the number of nodes and edges must be finite.
+
+> **_REPLY 12:_** OK. (a) OK : modified as proposed (b) KO: as we are talking about an actual artefact (an ONNX model), I think that this is not necessary.
+
+### Control-flow operators
+- ONNX provides a series of control flow operators such as `if`, `scan`, `loop`,...).
+- Those nodes take one (e.g, operators `for`, `loop`, `scan`,...) or two graphs (`if`) as attributes and execute this graph or those graphs according to their specific semantics.
+- An `if` node, for instance, takes one boolean input and two attributes, one specifying the graph to be executed when the boolean input is true (the `then_branch`) and another graph when the boolean is false (the `else_branch`).
+ - Note that one of the graph is not executed. This seems to contradict the execution semantics of a graph bit but is not since executing the `then_branch` or the `else_branch` concerns the semantics of the `if` node, not of the graph. From the graph's perspective, the only node that is visible is the `if`.
+
+ > **_NOTE 13:_** Hmmm, this paragraph sounds a little defensive. Is this disclaimer really necessary? In section "Behaviour", we do not specify whether all outputs should become defined after execution. Thus, we already allow arbitrary sub-graphs to remain non-executed. Am I missing the point here?
+
+ > **_REPLAY 13:_** OK. I remove this sentence. I have added that all outputs of the graph must be defined after execution otherwise the graph is considered to be malformed. Let's say that we have a graph with output O and has a simple IF node where the THEN branch produces O and the ELSE branch produces nothing. Tis graph is invalid.
+
+ - The same applies for the other control flow nodes.
+
+
+## Additional remarks
+
+### Properties of a graph
+- If all operators are purely functional (stateless), a graph is also purely functional, i.e., the values of its outputs only only depends on the values of its inputs and the values of the attributes of its nodes.
+
+> **_NOTE 14:_** Typo: "only only" is repeated.
+> **_REPLY 14:_** OK.
+
+- If all operators are deterministic, a graph is also deterministic, i.e., for a given set of input values, the execution of the graph always gives the same output values.
+- A graph has no side effect, i.e., the only visible effects of a graph are via its outputs.
+
+> **_NOTE 15:_** Does the latter requirement mean that all operators _must_ be stateless and deterministic? The former two requirements begin with "if", which makes it sound like an option.
+> **_REPLY 15:_** TBC: not sure to understand your comment.
+>
+Note:
+- The values of the outputs do not depend on the execution order of its nodes are executed.
+
+> **_NOTE 16:_** Typo: "execution order ... are executed".
+> **_REPLY 16:_** OK.
+
+- By construction, a graph makes it explicit the order according to which terms of expressions are computed. For instance, expression `a+b+c` is either represented by `(a+b)+c`or `(a+(b+c)`
+
+> **_NOTE 17:_** This requirement contradicts the previous one. The latter seems to forbid non-determinism in the execution order, the former seems to allow it as long as the output stays the same. I would prefer keeping only one of them, with strong preference towards fixing the execution order.
+> **_REPLY 17:_** OK. May sentence was really unclear! What I means is that in ONNX, `a+b+c` is explicitly represented by
+ `(a+b)+c`or `(a+(b+c)`.
+
+## Restrictions
+The following restrictions apply to graphs in the SONNX profile:
+- `[R1]` A graph shall not contain nodes with no connected outputs.
+ - Rationale: each node of the graph shall contribute to the function of the graph (no "dead node").
+
+> **_NOTE 18:_** Shall we change the illustration then? It contains a node "Sub" with a dangling output. Also, clarify whether I can have a node with multiple outputs, some connected, some disconnected. I think R1 allows it, but another reader may not.
+> **_REPLY 18:_** TBC. The illustration describes the general case. May be shoud I add a comment stating that this kind of graph is forbidden by R1. The question is : is R1 mandatory?
\ No newline at end of file
diff --git a/safety-related-profile/documents/profile_graph/reviews/jlf b/safety-related-profile/documents/profile_graph/reviews/jlf
new file mode 100644
index 00000000..3458c3d2
--- /dev/null
+++ b/safety-related-profile/documents/profile_graph/reviews/jlf
@@ -0,0 +1,92 @@
+[Answers from EJN]
+
+> I think that inputs and outputs shall be defined as elements of the graph.
+
+OK.Yes. Especially since we have (c1) saying that "A graph has zero or more inputs and at least one output."...
+However, I also think that we have to keep things simple since the concept of "input" and "output" is pretty intuitive.
+
+=> I propose to remove C1 and update the definition of a graph to introduce the concept of input/output
+
+> Edge: "the input" <-- "a specific input", idem for "output" and idem for the "graph".
+
+KO: Not convinced that it makes the definition clearer.\
+=> No modification
+
+
+> May be the concept of "port" could be used.
+
+??: I don't want to make the definition more complicated than needed, as far as
+there is no ambiguity for a "typical" reader.
+
+The concept of "port" would allow introducing the concept of "port delegation"
+that would make the connection between graph inputs (resp. outputs)
+and nodes inputs ( resp. outputs) cleaner (but not necessarily clearer).
+
+
+> The illustration highlights ports and a hierarchical graph structure
+> (one node includes the four nodes).
+
+OK: Yes, definitely.
+The squares in the diagram are actually "ports".\
+=> No modification. See my previous remark.
+
+
+> (C1): Why zero input? I would prefer at least one input.
+
+??: A graph may encode a constant (so, no input). Alone, such a graph will certainly nover be used in any actual use case, but a graph may also be called by a SCAN op (for instance). In that case, it may make sense to define a constant graph...
+
+
+> Add (C8): At least one output of each node is connected either to an output
+> of the graph or to an input of another node.
+
+> Rationale: Each component should have a function. May be redondant with [R1].
+
+KO: It is actually redundant with R1 which is not a constraint in the sense that it is part of the semantics.
+It is really a SONNX restriction since a valid ONNX graph may have unconnected nodes.
+
+> "Please note that the sub operator has been removed since it is not used
+
+> by any other node or as an output of the graph."
+
+> is contradictory with "%op4_out = Sub(%I1, %I2)".
+
+OK: Yes, you are absolutely right. I am wondering why I have written this…\
+=> Suppressed.
+
+> Behaviour: Add "Initially, all input values of the graph are defined."
+
+OK\:
+=> Added.
+
+> and "When an output of a node is connected to the input of another node
+> by an edge the definition of the value of the output of the first
+> node implies the definition of the value of the input of the second node".
+
+OK/KO: This is already part of the edge definition:
+
+"- An edge represents a connection between the input and the output
+ of two different nodes, or a connection between the input (resp. output) of the graph and the input (resp. output) of a node.
+
+-
+At any time, the inputs and outputs connected by an edge have the same value
+"
+
+But there is a slight difference in the sense that I am only talking about "values" here. Implicitly, I consider that "undefined" is a value. Which it is not.\
+=> At any time, the inputs and outputs connected by an edge have the same status (defined/undefined) and values
+
+
+
+
+> May be the definition of the graph should be exended to include
+
+> in its structure the special nodes.
+
+OK
+=> Moved.
+
+
+
+
+
+
+
diff --git a/safety-related-profile/documents/profile_graph/reviews/m.belcaid.md b/safety-related-profile/documents/profile_graph/reviews/m.belcaid.md
new file mode 100644
index 00000000..0a514914
--- /dev/null
+++ b/safety-related-profile/documents/profile_graph/reviews/m.belcaid.md
@@ -0,0 +1,41 @@
+Graph
+
+[T01] A graph is defined by:
+
+ A set of inputs and outputs parameters
+ An input (resp. output) parameter is a free variable that can be bound to some tensor.
+ A set of local (or "internal") tensors.
+ A set of nodes
+
+
+Remarks :
+
+ -Add a schema of the context of hierarchy of the Graph, with the notion of Node and Operator
+ Model
+ └── Graph
+ └── Node[ ]
+ └── Operator (referenced by op_type)
+
+ - "that can be bound" need to be replace by "must be bound to tensor"
+ (The node entries must be associated with a tensor, otherwise the calculation can't be performed)
+ - Add an Example: A Graph with 2 Nodes and the bindings Tensors inputs/ouputs.
+
+ [input_tensor]--+
+ |--[Add]--[add_output_tensor]--[Relu]--[output_tensor]
+ [const_tensor]--+
+
+
+
+Remarks on Operators and Nodes [T03a] and [T03b] :
+
+ - Precise that in Node " describes the binding of its inputs and outputs " is defined by name and not parameter contrary to the Operator;
+ -> "describes the binding of its inputs and outputs by named tensor"
+ - Precise that "A Node can only bind only one Operator."
+ - Add an example with the same Operator used multiple times.
+
+ Example:An Operator [Add] used twiced in this Graph.
+
+ Input a ----+
+ |--[Add]--(Output c)---+
+ Input b ----+ |--[Add]--( Output e)
+ Input d ----------------------------+
diff --git a/safety-related-profile/documents/profile_graph/reviews/mohammed.md b/safety-related-profile/documents/profile_graph/reviews/mohammed.md
new file mode 100644
index 00000000..48b3c091
--- /dev/null
+++ b/safety-related-profile/documents/profile_graph/reviews/mohammed.md
@@ -0,0 +1,56 @@
+[ERIC] General remark: I have tried to simplify the expression as far as possible.
+
+Graph
+
+[T01] A graph is defined by:
+
+ A set of inputs and outputs parameters
+ An input (resp. output) parameter is a free variable that can be bound to some tensor.
+ A set of local (or "internal") tensors.
+ A set of nodes
+
+
+Remarks :
+
+ -Add a schema of the context of hierarchy of the Graph, with the notion of Node and Operator
+ Model
+ └── Graph
+ └── Node[ ]
+ └── Operator (referenced by op_type)
+
+{ERIC] OK, figure to be added.
+
+ - "that can be bound" need to be replace by "must be bound to tensor"
+
+[ERIC] KO: There is a subtle distinction between a definition ("the free variable **can** be bound to a tensor") and the constraint ("the free variable **must** be bound to a tensor for the graph to be executable".). I keep the definition as is and I ensure that the operational constraint is actually stated somewhere.
+
+ (The node entries must be associated with a tensor, otherwise the calculation can't be performed)
+ - Add an Example: A Graph with 2 Nodes and the bindings Tensors inputs/ouputs.
+
+ [input_tensor] --+
+ |--[Add]--[add_output_tensor]--[Relu]--[output_tensor]
+ [const_tensor]--+
+
+
+
+Remarks on Operators and Nodes [T03a] and [T03b] :
+
+ - Precise that in Node " describes the binding of its inputs and outputs " is defined by name and not parameter contrary to the Operator;
+ -> "describes the binding of its inputs and outputs by named tensor"
+
+
+ - Precise that "A Node can only bind only one Operator."
+
+[ERIC] OK.
+
+ - Add an example with the same Operator used multiple times.
+
+ Example:An Operator [Add] used twice in this Graph.
+
+ Input a ----+
+ |--[Add]--(Output c)---+
+ Input b ----+ |--[Add]--( Output e)
+ Input d ----------------------------+
+
+[ERIC] OK, a complete example has been given.
+
diff --git a/safety-related-profile/documents/profile_opset/conv/conv.md b/safety-related-profile/documents/profile_opset/conv/conv.md
deleted file mode 100644
index 21eba0af..00000000
--- a/safety-related-profile/documents/profile_opset/conv/conv.md
+++ /dev/null
@@ -1,701 +0,0 @@
-# Conventions
-## Notations
-- Notations $h(X)$ and $w(X)$ respectively denote the _height_ and the _width_ of tensor $X$. If the tensor represents an image, $h(X)$ and $w(X)$ represent the _height_ and the _width_ of the image.
-## Usage of fonts
-- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "pads" attribute is represented by `pads`.
-## Tags
-- Restrictions with respect to the ONNX standard are indicated in the text with the tag `[Ri]` where `i` is a number.\
-A synthesis of all restrictions is given in section "Restrictions".
-## Types
-- Operators are first described for values in the domain of real numbers. A specific description is given for the other types (floats, integers).
-
-# `conv` operator (real)
-
-### Restrictions
-The following restrictions apply to the `conv` operator for the SONNX profile:
-- The number of spatial axes of the tensors is restricted to 2 `[R1]`
-- Attribute `auto_pad` is restricted to `NOTSET` `[R2]`
-- Attribute `group` is restricted to 1 (standard convolution) or to the number of channels of the input tensor (depthwise convolution) `[R3]`
-- All attributes shall be be given explicit values (i.e., default values for attributes are not supported) `[R4]`
-- The number of elements in the `strides` list is equal to 2. `[R5]`
-
-### Signature
-`Y = conv(X,W,[B])`
-where
-- `X`: input tensor
-- `W`: convolution kernel
-- `B`: optional bias
-- `Y`: output tensor
-
-#### Informal specification
-
-Operator `conv` computes the convolution of the input tensor `X` with the kernel `W` and adds bias `B` to the result. Two types of convolutions are supported: _standard convolution_ and _depthwise convolution_ `[R3]`. The SONNX profile limits the number of spatial axes to 2 `[R1]`.
-
-##### Standard convolution
-A _standard convolution_ applies a kernel (also called "filter") to the input tensor, aggregating information accross both spatial axes and channels. For a given output channel, the kernel operates accross all input channels and all contributions are summed to produce the output. This corresponds to the case where `group`= 1.
-
-The mathematical definition of the operator without padding is given hereafter.
-The formal specification is given in Section Formal specification. When considering padding, the same formula applies, in which `X` represents the padded version of the actual input `X`.
-
-$$\begin{gathered}
- Y[b, c, m, n] = \sum_{i=0}^{fm(W)-1} \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1} \\ (X[b,i,m \cdot strides[0]+ j \cdot dilations[0], n \cdot strides[1]+ z \cdot dilations[1]] \cdot W[c, i, j, z]) \\ + B[c]
-\end{gathered}$$
-
-Where
-- $b$ is the batch index, $b \in [0,b(Y)-1]$, $b(Y)$ is the batch size of output `Y`
-- $c$ is the data channel, $c \in [0,c(Y)-1]$, $c(Y)$ is the number of data channels of output `Y`
-- $m \in [0,h(Y)-1]$ is the index of the first spatial axis of output `Y`
-- $n \in [0,w(Y)-1]$ is the index of the second spatial axis of output `Y`
-- $fm(W)$ is the number of feature maps of kernel `W`
-- $h(W)$ is the size of the first spatial axis of kernel `W`
-- $w(W)$ is the sizes of the second spatial axis of kernel `W`
-
-`strides` and `dilations` are attributes of the operator. They are described later in this section.
-
-The effect of the operator is illustrated on the following figure. In this example
-- shape of `Y` is $1\times 1 \times 4 \times 4$ (batch size is 1, number of data channels is 1)
-- shape of `X` is $1 \times 1 \times 8 \times 8$ (batch size is 1, number of data channels is 1)
-- shape of `W` is $1 \times 1 \times 3 \times 2$ (number of data channels is 1)
-- shape of `B` is $1$
-- `pads` is set to (1,2,2,2) (1 column on the left, 2 columns on the right, 2 rows on the top, 2 rows on the bottom)
-- `dilations` is set to (2,2)
-- `strides` is set to (2,3)
-
-
-
-The following figure shows the case where the number of channels of `X` is 3. In this example:
-- shape of `Y` is $1 \times 1 \times 4 \times 4$
-- shape of `X` is $1 \times 3 \times 8 \times 8$
-- shape of `W` is $1 \times 3 \times 3 \times 2$
-- shape of `B` is $1$
-- `groups` is set to 1
-- the other attributes have the same values as in the previous figure.
-
-
-
-
-##### Depthwise convolution
-A _depthwise convolution_ applies a specific kernel (or "filter") to each input channels. The number of output channels is equal to the number of input channels. This corresponds to the case where `group`= $c(X)$.
-
-The mathematical definition is given hereafter:
-
-$$\begin{gathered}
- Y[b, c, m, n] = \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1}\\ (X[b, c, m \cdot strides[0] + j \cdot dilations[0], n \cdot strides[1] + z \cdot dilations[1]] \cdot W[c, 0, j , z] ) + B[c]
-\end{gathered}$$
-
-Variables are defined as for the standard convolution.
-The effect of the operator is illustrated on the following figure. In this example,
-- shape of `Y` is $1\times 3 \times 4 \times 4$
-- shape of `X` is $1 \times 3 \times 8 \times 8$
-- shape of `W` is $3 \times 1 \times 3 \times 2$
-- shape of `B` is $3$
-- `groups` is set to 3
-- the other attributes have the same values as in the previous figure.
-
-
-
-#### Inputs and outputs
-
-##### `X`
-
-Tensor `X` is the input tensor on which convolution with kernel `W` is computed.
-
-The shape of tensor `X` is $b(X) \times c(X) \times h(X) \times w(X)$.
-
-###### Constraints
-
-- (C1) Number of spatial axes of tensor `X`
- - Statement: The number of spatial axes of tensor `X` is 2. `[R1]`
- - Rationale: This restriction is intoduced to simplify the implementation considering the actual industrial use cases.
-- (C2) Consistency between the number of channels of `X` and `W`
- - Statement: $c(X)=fm(W)$
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`
-
- - Statement:
- * $$\left\lfloor{\frac{alpha-(dilations[0] \cdot h(W)-1)}{strides[0]}} \right\rfloor +1 = h(Y) \mbox{ with } alpha=h(X)+pads[0]+pads[2]$$
-
- and
-
- * $$\left\lfloor{\frac{beta-(dilations[1] \cdot w(W)-1)}{strides[1]}} \right\rfloor +1 = w(Y) \mbox{ with } beta=w(X)+pads[1]+pads[3]$$
- - Rationale: The size of the output is determined by the number of times the kernel can be applied on a given spatial axis.
-- (C4) Axis denotations
- - Statement: If axis denotation is in effect, the operation expects input data tensor to have axis denotation \[`DATA_BATCH`, `DATA_CHANNEL`, `DATA_FEATURE`, `DATA_FEATURE`\].
- - Rationale: Denotation convention
-
-##### `W`
-
-Tensor `W` is the convolution kernel.
-
-The shape of tensor `W`is $(c(W) \times fm(W) \times h(W) \times w(W))$, where
-- $c(W)$ is the number of output channels or number of feature maps
-- $fm(W)$ is the number of input channels
-- $h(W)$ and $w(W)$ are the sizes of the kernel for the two spatial axes.
-
-###### Constraints
-- (C1) Consistency between the number of channels of `X` and `W`
- - Statement: [See constraint (C2) of X](#channel_consist).
-- (C2) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist).
-- (C3) Consistency between `W` and `kernel_shape`
-
- - Statement: The size of `W` for an axis must bve equal to the value of `kernel_shape` for that axis
- - Rationale: `kernel_shape` represents the shape of `W`, where `kernel_shape[0]` = $w(W)$ and `kernel_shape[1]` = $h(W)$.
-- (C4) Axis denotations
- - Statement: If axis denotation is in effect, the operation expects the weight tensor to have axis denotation \[`FILTER_OUT_CHANNEL`, `FILTER_IN_CHANNEL`, `FILTER_SPATIAL`, `FILTER_SPATIAL`\].
- - Rationale: Denotation convention
-
-##### `B`
-
-Tensor `B` is the bias.
-
-The shape of tensor `B`is $c(B)$.
-
-###### Constraints
-- (C1) Consistency between the number of channels of `B` and `W`
- - Statement: $c(B) = fm(W)$.
-
-#### Attributes
-
-##### `strides`: list of int
-
-Attribute `strides` determines how the kernel is applied on tensor `X` during the convolution.
-
-For instance, with $\mbox{\texttt{stride}}[0]=2$ and $\mbox{\texttt{stride}}[1]=3$, the kernel is applied to data 2 units on right in the first spatial axis and to data 3 units down in the second spatial axis at each step of the convolution.
-
-> The previous sentence is not clear...
-
-The effect of the `strides` attribute is illustrated on the following figure. In this example, `strides`=(2,3).
-
-
-
-###### Constraints
-- (C1) Size of `strides`
- - Statement: the number of elements in the `strides` list is equal to 2. `[R5]`
- - Rationale: The SONNX profile only supports 2 spatial axes.
-- (C2) Value domain
- - Statement: `strides` is a list of strictly positive integers.
- - Rationale: Stride values are used in the denominator of expression in [constraint (C3) of X](#shape_consist)
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist)
-
-##### `auto_pad` : string
-
-The `auto_pad` attribute determines if and how automatic padding is done for the input tensor X.
-
-###### Constraints
-- (C1) Explicit padding
- - Statement: `auto_pad` shall be set to `NOTSET` `[R3]`
- - Rationale: The SONNX profile imposes explicit padding.
-
-##### `pads`: list of int
-
-Attribute `pads` determines the padding at the beginning and end along each spatial axis of the input tensor `X`.
-
-`pads` is a list of the form (`x1_begin`, `x2_begin`,..., `x1_end`, `x2_end`,...), where `xi_begin` is the number of elements (possibly zero) added at the beginning of axis $i$ and `xi_end` is the number of elements added at the end of axis $i$.
-
-The padding value is 0.
-
-The effect of the `pads` attribute is illustrated on the following figure. In this example, `pads`=(1,3,2,2).
-
-
-
-###### Constraints
-- (C1) Value domain
- - Statement: `pads` is a list of positive or null integers.
- - Rationale: A padding value gives a number of elements to be added to some spatial axis. This is positive[^2].
-- (C2) Consistency between the shape of `X` and the length of `pads`
- - Statement: The length of the `pads` list is two times the number of spatial axes of `X`
- - Rationale: Padding shall be given for all spatial axes, and a beggining value and an end value must be given for each axis.
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist)
-
-##### `dilations`: list of int
-
-Attribute `dilations` specifies the spacing between the kernel elements for each spatial axis of the filter `W`. It is a list of non-null integer values where each value gives the dilation factor for spatial axis $i$. If the dilation factor is greater than 1 for axis $i$, then the kernel points are spaced out by the dilation factor for that axis.
-
-The spacing value is 0.
-
-The effect of the `dilations` attribute for a tensor with two spatial axes is depicted on the following figure. In this example, `dilations`=(2,2).
-
-
-
-
-###### Constraints
-- (C1) Value domain
- - Statement: `dilations` is a list of strictly positive integers
-- (C2) Relation between `dilations` and `W`
- - Statement: The length of the `dilations` list is equal to number of spatial axes of `W`.
- - Rationale: Dilation is defined for all spatial axes of `W`.
-- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`.
- - Statement: [See constraint (C3) of X](#shape_consist)
-
-##### `group`: int
-
-This attribute specifies the number of groups the input channels and output channels are divided into. When `group`=1, a standard convolution is performed.
-When group is greater than 1, convolution is computed for each group separately with a specific set of filters.
-
-The effect of the `group` attribute for a tensor with two spatial axes is depicted on the following figure. In this example `group`=3.
-
-
-
-(Taken from https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning)
-
-In the example, with `group` set to 3 and an input `X` and an output `Y` with 3 channels, the input and output channels will be divided into 3 groups of 1 channel.
-
-###### Constraints
-- (C1) Support for standard and depthwise convolutions
- - Statement: `group`=1 (standard convolution) or `group`$=c(X)$ (depthwise convolution)
- - Rationale: SONNX only supports standard and depthwise convolutions
-
-##### `kernel_shape`: list of int
-
-This parameter specifies the shape of the convolution kernel `W`.
-
-###### Constraints.
-
-- (C1) Value domain
- - Statement: `kernel_shape` is a list of strictly positive integers
- - Rationale: A dimension is always positive and cannot be null.
-- (C2) Consistency between `W` and `kernel_shape`
- - Statement: [See constraint (C3) of W](#kernel_shape_w)
-
-#### Outputs
-
-##### `Y`
-
-The size of the output `Y` will be $(b(Y) \times c(Y) \times h(Y) \times w(Y))$ where
-- $b(Y)$ is the number of batches
-- $c(Y)$ is the number of channels
-- $h(Y)$ and $w(Y)$ are the sizes of the output for the two spatial axes
-
-###### Constraints.
-- (C1) Consistency between the number of channels of `B` and `Y`
- - Statement: HYPERLIEN VERS W / constraint kernel shape
-- (C2) Consistency between the shape of tensors `X`, `W`, `Y`, attributes `pads` and `strides`,
- - Statement: [see constraint (C3) of X](#shape_consist)
-
-#### Formal specification
-
-The following code specifies the `conv` operator using the Why3 language[^3].
-
-###### Nota: the specification does not cover all attributes values. Currently, there is no padding (`pads` is not set and `auto_pad = NOTSET`) and `dilations` is not set.
-
-``` ocaml
-module Conv
- use int.Int
- use real.RealInfix
- use array.Array
- use int.ComputerDivision
- use real.Truncate
- use real.FromInt
-
- type input_tensor = {
- x: array real;
- x_l: int;
- x_c: int;
- x_b: int;
- x_ch: int;
- }
-
- type convolution_kernel = {
- w: array real;
- w_l: int;
- w_c: int;
- w_ch_in: int;
- w_ch_out: int;
- }
-
- type bias_tensor = {
- b: array real;
- b_c: int;
- }
-
- type attributes = {
- stride: array int;
- pads: array int;
- auto_pad: int;
- dilation: array int;
- }
-
- type output_tensor = {
-
- y_b: int;
- y_ch: int;
- y_l: int;
- y_c: int;
-
- }
-
- function conv_size (out: output_tensor) : int =
- out.y_b * out.y_ch * out.y_l * out.y_c
-
- predicate conv_result
- (inp: input_tensor)
- (kernel: convolution_kernel)
- (bias: bias_tensor)
- (attr: attributes)
- (out: output_tensor)
- (res: array real)
- (bi ci hi wi: int)
- (ci_in ki_h ki_w: int) =
- let y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi in
- let x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] in
- let x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] in
-
- (0 <= x_l_idx < inp.x_l /\ 0 <= x_c_idx < inp.x_c) ->
- let x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx in
- let w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w in
- res.elts (y_idx) = bias.b[ci] +. (inp.x[x_idx] *. kernel.w[w_idx])
-
- val conv (inp: input_tensor)(kernel: convolution_kernel)(bias: bias_tensor)(attr: attributes)(out: output_tensor): array real
- requires{inp.x_ch = out.y_ch = kernel.w_ch_in = bias.b_c}
- requires{out.y_l = (div (inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l)) attr.stride[0]) + 1}
- requires{out.y_c = (div (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c)) attr.stride[1]) +1}
- requires { inp.x_l > 0 /\ inp.x_c > 0 /\ inp.x_ch > 0 /\ inp.x_b > 0}
- requires{kernel.w_l > 0 /\ kernel.w_c > 0 /\ kernel.w_ch_in > 0 /\ kernel.w_ch_out > 0}
- requires { out.y_b > 0 /\ out.y_ch > 0 /\ out.y_l > 0 /\ out.y_c > 0}
- requires { length inp.x = inp.x_l * inp.x_c * inp.x_ch * inp.x_b}
- requires{length kernel.w = kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out}
- requires{inp.x_l >= kernel.w_l}
- requires{inp.x_c >= kernel.w_c}
- requires{length bias.b = bias.b_c}
- requires{length attr.stride = 2}
- requires{length attr.dilation = 2}
- requires{length attr.pads = 4}
- requires{forall i. 0 <= i < length attr.pads -> attr.pads[i] = 0}
- requires{forall j. 0 <= j < length attr.dilation -> attr.dilation[j] >= 1}
- requires{forall k. 0 <= k < length attr.stride -> attr.stride[k] >= 1}
- ensures { length result = conv_size out }
- ensures { forall bi ci hi wi ci_in ki_h ki_w: int.
- 0 <= bi < out.y_b ->
- 0 <= ci < out.y_ch ->
- 0 <= hi < out.y_l ->
- 0 <= wi < out.y_c ->
- 0 <= ci_in < kernel.w_ch_in ->
- 0 <= ki_h < kernel.w_l ->
- 0 <= ki_w < kernel.w_c -> conv_result inp kernel bias attr out result bi ci hi wi ci_in ki_h ki_w }
-
-end
-
-
-module Test_conv
- use int.Int
- use real.RealInfix
- use array.Array
- use int.ComputerDivision
- use real.Truncate
- use real.FromInt
- use Conv
-
-let test_conv () =
- let inp_x = Array.make 9 1.0 in
- let inp = { x = inp_x; x_l = 3; x_c = 3; x_b = 1; x_ch = 1 } in
-
- let kernel_w = Array.make 4 0.0 in
- let kernel = { w = kernel_w; w_l = 2; w_c = 2; w_ch_in = 1; w_ch_out = 1 } in
-
- let bias_b = Array.make 1 0.5 in
- let bias = { b = bias_b; b_c = 1 } in
- let stride = Array.make 2 1 in (* Stride of 1 *)
- let pads = Array.make 4 0 in (* No padding *)
- let dilation = Array.make 2 1 in (* Dilation of 1 *)
- let attr = { stride = stride; pads = pads; auto_pad = 0; dilation = dilation } in
- let out_h = (div (inp.x_l + pads[0] + pads[2] - (dilation[0] * kernel.w_l)) stride[0]) + 1 in
- let out_w = (div (inp.x_c + pads[1] + pads[3] - (dilation[1] * kernel.w_c)) stride[1]) + 1 in
- let out = { y_b = 1; y_ch = 1; y_l = out_h ; y_c = out_w } in
- (* Call the conv function *)
- let result = conv inp kernel bias attr out in
- let actual_result = result.elts 0 in
- assert { conv_result inp kernel bias attr out result 0 0 0 0 0 0 0} ;
- assert { actual_result = 0.5 } ;
- ()
-
-end
-```
-
-Another formal specification of the `conv` operator using Frama-C
-language[^4] is presented below.
-
-``` objectivec
-#include
-#include
-#include
-
-/* Data Structures */
-typedef struct {
- float *x;
- int x_l, x_c, x_b, x_ch;
-} input_tensor;
-
-typedef struct {
- float *w;
- int w_l, w_c, w_ch_in, w_ch_out;
-} convolution_kernel;
-
-typedef struct {
- float *b;
- int b_c;
-} bias_tensor;
-
-typedef struct {
- int *stride;
- int *pads;
- int *dilation;
-} attributes;
-
-typedef struct {
- float *y;
- int y_b, y_ch, y_l, y_c;
-} output_tensor;
-/*@
- requires \valid_read(pads + (0 .. 3));
- requires \valid_read(stride + (0 .. 1));
- requires \valid_read(result + (0 .. 3));
- requires x_l > 0 && x_c > 0 && w_l > 0 && w_c > 0 && y_l > 0 && y_c > 0;
- assigns result[0 .. 3];
- behavior empty_or_notset:
- assumes (auto_pad == "") || (auto_pad == "NOTSET");
- ensures \forall integer i; 0 <= i < 4 ==> result[i] == pads[i];
-
-behavior valid:
- assumes (auto_pad == "VALID") ;
- ensures \forall integer i; 0 <= i < 4 ==> result[i] == 0;
-
-behavior same_upper:
- assumes (auto_pad == "SAME_UPPER") ;
- ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- \let pad_c = (y_c - 1) * stride[1] + w_c - x_c;
- ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==>
- (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2)) &&
- (pad_l % 2 != 0) && (pad_c % 2 != 0) ==>
- (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == ((pad_c / 2) + 1) && result[3] == ((pad_l / 2) + 1));
-
-behavior same_lower:
- assumes (auto_pad == "SAME_LOWER");
- ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- \let pad_c = (y_c - 1) * stride[1] + w_c - x_c;
- ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==>
- (result[0] == (pad_c / 2) && result[2] == (pad_c / 2) && result[1] == (pad_l / 2) && result[3] == (pad_l == 2)) &&
- ((pad_l % 2 != 0) && (pad_c % 2 != 0)) ==>
- (result[0] == ((pad_c / 2) + 1) && result[1] == ((pad_l / 2) + 1) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2));
-complete behaviors empty_or_notset, valid, same_upper, same_lower;
-disjoint behaviors empty_or_notset, valid, same_upper, same_lower;
-*/
-void compute_pad(const char* auto_pad, int pads[4], int stride[2], int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int result[4]) {
- int pad_l, pad_c;
-
- if ((auto_pad == "") || (auto_pad == "NOTSET")) {
- for (int i = 0; i < 4; i++) {
- result[i] = pads[i];
- }
- } else if ((auto_pad == "VALID")) {
- for (int i = 0; i < 4; i++) {
- result[i] = 0;
- }
- } else if ((auto_pad == "SAME_UPPER")) {
- pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- pad_c = (y_c - 1) * stride[1] + w_c - x_c;
-
- if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) {
- result[0] = result[2] = pad_c / 2;
- result[1] = result[3] = pad_l / 2;
- } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) {
- result[0] = pad_c / 2;
- result[1] = pad_l / 2;
- result[2] = (pad_c / 2) + 1;
- result[3] = (pad_l / 2) + 1;
- }
- } else if ((auto_pad == "SAME_LOWER")) {
- pad_l = (y_l - 1) * stride[0] + w_l - x_l;
- pad_c = (y_c - 1) * stride[1] + w_c - x_c;
-
- if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) {
- result[0] = result[2] = pad_c / 2;
- result[1] = result[3] = pad_l / 2;
- } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) {
- result[0] = (pad_c / 2) + 1;
- result[1] = (pad_l / 2) + 1;
- result[2] = pad_c / 2;
- result[3] = pad_l / 2;
- }
- }
-}
-
-/* Function to compute the convolution */
-//void compute_pad(int auto_pad, int *pads, int *stride, int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int *result);
-
-/*@
- requires \valid_read(inp.x + (0..(inp.x_l*inp.x_c*inp.x_b*inp.x_ch)-1));
- requires \valid_read(kernel.w + (0..(kernel.w_l*kernel.w_c*kernel.w_ch_in*kernel.w_ch_out)-1));
- requires \valid_read(bias.b + (0..bias.b_c-1));
- requires \valid_read(attr.stride+(0..1));
- requires \valid_read(attr.pads+(0..3));
- requires \valid_read(attr.dilation+(0..1));
- requires inp.x_ch == out.y_ch;
- requires out.y_ch == kernel.w_ch_in;
- requires kernel.w_ch_in == bias.b_c;
- requires out.y_l == ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1;
- requires out.y_c == ((inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) + 1;
- requires inp.x_l > 0 && inp.x_c > 0 && inp.x_ch > 0 && inp.x_b > 0;
- requires kernel.w_l > 0 && kernel.w_c > 0 && kernel.w_ch_in > 0 && kernel.w_ch_out > 0;
- requires bias.b_c > 0;
- requires out.y_l > 0 && out.y_c > 0 && out.y_ch > 0 && out.y_b > 0;
- requires inp.x_l >= kernel.w_l;
- requires inp.x_c >= kernel.w_c;
- requires \forall integer i; 0 <= i < 4 ==> attr.pads[i] >= 0;
- requires \forall integer i; 0 <= i < 2 ==> attr.dilation[i] >= 1;
- requires \forall integer i; 0 <= i < 2 ==> attr.stride[i] >= 1;
-
-
- assigns out.y[0..(out.y_b * out.y_ch * out.y_l * out.y_c)-1];
-
- ensures \forall integer bi, ci, hi, wi, ci_in, ki_h, ki_w;
- 0 <= bi < out.y_b ==>
- 0 <= ci < out.y_ch ==>
- 0 <= hi < out.y_l ==>
- 0 <= wi < out.y_c ==>
- 0 <= ci_in
- 0 <= ki_h < kernel.w_l ==>
- 0 <= ki_w < kernel.w_c ==>
- (0 <= hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) &&
- (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] < inp.x_l) &&
- (0 <= wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1]) &&
- (wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] < inp.x_c) ==>
- out.y[bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi] == inp.x[bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) * inp.x_c + ( wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1])] * kernel.w[ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w] + bias.b[ci];
-
-
-
-
-
-
-
- */
-float* conv(input_tensor inp, convolution_kernel kernel, bias_tensor bias, attributes attr, output_tensor out) {
-
- out.y_l = ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1;
- out.y_c = ( (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) +1;
- int y_size = out.y_b * out.y_ch * out.y_l * out.y_c;
- out.y = (float *)malloc(y_size * sizeof(float));
-
- if (out.y == NULL) {
- fprintf(stderr, "Memory allocation failed\n");
- exit(EXIT_FAILURE);
- }
-
- // Compute padding
- // compute_pad(attr.auto_pad, attr.pads, attr.stride, inp.x_l, inp.x_c, kernel.w_l, kernel.w_c, out.y_l, out.y_c, attr.pads);
-
- // Initialize result tensor to bias values
- for (int bi = 0; bi < out.y_b; ++bi) {
- for (int ci = 0; ci < out.y_ch; ++ci) {
- for (int hi = 0; hi < out.y_l; ++hi) {
- for (int wi = 0; wi < out.y_c; ++wi) {
- int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi;
- out.y[y_idx] = bias.b[ci];
- }
- }
- }
- }
-
- // Convolution computation
- for (int bi = 0; bi < out.y_b; ++bi) {
- for (int ci = 0; ci < out.y_ch; ++ci) {
- for (int hi = 0; hi < out.y_l; ++hi) {
- for (int wi = 0; wi < out.y_c; ++wi) {
- int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi;
-
- for (int ci_in = 0; ci_in < kernel.w_ch_in; ++ci_in) {
- for (int ki_h = 0; ki_h < kernel.w_l; ++ki_h) {
- for (int ki_w = 0; ki_w < kernel.w_c; ++ki_w) {
- int x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0];
- int x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1];
-
- if (x_l_idx >= 0 && x_l_idx < inp.x_l && x_c_idx >= 0 && x_c_idx < inp.x_c) {
- int x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx;
- int w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w;
-
- if (x_idx < inp.x_l * inp.x_c * inp.x_ch * inp.x_b && w_idx < kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out) {
- out.y[y_idx] += inp.x[x_idx] * kernel.w[w_idx];
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- return out.y;
-}
-```
-
-# Graph execution semantics
-
-
-
-Elements of the execution semantics is given on the [IR (Intermediate
-Representation) page](https://onnx.ai/onnx/repo-docs/IR.html) of the
-ONNX web site. In addition, a Python “reference implementation” is also
-provided (see ). The source
-code of this implementation can be found at
-.
-
-Very informally, the semantics is pretty simple: each operator (or
-function) is called according to its position in the topological sorting
-of the operators. The topological order is a partial order that ensures
-that an operator is executed only when its inputs are available. Being a
-partial order, it means that several valid orders exist for a given
-graph. Normally (?) each order should generate the same result, even in
-the presence of floating point operations.
-
-The Python code to execute a graph is given in class
-[`ReferenceEvaluator`](https://github.com/onnx/onnx/blob/main/onnx/reference/reference_evaluator.py)).
-After having processed the inputs and the initializers (i.e., fed the
-`results` dictorionary with these data), the nodes are executed in
-sequence. For each operator, the interpretor checks that its inputs are
-in the `results` dictionary. If they are not, an error is raised (if the
-operators are listed in topological order, this situation should not
-occur). Otherwise, the operator is simply executed (method `run`) with
-or without a context (composed of the current results) depending on the
-type of operators. (Check that this does not create a dependency to the
-total order of operators.)
-
-
-
-### Informal specification
-
-
-
-The semantics of an ONNX model is given in Section "Model Semantics" of
-the [Intermediate
-Representation](https://github.com/onnx/onnx/blob/main/docs/IR.md) page.
-Basically, an inference-model is a stateless function (except possibly
-for some specific nodes such as a random-generation node) represented by
-an acyclic `graph` of nodes. The `graph` is mainly represented by a set
-of inputs and outputs and a topologically sorted list of nodes. Each
-node represents a call to an operator or a function. A `function` is
-itself a graph.
-
-Note that the types of inputs and outputs are not systematically
-required because they can be inferred. In our case, I guess that we will
-forbib shape inference and rely on static tensor shapes (or, at least,
-shape inference can be bone before serializing the model). The proecss
-of shape inference is described in Section [ONNX Shape
-Inference](https://onnx.ai/onnx/repo-docs/ShapeInference.html).
-
-
-
-### Formal specification
-
-*To be completed.*
-
-[^1]: At least in a first phase...
-
-[^2]: Note: in the ONNX runtime implementation, the padding value may be
- negative, which corresponds to reducing the size of the tensor.
-
-[^3]: See [Why3 documentation](https://www(W)hy3.org/)
-
-[^4]: See [Frama-C
- documentation](https://www.frama-c.com/html/documentation.html)
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/README.md b/safety-related-profile/documents/profile_opset/conv/imgs/README.md
deleted file mode 100644
index 4a389a0a..00000000
--- a/safety-related-profile/documents/profile_opset/conv/imgs/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Images used in the CONV specification example
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/autopad.png b/safety-related-profile/documents/profile_opset/conv/imgs/autopad.png
deleted file mode 100644
index 614f6942..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/autopad.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep-3-channels.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep-3-channels.png
deleted file mode 100644
index 54b8ab1c..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep-3-channels.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep.png
deleted file mode 100644
index 54b8ab1c..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3 channels.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3 channels.png
deleted file mode 100644
index 8b1b8d10..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3 channels.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3-channels.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3-channels.png
deleted file mode 100644
index 8b1b8d10..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3-channels.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-std.png
deleted file mode 100644
index 660284cd..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv.png
deleted file mode 100644
index 660284cd..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv_pad.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv_pad.png
deleted file mode 100644
index 2679ecbc..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv_pad.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv_stride.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv_stride.png
deleted file mode 100644
index eed05a17..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv_stride.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/grouped_convolution.png b/safety-related-profile/documents/profile_opset/conv/imgs/grouped_convolution.png
deleted file mode 100644
index dfdf868c..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/grouped_convolution.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/onnx_conv.drawio b/safety-related-profile/documents/profile_opset/conv/imgs/onnx_conv.drawio
deleted file mode 100644
index 8585004a..00000000
--- a/safety-related-profile/documents/profile_opset/conv/imgs/onnx_conv.drawio
+++ /dev/null
@@ -1,3433 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/why3_workflow.png b/safety-related-profile/documents/profile_opset/conv/imgs/why3_workflow.png
deleted file mode 100644
index 97ceb89e..00000000
Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/why3_workflow.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/where/imgs/ONNX_WHERE.png b/safety-related-profile/documents/profile_opset/where/imgs/ONNX_WHERE.png
deleted file mode 100644
index 24058c54..00000000
Binary files a/safety-related-profile/documents/profile_opset/where/imgs/ONNX_WHERE.png and /dev/null differ
diff --git a/safety-related-profile/documents/profile_opset/where/imgs/README.md b/safety-related-profile/documents/profile_opset/where/imgs/README.md
deleted file mode 100644
index 325698b2..00000000
--- a/safety-related-profile/documents/profile_opset/where/imgs/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Images for the WHERE operator
diff --git a/safety-related-profile/documents/profile_opset/where/where.md b/safety-related-profile/documents/profile_opset/where/where.md
deleted file mode 100644
index 5185a0a7..00000000
--- a/safety-related-profile/documents/profile_opset/where/where.md
+++ /dev/null
@@ -1,259 +0,0 @@
-# Conventions
-## Notations
-- The notation $N(X)$ denotes the _number of elements_ in tensor $X$.
-- Notations $h(X)$ and $w(X)$ respectively denote the _height_ and the _width_ of tensor $X$.
-
-
-## Usage of fonts
-- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "condition" attribute is represented by `condition`.
-
-## Tags
-- Restrictions with respect to the ONNX standard are indicated in the text with the tag `[Ri]` where `i` is a number. A synthesis of all restrictions is given in section "Restrictions".
-
-## Types
-- Operators are first described for values in the domain of real numbers. Because the `WHERE` put in ouput the same type of X or Y according to the condition the ouput is of the same type of X or Y and could be of every possible type as tensor(bfloat16), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8).
-
-# `where` operator (real)
-
-### Restrictions
-The following restrictions apply to the `where` operator for the SONNX profile:
-- The tensors `condition`, `X`, and `Y` must have the same shape `[R1]`
-- The operator does not support sparse tensors `[R2]`
-- All X and Y inputs elements shall have the same explicit types `[R3]`
-- No broadcasting allowed for the tensors `condition`, `X`, and `Y` even if they are broadcastable to a common shape, the broadcasting is forbidden because dynamic computation time according to the shape is not deterministe (depend of dynamic dimension X,Y,Z and could crash) `[R4]`
-
-### Signature
-`Z = where(condition, X, Y)`
-where
-- `condition`: a tensor of boolean values where 0 represents False and non-zero represents True
-- `X`: input tensor to pick values from when `condition` is True
-- `Y`: input tensor to pick values from when `condition` is False
-- `Z`: output tensor based on the `condition`, `X`, and `Y`
-
-#### Informal specification
-
-The `where` operator selects elements from two input tensors `X` and `Y` based on the values in the `condition` tensor. For each element, if the corresponding entry in `condition` is True, the resulting tensor `Z` will contain the element from `X`. Otherwise, the resulting tensor `Z` will contain the element from `Y`.
-
-The mathematical definition of the operator is given hereafter.
-
-$$
-Z[i] =
-\begin{cases}
-X[i] & \text{if } condition[i] & \text{is different of false} \\
-Y[i] & \text{otherwise}
-\end{cases}
-$$
-
-Where
-- $i$ is an index covering all dimensions of the tensors.
-
-The effect of the operator is illustrated on the following examples. In these examples:
-- `condition` is a tensor of boolean values
-- `X` and `Y` are tensors holding numerical data
-
-
-
-Example 1:
-```math
-`condition` = \begin{bmatrix} True & False & True \end{bmatrix}
-```
-```math
-`X` = \begin{bmatrix} 9 & 8 & 7 \end{bmatrix}
-```
-```math
-`Y` = \begin{bmatrix} 6 & 5 & 4 \end{bmatrix}
-```
-Result `Z` will be :
-```math
-`Z` = \begin{bmatrix} 9 & 5 & 7 \end{bmatrix}
-```
-
-
-Example 2:
-```math
-`condition` = \begin{bmatrix} True & True \\ True & False \\ False & True \end{bmatrix}
-```
-```math
-`X` = \begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix}
-```
-```math
-`Y` = \begin{bmatrix} 12 & 11 \\ 10 & 9 \\ 8 & 7 \end{bmatrix}
-```
-Result `Z` will be :
-```math
-`Z` = \begin{bmatrix} 1 & 2 \\ 3 & 9 \\ 8 & 6 \end{bmatrix}
-```
-
-Note in python is is equivalent to do :
-```python
->>> import numpy as np
-np.where([[True, True], [True, False],[False, True]],[[1, 2], [3, 4],[5, 6]],[[12, 11], [10, 9], [8,7]])
-array([[1, 2],
- [3, 9],
- [8, 6]])
-```
-
-#### Inputs and outputs
-
-##### `condition`
-
-Tensor `condition` is a tensor of boolean values indicating which elements to choose from `X` and `Y`.
-
-The shape of tensor `condition` should be the same as `X` and same as `Y`. `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-###### Constraints
-
-- (C1) Consistency in shape
- - Statement: The shapes of `condition`, `X`, and `Y` must be the same. $N(condition)=N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-##### `X`
-
-Tensor `X` is one of the two input tensors from which elements are picked based on the `condition`.
-
-The shape of tensor `X` should be the same as `condition` and `Y`. $N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-###### Constraints
-
-- (C1) Consistency in shape
- - Statement: The shapes of `condition`, `X`, and `Y` must be the same. `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-##### `Y`
-
-Tensor `Y` is one of the two input tensors from which elements are picked based on the `condition`.
-
-The shape of tensor `Y` should be the same as `condition` and `X`. `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-###### Constraints
-
-- (C1) Consistency in shape
- - Statement: The shapes of `condition`, `X`, and `Y` must be the same. $N(condition)=N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-#### Outputs
-
-##### `Z`
-
-The tensor `Z` is the output tensor formed by picking values from `X` and `Y` based on `condition`.
-
-`Z` will have the resulting shape of `condition`, `X`, and `Y` and must be the same. `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-###### Constraints
-
-- (C1) Consistency in shape
- - Statement: The shape of `Z` will match the `condition`, `X`, and `Y` shape. $N(Z)=N(condition)=N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`.
-
-
-#### Attributes
-The `where` operator does not require any attributes.
-
-### Formal specification
-
-The formal specification of the `where` operator using the Why3 language[^1] is provided below. This specification ensures the consistency and desired behavior of the operator within the constraints described.
-
-```ocaml
-module Where
- use int.Int
- use bool.Bool
- use array.Array
-
- type tensor = {
- data: array real;
- dims: array int; (* tensor dimension *)
- }
-
- function size (t: tensor) : int =
- let rec product (a: array int) (i: int) : int =
- if i = length a then 1 else a[i] * product a (i + 1)
- in product t.dims 0
-
- predicate same_dimensions (dims1: array int) (dims2: array int) : bool =
- length dims1 = length dims2 /\
- (forall i: int. 0 <= i < length dims1 -> dims1[i] = dims2[i])
-
- predicate where_result
- (cond: tensor)
- (X: tensor)
- (Y: tensor)
- (Z: tensor)
- (i: int) =
- (cond.data[i] <> 0 -> Z.data[i] = X.data[i]) /\
- (cond.data[i] = 0 -> Z.data[i] = Y.data[i])
-
- val where (cond: tensor) (X: tensor) (Y: tensor): tensor
- requires { same_dimensions cond.dims X.dims }
- requires { same_dimensions cond.dims Y.dims }
- requires { same_dimensions X.dims Y.dims }
- ensures { Z.dims = cond.dims }
- ensures { length Z.data = size Z }
- ensures { forall i: int. 0 <= i < length Z.data -> where_result cond X Y Z i }
-```
-
-### Specification en Frama-C
-Another formal specification of the conv operator using Frama-C language is presented below[^2].
-
-
-```c
-#include
-
-/* Tensor type definition */
-typedef struct {
- double* data;
- int* dims;
- int num_elements; // Total Number of element in data
- int num_dims; // Number of dimensions
-} tensor;
-
-/* Function in order to compute the size total Number of element in a tensor */
-int size(const tensor* t) {
- int product = 1;
- for (int i = 0; i < t->num_dims; i++) {
- product *= t->dims[i];
- }
- return product;
-}
-
-/* Function to check if two sets of dimensions are the sames */
-bool same_dimensions(const int* dims1, int len1, const int* dims2, int len2) {
- if (len1 != len2) return false;
- for (int i = 0; i < len1; ++i) {
- if (dims1[i] != dims2[i]) {
- return false;
- }
- }
- return true;
-}
-
-/*@ predicate where_result {
- ensures (cond->data[i] != 0 ==> Z->data[i] == X->data[i]) &&
- (cond->data[i] == 0 ==> Z->data[i] == Y->data[i]);
- }
-*/
-
-/*@ requires same_dimensions(cond->dims, cond->num_dims, X->dims, X->num_dims);
- requires same_dimensions(cond->dims, cond->num_dims, Y->dims, Y->num_dims);
- requires same_dimensions(X->dims, X->num_dims, Y->dims, Y->num_dims);
-
- ensures Z->num_dims == cond->num_dims;
- ensures Z->num_elements == size(Z);
- ensures \forall int i; 0 <= i < Z->num_elements ==> where_result(cond, X, Y, Z, i);
-
- @*/
-tensor where(const tensor* cond, const tensor* X, const tensor* Y) {
- tensor Z;
-
- for (int i = 0; i < Z.num_elements; ++i) {
- if (cond->data[i] != 0) {
- Z.data[i] = X->data[i];
- } else {
- Z.data[i] = Y->data[i];
- }
- }
-
- return Z;
-}
-```
-
-
-[^1]: See [Why3 documentation](https://www(W)hy3.org/)
-
-[^2]: See [Frama-C
- documentation](https://www.frama-c.com/html/documentation.html)
diff --git a/safety-related-profile/documents/publications/ERTS2026/2025-06-02 SONNX - ERTS 2026 abstract - final.pdf b/safety-related-profile/documents/publications/ERTS2026/2025-06-02 SONNX - ERTS 2026 abstract - final.pdf
new file mode 100644
index 00000000..297b5a74
Binary files /dev/null and b/safety-related-profile/documents/publications/ERTS2026/2025-06-02 SONNX - ERTS 2026 abstract - final.pdf differ
diff --git a/safety-related-profile/documents/reqs.md b/safety-related-profile/documents/reqs.md
deleted file mode 100644
index 8612bea7..00000000
--- a/safety-related-profile/documents/reqs.md
+++ /dev/null
@@ -1 +0,0 @@
-File moved [here](../deliverables/reqs/reqs.md).
\ No newline at end of file
diff --git a/safety-related-profile/meetings/Local WGs/Tlse/agenda.md b/safety-related-profile/meetings/Local WGs/Tlse/agenda.md
new file mode 100644
index 00000000..52062023
--- /dev/null
+++ b/safety-related-profile/meetings/Local WGs/Tlse/agenda.md
@@ -0,0 +1,412 @@
+#### 2025/06/05
+- Discussion about the relation between the informal and formal spec. Are we really convinced on the benefits of formal spec if we don't add functional invariants? See e.g. .
+- Check [this ONNX PR](https://github.com/onnx/onnx/issues/8054#issuecomment-4622938091)
+
+##### Minutes
+
+### Meeting Notes
+- Discussion about formal specification
+ - The status
+ - Before being an input to the code generation process, the formal specification is **a specification**. So it shall define precisely what an operator shall do.
+ - Initially, we wanted to have a formal spec that is "close to the informal spec". We used to sad that the formal spec (i) should allow the user to express as intuitively as possible what he/she has in mind, (ii) should be easily traceable to the informal spec.
+ - As of today, this is not really the case.
+ - First, the formal spec is very large since it covers two levels of spec (abstract and concrete) and contains code only needed for the proof.
+ - In the abstract spec, specification often use induction, which slightly differs to the using of loops (sums, products) in the informal spec.
+ - The notation is a bit complicated
+ - For the **Conv** operator, for instance, the informal spec is specified using simple nested loops whereas a the abstract formal spec is broken down in a series of functions, each corresponding to a level of loops, and expressed inductively.
+ - The concrete specification is actually closer to the informal spec.
+ - So the first question is: **What can we do to facilitate the validation of the formal spec ?**
+ - Stick as much as possible to the notations used in the informal spec
+ - Introduce functions that would "mimick" the classical $\sum$ , or $\prod$ ?
+ - Improve the formal specification guidelines with rules to improve traceability?
+ - Finally, the main usage of the formal spec is to support the automatic generation of the implementation. But for most operators, the implementation is not complex once the specification is available, so being able to generate it automatically is interesting but not fundamental. So, besides being used to generate the code another use of the formal spec could be to verify some interesting properties of the spec, and so improve our confidence in the spec
+ - For **Conv**, we have for instance the following properties:
+ - **Conv**(X, W1 + W2) = **Conv**(X, W1) + **Conv**(X, W2)
+ - **Conv**(a X, W) = a **Conv**(X, W)
+ - **Conv**(X, a W) = a **Conv**(X, W)
+ - **Conv**(0, W) = 0
+ - **Conv**(X, 0) = 0
+ - **Conv**(X, W, B) = Conv(**X**, W, 0) + broadcast(B)
+ - permutation symmetry
+ - identity when applying a 1x1 kernel
+ - For stride 1, dilation 1, and no padding, shifting the input down and right by one cell should shift the output down and right by one cell, on the overlapping output domain.
+ - etc.
+ - If we could verify those properties on the formal spec, this would contribute to our confidence on the spec.
+ - The question is: **are such properties provable using Why3 with a reasonable effort?**
+- Templating...
+ - We see that whatever our effort,
+ - we leave some errors in the informal spec,
+ - we have hard times keeping the existing specs in-line with the ever-changing guidelines.
+ - One solution could be to create a template that would capture the recurrent part of the spec.
+ - [ ] Propose a templating scheme [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 hivaem
+ - An additional solution consists to apply a LLM to check our production. First tests show the efficiency of the approach.
+- [X] In **MatMul**, exchange the **Float** and **Int** sections [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 8b7vr2
+- We are still experiencing formatting issues with formulas.
+ - [ ] Check and correct formatting problems [project::[[SONXX]]] [assignee::[[SONNX]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 azjdc5
+- For the **Maxpool** operator.
+ - We keep the use of the padded kernel $X_p$ because it is intuitive (even if we know that a real implementation will never create this padded kernel...).
+ - The existing formula for the indice have to be updated. They will be using the $argmax$
+ - [ ] Update the **Maxpool** operator to reflect the decision taken about its specification [project::[[SONXX]]] [assignee::[[SOUYRIS Jean]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 tspe86
+ - [X] Send a mail to J&R to explain how the indice is computed [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 l2vlgb
+ - Mail sent on 08/06/2026
+- Hyperlinks
+ - Update hyperlinks according to what has been done for Div.
+- Use of mathematical operators
+ - Most operators refer to mathematical operators such as $+$ or $\times$ (including in $\sum$ or $\prod$ ), but these operators are not defined. For floating point number, it seems that we have made a general statement saying that they refer to IEEE, but not for int.
+ - One possibility could be to specify operators using the **Add** and **Mul** operators (e.g, for scalar). But this will make the informal spec harder to read.
+ - The other solution can be to use the usual $+$ and $\times$ and add a general notice saying that whenever those operators are used, they respectively refer to **Add** and **Mul**.
+ - We opt for the second solution.
+ - [X] Add a general notice saying that whenever those operators are used, they respectively refer to **Add** and **Mul**. [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 afe7zu
+ - This notice shall belong to the "SONNX Reader user manual"
+ - First version of the ["User manual"](../../../sonnx/ops/docs/guidelines/sonnx_user_manual.md) created.
+
+
+
+#### 2025/05/21
+- Jean-Loup comments on [testing strategy](../../../sonnx/ops/docs/guidelines/tests-part2.md)
+- Discussion about the use of Hypothesis
+- Review of Franck guidelines for accuracy evaluation
+- Review of pending informal spec.
+
+###### New actions
+- [ ] Update the specification guidelines to match with the current practice (see **Div**) [project::[[SONNX]]]➕ 2026-05-21
+- [X] Update the tagging of requirements of all specs (see **Div**) [project::[[SONNX]]]➕ 2026-05-21
+- [ ] Add tags on the general constraints [assignee::[[SOUYRIS Jean]]] ➕ 2026-05-21
+- [ ] Rename section "informal specification" to "Function" [project::[[SONNX]]] ➕ 2026-05-21
+- [ ] In the signature section, add the types (see **Div**). [project::[[SONNX]]] ➕ 2026-05-21
+- [ ] Have a closer look at the actual benefits of Hypothesis to support our test strategy [project::[[SONNX]]] ➕ 2026-05-21
+- [ ] Update the priority list of operators [project::[[SONNX]]] [assignee::[[SOUYRIS Jean]]]] ➕ 2026-05-21
+- [X] Update the **Add** operator to match the latest update to **Div** [project::[[SONNX]]] [assignee::[[SOUYRIS Jean]]] ➕ 2026-05-21
+- [ ] Update the test strategy [project::[[SONNX]]] ➕ 2026-05-21
+ - Add a "synthesis" section that provide clear explanation on how to implement it.
+ - Discriminate constraints concerning pre-conditions from constraints determined by the functional spec. Use different notation (e.g., $p$ and $q$).
+ - Modify the example on MC/DC to illustrate the handling of a functional constraint.
+ - Illustrate the approach on **Maxpool**
+ - Take care of tests cases that are actually identical due to absorption.
+- [ ] Create a "guide" to help reading the informal spec. [project::[[SONNX]]] ➕ 2026-05-21
+ - Introduce the notations, the compliance to IEEE
+ - Introduce the structure of the spec
+ - Explain how functional rules must be read (from top to bottom...).
+- [ ] Check problem with negation when dealing with NaNs [project::[[SONNX]]] ➕ 2026-05-21
+ - Less is specified as $A 2
+
+#### 2025/04/16
+- Discussion about Jean-Loup's mail dated 10/04 "Re: SONNX Graph status"
+- Discussion on the test strategy
+- Validation of guidelines on formal spec
+- Review of numerical accuracy
+- Review of operators TBD
+###### Minutes
+- Points 1 and 2 addressed.
+###### New actions
+###### Previous actions
+- [X] In the definition of tensor indexes in the glossary, add a range constraint of each index.
+- [ ] In the examples, check that we use consistent notations for tensors (use of square brackets)
+- [X] In the guidelines and templates, propose to use tags that are not visible in the rendered page
+ - This prevents cluttering of the spec. and tags can still be processed on the markdown source file.
+ - Proposed solution:
+ ```
+ This is a first traceable specification item. This is another traceable specification item. This is some information.
+ ```
+ - [X] Rollback to the previous version with visible tags.
+- [ ] In the guidelines, give precise rules about the factorization of the spec. between types
+ - For structural operators, there is a unique spec for all types including "reals"
+ - [ ] Check if we could suppress "real" for structural operators.
+- [ ] Add tests for the "special" values of tensor dimensions: scalar (rank 0) and null tensors
+- [ ] Address the case of negative axes
+- [ ] Check the behaviour of softmax for nb of axes > 2
+
+
+#### 2025/03/26
+
+##### Agenda
+- Finalization of **flatten**, **Clip** and **Tanh**.
+
+##### Minutes
+- Brief discussion on the composite operators (operators built out of other operators). No clear statement as of today...
+- Identification of an unexpected behaviour of **Clip** when using +0 and -0. **Clip(X,L,M)** is not equivalent to `min(M,max(X,L))`. For instance, `min(-0,max(-1,+0)) = -0` while, according to the specification, the value should be +0.
+- Note that the case of +0 and -0 raise a problem when writing the expression `Y= f(...)` to specify the value of output `Y` : `f()` may return +0 or -0 indifferently (because +0 = -0) whereas the subsequent behaviour (based on `Y`) may be different. Instead of `=` we should use an arrow annotation to denote the assignment, not the equality (or we must write somewhere that `=` must be understood `symbol-wise`)
+
+###### New actions
+- [ ] In the definition of tensor indexes in the glossary, add a range constraint of each index.
+- [ ] In the examples, check that we use consistent notations for tensors (use of brackets)
+- [ ] In the guidelines and templates, propose to use tages that are not visible in the rendered page
+ - Prevent cluttering of the spec. and tags can still be processed on the markdown source file.
+- [ ] In the guidelines, give precise rules about the factorization of the spec. between types
+ - For structural operators, there is a unique spec for all types including "reals"
+ - [ ] Check if we could suppress "real" for structural operators.
+- [ ] Add tests for the "special" values of tensor dimensions: scalar (rank 0) and null tensors
+###### Previous actions
+- [ ] Address the case of negative axes
+- [ ] Check the behaviour of softmax for nb of axes > 2
+
+#### 2025/03/13
+
+##### Agenda
+
+##### Minutes
+- Back on **MaxPool**: do we expect null tensor when preconditions are not satisfied? Do we consider that pre-conditions are necessarily satisfied?
+ - We consider that incompatible kernel / argument sizes leads to a null tensor.
+ - We keep the current organization where pre-conditions are expressed in the section dedicated to parameters and attributes, and provide an explanation of the expected behaviour in the "Error condition" section.
+ - In the most frequent case, we will simply state that the behaviour of the operator is not defined if any of the pre-conditions is not satisfied (see **Clip**). In some specific cases (e.g., the case of **MaxPool** and **Conv**, when the dimensions of the kernel and tensors are not compatible), we indicate that we return a null tensor.
+ - Why? Because it is not always trivial to verify that the condition is satisfied if dynamic shape modifications are performed. More often than not, conditions can be checked statically when all parameters related to sizes can be computed in advance.
+ - [X] Add a paragraph about constraint violation on the "informal spec guidelines".
+ - Eric: done
+- Segregation of "specifying elements" from "non specifying elements".
+ - There are two objectives:
+ - discriminate the specifying parts from the documentation parts
+ - provide "hooks" for traceability purposes
+ - We propose to
+ - use section names when traceability is a the scale of the section.
+ - introduce tags `[specx.y...]` and `[/specx.y...]`, where `x`, `y`,... are numbers, when a specific element in the section needs to be designated. The dot notation (`x.y...`) is introduced when a more specific element needs to be identified within an element that is already tagged.
+ The exact tag is : `[spec1]`
+ - Elements that are provided for information are tagged using `[info]` `[/info]`. All other elements are specifying. See the spec of operator [**Div**](../../../sonnx/ops/spec/informal/div/div.md).
+ - [X] Add a paragraph about tagging in guidelines
+ - Eric: done
+ - [ ] Address the case of negative axes
+ - [ ] Check the behaviour of softmax for nb of axes > 2
+- Review of **SoftMax**
+
+#### 2025/02/27
+
+##### Agenda
+- Review of actions
+- Operators: [MaxPool](../../../sonnx/ops/spec/informal/maxpool/maxpool.md) and [Pow](../../../sonnx/ops/spec/informal/pow/pow.md).
+- Accuracy: check the accuracy analysis of [div](../../../sonnx/ops/spec/informal/div/div_acc.md) and [tanh](../../../sonnx/ops/spec/informal/tanh/tanh_acc.md). See Franck's mail dated 2026/02/13.
+- (Re-)discussion about using composition when defining complex operator. The case of **Conv**.
+- Testing
+ - Shouldn't our reference implementation be using multiple precision (e.g., mpfr)?
+
+##### Minutes
+- Reviewed: **Maxpool**, **Where**, **Pow**
+- About **Pow**
+ - The specification currently imposes no restriction and goes beyond what is required by IEEE.
+- [ ] Review **Pow** against IEEE 754 and restrict **Pow** to match IEEE's spec of Pow.
+- About formal specification and verification
+ - Might be wise to limit format specification and proof to "complex" operators (and the graph semantics), especially the structural ones that make complex (hence error prone) manipulations of indexes.
+- [ ] Propose a restriction to the scope of formal specification and verification (at least in a first phase)
+
+
+#### 2025/02/13
+##### Agenda
+No agenda defined...
+
+##### Minutes
+- Travail sur **LeakyRelu**, **Max**, **Sigmoid**, **Sqrt**, **Exp**, **Log**
+
+- [X] Complete the disclaimer about tests and place emphasis on the last sentence (the rest being more methodological)
+ - (Eric) Done.
+- [X In the set of "basic mathematical operators" that , remove the inverse trigonometric functions
+ - (Eric) Done.
+- [ ] Check if, in the real domain, we use "$=\pm \infty$" without saying that it is a limit.
+- [ ] Check that the sections of the spec are always ordered as follows: Real, Float, Int
+- [ ] Introduce broadcasting for all operators that support it.
+ - (Eric) The other solution is to specify what is broadcasting and simply indicate, for a given operator, if it supports broadcasting or not. The effect of broadcasting on the operator can be explained once for all (see e.g., what has been done for **Max**).
+- [ ] Checks that all hyperlinks are relative (i.e., they shall not point to the current repo / branch)
+- [X] Introduce the IEEE special values in the definition section. In particular, define "+0", "-0"
+ - (Eric) Done.
+ - I think that a brief section about FP number would be worthwhile (with appropriate references to the standard and to the well-known "What Every Computer Scientist...") )
+- [X] When 0 is actually "+0", use "+0". "0" must be used either as the usual 0, or as "+0" or "-0".
+ - (eric) Added in [notations](../../../sonnx/ops/spec/informal/common/notations.md).
+- [ ] Check the behaviour of **Max** for NaNs
+- [ ] Check **Max** for "-0".
+- [ ] Check all integer operators against overflow conditions
+- [X] In the definition section, introduce the constants `minfloat16`, `maxfloat16`, etc. that are used (e.g.,) in the specification of **Exp**.
+ - (Eric) Added in [definitions](../../../sonnx/ops/spec/informal/common/definitions.md).
+
+
+#### 2025/02/04
+- Feedback on strange observations on $MaxPool$ and code analysis. (30 min max)
+- Proposal of guidelines modifications to factorize restrictions and constraints.
+- Discussion (1h max) about the specification of ops in FP.
+ - What do we need exactly?
+ - Specification *vs.* implementation... Up to what point shall we be prescriptive wrt the implementation?
+ - Paper pointed out by Jean-Loup "Make it Real: Effective Floating Point Reasoning via Exact Arithmetic"
+ - The question of accuracy...
+- Review of [Max](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/max/max.md), [Range](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/range/range.md), [Div](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/div/div.md)?
+
+#### Minutes
+
+##### Updates to the guidelines
+- The section about attributes must be described only once, in the subsection about reals. The other sections (for the other types) must simply refer to it and express other specific constraint is necessary.
+- The same applies to constraints: as far as they are not specific to a certain data type, they will not be restated, but simply refered to by an hyperlink. See the example of $Maxpool$.
+- About constraints :
+ - We add a line about the "general constraints" for all operators. We do not specify precisely which of those constraints are actually applicable. This is up to the user to check...
+- [X] Include factorizations of attributes and constraints and the sentence about general constraints in the guidelines and template
+ - Added in the guidelines (Eric)
+
+##### Update to preamble / lexicon etc.
+- Introduce a "preamble" / "introduction" that will express some important general information and disclaimers
+ - Tests are provided to support the validation of the specification and the verification of a given implementation against the reference implementation. However:
+ - They only cover functional tests (they do not cover implementation)
+ - They are not aimed at being exhaustive and do not substitute to applicable practices in the domain of use (e.g., aeronautics...)
+
+##### Mathematical operators
+- The specification is defined using the operators in R (in particular).
+- A specific implementation (the one that we will actually generate) may be described in the "Accuracy" section in order to support the accuracy analysis. In that case, the operators will not be those in R but thos ein F. So we will use (e.g., "+." to express the addition in F).
+- The specification will be written using a certain set of "basic" operator that will nt be defined any further. This includes at least `+`, `-`, `*`, `/`, `exp`, `sin`, etc. They have to be defined in the "preamble" or "introduction" to SONNX.
+- [X] Define the set of basic operators in the SONNX introduction
+ - Added in the guidelines (Eric)
+- The operator shall be specified for all special values (infs, +/-0, and NaN).
+ - This may be expressed using "if" clauses that will specify the behavior for those specific values, or using some general, informal sentence covering thoses cases. The point is that the behaviour of the operator must be covered for all these special values.
+- [X] Check if infs ($\pm \infty$) belong to set the of real numbers
+ - Infinities are **not** real numbers (simply R is a field, an infs do not satisfy the law of fields, think about the addition of infinities). $R$ only contains finite numbers (excluding infinities). Infinities are introduced in the "extended real number system". See [here]([Rudin W. Principles of Mathematical Analysis 3ed](https://david92jackson.neocities.org/images/Principles_of_Mathematical_Analysis-Rudin.pdf)).
+- (off meeting : if it is the case, then we will have to define what +inf (- | / ) -inf means exactly in R...)...
+- [ ] In the section about accuracy, clarify the purpose of the analysis (methodological)
+- [X] In the examples, use $\approx$ instead of $=$ when applicable
+ - Added in the guidelines (eric)
+- [X] The jupyter notebook used to compute the examples must be provided
+ - Added in the guidelines (Eric)
+
+##### Tests
+- We need a clear functional testing strategy. We cannot only rely to the tests automatically generated by Hypotheses. We may provide guidance on a "per operator class" basis. For instance, we may give specific guidance for operators using padding, etc.
+- [ ] Elaborate a clear testing strategy
+- The main test oracle is ORT. However, in some cases (e.g., MaxPool) the behaviour of ORT may be different from what we would expect (example: effect of inf in inputs). In that case, if the behaviour is not a manifest error (such as the case of "-4)...), we will add a notice explaining the discrepancy between our spec and the observable behaviour of ORT.
+ - Notice added (Eric)
+- [X] Move the existing tests in the test folder
+- [ ] (Henri) Check the behaviour of MaxPool with ORT on a GPU executor.
+
+##### Error conditions
+- An "error condition" is a condition for which an operator can not compute a value that maps to some real counterpart (i.e., excluding Infs and NaN). It shall not cover the case where the output is not a value due to a simple propagation (of NaN, of Inf...).
+- basically, we must put in the "error conditions" all results that are, , "not expected".
+- The name of this section could possibly be changed: "Special cases" ?
+
+#### 2025/01/15
+##### Agenda
+- Case of max:min/maxpool with NaN: for all operators, try to rely on the spec of Max and Min. (Nota: I don't like the spec NaN > Inf > ... I think that we should treat NaN as a special case because NaN is no normally comparable and here we are defining a comparison...)
+- Processing of existing issues: see [issues tagged "TLSE WG"](https://github.com/users/ericjenn/projects/4/views/14))
+ - Clip
+ - Relu
+- Sujet pour CEtIC
+- Interesting links identified by Jean-Loup in ONNX:
+ - https://onnx.ai/onnx/repo-docs/DimensionDenotation.html
+ - https://onnx.ai/onnx/repo-docs/TypeDenotation.html
+ - https://onnx.ai/onnx/repo-docs/MetadataProps.html
+- Status of formal specification of CONV (Mariem)
+- Discussion about SONNX event in late Feb / March
+##### Minutes
+- review of Clip
+- review of Relu
+- creation of Relu
+- discussion about CETiC
+
+##### Actions
+##### New actions
+ - [X] (1501-1, Jean-Baptiste) Faire une proposition de contenu pour une soumission à CETiC
+ - Proposition faite par JB le 16/01.
+ - Modifications proposées par Eric le <19/01>
+ - [X] (1501-2, Jean-Loup) Remove the relation NaN>Inf... and replace it by an explicit test for NaNs...
+ - [X] (1501-3, Eric) Introduce the term "Scalar" and (our) concept of "type" (numerical / value type) in the glossary.
+ - Done, see [here](../../../sonnx/ops/spec/informal/common/definitions.md).
+ - [X] (1501-4, eric Clarify the meaning of "heterogeneous" in ONNX
+ - ONNX, heterogeneous seems to mean that the inputs can be of different types. But this is not the case. For instance, operator `Where` has "heterogeneous" arguments but, in ORT, `Where` requires the same types for `X` and `Y`. For example, one cannot mix an int32 tensor with a float tensor, or an int32 tensor with an int64 tensor. There is no type promotion.
+ - Heterogeneous **seems** to mean that the operator have arguments of different type: boolean for B and other for `X` and `Y`.
+ - But this is not the case for operator `Add` that is also tagged `heterogeneous` even though all arguments must be of the same generic type `T`
+(`Add(float,int)` is rejected and so is `(int32,int64)`).
+ - Tag "Heterogeneous" is automatically generated by ONNX' documentation generator that is located in [onnx/docs/gen_doc.py](https://github.com/onnx/onnx/blob/main/onnx/defs/gen_doc.py).
+ - Normally, "heterogeneous" should only refers to variadic arguments that are not "homogeneous". The generation of "heterogeneous" appear in version ONNX 1.4.0. And it is always related to variadic arguments. The schema is generated by function `display_schema` which calls `generate_formal_parameter_tags`:
+
+ ```python
+ def generate_formal_parameter_tags(formal_parameter: OpSchema.FormalParameter) -> str:
+ tags: list[str] = []
+ if OpSchema.FormalParameterOption.Optional == formal_parameter.option:
+ tags = ["optional"]
+ elif OpSchema.FormalParameterOption.Variadic == formal_parameter.option:
+ if formal_parameter.is_homogeneous:
+ tags = ["variadic"]
+ else:
+ tags = ["variadic", "heterogeneous"]
+ differentiable: OpSchema.DifferentiationCategory = (
+ OpSchema.DifferentiationCategory.Differentiable
+ )
+ non_differentiable: OpSchema.DifferentiationCategory = (
+ OpSchema.DifferentiationCategory.NonDifferentiable
+ )
+ if differentiable == formal_parameter.differentiation_category:
+ tags.append("differentiable")
+ elif non_differentiable == formal_parameter.differentiation_category:
+ tags.append("non-differentiable")
+
+ return "" if len(tags) == 0 else " (" + ", ".join(tags) + ")"
+ ```
+
+ - By the way, even if the test was not only done for variadic argumebnts, it depends on the `is_homogeneous` boolean that comes from the schema defined in the `defs.cc` files (for instance, for `Where`: see [here](https://github.com/onnx/onnx/blob/main/onnx/defs/tensor/defs.cc#L2865). For the `Where` operator, the schema is the following:
+ ```
+ ONNX_OPERATOR_SET_SCHEMA(
+ Where,
+ 16,
+ OpSchema()
+ .SetDoc(GET_OP_DOC_STR(std::string(Where_ver16_doc) + GenerateBroadcastingDocMul()))
+ .Input(
+ 0,
+ "condition",
+ "When True (nonzero), yield X, otherwise yield Y",
+ "B",
+ OpSchema::Single,
+ true, // <= This is the "homogeneous" boolean
+ 1,
+ OpSchema::NonDifferentiable)
+ .Input(
+ 1,
+ "X",
+ "values selected at indices where condition is True",
+ "T",
+ OpSchema::Single,
+ true,
+ 1,
+ OpSchema::Differentiable)
+ .Input(
+ 2,
+ "Y",
+ "values selected at indices where condition is False",
+ "T",
+ OpSchema::Single,
+ true,
+ 1,
+ OpSchema::Differentiable)
+ .Output(
+ 0,
+ "output",
+ "Tensor of shape equal to the broadcasted shape of condition, X, and Y.",
+ "T",
+ OpSchema::Single,
+ true,
+ 1,
+ OpSchema::Differentiable)
+ .TypeConstraint("B", {"tensor(bool)"}, "Constrain to boolean tensors.")
+ .TypeConstraint(
+ "T",
+ OpSchema::all_tensor_types_ir4(),
+ "Constrain input and output types to all tensor types (including bfloat).")
+ .TypeAndShapeInferenceFunction([](InferenceContext& ctx) {
+ propagateElemTypeFromInputToOutput(ctx, 1, 0);
+ if (hasNInputShapes(ctx, 3)) {
+ std::vector shapes;
+ shapes.push_back(&ctx.getInputType(0)->tensor_type().shape());
+ shapes.push_back(&ctx.getInputType(1)->tensor_type().shape());
+ shapes.push_back(&ctx.getInputType(2)->tensor_type().shape());
+ multidirectionalBroadcastShapeInference(
+ shapes, *ctx.getOutputType(0)->mutable_tensor_type()->mutable_shape());
+ }
+ }));
+ ```
+
+ - As all boolean values are "true", "heterogeneous" should never be displayed!!
+
+ - [ ] (1501-5, ???) Clarify how we handle value constraints for attributes
+ - [X] (1501-6,Eric) Check the actual behavior of Relu and LeakyRelu in ONNX. Check if alpha can be negative.
+ - Relu: input [-1.0, 0.0, 1.0, float("nan")] => [ 0. 0. 1. nan]
+ - Leaky relu: [-1.0, 0.0, 1.0, float("nan")] => [nan 0. 1. nan] with alpha = NaN, as expected. Alpha can be negative and the value for X<0 becomes positive.
+ - [ ] (1501-7,???) Update the informal spec guidelines (enforce usage of ONNX names and provision of denotation, use of generic types: "with int in (int8,int16,...)" )
+
+##### Past actions
+None.
\ No newline at end of file
diff --git a/safety-related-profile/meetings/Local WGs/Tlse/attachments/conv_informa_formal.png b/safety-related-profile/meetings/Local WGs/Tlse/attachments/conv_informa_formal.png
new file mode 100644
index 00000000..c50dc33d
Binary files /dev/null and b/safety-related-profile/meetings/Local WGs/Tlse/attachments/conv_informa_formal.png differ
diff --git a/safety-related-profile/meetings/Other_meetings/2025-03-20-An-Er-Se-Je.md b/safety-related-profile/meetings/Other_meetings/2025-03-20-An-Er-Se-Je.md
new file mode 100644
index 00000000..dfce2110
--- /dev/null
+++ b/safety-related-profile/meetings/Other_meetings/2025-03-20-An-Er-Se-Je.md
@@ -0,0 +1,50 @@
+### Object: discussion about 4 points raised by Andreas and Sebastian:
+
+ 1) One if not the central meeting at Onnx is the SIG Operators meeting. It takes place every month. We should actively bring our points there to
+ a) Get a discussion about our thoughts, from the experts who know operators, know how the way would be to implement them.
+ b) We could also draw more attention to our initiative there.
+
+ 2) The next Onnx Community Meetup will probably take place in June 2025.
+ (https://github.com/onnx/steering-committee/blob/main/meeting-notes/2025/20250305.md)
+ There we will have the opportunity to present what we are doing and what we have achieved.
+
+ 3) I think we need to create a lot more issues at https://github.com/onnx/onnx in order to work out the concerns more clearly, or to be able to discuss them even more with the community there, or to be able to refer to them in the Operators Meeting.
+
+ 4) As there are certainly already issues concerning SONNX at the moment, I could well imagine a new label “sonnx” for this, so that we can filter even better according to the topics.
+
+### Participants
+Andreas, Sebastian, Jean, Eric
+
+### Main conclusions
+
+- We need to formalize issue processing and push the issues to ONNX
+ - Decisions:
+ - Issues reported by the WG member are proposed to the WG (use the existing [issues file](../../documents/issues.md))
+ - Issues are discussed during the meeting to determine whether or not they must be pushed to ONNX with the sonnx tag
+ - If there is an agreement, an ONNX issue is created and tagged "sonnx"
+ - Should the issue deserves it (or should there be too many "sonnx" issues not processed by ONNX), we may request a time slot to the ONNX operators SIG.
+- We need to present our work to ONNX
+ - Results to be presented to the ONNX meetup in June
+- We need to make our work more visible
+ - (Push our work more regularly to ONNX...)
+ - (Update our landing page to point to the main results...)
+- We need to decide how our results (operator spec, ref implementation, etc.) will actually be integrated in ONNX and become visible to the ONNX users.
+ - One possibility could be to add a SONNX tab to the ONNX operators page (see below).
+ - To be discussed wit the WG
+
+
+
+- We need to be careful when putting restrictions on operators not to repel potential SONNX users.
+ - Restriction shall be justified.
+ - We have two main categories of restrictions:
+ - restrictions related to a specific requirement (see [specs](../../documents/reqs.md))
+ - restrictions aimed at simplifying our work considering (i) the use case we target , (ii) the resource we have
+ - Restrictions in the second category may be relaxed upon demand, as long as someone (the demander?) does the specification, verification and implementation work
+
+### Actions
+
+- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues.
+- [ ] (2003-2, Eric) Update SONNX landing page to point to intersting material...
+- [ ] (2003-3, Eric ) Initiate discussion in WG about ONNX integration and propose solutions to ONNX
+- [ ] (2003-4, Eric ) Give an example of the two categories of restrictions
+
diff --git a/safety-related-profile/meetings/Other_meetings/2025-10-24 Work on specification of graph execution semantics.pdf b/safety-related-profile/meetings/Other_meetings/2025-10-24 Work on specification of graph execution semantics.pdf
new file mode 100644
index 00000000..46291a15
Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/2025-10-24 Work on specification of graph execution semantics.pdf differ
diff --git a/safety-related-profile/meetings/Other_meetings/POSTER_A0 MobiliT.pdf b/safety-related-profile/meetings/Other_meetings/POSTER_A0 MobiliT.pdf
new file mode 100644
index 00000000..1d38eeae
Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/POSTER_A0 MobiliT.pdf differ
diff --git a/safety-related-profile/meetings/Other_meetings/SONNX - ERTS2026.pdf b/safety-related-profile/meetings/Other_meetings/SONNX - ERTS2026.pdf
new file mode 100644
index 00000000..7dfc6083
Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/SONNX - ERTS2026.pdf differ
diff --git a/safety-related-profile/meetings/Other_meetings/SONNX - WG114- oct-2025.pdf b/safety-related-profile/meetings/Other_meetings/SONNX - WG114- oct-2025.pdf
new file mode 100644
index 00000000..47ceb5f8
Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/SONNX - WG114- oct-2025.pdf differ
diff --git a/safety-related-profile/meetings/Other_meetings/SONNX - WG114.pdf b/safety-related-profile/meetings/Other_meetings/SONNX - WG114.pdf
new file mode 100644
index 00000000..6f720d1f
Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/SONNX - WG114.pdf differ
diff --git a/safety-related-profile/meetings/Other_meetings/imgs/onnx_ops.png b/safety-related-profile/meetings/Other_meetings/imgs/onnx_ops.png
new file mode 100644
index 00000000..558b0f9f
Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/imgs/onnx_ops.png differ
diff --git a/safety-related-profile/meetings/README.md b/safety-related-profile/meetings/README.md
index 18573012..be6f0835 100644
--- a/safety-related-profile/meetings/README.md
+++ b/safety-related-profile/meetings/README.md
@@ -1,5 +1,5 @@
-Location of the SRP meeting minutes.
+- [Minutes of the SONNX WG](./minutes.md)
- Proposals for presentations during the WG meetings shall be done in [presentation proposals](https://github.com/ericjenn/working-groups/blob/3f64a7b8b5dc4d9214f3d69e4e3b7ec1523b56cc/safety-related-profile/meetings/presentation_proposals.md)
-
+- The slides of the presentations done during the WG meeting are located [here](./slides/)
diff --git a/safety-related-profile/meetings/analysis_sub_wg/README.md b/safety-related-profile/meetings/analysis_sub_wg/README.md
deleted file mode 100644
index 9b0d1b29..00000000
--- a/safety-related-profile/meetings/analysis_sub_wg/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-This is the working area for the ONNX analysis sub-wg.
-Results of this activities can be found in file [issues.md](../../documents/issues.md).
diff --git a/safety-related-profile/meetings/analysis_sub_wg/slides-18-12.md b/safety-related-profile/meetings/analysis_sub_wg/slides-18-12.md
deleted file mode 100644
index 47d6670e..00000000
--- a/safety-related-profile/meetings/analysis_sub_wg/slides-18-12.md
+++ /dev/null
@@ -1,15 +0,0 @@
----
-marp: true
-footer: '_2024-12-18 SONNX WG meeting_ - Eric'
----
-# SONNX
-## ONNX analysis sub-wg #1
-
----
-# **Objectives**
--
----
-# **Method**
----
-# **Participants**
----
diff --git a/safety-related-profile/meetings/attachments/conv2d.c b/safety-related-profile/meetings/attachments/conv2d.c
new file mode 100644
index 00000000..5e5dc8c2
--- /dev/null
+++ b/safety-related-profile/meetings/attachments/conv2d.c
@@ -0,0 +1,92 @@
+#include // Include necessary headers
+#include // For rand() function
+
+
+// Define constants
+#define F 2
+#define OH 28
+#define OW 28
+#define IH 5
+#define IW 5
+#define C 3
+#define KH 3
+#define KW 3
+#define STRIDES 1
+#define DILATATION 1
+#define PAD_LEFT 0
+#define PAD_TOP 0
+
+// Function prototype
+void conv2D(const float* input, float* output, const float* kernel, const float* biases);
+
+int main() {
+ // Define input, output, kernel, and biases arrays
+ float input[IH * IW * C];
+ float output[OH * OW * F];
+ float kernel[KH * KW * C * F];
+ float biases[F];
+
+ // Initialize input array with random values between 0 and 1
+ for (int i = 0; i < IH * IW * C; i++) {
+ input[i] = (float)rand() / RAND_MAX;
+ }
+
+ // Initialize kernel array with random values between -1 and 1
+ for (int i = 0; i < KH * KW * C * F; i++) {
+ kernel[i] = ((float)rand() / RAND_MAX) * 2 - 1;
+ }
+
+ // Initialize biases array with random values between -1 and 1
+ for (int i = 0; i < F; i++) {
+ biases[i] = ((float)rand() / RAND_MAX) * 2 - 1;
+ }
+
+ // Define and initialize output array
+ for (int i = 0; i < (IH - KH + 1) * (IW - KW + 1) * F; i++) {
+ output[i] = 0.0;
+ }
+
+ // Call conv2D function
+ conv2D(input, output, kernel, biases);
+
+ // Print the result
+ printf("Result:\n");
+ for (int f = 0; f < F; f++) {
+ printf("Filter %d:\n", f);
+ for (int i = 0; i < OH; i++) {
+ for (int j = 0; j < OW; j++) {
+ printf("%f ", output[i * OW * F + j * F + f]);
+ }
+ printf("\n");
+ }
+ }
+
+ return 0;
+}
+
+
+// conv2D(
+// input : f32[IH, IW, C] @DRAM,
+// output : f32[OH, OW, F] @DRAM,
+// kernel : f32[KH, KW, C, F] @DRAM,
+// biases : f32[F] @DRAM
+// )
+void conv2D( const float* input, float* output, const float* kernel, const float* biases ) {
+ for (size_t f = 0; f < F; f++) {
+ for (size_t i = 0; i < OH; i++) {
+ for (size_t j = 0; j < OW; j++) {
+ output[i * OW * F + j * F + f] = 0.0;
+ for (size_t c = 0; c < C; c++) {
+ for (size_t m = 0; m < KH; m++) {
+ for (size_t n = 0; n < KW; n++) {
+ if (0 <= i * STRIDES + m * DILATATION - PAD_LEFT && i * STRIDES + m * DILATATION - PAD_LEFT < IH && (0 <= j * STRIDES + n * DILATATION - PAD_TOP && j * STRIDES + n * DILATATION - PAD_TOP < IW)) {
+ output[i * OW * F + j * F + f] += input[(i * STRIDES + m * DILATATION - PAD_LEFT) * IW * C + (j * STRIDES + n * DILATATION - PAD_TOP) * C + c] * kernel[m * KW * C * F + n * C * F + c * F + f];
+ }
+ }
+ }
+ }
+ output[i * OW * F + j * F + f] += biases[f];
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/attachments/date2026_workshop.pdf b/safety-related-profile/meetings/attachments/date2026_workshop.pdf
new file mode 100644
index 00000000..3c33110b
Binary files /dev/null and b/safety-related-profile/meetings/attachments/date2026_workshop.pdf differ
diff --git a/safety-related-profile/meetings/attachments/image.png b/safety-related-profile/meetings/attachments/image.png
new file mode 100644
index 00000000..50590d77
Binary files /dev/null and b/safety-related-profile/meetings/attachments/image.png differ
diff --git a/safety-related-profile/meetings/attachments/why3_op_prio.png b/safety-related-profile/meetings/attachments/why3_op_prio.png
new file mode 100644
index 00000000..9094f486
Binary files /dev/null and b/safety-related-profile/meetings/attachments/why3_op_prio.png differ
diff --git a/safety-related-profile/meetings/errors/2024-11-06 - Numerical issues.pdf b/safety-related-profile/meetings/errors/2024-11-06 - Numerical issues.pdf
deleted file mode 100644
index 155851e6..00000000
Binary files a/safety-related-profile/meetings/errors/2024-11-06 - Numerical issues.pdf and /dev/null differ
diff --git a/safety-related-profile/meetings/errors/README.md b/safety-related-profile/meetings/errors/README.md
deleted file mode 100644
index 46f93a09..00000000
--- a/safety-related-profile/meetings/errors/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-This directory contains minutes of discussions dedicated to the question of numerical accuracy, specification of FP operators, etc. :
-- A [few elements](01_what_is_the_issue.md) about the issue
-- The [slides](slides-06-11.md) presented on 11/06 WG meeting (Eric)
diff --git a/safety-related-profile/meetings/errror conditions/2025-07-30 - Discussions.md b/safety-related-profile/meetings/errror conditions/2025-07-30 - Discussions.md
new file mode 100644
index 00000000..b5e6aad9
--- /dev/null
+++ b/safety-related-profile/meetings/errror conditions/2025-07-30 - Discussions.md
@@ -0,0 +1,75 @@
+See also the previous note [here](./error_conditions_2.md).
+
+- An operator in the SONNX set shall be fully specified.
+ - In particular, the domain and range of the operator must be fully defined.
+ - The expected value to be returned by the operator must be specified for all possible input value in the domain.
+
+- The domain must be specified by constraints on the inputs (e.g, "x > 0" for operator log(x)).
+ - For complex functions, providing an explicit definition of the domain may be difficult... For instance, what is the domain of definition of f(x,y)=tan(sqrt(x+y))? [well, thi sis not the most complicated case, but you probably get the point...]
+
+- Let's call "abstract" the mathematical operator.
+ - An abstract operator may be a *** partial function ***, i.e., a function that is not be defined for some values of the input.
+
+- Let's call "concrete" the actual, computer-based, implementation of an operator.
+ - Ideally, a concrete operator should be a *** total function ***, i.e., a function that *** always *** returns a value, even if the input value is not in the domain defined by the abstract function.
+ - For instance, the concrete Div(x,y) for the float32 data type shall return an Inf is y=0.0 whereas operator Div(x,y) is simply not defined in R.
+ - The SONNX specification shall specify the total function.
+
+- The principle of defining total functions is applicable for concrete operators in the domain of IEEE floating point numbers because this behaviour is completely supported by the current hardware targets.
+
+- This principle is *** not applicable *** to integers because there is no direct hardware support for that.
+ - This means that, for integer data types, there should be something such as an "undefined behaviour" and an "undefined output".
+ - Concerning "undefined behaviour"
+ - This occurs for instance in the case of a division by zero. The behaviour depends on the target (including HW, runtime, OS). For instance, a trap may be raised by the processor and handled by the OS, the runtime, etc. From a SONNX user perspective, this means that the operator never returns.
+ - We *** could *** specify an operator with a controlled behaviour, but
+ - (i) this would certainly require modifying the operator interface to carry the information that the operator failed (since this data cannot be carried in the integer value itself, as it is the case for floats).
+ - (ii) no implementation would be able to comply with it (and, in particular, no existing implementation would) because testing the condition of occurence of the error would be prohitively costly.
+ - Concerning "undefined value"
+ - In the case of wrap-arounds, the error may simply be undetected and propagate. Back in the days, there used to be traps for wrap-around conditions, but it is no more the case today: to detect a wrap-around, there shall be some code to test the condition (there might be some exceptions, but I think that this is the most general case).
+ - SONNX could require wrap-around conditions to be detected and processed. If detected, this error would be handled in the same way as division by zero (the operator simply does not return). But this is practically unfeasible since it would require testing the condition bits after every operator that may lead to a wraparound.
+ - So, the specification must indicate that an operator may return an "undefined value" when a wrap-around condition may occur.
+ - Another solution would be to specifiy the exact behaviour of the integer operators, *** including the wrap-around ***. In that case, there would be no "undefined value", but the specification would be slightly more complicated ("+" on signed ints would be specified in 2's complement arithmetic. See "Option 2" in my note.
+
+*[Edoardo]* I strongly favour the "undefined value" option. Specifying the behaviour of integer overflow would require specifying details about the implementation of operators (e.g. the order of every step of a matrix multiplication).
+> [Eric] I agree. To be discussed during meeting.
+>
+- So,
+ - For IEEE data types
+ - operators *** must *** be total functions, which means that we have to cover all cases, including those where inputs are Infs and NaNs and where operator may return Infs and NaNs. The specification shall indicate what is the value returned by the operator when the inputs contains Infs or NaNs.
+ - For integer data types,
+ - for wrap-around : we fully specify operators with wrap around, i.e., see "Option 2" in this note or we specify that operations may wrap around and return "undefined value"
+ - for div / 0 : we specify that some "undefined behaviour" may occur.
+
+- The specification shall not refer to or impose a specific implementation. If the specification uses an operational way (a series of computations) to compute the function, it does not imply that the function will actually be implemented that way.
+
+- We have to discriminate errors that are inherent to the function (i.e., that are part of the mathematical definition of the function, so they shall appear in the definition of the abstract operator) from errors that depend on the way the function is implemented.
+ - For instance, let's consider the SoftMax function. The abstract function is fully defined over ]-\intfy, +\infty[. There is no constraint inherent to the function.
+ - The concrete SoftMax for float32 should return the correct approximate value of the abstract SoftMax for any value between FLOAT_MIN and FLOAT_MAX. The specification shall also specify what is the expected output when inputs contains Inf or NaNs.
+ - In this very case, this specification is "feasible" (i.e., there is a simple way to comply with it): one has simply to implement SoftMax using the "-max(zi)" trick. This means that it is reasonable to specify it that way.
+ - In other cases, preventing error cases at the implementation level is much more difficult.
+ - For instance, preventing an overflow or even a NaN in a matrix multiplication is not (easily) feasible, which means that we cannot write that the MatMul should return the correct approximate value of the abstract MatMul for any value between FLOAT_MIN and FLOAT_MAX.
+ - This can only be required for a specific domain where we know that this property can be achieved "simply" (whatever that means...).
+
+- The SONXX specification may specify a "minimum" level of service". For instance, operator Op return f(x) for x in D and f(x) or NaN for x out of D.
+ - A specific implementation may provide a better "service" such as "operator Op returns f(x) for any x". See the example of Softmax given by Franck.
+ - In that case, SONNX would require the implementer to provide an updated version of the specification of Op corresponding to its own implementation. However, the operator will at least comply with the SONNX specification.
+
+*[Edoardo]* I am not against SONNX being a "minimum level of service" specification, but it would be good to agree on a reference level. Do we analyse existing implementations and decide what the minimum threshold is? E.g. I expect the convolution operator to fail (Inf/NaN) for different input domains depending on whether the implementation is naive (nested loops) or optimised (FFT, Winograd, etc). One option is to base our specification of valid input domain D on the naive implementation produced by Why3, however bad it is (e.g. it will probably not contain the "-max(zi)" trick for SoftMax).
+
+>[Eric] To be discussed. Note, howver, that the Why3-generated implementation will have to comply with its formal sepcification, which means that the "valid input domain" will have to be specified as a predcondition on the arguments.
+
+- The semantics of a SONNX model will be the one derived from the SONNX specification of operators, not the one considering the "improvements" done in some specific implementation. So, even if this approach makes sense, it cannot really be part of SONNX...
+
+*[Edoardo]* Regarding the integer overflow issue, just bear in mind that not all hardware platforms deal with overflow by wrap-around. Instead, some Digital Signal Processors (DSP) may favour saturation, i.e. INT_MAX + 1 == INT_MAX and INT_MIN - 1 == INT_MIN. Specifying overflow as undefined behaviour in SONNX will cover both cases.
+
+>[Eric] You are absolutely right and this is a good point because we have already deployed IA on TI's Jacinto target which hosts C6X and C7X DSPs... It also raises the question of fixed point computations (but thi s is yet another story).
+
+*[Edoardo - correction]* The two comments below are about approximation error, i.e. the expected return value of operators for inputs in the valid domain. Regarding failure modes instead, I only noticed a little discrepancy in the division-by-zero example. In 2025-07-30 - Discussions.md we say that Div(x,y) returns Inf when y=0.0. That is true in IEEE754 floating point only if x!=0.0. In error_specification.md we say that "x/y returns "NaN" when y = 0" (see IEEE floating point values (FP16, FP32, FP64) section).
+
+>[Eric] You are right, 0.0/0.0 returns Nan.
+
+*[Edoardo]* Regarding the floating-point behaviour, there are two opposite philosophies. The IEEE754 standard recommends implementing correctly-rounded operators. That is, the operator behaves as if it was computing the result in infinite precision and then rounding to the nearest flaoting-point value. In contrast, the C standard leaves room to implementation-dependent behaviour which, in the extreme case, could mean that log(x) == 42 for any x is a valid C operator. Interestingly, the upcoming C standard plans to introduce "correctly rounded" version of mathematical operators with a prefix "cr_". That is, log(x) is implementation dependent, but cr_log(x) must be correctly rounded.
+
+*[Edoardo]* Implementing a correctly-rounded operator is quite hard. Even more so if the operator is a function of many variables. Restricting the domain would not mitigate this issue. Hence, the biggest problem I see does not lie in specifying a restricted domain (e.g. Sigmoid(x) should be correctly-rounded for -4 <= x <= +4), but specifying a "softer" requirement than correct roundedness (e.g. the relative error produced by Sigmoid(x) should not exceed 2%). In this respect, I suggest considering bounds on the maximum relative and absolute error allowed. A thorough empirical analysis of existing float32 and float64 libraries might give an indication on how to fix reasonable bounds on the numerical error.
+
+> [Eric] As you noticed, we are dealing here with "error conditions" (or "failure modes"), not approximations. For approximations, Francks has already proposed an approach (see the [Guidelines](../../documents/profile_opset/guidelines.md)).
diff --git a/safety-related-profile/meetings/errror conditions/2025-07-30-SONNX_error.pdf b/safety-related-profile/meetings/errror conditions/2025-07-30-SONNX_error.pdf
new file mode 100644
index 00000000..e4620903
Binary files /dev/null and b/safety-related-profile/meetings/errror conditions/2025-07-30-SONNX_error.pdf differ
diff --git a/safety-related-profile/meetings/errror conditions/error_conditions_2.md b/safety-related-profile/meetings/errror conditions/error_conditions_2.md
new file mode 100644
index 00000000..8ce3a15e
--- /dev/null
+++ b/safety-related-profile/meetings/errror conditions/error_conditions_2.md
@@ -0,0 +1,78 @@
+Error conditions and runtime errors
+(Material discussed on 2025-07-16)
+
+## Case 1: floating point computation
+
+### General principles
+- No exception is raised during a floating point computation.
+- Error values occur and are propagated according to the IEEE 754 standard:
+
+### Example : Softmax
+
+`Softmax(input,axis)= Exp(input) / ReduceSum(Exp(Input), axis=axis, keepdims=1)`
+
+$$s(z_i) = {e^{z_i} \over \sum_{j=1}^K e^{z_j}} $$
+
+Described using `Exp` (exponential element wise) and `ReduceSum` (computes the sum of the input tensor’s elements along the provided axes).
+
+See the [Google Collab notebook](https://colab.research.google.com/drive/1fFDuuAK_lA3ScDk6I_VplbC5RYBncQD6#scrollTo=BfheCRKub5vl)
+
+Neither the ORT nor the Reference over or underflow.
+
+The ONNX reference implementation computes SoftMax as follows:
+class Softmax(OpRunUnaryNum):
+
+```python
+ def _run(self, X, axis=None):
+ axis = axis or self.axis
+ tmp = X - X.max(axis=axis, keepdims=1)
+ Y = np.exp(tmp)
+ Y /= Y.sum(axis=axis, keepdims=1)
+ return (Y.astype(X.dtype),)
+```
+
+The operation is not implemented in the naive way exactly the one specified.
+
+If we use the naive, direct implementation:
+```python
+output_data_underflow_naive = np.exp(input_data_underflow)
+output_data_underflow_naive /= output_data_underflow_naive.sum(axis=0, keepdims=1)
+```
+we have a nan.
+
+There can't be a nan for Onnxruntime thanks to normalization, but another implementation could under/overflow.
+
+Shall we simply write :
+
+>**Runtime errors**
+>Operator `SoftMax` may overflow (resp. underflow) for large (resp. small) input input values.
+> In that case, the operator will return a nan.
+
+## Case 2: Integer computations
+
+### Example 1: `add` over int32 values
+
+**Option 1**: complete the specification with preconditions
+- Operator `add` is specified using the standard $+$ operator, i.e, add(x,y) returns $x+y$ and
+ - a precondition is added: $$-2^{31} \leq x+y \leq 2^{31}-1 $$
+ - or conservative constraints on $x$ and $y$ are added
+ - If $x > 0$ and $y>0$ then $y ≤ (2^{31}-1) -x$
+ - If $x < 0$ and $y<0$ then $y ≥ -2^{31} - x$
+
+ - those conditions could be checked by the user by adding a dedicated sub-graph.
+
+**Option 2:** give a rigorous specification of the `add` operator
+
+$$z=\left\{
+ \begin{array}{ c l }
+ x + y - 2^{32} & \quad \textrm{if } x + y > 2^{31}-1 \\
+ x + y + 2^{32} & \quad \textrm{if } x + y < -2^{31} \\
+ x+y & \quad \textrm{otherwise}
+ \end{array}
+\right.$$
+
+**Option 3**
+>**Runtime errors**
+> Operator `add`may overflow in the positive or negative numbers for large positive of negative input values.
+> In that case the result returned by the operator does not comply with the specification.
+
diff --git a/safety-related-profile/meetings/errors/error_specification.md b/safety-related-profile/meetings/errror conditions/error_specification.md
similarity index 53%
rename from safety-related-profile/meetings/errors/error_specification.md
rename to safety-related-profile/meetings/errror conditions/error_specification.md
index fea19721..6cfc21bc 100644
--- a/safety-related-profile/meetings/errors/error_specification.md
+++ b/safety-related-profile/meetings/errror conditions/error_specification.md
@@ -15,13 +15,15 @@ Déf. : a constraint that is expressed in the specification as a relation involv
Those constraints are pre-conditions for the operator. For this reason they don't involve outputs.
Note that outputs aren't considered to be potentially part of the precondition. The cases where we want the shape of the output to be explicitly given by the user (and not "inferred") shall be managed using attributes.
-** This is to be discussed...**
+**To be discussed...**
### A.1 - Statically verifiable constraints
-Those constraints may depend on tensor's values, but those tensors are constants given in the ONNX file (e.g., biases, weights, etc.)
+
+Those constraints may depend on values, but those values are known before runtime (e.g., biases, weights, etc.). They are stored in the ONNX file or in some external file.
### A.2 - Non-statically verifiable constraints
-Those constraints cannot be checked on the ONNX file because they depend on the values of non-constant tensors, i.e. infered tensors. This is the case for simple operators such as "log" (input values must be strictly positive) or for more complicated operators such as "unsqueeze" (input "axes" must not contain duplicate values).
+
+Those constraints cannot be checked before execution because they depend on the values of non-constant inputs. This is the case for simple operators such as "log" (input values must be strictly positive) or for more complicated operators such as "unsqueeze" (input "axes" must not contain duplicate values).
## B. - Non expressed constraints
@@ -35,47 +37,51 @@ Déf. : A constraint that is not expressed in the specification, because definin
- Case B : Error conditions cannot be checked neither the ONNX file nor on the (inputs,outputs,attributes). Those errors can be checked by instrumenting the code (in the previous example, by checking that f(y) is not zero before doing the division), by hardware error handling mechanims (traps) or by using singular values (NaN, inf, etc.).
-## Recommandations
-
-### All cases
-
-- The specification of operators shall state that the behaviour of the operator is undefined should any constraints (in A or B) be violated.
+## Recommendations
-### Case A.1
+### Case A.1 (Statically verifiable constraints)
- A model that violates constraints in class A.1 for at least one of its operators is an **invalid model**.
- An **invalid model** can be detected by a "model checker"
- Those constraints must be clearly identified because they will constitute the specification for the model checker.
-### Case A.2
-- Option 1:
- - The error condition is specified and the behaviour (incl. output) in case of error is precisely defined.
-- Option 2:
- - The error condition is specified but the behaviour (incl. output) in case of error is not specified. The specification only states that the behaviour is undefined.
+### Case A.2 (Non statically verifiable constraints)
+The error condition is specified.
+The behaviour in case of error is specified.
+
+#### At operator level
+For instance, considering the `div` operator:
+- Error condition: "denominator equals 0"
+- Behaviour
+ - For real values, operation is "undefined".
+ - For FP64, FP32, FP16, BFLOAT16, behaviour is "returns 'NaN'".
+ - For INT8, behaviour is "undefined".
-Note that this may be part of the specification of the graph execution, not only the operator. It may also involves the Operational Design Domain (ODD) of the model.
+Note the difference between "operation is undefined" and "behaviour is undefined".
-### Case B
+#### At graph level
+The only thing to be specified is how "inf" and "nan" propagate.
-The specification shall indicate that an error may occur during computations even though we may not be able to describe precisely the conditions at the (inputs and attributes) level.
+### Case B (Non expressed constraints)
+
+The specification shall indicate that an error may occur during computation even though we may not be able to describe precisely the conditions at the (inputs and attributes) level.
The low-level error condition (e.g., "division by zero") must be described.
- Option 1:
- - The low-level error condition is specified and the behaviour (incl. output) in case of error is precisely defined.
+ - The low-level error condition is specified and the behaviour in case of error is precisely defined. This covers the case where `inf` and `nan` may be generated and propagated.
- Option 2:
- - The error condition is specified but the behaviour (incl. output) in case of error is not specified. The specification only states that the behaviour is undefined.
+ - The error condition is specified but the behaviour in case of error is not specified. The specification only states that the behaviour is undefined.
We consider the **two options** because checking the occurrence of the low-level error condition may be more or less difficult to detect. For instance, to detect that no overflow occur when adding integer numbers would require specific code on most architectures...
+# Miscellaneous Remarks
-# Remarks
-
-### Remark #1: Restricting SONNX to Explicit Constraints
+## Remark #1: Restricting SONNX to Explicit Constraints
The impact of restricting the scope of SONNX to operators whose domain of definition corresponds to explicit constraints on inputs and attributes on its expressiveness should be assessed.
-### Remark #2: Domains and Ranges for Operators
+## Remark #2: Domains and Ranges for Operators
For an operator, one can associate not only a domain for its inputs but also a range for its outputs. For example, the sigmoid function has a range:
$\text{range} = [0, 1]$
@@ -84,4 +90,44 @@ By defining both domains and ranges, it becomes possible to impose generic cons
- The ODD of the model should be shuch that inputs of the model must necessarily fall within the domain of the operators in the corresponding first layer.
- The range of an operator upstream of another operator must be included in the domain of the latter.
-These constraints lead to graph-level requirements aimed at ensuring error-free inference in the model.
\ No newline at end of file
+These constraints lead to graph-level requirements aimed at ensuring error-free inference in the model.
+
+## Remark #3: Handling of NaNs and INFs
+
+Three cases must be considered :
+- real values
+- floating point values
+ - IEEE floating point values (FP16, FP32, FP64)
+ - Non IEEE floating point values (e.g., BFLOAT16, FP8, etc.)
+- integers values
+
+
+### Real values
+
+For real values, we will simply indicate that the operation is not defined when the values are out of range. This will be expresses as constraint on the input parameters, attributes.
+
+### Floating point values
+
+#### IEEE floating point values (FP16, FP32, FP64)
+For IEEE floating point value, we will simply indicate that the IEEE 754 rules applies, e.g., x/y returns "NaN" when y = 0. Note that this makes sense since Python follows IEEE.
+This means that, e.g., a division by zero will lead to a NaN that will propagate in the rest of the computation.
+This value can be checked at the output level.
+
+Note that NaN can be detected in the hidden layers using the [IsNaN](https://onnx.ai/onnx/operators/onnx__IsNaN.html) ONNX operator. And, of course, they can be detected at the output.
+
+So, in the standard, we may simply recall that the IEEE 754 applies and specify what the operator is expected to generate in case when a parameter is out of range.
+
+#### Non IEEE floating point values (e.g., BFLOAT16, FP8, etc.)
+
+For BFloat16, the IEEE754 rules should apply for INF and NaNs. So we fall back to case
+
+As of today, ONNX does not support FP8...
+
+
+#### Integer values
+
+For integers values, two cases :
+- division by zero conditions, which lead to a trap and a "core dump" (in the best case)
+- overflow conditions, which are not detected
+
+(I propose to postpone the case of integer values for a while.)
diff --git "a/safety-related-profile/meetings/formal_methods/Meeting_Salom\303\251_Mariem.md" "b/safety-related-profile/meetings/formal_methods/Meeting_Salom\303\251_Mariem.md"
new file mode 100644
index 00000000..6f0c428f
--- /dev/null
+++ "b/safety-related-profile/meetings/formal_methods/Meeting_Salom\303\251_Mariem.md"
@@ -0,0 +1,100 @@
+# Compte rendu de réunion — Avancement sur le type `scalar` et génération du code C
+
+**Date :** 24/07/2025
+**Participants :** Salomé, Mariem
+**Sujet :** Avancement sur le type `scalar` et génération du code C
+
+---
+
+## 1. Points discutés
+
+Salomé a résumé ce qu'elle a fait pendant les deux dernières semaines. Elle a :
+
+1. Vérifié que l’opération d’arrondi utilisée dans ONNXRuntime est la même que celle utilisée dans Why3, à savoir **RNE**.
+2. Cloné le type `scalar` générique pour définir les types `ScalarInt32` et `ScalarFloat32`, en utilisant les bibliothèques `mach.int.Int32` et `ieee_float.Float32`.
+3. Implémenté l’opérateur générique `add` prenant un type `t` en paramètre.
+4. Cloné l’opérateur générique `add` pour définir les modules `OpAdd_Float32` et `OpAdd_Int32`.
+5. Généré le code OCaml.
+6. Créé les fichiers de tests et testé le comportement de l’opérateur `add` avec `int32` et `float32`, dans les cas d’addition avec et sans débordement.
+7. Réussi à générer un code C à partir de l’implémentation naïve du type `scalar`, mais la génération est incomplète : seule la structure de données `scalar` est traduite en C. Des problèmes sont rencontrés lors de la traduction de l’opérateur `add`.
+
+---
+
+## 2. Problématiques identifiées
+
+- Le test de l’opérateur `add` avec des `float32` en cas de débordement ne donne pas le même résultat que ONNX, car le type `Float32` n’existe pas dans OCaml (seul le type `float64` y est disponible).
+ **Solutions possibles :**
+ - Définir `float32` en OCaml.
+ - Générer du code C pour les tests.
+
+- Certaines expressions en WhyML ne peuvent pas se traduire en C, comme : `match ... with`.
+- La structure utilisée pour définir `scalar` est un peu complexe ; elle pourrait être simplifiée afin de faciliter la génération du code C.
+ Une autre solution possible : ne générer en C que les types problématiques en OCaml (notamment `float32`), pour éviter de devoir traduire le type générique complet.
+
+---
+
+## 3. Actions à venir
+
+- Enrichir le type `scalar` avec des opérations logiques et des preuves, car l’implémentation actuelle ne contient que des opérations fonctionnelles.
+- Optimiser la structure du type `scalar` générique traduite en C (à discuter avec l’équipe et Loïc).
+- Tenter de générer du code C à partir de l’implémentation **non naïve** du type `scalar`.
+
+
+---
+
+
+# Compte rendu de réunion — Avancement sur le type `scalar`
+
+**Date :** 03/07/2025
+**Participants :** Salomé, Mariem
+**Sujet :** Avancement sur le type `scalar`
+
+---
+
+## 1. Points discutés
+
+### Test de l'opérateur `add` (int32 et float32)
+
+- Salomé a implémenté un test de l'opérateur `add` en utilisant la librairie `ieee_int32` de Why3.
+- La création du **driver `type.drv`** a été complexe et a demandé un temps significatif.
+- À l’avenir, **solliciter Loïc**, qui dispose de **drivers déjà implémentés** pouvant être réutilisés.
+
+### Modifications des fichiers OCaml
+
+- Les fichiers `tensor.ml` et `tensor.mli` ont été modifiés pour :
+ - appeler la fonction à tester (`add`),
+ - afficher le résultat pour validation.
+
+### Création du fichier de test OCaml
+
+- Les fichiers `test_add.ml` et `test_add.expected` ont été générés pour :
+ - réaliser des cas simples de test des types `int32` ou `float32`,
+ - en exploitant les fonctions des fichiers `tensor.ml` et `tensor.mli`.
+
+---
+
+## 2. Problématiques identifiées
+
+### Limitation des types numériques en OCaml
+
+- OCaml ne propose que les types `int`, lié à l'architecture, et `int32`.
+- Il n'existe pas de prise en charge native pour des types comme `int8` ou `float32`, nécessaires à nos tests.
+
+### Comportement de l’arrondi dans Why3 vs ONNX
+
+- Why3 utilise un **arrondi RNE (Round to Nearest Even)** pour les opérations `float32`.
+- Il est nécessaire de **vérifier si cet arrondi correspond** à celui défini dans ONNX.
+- Il faudra **étudier le code source de l’opérateur `add` dans ONNX** pour :
+ - identifier les **opérations à redéfinir** dans `scalar`,
+ - et celles **importables directement** depuis Why3.
+
+---
+
+## 3. Actions à venir
+
+- Vérifier le comportement d’arrondi dans ONNX (comparé à RNE de Why3).
+- Déterminer quelles opérations seront :
+ - redéfinies dans le type `scalar`,
+ - ou importées depuis les bibliothèques Why3.
+- Formaliser les éléments **spécifiés informellement par Franck** dans la **spécification du type `scalar`**.
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/README.md b/safety-related-profile/meetings/formal_methods/code/2025-03-19/README.md
new file mode 100644
index 00000000..e79ae50c
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/README.md
@@ -0,0 +1 @@
+Code written during the 2025/03/19 workshop
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.gitignore b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.gitignore
new file mode 100644
index 00000000..757f50e2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.gitignore
@@ -0,0 +1,6 @@
+why3session.xml*
+why3shapes.gz*
+/.why3find
+/_build
+/html
+/lib/*.bak
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/00/009a248114cf07ccc6af2c142b6a16a6 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/00/009a248114cf07ccc6af2c142b6a16a6
new file mode 100644
index 00000000..605e95ef
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/00/009a248114cf07ccc6af2c142b6a16a6
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.016775 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/07/07aa782e56cc00fb1b5ae89a393206b7 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/07/07aa782e56cc00fb1b5ae89a393206b7
new file mode 100644
index 00000000..6560d664
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/07/07aa782e56cc00fb1b5ae89a393206b7
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.026858 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/0f/0fd4a9535c71b47657ce94fd1ab0d79e b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/0f/0fd4a9535c71b47657ce94fd1ab0d79e
new file mode 100644
index 00000000..7c3fc348
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/0f/0fd4a9535c71b47657ce94fd1ab0d79e
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.014509 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/19/190cd40d582da3801bd6b6f13f2560a1 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/19/190cd40d582da3801bd6b6f13f2560a1
new file mode 100644
index 00000000..2c700d87
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/19/190cd40d582da3801bd6b6f13f2560a1
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.013517 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/21/210d6dc9289578c32fe22d55b6045e3e b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/21/210d6dc9289578c32fe22d55b6045e3e
new file mode 100644
index 00000000..3d2a93c7
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/21/210d6dc9289578c32fe22d55b6045e3e
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.038737 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/24/24807122cba2c06caa5e7c05e4b9013f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/24/24807122cba2c06caa5e7c05e4b9013f
new file mode 100644
index 00000000..24f7d845
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/24/24807122cba2c06caa5e7c05e4b9013f
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.152687 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/27/27dd260b3c70c3aa37d7ed68ff32d5c8 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/27/27dd260b3c70c3aa37d7ed68ff32d5c8
new file mode 100644
index 00000000..2ba0da9e
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/27/27dd260b3c70c3aa37d7ed68ff32d5c8
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.01358 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/2d/2d8711f0cd64e71e6b70554b36cc5703 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/2d/2d8711f0cd64e71e6b70554b36cc5703
new file mode 100644
index 00000000..a64e4e28
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/2d/2d8711f0cd64e71e6b70554b36cc5703
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.010714 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/45/455b1f6c501467a80fa06ab091edf36c b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/45/455b1f6c501467a80fa06ab091edf36c
new file mode 100644
index 00000000..cd5797b8
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/45/455b1f6c501467a80fa06ab091edf36c
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.00949 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/49/49fa11f8873f2a6684f5a3bc078b7d6f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/49/49fa11f8873f2a6684f5a3bc078b7d6f
new file mode 100644
index 00000000..3d69ff75
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/49/49fa11f8873f2a6684f5a3bc078b7d6f
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.041714 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5b/5ba3121e7f510036ba2d321112c60901 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5b/5ba3121e7f510036ba2d321112c60901
new file mode 100644
index 00000000..19043317
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5b/5ba3121e7f510036ba2d321112c60901
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.010766 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5e/5e8aa769c1c11cd76dacec695ceb6102 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5e/5e8aa769c1c11cd76dacec695ceb6102
new file mode 100644
index 00000000..43fc48fd
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5e/5e8aa769c1c11cd76dacec695ceb6102
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.01245 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/64/64f56ead9607d441b3e6499517bba546 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/64/64f56ead9607d441b3e6499517bba546
new file mode 100644
index 00000000..f1ee69d5
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/64/64f56ead9607d441b3e6499517bba546
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.013068 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/66/6638a2a2f711f34178b2f8ef7c51bd49 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/66/6638a2a2f711f34178b2f8ef7c51bd49
new file mode 100644
index 00000000..90d4a4c8
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/66/6638a2a2f711f34178b2f8ef7c51bd49
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.010764 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6c/6cdac225b4d603050a5a6ffcb2ad523b b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6c/6cdac225b4d603050a5a6ffcb2ad523b
new file mode 100644
index 00000000..b3a8831d
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6c/6cdac225b4d603050a5a6ffcb2ad523b
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.010923 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4164cde7dc517b55021f31e652a373 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4164cde7dc517b55021f31e652a373
new file mode 100644
index 00000000..66a14459
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4164cde7dc517b55021f31e652a373
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.010678 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4f2eb4fe96173ca9fdcc22c836cf77 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4f2eb4fe96173ca9fdcc22c836cf77
new file mode 100644
index 00000000..8fd2cd18
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4f2eb4fe96173ca9fdcc22c836cf77
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.017609 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6fb457ec7cb096bc6b8b7442d6eb7bf7 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6fb457ec7cb096bc6b8b7442d6eb7bf7
new file mode 100644
index 00000000..b2bc8bd8
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6fb457ec7cb096bc6b8b7442d6eb7bf7
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.01784 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/79/790bf8c280b62b6da195591b8608e2c3 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/79/790bf8c280b62b6da195591b8608e2c3
new file mode 100644
index 00000000..7074a206
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/79/790bf8c280b62b6da195591b8608e2c3
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.015092 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7e/7e7fba10efe7cf51149ed3643b32f357 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7e/7e7fba10efe7cf51149ed3643b32f357
new file mode 100644
index 00000000..5e53c772
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7e/7e7fba10efe7cf51149ed3643b32f357
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.015117 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7f/7f8aa799ff46d92a4285df18b43026ad b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7f/7f8aa799ff46d92a4285df18b43026ad
new file mode 100644
index 00000000..cb32cf16
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7f/7f8aa799ff46d92a4285df18b43026ad
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.006981 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/81/81e36cd7c1af621157d27c8704db1f8a b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/81/81e36cd7c1af621157d27c8704db1f8a
new file mode 100644
index 00000000..084db191
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/81/81e36cd7c1af621157d27c8704db1f8a
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.011357 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/82/8299d64c6cb862109461796e7b07a77c b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/82/8299d64c6cb862109461796e7b07a77c
new file mode 100644
index 00000000..1719c2db
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/82/8299d64c6cb862109461796e7b07a77c
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.010261 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/86/8605df14510eb6c308c45fc361f17045 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/86/8605df14510eb6c308c45fc361f17045
new file mode 100644
index 00000000..045cfb65
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/86/8605df14510eb6c308c45fc361f17045
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.036923 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8a/8a85d0bd2ca0c894340c27399caea9e1 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8a/8a85d0bd2ca0c894340c27399caea9e1
new file mode 100644
index 00000000..d648462a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8a/8a85d0bd2ca0c894340c27399caea9e1
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.006913 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8e7e6b7a37f4870cca825d4640e642a9 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8e7e6b7a37f4870cca825d4640e642a9
new file mode 100644
index 00000000..13bd97d8
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8e7e6b7a37f4870cca825d4640e642a9
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.01024 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8ee6dc52a7f99925d560c9a2793409ae b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8ee6dc52a7f99925d560c9a2793409ae
new file mode 100644
index 00000000..6fdd0350
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8ee6dc52a7f99925d560c9a2793409ae
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.017217 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8f/8f6b84827b14b20ad4f55795ecc33da5 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8f/8f6b84827b14b20ad4f55795ecc33da5
new file mode 100644
index 00000000..11c71693
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8f/8f6b84827b14b20ad4f55795ecc33da5
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.013809 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/91/91e3acce87e899d31d72bfc4e344894e b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/91/91e3acce87e899d31d72bfc4e344894e
new file mode 100644
index 00000000..2011973e
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/91/91e3acce87e899d31d72bfc4e344894e
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.009571 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/92/9285cd54491d9208c9b05d2ddc547081 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/92/9285cd54491d9208c9b05d2ddc547081
new file mode 100644
index 00000000..ec03ed81
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/92/9285cd54491d9208c9b05d2ddc547081
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.018187 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/96/968a81aa5f33a7f54c9cefae9b292813 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/96/968a81aa5f33a7f54c9cefae9b292813
new file mode 100644
index 00000000..eb8664f6
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/96/968a81aa5f33a7f54c9cefae9b292813
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.018046 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9a/9a773cdf73af5690d8a8420677f0f740 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9a/9a773cdf73af5690d8a8420677f0f740
new file mode 100644
index 00000000..e6969806
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9a/9a773cdf73af5690d8a8420677f0f740
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.009723000000000001 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9e/9e8ce8946666fb9536598d9c2ae93024 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9e/9e8ce8946666fb9536598d9c2ae93024
new file mode 100644
index 00000000..cdd15741
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9e/9e8ce8946666fb9536598d9c2ae93024
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.011765 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a3/a3f21f0400e6d55abac5c856b28e9ffb b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a3/a3f21f0400e6d55abac5c856b28e9ffb
new file mode 100644
index 00000000..79182f8f
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a3/a3f21f0400e6d55abac5c856b28e9ffb
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.007213 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a9/a99ce9aa870ce03416372c1a304e07d2 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a9/a99ce9aa870ce03416372c1a304e07d2
new file mode 100644
index 00000000..314ee002
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a9/a99ce9aa870ce03416372c1a304e07d2
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.073545 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ab/ab307d7398ae30cec85828c09471e2d8 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ab/ab307d7398ae30cec85828c09471e2d8
new file mode 100644
index 00000000..82a930b0
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ab/ab307d7398ae30cec85828c09471e2d8
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.023023 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b3/b386b7fe2163827e25b780290f28a56a b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b3/b386b7fe2163827e25b780290f28a56a
new file mode 100644
index 00000000..136d7383
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b3/b386b7fe2163827e25b780290f28a56a
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.008283 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b8/b8a150eafcc9e28b770d58a235778aff b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b8/b8a150eafcc9e28b770d58a235778aff
new file mode 100644
index 00000000..59b74aa3
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b8/b8a150eafcc9e28b770d58a235778aff
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.011451 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bde5773e0993e0fc8cb2ae6f7275c32b b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bde5773e0993e0fc8cb2ae6f7275c32b
new file mode 100644
index 00000000..1d6d7f3d
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bde5773e0993e0fc8cb2ae6f7275c32b
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.007758 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bdf0e9ec6a0784ede3eeca45d653790f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bdf0e9ec6a0784ede3eeca45d653790f
new file mode 100644
index 00000000..663f9c65
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bdf0e9ec6a0784ede3eeca45d653790f
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.009259 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c4/c43334abf3abc246234be33ac096ce78 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c4/c43334abf3abc246234be33ac096ce78
new file mode 100644
index 00000000..e154e34a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c4/c43334abf3abc246234be33ac096ce78
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.016119 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c618acb790425ac1d4cc72fcd127ace0 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c618acb790425ac1d4cc72fcd127ace0
new file mode 100644
index 00000000..6857f2f4
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c618acb790425ac1d4cc72fcd127ace0
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.027442 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c6bd18162952013d3dfb676bc162459f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c6bd18162952013d3dfb676bc162459f
new file mode 100644
index 00000000..02aef59a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c6bd18162952013d3dfb676bc162459f
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.009154000000000001 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c9/c98d13b5b18abcc704601510cf32bdf1 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c9/c98d13b5b18abcc704601510cf32bdf1
new file mode 100644
index 00000000..ade3fdd3
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c9/c98d13b5b18abcc704601510cf32bdf1
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.016912 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd3bc78fdd932f0acd847e8adebe01f7 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd3bc78fdd932f0acd847e8adebe01f7
new file mode 100644
index 00000000..77dab5b2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd3bc78fdd932f0acd847e8adebe01f7
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.009802999999999999 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd4bba0dc0badbfc7ea872449401cd88 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd4bba0dc0badbfc7ea872449401cd88
new file mode 100644
index 00000000..51f0a7f0
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd4bba0dc0badbfc7ea872449401cd88
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.008982 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d1/d188627c778696fdf3bbe9bd44418546 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d1/d188627c778696fdf3bbe9bd44418546
new file mode 100644
index 00000000..3dd6ee70
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d1/d188627c778696fdf3bbe9bd44418546
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.378981 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d7/d79b99d51417a0d365cca1f90f5d5d26 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d7/d79b99d51417a0d365cca1f90f5d5d26
new file mode 100644
index 00000000..b9299137
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d7/d79b99d51417a0d365cca1f90f5d5d26
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.008257 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/df/dfd9b2245d87cbed501ff2b9d8e2dde9 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/df/dfd9b2245d87cbed501ff2b9d8e2dde9
new file mode 100644
index 00000000..86ef7e57
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/df/dfd9b2245d87cbed501ff2b9d8e2dde9
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.020709 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e3/e374265f63b80745a4379319733b7374 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e3/e374265f63b80745a4379319733b7374
new file mode 100644
index 00000000..b1f4520a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e3/e374265f63b80745a4379319733b7374
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.008892000000000001 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e497228f4c2738e74aab252e3c78411b b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e497228f4c2738e74aab252e3c78411b
new file mode 100644
index 00000000..6715abb2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e497228f4c2738e74aab252e3c78411b
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.028072 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e4edd764a4b69ff6b22a8d14be0726d8 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e4edd764a4b69ff6b22a8d14be0726d8
new file mode 100644
index 00000000..8b8775b2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e4edd764a4b69ff6b22a8d14be0726d8
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.007492 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ec/ecf9237df56d890381fb7505f5cb0e0d b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ec/ecf9237df56d890381fb7505f5cb0e0d
new file mode 100644
index 00000000..32b88358
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ec/ecf9237df56d890381fb7505f5cb0e0d
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.013478 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ee/eee171d3f90d6c7bcff96dc68a118ff4 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ee/eee171d3f90d6c7bcff96dc68a118ff4
new file mode 100644
index 00000000..636e0145
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ee/eee171d3f90d6c7bcff96dc68a118ff4
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.04214 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ff/ffc74e57bec68191ce00177e832d1b23 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ff/ffc74e57bec68191ce00177e832d1b23
new file mode 100644
index 00000000..31640be9
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ff/ffc74e57bec68191ce00177e832d1b23
@@ -0,0 +1 @@
+{ "status": "Valid", "time": 0.050584 }
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/version b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/version
new file mode 100644
index 00000000..a81004d9
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/version
@@ -0,0 +1 @@
+v4@why3.1.8.0
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/Makefile b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/Makefile
new file mode 100644
index 00000000..2efa3fa3
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/Makefile
@@ -0,0 +1,26 @@
+.PHONY: all prove doc lib test clean
+
+all: prove doc lib
+
+prove:
+ why3find prove -l -x
+
+doc:
+ why3find doc -t ONNX *.mlw
+
+lib:
+ @mkdir -p lib/extract
+ @rm -fr lib/extract/*
+ why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv sequence.Seq
+ why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv tensor.Shape
+ why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv tensor.Index
+ why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv tensor.Tensor
+ why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv opwhere.Where
+ why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv opmatrix.Matrix
+ dune build
+
+test:
+ @dune runtest
+
+clean:
+ rm -fr lib/*.ml html
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/README.md b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/README.md
new file mode 100644
index 00000000..4d05db7a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/README.md
@@ -0,0 +1,59 @@
+# ONNX Formal Methods
+
+To install the tool-chain:
+
+ $ apt-get opam
+ $ opam install why3find alt-ergo
+
+To install the VSCode extension:
+
+ $ wget https://git.frama-c.com/pub/why3find/-/jobs/1177337/artifacts/raw/vscode/why3-platform-1.1.1.vsix
+ $ code --install-extension why3-platform-1.1.1.vsix
+
+Documentation:
+
+- [Why3](https://www.why3.org)
+- [Why3 Manual](https://www.why3.org/doc/)
+- [Why3 Standard Library](https://www.why3.org/stdlib/)
+- [Why3find](https://git.frama-c.com/pub/why3find)
+
+# Proving
+
+ $ make prove
+ why3find prove -l -x
+ Theory opwhere.FlatWhere: ✔ (-)
+ Theory opwhere.Where: ✔ (2)
+ Theory utils.Product: ✔ (2)
+ Theory tensor.FlatTensor: ✔ (14)
+ Theory tensor.Tensor: ✔ (1)
+ Theory sequence.Seq: ✔ (24)
+ Theory sequence.Codomain: ✔ (6)
+ Cache 53/53
+ Proofs ✔ (49) (unchanged)
+ Provers 691ms, depth: 3
+ - alt-ergo@2.5.4 ( 49) ( 12ms - 44ms - 691ms)
+ - split_vc ( 8)
+
+# Documentation
+
+ $ make doc
+ why3find doc *.mlw
+ Generated /Users/correnson/onnx/html/index.html
+
+# Extraction & Test
+
+ $ make lib # extract and compile the lib
+ $ make test # compile and execute the tests
+
+The OCaml extracted code from Why3 is in the `lib` directory, which also contains
+hand-written files:
+
+ - `lib/dune` OCaml compilation directives
+ - `lib/tensor.mli` Public API of the Tensor library
+ - `lib/tensor.ml` Bindings of the public API to the extracted code
+ - `lib/extract/*.ml` Extracted code from Why3 specifications
+
+The unit tests are written in OCaml in `test` directory:
+
+ - `test/dune` OCaml compilation directives
+ - `test/test.ml` OCaml unit tests using the Tensor library
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/dune-project b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/dune-project
new file mode 100644
index 00000000..e94f5dfb
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/dune-project
@@ -0,0 +1,12 @@
+(lang dune 3.1)
+(generate_opam_files true)
+(using dune_site 0.1)
+(name onnx)
+(authors "Author Name(s)")
+(maintainers "Maintainer Name(s)")
+(license LICENSE)
+(package
+ (name onnx)
+ (synopsis "short synopsis")
+ (description "longer description")
+ (depends why3find))
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/dune b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/dune
new file mode 100644
index 00000000..1a535bd0
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/dune
@@ -0,0 +1,5 @@
+(library
+ (name tensorlib)
+ (package onnx))
+
+(include_subdirs unqualified)
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opmatrix__Matrix.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opmatrix__Matrix.ml
new file mode 100644
index 00000000..2cf1cb77
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opmatrix__Matrix.ml
@@ -0,0 +1,23 @@
+let swap (idx: (int) list) : (int) list =
+ match idx with
+ | i :: (j :: ([])) -> j :: i :: []
+ | _ -> assert false (* absurd *)
+
+let get : type a. (a Tensor__Tensor.tensor) -> (int) -> (int) -> a =
+ fun t i j -> t.Tensor__Tensor.value (i :: j :: [] )
+
+let cols : type a. (a Tensor__Tensor.tensor) -> (int) =
+ fun a -> Sequence__Seq.mixfix_lbrb a.Tensor__Tensor.shape 0
+
+let rows : type a. (a Tensor__Tensor.tensor) -> (int) =
+ fun a -> Sequence__Seq.mixfix_lbrb a.Tensor__Tensor.shape 1
+
+let transpose :
+ type a. (a Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) =
+ fun a -> let value (idx: (int) list) : a =
+ if Sequence__Seq.length idx = 2
+ then a.Tensor__Tensor.value (swap idx)
+ else a.Tensor__Tensor.value idx in
+ { Tensor__Tensor.shape = swap a.Tensor__Tensor.shape;
+ Tensor__Tensor.value = value }
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opwhere__Where.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opwhere__Where.ml
new file mode 100644
index 00000000..010a9ad0
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opwhere__Where.ml
@@ -0,0 +1,11 @@
+let where :
+ type a. ((bool) Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) ->
+ (a Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) =
+ fun cond a b -> { Tensor__Tensor.shape =
+ ((ignore ((ignore (b.Tensor__Tensor.shape) ; (a.Tensor__Tensor.shape))) ;
+ (cond.Tensor__Tensor.shape))); Tensor__Tensor.value =
+ (fun (i: (int) list) ->
+ if ((cond.Tensor__Tensor.value) i)
+ then ((a.Tensor__Tensor.value) i)
+ else ((b.Tensor__Tensor.value) i)) }
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/sequence__Seq.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/sequence__Seq.ml
new file mode 100644
index 00000000..445d9e76
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/sequence__Seq.ml
@@ -0,0 +1,77 @@
+type 'a seq = 'a list
+
+let length : type a. (a list) -> (int) = fun u -> ((List.length u))
+
+let rec mixfix_lbrb : type a. (a list) -> (int) -> a =
+ fun u k -> match u with
+ | x :: w -> if k = 0 then x else mixfix_lbrb w (k - 1)
+ | _ -> assert false (* absurd *)
+
+let rec equal :
+ type a. (a -> (a -> (bool))) -> (a list) -> (a list) -> (bool) =
+ fun eq u v -> match (u, v) with
+ | (x :: u', y :: v') -> eq x y && equal eq u' v'
+ | ([], []) -> true
+ | _ -> false
+
+let empty : type a. (a list) = []
+
+let elt : type a. a -> (a list) = fun e -> e :: []
+
+let hd : type a. (a list) -> a =
+ fun s -> match s with
+ | x :: _ -> x
+ | _ -> assert false (* absurd *)
+
+let tl : type a. (a list) -> a = fun s -> mixfix_lbrb s (length s - 1)
+
+let rec create : type a. a -> (int) -> (a list) =
+ fun e n -> if n > 0 then e :: create e (n - 1) else []
+
+let reset : type a b. a -> (b list) -> (a list) =
+ fun e s -> create e (length s)
+
+let rec init : type a. ((int) -> a) -> (int) -> (int) -> (a list) =
+ fun f a b -> if a < b then f a :: init f (a + 1) b else []
+
+let rec map : type a b. (a -> b) -> (a list) -> (b list) =
+ fun f s -> match s with
+ | [] -> []
+ | x :: xs -> f x :: map f xs
+
+let rec infix_plpl : type a. (a list) -> (a list) -> (a list) =
+ fun u v -> match u with
+ | [] -> v
+ | x :: w -> x :: infix_plpl w v
+
+let rec mixfix_lbdtdtrb : type a. (a list) -> (int) -> (int) -> (a list) =
+ fun u i j -> match u with
+ | [] -> []
+ | x :: w ->
+ if 0 < i
+ then mixfix_lbdtdtrb w (i - 1) (j - 1)
+ else begin if 0 < j then x :: mixfix_lbdtdtrb w 0 (j - 1) else [] end
+
+let mixfix_lbdtdt_rb : type a. (a list) -> (int) -> (a list) =
+ fun u i -> mixfix_lbdtdtrb u 0 i
+
+let mixfix_lb_dtdtrb : type a. (a list) -> (int) -> (a list) =
+ fun u i -> mixfix_lbdtdtrb u i (length u)
+
+let infix_pldt : type a. (a list) -> a -> (a list) =
+ fun s e -> infix_plpl s (elt e)
+
+let infix_dtpl : type a. a -> (a list) -> (a list) =
+ fun e s -> infix_plpl (elt e) s
+
+let rec mixfix_lblsmnrb : type a. (a list) -> (int) -> a -> (a list) =
+ fun u i x -> match u with
+ | x0 :: w -> if i = 0 then x :: w else x0 :: mixfix_lblsmnrb w (i - 1) x
+ | _ -> assert false (* absurd *)
+
+let memcpy :
+ type a. (a list) -> (int) -> (a list) -> (int) -> (int) -> (a list) =
+ fun u i v j n -> infix_plpl (infix_plpl (mixfix_lbdtdt_rb u i)
+ (mixfix_lbdtdtrb v j (j + n)))
+ (mixfix_lb_dtdtrb u (i + n))
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Index.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Index.ml
new file mode 100644
index 00000000..067334be
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Index.ml
@@ -0,0 +1,10 @@
+type index = (int) list
+
+let valid (idx: (int) list) (s: Tensor__Shape.shape) : bool =
+ let rec inrange (ks: (int) list) (ds: (int) list) : bool =
+ match (ks, ds) with
+ | ([], []) -> true
+ | (k :: ksr, d :: dsr) -> (0 <= k && k < d) && inrange ksr dsr
+ | _ -> false in
+ inrange idx s
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Shape.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Shape.ml
new file mode 100644
index 00000000..70202444
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Shape.ml
@@ -0,0 +1,9 @@
+type shape = (int) list
+
+let rec product (ds: (int) list) : int =
+ match ds with
+ | [] -> 1
+ | d :: ds1 -> d * product ds1
+
+let sizeof (s: shape) : int = product s
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Tensor.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Tensor.ml
new file mode 100644
index 00000000..7cbe5e8d
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Tensor.ml
@@ -0,0 +1,18 @@
+type 'a tensor = {
+ shape: Tensor__Shape.shape;
+ value: (int) list -> 'a;
+ }
+
+let dim : type a. (a tensor) -> (int) =
+ fun t -> Sequence__Seq.length t.shape
+
+exception Invalid_index
+
+let mem : type a. ((int) list) -> (a tensor) -> (bool) =
+ fun idx t -> Tensor__Index.valid idx t.shape
+
+let get : type a. ((int) list) -> (a tensor) -> a =
+ fun idx t -> if Tensor__Index.valid idx t.shape
+ then t.value idx
+ else raise Invalid_index
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.ml
new file mode 100644
index 00000000..90db725c
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.ml
@@ -0,0 +1,75 @@
+(* ONNX Tensor API *)
+
+module S = Tensor__Shape
+module I = Tensor__Index
+module T = Tensor__Tensor
+
+type 'a tensor = 'a T.tensor
+let dim = T.dim
+let shape t = t.T.shape
+
+let scalar v =
+ T.{
+ shape = [] ;
+ value = fun _ -> v ;
+ }
+
+exception Invalid_index = T.Invalid_index
+let mem = T.mem
+let get = T.get
+
+let (.%[]) t k = get [k] t
+let (.%[;..]) t ks = get (Array.to_list ks) t
+
+let vector vs =
+ let d = Array.of_list vs in
+ let n = Array.length d in
+ if n = 0 then invalid_arg "Tensor.vector" ;
+ T.{
+ shape = [ n ] ;
+ value = (function [ k ] -> d.(k) | _ -> raise Invalid_index) ;
+ }
+
+let matrix vs =
+ let d = Array.map Array.of_list @@ Array.of_list vs in
+ let n = Array.length d in
+ if n = 0 then invalid_arg "Tensor.matrix" ;
+ let m = Array.length d.(0) in
+ if m = 0 then invalid_arg "Tensor.matrix" ;
+ if Array.exists (fun r -> Array.length r <> m) d then
+ invalid_arg "Tensor.matrix" ;
+ T.{
+ shape = [ n ; m ] ;
+ value = (function [ i ; j ] -> d.(i).(j) | _ -> raise Invalid_index) ;
+ }
+
+let pretty pp fmt (t : 'a tensor) =
+ match t.shape with
+ | [] -> pp fmt @@ t.value []
+ | [n] ->
+ begin
+ Format.fprintf fmt "[" ;
+ for i = 0 to n-1 do
+ Format.fprintf fmt " %a" pp @@ t.value [i]
+ done ;
+ Format.fprintf fmt " ]" ;
+ end
+ | [n;m] ->
+ begin
+ Format.fprintf fmt "@[" ;
+ for i = 0 to n-1 do
+ Format.fprintf fmt "[" ;
+ for j = 0 to m-1 do
+ Format.fprintf fmt " %a" pp @@ t.value [i;j]
+ done ;
+ Format.fprintf fmt " ]@," ;
+ done ;
+ Format.fprintf fmt "@]" ;
+ end
+ | _ -> invalid_arg "Tensor.pretty"
+
+let transpose (t : 'a tensor) =
+ if dim t <> 2 then invalid_arg "Tensor.transpose" ;
+ Opmatrix__Matrix.transpose t
+
+let where = Opwhere__Where.where
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.mli b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.mli
new file mode 100644
index 00000000..6c924c1c
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.mli
@@ -0,0 +1,22 @@
+(** ONNX Tensor OCaml Interface *)
+
+type 'a tensor
+
+val dim : 'a tensor -> int
+val shape : 'a tensor -> int list
+
+val scalar : 'a -> 'a tensor
+val vector : 'a list -> 'a tensor
+val matrix : 'a list list -> 'a tensor
+
+exception Invalid_index
+
+val mem : int list -> 'a tensor -> bool
+val get : int list -> 'a tensor -> 'a
+val (.%[]) : 'a tensor -> int -> 'a
+val (.%[;..]) : 'a tensor -> int array -> 'a
+
+val pretty : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a tensor -> unit
+
+val transpose : 'a tensor -> 'a tensor
+val where : bool tensor -> 'a tensor -> 'a tensor -> 'a tensor
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/onnx.opam b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/onnx.opam
new file mode 100644
index 00000000..1dba3db6
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/onnx.opam
@@ -0,0 +1,28 @@
+# This file is generated by dune, edit dune-project instead
+opam-version: "2.0"
+synopsis: "short synopsis"
+description: "longer description"
+maintainer: ["Maintainer Name(s)"]
+authors: ["Author Name(s)"]
+license: "LICENSE"
+depends: [
+ "dune" {>= "3.1"}
+ "why3find"
+ "odoc" {with-doc}
+]
+build: [
+ ["dune" "subst"] {dev}
+ [
+ "dune"
+ "build"
+ "-p"
+ name
+ "-j"
+ jobs
+ "--promote-install-files=false"
+ "@install"
+ "@runtest" {with-test}
+ "@doc" {with-doc}
+ ]
+ ["dune" "install" "-p" name "--create-install-files" name]
+]
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix.mlw
new file mode 100644
index 00000000..f8a0b31a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix.mlw
@@ -0,0 +1,56 @@
+(**
+ Specification of Matrix operations on tensors.
+ *)
+
+module Matrix
+
+ use int.Int
+ use list.List
+ use sequence.Seq
+ use tensor.Tensor
+ let function swap (idx : seq int) : seq int
+ (*proof*)
+ requires { length idx = 2 }
+ ensures { length result = 2 }
+ ensures { Seq.(result[0] = idx[1]) }
+ ensures { Seq.(result[1] = idx[0]) }
+ = match idx with
+ | Cons i (Cons j Nil) -> Cons j (Cons i Nil)
+ end
+ (*qed*)
+
+ let function get (t : tensor 'a) (i j : int)
+ (*proof*)
+ = t.value (Cons i (Cons j Nil))
+ (*qed*)
+
+ let function cols (a : tensor 'a) : int
+ requires { dim a = 2 }
+ ensures { result = Seq.( a.shape[0] ) }
+ = Seq.(a.shape.dims[0])
+ let function rows (a : tensor 'a) : int
+ requires { dim a = 2 }
+ ensures { result = Seq.( a.shape[1] ) }
+ = Seq.(a.shape.dims[1])
+ let transpose (a : tensor 'a) : tensor 'a
+ requires { dim a = 2 }
+ ensures { result.shape = swap a.shape }
+ ensures {
+ forall i j.
+ 0 <= i < cols result ->
+ 0 <= j < rows result ->
+ get result i j = get a j i
+ }
+ = let value (idx : index) : 'a =
+ if length idx = 2 then a.value (swap idx) else a.value idx
+ in
+ assert {
+ forall i j.
+ swap (Cons i (Cons j Nil)) == (Cons j (Cons i Nil))
+ } ;
+ {
+ shape = { dims = swap a.shape.dims } ;
+ value = value
+ }
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix/proof.json
new file mode 100644
index 00000000..dffdffca
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix/proof.json
@@ -0,0 +1,21 @@
+{
+ "profile": [ { "prover": "alt-ergo@2.5.4", "size": 17, "time": 0.607 } ],
+ "proofs": {
+ "Matrix": {
+ "cols": { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ "rows": { "prover": "alt-ergo@2.5.4", "time": 0.011 },
+ "swap": { "prover": "alt-ergo@2.5.4", "time": 0.118 },
+ "transpose": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.066 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.012 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.036 }
+ ]
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere.mlw
new file mode 100644
index 00000000..8c51725c
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere.mlw
@@ -0,0 +1,18 @@
+(**
+ Specification of Where operation on tensors.
+ *)
+
+module Where
+ use int.Int
+ use map.Map
+ use utils.Same
+ use tensor.Shape
+ use tensor.Tensor
+
+ let function where (cond : tensor bool) (a b : tensor 'a) : tensor 'a =
+ {
+ shape = same cond.shape (same a.shape b.shape) ;
+ value = fun i -> if cond.value[i] then a.value[i] else b.value[i] ;
+ }
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere/proof.json
new file mode 100644
index 00000000..192bfce4
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere/proof.json
@@ -0,0 +1,7 @@
+{
+ "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ],
+ "proofs": {
+ "FlatWhere": {},
+ "Where": { "where": { "prover": "alt-ergo@2.5.4", "time": 0.024 } }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/out/opwhere__Where.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/out/opwhere__Where.ml
new file mode 100644
index 00000000..d57c5bfd
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/out/opwhere__Where.ml
@@ -0,0 +1,10 @@
+let where :
+ type a. ((bool) Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) ->
+ (a Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) =
+ fun cond a b -> { Tensor__Tensor.shape = cond.Tensor__Tensor.shape;
+ Tensor__Tensor.value =
+ (fun (i: (Z.t) list) ->
+ if Map__Map.mixfix_lbrb cond.Tensor__Tensor.value i
+ then Map__Map.mixfix_lbrb a.Tensor__Tensor.value i
+ else Map__Map.mixfix_lbrb b.Tensor__Tensor.value i) }
+
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence.mlw
new file mode 100644
index 00000000..d7fc3bae
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence.mlw
@@ -0,0 +1,274 @@
+(*@ # Finite Sequences
+
+ This modules provides a persistent array interface to lists.
+ It is typically used to model byte arrays of COSMO implementation.
+*)
+
+module Seq
+
+ use int.Int
+ use int.MinMax
+ use option.Option
+ use list.ListRich as L
+
+ (*@ ## Definition
+
+ A sequence `s` is defined by its size `n = length s` and its elements
+ `s[i]` with `0 <= i < n`.
+
+ All other operations on sequences are defined
+ in terms of the length and elements of the returned sequences.
+ *)
+
+ type seq 'a = L.list 'a
+
+ (*@ Length of a sequence. *)
+ let function length (u : seq 'a) = L.length u
+ meta coercion function length
+
+ (*@ Elements of a sequence. *)
+ let rec function ([]) (u : seq 'a) (k : int) : 'a
+ requires { 0 <= k < u }
+ ensures { L.nth k u = Some result }
+ (*proof*)
+ variant { u }
+ = match u with L.Cons x w -> if k = 0 then x else w[k-1] end
+ (*qed*)
+
+ lemma head: forall x:'a, u. (L.Cons x u)[0] = x
+ lemma tail: forall x:'a, u k. 0 < k <= length u -> (L.Cons x u)[k] = u[k-1]
+
+ (*@ Definitionnal equality: `u == v` states that sequences `u` and `v` have
+ the same length and same elements. *)
+ predicate (==) (u v : seq 'a) =
+ length u = length v /\ forall k. 0 <= k < u -> u[k] = v[k]
+
+ (*@ Equal lists are definitionnaly equals. *)
+ lemma reflexivity : forall u : seq 'a. u == u
+
+ (*@ Definitionnaly equal lists _are_ equals. *)
+ let rec lemma extensivity (a b : seq 'a)
+ requires { a == b }
+ ensures { a = b }
+ (*proof*)
+ variant { a, b }
+ = match a, b with
+ | L.Cons x a' , L.Cons y b' ->
+ assert { x = y } ;
+ let lemma shift (k: int)
+ requires { 0 <= k < length a' }
+ ensures { a'[k] = b'[k] }
+ = assert { a'[k] = a[k+1] } ;
+ assert { b'[k] = b[k+1] }
+ in extensivity a' b'
+ | _ -> ()
+ end
+ (*qed*)
+
+ (*@ Functional equality. *)
+ let rec equal (eq : 'a -> 'a -> bool) (u v : seq 'a) : bool
+ requires { forall x y. L.mem x u -> L.mem y v -> (eq x y <-> x = y) }
+ ensures { result <-> u = v }
+ (*proof*)
+ variant { u, v }
+ = match u, v with
+ | L.Cons x u', L.Cons y v' -> eq x y && equal eq u' v'
+ | L.Nil, L.Nil -> true
+ | _ -> false
+ end
+ (*qed*)
+
+ (*@ ## Constructors
+
+ Helper functions for constructing sequences.
+ *)
+
+ (*@ Empty sequence of length `0`. *)
+ let constant empty : seq 'a = L.Nil
+
+ (*@ Singleton sequence with a unique element. *)
+ let function elt (e : 'a) : seq 'a = L.Cons e L.Nil
+
+ (*@ Head element *)
+ let function hd (s : seq 'a) : 'a
+ requires { 0 < s.length }
+ ensures { result = s[0] }
+ (*proof*) = match s with L.Cons x _ -> x end (*qed*)
+
+ (*@ Tail element *)
+ let function tl (s : seq 'a) : 'a
+ requires { 0 < s.length }
+ ensures { result = s[ s.length - 1 ] }
+ (*proof*) = s[ s.length - 1 ] (*qed*)
+
+ (*@ `create e n` returns a sequence of length `n` filled with `e` elements. *)
+ let rec function create (e : 'a) (n : int) : seq 'a
+ requires { 0 <= n }
+ ensures { length result = n }
+ ensures { forall k. 0 <= k < result -> result[k] = e }
+ (*proof*)
+ variant { n }
+ = if n > 0 then L.Cons e (create e (n-1)) else L.Nil
+ (*qed*)
+
+ (*@ `reset e s` returns a sequence of same length `s` filled with `e` elements. *)
+ let function reset (e : 'a) (s : seq 'b) : seq 'a = create e s.length
+
+ (*@ `init f a b` returns the sequence of elements `f i` for `a <= i < b`. *)
+ let rec function init (f : int -> 'a) (a b : int) : seq 'a
+ requires { a <= b }
+ ensures { length result = b-a }
+ ensures { forall k. 0 <= k < result -> result[k] = f (a+k) }
+ (*proof*)
+ variant { b-a }
+ = if a < b then L.Cons (f a) (init f (a+1) b) else L.Nil
+ (*qed*)
+
+ (*@ `map f s` applies `f` to all elements f `s` *)
+ let rec function map (f : 'a -> 'b) (s : seq 'a) : seq 'b
+ ensures { length result = length s }
+ ensures { forall k. 0 <= k < result -> result[k] = f s[k] }
+ (*proof*)
+ variant { s }
+ = match s with L.Nil -> L.Nil | L.Cons x xs -> L.Cons (f x) (map f xs) end
+ (*qed*)
+
+ (*@ ## Slices
+
+ Sequences are commonly operated by a slice of their elements.
+ By convention, a slice `i..j` of elements denotes the range of elements
+ from `i` (_included_) to `j` (_excluded_), hence the range of indices `k`
+ such that `i <= k < j`.
+
+ The slice of all elements of a sequence of size `n` is then `0..n`, and
+ we also denote slice `0..i` by `..i` and slice `i..n` by `i..`.
+ Slice operations are properly defined only for a valid range of indices.
+
+ The dual operation of slice is the concatenation, here denoted by `(++)`.
+ *)
+
+ (*@ `u ++ v` is the concatenation of `u` and `v`,
+ where `u` elements comes first, and `v` elements follows. *)
+ let rec function (++) (u v : seq 'a) : seq 'a
+ ensures { length result = length u + length v }
+ ensures { forall k. 0 <= k < u -> result[k] = u[k] }
+ ensures { forall k. u <= k < result -> result[k] = v[k - u] }
+ (*proof*)
+ variant { u }
+ = match u with L.Nil -> v | L.Cons x w -> L.Cons x (w ++ v) end
+ (*qed*)
+
+ (*@ `u[i..j]` is the slice of `u` elements
+ from index `i` (_included_) to index `j` (_excluded_). *)
+ let rec function ([..]) (u : seq 'a) (i j : int) : seq 'a
+ requires { 0 <= i <= j <= u }
+ ensures { length result = j-i }
+ ensures { forall k. 0 <= k < result -> result[k] = u[i+k] }
+ (*proof*)
+ variant { u }
+ = match u with
+ | L.Nil -> L.Nil
+ | L.Cons x w ->
+ if 0 < i then w[i-1..j-1] else
+ if 0 < j then L.Cons x w[0..j-1]
+ else L.Nil
+ end
+ (*qed*)
+
+ (*@ `u[..i]` is the slice of `u` elements up to index `i` (_excluded_). *)
+ let function ([.._]) (u : seq 'a) (i : int) : seq 'a
+ requires { 0 <= i <= u }
+ ensures { length result = i }
+ ensures { forall k. 0 <= k < result -> result[k] = u[k] }
+ = u[0..i]
+
+ (*@ `u[i..]` is the slice of `u` elements from index `i` (_included_). *)
+ let function ([_..]) (u : seq 'a) (i : int) : seq 'a
+ requires { 0 <= i <= u }
+ ensures { result = u - i }
+ ensures { forall k. 0 <= k < result -> result[k] = u[i+k] }
+ = u[i..length u]
+
+ (*@ This property illustrates the duality of slice & concat. *)
+ goal split: forall u : seq 'a, i.
+ 0 <= i <= u ->
+ u[..i] ++ u[i..] = u
+ (*proof*)
+ by u[..i] ++ u[i..] == u
+ (*qed*)
+
+ (*@ This property illustrates the duality of slice & concat. *)
+ goal fullsplit: forall u : seq 'a, i j.
+ 0 <= i <= j <= u ->
+ u[..i] ++ u[i..j] ++ u[j..] = u
+ (*proof*)
+ by u[..i] ++ u[i..j] ++ u[j..] == u
+ (*qed*)
+
+ (*@ Tail insertion *)
+ let function (+.) (s : seq 'a) (e : 'a) : seq 'a
+ ensures { length result = length s + 1 }
+ ensures { forall k. 0 <= k < s -> result[k] = s[k] }
+ ensures { result[ s ] = e }
+ (*proof*) = s ++ elt e (*qed*)
+
+ (*@ Head insertion *)
+ let function (.+) (e : 'a) (s : seq 'a) : seq 'a
+ ensures { length result = length s + 1 }
+ ensures { forall k. 0 <= k < s -> result[1+k] = s[k] }
+ ensures { result[ 0 ] = e }
+ (*proof*) = elt e ++ s (*qed*)
+
+ (*@ ## Updates & Copies *)
+
+ (*@ `u[i<-x]` returns a copy of the sequence `u` where the element `u[i]`
+ has been replaced by `x`. *)
+ let rec function ([<-]) (u : seq 'a) (i : int) (x : 'a) : seq 'a
+ requires { 0 <= i < u }
+ ensures { length result = length u }
+ ensures { forall k. 0 <= k < result -> result[k] = if k=i then x else u[k] }
+ (*proof*)
+ variant { u }
+ = match u with L.Cons x0 w ->
+ if i = 0 then L.Cons x w else L.Cons x0 w[ i-1 <- x ]
+ end
+ (*qed*)
+
+ (*@ `memcpy u i v j n` returns a copy of `u` where the slice `u[i..i+n]` has been
+ replaced with the slice `v[j..j+n]` (last excluded). *)
+ let function memcpy (u : seq 'a) (i : int) (v : seq 'a) (j : int) (n : int)
+ requires { 0 <= i <= i+n <= u }
+ requires { 0 <= j <= j+n <= v }
+ ensures { length result = length u }
+ ensures { forall k. 0 <= k < i -> result[k] = u[k] }
+ ensures { forall k. i <= k < i+n -> result[k] = v[j+k-i] }
+ ensures { forall k. i+n <= k < u -> result[k] = u[k] }
+ = u[..i] ++ v[j..j+n] ++ u[i+n..]
+
+ (*@ `havoc u v p n` asserts that sequences `u` and `v` have identical elements
+ but elements in range `p..p+n` (last excluded). *)
+ predicate havoc (u v: seq 'a) (p n : int) =
+ u.length = v.length /\
+ (forall k. 0 <= k < p -> u[k] = v[k]) /\
+ (forall k. p+n <= k < u -> u[k] = v[k])
+
+end
+
+(*@ ## Sequence Codomain *)
+
+module Codomain
+
+ use int.Int
+ use set.Set
+ use Seq
+
+ let rec ghost function codomain (s : seq 'a) : set 'a
+ ensures { forall v. Set.mem v result <-> exists i. 0 <= i < s /\ s[i] = v }
+ (*proof*)
+ variant { s.length }
+ = if s.length = 0 then Set.empty else
+ let k = s.length-1 in
+ Set.add s[k] (codomain s[0..k])
+ (*qed*)
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence/proof.json
new file mode 100644
index 00000000..26c7e1a1
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence/proof.json
@@ -0,0 +1,64 @@
+{
+ "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ],
+ "proofs": {
+ "Codomain": {
+ "codomain": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.086 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.691 }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "Seq": {
+ "create": { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ "equal": { "prover": "alt-ergo@2.5.4", "time": 0.086 },
+ "extensivity": { "prover": "alt-ergo@2.5.4", "time": 0.115 },
+ "fullsplit": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 }
+ ]
+ },
+ "hd": { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ "head": { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ "infix ++": { "prover": "alt-ergo@2.5.4", "time": 0.061 },
+ "infix +.": { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ "infix .+": { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ "init": { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ "map": { "prover": "alt-ergo@2.5.4", "time": 0.043 },
+ "memcpy": { "prover": "alt-ergo@2.5.4", "time": 0.078 },
+ "mixfix [..]": { "prover": "alt-ergo@2.5.4", "time": 0.068 },
+ "mixfix [.._]": { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ "mixfix [<-]": { "prover": "alt-ergo@2.5.4", "time": 0.052 },
+ "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ "mixfix [_..]": { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ "reflexivity": { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ "reset": { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ "split": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 }
+ ]
+ },
+ "tail": { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ "tl": { "prover": "alt-ergo@2.5.4", "time": 0.024 }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.drv b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.drv
new file mode 100644
index 00000000..36294f0f
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.drv
@@ -0,0 +1,37 @@
+(* ONNX driver extensions for OCaml extraction *)
+
+module int.Int
+ syntax type int "int"
+ syntax literal int "%1"
+ syntax val zero "0"
+ syntax val one "1"
+ syntax val ( + ) "%1 + %2" prec 8 8 7
+ syntax val ( - ) "%1 - %2" prec 8 8 7
+ syntax val (-_) "- %1" prec 5 4
+ syntax val ( * ) "%1 * %2" prec 7 7 6
+ syntax val (=) "%1 = %2" prec 11 11 10
+ syntax val (<=) "%1 <= %2" prec 11 11 10
+ syntax val (<) "%1 < %2" prec 11 11 10
+ syntax val (>=) "%1 >= %2" prec 11 11 10
+ syntax val (>) "%1 > %2" prec 11 11 10
+end
+
+module list.Length
+ syntax val length "(List.length %1)"
+end
+
+module map.Map
+ syntax type map "%1 -> %2"
+ syntax val get "%1 %2"
+ syntax val ([]) "%1 %2"
+ syntax val set "(fun _x -> if _x = %2 then %3 else %1 _x)"
+ syntax val ([<-]) "(fun _x -> if _x = %2 then %3 else %1 _x)"
+end
+
+module map.Const
+ syntax val const "(fun _ -> %1)"
+end
+
+module utils.Same
+ syntax val same "(ignore %2 ; %1)"
+end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.mlw
new file mode 100644
index 00000000..845333b4
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.mlw
@@ -0,0 +1,124 @@
+(**
+ ONNX Tensor Definition.
+ *)
+
+module Shape
+ use int.Int
+ use list.Mem
+ use list.List
+ use sequence.Seq
+
+ predicate positive (ds : seq int) =
+ forall i. 0 <= i < length ds -> 0 < ds[i]
+
+ type shape = { dims : seq int }
+ invariant { positive dims }
+ meta coercion function dims
+
+ (*proof*)
+ (* Helper function for defining product *)
+ let rec function product (ds : list int) : int =
+ variant { ds }
+ match ds with
+ | Nil -> 1
+ | Cons d ds -> d * product ds
+ end
+
+ (* Lemma: shifted sequence *)
+ let ghost shift (xs : list 'a) (x : 'a) (rxs : list 'a)
+ requires { xs = Cons x rxs }
+ ensures { x = xs[0] }
+ ensures { forall i. 0 <= i < length rxs -> rxs[i] = xs[i+1] }
+ = ()
+
+ (* Lemma: forall s, 1 <= product s *)
+ let rec lemma product1 (ds : list int)
+ requires { positive ds }
+ ensures { 1 <= product ds }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons d rds ->
+ ghost shift ds d rds ;
+ product1 rds
+ end
+ (*qed*)
+ (*qed*)
+
+ let sizeof (s : shape) : int = product s.dims
+
+end
+
+module Index
+ use int.Int
+ use bool.Bool
+ use list.Mem
+ use list.List
+ use sequence.Seq
+ use Shape
+
+ type index = seq int
+
+ predicate valid (idx : index) (s : shape) =
+ length idx = length s /\
+ forall i. 0 <= i < length s -> 0 <= idx[i] < s[i]
+
+ let valid (idx : index) (s : shape) : bool
+ ensures { result <-> valid idx s }
+ (*proof*)
+ (* Efficient Implementation *)
+ = let rec inrange (ks ds : list int) : bool
+ requires { positive ds }
+ ensures { result <->
+ ( length ks = length ds /\
+ forall i. 0 <= i < length ks -> 0 <= ks[i] < ds[i] )
+ }
+ variant { ds }
+ = match ks, ds with
+ | Nil, Nil -> true
+ | Cons k ksr, Cons d dsr ->
+ ghost shift ks k ksr ;
+ ghost shift ds d dsr ;
+ 0 <= k < d && inrange ksr dsr
+ | _ -> false
+ end
+ in inrange idx s.dims
+ (*qed*)
+
+end
+
+module Tensor
+ use int.Int
+ use sequence.Seq
+ use export Shape
+ use export Index
+
+ type tensor 'a = {
+ shape : shape ;
+ value : index -> 'a ;
+ }
+
+ let function dim (t : tensor 'a) : int = length t.shape.dims
+
+
+ predicate (<<) (idx : index) (t : tensor 'a) = valid idx t.shape
+ function ([]) (t : tensor 'a) (idx : index) : 'a = t.value idx
+
+ exception Invalid_index
+
+ let mem (idx : index) (t : tensor 'a) : bool
+ ensures { result <-> idx << t }
+ (*proof*)
+ = valid idx t.shape
+ (*qed*)
+
+ let get (idx : index) (t : tensor 'a) : 'a
+ ensures { idx << t }
+ ensures { result = t[idx] }
+ raises { Invalid_index -> not (idx << t) }
+ (*proof*)
+ = if valid idx t.shape then t.value idx else raise Invalid_index
+ (*qed*)
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor/proof.json
new file mode 100644
index 00000000..85363c13
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor/proof.json
@@ -0,0 +1,116 @@
+{
+ "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ],
+ "proofs": {
+ "FlatTensor": {
+ "mixfix [<-]": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 }
+ ]
+ },
+ "mixfix []": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.022 }
+ ]
+ },
+ "mixfix []<-": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 }
+ ]
+ },
+ "scalar": { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ "shape": { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.013 }
+ },
+ "Index": {
+ "valid": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.035 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.044 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.014 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.02 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.027 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.019 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.032 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 }
+ ]
+ },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.015 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.023 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.046 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 }
+ ]
+ }
+ ]
+ },
+ { "prover": "alt-ergo@2.5.4", "time": 0.017 },
+ {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.025 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 }
+ ]
+ }
+ ]
+ }
+ },
+ "Shape": {
+ "product": { "prover": "alt-ergo@2.5.4", "time": 0.026 },
+ "product1": {
+ "tactic": "split_vc",
+ "children": [
+ { "prover": "alt-ergo@2.5.4", "time": 0.018 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.028 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.021 },
+ { "prover": "alt-ergo@2.5.4", "time": 0.051 }
+ ]
+ },
+ "shape": { "prover": "alt-ergo@2.5.4", "time": 0.031 },
+ "shift": { "prover": "alt-ergo@2.5.4", "time": 0.022 }
+ },
+ "Tensor": {
+ "get": { "prover": "alt-ergo@2.5.4", "time": 0.022 },
+ "mem": { "prover": "alt-ergo@2.5.4", "time": 0.022 }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/dune b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/dune
new file mode 100644
index 00000000..f413be10
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/dune
@@ -0,0 +1,4 @@
+(tests
+ (names matrix where)
+ (flags ("-open" "Tensorlib"))
+ (libraries tensorlib))
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.expected b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.expected
new file mode 100644
index 00000000..49915a28
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.expected
@@ -0,0 +1,2 @@
+ A = [ 1 2 3 ][ 4 5 6 ]
+tA = [ 1 4 ][ 2 5 ][ 3 6 ]
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.ml
new file mode 100644
index 00000000..faa31664
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.ml
@@ -0,0 +1,10 @@
+let pp_int fmt = Format.fprintf fmt "%2d"
+let pp_matrix = Tensor.pretty pp_int
+
+let a = Tensor.matrix [
+ [ 1; 2; 3 ];
+ [ 4; 5; 6 ];
+]
+
+let () = Format.printf " A = %a@." pp_matrix a
+let () = Format.printf "tA = %a@." pp_matrix (Tensor.transpose a)
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/test.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/test.ml
new file mode 100644
index 00000000..653e3139
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/test.ml
@@ -0,0 +1,23 @@
+
+(* pretty printer for float scalars *)
+let pp_real fmt v = Format.fprintf fmt "%3.2f" v
+(* pretty printer for boolean scalars *)
+let pp_bool fmt v = Format.fprintf fmt "%c" (if v then 'T' else 'F')
+
+let v = Tensor.scalar 0.0
+
+let () = Format.printf "Scalar: %a@." (Tensor.pretty pp_real) v
+
+let c = Tensor.vector [true;true;false;true;false]
+let a = Tensor.vector [1.0;2.0;3.0;4.0;5.0]
+let b = Tensor.vector [0.1;0.2;0.3;0.4;0.5]
+
+let w = Tensor.where c a b
+
+let () =
+ begin
+ Format.printf "C: %a@." (Tensor.pretty pp_bool) c ;
+ Format.printf "A: %a@." (Tensor.pretty pp_real) a ;
+ Format.printf "B: %a@." (Tensor.pretty pp_real) b ;
+ Format.printf "W: %a@." (Tensor.pretty pp_real) w ;
+ end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.expected b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.expected
new file mode 100644
index 00000000..91df10af
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.expected
@@ -0,0 +1,4 @@
+C: [ T T F T F ]
+A: [ 1.00 2.00 3.00 4.00 5.00 ]
+B: [ 0.10 0.20 0.30 0.40 0.50 ]
+W: [ 1.00 2.00 0.30 4.00 0.50 ]
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.ml
new file mode 100644
index 00000000..a96f7170
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.ml
@@ -0,0 +1,15 @@
+let pp_real fmt v = Format.fprintf fmt "%3.2f" v
+let pp_bool fmt v = Format.fprintf fmt "%c" (if v then 'T' else 'F')
+
+let c = Tensor.vector [true;true;false;true;false]
+let a = Tensor.vector [1.0;2.0;3.0;4.0;5.0]
+let b = Tensor.vector [0.1;0.2;0.3;0.4;0.5]
+let w = Tensor.where c a b
+
+let () =
+ begin
+ Format.printf "C: %a@." (Tensor.pretty pp_bool) c ;
+ Format.printf "A: %a@." (Tensor.pretty pp_real) a ;
+ Format.printf "B: %a@." (Tensor.pretty pp_real) b ;
+ Format.printf "W: %a@." (Tensor.pretty pp_real) w ;
+ end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils.mlw
new file mode 100644
index 00000000..9ebff261
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils.mlw
@@ -0,0 +1,25 @@
+module Same
+
+ val function same (a b : 'a) : 'a
+ ensures { a = b -> result = a = b }
+
+end
+
+module Product
+ use int.Int
+
+ (**
+ Notation for product, for `a <= i < b` of `f i`
+ *)
+ let rec function product (a b : int) (f : int -> int) : int
+ (*proof*)
+ variant { b-a }
+ = if b <= a then 1 else f a * product (a+1) b f
+ (*qed*)
+
+ lemma empty: forall a b f. b <= a -> product a b f = 1
+
+ axiom equal: forall a b f g.
+ (forall i. a <= i < b -> f i = g i) -> product a b f = product a b g
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils/proof.json
new file mode 100644
index 00000000..3eccaef9
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils/proof.json
@@ -0,0 +1,10 @@
+{
+ "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ],
+ "proofs": {
+ "Product": {
+ "empty": { "prover": "alt-ergo@2.5.4", "time": 0.013 },
+ "product": { "prover": "alt-ergo@2.5.4", "time": 0.012 }
+ },
+ "Same": {}
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/why3find.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/why3find.json
new file mode 100644
index 00000000..09ea7fdc
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/why3find.json
@@ -0,0 +1,10 @@
+{
+ "fast": 0.2,
+ "time": 1,
+ "depth": 4,
+ "packages": [],
+ "provers": [ "alt-ergo" ],
+ "tactics": [ "split_vc" ],
+ "drivers": [],
+ "warnoff": []
+}
diff --git a/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.drawio b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.drawio
new file mode 100644
index 00000000..c62cfdaf
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.drawio
@@ -0,0 +1,262 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.png b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.png
new file mode 100644
index 00000000..b32c392d
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.png differ
diff --git a/safety-related-profile/meetings/formal_methods/imgs/2025-06-20/image.png b/safety-related-profile/meetings/formal_methods/imgs/2025-06-20/image.png
new file mode 100644
index 00000000..fa574af0
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/imgs/2025-06-20/image.png differ
diff --git a/safety-related-profile/meetings/formal_methods/imgs/README.md b/safety-related-profile/meetings/formal_methods/imgs/README.md
new file mode 100644
index 00000000..43effb8a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/imgs/README.md
@@ -0,0 +1 @@
+All pictures referenced in the meeting minutes.
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/inputs/2026-02-18 - Inputs.md b/safety-related-profile/meetings/formal_methods/inputs/2026-02-18 - Inputs.md
new file mode 100644
index 00000000..9a593e7d
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/2026-02-18 - Inputs.md
@@ -0,0 +1,143 @@
+*Please provide your comments, questions, proposals here. They will be discussed during the Feb 18th 2026 workshop.*
+
+### Code generation
+When we try to generate code for the `Where` operator, the C file is empty...
+For more details: If we separate the general specification (ctensor) from the operator specification (abstract + concrete specification), generating C code produces the ctensor implementation (ctensor.c), but not the operator implementation (where.c).
+
+### Formalization of floating point operators
+- What do really want to express / need?
+- How shall we specify operations in F (floating point numbers)?
+ - For simple operations such as Add() or Mul(), we could specify the operation wrt their counter parts in R, e.g. something like: if $x$ and $y$ are FP tensors, and $+$ and $+.$ are the addition operators for floats and real, respectively:
+ $$
+ round(x+y)=round(round(x) + round(y))
+ $$
+ - Shall we adopt this "strategy" for simple operations (extending it to inf and Nan, non representable numbers)? even if it can't be appied to other operations...
+- Ideally : the formalization of floating point number and their operations should include the effect of rounding and the special values
+- Nota: In Why3, there are axiomatic models of IEEE754 numbers (see [Formalization of Floating-Point Arithmetic](https://www.why3.org/stdlib/ieee_float.html)).
+ The module provides constructors, some operations (add, sub, etc.).
+ It also provides some axioms that define the expected behaviour of FP numbers, by relating the result to the operation in R e.g.,
+ ```
+ axiom add_finite: forall m:mode, x y:t [add m x y].
+ is_finite x -> is_finite y -> no_overflow m (to_real x +. to_real y) ->
+ is_finite (add m x y) /\
+ to_real (add m x y) = round m (to_real x +. to_real y)
+ ```
+- Bottom line: model floating point operations as operations in R extended with Infs and NaN.
+
+### Modeling of booleans
+- Currely, booelans are modeled using floating point numbers (the value is "True" if it is greater than 0).
+- WOuld it be possible / make sense to use "built-in" booleans (in the "high-level" spec) and integers (in the "low-level" spec), with a mapping between them (`function b_to_i(nb: bool) : int = if b then 1 else 0`).
+
+---
+
+### Two levels of formalization
+- (João and Ricardo)
+ - Is there any motivation/reason for having two levels of formalization (abstract and concrete)?
+
+ - We assume that one of the reasons behind this choice was to have a link between the naturally structural representation of tensors, used by ONNX and multiple other frameworks, and the target representation proposed by SONNX (a flattened array). Is there any other motivation for adopting such formalisation strategy?
+
+ - As far as we have explored, in the abstract spec, the formalization seems to be carried out using a more functional style; on the other hand, in the concrete formalization a more imperative style is adopted.´
+
+ - Is this what is intended?
+
+ - If so, do the proofs become more complicated when different styles are used?
+---
+### Empty tensors
+- (João and Ricardo)
+ - We believe that, at this moment, in the abstract formalization, it is not possible to represent **empty tensors** - tensors with at least one of their dimensions equal to **zero**. What leads us to believe this is the following invariant.
+
+ ```
+ invariant { positive dims }
+ ```
+ - Is there any reason why empty tensors are not allowed in the abstract formalization?
+
+ \
+ We have noticed that, in the concrete formalization, several functions make use of the predicate `valid_tensor`, present in the file `libtensor.mlw`, or of the predicate `dimension`, present in the file `libvector.mlw`.
+
+ ```
+ predicate valid_tensor (t : ctensor) =
+ dimension t.t_dims t.t_rank /\
+ valid_range t.t_data 0 (tensor_size t) /\
+ writable t.t_data
+ ```
+ ```
+ predicate dimension (u : iarray) (n : int) =
+ 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32
+ ```
+ The predicate `dimension` in turn uses the predicate `pdim`, which is defined as:
+
+ ```
+ predicate pdim (u : iarray) (p q : int) =
+ forall k. p <= k < q -> 0 < value_at u k
+ ```
+
+ - Despite the absence of direct invariants over the ctensor type and the fact that we can create empty tensors at this level of the formalisations, they would not hold the predicate `valid_tensor`. After discussing this in the WG it became quite clear that empty tensors are indeed to be allowed. If so, what would be the consequences of this both at the abstract and concrete level?
+
+**Suggestion**
+- From our point of view these are the changes that would need to be done at each one of the levels:
+
+ - **Abstract level**:
+ - Instead of a **positive** predicate we would have to define a **non-negative**
+
+ - All the **let rec lemmas** previously defined for **positive** invariant would need to be adjusted to support the **non-negative** invariant
+
+ - **Concrete level**:
+ - Creation of an **ndim** predicate that is equal to **pdim** but instead of saying **positive**, says **non-negative**
+
+ - Creation of an **ndimension** predicate which copies the semantics of **dimension**, calling **ndim** instead of **pdim**
+ - Adjust all **let rec lemmas** that already exist for predicate **pdim** to be in conformity with the predicate **ndim**
+ - **cdim_size** should now accept a list of dimensions with 0s and therefore the **let ghost construct** _**mult_bound**_ does not hold in this context. There seems to be two possible ways of solving such issue:
+ - Change **let ghost construct** _**mult_bound**_ definition to hold for lists with non negative values (including zero). We have already tried this one, however, we were not successful.
+
+ - Having a **null_dimensions_check** that checks wether any dimension in the specified list is 0. **cdim_size** would now have an if statement based on the result of **null_dimensions_check**.
+ - **Valid_tensor** predicate will then be changed to use **ndimension** predicate instead of **dimension**.
+
+- [Empty tensors suggestion](./empty_tensors)
+---
+### Function Contracts
+- (João and Ricardo)
+
+ - In the abstract world, the definition and specification of functions enter as logical context as they are all ghost functions. It seems to us important to have contracts as they are the assurance that based on some well-defined pre-conditions some post-conditions will be verified for all possible inputs. Based on that we are struggling to understand what are the role of these contracts in the abstract level.
+
+ - Should they exist and specify properties of the functions?
+
+ - Should they not be present at this level?
+
+ - Should they be part only at specific functions in this level?
+
+ - Furthermore, and related to this, we would like to have your input in one of the structural operators we have worked on - flatten. The flatten's informal and formal specifications are, respectively, in the following links: [flatten informal specification](../../../sonnx/ops/spec/informal/flatten/flatten.md), [flatten formalization](../../../sonnx/ops/spec/formal/flatten/flatten.mlw).
+
+ - What is your opinion with regard to the abstract level contracts? (Are they enough? Should they be discarded? This question is not specific to the flatten function, but also to every auxiliar function in the file.)
+
+ - For this operator the concrete level formalisation will only make a copy of two different arrays.
+
+ - In such case how strong and meaningful should our invariant be in order to prove the link (post-condition/assert) between both levels?
+
+ - Do have any suggestions so that we can successfully prove the relation between both levels for this operator?
+
+ - For some operators wich we have already worked on, the Cformalisation implementation uses the **malloc** function. In such cases, for acessing the data we have to check if the pointer is not null. To do so we are currently using an if-statement. An operator in wich our formalisation strategy uses **malloc** is the [matmul formalization](../../../sonnx/ops/spec/formal/matmul/matmul.mlw). Regarding this strategy we fall in the same problem stated above: how can we sucessfully prove the relation between both levels for this operator, particularly in the case where **malloc** returns a null pointer.
+
+---
+### Generic tensors and type-specific tensors
+- (João and Ricardo)
+ - The strategy of formalisation used so far is not generic with respect to the **datatype of the ctensors**. In order to turn this strategy type-generic we have explored two different alternatives:
+
+ - **Clone-based approach**
+
+ - In this strategy we **declare** three types: a **concrete datatype**, an **abstract datatype** and a **function** that maps a concrete datatype to the respective abstract datatype.
+
+ - The already existing functions in the **ctensor module** were adapted to use the appropriate types and the respective mapping function.
+
+ - To create a specific tensor datatype we need only to clone the module instantiating the **concrete datatype**, the **abstract datatype** and the **function** that maps a concrete datatype to the respective abstract datatype.
+
+ - We were able to **generate C-code**, and **prove the cWhere operator** while using such strategy.
+
+ - **CTensor parametrized datatype**
+
+ - Similar to what was used for the abstract tensors formalisation, using an **a'** and a **b'** datatype to capture the concrete, abstract levels, respectively. Every function that needs to map the concrete onto the abstract would need to receive as an **argument** the **mapping function** that goes from **a'** to **b'**.
+
+ - With this strategy we were not able to extract C-code.
+
+ - What is your opinion regarding these two approaches? Is one of them more suited than the other, or is there any other approach that would be more adequate?
+
+- [Generic tensors suggestion](./generic_tensors)
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/Makefile b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/Makefile
new file mode 100644
index 00000000..132ecf50
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/Makefile
@@ -0,0 +1,37 @@
+# Makefile for libvector
+
+.PHONY: all prove update doc lib cc clean
+
+all: prove doc lib
+ @echo "------------------------------------------"
+
+prove:
+ @echo "------------------------------------------"
+ @echo "--- Proofs"
+ @echo "------------------------------------------"
+ @why3find prove *.mlw -l -s -x
+
+update:
+ why3find prove *.mlw -l -s -x -m
+
+doc:
+ @echo "------------------------------------------"
+ @echo "--- Documentation"
+ @echo "------------------------------------------"
+ @rm -fr html
+ @why3find doc *.mlw -t "Tensor Library"
+
+EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib
+
+lib:
+ @echo "------------------------------------------"
+ @echo "--- Extraction"
+ @echo "------------------------------------------"
+ @rm -fr lib
+ @why3 extract $(EXTRACT) libvector.CIndex
+ @why3 extract $(EXTRACT) libtensor.CTensor
+ @cd lib && gcc -c *.c
+ @find lib -type f | sort
+
+clean:
+ rm -fr lib html
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/cdriver.drv b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/cdriver.drv
new file mode 100644
index 00000000..7115821a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/cdriver.drv
@@ -0,0 +1,38 @@
+module std.Clib
+ interface export "#include "
+ interface export "#include "
+ syntax val to_uint32 "(uint32_t) %1"
+ syntax val is_null "!%1" prec 0
+ syntax val ([]) "%1[%2]" prec 1 1 15
+ syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14
+end
+
+module std.Cfloat
+ interface export "#include "
+
+ syntax type float "double"
+ syntax val zero "0.0"
+ syntax val one "1.0"
+ syntax val f32 "(double) %1"
+ syntax val f64 "(double) %1"
+ syntax val ( .+ ) "%1 + %2" prec 8 8 7
+ syntax val ( .- ) "%1 - %2" prec 8 8 7
+ syntax val (.-_) "- %1" prec 5 4
+ syntax val ( .* ) "%1 * %2" prec 7 7 6
+ syntax val ( ./ ) "%1 / %2" prec 7 7 6
+ syntax val ( .= ) "%1 = %2" prec 11 11 10
+ syntax val ( .< ) "%1 < %2" prec 11 11 10
+ syntax val ( .<= ) "%1 <= %2" prec 11 11 10
+end
+
+module libvector.CIndex
+ interface "#include "
+ interface "#include "
+end
+
+module libtensor.CTensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+ interface "#include "
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout.mlw
new file mode 100644
index 00000000..fd7bcf7b
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout.mlw
@@ -0,0 +1,91 @@
+module CFlat
+ use std.Int
+ use std.List
+ use tensor.Range
+
+ function offset (ks ds : list int) : int =
+ match ks , ds with
+ | Cons k ks , Cons _ ds -> k * size ds + offset ks ds
+ | _ -> 0
+ end
+
+ function index (p : int) (ds : list int) : list int =
+ match ds with
+ | Nil -> Nil
+ | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds)
+ end
+
+ let rec lemma offset_size (ks ds : list int)
+ requires { valid ks ds }
+ ensures { 0 <= offset ks ds < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k krs , Cons d drs ->
+ offset_size krs drs ;
+ let p = offset ks ds in
+ let q = offset krs drs in
+ assert { p = size drs * k + q } ;
+ assert {
+ size drs * k
+ <= size drs * (d - 1)
+ = d * size drs - size drs
+ } ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma index_range (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { valid (index p ds) ds }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds -> index_range (mod p (size rds)) rds
+ end
+ (*qed*)
+
+ let rec lemma index_of_offset (ks ds : list int)
+ requires { valid ks ds }
+ ensures { index (offset ks ds) ds = ks }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k rks , Cons _ rds ->
+ index_of_offset rks rds ;
+ let p = offset ks ds in
+ let n = size rds in
+ let q = offset rks rds in
+ euclide p k n q ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma offset_of_index (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { offset (index p ds) ds = p }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds ->
+ let n = size rds in
+ offset_of_index (mod p n) rds
+ end
+ (*qed*)
+
+ let rec lemma offset_push (ks ds : list int) (k d : int)
+ requires { valid ks ds }
+ ensures { offset (push ks k) (push ds d) = offset ks ds * d + k }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Cons _ krs , Cons _ drs -> offset_push krs drs k d
+ | _ -> ()
+ end
+ (*qed*)
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/proof.json
new file mode 100644
index 00000000..e220781a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/proof.json
@@ -0,0 +1,12 @@
+{
+ "profile": [],
+ "proofs": {
+ "CFlat": {
+ "index_of_offset": null,
+ "index_range": null,
+ "offset_of_index": null,
+ "offset_push": null,
+ "offset_size": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml
new file mode 100644
index 00000000..34174adf
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml.bak
new file mode 100644
index 00000000..34174adf
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml.bak
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz
new file mode 100644
index 00000000..16d9c3fe
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz.bak
new file mode 100644
index 00000000..16d9c3fe
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.c b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.c
new file mode 100644
index 00000000..e92baf0a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.c
@@ -0,0 +1,80 @@
+#include "cindex.h"
+
+int null_dimension_check(int32_t * u, int32_t n) {
+ int is_null_dim;
+ int32_t i, o;
+ is_null_dim = 0;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ if (u[i] == 0) {
+ is_null_dim = 1;
+ }
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return is_null_dim;
+}
+
+int32_t cdim_size(int32_t * u, int32_t n) {
+ int32_t p;
+ int32_t i, o;
+ if (null_dimension_check(u, n)) {
+ return 0;
+ } else {
+ p = 1;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ p = p * u[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+ }
+}
+
+int32_t * cdim_create_1(int32_t n) {
+ int32_t * cd;
+ cd = malloc(1U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = n;
+ }
+ return cd;
+}
+
+int32_t * cdim_create_2(int32_t p, int32_t q) {
+ int32_t * cd;
+ cd = malloc(2U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = p;
+ cd[1] = q;
+ }
+ return cd;
+}
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) {
+ int32_t p;
+ int32_t i, o, d, k;
+ p = 0;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ d = ds[i];
+ k = ks[i];
+ if (0 <= k && k < d) {
+ p = p * d + k;
+ } else {
+ return -1;
+ }
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.h b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.h
new file mode 100644
index 00000000..b689813d
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.h
@@ -0,0 +1,17 @@
+#ifndef CINDEX_H_INCLUDED
+
+#include
+#include
+
+int null_dimension_check(int32_t * u, int32_t n);
+
+int32_t cdim_size(int32_t * u, int32_t n);
+
+int32_t * cdim_create_1(int32_t n);
+
+int32_t * cdim_create_2(int32_t p, int32_t q);
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n);
+
+#define CINDEX_H_INCLUDED
+#endif // CINDEX_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.o b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.o
new file mode 100644
index 00000000..209fc57d
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.c b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.c
new file mode 100644
index 00000000..8c7c9520
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.c
@@ -0,0 +1,58 @@
+#include "ctensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ double * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(double));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = ((double) 0.0);
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, double v) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b,
+ struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = ((double) 0.0) < cond.t_data[i] ? a.t_data[i] : b.t_data[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.h b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.h
new file mode 100644
index 00000000..a9da013a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.h
@@ -0,0 +1,24 @@
+#ifndef CTENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ double * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r);
+
+void ctensor_reset(struct ctensor r, double v);
+
+void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b,
+ struct ctensor r);
+
+#define CTENSOR_H_INCLUDED
+#endif // CTENSOR_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.o b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.o
new file mode 100644
index 00000000..7406bb9c
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor.mlw
new file mode 100644
index 00000000..31700330
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor.mlw
@@ -0,0 +1,153 @@
+module CTensor
+ use std.Int
+ use std.List
+ use std.Clib
+ use std.Cfloat
+ use mach.int.Int32
+ use tensor.Range
+ use tensor.Tensor
+ use tensor.OPWhere
+ use layout.CFlat
+ use libvector.CIndex
+
+ (** C Tensor **)
+
+ type farray = ptr float
+
+ type ctensor = {
+ t_rank : int32 ;
+ t_dims : iarray ;
+ t_data : farray ;
+ }
+
+ (** Dimension List **)
+ function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank
+ (** Number of entries **)
+ function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank
+ (** Valid index predicate **)
+ predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t)
+ (** Empty tensor predicate **)
+ predicate empty_tensor (t : ctensor) = t.t_rank = 0
+
+ (** Valid tensor **)
+
+ (*New*)
+ predicate valid_tensor (t : ctensor) =
+ ndimension t.t_dims t.t_rank /\
+ valid_range t.t_data 0 (tensor_size t) /\
+ writable t.t_data
+ (*End New*)
+
+ (*Coordinate Index for ctensor*)
+ function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t)
+
+ function tensor_value_at (k : list int) (t : ctensor) : real =
+ if valid_index k t then
+ value_at t.t_data (tensor_offset k t)
+ else 0.0
+
+ function tensor_value (t : ctensor) : list int -> real =
+ fun k -> tensor_value_at k t
+
+ function tensor_valueb (t : ctensor) : list int -> bool =
+ fun k -> Real.(tensor_value_at k t > 0.0)
+
+ let ghost function tensor (t : ctensor) : tensor real
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_value t }
+ ensures { result.background = 0.0 }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_value t } ;
+ background = 0.0 ;
+ }
+
+ let ghost function tensorb (t : ctensor) : tensor bool
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_valueb t }
+ ensures { result.background = false }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_valueb t } ;
+ background = false ;
+ }
+
+ (*New*)
+ let ctensor_create (ds : iarray) (n : int32) : ctensor =
+ (*proof*)
+ requires { ndimension ds n }
+ ensures { empty_tensor result \/ valid_tensor result }
+ ensures { empty_tensor result \/ result.t_rank = n }
+ ensures { empty_tensor result \/ result.t_dims = ds }
+ (*qed*)
+ let m = cdim_size ds n in
+ let vs = malloc (to_uint32 m) in
+ {
+ t_rank = if is_null vs then 0 else n ;
+ t_dims = ds ;
+ t_data = vs ;
+ }
+ (*End New*)
+
+ let ctensor_clear (r : ctensor) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 }
+ (*qed*)
+ r.t_data[i] <- f32 0.0
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_reset (r : ctensor) (v : float) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = v }
+ (*qed*)
+ r.t_data[i] <- v
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_where (cond a b r : ctensor) =
+ (*proof*)
+ requires { valid_tensor cond }
+ requires { valid_tensor a }
+ requires { valid_tensor b }
+ requires { valid_tensor r }
+ requires { tensor a ~= tensor b ~= tensor r }
+ (*qed*)
+ requires { tensorb cond ~ tensor a ~ tensor b }
+ ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant {
+ forall k. 0 <= k < i ->
+ value_at r.t_data k =
+ if Real.(0.0 < value_at cond.t_data k)
+ then a.t_data[k] else b.t_data[k]
+ }
+ (*qed*)
+ r.t_data[i] <-
+ if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i]
+ done
+ (*proof*)
+ ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) }
+ (*qed*)
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/proof.json
new file mode 100644
index 00000000..e6f16a32
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/proof.json
@@ -0,0 +1,13 @@
+{
+ "profile": [],
+ "proofs": {
+ "CTensor": {
+ "ctensor_clear": null,
+ "ctensor_create": null,
+ "ctensor_reset": null,
+ "ctensor_where": null,
+ "tensor": null,
+ "tensorb": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml
new file mode 100644
index 00000000..cde8ef5b
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml.bak
new file mode 100644
index 00000000..cde8ef5b
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml.bak
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz
new file mode 100644
index 00000000..278c545d
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz.bak
new file mode 100644
index 00000000..278c545d
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector.mlw
new file mode 100644
index 00000000..34b6c547
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector.mlw
@@ -0,0 +1,187 @@
+(** C-Library to compute Offsets & Dimensions *)
+
+module CIndex
+ use std.Int
+ use std.List
+ use std.Clib
+ use mach.int.Int32
+ use tensor.Range
+ use layout.CFlat
+
+ type iarray = ptr int32
+
+ function ivector (u : iarray) (n : int) : list int
+ = map Int32.to_int (vector u n)
+
+ function islice (u : iarray) (p q : int) : list int
+ = map Int32.to_int (slice u p q)
+
+ (** ## Dimensions *)
+
+ function vdim (u : iarray) (n : int) : int = size (ivector u n)
+ function sdim (u : iarray) (p q : int) : int = size (islice u p q)
+
+
+ predicate pdim (u : iarray) (p q : int) =
+ forall k. p <= k < q -> 0 < value_at u k
+
+ (*New*)
+ predicate ndim (u : iarray) (p q : int) =
+ forall k. p <= k < q -> 0 <= value_at u k
+ (*End New*)
+ predicate dimension (u : iarray) (n : int) =
+ 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32
+
+ (*New*)
+ predicate ndimension (u : iarray) (n : int) =
+ 0 <= n /\ valid_range u 0 n /\ ndim u 0 n /\ 0 < vdim u n <= max_int32
+ (*End New*)
+
+ (** Equivalence betwen {pdim} and {tensor.Range.positive}. *)
+ let rec lemma positive_pdim (u : iarray) (p q : int)
+ (*proof*)
+ ensures { pdim u p q <-> positive (islice u p q) }
+ variant { q - p }
+ = if p < q then positive_pdim u (p+1) q
+ (*qed*)
+
+ (*New*)
+ (** Equivalence betwen {ndim} and {tensor.Range.non_negative}. *)
+ let rec lemma non_negative_ndim (u : iarray) (p q : int)
+ (*proof*)
+ ensures { ndim u p q <-> non_negative (islice u p q) }
+ variant { q - p }
+ = if p < q then non_negative_ndim u (p+1) q
+ (*qed*)
+
+ let ghost sdim_split (u : iarray) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { ndim u p q }
+ ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q }
+ = assert { islice u p k ++ islice u k q = islice u p q }
+ (*qed*)
+
+
+ let rec lemma vdim_zero (u:iarray) (n:int)
+ requires { ndim u 0 n }
+ ensures { (exists i. 0 <= i < n /\ value_at u i = 0) <-> vdim u n = 0 }
+ variant { n }
+ = if n > 0 then
+ if value_at u (n-1) = 0 then
+ ()
+ else
+ vdim_zero u (n-1)
+
+ let null_dimension_check (u : iarray) (n : int32) : bool =
+ requires { ndimension u n }
+ ensures { result = (exists i. 0 <= i < n /\ value_at u i = 0) }
+ ensures { result -> vdim u n = 0 }
+ ensures { not result = (forall i. 0 <= i < n -> value_at u i <> 0) }
+ ensures { not result -> vdim u n > 0 }
+ (*proof*)
+ let ref is_null_dim = false in
+ for i = 0 to n - 1 do
+ invariant {
+ (is_null_dim = false -> forall k. 0 <= k < i -> value_at u k <> 0) /\
+ (is_null_dim = true -> exists j. 0 <= j < i /\ value_at u j = 0)
+ }
+ if u[i] = 0 then
+ is_null_dim <- true
+ done ;
+ is_null_dim
+
+ let cdim_size (u : iarray) (n : int32): int32 =
+ ensures { result = vdim u n }
+ (*proof*)
+ requires { ndimension u n }
+ (*qed*)
+ if null_dimension_check u n then
+ 0
+ else
+ begin
+ let ref p = 1 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { p = vdim u i }
+ ghost
+ begin
+ ensures { 1 <= p * u[i] <= max_int32 }
+ let ghost dl = pure { sdim u 0 i } in
+ let ghost di = Int32.to_int u[i] in
+ let ghost dr = pure { sdim u (i+1) n } in
+ let ghost dn = pure { sdim u 0 n } in
+ sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * u[i] ;
+ done ; p
+ end
+
+
+ let cdim_create_1 (n : int32) : iarray =
+ requires { 0 < n }
+ ensures { is_not_null result -> vdim result 1 = n }
+ (*proof*)
+ ensures { is_not_null result -> ndimension result 1 }
+ ensures { is_not_null result -> value_at result 0 = n }
+ (*qed*)
+ let cd = malloc 1 in
+ if is_not_null cd then cd[0] <- n ; cd
+
+ let cdim_create_2 (p q : int32) : iarray =
+ requires { 0 < p /\ 0 < q /\ p * q <= max_int32 }
+ ensures { is_not_null result -> vdim result 2 = p * q }
+ (*proof*)
+ ensures { is_not_null result -> ndimension result 2 }
+ ensures { is_not_null result -> value_at result 0 = p }
+ ensures { is_not_null result -> value_at result 1 = q }
+ (*qed*)
+ let cd = malloc 2 in
+ if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd
+ (*End New*)
+
+
+ (** ## Coordinates *)
+
+ let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 =
+ (*proof*)
+ requires { 0 <= n }
+ requires { valid_range ks 0 n }
+ requires { dimension ds n }
+ ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) }
+ ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) }
+ (*qed*)
+ begin
+ let ref p = 0 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { valid (ivector ks i) (ivector ds i) }
+ invariant { p = offset (ivector ks i) (ivector ds i) }
+ (*qed*)
+ let d = ds[i] in
+ let k = ks[i] in
+ if 0 <= k && k < d then
+ begin
+ (*proof*)
+ assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ;
+ ghost
+ begin
+ ensures { Int32.in_bounds (p * d) }
+ ensures { Int32.in_bounds (p * d + k) }
+ let ghost dl = pure { sdim ds 0 i } in
+ let ghost di = Int32.to_int ds[i] in
+ let ghost dr = pure { sdim ds (i+1) n } in
+ let ghost dn = pure { sdim ds 0 n } in
+ sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * d + k ;
+ end
+ else return (-1)
+ done ; p
+ end
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/proof.json
new file mode 100644
index 00000000..94eecafd
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/proof.json
@@ -0,0 +1,13 @@
+{
+ "profile": [],
+ "proofs": {
+ "CIndex": {
+ "cdim_create_1": null,
+ "cdim_create_2": null,
+ "cdim_size": null,
+ "coffset": null,
+ "positive_pdim": null,
+ "sdim_split": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml
new file mode 100644
index 00000000..25c72ed2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml.bak
new file mode 100644
index 00000000..25c72ed2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml.bak
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz
new file mode 100644
index 00000000..b15d3416
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz.bak
new file mode 100644
index 00000000..b15d3416
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std.mlw
new file mode 100644
index 00000000..244ee393
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std.mlw
@@ -0,0 +1,234 @@
+(* Extensions to Standard Library *)
+
+module Int
+ use export int.Int
+ use export int.Abs
+ use export int.ComputerDivision
+ use list.List
+ use list.Length
+
+ (** Unicity of euclidian division decomposition *)
+ let ghost euclide (a b q r : int)
+ requires { 0 <= a /\ 0 <= r < q }
+ requires { a = b * q + r }
+ ensures { b = div a q }
+ ensures { r = mod a q }
+ (*proof*)
+ = let rec kernel (b r : int)
+ requires { b * q + r = 0 }
+ requires { abs r < q }
+ ensures { b = r = 0 }
+ variant { abs b }
+ = if b < 0 then kernel (b + 1) (r - q) else
+ if b > 0 then kernel (b - 1) (r + q) else ()
+ in kernel (b - div a q) (r - mod a q)
+ (*qed*)
+
+ (** Multiplication upper-bound *)
+ let ghost mult_bound (a b c : int)
+ requires { 0 < a * b <= c }
+ ensures { abs a <= c }
+ ensures { abs b <= c }
+ = ()
+
+ (** Helper function to extract element from list at given position *)
+ let rec function get_dim (dims : list int) (idx : int) : int
+ requires { 0 <= idx < length dims }
+ variant { dims }
+ = match dims with
+ | Nil -> 0 (* should not happen due to precondition *)
+ | Cons h t -> if idx = 0 then h else get_dim t (idx - 1)
+ end
+
+end
+
+module List
+ use export list.List
+ use export list.Map
+ use export list.Append
+
+ (** Appends an element to the end of the list *)
+ function push (xs : list 'a) (x : 'a) : list 'a =
+ xs ++ Cons x Nil
+
+ let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a)
+ ensures { map f (xs ++ ys) = map f xs ++ map f ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end
+ (*qed*)
+
+end
+
+module Clib
+ use map.Map
+ use int.Int
+ use mach.int.Int32
+ use export mach.c.C
+
+ type int32 = Int32.int32
+ type uint32 = UInt32.uint32
+
+ val function to_uint32 (v : int32) : uint32
+ requires { 0 <= v }
+ ensures { Int32.to_int v = UInt32.to_int result }
+
+ (** Null-Pointer *)
+ val predicate is_null (vp : ptr 'a) : bool
+ ensures { result <-> vp.zone = null_zone }
+
+ (** Array-range validity *)
+ predicate valid_range (vp : ptr 'a) (p q : int) =
+ q <= p \/
+ ( 0 <= vp.min <= vp.offset /\
+ 0 <= p <= q <= max_int32 /\
+ 0 <= vp.min <= vp.offset + p /\
+ vp.offset + q <= vp.max <= vp.plength )
+
+ let lemma valid_in_range (vp : ptr 'a) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { valid_range vp p q }
+ ensures { valid_ptr_shift vp k }
+ = ()
+ (*qed*)
+
+ (** Array as map *)
+ let ghost function value_at (vp : ptr 'a) : map int 'a =
+ pure { fun k -> vp.data.Array.elts (vp.offset + k) }
+
+ (** Array access (ghost) *)
+ function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k
+
+ (** Array access (C, inlined) *)
+ let ([]) (vp : ptr 'a) (k : int32) : 'a
+ (*proof*)
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { result = vp[k] }
+ = get_ofs vp k
+ (*qed*)
+
+ (** Array update (C, inlined) *)
+ let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit
+ requires { writable vp }
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v }
+ = set_ofs vp k v
+
+ (** Array as list *)
+ use List
+
+ let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a =
+ (*proof*)
+ variant { q - p }
+ (*qed*)
+ if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q)
+
+ let rec lemma slice_append (u : ptr 'a) (p q r : int)
+ requires { p <= q <= r }
+ ensures { slice u p r = slice u p q ++ slice u q r }
+ (*proof*)
+ variant { q - p }
+ = if p < q then slice_append u (p+1) q r
+ (*qed*)
+
+ let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n
+
+ let lemma vector_push (u : ptr 'a) (n : int)
+ requires { 0 <= n }
+ ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+ (*proof*)
+ = slice_append u 0 n (n+1)
+ (*qed*)
+
+val function malloc_int32 (s: uint32) : ptr int32
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+end
+
+
+module Cint
+ use int.Int
+ use mach.int.Int32
+ use mach.int.Int32 as Int32
+ use mach.int.UInt32
+ use mach.int.UInt32 as UInt32
+ use int.ComputerDivision
+ use Clib
+
+ val function ( .+ ) (a b : int) : int
+ ensures { result = a + b }
+
+ val function ( .- ) (a b : int) : int
+ ensures { result = a - b }
+
+ val function ( .* ) (a b : int) : int
+ ensures { result = a * b }
+
+ val function ( ./ ) (a b : int) : int
+ requires { b <> 0 }
+ ensures { result = ComputerDivision.div (a) ( b) }
+
+ val function of_int (v : int) : Int32.int32
+ ensures { Int32.to_int result = v }
+
+ val function malloc_int (s: uint32) : ptr int
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+ val function get (p: ptr int) (k: Int32.int32) : int
+ ensures { result = p[k] }
+
+ val function malloc_int32 (s: uint32) : ptr int32
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+ let ([]) (p : ptr int) (k :Int32.int32) : int = get p k
+
+end
+
+module Cfloat
+ use int.Int
+ use real.Real
+ use real.FromInt
+
+ type float = abstract { to_real : real }
+ meta coercion function to_real
+
+ type f32 = < float 8 24 > (* single precision literals *)
+ type f64 = < float 11 53 > (* double precision literals *)
+
+
+ val function f32 (k : f32) : float
+ ensures { result = f32'real k }
+
+ val function f64 (k : f64) : float
+ ensures { result = f64'real k }
+
+ let constant zero = f64 0.0
+ let constant one = f64 1.0
+
+ val function (.+) (a b : float) : float
+ ensures { Real.( result = a + b ) }
+
+ val function (.-) (a b : float) : float
+ ensures { Real.( result = a - b ) }
+
+ val function (.-_) (a : float) : float
+ ensures { Real.( result = (- a) ) }
+
+ val function ( .* ) (a b : float) : float
+ ensures { Real.( result = a * b ) }
+
+ val function ( ./ ) (a b : float) : float
+ requires { b <> 0.0 }
+ ensures { Real.( result = a / b ) }
+
+ val predicate ( .= ) (a b : float) : bool
+ ensures { result <-> Real.( a = b ) }
+
+ val predicate ( .< ) (a b : float) : bool
+ ensures { result <-> Real.( a < b ) }
+
+ val predicate ( .<= ) (a b : float) : bool
+ ensures { result <-> Real.( a <= b ) }
+
+end
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/proof.json
new file mode 100644
index 00000000..636ea4c6
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/proof.json
@@ -0,0 +1,20 @@
+{
+ "profile": [],
+ "proofs": {
+ "Cfloat": {
+ "one": { "tactic": "split_vc", "children": [] },
+ "zero": { "tactic": "split_vc", "children": [] }
+ },
+ "Cint": { "mixfix []": { "tactic": "split_vc", "children": [] } },
+ "Clib": {
+ "mixfix []": null,
+ "mixfix []<-": null,
+ "slice": null,
+ "slice_append": null,
+ "valid_in_range": null,
+ "vector_push": null
+ },
+ "Int": { "euclide": null, "get_dim": null, "mult_bound": null },
+ "List": { "map_concat": null }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml
new file mode 100644
index 00000000..75bc70f5
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml
@@ -0,0 +1,296 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml.bak
new file mode 100644
index 00000000..75bc70f5
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml.bak
@@ -0,0 +1,296 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz
new file mode 100644
index 00000000..38e5086b
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz.bak
new file mode 100644
index 00000000..38e5086b
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor.mlw
new file mode 100644
index 00000000..9b6263c4
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor.mlw
@@ -0,0 +1,196 @@
+(** Formalization of coordinates and dimensions *)
+
+module Range
+ use int.Int
+ use std.List
+
+ function size (ds : list int) : int =
+ match ds with
+ | Nil -> 1
+ | Cons d ds -> d * size ds
+ end
+
+
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+
+ (* New *)
+ predicate non_negative (ds: list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 <= d /\ non_negative ds
+ end
+ (* End New *)
+
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+
+ let rec lemma valid_push (ks ds : list int) (k d : int)
+ requires { 0 <= k < d }
+ requires { valid ks ds }
+ ensures { valid (push ks k) (push ds d) }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Nil , Nil -> ()
+ | Cons _ ks , Cons _ ds -> valid_push ks ds k d
+ | _ -> absurd
+ end
+ (*qed*)
+
+ let rec lemma size_append (xs ys : list int)
+ ensures { size (xs ++ ys) = size xs * size ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end
+ (*qed*)
+
+ lemma size_push: forall xs x. size (push xs x) = size xs * x
+
+ let rec lemma positive_size (ds : list int)
+ requires { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ds with Nil -> () | Cons _ ds -> positive_size ds end
+ (*qed*)
+
+ let rec lemma positive_valid (ks ds : list int)
+ requires { valid ks ds }
+ ensures { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons _ ks , Cons _ ds -> positive_valid ks ds
+ | _ -> ()
+ end
+ (*qed*)
+
+ (* New *)
+ let rec lemma non_negative_size (ds : list int)
+ requires { non_negative ds }
+ ensures { 0 <= size ds }
+ (*proof*)
+ variant { ds }
+ = match ds with Nil -> () | Cons _ ds -> non_negative_size ds end
+ (*qed*)
+
+ let rec lemma non_negative_valid (ks ds : list int)
+ requires { valid ks ds }
+ ensures { non_negative ds }
+ ensures { 0 <= size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons _ ks , Cons _ ds -> non_negative_valid ks ds
+ | _ -> ()
+ end
+ (*qed*)
+ (* End New *)
+
+end
+
+
+(** Formalization of Tensor *)
+module Tensor
+ use int.Int
+ use map.Map
+ use list.List
+ use Range
+
+ type data 'a = map (list int) 'a
+
+ type tensor 'a = {
+ dims : list int ;
+ data : data 'a ;
+ background : 'a ; (* default value, or value for 0-dimensions tensor *)
+ }
+
+ (*New*)
+ invariant { non_negative dims }
+ (*End New*)
+
+ invariant { forall k. valid k dims \/ data k = background }
+ (*proof*)
+ by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+ (*qed*)
+
+ meta coercion function dims
+ meta coercion function data
+
+ (** Tensor with the same dimensions *)
+ predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims
+
+ (** Tensor with the same dimensions and background value *)
+ predicate (~=) (a : tensor 'a) (b : tensor 'a) =
+ a ~ b /\ a.background = b.background
+
+ (** Equal tensors *)
+ predicate (==) (a b : tensor 'a) =
+ a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k
+
+ (** Extensionality *)
+ lemma exteq: forall a b : tensor 'a. a == b <-> a = b
+ (*proof*) by tensor'eq a b (*qed*)
+
+ (** Scalar Tensor *)
+ let ghost function scalar (v : 'a) : tensor 'a
+ ensures { result.dims = Nil }
+ ensures { result.background = v }
+ ensures { forall k. result k = v }
+ (*proof*)
+ = { dims = Nil ; data = (fun _ -> v) ; background = v }
+ (*qed*)
+
+ (*New*)
+ (** Null Tensor *)
+ let ghost function zero (e : 'a) (ds : list int) : tensor 'a
+ requires { non_negative ds }
+ ensures { result.dims = ds }
+ ensures { result.background = e }
+ ensures { forall k. result k = e }
+ (*proof*)
+ = { dims = ds ; data = (fun _ -> e) ; background = e }
+ (*qed*)
+
+
+ (** Constant Tensor *)
+ let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a
+ ensures { result.dims = ds }
+ requires { non_negative ds }
+ ensures { result.background = bg }
+ ensures { forall k. valid k ds -> result k = v }
+ (*proof*)
+ = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg }
+ (*qed*)
+
+ (** Constant & Null *)
+ goal zero_is_const: forall e : 'a, ds. non_negative ds -> zero e ds == const e e ds
+ (*End New*)
+end
+
+(** OP-Where Tensor Operation *)
+module OPWhere
+ use Tensor
+
+ let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+
+end
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/proof.json
new file mode 100644
index 00000000..73b56dab
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/proof.json
@@ -0,0 +1,24 @@
+{
+ "profile": [],
+ "proofs": {
+ "OPWhere": {
+ "dwhere": { "tactic": "split_vc", "children": [] },
+ "opwhere": null
+ },
+ "Range": {
+ "positive_size": null,
+ "positive_valid": null,
+ "size_append": null,
+ "size_push": null,
+ "valid_push": null
+ },
+ "Tensor": {
+ "const": null,
+ "exteq": null,
+ "scalar": null,
+ "tensor": null,
+ "zero": null,
+ "zero_is_const": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml
new file mode 100644
index 00000000..29b7ec0e
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml.bak
new file mode 100644
index 00000000..29b7ec0e
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml.bak
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz
new file mode 100644
index 00000000..7610217c
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz.bak
new file mode 100644
index 00000000..7610217c
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/Makefile b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/Makefile
new file mode 100644
index 00000000..b9f86156
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/Makefile
@@ -0,0 +1,41 @@
+# Makefile for libvector
+
+.PHONY: all prove update doc lib cc clean
+
+all: prove doc lib
+ @echo "------------------------------------------"
+
+prove:
+ @echo "------------------------------------------"
+ @echo "--- Proofs"
+ @echo "------------------------------------------"
+ @why3find prove *.mlw -l -s -x
+
+update:
+ why3find prove *.mlw -l -s -x -m
+
+doc:
+ @echo "------------------------------------------"
+ @echo "--- Documentation"
+ @echo "------------------------------------------"
+ @rm -fr html
+ @why3find doc *.mlw -t "Tensor Library"
+
+EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib
+
+lib:
+ @echo "------------------------------------------"
+ @echo "--- Extraction"
+ @echo "------------------------------------------"
+ @rm -fr lib
+ @why3 extract $(EXTRACT) libvector.CIndex
+ @why3 extract $(EXTRACT) libtensor.CTensor
+ @why3 extract $(EXTRACT) specifictensor.CInt32Tensor
+ @why3 extract $(EXTRACT) specifictensor.CInt64Tensor
+ @why3 extract $(EXTRACT) specifictensor.CBooleanTensor
+ @why3 extract $(EXTRACT) specifictensor.CFloatTensor
+ @cd lib && gcc -c *.c
+ @find lib -type f | sort
+
+clean:
+ rm -fr lib html
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/cdriver.drv b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/cdriver.drv
new file mode 100644
index 00000000..3a8870d9
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/cdriver.drv
@@ -0,0 +1,68 @@
+module std.Clib
+ interface export "#include "
+ interface export "#include "
+ syntax val to_uint32 "(uint32_t) %1"
+ syntax val is_null "!%1" prec 0
+ syntax val ([]) "%1[%2]" prec 1 1 15
+ syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14
+end
+
+module std.Cfloat
+ interface export "#include "
+
+ syntax type float "double"
+ syntax val zero "0.0"
+ syntax val one "1.0"
+ syntax val f32 "(double) %1"
+ syntax val f64 "(double) %1"
+ syntax val ( .+ ) "%1 + %2" prec 8 8 7
+ syntax val ( .- ) "%1 - %2" prec 8 8 7
+ syntax val (.-_) "- %1" prec 5 4
+ syntax val ( .* ) "%1 * %2" prec 7 7 6
+ syntax val ( ./ ) "%1 / %2" prec 7 7 6
+ syntax val ( .= ) "%1 = %2" prec 11 11 10
+ syntax val ( .< ) "%1 < %2" prec 11 11 10
+ syntax val ( .<= ) "%1 <= %2" prec 11 11 10
+end
+
+module libvector.CIndex
+ interface "#include "
+ interface "#include "
+end
+
+module libtensor.CTensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+ interface "#include "
+end
+
+module specifictensor.CInt32Tensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+ interface "#include "
+end
+
+
+module specifictensor.CInt64Tensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+ interface "#include "
+end
+
+module specifictensor.CBooleanTensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+ interface "#include "
+end
+
+
+module specifictensor.CFloatTensor
+ interface "#include "
+ interface "#include "
+ interface "#include \"cindex.h\""
+ interface "#include "
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor.mlw
new file mode 100644
index 00000000..c5052c7d
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor.mlw
@@ -0,0 +1,117 @@
+module GenericTensor
+ use std.Int
+ use std.List
+ use std.Clib
+ use mach.int.Int32
+ use tensor.Range
+ use tensor.Tensor
+ use tensor.OPWhere
+ use layout.CFlat
+ use libvector.CIndex
+
+ (* data_type defines the type of t_data on the ctensor *)
+ type data_type
+ (* abstract_data_type defines the type of abstract data corresponding to data_type *)
+ type abstract_data_type
+ (* Function to convert data_type to abstract_data_type *)
+ (* Here the function is only declared *)
+ function to_abstract (v : data_type) : abstract_data_type
+
+ (* Note *)
+ (* When clonning this module, data_type, abstract_data_type and to_abstract must be defined *)
+
+
+ (** C Tensor **)
+
+ type ctensor = {
+ t_rank : int32 ;
+ t_dims : iarray ;
+ t_data : ptr data_type ;
+ }
+
+ (** Dimension List **)
+ function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank
+ (** Number of entries **)
+ function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank
+ (** Valid index predicate **)
+ predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t)
+ (** Empty tensor predicate **)
+ predicate empty_tensor (t : ctensor) = t.t_rank = 0
+
+ (** Valid tensor **)
+ predicate valid_tensor (t : ctensor) =
+ dimension t.t_dims t.t_rank /\
+ valid_range t.t_data 0 (tensor_size t) /\
+ writable t.t_data
+
+ (*Coordinate Index for ctensor*)
+ function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t)
+
+ function tensor_value_at (k : list int) (t : ctensor) (cbackground : data_type) : abstract_data_type =
+ if valid_index k t then
+ to_abstract (value_at t.t_data (tensor_offset k t))
+ else to_abstract cbackground
+
+ function tensor_value (t : ctensor) (cbackground : data_type) : list int -> abstract_data_type =
+ fun k -> tensor_value_at k t cbackground
+
+ let ghost function tensor (t : ctensor) (cbackground : data_type) : tensor abstract_data_type
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_value t cbackground }
+ ensures { result.background = to_abstract cbackground }
+ (*qed*)
+ =
+ {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_value t cbackground } ;
+ background = to_abstract cbackground ;
+ }
+
+ let ctensor_create (ds : iarray) (n : int32) : ctensor =
+ (*proof*)
+ requires { dimension ds n }
+ ensures { empty_tensor result \/ valid_tensor result }
+ ensures { empty_tensor result \/ result.t_rank = n }
+ ensures { empty_tensor result \/ result.t_dims = ds }
+ (*qed*)
+ let m = cdim_size ds n in
+ let vs = malloc (to_uint32 m) in
+ {
+ t_rank = if is_null vs then 0 else n ;
+ t_dims = ds ;
+ t_data = vs ;
+ }
+
+
+ let ctensor_clear (r : ctensor) (cbackground : data_type) =
+ requires { valid_tensor r }
+ ensures { tensor r cbackground = Tensor.zero (to_abstract cbackground) (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> to_abstract (value_at r.t_data k) = to_abstract cbackground }
+ (*qed*)
+ r.t_data[i] <- cbackground
+ done
+ (*proof*)
+ ; assert { tensor r cbackground == Tensor.zero (to_abstract cbackground) (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_reset (r : ctensor) (v : data_type) (cbackground : data_type) =
+ requires { valid_tensor r }
+ ensures { tensor r cbackground == Tensor.const (to_abstract v) (to_abstract cbackground) (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = v }
+ (*qed*)
+ r.t_data[i] <- v
+ done
+ (*proof*)
+ ; assert { tensor r cbackground == Tensor.const (to_abstract v) (to_abstract cbackground) (tensor_dim r) }
+ (*qed*)
+
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml
new file mode 100644
index 00000000..f5b85d44
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml.bak
new file mode 100644
index 00000000..f5b85d44
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml.bak
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz
new file mode 100644
index 00000000..53882cc8
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz.bak
new file mode 100644
index 00000000..53882cc8
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout.mlw
new file mode 100644
index 00000000..fd7bcf7b
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout.mlw
@@ -0,0 +1,91 @@
+module CFlat
+ use std.Int
+ use std.List
+ use tensor.Range
+
+ function offset (ks ds : list int) : int =
+ match ks , ds with
+ | Cons k ks , Cons _ ds -> k * size ds + offset ks ds
+ | _ -> 0
+ end
+
+ function index (p : int) (ds : list int) : list int =
+ match ds with
+ | Nil -> Nil
+ | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds)
+ end
+
+ let rec lemma offset_size (ks ds : list int)
+ requires { valid ks ds }
+ ensures { 0 <= offset ks ds < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k krs , Cons d drs ->
+ offset_size krs drs ;
+ let p = offset ks ds in
+ let q = offset krs drs in
+ assert { p = size drs * k + q } ;
+ assert {
+ size drs * k
+ <= size drs * (d - 1)
+ = d * size drs - size drs
+ } ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma index_range (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { valid (index p ds) ds }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds -> index_range (mod p (size rds)) rds
+ end
+ (*qed*)
+
+ let rec lemma index_of_offset (ks ds : list int)
+ requires { valid ks ds }
+ ensures { index (offset ks ds) ds = ks }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons k rks , Cons _ rds ->
+ index_of_offset rks rds ;
+ let p = offset ks ds in
+ let n = size rds in
+ let q = offset rks rds in
+ euclide p k n q ;
+ | _ -> ()
+ end
+ (*qed*)
+
+ let rec lemma offset_of_index (p : int) (ds : list int)
+ requires { positive ds }
+ requires { 0 <= p < size ds }
+ ensures { offset (index p ds) ds = p }
+ (*proof*)
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons _ rds ->
+ let n = size rds in
+ offset_of_index (mod p n) rds
+ end
+ (*qed*)
+
+ let rec lemma offset_push (ks ds : list int) (k d : int)
+ requires { valid ks ds }
+ ensures { offset (push ks k) (push ds d) = offset ks ds * d + k }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Cons _ krs , Cons _ drs -> offset_push krs drs k d
+ | _ -> ()
+ end
+ (*qed*)
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/proof.json
new file mode 100644
index 00000000..e220781a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/proof.json
@@ -0,0 +1,12 @@
+{
+ "profile": [],
+ "proofs": {
+ "CFlat": {
+ "index_of_offset": null,
+ "index_range": null,
+ "offset_of_index": null,
+ "offset_push": null,
+ "offset_size": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml
new file mode 100644
index 00000000..4683ff19
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml.bak
new file mode 100644
index 00000000..4683ff19
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml.bak
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz
new file mode 100644
index 00000000..46e3585c
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz.bak
new file mode 100644
index 00000000..46e3585c
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.c
new file mode 100644
index 00000000..b587a3cb
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.c
@@ -0,0 +1,43 @@
+#include "cbooleantensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ int * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(int));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r, int cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = cbackground;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, int v, int cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.h
new file mode 100644
index 00000000..bfe1f9f0
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.h
@@ -0,0 +1,21 @@
+#ifndef CBOOLEANTENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ int * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r, int cbackground);
+
+void ctensor_reset(struct ctensor r, int v, int cbackground);
+
+#define CBOOLEANTENSOR_H_INCLUDED
+#endif // CBOOLEANTENSOR_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.o
new file mode 100644
index 00000000..c4887610
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.c
new file mode 100644
index 00000000..38d7e795
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.c
@@ -0,0 +1,43 @@
+#include "cfloattensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ double * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(double));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r, double cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = cbackground;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, double v, double cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.h
new file mode 100644
index 00000000..be29a6cb
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.h
@@ -0,0 +1,21 @@
+#ifndef CFLOATTENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ double * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r, double cbackground);
+
+void ctensor_reset(struct ctensor r, double v, double cbackground);
+
+#define CFLOATTENSOR_H_INCLUDED
+#endif // CFLOATTENSOR_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.o
new file mode 100644
index 00000000..4c45b456
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.c
new file mode 100644
index 00000000..6d82f3e4
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.c
@@ -0,0 +1,58 @@
+#include "cindex.h"
+
+int32_t cdim_size(int32_t * u, int32_t n) {
+ int32_t p;
+ int32_t i, o;
+ p = 1;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ p = p * u[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
+
+int32_t * cdim_create_1(int32_t n) {
+ int32_t * cd;
+ cd = malloc(1U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = n;
+ }
+ return cd;
+}
+
+int32_t * cdim_create_2(int32_t p, int32_t q) {
+ int32_t * cd;
+ cd = malloc(2U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = p;
+ cd[1] = q;
+ }
+ return cd;
+}
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) {
+ int32_t p;
+ int32_t i, o, d, k;
+ p = 0;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ d = ds[i];
+ k = ks[i];
+ if (0 <= k && k < d) {
+ p = p * d + k;
+ } else {
+ return -1;
+ }
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.h
new file mode 100644
index 00000000..f8c9d87c
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.h
@@ -0,0 +1,15 @@
+#ifndef CINDEX_H_INCLUDED
+
+#include
+#include
+
+int32_t cdim_size(int32_t * u, int32_t n);
+
+int32_t * cdim_create_1(int32_t n);
+
+int32_t * cdim_create_2(int32_t p, int32_t q);
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n);
+
+#define CINDEX_H_INCLUDED
+#endif // CINDEX_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.o
new file mode 100644
index 00000000..90634a76
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.c
new file mode 100644
index 00000000..35430cbf
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.c
@@ -0,0 +1,43 @@
+#include "cint32tensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ int32_t * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(int32_t));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r, int32_t cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = cbackground;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, int32_t v, int32_t cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.h
new file mode 100644
index 00000000..ad8b53b5
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.h
@@ -0,0 +1,21 @@
+#ifndef CINT32TENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ int32_t * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r, int32_t cbackground);
+
+void ctensor_reset(struct ctensor r, int32_t v, int32_t cbackground);
+
+#define CINT32TENSOR_H_INCLUDED
+#endif // CINT32TENSOR_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.o
new file mode 100644
index 00000000..1661c736
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.c
new file mode 100644
index 00000000..97df9d41
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.c
@@ -0,0 +1,43 @@
+#include "cint64tensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ int64_t * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(int64_t));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r, int64_t cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = cbackground;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, int64_t v, int64_t cbackground) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.h
new file mode 100644
index 00000000..90eff13c
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.h
@@ -0,0 +1,21 @@
+#ifndef CINT64TENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ int64_t * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r, int64_t cbackground);
+
+void ctensor_reset(struct ctensor r, int64_t v, int64_t cbackground);
+
+#define CINT64TENSOR_H_INCLUDED
+#endif // CINT64TENSOR_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.o
new file mode 100644
index 00000000..43cce2ac
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.c
new file mode 100644
index 00000000..8c7c9520
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.c
@@ -0,0 +1,58 @@
+#include "ctensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ double * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(double));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = ((double) 0.0);
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, double v) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b,
+ struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = ((double) 0.0) < cond.t_data[i] ? a.t_data[i] : b.t_data[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.h
new file mode 100644
index 00000000..a9da013a
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.h
@@ -0,0 +1,24 @@
+#ifndef CTENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ double * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r);
+
+void ctensor_reset(struct ctensor r, double v);
+
+void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b,
+ struct ctensor r);
+
+#define CTENSOR_H_INCLUDED
+#endif // CTENSOR_H_INCLUDED
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.o
new file mode 100644
index 00000000..7406bb9c
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.o differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor.mlw
new file mode 100644
index 00000000..a8322fac
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor.mlw
@@ -0,0 +1,151 @@
+module CTensor
+ use std.Int
+ use std.List
+ use std.Clib
+ use std.Cfloat
+ use mach.int.Int32
+ use tensor.Range
+ use tensor.Tensor
+ use tensor.OPWhere
+ use layout.CFlat
+ use libvector.CIndex
+
+ (** C Tensor **)
+
+ type farray = ptr float
+
+ type ctensor = {
+ t_rank : int32 ;
+ t_dims : iarray ;
+ t_data : farray ;
+ }
+
+ (** Dimension List **)
+ function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank
+ (** Number of entries **)
+ function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank
+ (** Valid index predicate **)
+ predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t)
+ (** Empty tensor predicate **)
+ predicate empty_tensor (t : ctensor) = t.t_rank = 0
+
+ (** Valid tensor **)
+ (**valid dimensions**)
+ (**valid_range [0, Number of entries] are valid indices**)
+ (**data writable??**)
+ predicate valid_tensor (t : ctensor) =
+ dimension t.t_dims t.t_rank /\
+ valid_range t.t_data 0 (tensor_size t) /\
+ writable t.t_data
+
+ (*Coordinate Index for ctensor*)
+ function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t)
+
+ function tensor_value_at (k : list int) (t : ctensor) : real =
+ if valid_index k t then
+ value_at t.t_data (tensor_offset k t)
+ else 0.0
+
+ function tensor_value (t : ctensor) : list int -> real =
+ fun k -> tensor_value_at k t
+
+ function tensor_valueb (t : ctensor) : list int -> bool =
+ fun k -> Real.(tensor_value_at k t > 0.0)
+
+ let ghost function tensor (t : ctensor) : tensor real
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_value t }
+ ensures { result.background = 0.0 }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_value t } ;
+ background = 0.0 ;
+ }
+
+ let ghost function tensorb (t : ctensor) : tensor bool
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_valueb t }
+ ensures { result.background = false }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_valueb t } ;
+ background = false ;
+ }
+
+ let ctensor_create (ds : iarray) (n : int32) : ctensor =
+ (*proof*)
+ requires { dimension ds n }
+ ensures { empty_tensor result \/ valid_tensor result }
+ ensures { empty_tensor result \/ result.t_rank = n }
+ ensures { empty_tensor result \/ result.t_dims = ds }
+ (*qed*)
+ let m = cdim_size ds n in
+ let vs = malloc (to_uint32 m) in
+ {
+ t_rank = if is_null vs then 0 else n ;
+ t_dims = ds ;
+ t_data = vs ;
+ }
+
+ let ctensor_clear (r : ctensor) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 }
+ (*qed*)
+ r.t_data[i] <- f32 0.0
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_reset (r : ctensor) (v : float) =
+ requires { valid_tensor r }
+ ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = v }
+ (*qed*)
+ r.t_data[i] <- v
+ done
+ (*proof*)
+ ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) }
+ (*qed*)
+
+ let ctensor_where (cond a b r : ctensor) =
+ (*proof*)
+ requires { valid_tensor cond }
+ requires { valid_tensor a }
+ requires { valid_tensor b }
+ requires { valid_tensor r }
+ requires { tensor a ~= tensor b ~= tensor r }
+ (*qed*)
+ requires { tensorb cond ~ tensor a ~ tensor b }
+ ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant {
+ forall k. 0 <= k < i ->
+ value_at r.t_data k =
+ if Real.(0.0 < value_at cond.t_data k)
+ then a.t_data[k] else b.t_data[k]
+ }
+ (*qed*)
+ r.t_data[i] <-
+ if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i]
+ done
+ (*proof*)
+ ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) }
+ (*qed*)
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/proof.json
new file mode 100644
index 00000000..e6f16a32
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/proof.json
@@ -0,0 +1,13 @@
+{
+ "profile": [],
+ "proofs": {
+ "CTensor": {
+ "ctensor_clear": null,
+ "ctensor_create": null,
+ "ctensor_reset": null,
+ "ctensor_where": null,
+ "tensor": null,
+ "tensorb": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml
new file mode 100644
index 00000000..7bb3ecf2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml.bak
new file mode 100644
index 00000000..7bb3ecf2
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml.bak
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz
new file mode 100644
index 00000000..89f716d3
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz.bak
new file mode 100644
index 00000000..89f716d3
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector.mlw
new file mode 100644
index 00000000..19e3903b
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector.mlw
@@ -0,0 +1,135 @@
+(** C-Library to compute Offsets & Dimensions *)
+
+module CIndex
+ use std.Int
+ use std.List
+ use std.Clib
+ use mach.int.Int32
+ use tensor.Range
+ use layout.CFlat
+
+ type iarray = ptr int32
+
+ function ivector (u : iarray) (n : int) : list int
+ = map Int32.to_int (vector u n)
+
+ function islice (u : iarray) (p q : int) : list int
+ = map Int32.to_int (slice u p q)
+
+ (** ## Dimensions *)
+
+ function vdim (u : iarray) (n : int) : int = size (ivector u n)
+ function sdim (u : iarray) (p q : int) : int = size (islice u p q)
+
+ predicate pdim (u : iarray) (p q : int) =
+ forall k. p <= k < q -> 0 < value_at u k
+
+ predicate dimension (u : iarray) (n : int) =
+ 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32
+
+ (** Equivalence betwen {pdim} and {tensor.Range.positive}. *)
+ let rec lemma positive_pdim (u : iarray) (p q : int)
+ (*proof*)
+ ensures { pdim u p q <-> positive (islice u p q) }
+ variant { q - p }
+ = if p < q then positive_pdim u (p+1) q
+ (*qed*)
+
+ let ghost sdim_split (u : iarray) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { pdim u p q }
+ ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q }
+ = assert { islice u p k ++ islice u k q = islice u p q }
+ (*qed*)
+
+ (** Extracted Library *)
+
+ let cdim_size (u : iarray) (n : int32): int32 =
+ ensures { result = vdim u n }
+ (*proof*)
+ requires { dimension u n }
+ (*qed*)
+ begin
+ let ref p = 1 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { p = vdim u i }
+ ghost
+ begin
+ ensures { 1 <= p * u[i] <= max_int32 }
+ let ghost dl = pure { sdim u 0 i } in
+ let ghost di = Int32.to_int u[i] in
+ let ghost dr = pure { sdim u (i+1) n } in
+ let ghost dn = pure { sdim u 0 n } in
+ sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * u[i] ;
+ done ; p
+ end
+
+ let cdim_create_1 (n : int32) : iarray =
+ requires { 0 < n }
+ ensures { is_not_null result -> vdim result 1 = n }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 1 }
+ ensures { is_not_null result -> value_at result 0 = n }
+ (*qed*)
+ let cd = malloc 1 in
+ if is_not_null cd then cd[0] <- n ; cd
+
+ let cdim_create_2 (p q : int32) : iarray =
+ requires { 0 < p /\ 0 < q /\ p * q <= max_int32 }
+ ensures { is_not_null result -> vdim result 2 = p * q }
+ (*proof*)
+ ensures { is_not_null result -> dimension result 2 }
+ ensures { is_not_null result -> value_at result 0 = p }
+ ensures { is_not_null result -> value_at result 1 = q }
+ (*qed*)
+ let cd = malloc 2 in
+ if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd
+
+ (** ## Coordinates *)
+
+ let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 =
+ (*proof*)
+ requires { 0 <= n }
+ requires { valid_range ks 0 n }
+ requires { dimension ds n }
+ ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) }
+ ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) }
+ (*qed*)
+ begin
+ let ref p = 0 in
+ for i = 0 to n - 1 do
+ (*proof*)
+ invariant { valid (ivector ks i) (ivector ds i) }
+ invariant { p = offset (ivector ks i) (ivector ds i) }
+ (*qed*)
+ let d = ds[i] in
+ let k = ks[i] in
+ if 0 <= k && k < d then
+ begin
+ (*proof*)
+ assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ;
+ ghost
+ begin
+ ensures { Int32.in_bounds (p * d) }
+ ensures { Int32.in_bounds (p * d + k) }
+ let ghost dl = pure { sdim ds 0 i } in
+ let ghost di = Int32.to_int ds[i] in
+ let ghost dr = pure { sdim ds (i+1) n } in
+ let ghost dn = pure { sdim ds 0 n } in
+ sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ;
+ mult_bound (dl * di) dr dn ;
+ end ;
+ (*qed*)
+ p <- p * d + k ;
+ end
+ else return (-1)
+ done ; p
+ end
+
+end
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/proof.json
new file mode 100644
index 00000000..94eecafd
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/proof.json
@@ -0,0 +1,13 @@
+{
+ "profile": [],
+ "proofs": {
+ "CIndex": {
+ "cdim_create_1": null,
+ "cdim_create_2": null,
+ "cdim_size": null,
+ "coffset": null,
+ "positive_pdim": null,
+ "sdim_split": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml
new file mode 100644
index 00000000..ac5cc189
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml
@@ -0,0 +1,461 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml.bak
new file mode 100644
index 00000000..ac5cc189
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml.bak
@@ -0,0 +1,461 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz
new file mode 100644
index 00000000..b7e55530
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz.bak
new file mode 100644
index 00000000..b7e55530
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor.mlw
new file mode 100644
index 00000000..08052637
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor.mlw
@@ -0,0 +1,224 @@
+module CInt32Tensor
+
+ use mach.int.Int32
+ use int.Int
+ use list.List
+ use tensor.Tensor
+
+ constant zero : int32 = 0
+ clone export generictensor.GenericTensor with
+ type data_type = int32,
+ type abstract_data_type = int,
+ function to_abstract = Int32.to_int
+
+ type ctensorint32 = ctensor
+
+end
+
+module CInt64Tensor
+
+ use mach.int.Int64
+ use int.Int
+ use list.List
+ use tensor.Tensor
+
+ constant zero : int64 = 0
+ clone export generictensor.GenericTensor with
+ type data_type = int64,
+ type abstract_data_type = int,
+ function to_abstract = Int64.to_int
+
+ type ctensorint64 = ctensor
+
+end
+
+module CBooleanTensor
+
+ use int.Int
+ use list.List
+ use tensor.Tensor
+
+ function id (b : bool) : bool = b
+
+ constant zero : bool = false
+ clone export generictensor.GenericTensor with
+ type data_type = bool,
+ type abstract_data_type = bool,
+ function to_abstract = id
+
+ type ctensorbool = ctensor
+
+end
+
+
+
+module CFloatTensor
+
+ use std.Cfloat
+ use real.Real
+ use std.Clib
+ use list.List
+ use tensor.Tensor
+
+ constant zero : float = Cfloat.zero
+ clone export generictensor.GenericTensor with
+ type data_type = float,
+ type abstract_data_type = real,
+ function to_abstract = Cfloat.to_real
+
+ type ctensorfloat = ctensor
+
+ (*Remove this?*)
+ function tensor_valueb (t : ctensorfloat) : list int -> bool =
+ fun k -> Real.(tensor_value_at k t Cfloat.zero > 0.0)
+
+ let ghost function tensorb (t : ctensorfloat) : tensor bool
+ (*proof*)
+ requires { valid_tensor t }
+ ensures { result.dims = tensor_dim t }
+ ensures { result.data = tensor_valueb t }
+ ensures { result.background = false }
+ (*qed*)
+ = {
+ dims = pure { tensor_dim t } ;
+ data = pure { tensor_valueb t } ;
+ background = false ;
+ }
+
+end
+
+
+module CWhere1
+
+ use CFloatTensor
+ use CBooleanTensor
+ use libvector.CIndex
+ use mach.int.Int32
+ use std.Cfloat
+ use std.Clib
+ use int.Int
+ use tensor.OPWhere
+ use tensor.Tensor
+
+
+ let ctensor_where (cond: ctensorbool) (a b r : ctensorfloat) =
+ (*proof*)
+ requires { CBooleanTensor.valid_tensor cond }
+ requires { CFloatTensor.valid_tensor a }
+ requires { CFloatTensor.valid_tensor b }
+ requires { CFloatTensor.valid_tensor r }
+ requires { CFloatTensor.tensor a Cfloat.zero ~= CFloatTensor.tensor b Cfloat.zero ~= CFloatTensor.tensor r Cfloat.zero }
+ (*qed*)
+ requires { CBooleanTensor.tensor cond CBooleanTensor.zero ~ CFloatTensor.tensor a Cfloat.zero ~ CFloatTensor.tensor b Cfloat.zero }
+ ensures { CFloatTensor.tensor r Cfloat.zero = opwhere (CBooleanTensor.tensor cond CBooleanTensor.zero)
+ (CFloatTensor.tensor a Cfloat.zero) (CFloatTensor.tensor b Cfloat.zero) }
+ let m = cdim_size r.CFloatTensor.t_dims r.CFloatTensor.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant {
+ forall k. 0 <= k < i ->
+ value_at r.CFloatTensor.t_data k =
+ if value_at cond.CBooleanTensor.t_data k
+ then a.CFloatTensor.t_data[k] else b.CFloatTensor.t_data[k]
+ }
+ (*qed*)
+ r.CFloatTensor.t_data[i] <-
+ if cond.CBooleanTensor.t_data[i] then a.CFloatTensor.t_data[i] else b.CFloatTensor.t_data[i]
+ done
+ (*proof*)
+ ; assert { CFloatTensor.tensor r Cfloat.zero == opwhere (CBooleanTensor.tensor cond CBooleanTensor.zero) (CFloatTensor.tensor a Cfloat.zero)
+ (CFloatTensor.tensor b Cfloat.zero) }
+ (*qed*)
+
+
+end
+
+
+module CWhere
+
+ use CFloatTensor
+ use libvector.CIndex
+ use mach.int.Int32
+ use std.Cfloat
+ use std.Clib
+ use int.Int
+ use tensor.OPWhere
+ use tensor.Tensor
+
+
+ let ctensor_where (cond a b r : ctensorfloat) =
+ (*proof*)
+ requires { valid_tensor cond }
+ requires { valid_tensor a }
+ requires { valid_tensor b }
+ requires { valid_tensor r }
+ requires { tensor a Cfloat.zero ~= tensor b Cfloat.zero ~= tensor r Cfloat.zero }
+ (*qed*)
+ requires { tensorb cond ~ tensor a Cfloat.zero ~ tensor b Cfloat.zero }
+ ensures { tensor r Cfloat.zero = opwhere (tensorb cond) (tensor a Cfloat.zero) (tensor b Cfloat.zero) }
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant {
+ forall k. 0 <= k < i ->
+ value_at r.t_data k =
+ if Real.(0.0 < value_at cond.t_data k)
+ then a.t_data[k] else b.t_data[k]
+ }
+ (*qed*)
+ r.t_data[i] <-
+ if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i]
+ done
+ (*proof*)
+ ; assert { tensor r Cfloat.zero == opwhere (tensorb cond) (tensor a Cfloat.zero) (tensor b Cfloat.zero) }
+ (*qed*)
+
+
+end
+
+
+
+
+module Test
+
+ (* =============================== *)
+ (* Demonstration Module - Important *)
+ (* This module is not intended for proofs. *)
+ (* Its purpose is to illustrate best practices when working with multiple cloned datatypes from the generic tensor module. *)
+ (* In particular, it shows how to properly prefix variables and functions to avoid ambiguity
+ and ensure clarity when using two or more different tensor types in the same context. *)
+ (* =============================== *)
+
+ use generictensor.GenericTensor
+ use CInt32Tensor
+ use CInt64Tensor
+ use tensor.Tensor
+ use libvector.CIndex
+ use mach.int.Int32
+ use mach.int.Int64
+ use std.Clib
+ use int.Int
+
+ let ctensor_where (cond : ctensorint32) (a b r : ctensorint64) (b64 : int64) (b32 : int32) =
+ requires { CInt32Tensor.valid_tensor cond }
+ requires { CInt64Tensor.valid_tensor a }
+ requires { CInt64Tensor.valid_tensor b }
+ requires { CInt64Tensor.valid_tensor r }
+ requires { CInt64Tensor.tensor a b64 ~= CInt64Tensor.tensor b b64 ~= CInt64Tensor.tensor r b64 }
+ requires { CInt32Tensor.tensor cond b32 ~ CInt64Tensor.tensor a b64 ~ CInt64Tensor.tensor b b64 }
+ let m = cdim_size (CInt32Tensor.t_dims cond) (CInt32Tensor.t_rank cond) in
+ for i = 0 to m-1 do
+ (*proof*)
+ invariant { forall k. 0 <= k < i ->
+ value_at (CInt64Tensor.t_data r) k =
+ if 0 < value_at (CInt32Tensor.t_data cond) k then
+ value_at (CInt64Tensor.t_data a) k
+ else
+ value_at (CInt64Tensor.t_data b) k
+ }
+ (*qed*)
+ (CInt64Tensor.t_data r)[i] <- if 0 < (CInt32Tensor.t_data cond)[i] then (CInt64Tensor.t_data a)[i] else (CInt64Tensor.t_data b)[i]
+ done;
+
+
+end
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml
new file mode 100644
index 00000000..c26c3066
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml.bak
new file mode 100644
index 00000000..c26c3066
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml.bak
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz
new file mode 100644
index 00000000..f98b6f5d
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz.bak
new file mode 100644
index 00000000..f98b6f5d
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std.mlw
new file mode 100644
index 00000000..244ee393
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std.mlw
@@ -0,0 +1,234 @@
+(* Extensions to Standard Library *)
+
+module Int
+ use export int.Int
+ use export int.Abs
+ use export int.ComputerDivision
+ use list.List
+ use list.Length
+
+ (** Unicity of euclidian division decomposition *)
+ let ghost euclide (a b q r : int)
+ requires { 0 <= a /\ 0 <= r < q }
+ requires { a = b * q + r }
+ ensures { b = div a q }
+ ensures { r = mod a q }
+ (*proof*)
+ = let rec kernel (b r : int)
+ requires { b * q + r = 0 }
+ requires { abs r < q }
+ ensures { b = r = 0 }
+ variant { abs b }
+ = if b < 0 then kernel (b + 1) (r - q) else
+ if b > 0 then kernel (b - 1) (r + q) else ()
+ in kernel (b - div a q) (r - mod a q)
+ (*qed*)
+
+ (** Multiplication upper-bound *)
+ let ghost mult_bound (a b c : int)
+ requires { 0 < a * b <= c }
+ ensures { abs a <= c }
+ ensures { abs b <= c }
+ = ()
+
+ (** Helper function to extract element from list at given position *)
+ let rec function get_dim (dims : list int) (idx : int) : int
+ requires { 0 <= idx < length dims }
+ variant { dims }
+ = match dims with
+ | Nil -> 0 (* should not happen due to precondition *)
+ | Cons h t -> if idx = 0 then h else get_dim t (idx - 1)
+ end
+
+end
+
+module List
+ use export list.List
+ use export list.Map
+ use export list.Append
+
+ (** Appends an element to the end of the list *)
+ function push (xs : list 'a) (x : 'a) : list 'a =
+ xs ++ Cons x Nil
+
+ let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a)
+ ensures { map f (xs ++ ys) = map f xs ++ map f ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end
+ (*qed*)
+
+end
+
+module Clib
+ use map.Map
+ use int.Int
+ use mach.int.Int32
+ use export mach.c.C
+
+ type int32 = Int32.int32
+ type uint32 = UInt32.uint32
+
+ val function to_uint32 (v : int32) : uint32
+ requires { 0 <= v }
+ ensures { Int32.to_int v = UInt32.to_int result }
+
+ (** Null-Pointer *)
+ val predicate is_null (vp : ptr 'a) : bool
+ ensures { result <-> vp.zone = null_zone }
+
+ (** Array-range validity *)
+ predicate valid_range (vp : ptr 'a) (p q : int) =
+ q <= p \/
+ ( 0 <= vp.min <= vp.offset /\
+ 0 <= p <= q <= max_int32 /\
+ 0 <= vp.min <= vp.offset + p /\
+ vp.offset + q <= vp.max <= vp.plength )
+
+ let lemma valid_in_range (vp : ptr 'a) (p k q : int)
+ (*proof*)
+ requires { p <= k < q }
+ requires { valid_range vp p q }
+ ensures { valid_ptr_shift vp k }
+ = ()
+ (*qed*)
+
+ (** Array as map *)
+ let ghost function value_at (vp : ptr 'a) : map int 'a =
+ pure { fun k -> vp.data.Array.elts (vp.offset + k) }
+
+ (** Array access (ghost) *)
+ function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k
+
+ (** Array access (C, inlined) *)
+ let ([]) (vp : ptr 'a) (k : int32) : 'a
+ (*proof*)
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { result = vp[k] }
+ = get_ofs vp k
+ (*qed*)
+
+ (** Array update (C, inlined) *)
+ let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit
+ requires { writable vp }
+ requires { vp.min <= vp.offset + k < vp.max }
+ ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v }
+ = set_ofs vp k v
+
+ (** Array as list *)
+ use List
+
+ let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a =
+ (*proof*)
+ variant { q - p }
+ (*qed*)
+ if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q)
+
+ let rec lemma slice_append (u : ptr 'a) (p q r : int)
+ requires { p <= q <= r }
+ ensures { slice u p r = slice u p q ++ slice u q r }
+ (*proof*)
+ variant { q - p }
+ = if p < q then slice_append u (p+1) q r
+ (*qed*)
+
+ let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n
+
+ let lemma vector_push (u : ptr 'a) (n : int)
+ requires { 0 <= n }
+ ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+ (*proof*)
+ = slice_append u 0 n (n+1)
+ (*qed*)
+
+val function malloc_int32 (s: uint32) : ptr int32
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+end
+
+
+module Cint
+ use int.Int
+ use mach.int.Int32
+ use mach.int.Int32 as Int32
+ use mach.int.UInt32
+ use mach.int.UInt32 as UInt32
+ use int.ComputerDivision
+ use Clib
+
+ val function ( .+ ) (a b : int) : int
+ ensures { result = a + b }
+
+ val function ( .- ) (a b : int) : int
+ ensures { result = a - b }
+
+ val function ( .* ) (a b : int) : int
+ ensures { result = a * b }
+
+ val function ( ./ ) (a b : int) : int
+ requires { b <> 0 }
+ ensures { result = ComputerDivision.div (a) ( b) }
+
+ val function of_int (v : int) : Int32.int32
+ ensures { Int32.to_int result = v }
+
+ val function malloc_int (s: uint32) : ptr int
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+ val function get (p: ptr int) (k: Int32.int32) : int
+ ensures { result = p[k] }
+
+ val function malloc_int32 (s: uint32) : ptr int32
+ ensures { is_null result || valid_range result 0 (UInt32.to_int s) }
+
+ let ([]) (p : ptr int) (k :Int32.int32) : int = get p k
+
+end
+
+module Cfloat
+ use int.Int
+ use real.Real
+ use real.FromInt
+
+ type float = abstract { to_real : real }
+ meta coercion function to_real
+
+ type f32 = < float 8 24 > (* single precision literals *)
+ type f64 = < float 11 53 > (* double precision literals *)
+
+
+ val function f32 (k : f32) : float
+ ensures { result = f32'real k }
+
+ val function f64 (k : f64) : float
+ ensures { result = f64'real k }
+
+ let constant zero = f64 0.0
+ let constant one = f64 1.0
+
+ val function (.+) (a b : float) : float
+ ensures { Real.( result = a + b ) }
+
+ val function (.-) (a b : float) : float
+ ensures { Real.( result = a - b ) }
+
+ val function (.-_) (a : float) : float
+ ensures { Real.( result = (- a) ) }
+
+ val function ( .* ) (a b : float) : float
+ ensures { Real.( result = a * b ) }
+
+ val function ( ./ ) (a b : float) : float
+ requires { b <> 0.0 }
+ ensures { Real.( result = a / b ) }
+
+ val predicate ( .= ) (a b : float) : bool
+ ensures { result <-> Real.( a = b ) }
+
+ val predicate ( .< ) (a b : float) : bool
+ ensures { result <-> Real.( a < b ) }
+
+ val predicate ( .<= ) (a b : float) : bool
+ ensures { result <-> Real.( a <= b ) }
+
+end
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/proof.json
new file mode 100644
index 00000000..636ea4c6
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/proof.json
@@ -0,0 +1,20 @@
+{
+ "profile": [],
+ "proofs": {
+ "Cfloat": {
+ "one": { "tactic": "split_vc", "children": [] },
+ "zero": { "tactic": "split_vc", "children": [] }
+ },
+ "Cint": { "mixfix []": { "tactic": "split_vc", "children": [] } },
+ "Clib": {
+ "mixfix []": null,
+ "mixfix []<-": null,
+ "slice": null,
+ "slice_append": null,
+ "valid_in_range": null,
+ "vector_push": null
+ },
+ "Int": { "euclide": null, "get_dim": null, "mult_bound": null },
+ "List": { "map_concat": null }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml
new file mode 100644
index 00000000..438f2e62
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml
@@ -0,0 +1,296 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml.bak
new file mode 100644
index 00000000..cba4785d
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml.bak
@@ -0,0 +1,244 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz
new file mode 100644
index 00000000..38e5086b
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz.bak
new file mode 100644
index 00000000..d99d9940
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor.mlw
new file mode 100644
index 00000000..7001ccdf
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor.mlw
@@ -0,0 +1,778 @@
+(** Formalization of coordinates and dimensions *)
+
+module Range
+ use int.Int
+ use std.List
+
+ function size (ds : list int) : int =
+ match ds with
+ | Nil -> 1
+ | Cons d ds -> d * size ds
+ end
+
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+
+ let rec lemma valid_push (ks ds : list int) (k d : int)
+ requires { 0 <= k < d }
+ requires { valid ks ds }
+ ensures { valid (push ks k) (push ds d) }
+ (*proof*)
+ variant { ks }
+ = match ks , ds with
+ | Nil , Nil -> ()
+ | Cons _ ks , Cons _ ds -> valid_push ks ds k d
+ | _ -> absurd
+ end
+ (*qed*)
+
+ let rec lemma size_append (xs ys : list int)
+ ensures { size (xs ++ ys) = size xs * size ys }
+ (*proof*)
+ variant { xs }
+ = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end
+ (*qed*)
+
+ lemma size_push: forall xs x. size (push xs x) = size xs * x
+
+ let rec lemma positive_size (ds : list int)
+ requires { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ds with Nil -> () | Cons _ ds -> positive_size ds end
+ (*qed*)
+
+ let rec lemma positive_valid (ks ds : list int)
+ requires { valid ks ds }
+ ensures { positive ds }
+ ensures { 0 < size ds }
+ (*proof*)
+ variant { ds }
+ = match ks , ds with
+ | Cons _ ks , Cons _ ds -> positive_valid ks ds
+ | _ -> ()
+ end
+ (*qed*)
+
+end
+
+(** Formalization of Tensor *)
+module Tensor
+ use int.Int
+ use map.Map
+ use list.List
+ use Range
+
+ type data 'a = map (list int) 'a
+
+ type tensor 'a = {
+ dims : list int ;
+ data : data 'a ;
+ background : 'a ; (* default value, or value for 0-dimensions tensor *)
+ }
+
+ invariant { positive dims }
+ invariant { forall k. valid k dims \/ data k = background }
+ (*proof*)
+ by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+ (*qed*)
+
+ meta coercion function dims
+ meta coercion function data
+
+ (** Tensor with the same dimensions *)
+ predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims
+
+ (** Tensor with the same dimensions and background value *)
+ predicate (~=) (a : tensor 'a) (b : tensor 'a) =
+ a ~ b /\ a.background = b.background
+
+ (** Equal tensors *)
+ predicate (==) (a b : tensor 'a) =
+ a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k
+
+ (** Extensionality *)
+ lemma exteq: forall a b : tensor 'a. a == b <-> a = b
+ (*proof*) by tensor'eq a b (*qed*)
+
+ (** Scalar Tensor *)
+ let ghost function scalar (v : 'a) : tensor 'a
+ ensures { result.dims = Nil }
+ ensures { result.background = v }
+ ensures { forall k. result k = v }
+ (*proof*)
+ = { dims = Nil ; data = (fun _ -> v) ; background = v }
+ (*qed*)
+
+ (** Null Tensor *)
+ let ghost function zero (e : 'a) (ds : list int) : tensor 'a
+ requires { positive ds }
+ ensures { result.dims = ds }
+ ensures { result.background = e }
+ ensures { forall k. result k = e }
+ (*proof*)
+ = { dims = ds ; data = (fun _ -> e) ; background = e }
+ (*qed*)
+
+
+ (** Constant Tensor *)
+ let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a
+ ensures { result.dims = ds }
+ requires { positive ds }
+ ensures { result.background = bg }
+ ensures { forall k. valid k ds -> result k = v }
+ (*proof*)
+ = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg }
+ (*qed*)
+
+ (** Constant & Null *)
+ goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds
+
+end
+
+(** OP-Where Tensor Operation *)
+module OPWhere
+ use Tensor
+
+ let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+
+end
+
+(*
+module OPSlice
+ use Tensor
+ use Range
+ use int.Int
+ use int.EuclideanDivision
+ use list.List
+ use list.Length
+ use list.Nth
+ use option.Option
+ use list.Map
+ use list.Mem
+
+ (*Notas*)
+ (*Possivelmente remover os contratos porque a função é pura*)
+ (*As pre condições podem ser necessarias. Pos podem ser removidas à partida*)
+ (*MUITO IMPORTANTE: usamos um axioma para ajudar com a sobrejetividade mas isto pode ser problemático.*)
+ function get (o: option int): int =
+ match o with
+ | Some v -> v
+ | None -> -1
+ end
+
+ function my_nth (l: list int) (i: int): int =
+ match nth i l with
+ | Some d -> d
+ | None -> -1
+ end
+
+
+ function head (l: list int): int =
+ match l with
+ | Cons d _ -> d
+ | Nil -> -1
+ end
+
+
+ let ghost function normalize_axis (dims: list int) (axis_dim : list int) (axis: tensor int): tensor int
+ (*1D Index Tensors*)
+ requires { length axis.dims = 1 }
+ (*X [C1]*)
+ requires { length dims = size axis.dims }
+ requires { axis.dims = axis_dim }
+ (*A [C2]*)
+ requires { forall ks. valid ks axis_dim -> -length dims <= axis.data ks <= length dims - 1}
+ (*A [C3]*)
+ requires { forall ks1 ks2. valid ks1 axis_dim /\ valid ks2 axis_dim ->
+ (mod ((axis.data ks1) + length dims) (length dims)) = (mod ((axis.data ks2) + length dims) (length dims)) -> ks1 = ks2 }
+ (*Rank > 0, see this better*)
+ requires { length dims > 0 }
+ (*Normalization*)
+ ensures { forall ks. valid ks axis_dim -> 0 <= result.data ks < length dims }
+ ensures {forall ks. valid ks axis_dim -> axis.data ks < 0 -> result.data ks = axis.data ks + length dims }
+ ensures {forall ks. valid ks axis_dim -> axis.data ks >= 0 -> result.data ks = axis.data ks }
+ (*Index Uniqueness*)
+ ensures { forall ks1 ks2. valid ks1 axis_dim /\ valid ks2 axis_dim -> result.data ks1 = result.data ks2 -> ks1 = ks2 }
+ (*Same dimension, Same background*)
+ ensures { result ~= axis}
+ (*proof*)
+ = let data = fun ks ->
+ if valid ks axis_dim then
+ let a = axis.data ks in
+ if a < 0 then a + length dims else a
+ else axis.background
+ in { dims = axis_dim ; data = data ; background = axis.background }
+
+ let ghost function normalize_start (dims: list int) (start_dims: list int) (start axis : tensor int): tensor int
+ (*Additionally*)
+ requires { start_dims = start.dims }
+ (*1D Index Tensors*)
+ requires { length start.dims = length axis.dims = 1 }
+ (*X [C1]*)
+ requires { length dims = size start.dims = size axis.dims }
+ (*Redundant with 2 previous requires*)
+ requires { start ~ axis }
+ (*A [C2]*)
+ requires { forall ks. valid ks axis.dims -> 0 <= axis.data ks <= length dims -1}
+ (*A [C3]*)
+ requires { forall ks1 ks2. valid ks1 axis.dims /\ valid ks2 axis.dims ->
+ (mod (axis.data ks1 + length dims) (length dims)) = (mod (axis.data ks2 + length dims) (length dims)) -> ks1 = ks2 }
+ (*S [C2]*)
+ requires { forall ks. valid ks start.dims -> -my_nth dims (axis.data ks) <= start.data ks <= my_nth dims (axis.data ks) - 1 }
+ (*Rank > 0, see this better*)
+ requires { length dims > 0 }
+ (*Normalization*)
+ ensures { forall ks. valid ks start.dims -> 0 <= result.data ks < my_nth dims (axis.data ks) }
+ ensures { forall ks. valid ks start.dims -> start.data ks < 0 -> result.data ks = start.data ks + my_nth dims (axis.data ks) }
+ ensures { forall ks. valid ks start.dims -> start.data ks >= 0 -> result.data ks = start.data ks }
+ (*Same dimension, Same background*)
+ ensures { result ~= start }
+ (*proof*)
+ = let data = fun ks ->
+ if valid ks start_dims then
+ let s = start.data ks in
+ let axis_index = axis.data ks in
+ if s < 0 then s + my_nth dims axis_index else s
+ else start.background
+ in { dims = start_dims ; data = data ; background = start.background }
+
+ let ghost function normalize_end (dims: list int) (ends_dims: list int) (end_ axis steps: tensor int): tensor int
+ (*Additionally*)
+ requires { ends_dims = end_.dims }
+ (*1D Index Tensors*)
+ requires { length end_.dims = length axis.dims = length steps.dims = 1 }
+ (**X [C1]*)
+ requires { length dims = size end_.dims = size axis.dims = size steps.dims }
+ (*Redundant with previous requires*)
+ requires { end_ ~ axis ~ steps }
+ (*A [C2]*)
+ requires { forall ks. valid ks axis.dims -> 0 <= axis.data ks <= length dims -1}
+ (*A [C3]*)
+ requires { forall ks1 ks2. valid ks1 axis.dims /\ valid ks2 axis.dims ->
+ (mod (axis.data ks1 + length dims) (length dims)) = (mod (axis.data ks2 + length dims) (length dims)) -> ks1 = ks2 }
+ (*K [C2]*)
+ requires { forall ks. valid ks steps.dims -> steps.data ks <> 0 }
+ (*E [C2]*)
+ requires { forall ks. valid ks end_.dims -> steps.data ks > 0 ->
+ let idx = my_nth dims (axis.data ks) in
+ -(idx) <= end_.data ks <= idx }
+ (*E [C3]*)
+ requires { forall ks. valid ks end_.dims -> steps.data ks < 0 ->
+ let idx = my_nth dims (axis.data ks) in
+ -(idx) - 1 <= end_.data ks <= idx - 1 }
+ (*Rank > 0, see this better*)
+ requires { length dims > 0 }
+ (**Normalization*)
+ ensures { forall ks. valid ks end_.dims ->
+ let idx = my_nth dims (axis.data ks) in
+ let step = steps.data ks in
+ let e = end_.data ks in
+ let res = if e < 0 then e + idx else e in
+ (step > 0 -> 0 <= res <= idx) /\
+ (step < 0 -> -1 <= res <= idx - 1)
+ }
+ ensures { forall ks. valid ks end_.dims ->
+ let idx = my_nth dims (axis.data ks) in
+ let e = end_.data ks in
+ result.data ks = (if e < 0 then e + idx else e)
+ }
+ (*Same dimension, Same background*)
+ ensures { result ~= end_ }
+ (*proof*)
+ = let data = fun ks ->
+ if valid ks ends_dims then
+ let e = end_.data ks in
+ let axis_index = axis.data ks in
+ if e < 0 then e + my_nth dims axis_index else e
+ else end_.background
+ in { dims = ends_dims ; data = data ; background = end_.background }
+
+
+ let rec ghost function dY_shape_rec (x:tensor 'a) (s e k a: tensor int) (t: list int) (i: int) : list int
+ (*1D Index Tensors*)
+ requires { length s.dims = length e.dims = length k.dims = length a.dims = 1 }
+ (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*)
+ requires { length x.dims > 0}
+ (*X [C1]*)
+ requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims = length t }
+ (*Redudant with 2 previous requires*)
+ requires { s ~ e ~ k ~ a }
+ (*Normalize Axis*)
+ requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims }
+ (*Index Uniqueness*)
+ requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 }
+ (*Normalize Start*)
+ requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) }
+ (*Normalize End*)
+ requires { forall ks. valid ks e.dims ->
+ let idx = my_nth x.dims (a.data ks) in
+ let step = k.data ks in
+ let e_value = e.data ks in
+ let res = if e_value < 0 then e_value + idx else e_value in
+ (step > 0 -> 0 <= res <= idx) /\
+ (step < 0 -> -1 <= res <= idx - 1)
+ }
+ (*K [C2]*)
+ (*requires { forall ks. valid ks k.dims -> k.data ks <> 0 }*)
+ requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 }
+ (*Real Index t*)
+ requires { forall h. 0 <= h < length x.dims -> mem h t }
+ (*R6*)
+ requires { forall ks. valid ks s.dims -> k.data ks > 0 -> s.data ks <= e.data ks }
+ (*R7*)
+ requires { forall ks. valid ks s.dims -> k.data ks < 0 -> s.data ks >= e.data ks }
+ (*Y [C2]*)
+ requires {
+ forall z. 0 <= z < length x.dims ->
+ let real_index = my_nth t z in
+ let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in
+ let step = k.data (Cons real_index Nil) in
+ div space step + (if mod space step = 0 then 0 else 1) > 0
+ }
+ requires { 0 <= i < length x.dims }
+ variant { length x.dims - i }
+ ensures { length result = length x.dims - i }
+ ensures { forall z. 0 <= z < length result -> my_nth result z > 0 }
+ = if i < (length x.dims - 1) then
+ let real_index = my_nth t i in
+ let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in
+ let f = if (mod space (k.data (Cons real_index Nil))) = 0 then 0 else 1 in
+ let y = div space (k.data (Cons real_index Nil)) + f in
+ Cons y (dY_shape_rec x s e k a t (i+1))
+ else
+ let real_index = my_nth t i in
+ let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in
+ let f = if (mod space (k.data (Cons real_index Nil))) = 0 then 0 else 1 in
+ let y = div space (k.data (Cons real_index Nil)) + f in
+ Cons y Nil
+
+
+ let ghost function dY_shape (x : tensor 'a) (s e k a: tensor int) (t: list int) : list int
+ (*1D Index Tensors*)
+ requires { length s.dims = length e.dims = length k.dims = length a.dims = 1 }
+ (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*)
+ requires { length x.dims > 0}
+ (*X [C1]*)
+ requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims = length t }
+ (*Redudant with 2 previous requires*)
+ requires { s ~ e ~ k ~ a }
+ (*Normalize Axis*)
+ requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims }
+ (*Index Uniqueness*)
+ requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 }
+ (*Normalize Start*)
+ requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) }
+ (*Normalize End*)
+ requires { forall ks. valid ks e.dims ->
+ let idx = my_nth x.dims (a.data ks) in
+ let step = k.data ks in
+ let e_value = e.data ks in
+ let res = if e_value < 0 then e_value + idx else e_value in
+ (step > 0 -> 0 <= res <= idx) /\
+ (step < 0 -> -1 <= res <= idx - 1)
+ }
+ (*K [C2]*)
+ requires { forall ks. valid ks k.dims -> k.data ks <> 0 }
+ requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 }
+ (*Real Index t*)
+ requires { forall h. 0 <= h < length x.dims -> mem h t }
+ (*R6*)
+ requires { forall ks. valid ks s.dims -> k.data ks > 0 -> s.data ks <= e.data ks }
+ (*R7*)
+ requires { forall ks. valid ks s.dims -> k.data ks < 0 -> s.data ks >= e.data ks }
+ (*Y [C2]*)
+ requires {
+ forall z. 0 <= z < length x.dims ->
+ let real_index = my_nth t z in
+ let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in
+ let step = k.data (Cons real_index Nil) in
+ div space step + (if mod space step = 0 then 0 else 1) > 0
+ }
+ ensures { length result = length x.dims }
+ ensures { forall z. 0 <= z < length result -> my_nth result z > 0 }
+ = dY_shape_rec x s e k a t 0
+
+
+ (*Given an axis, find the index of that specific axis representation in axis tensor*)
+ (*axis -> tensor normalized*)
+ (*x_rank -> rank of input X*)
+ (*i -> looking value*)
+ (*j -> current searching index*)
+ let rec ghost function find_j (axis: tensor int) (x_rank: int) (i: int) (j: int) : int
+ (*1D Index Tensors*)
+ requires { length axis.dims = 1 }
+ (*X [C1]*)
+ requires { x_rank = size axis.dims }
+ (*Rank > 0, see this better*)
+ requires { x_rank > 0 }
+ (*A [C2]*)
+ requires { forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank -1}
+ (*A [C3]*)
+ requires { forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank ->
+ (mod (axis.data (Cons z1 Nil) + x_rank) (x_rank)) = (mod (axis.data (Cons z2 Nil) + x_rank) (x_rank)) -> z1 = z2 }
+ requires { 0 <= i < x_rank }
+ requires { 0 <= j < x_rank }
+ requires { forall h. 0 <= h < j -> axis.data (Cons h Nil) <> i }
+ requires { exists z. 0 <= z < x_rank /\ axis.data (Cons z Nil) = i }
+ ensures { 0 <= result < x_rank }
+ ensures { axis.data (Cons result Nil) = i }
+ variant { x_rank - j }
+ = if j < (x_rank - 1) then
+ if axis.data (Cons j Nil) = i then
+ j
+ else find_j axis x_rank i (j+1)
+ else
+ if axis.data (Cons j Nil) = i then
+ j
+ else
+ absurd
+
+ (*REVIEW THIS AXIOM*)
+ axiom axis_data_surjective:
+ forall axis: tensor int, x_rank: int, v: int.
+ length axis.dims = 1 /\
+ x_rank = size axis.dims /\
+ x_rank > 0 /\
+ (forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank - 1) /\
+ (forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank ->
+ (mod (axis.data (Cons z1 Nil) + x_rank) x_rank) = (mod (axis.data (Cons z2 Nil) + x_rank) x_rank) -> z1 = z2) /\
+ 0 <= v < x_rank
+ -> exists h. 0 <= h < x_rank /\ axis.data (Cons h Nil) = v
+
+ (*Builds a list with the real indices representation for each index in axis*)
+ (* axis = [2,3,1,0]*)
+ (* real_axis = [3,2,0,1]*)
+ (* axis -> tensor normalized*)
+ (* axis_rank -> rank of axis tensor = rank of input tensor*)
+ (* i -> current index in the axis tensor*)
+ let rec ghost function real_index_aux (axis: tensor int) (x_rank: int) (i: int) : list int
+ (*1D Index Tensors*)
+ requires { length axis.dims = 1 }
+ (*X [C1]*)
+ requires { x_rank = size axis.dims }
+ (*Rank > 0, see this better*)
+ requires { x_rank > 0 }
+ (*A [C2]*)
+ requires { forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank -1}
+ (*A [C3]*)
+ requires { forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank ->
+ (mod (axis.data (Cons z1 Nil) + x_rank) (x_rank)) = (mod (axis.data (Cons z2 Nil) + x_rank) (x_rank)) -> z1 = z2 }
+ requires { 0 <= i < x_rank }
+ requires { forall v. i <= v < x_rank -> exists h. 0 <= h < x_rank /\ axis.data (Cons h Nil) = v }
+ ensures { forall k. 0 <= k < length result ->
+ let z = i + k in
+ axis.data (Cons (my_nth result k) Nil) = z }
+ ensures { length result = x_rank - i }
+ variant { x_rank - i }
+ = if i < (x_rank - 1) then
+ let j = find_j axis x_rank i 0 in
+ Cons j (real_index_aux axis x_rank (i+1))
+ else
+ let j = find_j axis x_rank i 0 in
+ Cons j Nil
+
+ let ghost function real_index (axis: tensor int): list int
+ (*1D Index Tensors*)
+ requires { length axis.dims = 1 }
+ (*Rank > 0, see this better*)
+ requires { size axis.dims > 0 }
+ (*A [C2]*)
+ requires { forall z. 0 <= z < size axis.dims -> 0 <= axis.data (Cons z Nil) <= size axis.dims -1}
+ (*A [C3]*)
+ requires { forall z1 z2. 0 <= z1 < size axis.dims /\ 0 <= z2 < size axis.dims ->
+ (mod (axis.data (Cons z1 Nil) + size axis.dims) (size axis.dims)) = (mod (axis.data (Cons z2 Nil) + size axis.dims) (size axis.dims)) -> z1 = z2 }
+ requires { forall v. 0 <= v < size axis.dims -> exists h. 0 <= h < size axis.dims /\ axis.data (Cons h Nil) = v }
+ ensures { forall k. 0 <= k < length result ->
+ let z = k in
+ axis.data (Cons (my_nth result k) Nil) = z }
+ ensures { length result = size axis.dims }
+ = let x_rank = size axis.dims in
+ real_index_aux axis x_rank 0
+
+
+ let rec ghost function dX_coords_rec (x: tensor 'a) (coords t: list int) (s k a: tensor int) (i: int) : list int
+ (*1D Index Tensors*)
+ requires { length s.dims = length k.dims = length a.dims = 1 }
+ (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*)
+ requires { length x.dims > 0}
+ (*X [C1]*)
+ requires {length x.dims = size s.dims = size k.dims = size a.dims }
+ (*Redudant with 2 previous requires*)
+ requires { s ~ k ~ a }
+ (*Normalize Axis*)
+ requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims }
+ (*Index Uniqueness*)
+ requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 }
+ (*Normalize Start*)
+ requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) }
+ (*K [C2]*)
+ requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 }
+ requires { length coords = length x.dims }
+ requires { 0 <= i < length coords }
+ ensures { length result = length x.dims - i }
+
+ variant { length coords - i }
+ = if i < ((length coords)-1) then
+ let real_index = my_nth t i in
+ let original_index = s.data (Cons real_index Nil) + my_nth coords i * k.data (Cons real_index Nil) in
+ Cons original_index (dX_coords_rec x coords t s k a (i+1))
+ else
+ let real_index = my_nth t i in
+ let original_index = s.data (Cons real_index Nil) + my_nth coords i * k.data (Cons real_index Nil) in
+ Cons original_index Nil
+
+
+ let ghost function dX_coords (x: tensor 'a) (coords t: list int) (s k a: tensor int): list int =
+ (*1D Index Tensors*)
+ requires { length s.dims = length k.dims = length a.dims = 1 }
+ (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*)
+ requires { length x.dims > 0}
+ (*X [C1]*)
+ requires {length x.dims = size s.dims = size k.dims = size a.dims }
+ (*Redudant with 2 previous requires*)
+ requires { s ~ k ~ a }
+ (*Normalize Axis*)
+ requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims }
+ (*Index Uniqueness*)
+ requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 }
+ (*Normalize Start*)
+ requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) }
+ (*K [C2]*)
+ requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 }
+ requires { length coords = length x.dims }
+ ensures { length result = length x.dims }
+ dX_coords_rec x coords t s k a 0
+
+ let ghost function dslice (x : tensor 'a) (s k a : tensor int) (dy_shape t: list int) : data 'a
+ (*1D Index Tensors*)
+ requires { length s.dims = length k.dims = length a.dims = 1 }
+ (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*)
+ requires { length x.dims > 0}
+ (*X [C1]*)
+ requires {length x.dims = size s.dims = size k.dims = size a.dims }
+ (*Redudant with 2 previous requires*)
+ requires { s ~ k ~ a }
+ (*Normalize Axis*)
+ requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims }
+ (*Index Uniqueness*)
+ requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 }
+ (*Normalize Start*)
+ requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) }
+ (*K [C2]*)
+ requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 }
+ requires { forall ks. valid ks dy_shape -> length ks = length x.dims }
+ (*proof*)
+ = fun ks ->
+ if valid ks dy_shape then
+ let x_coords = dX_coords x ks t s k a in
+ x.data x_coords
+ else x.background
+
+ let ghost function opslice (x: tensor 'a) (s e k a: tensor int): tensor 'a
+ (*Additional dslice*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let s_normalized = normalize_start x.dims s.dims s a_normalized in
+ let e_normalized = normalize_end x.dims e.dims e a_normalized k in
+ let t = real_index a_normalized in
+ let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in
+ positive y_shape /\ forall ks. valid ks y_shape -> length ks = length x.dims }
+
+ (*Additional dY_shape*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let t = real_index a_normalized in
+ forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 }
+
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let t = real_index a_normalized in
+ forall h. 0 <= h < length x.dims -> mem h t }
+
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let t = real_index a_normalized in
+ let s_normalized = normalize_start (x.dims) s.dims s a_normalized in
+ let e_normalized = normalize_end (x.dims) e.dims e a_normalized k in
+ forall z. 0 <= z < length x.dims ->
+ let real_index = my_nth t z in
+ let space = e_normalized.data (Cons real_index Nil) - s_normalized.data (Cons real_index Nil) in
+ let step = k.data (Cons real_index Nil) in
+ div space step + (if mod space step = 0 then 0 else 1) > 0 }
+
+ (*Additional normalize Axis*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ forall ks. valid ks a_normalized.dims -> 0 <= a_normalized.data ks <= length x.dims -1}
+ (*Necessary because of real_index but redundant, maybe check real_index and change this*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ forall z. 0 <= z < size a_normalized.dims -> 0 <= a_normalized.data (Cons z Nil) <= size a_normalized.dims -1}
+
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ forall ks1 ks2. valid ks1 a_normalized.dims /\ valid ks2 a_normalized.dims ->
+ (mod (a_normalized.data ks1 + length x.dims) (length x.dims)) = (mod (a_normalized.data ks2 + length x.dims) (length x.dims)) -> ks1 = ks2 }
+ (*Necessary because of real_index but redundant, maybe check real_index and change this*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ forall z1 z2. 0 <= z1 < size a_normalized.dims /\ 0 <= z2 < size a_normalized.dims ->
+ (mod (a_normalized.data (Cons z1 Nil) + size a_normalized.dims) (size a_normalized.dims)) = (mod (a_normalized.data (Cons z2 Nil) + size a_normalized.dims) (size a_normalized.dims)) -> z1 = z2 }
+
+ (*Additional normalize Start*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ length s.dims = length a_normalized.dims = 1 }
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ length x.dims = size s.dims = size a_normalized.dims }
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ s ~ a_normalized }
+ (*Additional normalize End*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ length e.dims = length a_normalized.dims = length k.dims = 1 }
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ length x.dims = size e.dims = size a_normalized.dims = size k.dims }
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ e ~ a_normalized ~ k }
+ (*1D Index Tensors*)
+ requires {length s.dims = length e.dims = length k.dims = length a.dims = 1 }
+ (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*)
+ requires { length x.dims > 0}
+ (*X [C1]*)
+ requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims }
+ (*Redudant with 2 previous requires*)
+ requires { s ~ e ~ k ~ a }
+ (*A [C2]*)
+ (*requires { forall i. 0 <= i < length x.dims -> -length x.dims <= a.data (Cons i Nil) <= length x.dims -1}*)
+ requires { forall ks. valid ks a.dims -> -length x.dims <= a.data ks <= length x.dims -1}
+ (*A [C3]*)
+ (*requires {forall i j. 0 <= i < length x.dims /\ 0 <= j < length x.dims -> ( mod (a.data (Cons i Nil) + length x.dims) (length x.dims) ) = (mod (a.data (Cons j Nil) + length x.dims) (length x.dims)) -> i = j }*)
+ requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims ->
+ ( mod (a.data ks1 + length x.dims) (length x.dims) ) = (mod (a.data ks2 + length x.dims) (length x.dims)) -> ks1 = ks2 }
+ (*K [C2]*)
+ (*requires { forall i. 0 <= i < length x.dims -> k.data(Cons i Nil) <> 0 }*)
+ requires { forall ks. valid ks k.dims -> k.data ks <> 0 }
+ (*S [C2]*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ forall ks. valid ks s.dims ->
+ let axis_index = a_normalized.data ks in
+ - (my_nth x.dims axis_index) <= s.data ks <= (my_nth x.dims axis_index) - 1 }
+ (*E [C2]*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ forall ks. valid ks e.dims ->
+ let axis_index = a_normalized.data ks in
+ let step = k.data ks in
+ let e_val = e.data ks in
+ (step > 0 -> - (my_nth x.dims axis_index) <= e_val <= (my_nth x.dims axis_index))}
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ forall ks. valid ks e.dims ->
+ let axis_index = a_normalized.data ks in
+ let step = k.data ks in
+ let e_val = e.data ks in
+ (step < 0 -> - (my_nth x.dims axis_index) - 1 <= e_val <= (my_nth x.dims axis_index) - 1) }
+ (*S [C3], E [C3], K [C3]*)
+ (*requires { forall i. 0 <= i < length x.dims -> k.data (Cons i Nil) > 0 -> s.data (Cons i Nil) <= e.data (Cons i Nil) }*)
+ (*requires { forall i. 0 <= i < length x.dims -> k.data (Cons i Nil) < 0 -> s.data (Cons i Nil) >= e.data (Cons i Nil) }*)
+ (*R6*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let s_normalized = normalize_start x.dims s.dims s a_normalized in
+ let e_normalized = normalize_end x.dims e.dims e a_normalized k in
+ forall ks. valid ks s_normalized.dims -> k.data ks > 0 -> s_normalized.data ks <= e_normalized.data ks }
+ (*R7*)
+ requires { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let s_normalized = normalize_start x.dims s.dims s a_normalized in
+ let e_normalized = normalize_end x.dims e.dims e a_normalized k in
+ forall ks. valid ks s_normalized.dims -> k.data ks < 0 -> s_normalized.data ks >= e_normalized.data ks }
+ (*X [C2]*)
+ ensures { length result.dims = length x.dims /\ result.background = x.background }
+ (*Y [C2]*)
+ ensures { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let s_normalized = normalize_start x.dims s.dims s a_normalized in
+ let e_normalized = normalize_end x.dims e.dims e a_normalized k in
+ let t = real_index a_normalized in
+ let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in
+ result.dims = y_shape }
+ ensures { let a_normalized = normalize_axis (x.dims) a.dims a in
+ let s_normalized = normalize_start x.dims s.dims s a_normalized in
+ let e_normalized = normalize_end x.dims e.dims e a_normalized k in
+ let t = real_index a_normalized in
+ let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in
+ result.data = dslice x s_normalized k a_normalized y_shape t }
+ (*proof*)
+ = let a_normalized = normalize_axis x.dims a.dims a in
+ let s_normalized = normalize_start x.dims s.dims s a_normalized in
+ let e_normalized = normalize_end x.dims e.dims e a_normalized k in
+ let t = real_index a_normalized in
+ let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in
+
+ { dims = y_shape; data = dslice x s_normalized k a_normalized y_shape t; background = x.background}
+end
+*)
+(*
+module OPClip
+ use Tensor
+ use int.Int
+ use list.List
+ use Range
+ use real.Real
+ use real.MinMax
+
+
+ predicate is_scalar_tensor (t : tensor real) =
+ t.dims = Nil /\ t.data = (fun _ -> t.background)
+
+ predicate ordered_bounds (l m y: tensor real) =
+ forall ks:list int. valid ks y.dims ->
+ (l.background <= m.background ->
+ l.background <= y.data ks /\ y.data ks <= m.background)
+
+ predicate inverted_bounds (l m y: tensor real) =
+ forall ks:list int. valid ks y.dims ->
+ (l.background > m.background ->
+ y.data ks = m.background)
+
+ let ghost function dclip (x l m : tensor real): data real
+ (*proof*)
+ = fun ks ->
+ if valid ks x.dims then
+ min m.background (max (x.data ks) l.background)
+ else x.background
+
+ let ghost function opclip (x l m : tensor real): tensor real
+ requires { is_scalar_tensor l }
+ requires { is_scalar_tensor m }
+ ensures { result ~= x }
+ ensures { ordered_bounds l m result }
+ ensures { inverted_bounds l m result }
+ (*proof*)
+ = { dims = x.dims; data = dclip x l m; background = x.background }
+ (*qed*)
+
+ goal clip_correct:
+ forall x l m: tensor real.
+ is_scalar_tensor l /\ is_scalar_tensor m ->
+ let y = opclip x l m in
+ ordered_bounds l m y /\ inverted_bounds l m y
+ (* same as *)
+ (*
+ if l.background <= m.background then
+ ordered_bounds l m y
+ else
+ inverted_bounds l m y
+ *)
+
+end
+*)
\ No newline at end of file
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/proof.json
new file mode 100644
index 00000000..73b56dab
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/proof.json
@@ -0,0 +1,24 @@
+{
+ "profile": [],
+ "proofs": {
+ "OPWhere": {
+ "dwhere": { "tactic": "split_vc", "children": [] },
+ "opwhere": null
+ },
+ "Range": {
+ "positive_size": null,
+ "positive_valid": null,
+ "size_append": null,
+ "size_push": null,
+ "valid_push": null
+ },
+ "Tensor": {
+ "const": null,
+ "exteq": null,
+ "scalar": null,
+ "tensor": null,
+ "zero": null,
+ "zero_is_const": null
+ }
+ }
+}
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml
new file mode 100644
index 00000000..5f30ea5f
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml
@@ -0,0 +1,291 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml.bak
new file mode 100644
index 00000000..5f30ea5f
--- /dev/null
+++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml.bak
@@ -0,0 +1,291 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz
new file mode 100644
index 00000000..4799493a
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz differ
diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz.bak
new file mode 100644
index 00000000..4799493a
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz.bak differ
diff --git a/safety-related-profile/meetings/formal_methods/minutes.md b/safety-related-profile/meetings/formal_methods/minutes.md
index b27f0aa2..14992cc6 100644
--- a/safety-related-profile/meetings/formal_methods/minutes.md
+++ b/safety-related-profile/meetings/formal_methods/minutes.md
@@ -1,8 +1,171 @@
-# 2025/01/28
+
+# 2026/02/18
+
+See the list of [topics](./inputs/2026-02-18%20-%20Inputs.md) and [Loïc's notes](../formal_methods/slides/2026-02-18%20-%20Loic%20notes.pdf).
+
+# 2025/06/20
+## Agenda
+
+[Schéma global](./imgs/2025-06-20/image.png)
+
+- (Mariem, Salomé, Eric et Jean) Présentation pour retour critique, de trois formalisations ainsi que des problèmes rencontrés
+ - concat : concat (repo)
+ - Problèmes rencontrés :
+ - Complexité de la formalisation de l'opérateur : La nature "variadique" de l'opérateur combinée à la possibilité d'avoir des tenseurs de diverses dimensions
+ - Introduction de clauses de spécification formelle (requires, ensures)
+ - Appréhension des mécanismes de preuve
+ - Exploitation efficace (?) des modules préexistants, comme Sequences et Tensors
+ - conv 2D standard : repo (repo)
+ - Problèmes rencontrés
+ - Est ce qu'il y a des améliorations et/ou simplifications à proposer.
+ - Comment gérer les erreurs de preuve (Exemple: la fonction conv2d_output_value)?
+ - Est-il possible de généraliser le type des tenseurs pour éviter de réecrire la spécification pour le type real.
+ - graphe ONNX : [graph](../../documents/profile_formal/onnxgraph.mlw) (repo)
+ - Problèmes rencontrés : (voir [ici](#formalisation-du-graphe) en fin de ce document
+- (Loïc) Preuves de l'existant
+ - Obligations de preuve générées par l'outil
+ - Propriétés utilisateurs
+ - Processus de preuve
+- (Mariem, Salomé, Eric et Jean) Besoin de spécification de type "relationnel" (voir schéma ci-dessus) en plus de la spécification fonctionnelle
+ - Utile si la fonctionnelle n'est pas une redite de la relationnelle.
+ - Exemples :
+ - Utile pour concat
+ - Inutile pour where (l'opérateur traité lors de la première session).
+- (Loïc) Processus de preuve spec fonctionnelle vs spec relationnelle
+- Synthèse méthodologique
+- Conclusion
+
+
+### Formalisation du graphe
+
+Voir le code [WhyML](../../documents/profile_formal/onnxgraph.mlw).
+
+#### Q1: Choix de modélisation / implémentation
+
+Pour implémenter la fonction qui associe une valeur à un tenseur, j'utilise une liste de (clé, valeur). Est-ce un bon choix?
+
+#### Q2 : Bibliothèques d'implémentation
+
+Existe-t-il un "catalogue" d'implémentations des types de la bibliothèque standard? Par exemple, une implémentation de `Map` sur la base des listes?
+
+#### Q3 : Documentation et apprentissage
+
+Existe-t-il une liste de "bons exemples" dont nous pourrions nous inspirer?
+
+#### Q4: Démonstration simple qui n'aboutit pas...
+
+Soit le petit exemple suivant :
+``` whyml
+let rec fold_left (f: 'acc -> 'a -> 'acc) (acc: 'acc) (l: list 'a) : 'acc
+ variant { l }
+= match l with
+ | Nil -> acc
+ | Cons x xs -> fold_left f (f acc x) xs
+ end
+
+predicate all_true (l: list bool) =
+ forall b: bool. mem b l -> b = true
+
+lemma and_true_true:
+ forall a: bool, b: bool. Bool.andb a b -> a /\ b
+
+lemma fold_left_and_equiv_all_true:
+ forall l: list bool.
+ fold_left Bool.andb true l = true <-> all_true l
+```
+
+Quelle approche préconiserais-tu pour spécifier la fonction "fold_left" ?
+
+#### Q5 : Approche de preuve
+Lorsqu'une preuve n'aboutit pas, il est difficle (pour un novice) de savoir comment procéder pour
+- déterminer s'il s'agit d'une incapacité du prouveur à réaliser la preuve ou une réelle erreur de spécification / implémentation
+- corriger l'erreur (de spécification ou d'implémentation)...
+Quelle démarche doit-on suivre pour traiter une preuve qui n'aboutit pas?
+
+#### Q6 : Approche pour la preuve
+Existe--il des constructions à éviter pour faciliter la preuve (et, réciproquement), existe-t-il des constructions à éviter (antipatterns?
+
+#### Q7 : Lien entre spécification et implémentation
+Je souhaite spécifier les fonctions en utilisant les constructions les plus abstraites possibles. Par exemple, je défini l'état d'un graphe comme une application d'un ensemble de tenseurs vers un ensemble de valeurs.
+
+La déclaration est la suivante :
+```
+type graph_state = Map.map tensor (option value)
+```
+Je voudrais donc que ma spécification du comportement du graphe fasse référence à ce type abstrait. En pratique, la Map.map est implémentée au moyen d'une liste de (tenseur, valeur) :
+```
+type fmap = list (tensor, option value)
+```
+Je spécifie deux fonctions sur le type fmap: get et set.
+- `fget_logic`
+- `fset_logic`
+
+J'implémente ces 2 fonctions (`fget` et `get`).
+
+Je démontre que
+- `fget` implémente bien `fget_logic`
+- `fset` implémente bien `fset_logic`
+
+Dans ce cas, il n'y a pas de différence entre l'implémentation et la spécification.
+
+Maintenant, je souhaite établir le lien entre mon implémentation et la `Map` abstraite.
+
+J'introduis deux lemmes montrer que les deux fonctions `fget` (resp. `fset`) et `Map.get` (resp. `Map.set`) retournent toujours les mêmes valeurs.
+
+Est-ce la bonne approche?
+
+#### Q8: Problème de preuve
+
+Voir les lemmes `get_set_eq` et `get_set_neq` dans le code [WhyML](../../documents/profile_formal/onnxgraph.mlw).
+
+
+# 2025/03/20
+## Agenda
+- Tutorial by Loïc Correnson (CEA) on Why3
+- Documentation about Why3 can be found [here](https://www.why3.org/).
+- For the tutorial, we will use [Why3find](https://git.frama-c.com/pub/why3find) which "Why3find "provides a collection of utilities to ease the development
+of Why3 project".
+
+## Participants
+Mariem, Loïc, Edoardo, Nicolas, Jean, Christophe G., Henri, Eric
+
+## Minutes
+- The Why3 code (written by Loïc...) can be found [here](./code)
+- Discussion about the role of Why3 in our process. See diagram below
+ - Why3 is used to specify the operators (and the graph execution semantics). For the moment, focus is placed on the operators.
+ - The specification may be relational or operational, depending on the operation. In some cases (e.g., `sqrt`) a relational specification (`sqrt(x)` is such that `sqrt(x)^2=x`) is simpler than the operational specification (which will describe how to compute the square root).
+ - In our case, most of the operations (not all) will need to be described operationally.
+ - We need to provide a test oracle to the user.
+ - The oracle may be an ocaml executable program (which can be generated automatically from some specs).
+ - It may also be the C code that can also be generated automatically from the spec if it has been written to allow this generation (see example of max value computation)
+ - The generated code is supposed to be compliant with the procedural specification since it has been generated from it (the code generator may still need to be qualified...)
+ - The generated C code may not comply with one's coding standard. It is up to him/her to refactor this code to make it comply with the coding rules.
+ - The modified C code may be proved to comply with the specification thanks to the FramaC toolchain. In that case, the ASCL spec may be directly imported from the Why3 spec. The loop invariants will still to be added manually...
+ - The FramaC part is not addressed by SONNX.
+ 
+- The specification must be kept as simple and readable as possible, with no fancy optimizations.
+ - The formal specification shall be as traceable as possible to the informal spec. (off-meeting: It may be the case that we have to formalize this traceability)
+ - In SONNX, we have limited the number of dimensions of tensors (e.g., 2 spatial dimensions for `conv`). This simplifies the implementation (e.g., the number of loop for convolution is bounded). (off-meeting: to we accept 1 dimension? if yes the code generator will have to handle this level of variability)
+- As far as possible, we shall specify the operators as **total functions**, since it strongly simplifies the specification and proof effort (we don't have to demonstrate systematically that we are in the operator's domain).
+ - We consider that providing the `requires` clause is important because, at the end of the day, the operator will only be usable in the domain defined by these clauses (the functions will not be total). And we have to demonstrate that all levels of the specification comply with the top-most spec.
+ - We may favor total functions and still add `require` clauses in order to restrict the domain .
+- Work on the formalization of tensors (see code). We tried two representations:
+ - a "flattened" representation in which tensor elements are "stored" in a mono-dimensional array.
+ - This representation raises strong difficulties when it comes to traversing all dimensions of the tensor. It also raises problem if the indexes are not in the domain allowed by the size of the tensor's array. So we moved to a second representation...
+ - a "hierarchical" representation in which the tensor elements are "represented" by a total function mapping an index to the value domain. This representation is based on the `seq`module that provides most of the needed constructs.
+- Work on the `where` operator
+ - Note: a version of this operator has been generated by Henri using ChatGPT. We have not checked thoroughly if this specification was correct (it looks like...), but it remains interesting. Surprisingly, chatGPT seems to "know" Why3 pretty well and can be used as a means to generate part of a spec or explanations about existing Why3 code.
+- Next steps: complete the work initiated during this meeting on the `where` operator:
+ - [ ] generate the ocaml code and build the test bench to be able to execute it
+ - [ ] build the procedural, C oriented, formal specification
+ - [ ] Prove the compliance of the C-spec to the top level spec
+ - [ ] generate the C code and build the test bench to be able to execute the spec
+
+# 2025/01/28
## Agenda
The objectives of the meeting are the following :
-- Determine the approach for operator specification. On the basis of Loïc’s proposal:
+- Determine the approach for operators and graphs specification. On the basis of Loïc’s proposal:
* Option 1: use C code
* The reference implementation (in C) is the specification. Compliance of an implementation to this specification will be demonstrated by testing with epsilon precision. No formal specification language (e.g., ACSL, Why3, etc.) is required.
* Option 2: use Why3
@@ -11,10 +174,45 @@ The objectives of the meeting are the following :
* Algebraic Properties: Show some good algebraic properties of the reference algorithm in Why3 (in ℝ) (e.g., linearity, commutativity, associativity, distributivity with respect to addition, existence of a neutral element, symmetry, etc.).
* Option 3: use ACSL
* Similar to the Why3 versions above, but using ACSL/Frama-C.
-
-- Determine the best approach for developing the reference specification for the Conv operator.
+- Determine the formalism for operators specification
+- Determine the process/worksharing for developing the operator (starting with CONV)
+
## Participants
+Edoardo, Loïc, Nicolas, Jean, Christophe Gar., Eric, Augustin, Mariem
## Minutes
+This document summarizes the discussions and conclusions from our meeting on the specification approaches for ONNX operators and graphs.
+### What is the purpose of the specification (ACSL or Why3)? What is the added value? Is the C reference algorithm not enough? (Eric)
+- (Loïc) It depends on our needs. If we only require tests, then the C implementation is sufficient. However, if we need proofs, we require a specification in a formal language.
+The specification cannot be written in C. It should either be a reference implementation (in Coq, Why3, etc.) or a mathematical specification.
+- (Jean) ACSL specification is suitable for industrial purposes.
+- (Eric) We have to ensure that what we propose is useful and useable for the type of targeted systems, operators, etc. If people develop fancy optimized implementations, proof will be complex, possibly out of the economically-viable domain with respect to the targeted development assurance level.
+### What can we do with Why3 specification?
+- Execute the specification, which may contribute to its validation.
+- Generate C program that complies with the specification
+- Generate coq program
+- Provide the axiomatization to facilitate ACSL-level verifications (easier than doing everything in ACSL)
+- Even if no formal proof is done, Why3 is an appropriate and efficient formalism to describe the operators' algorithms.
+### What do we need to succeed with the Why3 specification?
+ - Developing libraries such as matrices, algebraic signatures, logical signatures, etc. would reduce the specification effort.
+ - Having a detailled Why3 description of those libraries are not mandatory in the first place. We may rely on C implementations to execute the spec.
+### Is graph specification the same as operator specifications?
+(Loïc) No, it is not the same. It is a separate subject and likely more complex than operator specifications.
+The specification technique used for graph specification differs from that used for operator specifications, as we need to find a way to describe the graph.
+### Conclusion on objectives 1 and 2
+- We will use the Why3 specification approach. This involves describing the reference algorithm of ONNX operators in Why3.
+- From the Why3 implementation, it is possible to generate C programs and other implementations.
+- This template can be integrated into Edge.
+### How will we proceed with developing the reference operator (e.g., convolution or others)?
+- We will organize a workshop in Toulouse in March. During this workshop, Loïc will provide all the necessary foundations and knowledge about Why3, and we will develop the reference operator together.
+### Conclusion on objective 3
+- We will implement the specification of the reference operator together during a workshop to be held in Toulouse in March.
+- The date of the workshop is to be determined.
+### New Actions
+[ ] (280125-01 - Mariem) Organize Why3 workshop (mid-march)
+[ ] (280125-02 - Eric) Check how this work fits wit DeepGreen's objectives and check possible funding.
+[ ] (280125-03 - All) Check if/how we could fund this work...
+
+
# 2024/11/29
@@ -34,7 +232,7 @@ The objective of the meeting is to answer the following quesiotns:
Dumitru, Nicolas, Christophe Gar., Mariem, Augustin, Jean, Eric [ed.]
## Minutes
-- Supporting slides are available [here](./slides-29-11.pdf).
+- Supporting slides are available [here](./slides/slides-29-11.pdf).
### Can the code be a specification?
- For some simple operators, the code may be a sufficient specification. For others, the code may be more complicated (e.g., broadcasting). The combination of the informal specificaiton and the code can be sufficient.
diff --git a/safety-related-profile/meetings/formal_methods/slides/2026-02-18 - Loic notes.pdf b/safety-related-profile/meetings/formal_methods/slides/2026-02-18 - Loic notes.pdf
new file mode 100644
index 00000000..1bf047ef
Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/slides/2026-02-18 - Loic notes.pdf differ
diff --git a/safety-related-profile/meetings/formal_methods/slides-29-11.md b/safety-related-profile/meetings/formal_methods/slides/slides-29-11.md
similarity index 100%
rename from safety-related-profile/meetings/formal_methods/slides-29-11.md
rename to safety-related-profile/meetings/formal_methods/slides/slides-29-11.md
diff --git a/safety-related-profile/meetings/formal_methods/slides-29-11.pdf b/safety-related-profile/meetings/formal_methods/slides/slides-29-11.pdf
similarity index 100%
rename from safety-related-profile/meetings/formal_methods/slides-29-11.pdf
rename to safety-related-profile/meetings/formal_methods/slides/slides-29-11.pdf
diff --git a/safety-related-profile/meetings/general/2025-06-09 - MEET-UP/SONNX - Meetup 2025.7z b/safety-related-profile/meetings/general/2025-06-09 - MEET-UP/SONNX - Meetup 2025.7z
new file mode 100644
index 00000000..8eb131f3
Binary files /dev/null and b/safety-related-profile/meetings/general/2025-06-09 - MEET-UP/SONNX - Meetup 2025.7z differ
diff --git a/safety-related-profile/meetings/minutes.md b/safety-related-profile/meetings/minutes.md
index eed24832..90541bfe 100644
--- a/safety-related-profile/meetings/minutes.md
+++ b/safety-related-profile/meetings/minutes.md
@@ -1,3 +1,1775 @@
+# 2026/06/03
+## Participants
+- Ricardo, João, Eduardo, Jean-Loup, Franck, Jean, Eric
+## Agenda
+- Review of actions (Eric)
+- A quick look at the [local WG actions](../meetings/Local%20WGs/Tlse/agenda.md)
+- A quick feedback on the use of Gemini to assist writing specs...
+- Status of Graph spec (Ricardo)
+- Calls for contributors? When and how?
+- A quick feedback on Hypothesis
+- Roundtable
+## Minutes
+- Short presentation of the use of Gemini to generate an informal spec. Application to **Resize**
+- Graph spec:
+ - Abstract spec is OK
+ - Concrete spec is close to completion. J&R Thesis report will document the model design and proof.
+- Call for contributors
+ - The number of participants to our WG meeting seems to be steady, but it is low.
+ - R&J will both leave to the project by the end of Jun, and this will be a great loss ;-(...
+ - We have already done several communications (at ERTS and DATE) and other are soon to come (two workshops in Lisbon by J&R and CTIC by Eric in Tlse). The Lisbon SAIV conf. may raise some interest in the academic community.
+ - The current outputs of the WG (guidelines, specs...) represent a good basis to embark other people. Concerning formal methods, for example, we have good examples that can be reused and adapted to create (some of) the remaining ones with a reasonable effort.
+ - Edoardo could possibly propose students project in Q4.
+ - We have to contact the industrialists that were involved at the beginning of the project, and other that have contributed heavily at some point (Airbus Helico, Thales AVS, etc.)
+ - See actions (0306-2) and (0306-3) below.
+
+## Actions
+### New actions
+- [ ] (0306-1, Jean) Update the spec of **Maxpool** (indices generation part).
+ - Do not refer to "upper" but "min".
+ - Modify the definition of "indices" to specify the tie break rule when multiple values are equal to the max...
+ - To be review on Friday 5th.
+- [ ] (0306-2, Eric) Contact the industrialists that have shown some interest in ONNX (especially those that have expressed some specific needs in [scope.md](../deliverables/scope/scope.md)).
+- [ ] (0306-3, Eric) Organize a (remote) presentation of our work to the community in Sept or Oct.
+### Previous actions
+- [X] (0605-2, J&R) Check if possible to avoid operator specific logic (arg checking) in the graph formalization
+ - We have to ensure that the number of arguments is correct.
+ - Conclusion : we keep things as is.
+- [ ] (0804-1, Eric, local WG, Edoardo) Review Franck's work on **Div**, **Matmul**, **Tanh**.
+ - First [review by Eric](../sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md).
+ - Next review by local WG and Edoardo
+ - 3 things
+ - [ ] **guidelines**
+ - [ ] **applied guidelines on some ops (see above)**
+ - [ ] code to help generating the spec
+- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool**
+ - Spec of second output of MaxPool by Mariem (concrete part to be completed)
+- [X] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output...
+ - **MaxPool** was incomplete because the informal spec not clear on the second output. Inconsistency between R&J's and Jean's interpretation
+ - Check Jean's formula.
+ - Jean is currently updating the spec for the indices output.
+ - The spec has been updated.
+ - There is still a corner case to be sorted out...
+ - Spec to commit.
+- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing.
+ - Jean is working on the test strategy. To be discussed during Toulouse's meeting.
+ - First draft by Eric reviewed by Jean-Loup. Corrections integrated, some comments still to be processed. Consolidation with existing doc to be done.
+ - Doc reworked
+- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others)
+ - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...)
+- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed)
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+
+# 2026/05/20
+## Participants
+- Ricardo, João, Eduardo, Jean-Loup, Franck, Jean, Eric
+## Agenda
+- Review of actions (Eric)
+- Status of papers (Ricardo+João)
+- Test strategy (Eric). See [doc](../sonnx/ops/docs/guidelines/tests-part2.md).
+- An "idea" about formal spec... (eric)
+- Status of AIDGE integration (Eric)
+- Roundtable
+## Minutes
+- Discussion about the formal specification of generic properties of operators (symmetry, commutativity, neural element, etc.)
+ - This is an additional jutification for the effort on formal spec., in addition to providing the (potential) capability to develop a certified embedded code generator (that require a formal spec).
+ - Such properties have already been added in the formal spec by Ricardo and João. In their case, they are used as lemma to support other proofs.However, these lemma may be part of the verification / validation process of the formal specification.
+- Discussion on testing
+ - There is a question about the pertinence of the use of Hypothesis: is it useful? does it facilitate the development of tests?
+ - The approach is to first complete the definition of our test strategy (based on equivalence classes) and then see if Hypothesis facilitates or not its implementation.
+ - Discussion on the equivalence class-based strategy.
+ - See Eric's [doc](../sonnx/ops/docs/guidelines/tests-part2.md).
+- Formal spec and implementation of MaxPool
+ - The formal spec is complete for output Y what is missing is the specification of the indices output. This will be done once the problem with MaxPool is solved (see next point).
+ - To generate the code, the driver must be changed. To be discussed with Mariem.
+- Discussion on the computation of indices for **MaxPool**.
+ - A new "bug" has been discovered, when the tensor is padedd and full of -inf. The index shall be taken in the tensor area, not in the padded area. In particular, this means that we shall not return the index of the top left most value of the padded tensor...
+- Presentation of the latest result on the formalization of the graph (J&R)
+ - Some modification have been done so that the structure and the state of the graph are handled separately.
+ - Proof has been carried out to demonstrate that when a node is executed, then all its parent nodes (the nodes from which it depends) have been executed.
+ - Termination condition must be added: eventually, all nodes are executed.
+ - Question about the refinement: there is no real constraint about the refinement since we are not concerned with performance. So we have to chose the refinement that facilitate the verification and/or translation to C...
+- J&R theses: complete by end of June!!!!
+## Actions
+### New actions
+### Previous actions
+- [X] (0605-1, J&R) Check the modeling of varargs operators
+- [ ] (0605-2, J&R) Check if possible to avoid operator specific logic (arg checking) in the graph formalization
+- [ ] (0804-1, Eric, local WG, Edoardo) Review Franck's work on **Div**, **Matmul**, **Tanh**.
+ - First [review by Eric](../sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md).
+ - Next review by local WG and Edoardo
+- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool**
+ - Spec of second output of MaxPool by Mariem (concrete part to be completed)
+- [ ] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output...
+ - **MaxPool** was incomplete because the informal spec not clear on the second output. Inconsistency between R&J and Jean
+ - Check Jean's formula.
+ - Jean i scurrently updating the spec for the indices output.
+- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing.
+ - Jean is working on the test strategy. To be discussed during Toulouse's meeting.
+ - First draft by Eric reviewed by Jean-Loup. Corrections integrated, some comments still to be processed. Consolidation with existing doc to be done.
+- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others)
+ - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...)
+- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed)
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+
+# 2026/05/06
+## Participants
+- Eric, Mariem, Jean, Edoardo, Jean-Loup, Ricardo, João, Franck
+## Agenda
+- Review of actions (Eric)
+- "Feeedback" on DATE 2026
+- Status of papers (Ricardo+João)
+- ONNX meetup 2026
+- Test strategy
+- Roundtable
+## Minutes
+- Paper submissions
+ - SAIV 2026
+ - Notif. 15th May
+ - Overlay 2026
+ - Notif. 25th May
+- DATE 2026
+ - See the workshop program at https://www.date-conference.com/workshop/w03
+ - Very limited (and non satisfying...) number of attendees...
+- Roundtable
+ - R&J:
+ - Modification of the graph modelling.
+ - Closer to the graph informal spec.
+ - Pb with varargs op.
+ - Eric: test strategy
+ - Mariem
+ - Work on AIDGE benchmarking module to compare SONNX vs. ONNX runtime
+ - Use of static allocation for CONV?
+ - Loïc proposes to pass the array as a parameter rather than use a local decl.
+ - Mariem modified the formal spec.
+ - Solution validated by R&J.
+ - Hamza
+ - Integration of LeakyRelu + Mul + Tanh in AIDGE
+## Actions
+### New actions
+- [ ] (0605-1, J&R) Check the modeling of varargs operators
+- [ ] (0605-2, J&R) Check if possible to avoid operator specific logic (arg checking) in the graph formalization
+### Previous actions
+- [ ] (0804-1) Review Franck's work on **Div**, **Matmul**, **Tanh**.
+- To be done during next local meeting
+- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool**
+ - Spec of second output of MaxPool by Mariem (concrete part to be completed)
+- [ ] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output...
+ - **MaxPool** was incomplete because the informal spec not clear on the second output. Inconsistency between R&J and Jean
+ - Check Jean's formula.
+- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing.
+ - Jean is working on the test strategy. To be discussed during Toulouse's meeting.
+ - First draft
+- [X] (1103-1, Eric, Jean-Loup, Jean, Mariem, Edoardo) Review Ricardo and João's formal specification and verification guidelines
+ - Eric: done
+ - Jean-Loup: Done
+ - Edoardo: to be done by next week.
+- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others)
+ - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...)
+- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed)
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+
+# 2026/04/08
+## Participants
+- Eric, Mariem, Jean, Edoardo, Jean-Loup, Ricardo, João, Franck
+## Agenda
+- Review of actions (Eric)
+- Roundtable
+## Minutes
+- Jean is working on the test strategy (based on equivalence classes). To be discussed during the next local meeting.
+- J&R have completed the formal spec of **Maxpool**, except for the part concerning the indices output. See action.
+- J&R have proposed a first formal spec of a graph.
+ - They have also specified pre conditions for the execution of operators. (i.e., a graph is executable iff its operators are executable).
+ - Discussion on the way to model the fact that an operator has been executed. In J&R model, this is modled using a "flag" attached to the operator. In Eric's, this was modeled as a property of the tensor...
+ - Discussion of the relation between the abstract and concrete spec of the operator execution order spec.
+ - The abstract spec is non-deterministic with regards to the execution order (any order compatible with the dataflow semantics is fine) whereas the concrete spec imposes *one* specific order. In that sense, the two specs are not equivalent, but the concrete one complies with the abstract one, which is the expected "result"
+## Actions
+- [ ] (0804-1) Review Franck's work on **Div**, **Matmul**, **Tanh**.
+- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool**
+- [ ] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output...
+### New actions
+### Previous actions
+- [X] (2503-1, Eric, Amine) Organize meeting with Amine to setup a first
+- application of the proposed framework.
+ - Meeting with Amine and Andryi on 07/04
+- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing.
+ - Jean is working on the test strategy. To be discussed during Toulouse's meeting.
+- [X] (2503-3, Eric, Ricardo, João) Plan a meeting to review Eric's comments
+ - Done on 27/03
+- [ ] (1103-1, Eric, Jean-Loup, Jean, Mariem, Edoardo) Review Ricardo and João's formal specification and verification guidelines
+ - Eric: done
+ - Jean-Loup: Done
+ - Edoardo: to be done by next week.
+- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others)
+ - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...)
+- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed)
+- [X] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**.
+ - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides.
+ - **Div** to be done.
+ - Ready to be reviewed.
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+- [-] (1911-2, All) Review Jean's V&V proposal
+ - Eric to put updated slides on the repo : Done.
+ - File to be reviewed not yet delivered.
+- Actions from local work sessions
+- Cancelled.
+
+# 2026/03/25
+## Participants
+- Eric, Mariem, Jean, Edoardo, Jean-Loup, Ricardo, João, Henri, Franck, Amine
+## Agenda
+- Review of actions (Eric)
+- Submission of João and Ricardo's paper at SAIV'26 on formal verification
+- Testing (eric)
+ - See [current guidelines](../sonnx/ops/docs/guidelines/tests.md) and some [additionnal material (draft)](../sonnx/ops/docs/guidelines/reviews/tests/tests_additional%20_material.md).
+- Roundtable
+ - Presentation of the first work done on graph modeling (Ricardo & João)
+ - Latest news on the integration of operators in AIDGE (Henri)
+- Welcome to Amine Abid from Saneaon.
+- Local WG (Eric)
+ - See [actions](../meetings/Local%20WGs/Tlse/agenda.md).
+## Minutes
+- See agenda.
+- Discussion on testing and equivalence-classes based testing.
+- Amine presented his AI agentic framework. It could potentially be used to boost our activities by undertaking some of the specification or testing activities. A meeting may be useful to setup a first experiment.
+## Actions
+### New actions
+- [X] (2503-1, Eric, Amine) Organize meeting with Amine to setup a first
+- application of the proposed framework.
+ - Meetng with Amine and Andryi on 07/04
+- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing.
+ - Jean is working on the test strategy. To be discussed during Toulouse's meeting.
+- [ ] (2503-3, Eric, Ricardo, João) Plan a meeting to review Eric's comments
+- [ ] ### Previous actions
+- [ ] (1103-1, Eric, Jean-Loup, Jean, Mariem, Edoardo) Review Ricardo and João's formal specification and verification guidelines
+ - Eric: started but not yet finished...
+- [X] (1103-2, all) Concludes on the expected behaviour of a **Conv** or **MaxPool** with incompatible sizes (should these conditions be verified statically? what should happen if these conditions cannot be guaranteed statically?)
+ - "If conditions cannot be ensured statically" (this point can be discussed), the violation of a precondition is considered as an error conditions and, in that case, the behaviour of the operator, must be described explicitly.
+- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others)
+ - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...)
+- [X] (1103-4, all) Find a practical way to discriminate the "specifying" part of the informal specification from the informative part.
+ - From local WG:
+ - Introduce tags `[specx.y...]` and `[/specx.y...]`, where `x`, `y`,... are numbers, when a specific element in the section needs to be designated. The dot notation (`x.y...`) is introduced when a more specific element needs to be identified within an element that is already tagged.
+ The exact tag is : `[spec1]`
+ - Elements that are provided for information are tagged using `[info]` `[/info]`. All other elements are specifying. See the spec of operator [**Div**](../sonnx/ops/spec/informal/div/div.md).
+ - Use section names when traceability is a the scale of the section. (i.e., the complete section would be enclosed in `[specx.y...]` and `[/specx.y...]`).
+- [X] (1103-5, Mariem) Check why the SONNX backend has not been pushed in the AIDGE repo.
+- [X] (1103-6) Ensure that the **Conv** op integrated in Aidge is actually the one generated out of Ricardo and João's formal spec.
+ - It is currently the first version.
+- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed)
+- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**.
+ - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides.
+ - **Div** to be done.
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+- [ ] (1911-2, All) Review Jean's V&V proposal
+ - Eric to put updated slides on the repo : Done.
+ - File to be reviewed not yet delivered.
+- Actions from local work sessions
+
+# 2026/03/11
+## Participants
+- João, Ricardo, Hugo, Edoardo, Franck, Mariem, Henri, Jean, Eric
+## Agenda
+- Review of actions (Eric)
+- Status of [specification](https://github.com/users/ericjenn/projects/4)
+- Progress of João and Ricardo's work
+- The case of **MaxPool**, continued... (Jean)
+- Local WG (Eric)
+ - See [actions](../meetings/Local%20WGs/Tlse/agenda.md)
+## Minutes
+- João and Ricardo
+ - have completed the formal specification and verification guidelines (to be reviewed)
+ - have completed the formal spec of **Conv**, **Reshape**, **Flatten**
+ - are making progress on the extension or reals with IEEE special values
+ - have created the formal specification of complete (simple) networks and
+ - proved some properties on them (Lipschitz).
+ - generated a C implementation that can be executed and used as a reference implementation to verify some implementation (this is a simpler way than specifying a generic graph executor)
+- Jean: presentation of the new problem found on MaxPool when the size of the kernel is larger than the size of the argument...
+- Jean: We should clearly discriminate the formal part fo the specification for the informative part.
+ - This could be done by
+ - numbering paragraphs (not easy with markdown...)
+ - adding tags to the specifying paragraphs (a lot of tags)
+ - using a specific font / color to discriminate the informative part, as follows :
+ -
This is an informative part
+## Actions
+### New actions
+- [ ] (1103-1, all) Review Ricardo and João's formal specification and verification guidelines
+- [ ] (1103-2, all) Concludes on the expected behaviour of a **Conv** or **MaxPool** with incompatible sizes (should these conditions be verified statically? what should happen if these conditions cannot be guaranteed statically?)
+- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others)
+- [ ] (1103-4, all) Find a practical way to discriminate the "specifying" part of the informal specification from the informative part.
+- [ ] (1103-5, Mariem) Check why the SONNX backend has not been pushed in the AIDGE repo.
+- [ ] (1103-6) Ensure that the **Conv** op integrated in Aidge is actually the one generated out of Ricardo and João's formal spec.
+- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed)
+### Previous actions
+- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**.
+ - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides.
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+- [ ] (1911-2, All) Review Jean's V&V proposal
+ - Eric to put updated slides on the repo : Done.
+ - File to be reviewed not yet delivered.
+- Actions from local work sessions
+
+# 2026/02/25
+## Participants
+- Joõ, Camille, Eric, Mariem, Dumitru, Ricardo, Franck
+## Agenda
+- Review of actions (Eric)
+- Status of [specification](https://github.com/users/ericjenn/projects/4)
+- Meeting on accuracy (Eric+Jean+Franck), see the [minutes](../meetings/numerical%20accuracy/2026-02-17%20-%20DeepGreen%20-%20Accuracy.md).
+- Meeting on formal methods (Eric+All)
+- Progress and questions from Portugal! (Ricardo and João)
+- The case of **MaxPool**, continued, see the [Jupyter notebook](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/tests/maxpool/Test_Max_Pool_Divergence.ipynb) (Henri)
+- On the spec of mathematical operators, using [mpfr](https://www.mpfr.org/mpfr-current/mpfr.html#index-mpfr_005fpowr) (Eric)
+- Local WG (Eric)
+ - See [actions](../meetings/Local%20WGs/Tlse/agenda.md)
+## Minutes
+- Ricardo and João have explained their approach to specify **MatMul**. Having a written version of the general principles, methods (and possibly tricks...) that have been applied would be extremely useful for the team. Hopefully, this could be part of the R&J's master's thesis report.
+- On ORT, the behaviour of **Matmul** differs when applied on float null tensors and integer null tensors. Multiplying null tensors is well defined and can lead to non null tensors. We have to add a notice explaining that the behaviour of ORT differ from the spec.
+- More generally, we agree that the spec must provide a unique output even though different implementations may return different result. We will add a notice stating the possible divergence (limited to ORT). It is up to an implementer to show where his/her implementation does not comply with the spec. We may produce some kind of a "compliance report" for ORT CPU (for instance) as an example.
+- Back to **Maxpool**:
+ - we have to take care of optional outputs.
+ - the behaviour of **Maxpool** is extremely variable depending on the backend (CPU, tensortRT, CUDA). Besides the erroneous indexes when dealing with Infs, we also observe strange results on the max value themselves. Refer to [Henri's notebook](../sonnx/ops/spec/tests/maxpool/Test_Max_Pool_Divergence.ipynb) for further details.
+## Actions
+### New actions
+### Previous actions
+- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**.
+ - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides.
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+- [X] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available)
+ - Material shall be placed [here](https://github.dev/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/formal_methods/inputs/inputs-2026-02-18.md)
+- [ ] (1911-2, All) Review Jean's V&V proposal
+ - Eric to put updated slides on the repo : Done.
+ - File to be reviewed not yet delivered.
+- [ ] Actions from local work sessions
+ - (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+ - To be discussed during Feb session with Loïc.
+ - Some solutions are available. These solutions must be discussed with Loïc.
+ - Solution to be sent to Mariem first...
+ - On-going: First proposal sent by J&R ; currently being discussed with Jorge then to be discussed with Loïc
+
+# 2026/02/11 (shortened to 1h)
+## Participants
+- Henri, Ricardo, João, Jean, Mariem, Edoardo, Jean-Loup, Franck
+## Agenda
+- WG progress
+- The case of MaxPool
+- Accuracy analysis approach (Franck)
+- News from DeepGreen (Mariem)
+
+## Minutes
+- News
+ - Our work has been presented to the ERTS 2026 conference. The slides are [there](./Other_meetings/SONNX%20-%20ERTS2026.pdf).
+- Status of actions (Eric)
+ - See also [those for the local WG](./Local%20WGs/Tlse/agenda.md).
+- Work done by the Tlse WG and actions (Eric)
+ - See also [those for the local WG](./Local%20WGs/Tlse/agenda.md).
+- Discussion about the section on accuracy (see supporting slides)
+ - We have to provide the user some information to support the analysis of error, including propagated error. We provide the expression of the propagated error at first order.
+ - Examples are being developed (Franck) on **Div**, **Matmul**, and possibly **tanh**.
+ - (Note that the accurracy section shall be named `_acc.md` and be placed along with the informal specification (`op.md`). )
+
+## Actions
+### New actions
+- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**.
+### Previous actions
+- [X] (2801-1, eric) Check why Jean-loup cannot change the project's kanban
+- [X] (2801-2, all) Add a disclaimer on the tests
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+- [X] (2801-4, Eric) Add a question to Loïc about the handling of mathematical operators in the formal spec; Floats are basically handled as real extended with infs and NaN. For the rest, the semantics is that of the reals.
+- [ ] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available)
+ - Material shall be placed [here](https://github.dev/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/formal_methods/inputs/inputs-2026-02-18.md)
+- [X] (1401-5, Eric) Plan a meeting with Loïc to sort out the list of questions
+ - Planned on Feb 18th, 14:00-16:00
+- [X] (1401-6, Eric, Jean, Jean-Baptiste) Discuss a possible subject to be presented at CTiC
+ - Subject proposed by Jean-Baptiste on 2026/01/16
+ - Comments returned by Eric on 2026/01/19
+ - Waiting for Jean-Baptiste reply...
+- [X] (0312-1, Edoardo, Mohammed) Review of the broadcast operator
+ - Completed after Edoardo's comment have been taken into account.
+- [ ] (1911-2, All) Review Jean's V&V proposal
+ - Eric to put updated slides on the repo : Done.
+ - File to be reviewed not yet delivered.
+- Action from local work session
+ - (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+ - To be discussed during Feb session with Loïc.
+ - Some solutions are available. These solutions must be discussed with Loïc.
+ - Solution to be sent to Mariem first...
+ - On-going: First proposal sent by J&R ; currently being discussed with Jorge then to be discussed with Loïc
+
+# 2026/01/28
+## Participants
+- Hugo, Henri, Ricardo, João, Jean, Mariem, Edoardo, Jean-Baptiste, Jean-Loup
+## Agenda
+- Review of actions [Eric]
+- Review of project status ([Kanban](https://github.com/users/ericjenn/projects/4/views/8)
+- Finalization of Clip spec. [João, Ricardo]
+- Discussion about the "Generic Tensor Module and Potential Extension to Operators" [Ricardo and João]
+- Discussion about the "Formalization of Purely Structural Operators" [Ricardo and João]
+- Discussion about the formalization of mathematical ops [Henri]
+- Remarks from Adrien (Airbus)[Jean]
+## Minutes
+- Finalization of Clip spec. [João, Ricardo]
+ - We keep the 3 "ifs" and we add a remark that the 2nd and 3rd can be synthesized as min(m...max(...)).
+- Generic Tensor Module and Potential Extension to Operators
+ - We use a generic type and provide the necessary operator on type T.
+ - Where op: use int tensor for booleans...
+- Formalization of Purely Structural Operators
+ - Data are copied "naively" (no move)
+- Question on $tanh$
+ - There are multiple ways to *implement* a $tanh$, with variable accuracy. In reality, we are not really specifying the floating point version of the operator (e.g., we are not specifying the expected accuracy): we are specifying the operator according to the semantics of real number + infinities and NaNs. In this context, the 3 versions of the $tanh$ (let's say I1, I2 and I3 see spec) are equivalent. However, we **know** that this is not true (i.e., I1 =/= I2 =/= I3). If we want to promote one implementation rather than another, we have to write the spec for that implementation.
+ - Off meeting (eric): in that case, we should not use "x" for the multiplication, but "x." (for instance), to refer to the multiplication with floating point numbers. Otherwise, if we propose one spec, let's say $a+b$, any one can interpret it as "b+a" because, in the domain of real numbers, these two forms are strictly equivalent.
+ - Remarks from Adrien
+ - In "**Informal** specification", the adjective "Informal" is negatively connoted: we should use "Specification".
+ - The test provided by SONNX shall **not** be considered as complete in the sense of the certification objectives. They are provided "as is" for the purpose of debugging, early testing, etc. We should add a "disclaimer" in the doc.
+ - Other
+ - Focus shall be placed on the operators in the "scope" list (esp. Airbus' and Thales' who contibutes significantly to the work...
+## Actions
+### New actions
+- [ ] (2801-1, eric) Check why Jean-loup cannot change the project's kanban
+- [ ] (2801-2, all) Add a disclaimer on the tests
+- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly.
+- [ ] (2801-4, Eric) Add a question to Loïc about the handling of mathematical operators in the formal spec; Floats are basically handled as real extended with infs and NaN. For the rest, the semantics is that of the reals.
+### Previous actions
+- [ ] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available)
+ - Material shall be placed [here](https://github.dev/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/formal_methods/inputs/inputs-2026-02-18.md)
+- [X] (1401-5, Eric) Plan a meeting with Loïc to sort out the list of questions
+ - Planned on Feb 18th, 14:00-16:00
+- [X] (1401-6, Eric, Jean, Jean-Baptiste) Discuss a possible subject to be presented at CTiC
+ - Subject proposed by Jean-Baptiste on 2026/01/16
+ - Comments returned by Eric on 2026/01/19
+ - Waiting for Jean-Baptiste reply...
+- [X] (0312-1, Edoardo, Mohammed) Review of the broadcast operator
+ - Completed after Edoardo's comment have been taken into account.
+- [ ] (1911-2, All) Review Jean's V&V proposal
+ - Eric to put updated slides on the repo : Done.
+ - File to be reviewed not yet delivered.
+- Action from local work session
+ - (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+ - To be discussed during Feb session with Loïc.
+ - Some solutions are available. These solutions must be discussed with Loïc.
+ - Solution to be sent to Mariem first...
+ - On-going: First proposal sent by J&R ; currenlty being discussed with Jorge then to be discussed with Loïc
+
+# 2026/01/14
+## Participants
+*To be completed*
+## Agenda
+- Review of actions [Eric]
+- Review of project status ([Kanban](https://github.com/users/ericjenn/projects/4/views/8)
+- Discussion about issues raised by João and Ricardo
+ - Support for Integer Data Types in Tensor Modules
+ - Use generic types (clone a generic version of Tensor)
+ - [ ] (1401-1) Mariem: provide what has been done on the use of generic types and cloning...
+ - Formalization of empty tensors (C-Level)
+ - [X] (1401-2, Eric) Check how ONNX behaves with respect to empty tensors (is it possible to create an empty tensor using OP1 and resurrect it using OP2)
+ - It is not possible, see the paragraph about "null tensors" in the [glossary](../sonnx/ops/spec/informal/common/definitions.md).
+ - Memory Allocation and C-Code Behavior
+ - Consensus: use **parameter**
+ - cdrivers
+ - [X] (1401-3) Find a reference to justify the precedence order of `max a b + c = max (a,b) + c`
+ - The precedence is defined in the Why3 manual. For `max`, the rule about precedence of functions applies. See picture below
+ 
+ - NaNs representation in Why3
+ - The high-level spec should match with the informal spec. So, if there are NaNs and Inf in the informal spec, then we should have NaNs and Infs in the high-level spec too. The refinement (high=>low) should focus on the structure of tensors not on the operations. In addition, we have to be clear that the formal spec does not model floating point but just R extended with special values.
+ The interpretation of the two levels of spec must be discussed and validated with Loïc (esp. with respect to special values...).
+ - Precedence Order in Operations
+- Status of activities on AIDGE [Mariem]
+ - See [slides]()
+- Next meeting local WG (Toulouse)
+ - Agenda is [there](./Local%20WGs/Tlse/agenda.md)
+- Discuss opportunity to present our work in CTiC?
+- Review of [ONNX IR](https://github.com/onnx/onnx/blob/main/docs/IR.md). Who?
+- Announces
+ - [DATE 2026 workshop](./attachments/date2026_workshop.pdf), April 20-22, Verona, Italy
+ - [ERTS 2026](https://conference-erts.org/), February 5-6, Toulouse, France
+ - [Certification Together (CTiC)](https://www.certification-together.com/)
+## Minutes
+*To be completed*
+## Actions
+### New actions
+- [ ] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available)
+- [ ] (1401-5, Eric) Plan a meeting with Loïc to sort out the list of questions
+- [ ] (1401-6, Eric, Jean, Jean-Baptiste) Discuss a possible subject to be presented at CTiC
+### Previous actions
+- [X] (1712-1, Joao, Ricardo) Give examples of values leading to discrepancies with operator **Range**
+(see Range folder review)
+ - L=6459650.5, M=1928958.0 => 8388608.0 in fp32
+ - L=6459650.5, M=1928958.0 => 8388608.5 in fp64
+ - 8388608 is $2^23$. At this magnitude, ULP=1 (minimum distance between two successive values) hence 2^23+0.5=2^23, 0.5 is absorbed.
+- [X] (1712-2, Eric, Jean) Check opportunity to present our work at CTIC
+ - Date: June 17th-19th 2026, Toulouse, see [CfP](https://www.certification-together.com/index.php/call-for-papers/)
+ - Abstract or proposal for workshop expected before mid-March 2026
+ - Our work will be presented at ERTS2026 (in **February**) and (partially) DATE2026 (in April). Would it make sense to present an update to CTIC in **June**?
+ - Jean: we have to focus on certification => If we have something to present on (e.g, ARP). Jean-Baptiste: we have already some material => To be discussed.
+- [ ] (0312-1, Edoardo, Mohammed) Review of the broadcast operator
+ - Edoardo : Done, analyzed by Jean-Loup, still some point to be analyze / discussed
+ - Mohammed : To be done during Jan. 2026
+- [ ] (1911-2, All) Review Jean's V&V proposal
+ - Eric to put updated slides on the repo : Done.
+ - File to be reviewed not yet delivered.
+- Action from local work session
+ - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+ - Some solutions are available. These solutions must be discussed with Loïc.
+ - Solution to be sent to Mariem first...
+ - On-going: First proposal sent by J&R ; currenlty being discussed with Jorge then to be discussed with Loïc
+
+
+# 2025/12/17
+## Participants
+ - _To be completed_
+## Agenda
+- Review of actions [Eric]
+- Project status ([Kanban](https://github.com/users/ericjenn/projects/4/views/8)
+- Sync of the project Kanban (esp. formal specs)
+- Discussion about João and Ricardo's issues:
+ - On **Range** (pb with FP),
+ - On **Reshape** (shape inference)
+- Status of formal spec of **Conv** (and FP values modeling)
+- Numerical accurracy, the case of [**Conv**](https://github.com/ericjenn/working-groups/blob/spec-with-numerical-accuracy-info/safety-related-profile/sonnx/ops/spec/informal/conv/conv.md)
+ - Separation of accuracy from the rest of the spec (e.g., "conv_acc.md").
+- About guidelines
+ - for formal spec...
+ - for numerical precision...
+- About the opportunity to organize an "event" in Feb. or March 2026
+ - Objective: present the status of our activity to the community
+ - Have **at least** one full example covering: informal spec, tests, formal spec (+proofs), generated code, integration in Aidge (ideally, a simple graph...)
+ - Have all guidelines : informal (OK), formal (todo), numerical accuracy (todo), tests (todo)
+- About contacts to increase participation...
+## Minutes
+ - See agenda.
+ - Possibe opportunity to present our work at the CTIC conf. (CfP in Jan. 2026).
+## Actions
+### New actions
+ - [ ] (1712-1, Joao, Ricardo) Give examples of values leading to discrepancies with operator **Range**
+ - [ ] (1712-2, Eric, Jean) Check opportunity to present our work at CTIC
+### Previous actions
+- [ ] (0312-1, Edoardo, Mohammed) Review of the broadcast operator
+ - Edorardo : Done
+ - Mohammed : To be done during Jan. 2026
+- [X] (0312-2,Ricardo, Joao) Open PR on unsqueeze and flatten
+- [X] (0312-3,Jean) Update the project's [Kanban](https://github.com/users/ericjenn/projects/4/views/8) to reflect the actual status of SONNX...
+- [X] (1911-1, Jean) Put the contents of the [presented slides](./slides/2025-11-20-Jean-verification-plan.pdf) into some nice markdown file, and put it in [guidelines area](../sonnx/ops/docs/guidelines/) for review
+ - Eric to put updated slides on the repo : Done.
+- [ ] (1911-2, All) Review Jean's proposal (see above)
+ - File to be reviewed not yet delivered.
+- [X] (0511-3, Eric) Give access to the SONNX github "project" in order to facilitate the management of the artifacts statuses.
+- [X] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip
+ - There is a dependency to the execution platform. The test setup must be described along with the test script.
+ - [X] Check if there is a possible way to set the CPU provider so that test pass.
+ - When running the script, the provider has to be chosen (with different types)
+- Action from local work session
+ - [X] Modify existing operator specifications to comply with new conventions.
+ - Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.)
+ - [-] (Mariem) Give R&J a feedback on the formal spec (in particular: recall a few principles to be followed).
+ - Cancelled
+ - [X] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$)
+ - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+ - Some solutions are available. These solutions must be discussed with Loïc.
+ - Solution to be sent to Mariem first...
+ - On-going: First proposal sent by J&R ; currenlty being discussed with Jorge then to be discussed with Loïc
+
+# 2025/12/03
+## Participants
+ - Eric, João, Jean-Loup, Edoardo, Ricardo Silva, Mariem, Franck, Jean, Nicolas, Hugo, Mohammed, Henri.
+## Agenda
+- Review of actions [Eric]
+- DATE 2026 workshop 20-22 april, Verona (❤️) [Eric]
+- Status of SONNX as of end 2025 [Eric]
+ - Where should we be?
+ - Where are we?
+ - Where do we want to go now?
+ - How do we go there?
+ - Funding... proposal by IRT
+## Minutes
+- See agenda ;-)
+## Actions
+### New actions
+
+- [ ] (0312-1,Edorado, Mohammed) Review of the broadcast operator
+- [ ] (0312-2,Ricardo, Joao) Open PR on unsqueeze and flatten
+- [ ] (0312-3,Jean) Update the project's [Kanban](https://github.com/users/ericjenn/projects/4/views/8) to reflect the actual status of SONNX...
+
+### Previous actions
+- [ ] (1911-1, Jean) Put the contents of the [presented slides](./slides/2025-11-20-Jean-verification-plan.pdf) into some nice markdown file, and put it in [guidelines area](../sonnx/ops/docs/guidelines/) for review
+ - Eric to put updated slides on the repo : Done.
+- [ ] (1911-2, All) Review Jean's proposal (see above)
+ - File to be reviewed not yet delivered.
+- [ ] (0511-3, Eric) Give access to the SONNX github "project" in order to facilitate the management of the artifacts statuses.
+- [ ] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip
+ - There is a dependency to the execution platform. The test setup must be described along with the test script.
+ - [ ] Check if there is a possible way to set the CPU provider so that test pass.
+- Action from local work session
+ - [ ] Modify existing operator specifications to comply with new conventions.
+ - Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.)
+ - [ ] (Mariem) Give R&J a feedback ont the formal spec (in particular: recall a few principles to be followed).
+ - [ ] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$)
+ - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+ - Some solutions are available. These solutions must be discussed with Loïc.
+ - Solution to be sent to Mariem first...
+
+# 2025/11/19
+## Participants
+ - Eric, Edoardo, Franck, Ricardo Silva, João Machado, Henri, Mariem, Jean-Loup, Mohammed, Jean, Andreas
+## Agenda
+- Review of actions [Eric]
+- Review process [Jean]
+- Status on broadcasting [Jean-Loup]
+- Back to the factorization of operators...
+- Fun facts
+ - The case of NaNs in Clip [Ricardo and Joao]
+ - The case of empty tensors... [Ricardo and Joao] (see [here](https://onnx.ai/onnx/repo-docs/IR.html#tensor-definition))
+- Management of work using github's "project management" [Eric] (see [here](../sonnx/ops/docs/guidelines/lifecycle.md))
+## Minutes
+ - All the above items minus "broadcasting" and "fun facts" and (to be addressed during next meeting).
+ - Jean's slides can be found [here](./slides/2025-11-20-Jean-verification-plan.pdf)
+ - Franck's slides can be found [there](./slides/2025-11-20-Franck-numerical-accuracy.pdf)
+ - In order to involve more people, we also have to provide more guidelines. This includes (procedure + example)
+ - Guidelines on test using
+ - Guidelines on formal specification and proof
+ - Guidelines on specifying numerical errors
+ - Franck's slides already provide a good example
+## Actions
+### New actions
+- [ ](1911-1, Jean) Put the contents of the [presented slides]() into some nice markdown file to be put in [guidelines area](../sonnx/ops/docs/guidelines/)
+- [ ](1911-2, All) Review Jean's proposal (see above)
+### Previous actions
+- From work session
+ - [ ] Modify existing operator specifications to comply with new conventions.
+ - Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.)
+ - [X] (Eric) Create a side note about "empty tensors" (To be placed in "doc").
+ - Not necessary: tensors (including scalar and empty tensors) are well-defined in the ONNX IR documentation ([here](https://onnx.ai/onnx/repo-docs/IR.html#tensor-definition))
+ - [X] (Mariem) Give R&J a pointer to Why3 where NaN are handled.
+ - [ ] (Mariem) Give R&J a feedback ont the formal spec (in particular: recall a few principles to be followed).
+ - [X] (Jean-loup) Separate the spec of the pseudo op broadcasting and the max operator
+ - [X] (Eric) Provide explanations about the new way to manage modifications (using Pull Requests).
+ - See this [note](../sonnx/ops/docs/guidelines/lifecycle.md)
+ - [ ] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$)
+ - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+- [X] (0511-2, Jean) Provide a first draft of the document about verification.
+- [ ] (0511-3, Eric) Give access to the SONNX github project in order to facilitate the management of the artifacts statuses.
+- [ ] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip
+ - Eric :
+ > See ORT issue #15304 that was asking for the support of int32, uint32 for Clip. Solved by #15306, so ORT supports at least `uint32` and `int32`.
+ > Seems to work on Google Collab:
+ - 
+ - Ricardo & João:
+ > We found this, onnxruntime/docs/OperatorKernels.md at main · microsoft/onnxruntime.
+ > And for CPUProvider the following types are not supported: INT16, UINT16,BFLOAT16.
+ > We were following ONNX documentation Clip - ONNX 1.21.0 documentation and
+ > we didn’t expect that some types are only supported by specific providers.
+ > For example, int16 and uint16 are not supported by CPUProvider but are supported by DmlExecutionProvider.
+ > BFloat16 is said to be supported by ONNX, although we didn’t find any provider that does so.
+ > Apparently, this is no longer a doubt but we will have to check both these documentation to ensure that our provider(CPUProvider) supports the respective types. In this context the test generation will also depend on the provider being used
+
+### Past actions
+- [-] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+ - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment).
+ - Action cancelled.
+
+
+# 2025/11/05
+## Participants
+Jean, Eric, Mariem, Ricardo, João, Jean-Loup, Franck, Dumitru, Henri, Jean-Baptiste, Mohammed.
+
+## Agenda
+ - Review of actions [Eric]
+ - Status on repo organization [Mariem]
+ - Status on operator informal and formal specs [All]
+ - Discussion about setting up a real and clear management of item status (update the existing [table](../meetings/operator_spec_sub_wg/worksharing.md), use Github features?) [All]
+ - A few words about the integration in AIDGE? [Mariem]
+ - Meeting on graph specification [Eric]
+ - Next work session...
+ - Discussion of the overall V&V strategy [Jean]
+## Minutes
+ - Status on repo organization: operators have been moved. Problem to generate the C code fo the where op (Loïc will have a look at this)
+ - Jean, Eric, Marie: DIV, ADD, MUL, SUB / SOFTMAX, PAD, RELU
+ - João, Ricardo: CLIP (update + hypothesis + formal: pb with NaNs), SLICE (informal, hypothesis, formal at high level, working on the proof)
+## Actions
+### New actions
+ - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link.
+ - [ ] (0511-2, Jean) Provide a first draft of the document about verification.
+ - [ ] (0511-3, Eric) Give access to the SONNX github project in order to facilitate the management of the artifacts statuses.
+ - [ ] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip
+
+### Past actions
+- [X] (2210-1, Mariem) Write a "readme.md" to explain the (new) organization of the repo (for the SONXX products)
+- [X] (2210-2, Mariem) Move all operators to the new location
+- [X] (2210-3, Eric, Jean-Baptiste, Jean) Review Hypothesis-based test cases description from João and Ricardo (on **conv**)
+ - Eric: [Done](../documents/onnx/ops/spec/informal/conv/reviews/eric_tests.py)
+- [X] (2210-4, Eric, Jean, Mariem) Review João and Ricardo's work on the operator **clip**
+ - [Done](../documents/profile_opset/clip/reviews/jean-eric.md)
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+ - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment).
+### Long term actions
+- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+
+
+
+# 2025/10/22
+## Participants
+ Jean, Eric, Franck Védrine, Ricardo Silva, Edoardo Manino, João Machado, Hugo, Jean-Baptiste Rouffet, Mariem, Henri Belfy, Jean-Loup Farges et Nicolas Valot
+## Agenda
+ - Events
+ - Technical presentation to WG114 on Oct. 16th ([see slides](./Other_meetings/SONNX%20-%20WG114-%20oct-2025.pdf))
+ - Poster at Mobilit'AI (Toulouse)
+ - Review of actions [Eric]
+ - Status on repo organization [Mariem]
+ - Status of testing activities [João, Ricardo]
+ - How to extend this work?
+ - Other activities
+ - Local activities on operators [Eric, Jean, Mariem]
+ - Meeting on graph [Eric]
+## Minutes
+ - See agenda.
+ - Jean-Baptiste attended the Mobilit'AI conference. He met people interestied by our work. They were wondering how we will ensure compliance between the design model (e.g., Pytorch) and the exported SONNX model and between the SONNX model and its implementation for a given target... These are very good question that we do not really address in SONNX. This may be something to be done collectively on the basis of some existing, open source training and implementation framework (such as CEA's [AIDGE](https://projects.eclipse.org/projects/technology.aidge) platform.).
+ - Eric presented the SONNX work to the WG114. There was a great interest on the numerical aspect. Providing a working example of the approach developed by Franck would be very appreciated...
+ - Ricardo and João have completely covered operator **clip** (from informal spe to formal with proof)... That's really GREAT!!! Kudos to those extremely efficient and proactive students (and their advisers)!!!
+ - Jean propose that Ricardo and João next operator be one common to Thales and Airbus' use cases (e.g., **maxpool**, **relu**, etc.).
+ - Jean-Loup's working on operator **max**. He is addressing the question of broadcasting. For the moment, the broadcasting operating may be specified for **max**. if it can be "factorized" (i.e., be expressed in such as way it can apply to other operators), we will do it later.
+ - A meeting on the graph semantics is planned on Friday. It was initially planned to address the formal specification of an ONNX graph. I (eric) propose that we shorten the meeting and address, first, the informal specification of a graph, including control flow operators (such as **loop** and **if**...).
+## Actions
+### New actions
+- [ ] (2210-1, Mariem) Write a "readme.md" to explain the (new) organization of the repo (for the SONXX products)
+- [ ] (2210-2, Mariem) Move all operators to the new location
+- [ ] (2210-3, Eric, Jean-Baptiste, Jean) Review Hypothesis-based test cases description from João and Ricardo (on **conv**)
+- [ ] (2210-4, Eric, Jean-Mariem) Review João and Ricardo's work on the operator **clip**
+### Past actions
+- [X] (0810-1, all) Review [Jean-Baptiste's document on ED324](../analyses/certification/SONNX_ED324_interest.docx)
+ - Eric comments (in the doc)
+- [X] (0810-2, all) Review [Henri's spec of DIV](../documents/profile_opset/div/div.md)
+ - See work done with Mariem and Jean on div, mul, add...
+- [X] (0810-3, all) Review [Mariem's proposal](tbc) for a new repo
+- [ ] (1009-1, Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting.
+ - Among the questions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks?
+ - Waiting for Eric
+- [X] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat`
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+ - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment).
+### Long term actions
+- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+
+
+# 2025/10/08
+## Participants
+ *To be completed*
+## Agenda
+ - Presentation on MLIR/ONNX-MLIR by [Alexandre EICHENBERGER](https://research.ibm.com/people/alexandre-eichenberger). Alexandre's presentation is available on [LFX](https://openprofile.dev/my-meetings).
+ - New organization of repo [Mariem]
+ - Feedback on testing activities [Ricardo,João], see their [reviews](../documents/profile_opset/conv/reviews/review-joao-ricardo.md)
+ - Review of actions [Eric]
+ - Feedback on SONNX <=> ED 324 traceability [Jean-Baptiste], see [his document](../analyses/certification/SONNX_ED324_interest.docx)
+## Minutes
+See agenda and new actions
+## Actions
+### New actions
+- [ ] (0810-1, all) Review [Jean-Baptiste's document on ED324](../analyses/certification/SONNX_ED324_interest.docx)
+- [ ] (0810-2, all) Review [henri's spec of DIV](../documents/profile_opset/div/div.md)
+- [ ] (0810-3, all) Review [Mariem's proposal](tbc) for a new repo
+### Past actions
+- [ ] (1009-1, Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting.
+ - Among the questions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks?
+ - Waiting for Eric
+- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat`
+- [X] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability
+ - In progress. To be reviewed during next meeting.
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+ - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment).
+### Long term actions
+- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+
+
+# 2025/09/24
+## Participants
+ *To be completed*
+## Agenda
+ - Review of actions
+ - News about work on formal specs [Mariem, Salomé]
+ - Presentation "Using Hypothesis to test SONNX operators" [João + Ricardo]
+ - Status on [contributions](./operator_spec_sub_wg/worksharing.md)
+## Minutes
+See agenda.
+## Actions
+### New actions
+No new action.
+### Past actions
+- [ ] (1009-1, Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting.
+ - Among the questions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks?
+ - Waiting for Eric
+- [X] (1009-2, Eric) Mail to be send on SONNX mailing list for contributions
+ - Request sent: no answer...
+- [X] (2708-1, Eric) Give short guidelines about error / failure conditions.
+ - See minutes of 2025/08/27 meeting.
+ - Done.
+- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat`
+- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability
+ - In progress. To be reviewed during next meeting.
+- [-] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md))
+ - Cancelled
+- [X] (1806-4, Eric) Organize a "physical" working session on the graph specification
+ - Poll link sent during meeting. Possibility to organize a "physical" meeting at IRT.
+ - Date fixed on Oct. 24th
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+ - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment).
+### Long term actions
+- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - Cancelled
+- [-] (1205-6, Eric, Jean) See how to proceed with tool implementation
+ - Cancelled
+- [-] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Cancelled.
+
+# 2025/09/10
+## Participants
+ *To be completed*
+## Agenda
+ - Actions
+ - Code generation from Why3 spec. [Mariem + Salomé]
+ - Actions about testing?
+ - Actions to reactivate the WG
+ - Workshop with Loïc
+
+## Minutes
+- About "failures" (see action 2708-1 below)
+ - Add in the specification that the implementer have to specify if they have actually done some kind of failure analysis or not. For instance "The conditions of occurrence of failure conditions (overflows, underflows, wraparound, etc.) have not been analyzed."
+- Mariem: Presentation of the archive provided by Loïc's that covers the complete process, from formal specification to C code generation for the "where" operator.
+ - See the [document](../meetings/c_code_generation/sources.md) explaining the content of the archive.
+ - The "interface" of the Tensor formal specification has slightly changed (use of `List`s instead of `Sequence`s. ). This will require some (slight) modifications of the existing formal specifications. *To be checked*.
+ - Next step is to use this as an example to implement `concat` and other operators.
+
+## Actions
+- [ ] (1009-1, Eric+Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting.
+ - Among the quesrtions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks?
+- [ ] (1009-2, Eric) Mail to be send on SONNX mailing list for contributions
+### New actions
+### Past actions
+- [ ] (2708-1, Eric) Give short guidelines about error / failure conditions.
+ - See minutes of 2025/08/27 meeting. The "rules" could be:
+ > - If the conditions can be expressed on the inputs (e.g., $x \ge 0$ for `sqrt(x)`) , add a condition on the input domain in the specification
+ > - If the condition cannot be expressed on (or "propagated to") the inputs, express the failure condition at the appropriate level (for instance:
+ "When computing a matrix multiplication, the result of the accumulation may overflow and the result may "wraparound", leading to an incorrect result."
+ > - If possible, give a link to the location in the specification where this accumulation is done.
+ > - Note that some smart implementation may avoid the problem. For instance, when accumulating 2 bits values on a 2 bits accumulator, "3+3-3-3" overflows while "3-3+3-3" does not.
+ > - So, the relevance of the warning (i.e., "When computing [...]") actually depends on the implementation, but we know that -- in principle -- there might be some cases where an overflow can occur. And this is due to the the very fact that the operation accumulates values.
+ > - If no indication is given about occurrence of a "failure", this means that the specification is complete and defines what is the expected value for **any** input.
+ > - In addition, provide "recommendations" about the implementation. A typical example is the one of `SoftMax` where we could recommend the use of the `-max(Xi)` trick.
+- [X] (2708-2, Mariem) Put Loïc's contribution in the repo.
+- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat`
+- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability
+ - In progress. To be reviewed during next meeting.
+- [X] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators?
+ - Introduce a specific "broadcast" operator to make the operation explicit in the spec. See 1607-3.
+ - Conclusion meetings at AI => do a dedicated meeting in SONNX to share the position (see followup 1009-1))
+- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md))
+- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification
+ - Poll link sent during meeting. Possibility to organize a "physical" meeting at IRT.
+ - Date fixed end of next week.
+- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec)
+ - Modalities to be discussed
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+ - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment).
+### Long term actions
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form to support the analysis
+
+# 2025/08/27
+## Participants
+ *To be completed*
+## Agenda
+ - Actions [eric]
+ - Status of [guidelines](../documents/profile_opset/guidelines.md) and application to [`conv`](../documents/profile_opset/conv/conv.md) and [`concat`](../documents/profile_opset/concat/concat.md)
+ - Error / failure conditions; Eduardo's remarks on [discussion elements](../meetings/errror%20conditions/2025-07-30%20-%20Discussions.md) with Eduardo's comments.
+ - Code generation
+ - Link with the DeepGreen project (see [slide](./slides/AIDGE.pptx))
+ - Events (Mobilit'AI)
+ - Presentation Alexandre Eichenberger on ONNX-MLIR on 08/10/2025
+## Minutes
+- Continuation of the discussion about [Failure modes / error conditions](../meetings/errror%20conditions/2025-07-30%20-%20Discussions.md) with Eduardo's comments. on the basis of Edoardo's comments:
+ - It seems that we converge on a baseline where we would simply indicate whether or not the operator may fail (due to a division by zero, wrap-around, etc.) and, if possible, in which conditions such situation may occur.
+ - If the conditions can be expressed on the inputs, this means that we could possibly add the condition in the specification (a conditions on the input domain)
+ - If the condition cannot be expressed on (or "propagated to") the inputs, we express the condition at the appropriate level (for instance:
+ > "When computing a matrix multiplication, the result of the accumulation may overflow and the result may "wraparound", leading to an incorrect result."
+ - We may give a link to the location in the formula where this accumulation is done.
+ - Note that some smart implementation may avoid the problem. For instance, when accumulating 2 bits values on a 2 bits accumulator, "3+3-3-3" overflows while "3-3+3-3" does not.
+ - So, the relevance of the warning (i.e., "When computing [...]") actually depends on the implementation, but we know that -- in principle -- there might be some cases where an overflow can occur. And this is due to the the very fact that the operation accumulates values.
+ - If no indication is given about occurrence of a "failure", this means that the specification is complete and defines what is the expected value for any input.
+ - In addition, we will also provide "recommendations" about the implementation. A typical example is the one of `SoftMax` where we could recommend the use of the `-max(Xi)` trick.
+- Concerning code generation from the Why3 spec.: Loïc has provided us with an example. We are currently analyzing it and will try to apply it on `conv` and `concat`.
+## Actions
+### New actions
+- [X] (2708-1, Eric) Give short guidelines about error / failure conditions.
+ - See minutes of 2025/08/27 meeting.
+- [ ] (2708-2, Mariem) Put Loïc's contribution in the repo.
+- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat`
+### Past actions
+- [X] (3007-1, Eric, All) Collect ideas exchanged on Error Conditions during the meeting. To be discussed during next meeting.
+ - Document is [here](../meetings/errror%20conditions/2025-07-30%20-%20Discussions.md) with Eduardo's comments.
+- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability
+- [ ] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators?
+ - Introduce a specific "broadcast" operator to make the operation explicit in the spec. See 1607-3.
+- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md))
+- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification
+- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec)
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+ - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment).
+- [-] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+ - Cancelled
+### Long term actions
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form to support the analysis
+
+
+# 2025/07/16
+## Participants
+ *To be completed*
+## Agenda
+ - Actions [eric]
+ - A few words about the idea of a core set of operators
+ - See note [here](./core_ops/core_ops.md) [eric]
+ - Guidelines
+ - See [here](../documents/profile_opset/guidelines.md).
+ - Status of MLIR presentation by Alexandre. (2025/10/08) [eric]
+ - Status of work on formal spec [Mariem and Salomé]
+ - Presentation of work on the formalization of Scalar and the first results about C code generation.
+ - Ideas on error condition spec [Franck]
+ - Status of discussions about tests (Andreas, Justin, Christian, Eric, Jean) [eric+jean]
+ - See [minutes](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/testing/2025-07-29-tests.md)
+## Minutes
+ - See above.
+ - See Franck slides [here](./errror%20conditions/2025-07-30-SONNX_error.pdf)
+ - See elements discussed about the errors conditions [here](./errror%20conditions/2025-07-30%20-%20Discussions.md)
+## Actions
+### New actions
+- [ ] (3007-1, Eric, All) Collect ideas exchanged on Error Conditions during the meeting. To be discussed during next meeting.
+### Past actions
+- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability
+- [ ] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators?
+ - Introduce a specific "broadcast" operator to make the operation explicit in the spec. See 1607-3.
+- [X] (1607-3, Dumitru) Write a few lines to explain the "mixed approach" to handle broadcasting.
+ - Mail exchange collected in [here](./broadcasting/dumitru-2025-07-21.md)
+- [X] (1607-4, Franck) Write a few lines to explain the approach to handle errors: ask implementers to provide error conditions
+ - Description sent on 2025/07/16
+- [X] (0207-1, Eric) Do a "synthesis" of the discussion about overflows, etc., discuss with the WG, find a consensus, add to the guidelines...
+ - First proposal in the [guidelines](../documents/profile_opset/guidelines.md). To be discussed (see Franck's pres.)
+- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md))
+- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification
+- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec)
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+### Long term actions
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form to support the analysis
+
+
+
+# 2025/07/16
+## Participants
+ Salomé, Jean, Tomé, Cong, Jean-Baptiste, Franck, Eric, Dumitru, ...
+## Agenda
+ - Actions
+ - A few words about shape inference and broadcasting
+ - A few words about the "minimal corpus of operators"
+ - A few words about runtime errors
+ - Feedback on presentation to WG114 (see [slides](./Other_meetings/SONNX%20-%20WG114.pdf))
+ - ED324 = ARP 6983
+ - Feedback on meeting with DeepGreen
+ - Meeting with ONNX infra about testing
+ - Opportunities for presentation of our work:
+ - Mobilit'AI.
+ - Stragegy for next period
+## Minutes
+ - A few words about shape inference and broadcasting
+ - Forbidding broadcasting may reveal extremely penalizing because making it "explicit" boils down to creating actual tensors whereas broadcasting is essentially a manipulation of indexes done at operation level.
+ - We have to check if this restriction is necessary, sensible, and applicable.
+ - See actions (1607-2) and (1607-3)
+ - A few words about the "minimal corpus of operators"
+ - Some operators can be described on the basis of simpler, "atomic" operators. For instance, a `softmax` can be described as the composition of `Exp` and `ReduceSum`. It may also be described as $s(z_i) = {e^{z_i} \over \sum_{j=1}^K e^{z_j}}$. A `Relu` can also be described as a combination of operators, etc.
+ - Do we want to apply this modular approach?
+ - What would be the "minimal corpus of operators"?
+ - Could we have a "non-modular" informal specification (that describes the operation using a mathematical formula) and a modular formal specification?
+ - A few words about runtime errors
+ - See this [document](./errror%20conditions/error_conditions_2.md)
+ - The question is to define a strategy to specify the error condition that may occur during the execution of an operator.
+ - In the FP domain, we agree that no exception is considered but that we use the IEEE special values Inf, NaN to propagate the errors up to the operator's output.
+ - We have to do a systematic analysis of the error conditions and indicate what are the possible error conditions (e.g., in the case of the SoftMax : overflow). The specification may still indicate that no error shall occur (i.e., no NaNs) because we know (as it is the case for SoftMax) that there is a means to avoid it (e.g., in the case of SoftMax : substract the max value). This means that all implementations will have to apply this means (or something equivalent). Otherwise, the specificaton of the operator shall indicate in the "Error conditions" section what errors can happen (e.g., an overflow) with a NaN as a result.
+ - Franck proposes an approach in which the implmeter would provide the description of error behaviour (see 1607-4)
+ - For integer operations, we will provide the **exact spec** of the operations. For signed ops, the specification will show the 2's complement description of the operation, see [here](./errror%20conditions/error_conditions_2.md))
+ - Feedback on presentation to WG114 (see [slides](./Other_meetings/SONNX%20-%20WG114.pdf))
+ - Presentation was appreciated.
+ - One slide or two on ED 324 <=> SONNX traceability must be done (see 1607-1)
+ - Feedback on meeting with DeepGreen
+ - SONNX will be used to specify (and partically provide code) for a C-code backend
+ - Meeting with ONNX infra about testing
+ - Meeting planned on July 29th with people from the infra WG (Andreas, Justin, Christian).
+ - Opportunities for presentation of our work:
+ - Mobilit'AI.
+ - We will probably present a poster à that occasion.
+ - Stragegy for next period
+ - For the informal specification
+ - Phase 1: Clean-up and ensure consistency between the guidelines and the 3 cononical examples (CONV2D, CONCAT, and aanother simple operator)
+ - Phase 2: mailing to the potential contributor to ask for condibution on the basis of the guidelines and existing examples
+ - Phase 3: meeting to present the approach and share work
+ - The same approach shall be followed for the formal specification.
+ - Presentation of the `Add` operator by Salomé.
+## Actions
+### New actions
+- [ ] (1607-1) Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 tracaibility
+- [ ] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators?
+- [ ] (1607-3, Dumitru) Write a few lines to explain the "mixed approach" to handle broadcasting.
+- [ ] (1607-4, Franck) Write a few lines to explain the approach to handle errors: ask implementers to provide error conditions
+- [X] (1607-6, Tomé) Provide the description of the 2 interships.
+ - Description sent on 2025/07/16
+### Past actions
+- [ ] (0207-1, Eric) Do a "synthesis" of the discussion about overflows, etc., discuss with the WG, find a consensus, add to the guidelines...
+- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md))
+- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification
+- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec)
+- [X] (1806-6, Eric) Initiate the specification of matrix multiplication
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+### Long term actions
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form to support the analysis
+
+# 2025/07/02
+## Participants
+- Salomé, Alexandre, Jean, Sebastian, Cong, Mariem, Jean-Baptiste, Eric,...
+## Agenda
+- Status of actions
+- Misc news:
+ - Feedback on June 20th workshop on formal specification and verification (Eric, Mariem, Jean)
+ - Structure of repo for Why3 formal specifications (Mariem) - A question about model readability (Eric)
+ - Slides for WG114 (see [here](./Other_meetings/SONNX%20-%20WG114.pdf) (draft), Eric)
+ - Issues [see [here](https://github.com/onnx/onnx/issues/3651)]
+ - Back to action 0412-6...
+ - Two questions
+ - What shall we "say" about inner overflows, division par zeros, etc.?
+ - *Do we need the model to be human-readable?*
+ - For information: SIONNX (see [here](https://github.com/alibaba/sionnx))
+ - Description of the algorithm (python). Example for [conv](https://github.com/alibaba/sionnx/blob/master/include/conv.algorithm)
+ - Description of the signature. Example for [cov](https://github.com/alibaba/sionnx/blob/master/include/onnx_conv.td)
+## Minutes
+- Long discussion about overflows etc. See action (0207-1).
+## Actions
+### New actions
+- [ ] (0207-1, Eric) Do a "synthesis" of the discussion about overflows, etc., discuss with the WG, find a consensus, add to the guidelines...
+### Past actions
+- [X] (1806-1, Eric) Provide a complete (simple) spec example for 1 op that can be reproduced on the other ops...
+ - See [matmul](../documents/profile_opset/matmul/matmul.md)
+- [X] (1806-2, Mariem) Provide Franck with the C code of the conv2d operator.
+ - Done. The code is [here](./attachments/conv2d.c).
+- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md))
+- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification
+- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec)
+- [ ] (1806-6, Eric) Initiate the specification of matrix multiplication
+- [X] (1806-7, Jean-Baptiste) Provide Eric with ARP/SONNX analysis material
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+ - Done on the [matmul](../documents/profile_opset/matmul/matmul.md)
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+### Long term actions
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form to support the analysis
+
+
+# 2025/06/18
+## Participants
+- Eric, Mariem, Salomé, Alex, Henri, Dumitru, Jean-Baptiste, Mohammed, Franck, Jean-Loup, Jean
+## Agenda
+- Status of actions.
+- Misc news:
+ - Presentation to ONNX meetup (["video"](./general/2025-06-09%20-%20MEET-UP/SONNX%20-%20Meetup%202025.7z))
+ - Review and update of ops...
+ - The existing specifications must be updated to comply with the [specification guidelines](../documents/profile_opset/guidelines.md). A first pass has been done on [`abs`](../documents/profile_opset/abs/abs.md).
+ - Request for participation to work on the graph execution
+ - Jean and Jean-Loup are OK to help. Eric to organize a working session. (See action 1806-4)
+ - Eric to present SONNX to the WG114. One slide about the ARP/SONNX mapping could be useful. (see action 1806-7)
+- Numerical accuracy
+ - Franck is preparing some elements to be put in the guidelines.
+ - Franck is also working on the analysis of the matrix multiplication. It could be wise to specify this operator in order to have a complete example including numerical accuracy analysis (see action 1806-6).
+ - He needs some C code for the conv2D. (see action 1806-2)
+ - Numerical analysis verification will be computed by executing the C++ code. The C code of the operators (generated using Why3) will be integrated as is. The tool leverage's C++ operator overloading capability.
+- The number of participants to the bi-weekly meetings is decreasing steadily...
+ - Give a "good" example of spec and invite people in the mailing list to contribute...
+ - Organize new presentations (see action 1806-3)
+- Discussion about June 20th second workshop on formal methods (off-main meeting).
+ - This workshop concerns those that have been involved in the first workshop (other may join, please contact me). It will take place at IRT. A link will be provided.
+ - On the basis of what has been done on conv2D, concat, and graph, clarify/complete the method, obtain guidelines to carry out proofs, obtain material to support self-training, obtain guidelines to generate C code, etc.
+
+## Actions
+### New actions
+- [ ] (1806-1, Eric) Provide a complete (simple) spec example for 1 op that can be reproduced on the other ops...
+- [X] (1806-2, Mariem) Provide Franck with the C code of the conv2d operator.
+ - Done. The code is [here](./attachments/conv2d.c).
+- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md))
+- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification
+- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec)
+- [ ] (1806-6, Eric) Initiate the specification of matrix multiplication
+- [ ] (1806-7, Jean-Baptiste) Provide Eric with ARP/SONNX analysis material
+
+### Past actions
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+ - First trial on something simpler than the conv (matrix multiplication).
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+### Long term actions
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form
+
+# 2025/06/04
+## Participants
+- Nicolas, Salomé, Sebastian, Henri, Jean-Loup, Jean, Eric, Jean-Baptiste, Frédéric, Eduardo,...
+## Agenda
+- Status of actions.
+- Misc news:
+ - ONNX meetup on June 9th: a ["video"](./general/2025-06-09%20-%20MEET-UP/SONNX%20-%20Meetup%202025.7z) has been prepared.
+ - The abstract of our [paper on SONNX](../documents/publications/ERTS2026/2025-06-02%20SONNX%20-%20ERTS%202026%20abstract%20-%20final.pdf) has been submitted to [ERTS 2026](https://conference-erts.org/).
+ - Salomé has updated the informal and formal specification of the ``concat`` operator. Will be pushed soon.
+ - Frédéric has presented his first attempt to specify errors for operators using floating point values.
+ - As a proof of concept, it has been applied to the [``abs``](https://github.com/ericjenn/working-groups/blob/spec-with-numerical-accuracy-info/safety-related-profile/documents/profile_opset/add/abs.md) and [``add``](https://github.com/ericjenn/working-groups/blob/spec-with-numerical-accuracy-info/safety-related-profile/documents/profile_opset/add/add.md) operators.
+ - The specification gives the properties that an implementation shall satisfy considering the errors due to the floating point arithmetic. Methods errors are not considered. The properties are "conservative" in the sense that they consider any floating point values. Tighter bounds could be obtained for smaller domains.
+ - A C++ implementation to compute the errors is provided (not fully implemented for the moment...).
+ - This implementation evaluates (will eventually evaluate) the error **symbolically**.
+ - Verification of the assertion will also be done symbolically.
+ - The next step would be to address the ``conv`` operator.
+## Actions
+### New actions
+- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator.
+### Past actions
+- [X] (2105-1, Salomé) Provide answers (OK,KO, TBdiscussed) to [Eric's comments](../documents/profile_opset/concat/reviews/eric.md) for the `concat` operator.
+- [X] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [X] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities.
+ - Will be done during next ONNX team up
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form
+
+# 2025/05/21
+## Participants
+- At least: Salomé, Sabastian, Dumitru, Sergei, Jean, Eric, Mariem, Mohammed, Nicolas, ...
+## Agenda
+- Status of actions.
+- Misc news:
+ - next workshop on formal specification + proof...
+ - discussion with WG114 and presentation to WG114
+- Presentation of the `concat` operator by Salomé
+- Brief presentation of the informal and formal specification of a `graph` by Eric
+ - Review of the `graph` informal specification by Mohammed.
+- Overview of the [Specification guidelines](../documents/profile_opset/guidelines.md)
+- Status of operator specification
+## Actions
+### New actions
+- [ ] (2105-1, Salomé) Provide answers (OK,KO, TBdiscussed) to [Eric's comments](../documents/profile_opset/concat/reviews/eric.md) for the `concat` operator.
+- [X] (2105-2, Mohammed) Upload graph review form
+### Past actions
+- [X] (2304-1, Eric+Jean) Plan presentation of SONNX to Christophe R.
+ - Done on May 13th.
+- [X] (2304-2, Mohamed) Review of the informal spec of the [graph semantics](../documents/profile_graph/graph.md). *Please place the review in the "review" directory.*
+ - Done and to be presented during the meeting.
+- [X] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md)
+ - [Review by Edoardo](../documents/profile_graph/reviews/edoardo.md)
+- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX
+- [X] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator.
+ - Work in progress. Fist review by Eric.
+ - Done, to be presented during the meeting
+- [X] (0904-4, Joao) Investigate internship to support SONNX
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+- [X] (2603-3, all) Think about our expectation concerning numerical precision (add req)
+ - See minutes of [meeting with Franck ](../meetings/numerical%20accuracy/2025-04-11_Meeting_with_Franck.md)
+- [C] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [X] Tag created.
+ - [C] Determine who can apply this tag?
+ - Cancelled.
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities.
+ - Will be done during next ONNX team up
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form
+
+# 2025/05/07
+## Participants
+TBC
+## Agenda
+- Presentation Alex: "[About quantization and ONNX in Airbus' context"](./slides/2025-05-07-Alex-DIGONNET-SONNX%20quantization%20representation%20formats.pdf)"
+- Certification referential (RMT) by Jean-Baptiste
+- Presentation of SONNX to ALTERA and ANSYS
+## Actions
+### New actions
+No new action.
+### Past actions
+- [ ] (2304-1, Eric+Jean) Plan presentation of SONNX to Christophe R.
+- [ ] (2304-2, Mohamed) Review of the informal spec of the [graph semantics](../documents/profile_graph/graph.md). *Please place the review in the "review" directory.*
+- [ ] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md)
+ - [Review by Edoardo](../documents/profile_graph/reviews/edoardo.md)
+- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX (to be done for next meeting)
+- [ ] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator.
+ - Work in progress. Fist review by Eric.
+- [X] (0904-4, Joao) Investigate internship to support SONNX
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+- [ ] (2603-3, all) Think about our expectation concerning numerical precision (add req)
+ - See minutes of meeting with Franck:
+- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [X] Tag created.
+ - [ ] Determine who can apply this tag?
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities.
+ - Will be done during next ONNX team up
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [X] (1202-3, All) Review new operators processed by Henri
+ - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator
+ - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md)
+- [C] (1501-1, Sebastian) Specify some operators...
+ - Sebastian is working on `reshape`and other ops...
+ - Cancelled
+- [C] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
+ - Cancelled
+- [C] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance")
+ - Cancelled
+- [] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form
+
+# 2025/04/23
+## Participants
+TBC
+## Agenda
+- Feedback on meeting with Franck (CEA) about error estimation.
+- ERTS 2026 paper (see [here](https://share-is.pf.irt-saintexupery.com/s/ipMLHmEZ8adgBDY), read access)
+## Minutes
+- Review of actions
+- Feedback on meeting with Franck.
+- Welcome to Christophe Ratajczak from [EM Microelectronics](https://www.emmicroelectronic.com/welcome)
+- Salomé has made some significant progress on the informal and formal specification of the [``concat``](https://onnx.ai/onnx/operators/onnx__Concat.html) operator. Work to be pushed to the repo by the end of the week. Presentation to be done in a future meeting.
+## Actions
+### New actions
+- [ ] (2304-1, Eric+Jean) Plan presentation of SONNX to Christophe R.
+- [ ] (2304-2, Mohamed) Review of the informal spec of the [graph semantics](../documents/profile_graph/graph.md). *Please place the review in the "review" directory.*
+### Past actions
+- [ ] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md)
+- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX (to be done for next meeting)
+- [ ] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator.
+- [ ] (0904-4, Joao) Investigate internship to support SONNX
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+- [X] (0904-6, Eric) Clarify the concept and req of [numerical stability](https://en.wikipedia.org/wiki/Numerical_stability) in our context.
+ > Numerical stability in numerical analysis refers to how errors are propagated by an algorithm during computation. These errors can come from several sources, like round-off errors due to finite precision (e.g., floating-point arithmetic), truncation errors from approximating infinite processes (like Taylor series), input errors (e.g., measurement uncertainty). A numerically stable algorithm is one in which small changes in input or small intermediate errors do not grow significantly and affect the final output too much. In contrast, an unstable algorithm may amplify these small errors, leading to wildly incorrect results. See [this page](./numerical%20accuracy/numerical_stability.md).\
+ **=> The algorithm given in the profile specifies the result. It does not specify how to *compute* the result. So the algorithm may be naïve and unstable. Thi sis true for the specification in $\mathbb{R}$.\
+ => What about specifications in other domains (floats,integers)?**
+- [ ] (2603-3, all) Think about our expectations concerning numerical precision (add req)
+ - See minutes of meeting with Franck:
+- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [X] Tag created.
+ - [ ] Determine who can apply this tag?
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities.
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (1202-3, All) Review new operators processed by Henri
+ - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator
+ - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md)
+- [ ] (1501-1, Sebastian) Specify some operators...
+ - Sebastian is working on `reshape`and other ops...
+- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
+- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance")
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form
+# 2025/04/09
+## Participants
+- Eric, Jean, Salomé, Sergei, Dumitru, Cong, Andreas, Sebastian, Mohammed, Jean-Baptiste, Joao, Edoardo, Henri, Jean-Loup, Alexandre
+## Agenda
+- Presentation of ONNX MLIR by Alexandre Eichenberger
+- Discussion about the "hierarchical/modular" way to specify operator, use of `onnxscript` (with Nicolas and Dumitru)
+- Status of reviews
+ - LSTM
+ - Graph
+ - Requirements
+- Feedback on discussions with WG 114 and what to do next.
+## Minutes
+- Newcomers.
+- Alexandre was not able to do the presentation today. To be (re-)rescheduled...
+- Very long discussion on the specification of operators...
+ - Our objective is (i) to keep the informal specification as simple and readable as possible and (ii) to avoid multiplying formalisms.
+ - To make a long story short, and since we have already decided to use Why3 as our formal language... we have concluded that
+ - the informal specification will continue using a simple, informal, possibly mathematical representation (see e.g., [``lstm``](../documents/profile_opset/lstm/lstm.md)),
+ - other representations may be provided as "examples"
+ - the formal specification (in Why3) must adopt some "modular" hierarchical approach where, when applicable, an operator shall be specified using some "primitive" operators (e.g., ``lstm`` is specified using ``scan``).
+ - Review of the informal [graph spec](../documents/profile_graph/graph.md): done by Jean-Loup (thanks!). To be completed to cover e.g., ``loop`` operator. To be reviewed again by Edoardo and Sebastian (action 0904-1)
+ - [Requirements](../deliverables/reqs/reqs.md): reviewed by Edorardo (thanks). Comments have been taken into account and discussed with Edoardo.
+ - Discussions about requirements about
+ - "determinism of resource usage and execution times"
+ - Remove the req about memory
+ - Concerning execution times, there is at least the ``loop`` operator that can (possibly) raise some problem: we have to ensure that the number of iterations is bounded. Dumitru will checks if there are other operators whose execution time or resource may not be bounded statically. (See action 0904-5)
+ - "traceability to training model and environment"
+ - To be removed because (i) it'll be too complicated if we were to specify the exact training environment, (ii) traceability can be ensured by conf mngt, etc. We simply have to give the capability to embed meta-data in the model (there is already a req about that).
+ - "numerical stability"
+ - We were not able to remember where this req comes from. The concept of [numerical stability](https://en.wikipedia.org/wiki/Numerical_stability) was discussed and various definitions/interpretations were given, none of them being really convincing. The main questions are: (i) what do we actually require? and (ii) why do we require it? (See action 0904-6 )
+- Feedback to WG 114
+ - The main point concerned the concepts "exact" and "approximate" replications. After many discussions, we agreed to remove these concepts from the core of the document and put it in an Annex. Those concepts where not at the "same level": "exact" replication expresses a relation between output or intermediate tensor values while "approximate" replication expresses a relation between performances. FurthermoreIn addition, the practical usage of these concepts was not that clear, especially for the "approximate replication" that basically state that the implemented model must comply with its spec...
+ - We have to complete the analysis of the ARP+concept paper in order to ensure that we are not missing reqs. See action 0904-2.
+- Shouldn't we do the same type of analysis for other domains (e.g., ISO 8800 in the automotive domain)...
+- Formal specification
+ - We shall start from [Loïc's proposal](../meetings/formal_methods/code/) in which he specifies the ``where`` operator. Salomé will do the specification work on ``concat`` (action 0904-3). This will need some additional work due to the use of lists of tensors as inputs.
+- Other contributions
+ - Joao is investigating starting an internship to support SONNX activities by the end of Q2.
+## Actions
+### New actions
+- [ ] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md)
+- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX (to be done for next meeting)
+- [ ] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator.
+- [ ] (0904-4, Joao) Investigate intership to support SONNX
+- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``.
+- [ ] (0904-6, Eric) Clarify the concept and req of [numerical stability](https://en.wikipedia.org/wiki/Numerical_stability) in our context.
+ - > > (From Wikipedia: The usual definition of numerical stability uses a more general concept, called mixed stability, which combines the forward error and the backward error. An algorithm is stable in this sense if it solves a nearby problem approximately, i.e., if there exists a Δx such that both Δx is small and f (x + Δx) − y* is small. Hence, a backward stable algorithm is always stable.
+### Past actions
+- [X] (2603-1, Eric, Nicolas,Jean-Loup) Analysis of all remarks about operator [lstm](../documents/profile_opset/lstm/lstm.md)
+- [X] (2603-2, Eric, Edoardo) Review of the [requirements](../deliverables/reqs/reqs.md) in order to produce a clean version (possibly incomplete).
+ - Done on 2025/04/08
+- [ ] (2603-3, all) Think about our expectation concerning numerical precision (add req)
+- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [X] Tag created.
+ - [ ] Determine who can apply this tag?
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [X] (1203-2, Dumitru) Propose an draft spec of LSTM where the operator would be specified using SCAN.
+ - First version presented during the 26/03 meeting.
+ - To be completed for next meeting (Dumitru and Nicolas)
+ - Discussed during the 2025/04/09 meeting.
+- [X] (1203-4, Nicolas) The relation between the directions and the dimension of the tensors shall be expressed by a constraint, not the assignment of a variable. The attributes must be presented before the description of the operator. Check that any activation function can be used for atc1 to act3. For the backward LSTM, check if the output needs to be reverted. Create a jupyter note (in collab) to illustrate the use of the operator (in the same way as for the DIV operator).
+- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities.
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [ ] (1202-3, All) Review new operators processed by Henri
+ - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator
+ - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md)
+- [ ] (1501-1, Sebastian) Specify some operators...
+ - Sebastian is working on `reshape`and other ops...
+- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
+- [X] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator)
+ - Cancelled.
+- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance")
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form
+# 2025/03/26
+## Participants
+- E. Jenn, J. Souyris, H. Belfy, M. Turki, J.L. Farges, E. Manino, D. Potop Butucaru, M. Belcaid, N. Valot et Duy Khoi Vo.
+## Agenda
+ - Operators
+ - [Current list](../documents/profile_opset/): abs, add, constant, conv, div, gemm, less, log, lstm, matmul, mul, net, pow, sigmoid, sqrt, sub, tanh, where
+ - We have to have at least 1 reference example (considered consensually "perfect").
+ - Then we have to write / review the other operators against it. Which operator?
+ - [LSTM operator](../documents/profile_opset/lstm/lstm.md). [Review by Jean-Loup](../documents/profile_opset/lstm/reviews/jean-loup.md)
+ - [Graph semantics](../documents/profile_graph/graph.md)
+ - Feedback on [2025/03/18 workshop on formal specification and verification](./formal_methods/minutes.md)
+ - Review and finalization of the [specification](../deliverables/reqs/reqs.md). Who want's to join?
+ - Status on ARP
+ - Discussion on March 14th :
+ > 1. The exact replication is the only option to preserve the properties demonstrated during the design phase. It means the “bit accurate” replication of the semantics of the designed model (expressed by the MLMD) in the target environment.
+ > 2. The approximative replication means that the properties (performance, generalization, stability and robustness) of the designed model (expressed by the MLMD) are re-assessed in the target environment and that the results are within an acceptable epsilon from the results obtained in the design environment. The epsilon should be specified by the applicant in the MLC requirements.
+ - Review of ONNX IR. Who?
+ - Work on formal spec.
+ - Traceability to certification constraints
+ - *What do we do next?*
+ - Mail to the troop? [here](../documents/Attic/call.md)
+
+## Minutes
+ - Discussion about the need to have a few set of "informal specifications" to be used as references for the writing and the review of the operators.
+ - As of today, none of the operators are completely satisfying. "conv", "where" and "lstm" are good candidate for they cover different aspects, issues. We may add "div" for it handles INF and NaNs and possible "sigmoid" since it raises problems wrt overflow / underflow (see below)
+ - Presentation of the latest version of the [LSTM operator](../documents/profile_opset/lstm/lstm.md) by Nicolas and discussion of [Jean-Loup's review](../documents/profile_opset/lstm/reviews/jean-loup.md).
+ - Several comments fro Jean-Loup were discussed. A specific meeting with Nicolas, Jean-Loup and Eric has to be organized to complete the review process.
+ - Dumitru proposes to build the informal specification of "lstm" on more primitive operators (e.g., "scan"). A first version was presented that defines "lstm" in a hierarchical way using more primitive operators. This first version will be completed by Dumitru (see action (1203-2, Dumitru)) and we will take a decision about the most appropriate way afterwards.
+ - Presentation of the [sigmoid operator](../documents/profile_opset/sigmoid/sigmoid.md) by Nicolas.
+ - For the float version of the operator, the specification proposes an "algorithm" that discriminates two cases: $X \gt 0$ and $X \leq 0$. This discrimination is aimed at providing the best output (prevent over/under flow) for the largest input domain.
+ - In some sense, it could be considered as a "guideline", a "recommendations". We need at least to give the very reason for discriminating the two cases (some hints are given in the spec).
+ - Note that this design may also be justified with respect to the symmetry of the function.
+ - Another possibility could be to define the `sigmoid` using `tanh` that is a standard IEEE 754 operator ($\sigma(x)={1+tanh(2x)\over 2}$).
+ - This discussion raises, again, our problem with the specification of numerical properties... (For the record, see [this document](../meetings/numerical%20accuracy/01_what_is_the_issue.pdf) presented in a previous meeting)
+## Actions
+### New actions
+- [ ] (2603-1, Eric, Nicolas,Jean-Loup) Analysis of all remarks about operator [lstm](../documents/profile_opset/lstm/lstm.md)
+- [ ] (2603-2, Eric, Edoardo) Review of the [requirements](../deliverables/reqs/reqs.md) in order to produce a clean version (possibly incomplete).
+- [ ] (2603-3, all) Think about our expectation concerning numerical precision (add req)
+### Past actions
+- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [X] Tag created.
+ - [ ] Determine who can apply this tag?
+- [X] (2003-2, Eric) Update SONNX landing page to point to interesting material... (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - Added a "contents" section in the [SONNX main page](../README.md). (Not yet pulled to the main branch)
+- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+- [X] (2003-4, Eric) Give an example of the two categories of restrictions (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - See proposal for the [`conv` operator](../documents/profile_opset/conv/conv.md)
+- [X] (1203-1, Eric) Propose a first specification of the graph execution semantics on the basis of Dumitru's slide and ONNX doc.
+ - See [here](../documents/profile_graph/graph.md)
+- [ ] (1203-2, Dumitru) Propose an draft spec of LSTM where the operator would be specified using SCAN.
+ - First version presented during the 26/03 meeting.
+ - To be completed for next meeting (Dumitru and Nicolas)
+- [X] (1203-3, Jean-loup) Do a review of the LSTM operator
+ - Review is [here](../documents/profile_opset/lstm/reviews/jean-loup.md)
+- [ ] (1203-4, Nicolas) The relation between the directions and the dimension of the tensors shall be expressed by a constraint, not the assignment of a variable. The attributes must be presented before the description of the operator. Check that any activation function can be used for atc1 to act3. For the backward LSTM, check if the output needs to be reverted. Create a jupyter note (in collab) to illustrate the use of the operator (in the same way as for the DIV operator).
+- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities.
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+- [X] (1202-2, Eric) Discussion to be initiated with ONNX about the integration of our work...
+ - Moved to action (2003-3)
+- [ ] (1202-3, All) Review new operators processed by Henri
+ - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator
+ - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md)
+- [X] (1202-5, All) Define appropriate rules to handle multiples types without multiplying the specifications.
+ - See example of operator [`Div`](../documents/profile_opset/div/div.md))
+- [cancelled)] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM.
+- [X] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th.
+- [X] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`.
+- [ ] (1501-1, Sebastian) Specify some operators...
+ - Sebastian is working on `reshape`and other ops...
+- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
+- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator)
+- [X] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.)
+ - Moved to the general activity on formal spec.
+- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance")
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+ - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md))
+ - [ ] Create a review form
+
+# 2025/03/12
+## Participants
+ - Nicolas, Cong, Dumitru, Anne-Sophie, Eric, Jean-Baptiste, Henri, Jean-Loup, Andreas (partially) (at least).
+ - As usual, the meeting has been recorded. See [here]
+## Agenda
+ - Problem with meeting invitations (?)
+ - Review of actions
+ - Presentation of ONNX graph semantics (Dumitru)
+ - Status on ARP
+ - First set of answers received. Will be completed by Friday 14th
+ - Review of newly described operators (see [DIV](../documents/profile_opset/div/div.md), [LSTM](../documents/profile_opset/lstm/lstm.md), other?)
+ - Subjects raised by Andreas and Sebastian and further discussed with Nicolas (see [minutes](../documents/profile_opset/lstm/reviews/meeting_mom.md)):
+ - Relations with ONNX
+ - Compliance with ONNX spec
+ - Nicolas:
+ > I propose that for each operator .md file, we specify the full compliance with ONNX spec and we add a bottom section SONNX in the operator page, where we express the restrictions like : this input XXX shall be static, the B bias shall be explicitly defined, Broadcast not supported (reshape shall be prepended when required)...
+ > This will show our specification work (math expressions, illustrations) mainstream to all the ONNX community from the ONNX documentation entry point, and also the SONNX 'value' for curious/interested people at the bottom of each operator page.
+ - Visibility
+ - Sebastian/Nicolas:
+ > For visibility and adoption, we shall avoid having a SONNX documentation entry point different from mainstream ONNX.
+ - Andreas
+ > 1. One if not the central meeting at Onnx is the SIG Operators meeting. It takes place every month.
+ > I think we should actively bring our points there to\
+ > a. Get a discussion about our thoughts, from the experts who know operators, know how the way would be to implement them.\
+ > b) We could also draw more attention to our initiative there.
+ > 2. The next Onnx Community Meetup will probably take place in June 2025. (https://github.com/onnx/steering-committee/blob/main/meeting-notes/2025/20250305.md)\
+ There we will have the opportunity to present what we are doing and what we have achieved.
+ > 3. I think we need to create a lot more issues at https://github.com/onnx/onnx in order to work out the concerns more clearly,or to be able to discuss them even more with the community there, or to be able to refer to them in the Operators Meeting.
+ > 4) As there are certainly already issues concerning SONNX at the moment, I could well imagine a new label “sonnx” for this, so that we can filter even better according to the topics. => you could ping me "andife" directly at the github issue and I add that specific label
+ - Tooling
+ - ONNX to SONNX converter
+ - Model checker
+ - *How to fund those developments?*
+ - Other
+ - Graph semantics... Who?
+## Minutes
+- [Presentation of the ONNX graph semantics by Dumitru](./slides/2025-03-12-Dumitru%20on%20ONNX-graph-semantics.pdf)
+ - The semantics is pretty simple (as far as I [eric] understand
+ - considering a Directed Graph composed of nodes and edges, where
+ - a node is an operator
+ - a edge connects a node output to a node input
+ - the graph is acyclic
+ - considering that an edge carries either no value of a value(ii) the graph contains no cycle,
+ - considering that all edges carry no value initially
+ - considering that an edge gets a value when the node whose output is associated with is executed
+ - considering that a node can ony be executed when all edges associated with its inputs have a value
+ - executing a graph means executing every node until all nodes have been executed.
+ - The graph is stateless, so if the computation requires a state, this state shall be managed outside of the graph. Such question are (very) relevant when dealing with reactive systems, but they do not concern the SONNX spec.
+ - Pipelining or other implementation concerns (e.g., scheduling of nodes execution) are not relevant to the specification
+ - Questions were raised about
+ - operators implementing a random behaviour
+ - a deterministic behavior can be achieved by providing seeds to each and every random op.
+ - batch norm
+ - batch norm is replaced by a constant at inference time
+ - Brief presentation of the DIV operator that shows of implementation of the same operator for Real, Float and Int number can be specified.
+ - Presentation of the LSTM operator
+ - A few verifications and corrections needs to be done, see action [1203-4].
+ - Discussion about the presentation of our work to ONNX
+ - Eric: I would prefer to present our results once we are completely happy with a first subset of operators. This will hopefully be the case in for the Meetup in June. In the meantime, we have to figure out with ONNX people who our work needs to be "integrated" with theirs. See action [1203-5]
+ - Discussion about the tooling
+ - We have identified two tools: one to check a model with respect to SONNX, and one to convert a model from standard ONNX to SONNX.
+ - With respect to certification, the second one may not be a "good idea" . see acton [1205-6].
+ - The last point about "issues" has not been discussed.
+## New actions
+- [ ] (1203-1, Eric) Propose a first specification of the graph execution semantics on the basis of Dumitru's slied and ONNX doc.
+- [ ] (1203-2, Dumitru) Propose an draft spec of LSTM where the operator would be specified using SCAN.
+- [ ] (1203-3, Jean-loup) Do a review of the LSTM operator
+- [ ] (1203-4, Nicolas) The relation between the directions and the dimension of the tensors shall be expressed by a constraint, not the assignment of a variable. The attributes must be presented before the description of the operator. Check that any activation function can be used for atc1 to act3. For the backward LSTM, check if the output needs to be reverted. Create a jupyter note (in collab) to illustrate the use of the operator (in the same way as for the DIV operator).
+- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities.
+- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation
+## Past actions
+- [X] (1202-1, Eric) Reschedule Alexandre presentation
+ - Presentation planned on April 9th.
+- [ ] (1202-2, Eric) Discussion to be initiated with ONNX about the integration of our work...
+- [ ] (1202-3, All) Review new operators processed by Henri
+ - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator
+ - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md)
+- [ ] (1202-4, All) Define the appropriate way to specify the behaviour of operators for value out of range. Apply the approach on the `div` operator, for parameters in $\mathcal R$ and, `double`and `int`.
+ - [X] Eric: See operator [`Div`](../documents/profile_opset/div/div.md)
+- [ ] (1202-5, All) Define appropriate rules to handle multiples types without multiplying the specifications.
+ - See example of operator [`Div`](../documents/profile_opset/div/div.md))
+- [X] (2901-1, Eric) Check how to express constraints about SparseTensor at operator level.
+ - Only tensors of class "Tensors" are supported (SparseTensor are not supported). Such restriction applies to all operators. They are placed in document [General restrictions](../documents/profile_opset/general_restrictions.md)
+- [X] (2901-2, Anne-Sophie) Put back the issues (in the appropriate section) and add the answers given by Sebastian.
+- [X] (2901-3, Eric) Provide a Jupyter notebook for the `conv` operator (see [here](../documents/profile_opset/conv/tests/conv_onnx.ipynb)).
+ - Done for operator `Div`.
+- [ ] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM.
+- [ ] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th.
+- [X] (2901-6, Edoardo) Check how to involve students in the specification work.
+ - On 12/02 : Edoardo has done some internal advertisement... waiting...
+- [X] (2901-7, Jean-Baptiste) Analysis of EASA's Concept Paper.
+- [ ] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`.
+- [ ] (1501-1, Sebastian) Specify some operators...
+ - Sebastian is working on `reshape`and other ops.
+- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
+ - *Thinking...*
+- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator)
+- [X] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me.
+- [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.)
+ - Discussion on-going with Loïc on the formal specification strategy...
+ - Meeting planned to reach a final consensus...
+ - Meeting done. See [minutes](../meetings/formal_methods/minutes.md).
+- [ ] (1812-5, All) Indicate on which operator one can contribute (writer/reviewer). Put your id in this [table](./operator_spec_sub_wg/worksharing.md) The list of operators with their "complexity" and links to the ONNX doc are in this [Excel sheet](./operator_spec_sub_wg/SONNX_Operator_List.xlsx)
+- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance")
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+
+# 2025/02/26
+*Canceled.*
+
+# 2025/02/12
+## Agenda
+- Presentation of ONNX MLIR (Alexandre Eichenberger) [postponed]
+- Review of actions
+- Some questions from Sebastian
+ > I'm still not 100% clear, what we want to achieve with our operator specifications.
+
+ > **Is it intended that they appear in the official ONNX documentation and then the restrictions for SONNX may be below in the same text?**
+ > Otherwise, they will probably not be very visible to the public... See action (1202-0)
+
+ > And is it clear that the norms you have to fulfill will require these formal specifications?
+
+ > We work in very safety-critical projects and so far, it seems that a formal verification of numerical correctness compared to the original trained model is enough for most customers on our side (...]
+
+ > For me the most critical operators are those that can create overflow or division by zero and there are a lot of them: Div, Exp, Log, Pow, Softmax, SoftPlus, Sqrt See action (1202-3)
+- "Review" of Henri's work
+- Review of Jean-Baptiste work ( See action (1202-2) )
+- Reply from the WG114 on our questions
+
+## Attendees
+(???)
+
+## Minutes
+- Presentation of ONNX MLIR (Alexandre Eichenberger) to be rescheduled. ( See action (1202-1) )
+- "Review" of Henri's work
+ - See action (1202-3)
+ - See new operators [here](../documents/profile_opset/)
+ - Question about error conditions: *How do we specify as the expected behaviour of an operator when a parameter is out of range (e.g., denominator of `div` is 0?)*
+ - In the `div`spec, for instance, Henri has proposed to return `inf` (when the numerator is different from zero, otherwise a NaN shall be returned).
+ - We have proposed a [first set of rules](./errors/error_specification.md). Basically, the behaviour of the operator is not defined when we cannot guarantee that its parameter are in range. However:
+ - This raises a problem because more often than not, we cannot guarantee that the error condition (e.g., $x<=0$ for operator `log`) will not occur.
+ - This would mean that any output value would be suspicious while we could actually be able to handle appropriately some singular values (such as /0)...
+ - *This is clearly not satisfying.* We have to propose a better way to handle these cases. We may get some inspiration from the C standard: our interpretation would be determined according to the semantics of ISO C.
+ - Note that we have to discriminate cases where the operation is mathematically undefined (e.g. /0) from cases where the implementation of the operator may go wrong (e.g., overflow). Considering the `div` operator, if we are in $\mathcal R$ >, there is no `inf` value to be returned: the operation is simply undefined.
+ - See action (1202-4)
+ - Question about types
+ - The first batch of operators are specified in $\mathcal R$, so there should be no reference to types. In a second phase, we will have to specify the operators with actual types. When an operator has multiple parameters, we should either require all parameters to have a specific type, or the same types, etc. This may lead to many specifications...
+ - See action (1202-5)
+- Discussion about the integration of our work in ONNX (see Sebastian's question above).
+ - Our work must be part of the official ONNX documentation
+ - Our spec could be added to the existing doc as a link to "SONNX".
+ - A process shall be defined in order to ensure the consistent evolution of ONNX and SONNX.
+ - We propose to wait until we have fully covered a few operators (incl. with their implementation types)
+ - See action (1202-6)
+- Presentation of Jean-Baptiste's work
+ - See action (1202-2)
+ - This work makes a mapping between the EASA's concept paper and the ARP. it also identifies the EASA's objectives that are relevant to SONNX. Note that Mohamed and Jean-oup have done a similar work (yet more focused) in the context of the DeepGreen project.
+ - First draft to be validated at Airbus and with people from WG114.
+- About the management of operators and reviews:
+ - /!\ Don't forget to indicate on which operator you are working (in this [table](./operator_spec_sub_wg/worksharing.md)) in order to prevent overlaps... /!\
+ - Please use the gconf to do your review. And when taking account of reviews, authors shall indicate in the review form what has been taken into account (KO/OK/TBD).
+
+## New actions
+- [X] (1202-1, Eric) Reschedule Alexandre presentation
+- [ ] (1202-2, Eric) Discussion to be initiated with ONNX about the integration of our work...
+- [ ] (1202-3, All) Review new operators processed by Henri
+ - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator
+- [ ] (1202-4, All) Define the appropriate way to specify the behaviour of operators for value out of range. Apply the approach on the `div` operator, for parameters in $\mathcal R$ and, `double`and `int`.
+- [ ] (1202-5, All) Define appropriate rule to handle multiples types without multiplying the specifications.
+- [ ] (1202-6, Eric) Check with ONNX how to integrate our work.
+## Past actions
+
+- [ ] (2901-1, Eric) Check how to express constraints about SparseTensor at operator level.
+- [ ] (2901-2, Anne-Sophie) Put back the issues (in the appropriate section) and add the answers given by Seb.
+- [ ] (2901-3, Eric) Provide a Jupyter notebook for the `conv` operator (see [here](../documents/profile_opset/conv/tests/conv_onnx.ipynb)).
+- [ ] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM.
+- [ ] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th.
+- [ ] (2901-6, Edoardo) Check how to involve students in the specification work.
+- [ ] (2901-7, Jean-Baptiste) Analysis of EASA's Concept Paper.
+- [ ] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`.
+- [ ] (1501-1, Sebastian) Specify some operators...
+ - Sebastian is working on `reshape`and other ops.
+- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
+ - *Thinking...*
+- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator)
+- [ ] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me.
+- [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.)
+ - Discussion on-going with Loïc on the formal specification strategy...
+ - Meeting planned to reach a final consensus...
+ - Meeting done. See [minutes](../meetings/formal_methods/minutes.md).
+- [ ] (1812-5, All) Indicate on which operator one can contribute (writer/reviewer). Put your id in this [table](./operator_spec_sub_wg/worksharing.md) The list of operators with their "complexity" and links to the ONNX doc are in this [Excel sheet](./operator_spec_sub_wg/SONNX_Operator_List.xlsx)
+- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance")
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+
+
+# 2025/01/29
+## Agenda
+- Review of actions
+- Other news
+ - Contact with Altera.
+- Operator specifications
+ - Overview of the [`matmul`](../documents/profile_opset/matmul/matmul.md) and [`where`](../documents/profile_opset/matmul/where.md) operators
+ - Current status of the [operators](./operator_spec_sub_wg/SONNX_Operator_List.xlsx) and [contributors](./operator_spec_sub_wg/worksharing.md) list.
+- Formal specification
+ - Feedback on [last meeting](./formal_methods/minutes.md).
+- Other topics:
+ - Verification tool
+ - talks in 2025
+## Attendees
+Jean, Julien, Eric, Mariem, Dumitru, Jean-Loup, Andreas, Sebastian, Jean-Baptiste, Tomé, João, Augustin, Anne-Sophie
+
(Zoom does not give me a list of participants and I have not noted who was there...so the list if incomplete, sorry about that. Please add your name...
+
+## Minutes
+- ONNX model verification tool
+ - This tool will implement the constraints and restrictions identified at operator and graph level
+ - The tool shall be part of the ONNX distribution
+ - Using (e.g.) DeepGreen's ONNX parser could make sense but (i) we have to be sure that we can distribute it as part of ONNX, (ii) it will bring with it part of the AIDGE framework (iii) it may be overkill with respect to the level of parsing that we actually need.
+ - Developing our own parser (possibly from something already available...) could also be a way to specify formally the file format (i.e., a correct ONNX file is a file that can be parsed successfully by our parser).
+- Review of the comments on the [`where`](../documents/profile_opset/matmul/where.md) operator
+ - Sparse tensors have to be forbidden. This can be checked at the file level by looking for the usage of the SparseTensorProto class.
+ - In the specification of operators, we should state that such tensors cannot be used. This should be part of the operators' signatures. To be elaborated, see action [2901-1]
+ - Henri has given an example in Python. Should we generalize this?
+ - We propose to add a few example in a Jupyter Notebook for each operator.
+ - The examples must use ONNX.
+ - Those examples are not a test suite. They allow the user to get acquainted / play with of the operator.
+ - The behaviour of the operator in the Jupytrer notebook may be different from the one of reference implementation.
+ - In the Jupyter notebook, we have to be clear that the example is given for documentation only.
+ - See action [2901-3].
+- Specification work
+ - Nicolas has added LSTM to the operator's list. Dumitru proposes to help. See action [2901-4].
+ - It could be useful to start thinking about the graph semantics... See action [2901-5].
+ - The work moves slowly...
+ - We lack volunteers to do the hard work...
+ - We may become more visible and reach out a larger community (and hopefully, have more volunteers) once we have a first set of specification, completed documents.
+ - Edoardo proposes to use students to help us on the specification activity. (Jean will have one student to work on this topic) See action [2901-6].
+
Note that the initial list of interested people was pretty large... we may ask if some of them are still interested to contribute...
+- List of issues
+ - Anne-Sophie has removed some entries from [issues.md](../deliverables/issues/issues.md) since some of the ambiguities have been solved thanks to Sebastian.
+ - However, it make sense to keep them in the list since part of our job is to prevent such ambiguities... See action [2901-2].
+- On the specification of behavior in case of errors
+ - At operator level
+ - Whenever a value is out of the operator's domain, its behavior is "undefined" (e.g. xi<=0 in x for operator log(x)). In that case, the specification shall (i) indicate the domain constraint and (ii) state that the behaviour of the operator is undefined should the constraint be violated.
+ - For some operators, some inputs will generally be static, even though the ONNX does not enforce this. In that case, we may add a restriction to ensure that the tensor's value will be known before runtime, and add a constraints on its value to prevent runtime error.
+ - At graph level
+ - In some cases, the structure of the graph can ensure that the inputs values of an operator will always be in domain even though, they may be not in the general case.
+ - We may also propagate the domain constraints backward up to the inputs, and provide the user with these constraint (which would restrict the graph input domain). This could be interesting, but goes further than a verification.
+ - Whenever we cannot guarantee the absence of runtime error, the model wil be considered "unsafe".
+- Compliance with regulatory docs.
+ - It would be useful to identify the constraints/reqs in the EASA's concept paper that are relevant to our work. See action [2901-7]
+- Formal methods.
+ - See [minutes](../meetings/formal_methods/minutes.md) of FM sub-group on Feb. 28th.
+ - Main conclusion: We will be using Why3 as the specification language. Work will start with a training session (being planned mid-march) done by Loïc.
+## New actions
+- [ ] (2901-1, Eric) Check how to express constraints about SparseTensor at operator level.
+- [ ] (2901-2, Anne-Sophie) Put back the issues (in the appropriate section) and add the answer given by Seb.
+- [ ] (2901-3, Eric) Provide a Jupyter notebook for the `conv` operator (see [here](../documents/profile_opset/conv/tests/conv_onnx.ipynb)).
+- [ ] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM.
+- [ ] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th.
+- [ ] (2901-6, Edoardo) Check how to involve students in the specification work.
+- [ ] (2901-7, Jean-Baptiste) Analysis of EASA's Concept Paper.
+- [ ] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`.
+## Past actions
+- [ ] (1501-1, Sebastian) Specify some operators...
+ - Sebastian is working on `reshape`and other ops.
+- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
+ - *Thinking...*
+- [X] (1501-4, All) Review the specification of the [`where` operator](../documents/profile_opset/where/where.md). Put your remarks in the [reviews](../documents/profile_opset/where/reviews/) directory (in file `.md`) or send them to me (eric).
+ - See Eric's [remarks](../documents/profile_opset/where/reviews/eric.md).
+- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator)
+- [ ] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me.
+- [X] (1501-7, Eric) Check how to communicate with ONNX to sort out ambiguities...
+ - The best solution is probably to use the [ONNX] test suite, which covers operators and graph. "Just in case", I have also contacted RAM at ONNX.
+ - After a discussion with Ram : We can use the LFx slack channel (Operator SIG). Two contacts : G. Ramalingam and Justin Chu
+- [X] (1508-1, Eric) Send the list of questions to the WG 114 leader.
+ - List of questions sent to the WG114 chairwoman on Jan. 16th.
+ - They'll analyze them and come back to us.
+- [X] (1812-2, Eric) Complete the discussion about numerical accuracy and error management.
+ - See mail dated 19/12.
+ - See [new version of the document](./errors/error_specification.md)
+- [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.)
+ - Discussion on-going with Loïc on the formal specification strategy...
+ - Meeting planned to reach a final consensus...
+ - Meeting done. See [minutes](../meetings/formal_methods/minutes.md).
+- [ ] (1812-5, All) Indicate on which operator one can contribute (writer/reviewer). Put your id in this [table](./operator_spec_sub_wg/worksharing.md) The list of operators with their "complexity" and links to the ONNX doc are in this [Excel sheet](./operator_spec_sub_wg/SONNX_Operator_List.xlsx)
+- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance")
+- [X] (0412-4, Thiziri, Nicolas, Jean, Sebastian, Jean-Loup) Review of the [updated version of CONV2D](../documents/conv_specification_example/README.md)
+ - Review from Thiziri to be received on 2024/12/20.
+ - *No answer => Closed*
+- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way...
+ - Contribution of Anne-Sophie. But WG to be set.
+
+
# 2025/01/15
## Agenda
- Edoardo's presentation about the "Evaluation and improvement of SW verifiers on FP Neural Networks"
@@ -10,10 +1782,10 @@
- Discussion on the behavior in the presence of errors (see action 1812-2)
## Attendees
-Eduardo, Eric, Jean, Mariem, Sebastian, Augustin, Dumitru, Henri, Nicolas, Cong, Jean-Loup [sorry, some names are probably missing ; I don't know how to obtain the list of attendees using LFAI...]
+Edoardo, Eric, Jean, Mariem, Sebastian, Augustin, Dumitru, Henri, Nicolas, Cong, Jean-Loup [sorry, some names are probably missing ; I don't know how to obtain the list of attendees using LFAI...]
## Minutes
-- Edorado's slides can be found [here](./slides/2025-01-15-Edoardo_Manino_SONNX_slides.pdf).
+- Edoardo's slides can be found [here](./slides/2025-01-15-Edoardo_Manino_SONNX_slides.pdf).
- Status of the current outoputs of our WG. We are a bit late with respect to the initial plan...
- Concerning the specification work, we can take `conv` as an example, even though it may be later modified thanks to the lessons learnt on other operators.
- We have to find people to work on the specification. See action [1501-1] and [1501-2].
@@ -34,18 +1806,18 @@ Eduardo, Eric, Jean, Mariem, Sebastian, Augustin, Dumitru, Henri, Nicolas, Cong,
## New actions
- [ ] (1501-1, Sebastian) Specify some operators...
- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work...
-- [ ] (1501-3, all) Drop an e-mail to Mariem should you be interested in the work on formal methods
+- [X] (1501-3, all) Drop an e-mail to Mariem should you be interested in the work on formal methods
- [ ] (1501-4, All) Review the specification of the [`where` operator](../documents/profile_opset/where/where.md). Put your remarks in the [reviews](../documents/profile_opset/where/reviews/) directory (in file `.md`) or send them to me.
- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator)
- [ ] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me.
- [ ] (1501-7, Eric) Check how to communicate with ONNX to sort out ambiguities...
-- [X] (1508-1, Eric) Send the list of questions to the WG 114 leader.
+- [ ] (1508-1, Eric) Send the list of questions to the WG 114 leader.
- List of questions sent to the WG114 chairwoman on Jan. 16th.
## Past actions
- [X] (1812-1, Mariem et Eric) Process reviews of `conv`.
- Done. Spec moved [here](../documents/profile_opset/conv/)
-- [X] (1812-2, Eric) Complete the discussion about numerical accuracy and error management.
+- [ ] (1812-2, Eric) Complete the discussion about numerical accuracy and error management.
- See mail dated 19/12.
- [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.)
- Discussion on-going with Loïc on the formal specification strategy...
@@ -636,3 +2408,11 @@ Other (off-meeting)
- [ ] <10/07>(eric+jean) Organize some meetings as “advisory boards”
- [x] <21/08> (Eric) Convert the SOW to markdown
+
+
diff --git a/safety-related-profile/meetings/errors/01_what_is_the_issue.md b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.md
similarity index 92%
rename from safety-related-profile/meetings/errors/01_what_is_the_issue.md
rename to safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.md
index 2ee0a692..d5e61701 100644
--- a/safety-related-profile/meetings/errors/01_what_is_the_issue.md
+++ b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.md
@@ -9,7 +9,7 @@ This document contains various elements concerning computation errors and their
- *Floating-point* numbers are encoded on a finite number of bits. Therefore, all real numbers can't be represented exactly. Non-representable numbers must "mapped" (rounded) to representable numbers according to a rounding strategy.
- Concerning operations, the principle (IEEE 754) is that the result of an operation on rounded values shall be the same as the result that would be obtained by rounding the result computed using exact values. The exact statement, from [IEEE754] is the following:
> "[...] each of the computational operations specified by this standard that returns a numeric result shall be performed as if it first produced an intermediate result correct to infinite precision and with unbounded range, and then rounded that intermediate result, if necessary, to fit in the destination’s format"
-- In addition, due to the finitness of the nuber of bits, overflow and underflow conditions may occur. Conditions may apply on input values to prevent runtime errors. At least, runtime errors shall be signaled. In principle, these preconditions should be be part of the specification of the network (otherwise, there may be conditions in which the model doesn't produce any sensible result).
+- In addition, due to the finiteness of the number of bits, overflow and underflow conditions may occur. Conditions may apply on input values to prevent runtime errors. At least, runtime errors shall be signaled. In principle, these preconditions should be be part of the specification of the network (otherwise, there may be conditions in which the model doesn't produce any sensible result).
## Non-reproductibility
- Depending on some internal conditions, the same piece of code given the same inputs may lead to different sequences of operations, then different numerical results due to the non associativity of floating point operations. Those internal conditions may be difficult to determine in the absence of a full description of the execution platform. See for instance [Dem-15, Col-15]. In [Dem-15], this effect is studied for a simple parallel summation.
@@ -18,10 +18,10 @@ This document contains various elements concerning computation errors and their
## IEEE-754 and non IEEE-754 data types
-IEEE 754 provides a set of guarantees about floating point computations [IEEE-754], howwever :
+IEEE 754 provides a set of guarantees about floating point computations [IEEE-754], however :
- Are all hardware (GPUs, accelerators) implementing the IEEE-754 standard?
- - No: NVIDUA uses TF32.
+ - No: NVIDIA uses TF32.
- Are all hardware devices IEEE-754 compliant (GPUs, FPGA and ASIC accelerators)?
- Some of the data types used in machine learning to not belong to the IEEE 754 standards. For example: [BF16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format) (bfloat16), [TF32](https://en.wikipedia.org/wiki/TensorFloat-32) (TensorFlow 32 bits)
- What are the properties of those representations (do they respect the same principle as IEEE 754 numbers?)
@@ -44,7 +44,7 @@ IEEE 754 provides a set of guarantees about floating point computations [IEEE-75
# Relation between accuracy and ML properties...
-This section deals with the relation between computaton errors due to (e.g.,) rounding, and the actual expected properties of the ML model. The rationale is that it *may be the case* that bitwise reproducibility is "overkill" with respect to the actual performance of the ML model.
+This section deals with the relation between computation errors due to (e.g.,) rounding, and the actual expected properties of the ML model. The rationale is that it *may be the case* that bitwise reproducibility is "overkill" with respect to the actual performance of the ML model.
## About the effects of floating point errors on ML model performance...
- To what extent is the question of computation errors pertinent with respect to the other sources of errors in Machine Learning algorithms (or "how do computation errors compare to other sources of errors")?
@@ -70,25 +70,25 @@ Complex operations are described using a small corpus of basic operators and fun
## Standards
-All programming languages come with a set of basic mathematical operators, either directly implemented by the hardware or implemented in a mathematical library (e.g., ``libm``). Libraries also exist for high-level function such as Basic Linear Agebra (e.g., [BLAS](https://www.netlib.org/blas/), [LINPACK](https://www.netlib.org/linpack/),etc.). Some libraries are provided by chip manufacturers in order to take the most out of their hardware ([cuBLAS](https://docs.nvidia.com/cuda/cublas/index.html), Intel math lib)
+All programming languages come with a set of basic mathematical operators, either directly implemented by the hardware or implemented in a mathematical library (e.g., ``libm``). Libraries also exist for high-level function such as Basic Linear Algebra (e.g., [BLAS](https://www.netlib.org/blas/), [LINPACK](https://www.netlib.org/linpack/),etc.). Some libraries are provided by chip manufacturers in order to take the most out of their hardware ([cuBLAS](https://docs.nvidia.com/cuda/cublas/index.html), Intel math lib)
### Mathematical libraries
- ``libm``
- The newlib mathematical library does not give the accuracy of operations. (Note that the way mathematical operations are described could be inspiring) In addition, as stated in [Dar-06] "current [as of 2006] libm implementation do not always return the floating point number that is closest to the exact mathematical result. As a consequence, different libm implementation will return different results for the same input, which prevents fill portability for floating-point applications". The same claim can be read in [Gla-24]:
- > The IEEE 754 standard, even in its latest 2019 revision [17], does not require correctly rounded mathematical functions, it only recommends them. In turn, current athematical libraries do not provide correct rounding, which is the best possible result. Thus, users might get different results with different libraries, or different versions of the same library. This can have dramatic consequences: for example missed collisions in the Large Hadron Collider [5] or reproducibility issues in neuroimaging [13].
- - BLAS and other linear algrebra libraries
+ > The IEEE 754 standard, even in its latest 2019 revision [17], does not require correctly rounded mathematical functions, it only recommends them. In turn, current mathematical libraries do not provide correct rounding, which is the best possible result. Thus, users might get different results with different libraries, or different versions of the same library. This can have dramatic consequences: for example missed collisions in the Large Hadron Collider [5] or reproducibility issues in neuroimaging [13].
+ - BLAS and other linear algebra libraries
- BLAS is not accurately rounded. For an accurately rounded BLAS, see e.g., [Cho-16].
- cuBLAS
- cuBLAS is designed so that "all [...]] API routines from a given toolkit version, generate the same bit-wise results at every run when executed on GPUs with the same architecture and the same number of SMs. However, bit-wise reproducibility is not guaranteed across toolkit versions because the implementation might differ due to some implementation changes." ([cuBLAS documentation](https://docs.nvidia.com/cuda/cublas/index.html#using-the-cublas-api), section 2.1.4).
- The precision of operators is not specified. See e.g, ``gemm`` in section [2.7.1 ](https://docs.nvidia.com/cuda/cublas/index.html#cublas-t-gemm).
- [Intel MKL BLAS ](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/overview.html)
- - See e.g.., the [``gemm``](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/cblas-gemm-001.html) function: no inidcations whatsoever about accuracy.
+ - See e.g.., the [``gemm``](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/cblas-gemm-001.html) function: no indications whatsoever about accuracy.
## Programming languages
How are floating point operations specified in programming languages?
Here are a few examples:
- C
- - In the C99 standard [C99], nothing is said about the accuraty of operator. For instance, for the sin operator, the specification is the following:
+ - In the C99 standard [C99], nothing is said about the accuracy of operator. For instance, for the sin operator, the specification is the following:
```
Description
The sin functions compute the sine of x (measured in radians).
@@ -104,7 +104,7 @@ Note 1: the result of a computation also depends on the compiler, on the hardwa
Note 2: an interesting quotation from [Mul-10]:
> As pointed out by David Goldberg [...] ) all these uncertainties make it impossible, in many cases, to figure out the exact semantics of a floating-point C program just by reading its code.
-Several examples of strange behaviours are given in the book. One example considers the case where a symmetric function (e.g., a function $f(x,y)$ computing the distance between $x$ and $y$) may be compiled in an asymetrical way using, e.g., ``fma`` in such a way that $f(x,y) \neq f(y,x)$, potentially breaking algorithms relying on the symmetry of the fonction (such as sorting algorithms).
+Several examples of strange behaviours are given in the book. One example considers the case where a symmetric function (e.g., a function $f(x,y)$ computing the distance between $x$ and $y$) may be compiled in an asymmetrical way using, e.g., ``fma`` in such a way that $f(x,y) \neq f(y,x)$, potentially breaking algorithms relying on the symmetry of the function (such as sorting algorithms).
### Industrial standards
@@ -131,7 +131,7 @@ What do the industrial standards say about computation errors (in space, aeronau
#### Aeronautical standards
- The DO-178C [DO-178C] doesn't say much besides that the source code must be correct with respect to floating point arithmetic (§6.3.4.f) (the term "floating point" appears only once).
-- (May refer to the MOPS, but it is unlikely that we will find anything concerning the allocateion of errors to computations.)
+- (May refer to the MOPS, but it is unlikely that we will find anything concerning the allocation of errors to computations.)
#### Automotive standards
- The ISO 26262-6 [ISO26262] ("Product development at the software level") doesn't say much about floating point computations.
@@ -141,7 +141,7 @@ What are our needs concerning computation accuracy, and **for what purpose**?
## Industrial needs and requirement on SONNX
- [**Needs**] The executions of a SONNX model shall be reproducible to support debugging activities.
- - Reproducibility means that given the same inputs, all computations shall give the same ouputs.\
+ - Reproducibility means that given the same inputs, all computations shall give the same outputs.\
The level of reproducibility be defined by (i) a bound on the errors on the outputs, (ii) the supported variability on the target SW or HW.\
For instance, one may require that "the model shall give bitwise identical results when executed on the same HW architecture" or "the model shall give results $\pm \epsilon$ when executed on the same HW architecture", etc.\
Note that this requirement is only about reproducibility, i.e., the results may be perfectly reproducible but completely wrong with respect to the model "semantics".
@@ -154,7 +154,7 @@ In particular, this means that the (meta-)model (i.e., the SONNX standard) must
For instance, using floating point can lead to unsound verification results in the sense that a model that is robust in $\mathbb{R}$ (i.e, verified formally to be robust considering values in R) may not be robust when implemented using finite precision numbers [???]. In this example, the MLMD would have to be implemented in R (if it were possible) for the robustness property to be preserved.
*Note (a): "Reproduction" is different from "replication" because the former concerns the relation between different executions of the same model implementation, whereas the latter concerns the relation between the model and its implementation(s).*\
-*Note (b): Instead of specifiying requirements about accuracy and precision, couldn't we just specify implementation requirements, i.e, the way computations must be done.*
+*Note (b): Instead of specifying requirements about accuracy and precision, couldn't we just specify implementation requirements, i.e, the way computations must be done.*
## Certification "needs"
@@ -228,7 +228,7 @@ From the previous needs, what are the requirements applicable to SONNX? The foll
# Means of analysis techniques and tools
- What are the technical means available to estimate the impact of errors on results? (e.g., fluctuat, CADNA...)
- - See [Beu-24, Ch. 4] who proposes forward / backward error estiation for neural networks, taking into account nn linear activation functions.
+ - See [Beu-24, Ch. 4] who proposes forward / backward error estimation for neural networks, taking into account nn linear activation functions.
- Do we need the intervention of some FP experts? Who?
diff --git a/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.pdf b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.pdf
new file mode 100644
index 00000000..62c708c6
Binary files /dev/null and b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.pdf differ
diff --git a/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.png b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.png
new file mode 100644
index 00000000..f9f02074
Binary files /dev/null and b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.png differ
diff --git a/safety-related-profile/meetings/numerical accuracy/2025-04-11_Meeting_with_Franck.md b/safety-related-profile/meetings/numerical accuracy/2025-04-11_Meeting_with_Franck.md
new file mode 100644
index 00000000..6091609f
--- /dev/null
+++ b/safety-related-profile/meetings/numerical accuracy/2025-04-11_Meeting_with_Franck.md
@@ -0,0 +1,76 @@
+### **Participants**
+- **Franck VEDRINE** (CEA, developer of the Fluctuat tool)
+- **Mariem TURKI** (IRT St-Ex)
+- **Jean SOUYRIS** (Airbus)
+- **Eric JENN** (IRT St-Ex)
+
+### **Subject:**
+- Introduction of the SONNX working group to Franck
+- Open discussion and potential directions
+
+### **Discussion Summary**
+
+- **Initial Focus:**
+ - We will first concentrate on networks using:
+ - **Integer data (quantized networks)**, e.g., int8.
+ - **Floating-point data** (16, 32, and 64-bit), provided they comply with the IEEE standard.
+
+- **Analogy with SCADE Operators:**
+ - There are parallels between our work on graph operators and SCADE operators. The requirements are quite similar, but differences lie in:
+ - The sheer volume of operations (much higher in neural networks).
+ - The challenge of assessing the impact of computational errors on function performance (e.g., how does an error in an operator affect the final decision?).
+ - In any case, it seems difficult to specify error objectives (relative or absolute)* at the operator level.
+ - Even in more conventional cases, such requirements are rarely defined *a priori*, except in specific scenarios.
+
+- **Current Approach:**
+ - More "pragmatic"—determining the maximum error that can be guaranteed with a given effort (*best effort*).
+ - This error becomes the *de facto* specification, as it can be accounted for at the system level (e.g., for filtering functions).
+
+- **Error Evaluation Considerations:**
+ - Must account for:
+ - **Method-related errors**
+ - **Implementation-related errors**
+ - Currently, SONNX does **not** specify the method or implementation, so this approach is not directly applicable at this level.
+
+- **Reference Implementation Level:**
+ - An **error estimate** can be provided for the reference implementation.
+ - **Caution:**
+ - This error might only be achievable in a simple implementation and degrade in more complex, optimized versions.
+ - This could lead to specifying unrealistic error bounds.
+ - **Proposal:** Provide estimates for multiple implementations:
+ - A simple one (our "reference implementation" via Why3), easier to analyze but potentially less efficient.
+ - A more complex, optimized one.
+ - **Key Points:**
+ - In critical domains, implementations should remain relatively simple.
+ - Otherwise, applicants are free to develop their own implementation and apply the recommended method.
+
+- **Error Evaluation Categories:**
+ To handle a wide range of cases (including complex ones without formal estimates), evaluations could be categorized as:
+ - **Bronze:** Incomplete error assessment.
+ - **Silver:** Abstract interpretation (Fluctuat).
+ - **Gold:** Axiomatic proof.
+
+- **Alternative Approach:**
+ - We could also simply describe the error estimation method.
+ - However, whenever possible, we will aim to provide tools for estimating the error, potentially as:
+ - An **analytical expression**.
+ - A **calculation program** (if the error depends on input tensor characteristics, e.g., dimensions and size).
+
+- **Input Domain Constraints:**
+ - In some use cases, input parameter ranges may be bounded and known (e.g., values in [-1, 1]).
+ - Such constraints could improve error estimation accuracy.
+ - Further study is needed to determine when these apply.
+
+- **Benefits of Error Estimation:**
+ - Facilitates model debugging by identifying error sources.
+ - Enables post-mortem analysis/diagnostics in case of failure.
+ - Provides insights into the origin of output errors.
+
+- **Next Steps with Franck:**
+ - Franck expressed interest in the topic and agreed to join the working group.
+ - **Actions:**
+ - [X] Send meeting invitations.
+ - [X] Share document links.
+ - [X] Grant Git access.
+
+
diff --git a/safety-related-profile/meetings/numerical accuracy/2026-02-17 - DeepGreen - Accuracy.md b/safety-related-profile/meetings/numerical accuracy/2026-02-17 - DeepGreen - Accuracy.md
new file mode 100644
index 00000000..634988f6
--- /dev/null
+++ b/safety-related-profile/meetings/numerical accuracy/2026-02-17 - DeepGreen - Accuracy.md
@@ -0,0 +1,80 @@
+Date :
+
+---
+
+## Participants
+
+* Yves THUILLIER (CEA)
+* Franck VEDRINE (CEA)
+* Augustin LEMESLE (CEA)
+* Pierre GAILLARD (CEA)
+* Jean SOUYRIS (Airbus)
+* Eric JENN (IRT St-Ex)
+* Mariem TURKI (IRT St-Ex)
+
+---
+
+## Purpose of the Meeting
+
+* Presentation of the various works carried out by CEA on the numerical precision of neural networks.
+* Discussion on the overall articulation of the work, particularly its relationship with the objectives of the SONNX working group and those of the Aidge platform.
+
+---
+
+## Minutes
+
+### Presentation of the Context
+
+* Brief presentation of the SONNX group’s work
+* Brief presentation of the Aidge platform
+
+### Presentation of the Work on Numerical Precision
+
+* Presentation by Yves (see slides)
+* Presentation by Franck (see slides)
+* Presentation by Augustin (see slides)
+
+---
+
+## (Attempted) Summary
+
+From SONNX’s perspective, the objective is to ensure that the specification is sufficiently precise to:
+
+(i) capture the intentions of the model designer,
+(ii) preserve a set of properties that have been verified on the model (e.g., ML performance, robustness, etc.),
+(iii) enable the model implementer to preserve (i) and (ii).
+
+Regarding (i), SONNX describes the semantics of operators *while taking into account certain implementation-related aspects.* In general, it is simply impossible to implement exactly a specification that would be purely mathematical. Therefore, the “user” must be aware of certain consequences related to implementation choices.
+
+In practice, each operator is specified by explicitly describing the effects of IEEE special values and domain bounds, but *the precision of the operators is not specified.* This is, of course, debatable since, *in principle*, not specifying the precision of the result of a numerical computation amounts to saying nothing at all.
+
+However, it is difficult to specify precision in absolute terms without referring to the original need (i.e., linking it to the network’s own performance) and without over-specifying. This is particularly true in ML, where:
+
+1. The performance of the function (e.g., network accuracy) does not depend very directly on computational precision (see quantization).
+2. Performance (latency) plays a major role given the large number of computations involved, which encourages numerous optimizations.
+
+In reality, mathematical libraries that provide guaranteed precision values are relatively rare (please correct me if this is wrong).
+
+The approach proposed by SONNX, as described by Franck, consists in providing methodological means and tools to evaluate a guaranteed formal error, rather than specifying it.
+
+The analytical formulation of precision depends on the chosen computation method (“algorithm”). In the example of ( \tanh ) presented by Franck, a computation method is proposed that is generally more precise than the usual formulation. However, it is certainly not the only or the best method; it is one method, and it is the one that will be implemented in the SONNX reference implementation.
+
+One could consider that any implementation of ( \tanh ) should be at least as precise as the reference implementation, but in that case, we would return to the idea of specifying precision. Instead, it will be considered as indicative guidance.
+
+It should also be noted that certain characteristics of value domains inherent to neural networks are not taken into account (e.g., the fact that values are often normalized to belong to the interval ([0,1])).
+
+The analysis of error introduction is analytical (sometimes defined recursively with respect to the number of dimensions) and conservative. Again, there is no single unique way to perform the analysis, and the choice of method is mainly guided by complexity and effort considerations. The user remains free to perform a more precise analysis.
+
+(It should also be noted that an analysis of error propagation is proposed, but this is mainly for informative purposes.)
+
+Franck also proposes a tooling approach (a class library) that, by simply changing the variable types, makes it possible to obtain an error estimate for a given algorithm implementation (code). If I understood correctly, this could replace manual analysis, although it cannot be fully automated, as certain choices require user intervention.
+
+The methods and tools proposed by Yves operate through code instrumentation (via the same operator overloading mechanism used by Franck, if I understood correctly). Error computation is performed trajectory by trajectory by introducing a random perturbation in each computation (rounding upward or downward). The method is slightly more complex in order to avoid being overly conservative (for example, the error on the computation of the expression ( a - a ) must be zero regardless of the error made in evaluating ( a )).
+
+The result is not an upper bound on the error, unlike Franck’s approach, but rather an error closer to empirical observation. It notably accounts for compensation effects that may occur during computations. Yves’ method makes it possible to compute errors on complete graphs.
+
+The method proposed by Augustin does not aim to calculate the introduction and propagation of errors, but rather to provide bounds on the values taken by the outputs of a network (final or intermediate), assuming the operators are ideal (i.e., mathematically exact). However, the effects of errors are taken into account in the computation of abstract domains. Thus, if an interval ([m, M]) is computed, the values of ( m ) and ( M ) are determined so that they are valid lower and upper bounds, considering that they were computed using floating-point numbers.
+
+The objective of the PyRat tool is therefore to provide bounds on network values, which is useful for assessing network robustness, a property often required for safety. Since operators are considered mathematically perfect (i.e., free of errors), it is possible that conclusions drawn by PyRat on the abstract model may not be preserved in the implementation of the model.
+
+One could imagine combining the approach aimed at estimating upper error bounds (Yves and Franck) with the abstract interpretation implemented by Augustin: each “ideal” computation would take into account the error (worst-case or average) computed using Yves’ and Franck’s methods.
diff --git a/safety-related-profile/meetings/numerical accuracy/README.md b/safety-related-profile/meetings/numerical accuracy/README.md
new file mode 100644
index 00000000..90dc86ab
--- /dev/null
+++ b/safety-related-profile/meetings/numerical accuracy/README.md
@@ -0,0 +1,5 @@
+This directory contains minutes of discussions dedicated to the question of numerical accuracy, specification of FP operators, etc. :
+- A [few elements](01_what_is_the_issue.md) about the issue (Eric)
+- The [slides](slides-06-11.md) presented on 11/06 WG meeting (Eric)
+- The [minutes of the meeting](./2025-04-11_Meeting_with_Franck.md) with Franck VEDRINE (CEA)
+- The [slides presented by Franck VEDRINE](./franck_slides_19_11_25.pdf) that depict the approach followed in SONNX.
diff --git a/safety-related-profile/meetings/errors/images/ada_accuracy.png b/safety-related-profile/meetings/numerical accuracy/images/ada_accuracy.png
similarity index 100%
rename from safety-related-profile/meetings/errors/images/ada_accuracy.png
rename to safety-related-profile/meetings/numerical accuracy/images/ada_accuracy.png
diff --git a/safety-related-profile/meetings/numerical accuracy/images/stability.png b/safety-related-profile/meetings/numerical accuracy/images/stability.png
new file mode 100644
index 00000000..877c81cc
Binary files /dev/null and b/safety-related-profile/meetings/numerical accuracy/images/stability.png differ
diff --git a/safety-related-profile/meetings/numerical accuracy/numerical_stability.md b/safety-related-profile/meetings/numerical accuracy/numerical_stability.md
new file mode 100644
index 00000000..a37d2984
--- /dev/null
+++ b/safety-related-profile/meetings/numerical accuracy/numerical_stability.md
@@ -0,0 +1,112 @@
+
+
+## Python Example: Stable vs Unstable Computation
+
+We'll compute the function:
+$f(x) = \frac{1 - \cos(x)}{x^2}$ as $x \rightarrow 0$.
+
+This form is **numerically unstable** due to cancellation (subtracting two nearly equal numbers).
+
+### Stable Alternative:
+Use the Taylor expansion:
+
+$1 - \cos(x) \approx \frac{x^2}{2} - \frac{x^4}{24}$
+
+So $f(x) \approx 0.5 - \frac{x^2}{24}$
+
+### Python Code
+
+```python
+import numpy as np
+import matplotlib.pyplot as plt
+
+# Unstable implementation
+def f_unstable(x):
+ return (1 - np.cos(x)) / x**2
+
+# Stable implementation using Taylor series
+def f_stable(x):
+ return 0.5 - x**2 / 24
+
+# Values close to 0
+x_vals = np.logspace(-16, -1, 100)
+y_unstable = f_unstable(x_vals)
+y_stable = f_stable(x_vals)
+
+plt.figure(figsize=(10, 6))
+plt.plot(x_vals, y_unstable, label="Unstable", color='red')
+plt.plot(x_vals, y_stable, label="Stable", linestyle='--', color='green')
+plt.axhline(0.5, color='blue', linestyle=':', label="True Limit = 0.5")
+plt.xscale('log')
+plt.xlabel("x")
+plt.ylabel("f(x)")
+plt.title("Numerical Stability Example")
+plt.legend()
+plt.grid(True)
+plt.tight_layout()
+plt.show()
+```
+On the following picture, you can see that the unstable version (red line) becomes wildly inaccurate as x gets very small, due to numerical errors. The stable version (green dashed line) stays close to the true limit of 0.5 (blue dotted line).
+
+
+
+
+## Cheat sheet
+
+### Common Pitfalls & Fixes
+
+| ❌ Unstable Pattern | ✅ Stable Alternative |
+|-----------------------------------------|--------------------------------------------------|
+| `a - b` where `a ≈ b` | Algebraic manipulation or Taylor expansion |
+| Matrix inversion: `inv(A) @ b` | Use `np.linalg.solve(A, b)` |
+| Classical Gram-Schmidt | Use **Modified Gram-Schmidt** |
+| Small `x`: `(1 - cos(x)) / x^2` | Use series: `0.5 - x^2 / 24` |
+| Large/small values in matrix | Rescale or normalize data |
+
+---
+
+### Stability Best Practices
+
+- Avoid subtracting nearly equal numbers
+- Use backward stable algorithms
+- Normalize inputs when scales vary wildly
+- Use high-precision types (`float64`, `mpmath`)
+- Prefer library routines (NumPy, SciPy, LAPACK)
+
+
+### Tools to Assess Stability
+
+| Tool | Description |
+|----------------------------------|----------------------------------------------------|
+| `np.linalg.cond(A)` | Matrix condition number (sensitivity) |
+| `np.seterr(all='warn')` | Catch floating-point warnings |
+| Compare multiple methods | Sanity check results across techniques |
+| Refine resolution or input size | See how solution evolves (esp. in ODEs/PDEs) |
+
+---
+
+### Stability Testing Techniques
+
+#### Forward Error Analysis
+Compare computed output to exact result:
+```
+Forward Error = || y_computed - y_exact ||
+```
+
+#### Backward Error Analysis
+Show computed output is exact for a nearby input:
+```
+A * x_computed = b + δb → Backward Stable
+```
+
+#### Condition Number
+Estimates sensitivity of problem:
+```
+κ = ||A|| * ||A⁻¹|| (depends on chosen norm)
+```
+Large `κ` ⇒ ill-conditioned ⇒ sensitive to input errors
+
+
+
+
+
diff --git a/safety-related-profile/meetings/errors/slides-06-11.md b/safety-related-profile/meetings/numerical accuracy/slides-06-11.md
similarity index 100%
rename from safety-related-profile/meetings/errors/slides-06-11.md
rename to safety-related-profile/meetings/numerical accuracy/slides-06-11.md
diff --git a/safety-related-profile/meetings/errors/slides-06-11.pdf b/safety-related-profile/meetings/numerical accuracy/slides-06-11.pdf
similarity index 100%
rename from safety-related-profile/meetings/errors/slides-06-11.pdf
rename to safety-related-profile/meetings/numerical accuracy/slides-06-11.pdf
diff --git a/safety-related-profile/meetings/presentation_proposals.md b/safety-related-profile/meetings/presentation_proposals.md
index a4938377..d7004375 100644
--- a/safety-related-profile/meetings/presentation_proposals.md
+++ b/safety-related-profile/meetings/presentation_proposals.md
@@ -4,8 +4,10 @@
| Code generation for neural networks | Sebastian Boblest (Bosch) | 2024/11 | 2024/12/18 |
| ONNX MLIR | Alexandre Eichenberger (IBM) | 2024/10 | 2025/02/12 |
| Evaluation and improvement of SW verifiers on FP Neural Networks | Edoardo Manino (Manchester U) | 2024/11 | 202(/01/15) |
-
-
+| About quantization and ONNX in Airbus' context | Alex Digonnet | 2025/05/07 | 2025/05/07 |
+| RMT | Jean-Baptiste | 2025/05/07 | |
+|Specification of concat| Salomé Marty Laurent | 2025/05/21 | 2025/05/21 |
+| TITLE| Dumitru Potop | 2025/06/18 | TBC |
# Meetings
- 2024/11/20
@@ -13,5 +15,15 @@
- 2024/12/18 : Sebastian
- 2025/01/15 : Edoardo
- 2025/01/29
-- 2025/02/12 : Alexandre
-- 2025/02/26
+- 2025/02/12
+- 2025/02/26 (Eric on vacations)
+- 2025/03/12
+- 2025/03/26
+- 2025/04/09 : Alexandre => to be rescheduled
+- 2025/04/23
+- 2025/05/07 : Jean-Baptiste ; Alex
+- 2025/05/21 : Salomé
+- 2025/09/10 :
+- 2025/10/08 :
+- 2025/10/22 :
+- 2025/11/05 :
diff --git a/safety-related-profile/meetings/slides/2025-03-12-Dumitru on ONNX-graph-semantics.pdf b/safety-related-profile/meetings/slides/2025-03-12-Dumitru on ONNX-graph-semantics.pdf
new file mode 100644
index 00000000..03ee18d9
Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-03-12-Dumitru on ONNX-graph-semantics.pdf differ
diff --git a/safety-related-profile/meetings/slides/2025-05-07-Alex-DIGONNET-SONNX quantization representation formats.pdf b/safety-related-profile/meetings/slides/2025-05-07-Alex-DIGONNET-SONNX quantization representation formats.pdf
new file mode 100644
index 00000000..936063af
Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-05-07-Alex-DIGONNET-SONNX quantization representation formats.pdf differ
diff --git a/safety-related-profile/meetings/slides/2025-05-21-Salome_MARTY_LAURENT_concat_operator_specification.pdf b/safety-related-profile/meetings/slides/2025-05-21-Salome_MARTY_LAURENT_concat_operator_specification.pdf
new file mode 100644
index 00000000..e080e8b0
Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-05-21-Salome_MARTY_LAURENT_concat_operator_specification.pdf differ
diff --git a/safety-related-profile/meetings/slides/2025-07-16-Salome_MARTY_LAURENT_add_operator_specification.pdf b/safety-related-profile/meetings/slides/2025-07-16-Salome_MARTY_LAURENT_add_operator_specification.pdf
new file mode 100644
index 00000000..e6c35376
Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-07-16-Salome_MARTY_LAURENT_add_operator_specification.pdf differ
diff --git a/safety-related-profile/meetings/slides/2025-11-19 - Jean- SONNX V&V .pdf b/safety-related-profile/meetings/slides/2025-11-19 - Jean- SONNX V&V .pdf
new file mode 100644
index 00000000..5915ed8b
Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-11-19 - Jean- SONNX V&V .pdf differ
diff --git a/safety-related-profile/meetings/slides/2025-11-20-Franck-numerical-accuracy.pdf b/safety-related-profile/meetings/slides/2025-11-20-Franck-numerical-accuracy.pdf
new file mode 100644
index 00000000..69f075e0
Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-11-20-Franck-numerical-accuracy.pdf differ
diff --git a/safety-related-profile/meetings/slides/2025-11-20-Jean-verification-plan.pdf b/safety-related-profile/meetings/slides/2025-11-20-Jean-verification-plan.pdf
new file mode 100644
index 00000000..11acd612
Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-11-20-Jean-verification-plan.pdf differ
diff --git a/safety-related-profile/meetings/slides/AIDGE.pptx b/safety-related-profile/meetings/slides/AIDGE.pptx
new file mode 100644
index 00000000..ef20a95d
Binary files /dev/null and b/safety-related-profile/meetings/slides/AIDGE.pptx differ
diff --git a/safety-related-profile/meetings/slides/README.md b/safety-related-profile/meetings/slides/README.md
index 2ae9ffc2..4fe58abb 100644
--- a/safety-related-profile/meetings/slides/README.md
+++ b/safety-related-profile/meetings/slides/README.md
@@ -1,3 +1,5 @@
Slides presented during our periodic meetings.
- [Slides presented by Sebastian on Bosch's code generatuoin tool](./2024-12-18-Embedded_AI_Coder_Bosch_Boblest.pdf)
- [Slides presented by Edoardo on bit-precise neural network verification](./2025-01-15-Edoardo_Manino_SONNX_slides.pdf)
+- [Slides presented by Dumitru on ONNX graph semantics](./2025-03-12-Dumitru%20on%20ONNX-graph-semantics.pdf)
+
diff --git a/safety-related-profile/meetings/testing/2025-07-29-tests.md b/safety-related-profile/meetings/testing/2025-07-29-tests.md
new file mode 100644
index 00000000..ba986c88
--- /dev/null
+++ b/safety-related-profile/meetings/testing/2025-07-29-tests.md
@@ -0,0 +1,32 @@
+**Objective of the meeting**
+- Establish a technical link between the work on test definition and generation done by Christian using Hypothesis and the work of SONNX.
+
+**Minutes**
+- Brief presentation of the work of SONNX. The full slide deck can be found [here](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/Other_meetings/SONNX%20-%20WG114.pdf)
+- Brief presentation of the [Hypothesis](https://hypothesis.readthedocs.io/en/latest/reference/strategies.html) test framework and its use to generate test for ONNX ops. Christian work is available [here](https://github.com/cbourjau/onnx-tests). The list of operators already covered is [there](https://github.com/cbourjau/onnx-tests/blob/main/report/coverage.md).
+- Basically, Hypothesis allows to describe conditions on the inputs of a function and generate automatically (more or less randomly) inputs compliant with these conditions. This facilitate the generation of large test sets, even though it does not provide any coverage "guarantee" with respect to some set of equivalence classes (themselves defined with respect to some test objective).
+- The first tests carried out by Christian show that there are still errors in ONNX runtime. Most of the errors activated by those tests actually concern non implemented functions (with respect to types), or boundary values corresponding to very unlikely situations (dimensions equal to 0).
+- ORT comes with a bunch of tests, but they are hand crafted, which means the coverage is currently not satisfying completely .
+- We agreed that there is a strong relation between the specification developed in SONNX, since this specification defines the domain of the operators (so-called "constraints" in the SONNX spec).
+- The Hypothesis constraints (or part of them) could be generated from the formal specification.
+
+[ ] (SONNX) Try to do the exercise on the Conv2D operator for which we both have the formal specification and the Hypothesis tests. The same could be done for CONCAT (see next action).
+[ ] (Christian) Provide an Hypothesis definition of test cases for the CONCAT operator.
+
+- The reference implementation to be developed in SONNX could be used as a possible oracle for Hypothesis tests.
+- Note that during the development of the SONNX profile, the tests could also be used to detect errors the specification: a test that does not pass (i.e., there is a discrepancy between our reference implementation and, let's say, the ORT) may reflect either an error in the spec or in the implementation. It will be up to the analysis to identify the root cause of the discrepancy.
+- Eric mentioned that the [DeepGreen](https://deepgreen.ai/en) project, which is developing the [AIDGE](https://gitlab.eclipse.org/eclipse/aidge/aidge) platform, is also developing tests cases. There may be an opportunity to share some of the work.
+- We have had a discussion about the generation of tests for a complete graph using Hypothesis. In that case, the difficulty is to define the ODD using Hypothesis. More often than not the ODD is actually defined by a representative set of samples. For high-dimensional inputs, defining conditions on the inputs is usually very difficult (not impossible depending on the application).
+- Discussion about the demonstration of semantic equivalence in the presence of graph transformation. Inspiration could be obtained from what is done on compilers (e.g., LLVM).
+
+[ ] Eric to provide contact to the [DeepGreen](https://deepgreen.ai/en) project
+
+[ ] Eric to provide pointers to te SONNX documentation
+=>
+- The repository is [here](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile) (this is Eric's repo ; the ONNX repo is not up to date).
+- You will find [there](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/minutes.md) the minutes of all our meetings.
+- The set of operators is [here](http://safety-related-profile/documents/profile_opset). /!\ This is a work in progress /!\
+- Note that [conv](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/conv/conv.md) and [concat](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/concat/concat.md) are the most up to date specifications.
+- All the operators should comply with a certain set of [guidelines](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/guidelines.md).
+- The formal specifications are [here](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/conv/why3/standard_convolution.mlw) for conv and [there](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/conv/why3/standard_convolution.mlw) for concat. They are expressed using the WhyMl language.
+- There is more material, about the semantics of the [graph](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_graph/graph.md) execution, about [error conditions](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/meetings/errror%20conditions), about [numerical accuracy](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/meetings/numerical%20accuracy), etc.
diff --git a/safety-related-profile/sonnx/README.md b/safety-related-profile/sonnx/README.md
new file mode 100644
index 00000000..b712ba72
--- /dev/null
+++ b/safety-related-profile/sonnx/README.md
@@ -0,0 +1,58 @@
+# Directory Structure
+
+This repository contains both formal and informal specifications for operators and graph, documentation, tests, and verification tools for various operators. The project is organized as follows:
+
+## General Structure
+
+```plaintext
+sonnx/
+├── ops/
+│ ├── spec/
+│ │ ├── informal/
+│ │ │ ├── common
+│ │ │ ├── add/
+│ │ │ │ ├── assets
+│ │ │ │ ├── reviews
+│ │ │ │ ├── tests
+│ │ │ │ └── README.md
+│ │ │ ├── conv
+│ │ │ └── ...
+│ │ └── formal/
+│ │ ├── common/
+│ │ │ ├── libs
+│ │ │ └── Makefile
+│ │ ├── add
+│ │ ├── conv
+│ │ └── ...
+│ ├── code/
+│ │ ├── common/
+│ │ │ ├── drivers
+│ │ │ ├── Makefile
+│ │ │ └── libs
+│ │ ├── add/
+│ │ │ ├── generated_code/
+│ │ │ │ ├── ocaml_code
+│ │ │ │ └── c_code
+│ │ │ ├── generated_doc
+│ │ │ └── tests
+│ │ ├── conv
+│ │ └── ...
+│ └── docs/
+│ ├── guidelines
+│ └── tensor_lib
+
+└── graph
+```
+## Main content
+- [Informal specification](ops/spec/informal/)
+- [Formal specification](ops/spec/formal/)
+- [Guidelines](ops/docs/guidelines/)
+ - to [write informal specifications](./ops/docs/guidelines/informal.md)
+ - to write formal specifications (TBC)
+ - to write tests (TBC)
+ - [to manage the items's lifecycle](./ops/docs/guidelines/lifecycle.md)
+- [Code and documentation generated from Why3](ops/code/)
+- [Why3 installation guide](ops/docs/installation/)
+- [Informal specification template](ops/spec/informal/common/template.md)
+- [Tensor library formal specification](ops/spec/formal/common/libs/tensor/)
+- [Tensor library documentation](ops/docs/tensor_lib/)
diff --git a/safety-related-profile/meetings/reqs_sub_wg/minutes.md b/safety-related-profile/sonnx/graph/README.md
similarity index 100%
rename from safety-related-profile/meetings/reqs_sub_wg/minutes.md
rename to safety-related-profile/sonnx/graph/README.md
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.c
new file mode 100644
index 00000000..6d82f3e4
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.c
@@ -0,0 +1,58 @@
+#include "cindex.h"
+
+int32_t cdim_size(int32_t * u, int32_t n) {
+ int32_t p;
+ int32_t i, o;
+ p = 1;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ p = p * u[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
+
+int32_t * cdim_create_1(int32_t n) {
+ int32_t * cd;
+ cd = malloc(1U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = n;
+ }
+ return cd;
+}
+
+int32_t * cdim_create_2(int32_t p, int32_t q) {
+ int32_t * cd;
+ cd = malloc(2U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = p;
+ cd[1] = q;
+ }
+ return cd;
+}
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) {
+ int32_t p;
+ int32_t i, o, d, k;
+ p = 0;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ d = ds[i];
+ k = ks[i];
+ if (0 <= k && k < d) {
+ p = p * d + k;
+ } else {
+ return -1;
+ }
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.h
new file mode 100644
index 00000000..f8c9d87c
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.h
@@ -0,0 +1,15 @@
+#ifndef CINDEX_H_INCLUDED
+
+#include
+#include
+
+int32_t cdim_size(int32_t * u, int32_t n);
+
+int32_t * cdim_create_1(int32_t n);
+
+int32_t * cdim_create_2(int32_t p, int32_t q);
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n);
+
+#define CINDEX_H_INCLUDED
+#endif // CINDEX_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.o
new file mode 100644
index 00000000..493a1869
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.o differ
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.c
new file mode 100644
index 00000000..7c818502
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.c
@@ -0,0 +1,43 @@
+#include "ctensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ double * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(double));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = ((double) 0.0);
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, double v) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.h
new file mode 100644
index 00000000..7aa119d5
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.h
@@ -0,0 +1,20 @@
+#ifndef CTENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ double * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r);
+
+void ctensor_reset(struct ctensor r, double v);
+
+#define CTENSOR_H_INCLUDED
+#endif // CTENSOR_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.o
new file mode 100644
index 00000000..56705066
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.o differ
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.c b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.c
new file mode 100644
index 00000000..ab6def31
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.c
@@ -0,0 +1,15 @@
+#include "ctensoradd.h"
+
+void ctensor_add(struct ctensor a, struct ctensor b, struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = a.t_data[i] + b.t_data[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.h b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.h
new file mode 100644
index 00000000..aeeafc77
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.h
@@ -0,0 +1,11 @@
+#ifndef CTENSORADD_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include "ctensor.h"
+
+void ctensor_add(struct ctensor a, struct ctensor b, struct ctensor r);
+
+#define CTENSORADD_H_INCLUDED
+#endif // CTENSORADD_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.o b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.o
new file mode 100644
index 00000000..80a3eed9
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.o differ
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/ocaml_code/README.md b/safety-related-profile/sonnx/ops/code/add/generated_code/ocaml_code/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/safety-related-profile/sonnx/ops/code/add/generated_doc/README.md b/safety-related-profile/sonnx/ops/code/add/generated_doc/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.c
new file mode 100644
index 00000000..6d82f3e4
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.c
@@ -0,0 +1,58 @@
+#include "cindex.h"
+
+int32_t cdim_size(int32_t * u, int32_t n) {
+ int32_t p;
+ int32_t i, o;
+ p = 1;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ p = p * u[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
+
+int32_t * cdim_create_1(int32_t n) {
+ int32_t * cd;
+ cd = malloc(1U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = n;
+ }
+ return cd;
+}
+
+int32_t * cdim_create_2(int32_t p, int32_t q) {
+ int32_t * cd;
+ cd = malloc(2U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = p;
+ cd[1] = q;
+ }
+ return cd;
+}
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) {
+ int32_t p;
+ int32_t i, o, d, k;
+ p = 0;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ d = ds[i];
+ k = ks[i];
+ if (0 <= k && k < d) {
+ p = p * d + k;
+ } else {
+ return -1;
+ }
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.h
new file mode 100644
index 00000000..f8c9d87c
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.h
@@ -0,0 +1,15 @@
+#ifndef CINDEX_H_INCLUDED
+
+#include
+#include
+
+int32_t cdim_size(int32_t * u, int32_t n);
+
+int32_t * cdim_create_1(int32_t n);
+
+int32_t * cdim_create_2(int32_t p, int32_t q);
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n);
+
+#define CINDEX_H_INCLUDED
+#endif // CINDEX_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.o
new file mode 100644
index 00000000..493a1869
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.o differ
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.c
new file mode 100644
index 00000000..7c818502
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.c
@@ -0,0 +1,43 @@
+#include "ctensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ double * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(double));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = ((double) 0.0);
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, double v) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.h
new file mode 100644
index 00000000..7aa119d5
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.h
@@ -0,0 +1,20 @@
+#ifndef CTENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ double * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r);
+
+void ctensor_reset(struct ctensor r, double v);
+
+#define CTENSOR_H_INCLUDED
+#endif // CTENSOR_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.o
new file mode 100644
index 00000000..56705066
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.o differ
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.c b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.c
new file mode 100644
index 00000000..5e6f34d0
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.c
@@ -0,0 +1,20 @@
+#include "tensorclip.h"
+
+void ctensor_clip(struct ctensor x, struct ctensor l, struct ctensor m,
+ struct ctensor r) {
+ int32_t n, i, o;
+ double l_background, m_background;
+ n = cdim_size(r.t_dims, r.t_rank);
+ l_background = l.t_data[0];
+ m_background = m.t_data[0];
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = infix_dtlsls(m_background,
+ infix_dtgtgt(x.t_data[i], l_background));
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.h b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.h
new file mode 100644
index 00000000..f543106d
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.h
@@ -0,0 +1,12 @@
+#ifndef TENSORCLIP_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+#include "ctensor.h"
+
+void ctensor_clip(struct ctensor x, struct ctensor l, struct ctensor m,
+ struct ctensor r);
+
+#define TENSORCLIP_H_INCLUDED
+#endif // TENSORCLIP_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.o b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.o
new file mode 100644
index 00000000..cc67f031
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.o differ
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/README.md b/safety-related-profile/sonnx/ops/code/common/libs/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.c
new file mode 100644
index 00000000..6d82f3e4
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.c
@@ -0,0 +1,58 @@
+#include "cindex.h"
+
+int32_t cdim_size(int32_t * u, int32_t n) {
+ int32_t p;
+ int32_t i, o;
+ p = 1;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ p = p * u[i];
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
+
+int32_t * cdim_create_1(int32_t n) {
+ int32_t * cd;
+ cd = malloc(1U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = n;
+ }
+ return cd;
+}
+
+int32_t * cdim_create_2(int32_t p, int32_t q) {
+ int32_t * cd;
+ cd = malloc(2U * sizeof(int32_t));
+ if (cd) {
+ cd[0] = p;
+ cd[1] = q;
+ }
+ return cd;
+}
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) {
+ int32_t p;
+ int32_t i, o, d, k;
+ p = 0;
+ o = n - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ d = ds[i];
+ k = ks[i];
+ if (0 <= k && k < d) {
+ p = p * d + k;
+ } else {
+ return -1;
+ }
+ if (i == o) {
+ break;
+ }
+ }
+ }
+ return p;
+}
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.h
new file mode 100644
index 00000000..f8c9d87c
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.h
@@ -0,0 +1,15 @@
+#ifndef CINDEX_H_INCLUDED
+
+#include
+#include
+
+int32_t cdim_size(int32_t * u, int32_t n);
+
+int32_t * cdim_create_1(int32_t n);
+
+int32_t * cdim_create_2(int32_t p, int32_t q);
+
+int32_t coffset(int32_t * ks, int32_t * ds, int32_t n);
+
+#define CINDEX_H_INCLUDED
+#endif // CINDEX_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.o
new file mode 100644
index 00000000..e7d84030
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.o differ
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.c
new file mode 100644
index 00000000..7c818502
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.c
@@ -0,0 +1,43 @@
+#include "ctensor.h"
+struct ctensor;
+
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n) {
+ int32_t m;
+ double * vs;
+ struct ctensor ctensor;
+ m = cdim_size(ds, n);
+ vs = malloc(((uint32_t) m) * sizeof(double));
+ ctensor.t_rank = !vs ? 0 : n;
+ ctensor.t_dims = ds;
+ ctensor.t_data = vs;
+ return ctensor;
+}
+
+void ctensor_clear(struct ctensor r) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = ((double) 0.0);
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
+
+void ctensor_reset(struct ctensor r, double v) {
+ int32_t m, i, o;
+ m = cdim_size(r.t_dims, r.t_rank);
+ o = m - 1;
+ if (0 <= o) {
+ for (i = 0; ; ++i) {
+ r.t_data[i] = v;
+ if (i == o) {
+ break;
+ }
+ }
+ }
+}
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.h
new file mode 100644
index 00000000..7aa119d5
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.h
@@ -0,0 +1,20 @@
+#ifndef CTENSOR_H_INCLUDED
+
+#include
+#include
+#include "cindex.h"
+
+struct ctensor {
+ int32_t t_rank;
+ int32_t * t_dims;
+ double * t_data;
+};
+
+struct ctensor ctensor_create(int32_t * ds, int32_t n);
+
+void ctensor_clear(struct ctensor r);
+
+void ctensor_reset(struct ctensor r, double v);
+
+#define CTENSOR_H_INCLUDED
+#endif // CTENSOR_H_INCLUDED
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.o
new file mode 100644
index 00000000..43e64d66
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.o differ
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff
new file mode 100644
index 00000000..8f5e63c0
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff differ
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff2 b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff2
new file mode 100644
index 00000000..f106190e
Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff2 differ
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/icofont.min.css b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/icofont.min.css
new file mode 100644
index 00000000..8402c60f
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/icofont.min.css
@@ -0,0 +1,7 @@
+/*!
+* @package IcoFont
+* @version 1.0.1
+* @author
+* @copyright 2024
+* @license - https://icofont.com/license/
+*/@font-face{font-family:IcoFont;font-weight:400;font-style:Regular;src:url(fonts/icofont.woff2) format("woff2"),url(fonts/icofont.woff) format("woff")}[class*=" icofont-"],[class^=icofont-]{font-family:IcoFont!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;line-height:1;-webkit-font-feature-settings:"liga";-webkit-font-smoothing:antialiased}.icofont-check:before{content:"\eed8"}.icofont-warning:before{content:"\f026"}.icofont-question-circle:before{content:"\efca"}.icofont-star:before{content:"\f000"}.icofont-error:before{content:"\ef16"}.icofont-warning-alt:before{content:"\f025"}[class*=" icofont-"].icofont-duotone,[class^=icofont-].icofont-duotone{position:relative}[class*=" icofont-"].icofont-duotone:before,[class^=icofont-].icofont-duotone:before{position:absolute;left:0;top:0}[class*=" icofont-"].icofont-duotone:after,[class^=icofont-].icofont-duotone:after{opacity:.4}.icofont-xs{font-size:.5em}.icofont-sm{font-size:.75em}.icofont-md{font-size:1.25em}.icofont-lg{font-size:1.5em}.icofont-1x{font-size:1em}.icofont-2x{font-size:2em}.icofont-3x{font-size:3em}.icofont-4x{font-size:4em}.icofont-5x{font-size:5em}.icofont-6x{font-size:6em}.icofont-7x{font-size:7em}.icofont-8x{font-size:8em}.icofont-9x{font-size:9em}.icofont-10x{font-size:10em}.icofont-fw{text-align:center;width:1.25em}.icofont-ul{list-style-type:none;padding-left:0;margin-left:0}.icofont-ul>li{position:relative;line-height:2em}.icofont-ul>li .icofont{display:inline-block;vertical-align:middle}.icofont-border{border:solid .08em #f1f1f1;border-radius:.1em;padding:.2em .25em .15em}.icofont-pull-left{float:left}.icofont-pull-right{float:right}.icofont.icofont-pull-left{margin-right:.3em}.icofont.icofont-pull-right{margin-left:.3em}.icofont-spin{-webkit-animation:icofont-spin 2s infinite linear;animation:icofont-spin 2s infinite linear;display:inline-block}.icofont-pulse{-webkit-animation:icofont-spin 1s infinite steps(8);animation:icofont-spin 1s infinite steps(8);display:inline-block}@-webkit-keyframes icofont-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes icofont-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.icofont-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.icofont-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.icofont-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.icofont-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.icofont-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.icofont-flip-horizontal.icofont-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .icofont-flip-horizontal,:root .icofont-flip-vertical,:root .icofont-rotate-180,:root .icofont-rotate-270,:root .icofont-rotate-90{-webkit-filter:none;filter:none;display:inline-block}.icofont-inverse{color:#fff}
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/index.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/index.html
new file mode 100644
index 00000000..133d9f64
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/index.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+Tensor Library
+
+
+index — Tensor Library
+
+ letreclemmapositive_pdim (u : iarray) (p q : int)
+ {proof…}
+ ensures { pdim u p q <-> positive (islice u p q) }
+ variant { q - p }
+ = if p < q then positive_pdim u (p+1) q
+ {qed}
+
+ letghostsdim_split (u : iarray) (p k q : int)
+ {proof…}
+ requires { p <= k < q }
+ requires { pdim u p q }
+ ensures { sdim u p k *value_at u k *sdim u (k+1) q = sdim u p q }
+ = assert { islice u p k ++islice u k q = islice u p q }
+ {qed}
+
+ useList
+
+ letrecghostfunctionslice (u : ptr 'a) (p q : int) : list 'a =
+ {proof…}
+ variant { q - p }
+ {qed}
+ if q <= p thenNilelseCons (value_at u p) (slice u (p+1) q)
+
+ letreclemmaslice_append (u : ptr 'a) (p q r : int)
+ requires { p <= q <= r }
+ ensures { slice u p r = slice u p q ++slice u q r }
+ {proof…}
+ variant { q - p }
+ = if p < q then slice_append u (p+1) q r
+ {qed}
+
+ letghostfunctionvector (u : ptr 'a) (n : int) = slice u 0 n
+
+ letlemmavector_push (u : ptr 'a) (n : int)
+ requires { 0 <= n }
+ ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+ {proof…}
+ = slice_append u 0 n (n+1)
+ {qed}
+
+end
+
+ letghosteuclide (a b q r : int)
+ requires { 0 <= a /\ 0 <= r < q }
+ requires { a = b * q + r }
+ ensures { b = div a q }
+ ensures { r = mod a q }
+ {proof…}
+ = letrec kernel (b r : int)
+ requires { b * q + r = 0 }
+ requires { abs r < q }
+ ensures { b = r = 0 }
+ variant { abs b }
+ = if b < 0 then kernel (b + 1) (r - q) else
+ if b > 0 then kernel (b - 1) (r + q) else ()
+ in kernel (b -div a q) (r -mod a q)
+ {qed}
+
+
+
Multiplication upper–bound
+
+
+ letghostmult_bound (a b c : int)
+ requires { 0 < a * b <= c }
+ ensures { abs a <= c }
+ ensures { abs b <= c }
+ = ()
+
+end
+
+moduleTensor
+ use int.Int
+ use map.Map
+ use list.List
+ useRange
+
+ typedata 'a = map (list int) 'a
+
+ typetensor 'a = {
+ dims : list int ;
+ data : data 'a ;
+ background : 'a ; (* default value, or value for 0-dimensions tensor *)
+ }
+ invariant { positive dims }
+ invariant { forall k. valid k dims \/ data k = background }
+ {proof…}
+ bylet d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+ {qed}
+
+ meta coercion functiondims
+ meta coercion functiondata
+
+ letreclemmapositive_pdim (u : iarray) (p q : int)
+ {proof…}
+ ensures { pdim u p q <-> positive (islice u p q) }
+ variant { q - p }
+ = if p < q then positive_pdim u (p+1) q
+ {qed}
+
+ letghostsdim_split (u : iarray) (p k q : int)
+ {proof…}
+ requires { p <= k < q }
+ requires { pdim u p q }
+ ensures { sdim u p k *value_at u k *sdim u (k+1) q = sdim u p q }
+ = assert { islice u p k ++islice u k q = islice u p q }
+ {qed}
+
+ useList
+
+ letrecghostfunctionslice (u : ptr 'a) (p q : int) : list 'a =
+ {proof…}
+ variant { q - p }
+ {qed}
+ if q <= p thenNilelseCons (value_at u p) (slice u (p+1) q)
+
+ letreclemmaslice_append (u : ptr 'a) (p q r : int)
+ requires { p <= q <= r }
+ ensures { slice u p r = slice u p q ++slice u q r }
+ {proof…}
+ variant { q - p }
+ = if p < q then slice_append u (p+1) q r
+ {qed}
+
+ letghostfunctionvector (u : ptr 'a) (n : int) = slice u 0 n
+
+ letlemmavector_push (u : ptr 'a) (n : int)
+ requires { 0 <= n }
+ ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+ {proof…}
+ = slice_append u 0 n (n+1)
+ {qed}
+
+end
+
+ letghosteuclide (a b q r : int)
+ requires { 0 <= a /\ 0 <= r < q }
+ requires { a = b * q + r }
+ ensures { b = div a q }
+ ensures { r = mod a q }
+ {proof…}
+ = letrec kernel (b r : int)
+ requires { b * q + r = 0 }
+ requires { abs r < q }
+ ensures { b = r = 0 }
+ variant { abs b }
+ = if b < 0 then kernel (b + 1) (r - q) else
+ if b > 0 then kernel (b - 1) (r + q) else ()
+ in kernel (b -div a q) (r -mod a q)
+ {qed}
+
+
+
Multiplication upper–bound
+
+
+ letghostmult_bound (a b c : int)
+ requires { 0 < a * b <= c }
+ ensures { abs a <= c }
+ ensures { abs b <= c }
+ = ()
+
+end
+
+moduleTensor
+ use int.Int
+ use map.Map
+ use list.List
+ useRange
+
+ typedata 'a = map (list int) 'a
+
+ typetensor 'a = {
+ dims : list int ;
+ data : data 'a ;
+ background : 'a ; (* default value, or value for 0-dimensions tensor *)
+ }
+ invariant { positive dims }
+ invariant { forall k. valid k dims \/ data k = background }
+ {proof…}
+ bylet d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+ {qed}
+
+ meta coercion functiondims
+ meta coercion functiondata
+
+moduleOPWhere
+ use tensor.tensor.Tensor
+
+ letghostfunctiondwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ letghostfunctionopwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ {proof…}
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ {qed}
+
+end
+
+
+
+
diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/where.index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.index.html
new file mode 100644
index 00000000..90f575f5
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.index.html
@@ -0,0 +1,15 @@
+
+
+
+
+
+Library where
+
+
+index — library where
+
+
+
diff --git a/safety-related-profile/sonnx/ops/code/where/tests/README.md b/safety-related-profile/sonnx/ops/code/where/tests/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/safety-related-profile/sonnx/ops/docs/c_code_generation/why3_to_c.md b/safety-related-profile/sonnx/ops/docs/c_code_generation/why3_to_c.md
new file mode 100644
index 00000000..2f4ad6e3
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/c_code_generation/why3_to_c.md
@@ -0,0 +1,152 @@
+# Converting Why3 Specifications to C Code
+
+This document outlines how to extract C code from Why3 (`.mlw`) specifications using the Why3 extraction mechanism and a C driver. It includes an example, supported features, and key constraints.
+
+## Compiling Why3 to C language
+### Example
+
+This example defines a function `locate_max` that returns the index of the maximum element in an array `a` of size `n`.
+
+```whyml
+use int.Int
+use map.Map as Map
+use mach.c.C
+use mach.int.Int32
+use mach.int.Int64
+
+function ([]) (a: ptr 'a) (i: int): 'a = Map.get a.data.Array.elts (a.offset + i)
+
+let locate_max (a: ptr int64) (n: int32): int32
+ requires { 0 < n }
+ requires { valid a n }
+ ensures { 0 <= result < n }
+ ensures { forall i. 0 <= i < n -> a[i] <= a[result] }
+= let ref idx = 0 in
+ for j = 1 to n - 1 do
+ invariant { 0 <= idx < n }
+ invariant { forall i. 0 <= i < j -> a[i] <= a[idx] }
+ if get_ofs a idx < get_ofs a j then idx <- j
+ done;
+ idx
+ ```
+
+### Extraction Command
+```bash
+why3 extract -D c locate_max.mlw
+```
+With debug details:
+```bash
+why3 extract -D c --debug-all locate_max.mlw -o locate_max.c
+```
+### Output C code
+```c
+#include
+
+int32_t locate_max(int64_t * a, int32_t n) {
+ int32_t idx;
+ int32_t j, o;
+ idx = 0;
+ o = n - 1;
+ if (1 <= o) {
+ for (j = 1; ; ++j) {
+ if (a[idx] < a[j]) {
+ idx = j;
+ }
+ if (j == o) break;
+ }
+ }
+ return idx;
+}
+```
+
+## Supporte Features and Rules
+### Basic Types
+
+| WhyML Type | Description | C Equivalent |
+|----------------------|------------------------------------|------------------------|
+| `int32`, `uint64`, etc. | From `mach.int` | `int32_t`, `uint64_t`, etc. |
+| `bool` | Translated from `bool.Bool` | `int` |
+| `char`, `string` | Partial support via `mach.c.String`| `char`, `char *` |
+| `int` (mathematical) | ❌ Not supported | — |
+| `float` | ❌ Not supported | — |
+---
+
+### Compound Types
+#### Immutable Records
+
+**WhyML:**
+```whyml
+type r = { x : int32; y : int32 }
+let swap (a : r) : r = { x = a.y ; y = a.x }
+```
+**C:**
+```c
+struct r {
+ int32_t x;
+ int32_t y;
+};
+
+struct r swap(struct r a) {
+ struct r r;
+ r.x = a.y;
+ r.y = a.x;
+ return r;
+}
+```
+
+#### Mutable Records
+**WhyML:**
+```whyml
+type r = { mutable x : int32; mutable y : int32 }
+let swap (a : r) : unit =
+ let tmp = a.y in a.y <- a.x; a.x <- tmp
+```
+**C:**
+```c
+struct r {
+ int32_t x;
+ int32_t y;
+};
+
+void swap(struct r * a) {
+ int32_t tmp;
+ tmp = a->y;
+ a->y = a->x;
+ a->x = tmp;
+}
+```
+## Unsupported Features
+- WhyML arrays
+
+- Algebraic data types (including enumerations)
+
+- Pattern matching
+
+- Exception raising and catching
+
+- Floating-point types
+
+## Control Stuctures
+
+| Feature | Support |
+|----------------------------|---------|
+| `if` / `else` | ✅ |
+| `while` loops | ✅ |
+| `for` loops | ✅ |
+| Sequence (`;`) | ✅ |
+| `break`, `continue`, `return` | ✅ |
+| Pattern matching | ❌ |
+| Exceptions | ❌ |
+
+## Notes
+- **Example of driver file used for C extraction**: [`c.drv`](https://gitlab.inria.fr/why3/why3/-/blob/master/drivers/c.drv)
+- This driver defines how WhyML types and functions are translated into equivalent C constructs.
+
+## References
+
+- [Why3 Documentation](https://why3.lri.fr/doc/)
+- [Why3 GitLab Repository](https://gitlab.inria.fr/why3/why3)
+
+
+
+
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/README.md b/safety-related-profile/sonnx/ops/docs/guidelines/README.md
new file mode 100644
index 00000000..8738c7de
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/README.md
@@ -0,0 +1,5 @@
+This folder contains the guidelines used to develop the SONNX artifacts:
+- [Guidelines to carry out accuracy analysis](accuracy.md)
+- [Guidelines to develop tests](./tests.md)
+- [Guidelines to develop the non-formal specifications](./formal.md)
+- [Guidelines to develop the formal specifications](./informal.md)
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/accuracy.md b/safety-related-profile/sonnx/ops/docs/guidelines/accuracy.md
new file mode 100644
index 00000000..2521a07f
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/accuracy.md
@@ -0,0 +1,455 @@
+#### Objectives and limits
+
+This document provides theory and techniques to write a formal specification for the
+accuracy of numerical components used in neural networks. The result is
+a specification that should be verified by any implementation for the
+considered hypotheses. Here is a list of acceptable hypotheses :
+
+* floating-point computation conformed to the IEEE-754 standard
+* fixed-point computation
+* concrete or symbolic range for the input values
+* symbolic constraints over some computations
+
+There exists many possible specifications for the accuracy. For instance, the specification
+for the matrix multiplication will be different if one matrix is diagonal or if both matrices
+are dense with a same order of magnitude for every coefficients.
+
+We will favour short formulas, even if they may seem approximate.
+
+#### Numerical Accuracy
+
+This section provides a tight and verifiable specification of the numerical error
+on the operator's results. The numerical error is the difference between the
+current implementation on data with error resulting from previous approximated
+computations and an ideal algorithm operating on data also coming from previous ideal
+computations.
+
+$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x})$$
+
+This framework decomposes the error into two parts:
+
+* the first, the propagated error, depends on the numerical
+ error and the numerical values of the inputs - in particular, it is independent of the
+ implementation and the storage format
+* the second part, the introduced error, depends on the
+ concrete value of the inputs and the implementation with its storage format.
+
+$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x}) = \textcolor{blue}{(op_{\textit{ideal}}(\overrightarrow{x+e}) - op_{\textit{ideal}}(\overrightarrow{x}))} + \textcolor{red}{(op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x+e}))}$$
+
+The error associated with the result of the operator corresponds to the sum of the propagated
+errors and the introduced error and this new error is then propagated by the next operator.
+
+The provided specification results from an over-approximated semantics (ex: IEEE-754) of the
+numerical error of native computer operations approximating real number
+operations. In order to preserve the readability of the formulas, the general specification introduces additional (conservative) simplifications compared to the original specifications.
+However, this general specification may be too over-approximated for some specific inputs (ex tensor representing diagonal matrices). In this case, more precise specific specifications are provided alongside the general specification.
+
+The error specification comes with unit verification scenarios to verify the implementation's conformity. In the absence of value ranges for the inputs, the unit verification scenarios operate on symbolic values and errors to propagate correct formulas throughout the scenario and thus provide a proof for the assertions. In particular, the C implementation generated from the Why3 formal specification must be verified using these scenarios, for example by using symbolic instrumentation libraries.
+
+###### Error Propagation
+
+This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of an operator.
+
+**It is only for information**, since the properties does not depend on the implementation.
+The formula aims to explain how an input error is amplified by the operator just from its
+functional description.
+
+From the theoretical point of view, let us consider a function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^n$ and
+an existing error for each argument $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$.
+
+The **propagated error** of the $f$ function as ideal operator is
+
+$$f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})$$
+
+Hence if $f$ is derivable two times, the formula
+
+$$\sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+ \textit{ where } X_{\textit{err}} = \max(x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{err}}) \leq 1$$
+
+is a correct propagated error with the natural following definition for $\mathcal{O}(X^2_{\textit{err}})$:
+
+$$\mathcal{O}(X^2_{\textit{err}}) = \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}$$
+
+If there are no error on the arguments, the propagated error is $0$.
+Otherwise, we preconise a first order expression like:
+
+The absolute value of the propagated error is less or equal than
+
+$$\sum_{0 \leq i < n} \left| \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}}) \right| \times |x^i_{\textit{err}}|$$
+
+**Example:** multiplication $f(x, y) = x\times y$ and division $g(x, y) = x / y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$
+
+For the multiplication, the **propagated error** $PE(f)$ is
+
+$$\begin{array}{rcl}
+PE(f) & = & (x_{\textit{val}} + x_{\textit{err}})\times (y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} \\
+ & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + x_{\textit{err}}\times y_{\textit{err}}
+\end{array}$$
+
+that is simplified into
+
+$$\begin{array}{rcl}
+PE(f) & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+\end{array}$$
+
+with $\mathcal{O}(X^2_{\textit{err}}) = x_{\textit{err}}\times y_{\textit{err}}$.
+
+Hence, the accuracy definition of the operator will just indicate that
+
+$$\begin{array}{rcl}
+|PE(f)| & \leq & |y_{\textit{val}}| \times |x_{\textit{err}}| + |x_{\textit{val}}|\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})|
+\end{array}$$
+
+The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional, since it is always
+
+$$\begin{array}{rcl}
+ \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\
+ & = & (x_{\textit{val}} + x_{\textit{err}})\times(y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} - (y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}})\\
+ & = & x_{\textit{err}}\times y_{\textit{err}}
+\end{array}$$
+
+For the division, the **propagated error** $PE(g)$ is
+
+$$\begin{array}{rcl}
+PE(g) & = & \frac{x_{\textit{val}} + x_{\textit{err}}}{y_{\textit{val}} + y_{\textit{err}}} - \frac{x_{\textit{val}}}{y_{\textit{val}}} \\
+ & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})}
+\end{array}$$
+
+that is simplified into
+
+$$\begin{array}{rcl}
+PE(g) & = & \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+\end{array}$$
+
+The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional
+
+$$\begin{array}{rcl}
+ \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\
+ & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} - \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}}\\
+ & = & \frac{x_{\textit{val}}\times y^2_{\textit{err}} - y_{\textit{val}}\times x_{\textit{err}}\times y_{\textit{err}}}{y_{\textit{val}}^2(y_{\textit{val}} + y_{\textit{err}})}
+\end{array}$$
+
+Hence, the accuracy definition of the operator will just indicate that
+
+$$\begin{array}{rcl}
+|PE(g)| & \leq & \frac{1}{|y_{\textit{val}}|}\times |x_{\textit{err}}| + \frac{|x_{\textit{val}}|}{y^2_{\textit{val}}}\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})|
+\end{array}$$
+
+**Example:** 2D Matrix multiplication
+
+Tensor operators require a methodology to propose an **over-approximation of the propagated error**,
+since the algorithm combine many atomic operations on $\mathbb{R}$.
+
+1. Naïve Algorithm Description
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+2. Progressive decoration of the algorithm starting from inner loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // OAPE(C[i][j]) = 0
+ C[i][j] += A[i][0]*B[0][j];
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ // |PE(C[i][j])| <= a*be + b*ae
+ C[i][j] += A[i][1]*B[1][j];
+ // |PE(C[i][j])| <= 2*a*be + 2*b*ae
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // |PE(C[i][j]| = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |PE(C[i][j]| <= k*a*be + k*b*ae
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ If the pattern verify a loop induction, it becomes a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // PE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |PE(C[i][j])| <= k*a*be + k*b*ae
+
+ int k = 2;
+ // if |PE(C[i][j])| <= k*a*be + k*b*ae
+ C[i][j] += A[i][k]*B[k][j];
+ ++k;
+ // then |PE(C[i][j])| <= k*a*be + k*b*ae // same formula than the induction hypotheses
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ Then the loop invariant enables to establish the post-condition when exiting the loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // PE(C[i][j]) = 0
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |OAPE(C[i][j]| <= k*a*be + k*b*ae
+ // |PE(C[i][j])| <= p*a*be + p*b*ae
+ ```
+
+3. Final specification
+
+ At the end, we can specify that
+
+ * If A is a matrix of dimension $n \times p$, B a matrix of dimension $p \times q$,
+ * if the absolute value of every coefficient of A is bound by $a$ with an error whose absolute value is bound by $ae$,
+ * if the absolute value of every coefficient of B is bound by $b$ with an error whose absolute value is bound by $be$,
+ * then the propagated error of every coefficient of C is bound by $p\times a\times be + p\times b \times ae$
+
+###### Error Introduction
+
+This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$,
+the introduced error, where $Y$ is the tensor result of an operator.
+The objective is to provide a specification that any implementation should respect. Hence
+
+$$Y_{\textit{err}}^{\textit{intro}} = op_{\textit{impl}}(\overrightarrow{x}) - op_{\textit{ideal}}(\overrightarrow{x})$$
+
+From the theoretical point of view, the introduced error depends on the storage format of the result of any intermediate computation.
+It is always defined as the difference between the implementation result and the ideal result, for which we additionaly
+substract the propagated error of the previous section.
+
+The introduced error is an over-approximation of difference between the implementation result and the ideal result
+when there is no error on the input.
+
+For IEEE-754 format, let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon)
+for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. For every floating-point operator
+whose result $r$ is a normal floating-point number, the introduced error is less or equal than
+$|r| \times \frac{\varepsilon}{2} = |r| \times \textit{\bf u}$ in the standard mode
+round to nearest even. Moreover if $r \in [a, b]$ with $a$ and $b$ normal numbers (the interval
+may contain denormal numbers), the introduced error is less or equal than
+$\max(|a|, |b|) \times \textit{\bf u}$ in the standard mode round to nearest even.
+There exists more accurate formulas, but such formulas usually take too much
+details into account.
+
+**Example:** multiplication $f(x, y) = x * y$ and division $g(x, y) = x / y$
+
+For the multiplication, the **introduced error** $IE(f)$ should be less or equal than
+
+$$\begin{array}{rcl}
+|IE(f)| & \leq & |x|\times |y|\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$|x|\times |y|$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(f)| & \leq & \max(|a|, |b|)\times \max(|c|, |d|)\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number.
+
+$$\begin{array}{rcl}
+|IE(f)| & < & \max(|a|, |b|)\times \max(|c|, |d|)\times\varepsilon
+\end{array}$$
+
+for any floating-point implementation with other rounding mode, provided
+$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number.
+
+
+$$\begin{array}{rcl}
+|IE(f)| & < & 1
+\end{array}$$
+
+for any fixpoint implementation.
+
+For the division, the **introduced error** $IE(g)$ should be less or equal than
+
+$$\begin{array}{rcl}
+|IE(g)| & \leq & \frac{|x|}{|y|}\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$\frac{|x|}{|y|}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & \leq & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & < & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\varepsilon
+\end{array}$$
+
+for any floating-point implementation with other rounding mode, provided
+$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & < & 1
+\end{array}$$
+
+for any fixpoint implementation.
+
+**Example:** 2D Matrix multiplication
+
+Tensor operators require a methodology to find an **over-approximation of the introduced error**,
+since their algorithms combine many atomic operations.
+
+1. Naïve Algorithm Description
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+2. Progressive decoration of the algorithm starting from inner loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ C[i][j] += A[i][0]*B[0][j];
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ // |IE(C[i][j])| <= a*b*u
+ // |C[i][j]| <= a*b*(1+u)
+ C[i][j] += A[i][1]*B[1][j];
+ // |IE(C[i][j])| <= a*b*u + a*b*u + 2*a*b*(1+u)*u = a*b*u*(4+2u)
+ // |C[i][j])| <= (a*b*(1+u) + a*b*(1+u))*(1+u) = 2*a*b*(1+u)²
+ C[i][j] += A[i][2]*B[2][j];
+ // |IE(C[i][j])| <= a*b*u*(4+2u) + a*b*u + (2*a*b*(1+u)² + a*b*(1+u))*u = a*b*u*(8+7u+2u²)
+ // |C[i][j]| <= (2*a*b*(1+u)² + a*b*(1+u))*(1+u) = (3+2*u)*a*b*(1+u)²
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ If the pattern verify a loop induction, it becomes a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+
+ int k = 2;
+ // if |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // and if |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ // if OAPE(C[i][j]) <= k*a*be + k*b*ae
+ C[i][j] += A[i][k]*B[k][j];
+ ++k;
+ // then |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses
+ // and then |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ Then the loop invariant enables to establish the post-condition when exiting the loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ // |IE(C[i][j])| <= (((1+u)^(p+1)-1)/u*(1+u)² - p-1)*a*b // same formula than the induction hypotheses
+ // |C[i][j])| <= ((1+u)^(p+1)-1)/u*a*b*(1+u)²
+ ```
+
+Hence, for the matrix multiplication of A, matrix of dimension $n \times p$ with B, matrix of dimension $p \times q$, the **introduced error** should be less or equal than
+
+$$\begin{array}{rcl}
+ |IE(C[i][j])| & \leq & \left(\frac{(1+u)^{p+1}-1}{u}\times(1+u)^2 - p-1\right)\times a \times b\\
+ & \leq & \left(\frac{(1+u)^{p+1}-1-(p+1)\times u }{u^2}\times(1+u)^2 - (p+1)\times(2+u)\right)\times a \times b \times u\\
+\end{array}$$
+
+where the absolute value of every coefficient of A is bound by $a$ and the absolute value of every coefficient of B is bound by $b$.
+
+###### Unit Verification
+
+This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields:
+
+* The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations.
+* The `float` field is a symbolic abstract domain for the computed value.
+* The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`.
+* The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`.
+
+# Formal specification guidelines
+
+*To be completed.*
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/assets/domains.png b/safety-related-profile/sonnx/ops/docs/guidelines/assets/domains.png
new file mode 100644
index 00000000..33123121
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/assets/domains.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/assets/mcdc.png b/safety-related-profile/sonnx/ops/docs/guidelines/assets/mcdc.png
new file mode 100644
index 00000000..1ad0429b
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/assets/mcdc.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/clip.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/clip.mlw
new file mode 100644
index 00000000..f9df4fd7
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/clip.mlw
@@ -0,0 +1,27 @@
+module OPClip
+ use tensor.Tensor
+ use int.Int
+ use list.List
+ use tensor.Range
+ use real.Real
+ use real.MinMax
+
+ predicate is_scalar_tensor (t : tensor real) =
+ t.dims = Nil /\ t.data = (fun _ -> t.background)
+
+ function dclip (x l m : tensor real): data real
+ = fun ks ->
+ if valid ks x.dims then
+ min m.background (max (x.data ks) l.background)
+ else x.background
+
+ let ghost function opclip (x l m : tensor real): tensor real
+ requires { is_scalar_tensor l }
+ requires { is_scalar_tensor m }
+ ensures { result ~= x }
+ ensures { result.data = dclip x l m }
+ (*proof*)
+ = { dims = x.dims; data = dclip x l m; background = x.background }
+ (*qed*)
+
+end
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/conv.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/conv.mlw
new file mode 100644
index 00000000..519ea716
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/conv.mlw
@@ -0,0 +1,761 @@
+module OPConv2d
+
+ use tensor.Tensor
+ use real.Real
+ use int.Int
+ use std.Int
+ use list.List
+ use int.EuclideanDivision
+ use tensor.Range
+ use list.Length
+
+ function convDims (x w :tensor real) (str_h str_w :int)
+ (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : list int
+ =
+ let dY_0 = get_dim x.dims 0 in
+ let dY_1 = get_dim w.dims 0 in
+
+ let alpha = get_dim x.dims 2 + pad_top + pad_bottom in
+ let theta = dir_h * (get_dim w.dims 2 - 1) + 1 in
+ let dY_2 = div (alpha - theta) str_h + 1 in
+
+ let beta = get_dim x.dims 3 + pad_left + pad_right in
+ let gamma = dir_w * (get_dim w.dims 3 - 1) + 1 in
+ let dY_3 = div (beta - gamma) str_w + 1 in
+
+ Cons dY_0 (Cons dY_1 (Cons dY_2 (Cons dY_3 Nil)))
+
+
+ function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real
+ =
+ let b = get_dim x_p_coords 0 in
+ let c = get_dim x_p_coords 1 in
+ let n = (get_dim x_p_coords 2) - pad_top in
+ let m = (get_dim x_p_coords 3) - pad_left in
+ let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in
+ if valid x_coords x.dims then
+ x.data x_coords
+ else
+ 0.0
+
+ let rec ghost function w_cools_calculate (x w : tensor real) (c: int) (i:int) (j:int) (jj:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real
+ variant { j - jj }
+ =
+ if jj >= j then
+ 0.0
+ else
+ let x_h = y_h * str_h + i * dil_h in
+ let x_w = y_w * str_w + jj * dil_w in
+ let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in
+ let w_val = w.data (Cons m (Cons c (Cons i (Cons jj Nil)))) in
+ Real.(x_val * w_val + w_cools_calculate x w c i j Int.(jj + 1) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left)
+
+ let rec lemma w_cools_split (x w : tensor real) (c i j jj k n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int)
+ requires { jj <= k < j }
+ variant { k - jj }
+ ensures {
+ let x_h = y_h * str_h + i * dil_h in
+ let x_w = y_w * str_w + k * dil_w in
+ let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in
+ let w_val = w.data (Cons m (Cons c (Cons i (Cons k Nil)))) in
+ w_cools_calculate x w c i Int.(k+1) jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left =
+ Real.(x_val * w_val + w_cools_calculate x w c i k jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left)
+ }
+ =
+ if jj < k then w_cools_split x w c i j Int.(jj+1) k n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else ()
+
+ let rec ghost function w_lines_calculate (x w : tensor real) (c: int) (i:int) (ii:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real
+ variant { i - ii }
+ =
+ if ii >= i then
+ 0.0
+ else
+ w_cools_calculate x w c ii j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c i Int.(ii + 1) j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left
+
+ let rec lemma w_lines_split (x w : tensor real) (c i ii k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int)
+ requires { ii <= k < i }
+ variant { k - ii }
+ ensures {
+ w_lines_calculate x w c Int.(k+1) ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left =
+ Real.(w_cools_calculate x w c k j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c k ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left)
+ }
+ =
+ if ii < k then w_lines_split x w c i Int.(ii+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else ()
+
+ let rec ghost function w_channels_calculate (x w :tensor real) (c:int )(cc:int) (i:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real
+ variant { c - cc }
+ =
+ if cc >= c then
+ 0.0
+ else
+ w_lines_calculate x w cc i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w c Int.(cc + 1) i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left
+
+ let rec lemma w_channels_split (x w : tensor real) (c i cc k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int)
+ requires { cc <= k < c }
+ variant { k - cc }
+ ensures {
+ w_channels_calculate x w Int.(k+1) cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left =
+ Real.(w_lines_calculate x w k i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w k cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left)
+ }
+ =
+ if cc < k then w_channels_split x w c i Int.(cc+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else ()
+
+ function dconv (x w : tensor real) (y_dims : list int) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : data real
+ =
+ let c = get_dim w.dims 1 in
+ let i = get_dim w.dims 2 in
+ let j = get_dim w.dims 3 in
+ fun ks ->
+ if valid ks y_dims then
+ let n = get_dim ks 0 in
+ let m = get_dim ks 1 in
+ let y_h = get_dim ks 2 in
+ let y_w = get_dim ks 3 in
+ w_channels_calculate x w c 0 i j n m y_h y_w str_h str_w dir_h dir_w pad_top pad_left
+ else
+ x.background
+
+ let rec lemma get_dim_positive (ds : list int)
+ requires { positive ds }
+ ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i }
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons d ds -> get_dim_positive ds end
+
+ let ghost function conv (x w : tensor real) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : tensor real
+ (*Strides [C1]*)
+ requires { str_h > 0 /\ str_w > 0 }
+ (*Pads [C1], [C2] *)
+ requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 }
+ (*Dilations [C1], [C2]*)
+ requires { dir_h > 0 /\ dir_w > 0 }
+ (*X [C1],[C4]*)
+ requires { length x.dims = 4 }
+ (*X [C2] *)
+ requires { get_dim x.dims 1 = get_dim w.dims 1 }
+ (* W [C4]*)
+ requires { length w.dims = 4 }
+ (* Strides [C2] *)
+ requires { let y_shape = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in
+ get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0
+ }
+ =
+ let y_dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in
+ { dims = y_dims;
+ data = dconv x w y_dims str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right;
+ background = x.background;
+ }
+end
+
+
+module COPConv2d
+
+ use OPConv2d
+ use tensor.Tensor
+ use tensor.Range
+ use real.Real
+ use int.Int
+ use list.List
+ use layout.CFlat
+
+ use libtensor.CTensor
+ use libvector.CIndex
+ use std.Clib
+ use mach.int.Int32
+ use std.Cfloat
+ use std.Int
+ use list.Length
+ use layout.CFlat
+ use tensor.Range
+
+
+ let rec lemma ivector_get_dim_value_at (coords: iarray) (n: int) (i: int)
+ requires { n = 4 }
+ requires { 0 <= i < n }
+ requires { valid_range coords 0 n }
+ ensures { get_dim (ivector coords n) i = value_at coords i }
+ variant { i }
+ =
+ if i > 0 then
+ ivector_get_dim_value_at coords n (i - 1)
+
+
+
+ let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) =
+ requires { valid_tensor x }
+ requires { x.t_rank = 4 }
+ requires { valid_range x_p_coords 0 4 }
+ requires { writable x_p_coords }
+ requires { in_bounds (value_at x_p_coords 2 - pad_top) }
+ requires { in_bounds (value_at x_p_coords 3 - pad_left) }
+
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+
+ let ref flag = False in
+ let b = x_p_coords[0] in
+ let c = x_p_coords[1] in
+ let n = (x_p_coords[2]) - pad_top in
+ let m = (x_p_coords[3]) - pad_left in
+ let x_coords_array = malloc (to_uint32 4) in
+ if is_not_null x_coords_array then begin
+ flag <- True;
+ x_coords_array[0] <- b;
+ x_coords_array[1] <- c;
+ x_coords_array[2] <- n;
+ x_coords_array[3] <- m;
+
+ assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ;
+
+ let x_coords = coffset x_coords_array x.t_dims x.t_rank in
+ if x_coords >= 0 then begin
+ (x.t_data[x_coords], flag)
+ end
+ else begin
+ ((f32 0.0), flag)
+ end
+ end
+ else begin
+ flag <- False;
+ ((f32 0.0), flag)
+ end
+
+
+ let conv2d (x w r : ctensor) (str_h str_w : int32) (dir_h dir_w : int32)
+ (pad_top pad_left pad_bottom pad_right : int32) : ctensor
+
+ requires { valid_tensor x}
+ requires { valid_tensor w}
+ requires { valid_tensor r}
+ requires { x.t_rank = w.t_rank = r.t_rank = 4 }
+ requires { ivector r.t_dims r.t_rank = convDims (tensor x) (tensor w) str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right }
+ ensures { (tensor r).dims = convDims (tensor x) (tensor w) str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right }
+ =
+ r
+
+
+ let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int)
+ requires { p <= q }
+ requires { valid_range ks p q }
+ requires { valid_range ds p q }
+ requires { pdim ds p q }
+ requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i }
+ ensures { Range.valid (islice ks p q) (islice ds p q) }
+ variant { q - p }
+ = if p < q then
+ begin
+ assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) };
+ assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) };
+ valid_bounds_2 ks ds (p+1) q
+ end
+
+ let w_cools_calculate (x w: ctensor) (c: int32) (i:int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool)
+ requires { valid_tensor x }
+ requires { valid_tensor w }
+ requires { x.t_rank = w.t_rank = 4 }
+ requires { 0 <= m < value_at w.t_dims 0 }
+ requires { 0 <= c < value_at w.t_dims 1 }
+ requires { 0 <= i < value_at w.t_dims 2 }
+
+ (*Try to remove this block like Loic do it*)
+ requires { in_bounds (i * dil_h) }
+ requires { in_bounds (y_h * str_h) }
+ requires { in_bounds (y_h * str_h + i * dil_h) }
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) }
+ requires { in_bounds (y_w * str_w) }
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) }
+ requires { let x_h = y_h * str_h + i * dil_h in
+ in_bounds (x_h - pad_top)
+ }
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 ->
+ let x_w = y_w * str_w + jj * dil_w in
+ in_bounds (x_w - pad_left)
+ }
+
+ ensures { let (value, flag) = result in
+ flag -> w_cools_calculate (tensor x) (tensor w) c i (value_at w.t_dims 3) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value
+ }
+ =
+ let ref flag = False in
+ let ref sum = (f32 0.0) in
+ let x_coords_array = malloc (to_uint32 4) in
+ let w_coords_array = malloc (to_uint32 4) in
+ let cols = w.t_dims[3] in
+ if is_not_null x_coords_array && is_not_null w_coords_array then begin
+ flag <- True;
+ for jj = 0 to cols - 1 do
+ invariant { sum = w_cools_calculate (tensor x) (tensor w) c i jj 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left }
+ let x_h = y_h * str_h + i * dil_h in
+ let x_w = y_w * str_w + jj * dil_w in
+ x_coords_array[0] <- n;
+ x_coords_array[1] <- c;
+ x_coords_array[2] <- x_h;
+ x_coords_array[3] <- x_w;
+ assert { ivector x_coords_array 4 = Cons (to_int n) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) } ;
+ let (x_val, aux_flag) = coords_from_X_p x x_coords_array pad_top pad_left in
+ assert { aux_flag -> x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ;
+ if aux_flag then begin
+ w_coords_array[0] <- m;
+ w_coords_array[1] <- c;
+ w_coords_array[2] <- i;
+ w_coords_array[3] <- jj;
+
+ ghost
+ begin
+ valid_bounds_2 w_coords_array w.t_dims 0 4;
+ assert { Range.valid (ivector w_coords_array w.t_rank) (ivector w.t_dims w.t_rank) };
+ w_cools_split (tensor x) (tensor w) (to_int c) (to_int i) (to_int cols) 0 (to_int jj) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ end;
+
+
+ let w_coords = coffset w_coords_array w.t_dims w.t_rank in
+ let w_val = w.t_data[w_coords] in
+ assert {Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil))) = ivector w_coords_array w.t_rank } ;
+ assert { (tensor w).Tensor.data (Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil)))) = w_val } ;
+ assert { x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ;
+ sum <- sum .+ (x_val .* w_val);
+ assert { sum = w_cools_calculate (tensor x) (tensor w) c i Int.(jj+1) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left }
+ end
+ else begin
+ flag <- False;
+ return (f32 0.0, flag)
+ end
+ done;
+ (sum, flag)
+ end
+ else begin
+ flag <- False;
+ (f32 0.0, flag)
+ end
+
+ let w_lines_calculate (x w : ctensor) (c: int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool)
+ requires { valid_tensor x }
+ requires { valid_tensor w }
+ requires { x.t_rank = w.t_rank = 4 }
+ requires { 0 <= m < value_at w.t_dims 0 }
+ requires { 0 <= c < value_at w.t_dims 1 }
+
+ (*Try to remove this block like Loic do it*)
+ requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) }
+ requires { in_bounds (y_h * str_h) }
+ requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) }
+
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) }
+ requires { in_bounds (y_w * str_w) }
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) }
+
+ requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 ->
+ let x_h = y_h * str_h + ii * dil_h in
+ in_bounds (x_h - pad_top)
+ }
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 ->
+ let x_w = y_w * str_w + jj * dil_w in
+ in_bounds (x_w - pad_left)
+ }
+ ensures { let (value, flag) = result in
+ flag -> w_lines_calculate (tensor x) (tensor w) c (value_at w.t_dims 2) 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value
+ }
+ =
+ let i = w.t_dims[2] in
+ let ref flag = False in
+ let ref sum = (f32 0.0) in
+ for ii = 0 to i - 1 do
+ invariant { sum = w_lines_calculate (tensor x) (tensor w) c ii 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left }
+ let (value, aux_flag) = w_cools_calculate x w c ii n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in
+ if aux_flag then begin
+ flag <- True;
+ ghost
+ begin
+ w_lines_split (tensor x) (tensor w) (to_int c) (to_int i) 0 (to_int ii) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ end;
+ sum <- sum .+ value;
+ end
+ else begin
+ flag <- False;
+ return (f32 0.0, flag)
+ end
+ done;
+ (sum, flag)
+
+
+let w_channels_calculate (x w : ctensor) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool)
+ requires { valid_tensor x }
+ requires { valid_tensor w }
+ requires { x.t_rank = w.t_rank = 4 }
+ requires { 0 <= m < value_at w.t_dims 0 }
+
+ (*Try to remove this block like Loic do it*)
+ requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) }
+ requires { in_bounds (y_h * str_h) }
+ requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) }
+
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) }
+ requires { in_bounds (y_w * str_w) }
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) }
+
+ requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 ->
+ let x_h = y_h * str_h + ii * dil_h in
+ in_bounds (x_h - pad_top)
+ }
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 ->
+ let x_w = y_w * str_w + jj * dil_w in
+ in_bounds (x_w - pad_left)
+ }
+ ensures { let (value, flag) = result in
+ flag -> w_channels_calculate (tensor x) (tensor w) (to_int(value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) = value
+ }
+ =
+ let c = w.t_dims[1] in
+ let ref flag = False in
+ let ref sum = (f32 0.0) in
+ for cc = 0 to c - 1 do
+ invariant { sum = w_channels_calculate (tensor x) (tensor w) cc 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) }
+ let (value, aux_flag) = w_lines_calculate x w cc n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in
+ if aux_flag then begin
+ flag <- True;
+ (*Lemma is not needed on this function*)
+ (*
+ ghost
+ begin
+ w_channels_split (tensor x) (tensor w) (to_int c) (to_int(value_at w.t_dims 2)) 0 (to_int cc) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ end;
+ *)
+ sum <- sum .+ value;
+ end
+ else begin
+ flag <- False;
+ return (f32 0.0, flag)
+ end
+ done;
+ (sum, flag)
+
+
+let c_conv (x w r : ctensor) (str_h str_w : int32) (dil_h dil_w : int32)
+ (pad_top pad_left pad_bottom pad_right : int32) : bool
+
+ requires { valid_tensor x}
+ requires { valid_tensor w}
+ requires { valid_tensor r}
+ requires { x.t_rank = w.t_rank = r.t_rank = 4 }
+ requires { ivector r.t_dims r.t_rank = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right }
+ (*Strides [C1]*)
+ requires { str_h > 0 /\ str_w > 0 }
+ (*Pads [C1], [C2] *)
+ requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 }
+ (*Dilations [C1], [C2]*)
+ requires { dil_h > 0 /\ dil_w > 0 }
+ (*X [C1],[C4]*)
+ requires { length (tensor x).dims = 4 }
+ (*X [C2] *)
+ requires { get_dim (tensor x).dims 1 = get_dim (tensor w).dims 1 }
+ (* W [C4]*)
+ requires { length (tensor w).dims = 4 }
+ (* Strides [C2] *)
+ requires { let y_shape = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right in
+ get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0
+ }
+
+ (*Try to remove this block like Loic do it*)
+ requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) }
+ requires { forall y_h: int. 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h) }
+ requires { forall ii y_h:int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) }
+
+ requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) }
+ requires { forall y_w: int. 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w) }
+ requires { forall jj y_w:int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) }
+
+ requires { forall ii y_h: int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 ->
+ let x_h = y_h * str_h + ii * dil_h in
+ in_bounds (x_h - pad_top)
+ }
+ requires { forall jj y_w: int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 ->
+ let x_w = y_w * str_w + jj * dil_w in
+ in_bounds (x_w - pad_left)
+ }
+
+ requires { forall mm:int. 0 <= mm < value_at r.t_dims 1 -> mm < value_at w.t_dims 0 }
+
+ ensures { result -> tensor r = conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) }
+
+ =
+ let n = r.t_dims[0] in
+ let m = r.t_dims[1] in
+ let y_h = r.t_dims[2] in
+ let y_w = r.t_dims[3] in
+ let r_coords_array = malloc (to_uint32 4) in
+ if is_not_null r_coords_array then begin
+ for nn = 0 to n - 1 do
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ r_coords_array[0] <- nn;
+ for mm = 0 to m - 1 do
+ invariant { value_at r_coords_array 0 = nn }
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ invariant {
+ forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ }
+ r_coords_array[1] <- mm;
+ for y_hh = 0 to y_h - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm }
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ invariant {
+ forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+
+ }
+ r_coords_array[2] <- y_hh;
+ for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ invariant {
+ forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+
+ assert { ivector r_coords_array r.t_rank = Cons ( nn) (Cons ( mm) (Cons ( y_hh) (Cons ( y_ww) Nil))) } ;
+ assert { value = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left } ;
+ assert { let r_coords = CFlat.offset (ivector r_coords_array r.t_rank) (ivector r.t_dims r.t_rank) in
+ value_at r.t_data r_coords = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left };
+
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ };
+
+ assert {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ };
+
+
+ assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ )
+ };
+ assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ )
+ };
+
+ assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ )
+ };
+
+ assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ )
+ };
+
+
+ assert { forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ )
+ )
+ };
+
+ assert { forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ )
+ )
+ };
+
+ assert { forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ )
+ )
+ };
+
+ assert {
+ forall nn_. 0 <= nn_ < nn ->
+ (forall mm_. 0 <= mm_ < m ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ )
+ )
+ )
+ };
+
+ assert {
+ forall nn_. 0 <= nn_ < nn ->
+ (forall mm_. 0 <= mm_ < m ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ )
+ )
+ )
+ };
+
+ assert { forall nn_. 0 <= nn_ < nn ->
+ (forall mm_. 0 <= mm_ < m ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ )
+ )
+ )}
+ end
+ else
+ return false
+ done;
+ done;
+ done;
+ done;
+ assert { tensor r == conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) } ;
+ true
+ end
+ else
+ false
+
+end
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/dot_product.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/dot_product.mlw
new file mode 100644
index 00000000..162e46e5
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/dot_product.mlw
@@ -0,0 +1,101 @@
+module OPMatmul
+
+ use tensor.Tensor
+ use std.Int
+ use list.List
+ use tensor.Range
+ use list.Length
+ use int.Int
+ use real.Real
+
+
+ let rec ghost function dot_product_rec (a b: tensor real) (row col i iter: int) : real
+ variant { Int.(iter - i) }
+ =
+ if Int.(>=) i iter then
+ 0.0
+ else
+ let a_val = a.data (Cons row (Cons i Nil)) in
+ let b_val = b.data (Cons i (Cons col Nil)) in
+ Real.(a_val * b_val + dot_product_rec a b row col Int.(i + 1) iter)
+
+end
+
+
+module COPMatmul
+
+ use OPMatmul
+ use tensor.Tensor
+ use tensor.Range
+ use real.Real
+ use int.Int
+ use list.List
+ use layout.CFlat
+
+ use libtensor.CTensor
+ use libvector.CIndex
+ use std.Clib
+ use mach.int.Int32
+ use std.Cfloat
+ use std.Int
+ use list.Length
+
+
+ let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int)
+ requires { p <= q }
+ requires { valid_range ks p q }
+ requires { valid_range ds p q }
+ requires { pdim ds p q }
+ requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i }
+ ensures { Range.valid (islice ks p q) (islice ds p q) }
+ variant { q - p }
+ = if p < q then
+ begin
+ assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) };
+ assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) };
+ valid_bounds_2 ks ds (p+1) q
+ end
+
+ let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) =
+ requires { valid_tensor a }
+ requires { valid_tensor b }
+ requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter }
+ requires { a.t_rank = b.t_rank = 2 }
+ requires { iter >= 0 }
+ requires { row >= 0 /\ row < value_at a.t_dims 0 }
+ requires { col >= 0 /\ col < value_at b.t_dims 1 }
+ ensures { let (value, flag) = result in
+ flag -> value = dot_product_rec (tensor a) (tensor b) row col 0 iter
+ }
+ let ref sum = (f32 0.0) in
+ let ref flag = False in
+ let a_coords_array = malloc (to_uint32 2) in
+ let b_coords_array = malloc (to_uint32 2) in
+ if is_not_null a_coords_array && is_not_null b_coords_array then begin
+ flag <- True;
+ for i = 0 to iter - 1 do
+ invariant { sum = dot_product_rec (tensor a) (tensor b) row col 0 i }
+ set_ofs a_coords_array 0 row;
+ set_ofs a_coords_array 1 i;
+ set_ofs b_coords_array 0 i;
+ set_ofs b_coords_array 1 col;
+
+ let a_coords = coffset a_coords_array a.t_dims a.t_rank in
+ let a_val = a.t_data[a_coords] in
+ let b_coords = coffset b_coords_array b.t_dims b.t_rank in
+ let b_val = b.t_data[b_coords] in
+
+ assert {Cons ((to_int row)) (Cons (i) Nil) = ivector a_coords_array a.t_rank };
+ assert { (tensor a).Tensor.data (Cons ((to_int row)) (Cons (i) Nil)) = value_at a.t_data a_coords };
+
+ assert {Cons (i) (Cons ((to_int col)) Nil) = ivector b_coords_array b.t_rank };
+ assert { (tensor b).Tensor.data (Cons (i) (Cons ((to_int col)) Nil)) = value_at b.t_data b_coords };
+
+ sum <- sum .+ (a_val .* b_val);
+ done;
+ (sum, flag)
+ end
+ else
+ (sum, flag)
+
+end
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/flatten.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/flatten.mlw
new file mode 100644
index 00000000..4f8482a7
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/flatten.mlw
@@ -0,0 +1,147 @@
+module OPFlatten
+
+ use tensor.Tensor
+ use tensor.Range
+ use layout.CFlat
+ use int.Int
+ use real.Real
+ use std.Int
+ use list.List
+ use list.Length
+
+
+ let rec lemma get_dim_positive (ds : list int)
+ requires { positive ds }
+ ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i }
+ variant { ds }
+ = match ds with
+ | Nil -> ()
+ | Cons d ds -> get_dim_positive ds end
+
+
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ (*Need this requires because of get_dim requires*)
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ variant { axis - i }
+ =
+ if Int.(i >= axis) then
+ 1
+ else
+ get_dim x.dims i * calculate_dY0 x axis Int.(i + 1)
+
+
+ let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int)
+ requires { positive x.dims }
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ ensures { 0 < calculate_dY0 x axis i }
+ variant { axis - i }
+ =
+ if i >= axis then
+ ()
+ else
+ let d = get_dim x.dims i in
+ calculate_dY0_positive x axis (i + 1)
+
+ let rec ghost function calculate_dY1 (x : tensor real) (axis : int) (i : int) : int
+ requires { 0 <= axis <= length x.dims }
+ requires { axis <= i <= length x.dims }
+ variant { length x.dims - i }
+ =
+ if Int.(i >= length x.dims) then
+ 1
+ else
+ get_dim x.dims i * calculate_dY1 x axis Int.(i + 1)
+
+ let rec lemma calculate_dY1_positive (x: tensor real) (axis: int) (i: int)
+ requires { positive x.dims }
+ requires { 0 <= axis <= length x.dims }
+ requires { axis <= i <= length x.dims }
+ ensures { 0 < calculate_dY1 x axis i }
+ variant { length x.dims - i }
+ =
+ if i >= length x.dims then
+ ()
+ else
+ let d = get_dim x.dims i in
+ assert { 0 <= i < length x.dims };
+ calculate_dY1_positive x axis (i + 1)
+
+ function calculate_dims (x : tensor real) (axis : int) : list int
+ =
+ let dY0 = calculate_dY0 x axis 0 in
+ let dY1 = calculate_dY1 x axis axis in
+ Cons dY0 (Cons dY1 Nil)
+
+
+ function dflatten (x : tensor real) (y_shape: list int) : data real
+ =
+ fun ks ->
+ if valid ks y_shape then
+ let y_coords_flat = offset ks y_shape in
+ let x_coords = index y_coords_flat x.dims in
+ x.data x_coords
+ else
+ x.background
+
+ function normalize_axis (axis : int) (x_rank : int) : int
+ =
+ if axis < 0 then
+ axis + x_rank
+ else
+ axis
+
+ let ghost function flatten (x : tensor real) (axis : int) : tensor real
+ requires { -length x.dims <= axis <= length x.dims }
+
+ ensures { let axis_normalized = normalize_axis axis (length x.dims) in
+ result.dims = calculate_dims x axis_normalized }
+
+ ensures { result.background = x.background }
+
+ ensures { let axis_normalized = normalize_axis axis (length x.dims) in
+ result.data = dflatten x (calculate_dims x axis_normalized) }
+ =
+ let axis_normalized = normalize_axis axis (length x.dims) in
+ let y_shape = calculate_dims x axis_normalized in
+ { dims = y_shape ; data = dflatten x y_shape ; background = x.background }
+
+end
+
+
+module COPFlatten
+
+ use OPFlatten
+ use tensor.Tensor
+ use list.List
+ use list.Length
+ use int.Int
+ use libtensor.CTensor
+ use libvector.CIndex
+ use std.Clib
+ use mach.int.Int32
+ use std.Cfloat
+
+
+ let flatten (x r : ctensor) (axis: int32)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m - 1 do
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k }
+ r.t_data[i] <- x.t_data[i]
+ done;
+ assert { tensor r == flatten (tensor x) (to_int axis) }
+
+end
+
+
+
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/matmul.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/matmul.mlw
new file mode 100644
index 00000000..894748d5
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/matmul.mlw
@@ -0,0 +1,43 @@
+module OPMatmul
+ use tensor.Tensor
+ use std.Int
+ use list.List
+ use tensor.Range
+ use list.Length
+ use int.Int
+ use real.Real
+
+ let rec function dot_product (a b: tensor real) (row col i iter: int) : real
+ variant { Int.(iter - i) }
+ =
+ if Int.(>=) i iter then
+ 0.0
+ else
+ let a_val = a.data (Cons row (Cons i Nil)) in
+ let b_val = b.data (Cons i (Cons col Nil)) in
+ Real.(a_val * b_val + dot_product a b row col Int.(i + 1) iter)
+
+ function dmatmul ( a b: tensor real) (a_rows b_cols : int) : data real
+ =
+ let iter = get_dim a.dims 1 in
+ let y_shape = Cons a_rows (Cons b_cols Nil) in
+ fun ks ->
+ if valid ks y_shape then
+ let row = get_dim ks 0 in
+ let col = get_dim ks 1 in
+ dot_product a b row col 0 iter
+ else
+ a.background
+
+ let ghost function matmul ( a b: tensor real) : tensor real
+ requires { a.background = b.background }
+ requires { length a.dims = length b.dims = 2 }
+ requires { get_dim a.dims 1 = get_dim b.dims 0 }
+ ensures { result.background = a.background }
+ ensures { result.dims = Cons (get_dim a.dims 0) (Cons (get_dim b.dims 1) Nil) }
+ ensures { length result.dims = 2 }
+ ensures { result.data = dmatmul a b (get_dim a.dims 0) (get_dim b.dims 1) }
+ =
+ { dims = Cons (get_dim a.dims 0) (Cons (get_dim b.dims 1) Nil) ; data = dmatmul a b (get_dim a.dims 0) (get_dim b.dims 1) ; background = a.background }
+
+end
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation.mlw
new file mode 100644
index 00000000..7a545dc0
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation.mlw
@@ -0,0 +1,32 @@
+module OPSummation
+ use int.Int
+ use real.Real
+ use real.FromInt
+
+ let rec function summation (i : int) (iter : int) : real =
+ variant { Int.(iter - i) }
+ if i < iter then
+ 1.0 + summation (i + 1) iter
+ else
+ 0.0
+end
+
+
+module COPSummation
+ use OPSummation
+ use mach.int.Int32
+ use int.Int
+ use std.Cfloat
+ use real.Real
+
+ let summation (iter: int32) : float =
+ requires { Int.(iter >= 0) }
+ requires { Int.(iter < max_int32) }
+
+ let ref sum = (f32 0.0) in
+ for k = 0 to iter - 1 do
+ invariant { sum = summation 0 k }
+ sum <- sum .+ (f32 1.0);
+ done;
+ sum
+end
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation2.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation2.mlw
new file mode 100644
index 00000000..04fdd9c5
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation2.mlw
@@ -0,0 +1,41 @@
+module OPSummation
+ use int.Int
+ use real.Real
+ use real.FromInt
+
+ let rec function summation (i : int) (iter : int) : real =
+ variant { Int.(iter - i) }
+ if i < iter then
+ 1.0 + summation (i + 1) iter
+ else
+ 0.0
+
+ let rec lemma summation_correctness (i: int) (iter: int)
+ requires { Int.(0 <= i /\ i <= iter) }
+ variant { Int.(iter - i) }
+ ensures { summation i iter = from_int (Int.(iter - i)) }
+ = if i < iter then
+ summation_correctness (i + 1) iter
+ else
+ ()
+end
+
+
+module COPSummation
+ use OPSummation
+ use mach.int.Int32
+ use int.Int
+ use std.Cfloat
+ use real.Real
+
+ let summation (iter: int32) : float =
+ requires { Int.(iter >= 0) }
+ requires { Int.(iter < max_int32) }
+
+ let ref sum = (f32 0.0) in
+ for k = 0 to iter - 1 do
+ invariant { sum = summation 0 k }
+ sum <- sum .+ (f32 1.0);
+ done;
+ sum
+end
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-ejn.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-ejn.md
new file mode 100644
index 00000000..78d69e61
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-ejn.md
@@ -0,0 +1,1695 @@
+# SONNX — Formalization Guidelines
+
+**A structured guide for writing formal specifications of SONNX operators in Why3**
+
+---
+
+## Table of Contents
+
+- [SONNX — Formalization Guidelines](#sonnx--formalization-guidelines)
+ - [Table of Contents](#table-of-contents)
+- [Part 1 — Formalization Styles](#part-1--formalization-styles)
+ - [1.1 Abstract Formalization](#11-abstract-formalization)
+ - [Tensor](#tensor)
+ - [Specification Style](#specification-style)
+ - [Importance of this level](#importance-of-this-level)
+ - [1.2 Concrete Formalization](#12-concrete-formalization)
+ - [Tensor](#tensor-1)
+ - [Specification Style](#specification-style-1)
+ - [Importance of this level](#importance-of-this-level-1)
+ - [1.3 Link between the two levels](#13-link-between-the-two-levels)
+ - [Why is it important to have two levels?](#why-is-it-important-to-have-two-levels)
+- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization)
+ - [2.1. Module Structure](#21-module-structure)
+ - [2.2 Function Declarations](#22-function-declarations)
+ - [2.2.1 Termination and Variants](#221-termination-and-variants)
+ - [2.2.2 Verification Conditions and Requires Clauses](#222-verification-conditions-and-requires-clauses)
+ - [2.2.3 TypeInvariant Lemmas](#223-typeinvariant-lemmas)
+ - [2.2.4 Main Operator Function](#224-main-operator-function)
+ - [2.2.5 Guidelines](#225-guidelines)
+ - [Examples](#examples)
+ - [2.3. Contracts on the Main Function](#23-contracts-on-the-main-function)
+ - [Example](#example)
+ - [2.4. Data Function Pattern](#24-data-function-pattern)
+ - [Anonymous function declaration](#anonymous-function-declaration)
+ - [Recursive dimensions constructs](#recursive-dimensions-constructs)
+ - [Examples](#examples-1)
+ - [2.5 Operator tensor types](#25-operator-tensor-types)
+- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization)
+ - [3.1. Module Structure](#31-module-structure)
+ - [3.2 Auxiliary helper functions](#32-auxiliary-helper-functions)
+ - [3.3 Main operator function](#33-main-operator-function)
+ - [3.3.1 Contracts](#331-contracts)
+ - [3.4 Invariants](#34-invariants)
+ - [Loop Invariants for Proving Data Refinement](#loop-invariants-for-proving-data-refinement)
+ - [The innermost loop](#the-innermost-loop)
+ - [The outer loops](#the-outer-loops)
+- [Part 4 - Tips, Hints and Strategies](#part-4---tips-hints-and-strategies)
+ - [4.1. Scope Resolution](#41-scope-resolution)
+ - [4.2. IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints)
+ - [Lemma / Axiom Instantiation](#lemma--axiom-instantiation)
+ - [Instantiation via Lemma Functions](#instantiation-via-lemma-functions)
+ - [Function Unfolding](#function-unfolding)
+ - [4.3. How to Debug](#43-how-to-debug)
+ - [Reading the Logical Context](#reading-the-logical-context)
+
+
+
+
+
+---
+
+
+
+
+> (eric) General (detail) remark: use **bold** characters only when it is absolutely necessary.
+
+# Part 1 — Formalization Styles
+
+In SONNX, every operator is formalized at **two distinct levels**.
+
+Each level serves a different purpose and follows a different style.
+
+> (eric) I would also add that these two levels are a way to lower gradually the specification down to a level where it can be translated automatically into C code. Without this need, we could stick to the first level only.
+
+Understanding both is essential before writing any specification.
+
+
+## 1.1 Abstract Formalization
+
+At this level the operator must be specified based only on its behavior and **mathematical semantics**.
+
+> I would rather say that the specification shall make "no" reference (or "carry") implementation constraints. Providing an algorithm as a specification could be considered as an implementation solution but it is not. The algorithm is only one possible way to implement the operator and the choice of the actual implementation is completely left to the implementer.
+
+It should the **as close as possible transalation** of the mathematical definition of the operator - **presented in the informal specification**, being therefore easy to read and understand.
+
+> (eric) The formal specification shall be traceable to the informal specification. Traceability must be enforced
+> - first by keeping the formal specification as close to the informal specification (e.g., stick to the mathematical specification given in the informal specification)
+> - second by providing explicit link via `[tags]` to specific parts of the informal specification.
+
+> (eric) "being easy to validate, first and easy to read and understand, second.
+> Indeed, the translation from the informal specification to the formal specification cannot (yet) be performed automatically and will have to be checked manually.
+
+No implementation details should be present at this level, which means that the **tensor' representation** should **mimic the mathematical definition of a tensor** (also similar to the ONNX tensor concept) - a multidimensional array - in which each entry is accessed by its coordinates.
+
+> (eric) I am not sure that "mimic" is the appropriate word => "which means that the specification of tensors shall remain as abstract as possible, focusing on their mathematical properties.
+> Nota that this may be arguable and (possibly) contradictory with the previous requirement about readability. Using arrays to represent tensors is something pretty familiar... Hopefully, wwe use "general indexes" in the informal spec that makes representing a tensor as a function from index to values pretty natural...
+
+### Tensor
+> Tensor**s**?
+
+- **Type**: At this level tensors are said to be **polymorphic**.
+
+> (eroc) Maybe explain what you mean by "polymorphic", i.e., they must be parameterized by the type of the values "contained" in the tensor.
+> (eric) They are not "said to be polymorphic", they **are** specified as polymorphic structures.
+
+ This means that the specifier should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types).
+
+- **Representation**: Tensors are represented as structures containing the following records:
+
+ - `dims` - List of integers representing the shape
+
+ - `data` - A map from coordinates to values
+
+> (eric) In the informal specification, we use "[indexes](../../../spec/informal/common/definitions.md)" rather than "coordinates".
+
+ - `background` - The default value for out-of-bounds coordinates
+
+> (eric) "for indexes out of the bounds defined by the shape"?
+
+- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representatio:
+
+> (eric) "representation" (missing "n")
+
+ 1. **Positive Dimensions**
+
+ All dimensions in the `dims` list of a tensor must be positive integers.
+
+> (eric) You mean "strictly positive integers" (otherwise I don't understand the following remark).
+
+From our point of view this will be updated to support **non-negative integers**, therefore allowing for tensors with null dimensions. [***Work in Progress***]
+> (eric) Maybe: "This constraint may be relaxed in a lated version of the specification to support tensors with null dimensions.
+
+ **Definition:**
+
+ - Predicate `positive`
+
+ ```why3
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+ ```
+ - `invariant { positive dims }`
+
+> (eric) Stupid question : why `0 < d` rather than `d > 0`?
+
+ 2. **Valid values**
+
+ Coordinates are either valid for the tensor shape or the value mapped by those coordinates is the `background` value.
+
+> (eric) In the definition of the background value above, maybe use the term "valid index" since it is used here.
+>
+> (eric) A "valid index" is an index compatible with the dimension of the tensor, i.e. the i-th components of the index is strictly positive and strictly less than the i-th dimension of the tensor:
+
+ **Definition:**
+
+ - Predicate `valid`
+
+ ```why3
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+ ```
+ - `invariant { forall k. valid k dims \/ data k = background }`
+
+
+
+ These invariants are extremely important as they will sometimes define how the operator should be formalized.
+
+ For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant.
+
+ > The previous sentence is a bit strange. Isn't this invariant a part of the specification of the tensor?
+
+### Specification Style
+
+Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output).
+
+Looping constructs are forbidden at this level, and instead, **recursive functions** and **mathematical constructs** should be used to define the behavior of the operator.
+
+> (eric) Again, this is not very compatible with the "principle" of simplicity and traceability stated earlier. May we should say something about "provability" or "ease of proof"?
+
+### Importance of this level
+
+It establishes the logical and mathematical specification of the operator, which serves as the **source of truth** for correctness.
+
+> (eric) why do you separate "logical" from "mathematical"?
+
+It also provides the perfect link between the informal specification and the formalization.
+
+## 1.2 Concrete Formalization
+
+The concrete level describes the **imperative implementation** that will be extracted to C code.
+
+> (eric) Stupid remark: Here, you use the work "imperative", as if the specification at the higher level was purely "declarative". But I feel that more often than not, our high-level specification are more "imperative" (they describe how to calculate something) than "declarative" (e.g., they describe the post-condition to be verified).
+
+It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory.
+
+### Tensor
+
+- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc..., and the operator should be defined specifically for that type.
+
+>(eric) "etc."
+
+- **Representation**: Tensors are represented as pointers to structures that contain:
+
+ - `t_rank`: Number of dimensions
+
+ - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array
+
+ - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...)
+
+
+
+Note that, unlike the previous level, here the tensors are defined based on a concrete target representation - tensors are essentially flat arrays in memory.
+
+Moreover, there is no `background` value in this representation - **tensors entries must be accessed only in valid coordinates** (i.e., coordinates that are within the bounds of the tensor shape).
+
+> (eric) "only valid indexes must be used to access values of tensors"
+
+### Specification Style
+
+Throughout this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation.
+
+>(eric) "In this level..."
+
+### Importance of this level
+
+It provides the necessary details to extract executable C code and reason about memory, bounds, and machine integers.
+
+> (eric) To be consistent with what follows: "integers" => "data types"
+
+## 1.3 Link between the two levels
+
+### Why is it important to have two levels?
+
+- We need somehow to express that the **concrete implementation** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**.
+
+>(eric) "concrete implementation" or "concrete specification"?
+
+- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification.
+
+- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes.
+
+- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification.
+
+The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement:
+
+
+
+
+
+
+
+The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**.
+
+Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections.
+
+---
+
+
+
+# Part 2 — Guidelines for Abstract Formalization
+
+## 2.1. Module Structure
+
+Each abstract module should follow this general structure:
+
+
+```
+module OP
+ (* 1. Imports *)
+ (* 2. Auxiliary predicates *)
+ (* 3. Auxiliary helper functions *)
+ (* 4. Dimension-computing function *)
+ (* 5. Data-computing function *)
+ (* 6. Main operator function *)
+end
+```
+
+Neither all the above sections are mandatory nor its order is fixed, however, it is recommended to follow this structure as much as possible for consistency and readability across different operator specifications.
+
+> (eric) On the contrary, shouldn't we enforce this structure and make it possible for a section to be left empty?
+
+On the other hand, **4**, **5** and **6** are usually written in such order as each one of them depends on the previous one.
+
+
+## 2.2 Function Declarations
+
+Why3 supports several ways to declare functions, each with different purposes and objectives.
+
+We can have any of the following function signatures:
+
+> (eric) I have added some elements of description below:
+
+- `function`, which introduces a pure logical function that can be used in contracts, lemmas , etc. and cannot contain algorithmic code.
+
+- `let`
+
+- `let function`, which introduces a function that can be used both in code and specifications.
+
+- `let ghost function`, which is similar to `let function`, but that can only be used for verification.
+
+- `let rec function`, which introduces a recursive function that can be used both in code and specifications.
+
+- `let rec ghost function`, which is similar to a `let ghost function` but for a recursive function.
+
+- more information on this in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html)
+
+> (eric) A precise definition of these constructs is given in [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html).
+
+Ideally, according to Loïc's proposal, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used.
+
+> Remove reference to Loîc in the guidelines... Instead, recall why this is considered as good practice.
+
+> (eric) Todo (not necessarily by you!): give a few words of explanation on the major differences between these types of functions and their main usage.
+
+
+However, this happens to be not always possible.
+
+### 2.2.1 Termination and Variants
+
+In order to understand that have a look at the following module which computes the unit summation in the range $[0, n-1]$.
+
+>(eric) link with previous paragraph to be modified.
+
+
+```why3
+module OPSummation
+ use int.Int
+
+ function summation (i : int) (iter : int) : int =
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+end
+```
+
+> This way of computing the sum seem pretty strange to me... sum (i) = if i > 0 then i + sum (i -1) else 0... but I guess that this was part of some more complicated function.
+
+In this case, if you attempt to open the why3 ide the following error will be shown:
+
+
+
+In fact, `functions` are too weak to successfully prove recursion termination and therefore we need to help them prove termination.
+
+>(eric) "... we need to help the prover..."
+
+>(eric) It is not really that "function is too weak". It is the consequence of what a function denotes: a `function` is a purely logical construct that has no semantics of "execution", so it does not denote an algorithm, i.e, something that iterates and maintain some state. Consequently, it cannot have a "variant" (since nothing "variates").
+
+To do so, we have to introduce a **variant** (some expression that repeatedly decreases with each recursive call).
+
+However, `function` **does not support contracts** and therefore **variants** are **forbidden**, so we need to switch to `let rec function` and provide the variant there.
+
+> (eric) Maybe should we explain things the other rund: when the function denotes an algorithm, then we have to use a `let [rec] function` and specify variants.
+
+```why3
+ let rec function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+end
+```
+
+### 2.2.2 Verification Conditions and Requires Clauses
+
+Note that, functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**.
+
+> (eric) "Note that, pure logical functions declared by the keyword..."
+
+Changing the signature from `function` to `let rec function` will, on the other hand, generate such verification conditions.
+
+>(eric) We could possibly write that a `function` is a definition and, in that sense, it cannot be verified. Stated differently, a verification condition is only generated when there is something to be verified, such as a precondition before calling some function or a post condition after having executed a function. Here again, the key point is that to generate a verification condition, we need an algorithm, i.e., something to be executed. And a `function` does not specify an algorithm.
+
+That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions.
+
+To compare such different behaviors, check the examples below:
+
+**Explicitly need for requires clauses**
+
+```why3
+ (** Helper function to extract element from list at given position **)
+
+ let rec function get_dim (dims : list int) (idx : int) : int
+ requires { 0 <= idx < length dims }
+ variant { dims }
+ = match dims with
+ | Nil -> 0 (* should not happen due to precondition *)
+ | Cons h t -> if idx = 0 then h else get_dim t (idx - 1)
+ end
+```
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ =
+ if Int.(i >= axis) then
+ 1
+ else
+ get_dim x.dims i * calculate_dY0 x axis Int.(i + 1)
+```
+
+This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list.
+
+Consequently, verification conditions will be raised as is depicted in the picture below:
+
+> (eric) "raised" => "generated".
+
+
+In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions.
+
+Therefore, the code above should be adapted to include such contracts:
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ (*Need this requires because of get_dim requires*)
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ variant { axis - i }
+ =
+ (...)
+```
+
+
+
+**No need for requires clauses**
+
+```why3
+ function calculate_dims (x : tensor real) (axis : int) : list int
+ =
+ let dY0 = calculate_dY0 x axis 0 in
+ let dY1 = calculate_dY1 x axis axis in
+ Cons dY0 (Cons dY1 Nil)
+```
+
+
+The code written above will not generate any verification conditions, even if the functions called inside it explicitly need **requires clauses**.
+
+> (eric) Maybe the word "called" is a bit misleading. As far as I understand, When a function is referred to in a pure logical function, as it is a definition, it only refers to the contract of the "called" function (pre, post) which define the function, but not describe how it behaves.
+
+Therefore, no verification condtions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left pannel in the picture above).
+
+> (eric) "condtions" => "conditions", "pannel" => "panel".
+
+### 2.2.3 TypeInvariant Lemmas
+
+When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form.
+
+> (eric) I am not sure to understand what you mean by "it may appear only...".
+
+As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties.
+
+The `calculate_dY0` function defined above is a concrete example of this issue.
+
+The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive:
+
+```why3
+ let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int)
+ requires { positive x.dims }
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ ensures { 0 < calculate_dY0 x axis i }
+ variant { axis - i }
+ =
+ if i >= axis then
+ ()
+ else
+ let d = get_dim x.dims i in
+ calculate_dY0_positive x axis (i + 1)
+```
+
+The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints).
+
+### 2.2.4 Main Operator Function
+
+Moreover, the main operator function cannot be declared as `function` because it needs to have contracts (`requires` / `ensures`) and `function` does not support contracts.
+
+>(eric) Again, I feel as if we were presenting the problem the other way round : what we are specifying is an algorithm to compute the operator because we want to generate code. So, as we are dealing with an algorithm and not a pure logical definition, what we specify is a "let [rec) function."...
+> (eric) But I am not sure why we use a ghost function for an operator since we want to generate code out of it.
+
+Therefore, the main operator function should be declared as `let ghost function` with full contracts.
+
+```why3
+module OPSummation
+ use int.Int
+
+ let rec function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+
+ let ghost function unitSum (n: int) : int =
+ requires { n >= 0 }
+ ensures { result = n }
+ summation 0 n
+end
+```
+
+### 2.2.5 Guidelines
+
+| Guideline | Details |
+|:---|:---|
+| Use `function` for total functions and whenever it is possible! | A `function` is a logical function. |
+| Use `let rec function` only when recursion needs a variant. | If Why3 cannot prove termination of a `function`, switch to `let rec function` and provide a `variant` clause. |
+| Use `let ghost function` for the **main operator**. | The main function should be a `let ghost function` with full contracts (`requires` / `ensures`). |
+
+>(eric) I think that we should discuss those "guidelines" because I feel as if we were presenting things in the "wrong direction"...
+
+### Examples
+
+Besides the summation example above, here are some examples of such formalization styles:
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+## 2.3. Contracts on the Main Function
+
+Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec`.
+
+The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.
+
+On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions:
+
+| Postcondition | Purpose |
+|:---|:---|
+| `ensures { result.dims = ... }` | Specifies the **output shape** |
+| `ensures { result.data = ... }` | Specifies the **output data** (as a map) |
+| `ensures { result.background = ... }` | Specifies the **background value** |
+
+### Example
+
+```why3
+let ghost function opclip (x l m : tensor real) : tensor real
+ requires { is_scalar_tensor l }
+ requires { is_scalar_tensor m }
+ ensures { result.dims = x.dims } (* ← shape *)
+ ensures { result.data = dclip x l m } (* ← data *)
+ ensures { result.background = x.background } (* ← background *)
+=
+ { dims = x.dims; data = dclip x l m; background = x.background }
+```
+
+## 2.4. Data Function Pattern
+
+Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern:
+
+> (eric) I am HERE.
+
+### Anonymous function declaration
+
+Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics.
+
+We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function.
+
+Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**.
+
+That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal).
+
+```why3
+let ghost function d (x: tensor a') (output_shape: list int) : data a'
+=
+fun ks ->
+ if valid ks output_shape then
+ ...
+ else
+
+```
+
+The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics.
+
+The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics.
+
+This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor.
+
+### Recursive dimensions constructs
+
+An alternative way would be to define as much recursive helper functions as the number of dimensions and then define the data function based on these recursive functions.
+
+This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof.
+
+As a standard we will stick with the `anonymous function` definition.
+
+### Examples
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+
+## 2.5 Operator tensor types
+
+Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level.
+
+For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors.
+
+However, there are operator whose abstract tensor type can be completely polymorphic.
+
+Take a look ate the example below:
+
+```why3
+module OPWhere
+ use tensor.Tensor
+
+ function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+end
+```
+
+
+Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`.
+
+Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**.
+
+Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic.
+
+So whenever possible one should resort to **polymorphic** abstract datatypes.
+
+This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others.
+
+
+# Part 3 — Guidelines for Concrete Formalization
+
+## 3.1. Module Structure
+
+The concrete module should follow this general structure:
+
+```why3
+ module COP
+ (* 1. Imports *)
+ (* 2. Auxiliary helper functions *)
+ (* 3. Main operator function *)
+ end
+```
+
+### 3.2 Auxiliary helper functions
+
+All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well.
+
+Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ...
+
+Under this context, translation means:
+
+- Defining the function in an imperative style
+
+- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping)
+
+To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw):
+
+```why3
+ (* Abstract level *)
+ function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real
+ =
+ let b = get_dim x_p_coords 0 in
+ let c = get_dim x_p_coords 1 in
+ let n = (get_dim x_p_coords 2) - pad_top in
+ let m = (get_dim x_p_coords 3) - pad_left in
+ let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in
+ if valid x_coords x.dims then
+ x.data x_coords
+ else
+ 0.0
+```
+
+```why3
+ (* Concrete level *)
+ let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) =
+ requires { valid_tensor x }
+ requires { x.t_rank = 4 }
+ requires { valid_range x_p_coords 0 4 }
+ requires { writable x_p_coords }
+ requires { in_bounds (value_at x_p_coords 2 - pad_top) }
+ requires { in_bounds (value_at x_p_coords 3 - pad_left) }
+
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+
+ let ref flag = False in
+ let b = x_p_coords[0] in
+ let c = x_p_coords[1] in
+ let n = (x_p_coords[2]) - pad_top in
+ let m = (x_p_coords[3]) - pad_left in
+ let x_coords_array = malloc (to_uint32 4) in
+ if is_not_null x_coords_array then begin
+ flag <- True;
+ x_coords_array[0] <- b;
+ x_coords_array[1] <- c;
+ x_coords_array[2] <- n;
+ x_coords_array[3] <- m;
+
+ assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ;
+
+ let x_coords = coffset x_coords_array x.t_dims x.t_rank in
+ if x_coords >= 0 then begin
+ (x.t_data[x_coords], flag)
+ end
+ else begin
+ ((f32 0.0), flag)
+ end
+ end
+ else begin
+ flag <- False;
+ ((f32 0.0), flag)
+ end
+```
+
+These two functions perform the same exact computation.
+
+To better understand how these two relate one can compare both specifications:
+
+**Coordinates**
+
+- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator.
+
+ - These functions are equivalent and such equivalence proof is already available in the tensors library
+
+- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps:
+
+ - **Firstly**, the appropriate space is allocated with the `malloc` function
+
+ - **If the malloc is successful**, we then set each index of the array with the appropriate value.
+
+ - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause.
+
+**Computation**
+
+- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0.
+
+- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0.
+
+- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels.
+
+**Refinement mapping**
+
+- The refinement mapping relation is expressed with an ensures. In this function this realtion is expressed by:
+
+ ```
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+ ```
+
+- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels.
+
+- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created.
+
+### 3.3 Main operator function
+
+This is naturally the most important function and the one whith more details to be taken into account.
+
+#### 3.3.1 Contracts
+
+The **main operator function** must include contracts at both levels.
+
+**Requires**
+
+- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.
+
+- On the **concrete level** we should have preconditions for:
+
+ 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor**
+
+ 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**.
+
+ 3. All the **preconditions** already present at the abstract level. The **concrete** will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change).
+
+**Ensures**
+
+- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**:
+
+- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form:
+
+ - `ensures { tensor result = (tensor x) ... }`
+
+The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw).
+
+- **Concrete level**
+
+ ```why3
+ let flatten (x r : ctensor) (axis: int32)
+ (* Requires*)
+ (* 1 - Valid tensors *)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+
+ (* 2 - Shape match *)
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+
+ (* 3 - Informal Spec *)
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+
+ (* Ensures *)
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m - 1 do
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k }
+ r.t_data[i] <- x.t_data[i]
+ done;
+
+ assert { tensor r == flatten (tensor x) (to_int axis) }
+ ```
+
+**Requires**
+
+Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors.
+
+Expressing the **shape match** on the other hand is not that trivial.
+There are multiple ways of doing this match.
+The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same.
+To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`.
+
+- `tensor`: Converts a **ctensor** into an **abstract tensor**
+
+- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int`
+
+- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that´s why such convertion is important.
+
+ - Takes as inputs: the **pointer** to convert and the **desired size** of conversion
+
+For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level.
+
+Translating the preconditions from the abstract level to the concrete level can be done in two different ways:
+
+ 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions
+
+ 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure.
+
+In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provavle than the other, so it is up to the specifier to choose which one to use. Having said so, the second **precondition** could also have been expressed through abstract terms:
+
+- `requires { size (tensor x) = size (tensor r) }`
+
+To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate wether or not the verification conditions generated are automatically proved.
+
+This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them.
+
+Check the following example:
+
+```why3
+ let flatten (x r : ctensor) (axis: int32)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let ghost _ = flatten (tensor x) (to_int axis) in
+ (...)
+```
+
+This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator.
+
+The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved
+
+
+
+If one had forgotten to include this precondition:
+
+```
+requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+```
+
+then, the strategy proposed above would have generated the following verification condition which is not automatically proved:
+
+
+
+By inspecting the task menu, the specifier would be able to understand which precondition is missing and add it to the specification.
+
+
+
+**Ensures**
+
+Our final goal is to prove the **ensures** clause stating the refinement between both levels.
+
+Usually the following pipeline is followed to do so:
+
+$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$
+
+Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof.
+
+### 3.4 Invariants
+
+**Nested Loop Invariants**
+
+Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**.
+
+A loop invariant must satisfy two proof obligations:
+
+1. **Initialization** — the invariant holds for the first iteration.
+
+2. **Preservation** — if the continually holds for all iterations up to the last one, inclusive.
+
+When loops are nested, these obligations form a dependency chain:
+
+- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant
+
+- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant.
+
+The following example illustrates this:
+
+```why3
+let ref sum = (f32 0.0) in
+(* ... *)
+for i = 0 to rows - 1 do
+ invariant { sum = 0.0 }
+ for j = 0 to cols - 1 do
+ invariant { sum = 0.0 }
+ for k = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) i j 0 k }
+ (* ... *)
+ done;
+ sum <- 0.0;
+ done;
+done;
+```
+
+The innermost loop (`k`) accumulates a dot product in `sum`.
+
+For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore:
+
+$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$
+
+In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$.
+
+### Loop Invariants for Proving Data Refinement
+
+**Example 1 - Unit Summation**
+
+To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range.
+
+The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one.
+
+The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove:
+
+
+
+The goal and the respective logical context are below:
+
+
+
+What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration.
+
+To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level.
+
+Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1:
+
+```why3
+ let rec lemma summation_lemma (i iter : int)
+ requires { i < iter }
+ ensures { 1 + summation (i + 1) iter = summation i iter }
+ variant { iter - i }
+ =
+ if i >= iter then
+ ()
+ else
+ summation_lemma (i + 1) iter
+```
+
+After adding this, the invariant preservation is easily proved.
+
+This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation.
+
+**Example 2 - Dot Product**
+
+The example presented above is a very simple one, but the same strategy can be applied to much more complex operators.
+
+Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator.
+
+The file is available [here](./examples/dot_product.mlw).
+
+Once again the proof is not achievable and the task menu displays the following:
+
+
+
+To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma.
+
+The strategy is essentially the same:
+
+```why3
+ let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int)
+ requires { Int.(i <= k < iter) }
+ variant { Int.(k - i) }
+ ensures {
+ let a_val = a.data (Cons row (Cons k Nil)) in
+ let b_val = b.data (Cons k (Cons col Nil)) in
+ dot_product_rec a b row col i Int.(k + 1) =
+ Real.(dot_product_rec a b row col i k + a_val * b_val)
+ }
+=
+ if i < k then
+ Real.(dot_product_lemma a b row col (i + 1) k iter)
+ else
+ ()
+```
+
+This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma.
+
+
+
+To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables.
+
+We will do so by introducing a ghost block:
+
+```why3
+ghost begin
+ dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter)
+end
+```
+
+After this, the invariant preservation is easily proved.
+
+More information regarding lemmas intantiation in [4.2](#42-ide-transformations-and-prover-hints).
+
+**Convolution**
+
+The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops.
+
+Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates.
+
+Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above.
+
+The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration.
+
+This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate.
+
+The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement.
+
+Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor.
+
+Below are the four loop invariants used in the concrete Conv implementation:
+
+```why3
+ for nn = 0 to n - 1 do
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ r_coords_array[0] <- nn;
+ for mm = 0 to m - 1 do
+ invariant { value_at r_coords_array 0 = nn }
+ invariant {
+ forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ }
+ r_coords_array[1] <- mm;
+ for y_hh = 0 to y_h - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+
+ }
+ r_coords_array[2] <- y_hh;
+ for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+```
+
+The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically.
+
+The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected.
+
+They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten.
+
+This must be asserted explicitly each time.
+
+### The innermost loop
+
+The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop):
+
+```why3
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+```
+
+The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct:
+
+```why3
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ };
+
+ assert {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ };
+```
+
+With these assertions in place, the proof of the innermost invariant succeeds.
+
+### The outer loops
+
+The preservation of the outer invariants still fails.
+
+To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant:
+
+```why3
+constant yy_hh : int
+
+H3 : 0 <=' yy_hh
+
+H2 : yy_hh <' (y_hh +' 1)
+
+constant yy_ww : int
+
+H1 : 0 <=' yy_ww
+
+H : yy_ww <' int32'int y_w
+
+constant coords : list int =
+ Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int))))
+
+constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank))
+
+------------------------------- Goal --------------------------------
+
+goal c_conv'vc :
+ (value_at (r.t_data) @ offset1 coords dims).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h)
+ (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)
+```
+
+Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant:
+
+```
+LoopInvariant2 :
+ forall yy_hh1:int.
+ 0 <=' yy_hh1 /\ yy_hh1 <' y_hh ->
+ (forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r1.t_data)
+ @ offset1
+ (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0
+ (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3))
+ nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w)
+ (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)))
+```
+
+At first glance, the hypothesis and the goal look nearly identical.
+
+However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`.
+
+The only relationship between `r` and `r1` available in the logical context is:
+
+```why3
+H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data)
+```
+
+Meanwhile, from the innermost loop invariant, the following hypothesis is also available:
+
+```
+LoopInvariant1 :
+ forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r.t_data)
+ @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h)
+ (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left))
+```
+
+The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices.
+
+This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop.
+
+The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant:
+
+```
+for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+
+```
+
+By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant.
+
+However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop.
+
+The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten.
+
+The same pattern of assertions resolves this:
+
+```why3
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ )
+ };
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ )
+ };
+```
+
+Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator.
+
+The completed file is available [here](./examples/conv.mlw).
+
+# Part 4 - Tips, Hints and Strategies
+
+## 4.1. Scope Resolution
+
+When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors.
+
+Take a look at the following example:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { iter - i }
+ =
+ if i < iter then
+ 1.0 + summation (i + 1) iter
+ else
+ 0.0
+end
+```
+
+If you attempt to open the Why3 ide with the above code, you will get the following error:
+
+
+
+This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`.
+
+In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name.
+
+In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules.
+
+Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope.
+
+To solve the issue we will have to specify the scope operations on the `Int` domain:
+
+
+$$(iter - i) \rightarrow Int.(iter - i)$$
+
+$$i < iter \rightarrow Int.(i < iter)$$
+
+$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$
+
+The corrections applied to the code above are the following:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { Int.(iter - i) }
+ =
+ if Int.(i < iter) then
+ 1.0 + summation Int.(i + 1) iter
+ else
+ 0.0
+end
+```
+
+## 4.2. IDE Transformations and Prover Hints
+
+Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals.
+
+Below are techniques we have found useful in practice.
+
+The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE.
+
+---
+
+### Lemma / Axiom Instantiation
+
+When an axiom or lemma is stated universally, for example:
+
+```why3
+axiom test:
+ forall x: int. P(x) -> Q(x)
+```
+
+The automated prover may either fail or take significantly longer to apply it in a specific context.
+
+Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful.
+
+**Example — Mean Value Theorem:**
+
+Consider the following axiom:
+
+```why3
+(* Lagrange Mean Value Theorem *)
+axiom mean_value_theorem_sigmoid:
+ forall x y: real.
+ exists c: real.
+ (x <= c <= y \/ y <= c <= x) /\
+ sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y)
+```
+
+And the following lemma to be proved:
+
+```why3
+lemma lipschitz_sigmoid:
+ forall x y: real.
+ abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y)
+```
+
+To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem.
+
+Instantiating the axiom with the specific variables `x` and `y` greatly helps.
+
+**IDE instantiation syntax:**
+
+```
+instantiate
+```
+
+If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step.
+
+The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it.
+
+This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE.
+
+---
+
+### Instantiation via Lemma Functions
+
+Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved.
+
+This is the preferred approach, as it keeps the proof self-contained and reproducible.
+
+**Example — `valid_bounds_2` lemma function:**
+
+The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions:
+
+```why3
+let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int)
+ requires { p <= q }
+ requires { valid_range ks p q }
+ requires { valid_range ds p q }
+ requires { pdim ds p q }
+ requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i }
+ ensures { Range.valid (islice ks p q) (islice ds p q) }
+ variant { q - p }
+ = if p < q then
+ begin
+ assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) };
+ assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) };
+ valid_bounds_2 ks ds (p+1) q
+ end
+```
+
+Now consider the `dot_product` function, which allocates coordinate arrays at runtime:
+
+```why3
+let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) =
+ requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter }
+ requires { a.t_rank = b.t_rank = 2 }
+ requires { iter >= 0 }
+ requires { row >= 0 /\ row < value_at a.t_dims 0 }
+ requires { col >= 0 /\ col < value_at b.t_dims 1 }
+ (* ... *)
+ let ref sum = (f32 0.0) in
+ let ref flag = False in
+ let a_coords_array = malloc (to_uint32 2) in
+ let b_coords_array = malloc (to_uint32 2) in
+ if is_not_null a_coords_array && is_not_null b_coords_array then begin
+ flag <- True;
+ for i = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) row col 0 i }
+ set_ofs a_coords_array 0 row;
+ set_ofs a_coords_array 1 i;
+ set_ofs b_coords_array 0 i;
+ set_ofs b_coords_array 1 col;
+
+ ghost valid_bounds_2 a_coords_array a.t_dims 0 2;
+ (* Rest of code ... *)
+```
+
+Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own.
+
+Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue.
+
+---
+
+### Function Unfolding
+
+When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle.
+
+Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE.
+
+**`unfold`** expands a specific function by its definition at the point where it appears in the goal.
+
+To use `unfold` transformation, enter the following command in the **"Type command here"** field:
+```
+unfold
+```
+
+Before applying `unfold`:
+
+
+
+After applying `unfold`:
+
+
+
+**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously.
+
+It requires no arguments:
+
+```
+compute_in_goal
+```
+
+Result after applying `compute_in_goal`:
+
+
+
+
+---
+
+## 4.3. How to Debug
+
+"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal.
+
+There are two common root causes:
+
+1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated.
+
+2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces.
+
+The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap.
+
+### Reading the Logical Context
+
+Consider the following goal:
+
+```
+------------------------------- Goal --------------------------------
+
+goal w_cools_calculate'vc :
+ to_extended_real max_value =
+ w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b)
+ (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w)
+ (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom)
+ (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w)
+```
+
+In the logical context, among other hypotheses, we have:
+
+```
+constant x_val : float
+
+constant aux_flag : bool
+
+Ensures4 :
+ aux_flag = True ->
+ coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top)
+ (to_int2 pad_left) = to_extended_real x_val
+
+constant ww1 : int321
+
+constant ww : int = int32'int ww1
+
+H31 : 0 <=' ww
+
+H30 : ww <=' int32'int o2
+
+LoopInvariant :
+ to_extended_real max_value1 =
+ w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c)
+ (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h)
+ (to_int2 str_w)
+
+H1 :
+ w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0
+ (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h)
+ (int32'int dil_w) (int32'int pad_top) (int32'int pad_left)
+ (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h)
+ (int32'int str_w) =
+ max_extended_real
+ (coords_from_X_p (tensor x)
+ (Cons (int32'int b)
+ (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int)))))
+ (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b)
+ ...)
+
+Ensures :
+ to_extended_real (max max_value1 x_val) =
+ max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+
+constant max_value : float
+
+H : max_value = max max_value1 x_val
+```
+
+The logical context is sufficient to prove the goal. Here is the reasoning:
+
+**Left-hand side of the goal.**
+
+From `H`, we know `max_value = max max_value1 x_val`, so:
+
+```
+to_extended_real max_value = to_extended_real (max max_value1 x_val)
+```
+
+Applying `Ensures`:
+
+```
+= max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+```
+
+**Right-hand side of the goal.**
+
+Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`:
+
+```
+w_cools_calculate ... (ww +' 1) ...
+ = max_extended_real
+ (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...)
+```
+
+From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`.
+
+For the first argument, `Ensures4` tells us (given `aux_flag = True`) that:
+
+```
+coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left)
+ = to_extended_real x_val
+```
+
+So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity).
+
+However, Why3 cannot discharge the goal automatically.
+
+**Why Why3 doesn't make the proof**
+
+The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor:
+
+```
+coords_from_X_p (tensor x)
+ (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil))))
+ (int32'int pad_top) (int32'int pad_left)
+```
+
+whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`.
+
+These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own.
+
+The connection must be made explicit with an intermediate assertion:
+
+```why3
+assert { ivector x_p_coords_array 4 =
+ Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) };
+```
+
+This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through.
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-em.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-em.md
new file mode 100644
index 00000000..c34faab4
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-em.md
@@ -0,0 +1,1583 @@
+# SONNX — Formalization Guidelines
+
+==all coding examples in here should have syntax highlighting if possible==
+
+A structured guide for writing formal specifications of SONNX operators in Why3.
+
+This guidelines do not cover the basics of Why3, so familiarity with its syntax, semantics and features is expected.
+
+---
+
+## Table of Contents
+
+- [Part 1 — Formalization Styles](#part-1--formalization-styles)
+ - [1.1 — Abstract Formalization](#11-abstract-formalization)
+ - [1.2 — Concrete Formalization](#12-concrete-formalization)
+ - [1.3 — Link between the two levels](#13-link-between-the-two-levels)
+- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization)
+ - [2.1 — Module Structure](#21-module-structure)
+ - [2.2 — Function Declarations](#22-function-declarations)
+ - [2.3 — Contracts on the Main Function](#23-contracts-on-the-main-function)
+ - [2.4 — Data Function Pattern](#24-data-function-pattern)
+ - [2.5 — Operator tensor types](#25-operator-tensor-types)
+- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization)
+ - [3.1 — Module Structure](#31-module-structure)
+ - [3.2 — Auxiliary helper functions](#32-auxiliary-helper-functions)
+ - [3.3 — Main operator function](#33-main-operator-function)
+ - [3.4 — Invariants](#34-invariants)
+- [Part 4 — Tips, Hints and Strategies](#part-4---tips-hints-and-strategies)
+ - [4.1 — Scope Resolution](#41-scope-resolution)
+ - [4.2 — IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints)
+ - [4.3 — How to Debug](#43-how-to-debug)
+
+
+
+
+
+---
+
+
+
+# Part 1 — Formalization Styles
+
+In SONNX, every operator must be formalized at two distinct levels: abstract formalization and concrete formalization.
+
+Each level serves a different purpose and follows a different style.
+
+We start from an abstract formalization — which captures the mathematical semantics of the operator and serves as the **source of truth** for correctness — and progressively refine it into a concrete formalization that is close enough to the target implementation to be automatically extracted into C code - reasoning about memory, bounds, and machine data types.
+
+Understanding both is essential before writing any specification.
+
+## 1.1 Abstract Formalization
+
+At this level the operator must be specified making no reference to any implementation detail, and therefore it should be **implementation-agnostic**.
+
+It shall be **traceable** to the **informal specification**, being easy to validate, first and easy to read and understand, second. ==add link to informal spec== Traceability must be enforced:
+
+- first by keeping the formal specification as close to the informal specification (e.g., stick to the mathematical specification given in the informal specification)
+
+- second by providing explicit link via `[tags]` to specific parts of the informal specification.
+
+No implementation details should be present at this level, which means that the specification of tensors shall remain as abstract as possible, focusing on their mathematical properties.
+
+### Tensors
+
+- **Type**: At this level tensors are specified as **polymorphic structures**, i.e., they are parameterized by the type of the values they contain.
+ This means that the specification author should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types).
+
+- **Representation**: Tensors are represented as structures containing the following records:
+
+ - `dims` - List of integers representing the shape
+
+ - `data` - A map from [indexes](../../../spec/informal/common/definitions.md) to values
+
+ - `background` - The default value for indexes out of the bounds defined by the shape
+
+ A "valid index" is an index compatible with the dimension of the tensor, i.e. the i-th components of the index is strictly positive and strictly less than the i-th dimension of the tensor
+
+ The data mapping is complete since it maps all possible indexes to values, but the value mapped by invalid indexes is the `background` value.
+
+- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representation:
+
+ 1. **Positive Dimensions**
+
+ All dimensions in the `dims` list of a tensor must be **strictly positive integers**.
+ This constraint may be relaxed in a lated version of the specification to support tensors with **null** dimensions. [***Work in Progress***]
+
+ - Predicate `positive`
+
+ ```why3
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+ ```
+ - `invariant { positive dims }`
+
+ 2. **Valid values**
+
+ Indexes are either valid for the tensor shape or the value mapped by those indexes is the `background` value.
+
+ - Predicate `valid` ==the code below returns true for an empty index access to an empty tensor. This will be a problem when we introduce null tensors==
+
+ ```why3
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+ ```
+ - `invariant { forall k. valid k dims \/ data k = background }`
+
+
+
+ These invariants are extremely important as they will sometimes define how the operator should be formalized.
+
+ For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant.
+
+ An alternative was to define the data through recursive definitions once the shape is already computed, but this approach is much more complex and usually requires auxiliary lemmas to help the proof. ==This sentence is a bit obscure for the casual reader. If the message is important, and it might be as it seems to contain a recommendation on how to do things, I suggest expanding the explanation to two-three sentences==
+
+ The above predicates and invariants are available in the [tensors library](../../../spec/formal/common/libs/tensor/).
+
+### Specification Style
+
+Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output).
+
+Users should specify the operator based on mathematically and recursive definitions, avoiding loops and mutable state, increasing the **provability** and **ease of proof** of this specification.
+
+## 1.2 Concrete Formalization
+
+The concrete level describes the **imperative implementation** that will be extracted to C code.
+
+It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory.
+
+### Tensors
+
+- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc. and the operator should be defined specifically for that type.
+
+- **Representation**: Tensors are represented as pointers to structures that contain:
+
+ - `t_rank`: Number of dimensions
+
+ - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array ==What happens if the tensor dimension (or number of dimensions) is larger that the max value of int32? It is a ridicolous case in practice, but may happen in a cybersec attack scenario==
+
+ - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...)
+
+
+
+Note that, unlike the previous level, here the tensors are defined based on a target C representation - tensors are essentially flat arrays in memory.
+
+Moreover, there is no `background` value in this representation - **only valid indexes must be used to access values of tensors** (i.e. coordinates that are within the bounds of the tensor shape).==Any rationale behind this choice, e.g. computational efficiency? What happens if we try to access an invalid index? Do we guarantee that it can never happen?==
+
+### Specification Style
+
+In this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation.
+
+### Importance of this level
+
+It provides the necessary details to extract executable C code and reason about memory, bounds, and machine data types.
+
+## 1.3 Link between the two levels
+
+### Why is it important to have two levels?
+
+- We need somehow to express that the **concrete specification** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**.
+
+- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification.
+
+- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes.
+
+- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification. ==Maybe "same result" is a little strong, especially regarding numerical error with floating point. I can easily see how the abstract specification defines a set of possible correct executions, and the concrete specification chooses only one (or a small subset of them)==
+
+The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement:
+
+
+
+
+
+
+
+The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**. ==Again, I can see that the result of the concrete execution is valid/compatible according to the abstract spec, but I feel equality will be impossible for floating point data types==
+
+Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections.
+
+---
+
+
+# Part 2 — Guidelines for Abstract Formalization
+
+## 2.1. Module Structure
+
+Each abstract module should follow this general structure:
+
+
+```
+module OP
+ (* 1. Imports *)
+ (* 2. Auxiliary predicates *)
+ (* 3. Auxiliary helper functions *)
+ (* 4. Dimension-computing function *)
+ (* 5. Data-computing function *)
+ (* 6. Main operator function *)
+end
+```
+
+If any section is not needed, it can be left empty, but the overall structure must be maintained for consistency and readability across different operator specifications.
+
+## 2.2 Function Declarations
+
+Why3 supports several ways to declare functions, each with different purposes and objectives.
+
+In order to fully understand each one of this features please take into account that whyml supports **two different programming namespaces**, a **logical** and a **programming** one, each one of them built upon a different syntax and with different features. For instance both of them support **conjunctions** but while the logical one expresses it through $\land$, the programming one expresses it through the `&&` operator.
+
+
+We can have any of the following function signatures:==If an introduction to Why3 namespaces is really needed, I would prefer a more structured one. E.g. organise it by keyword (let, function, rec, ghost) rather than covering all possible combinations thereof.==
+
+- `function`: Belongs to the **logical namespace** and is used to define purely logical functions. Contracts are not supported.
+
+- `let`: Belongs to the **programming namespace** and defines a non recursive function.
+
+- `let rec`: Belongs to the **programming namespace** and defines a recursive function.
+
+- `let function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and allows them to be used at the logical namespace as well.
+
+- `let rec function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and allows them to be used at the logical namespace as well.
+
+- `let ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and **can only** be used at the **logical namespace**.
+
+- `let rec ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and **can only** be used at the **logical namespace**.
+
+- A precise definition of these constructs is given in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html)
+
+Ideally, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used.
+
+> Recall why this is considered as good practice.==This is probably the most important piece of information. Recording the "why" of your design choices is crucial==
+
+### 2.2.1 Termination and Variants
+
+==This subsection is a bit weak: (1) it contradicts the recommendation above that the abstract specification must only use the "function" keyword (by showing when to use "let rec ghost function"), (2) it is written in the style of a tutorial with examples, instead of a set of guidelines (e.g. "you shall only use function unless non-trivial recursion is absolutely needed")==
+
+It is not always possible to define all the necessary functions with the `function` construct, especially when we need to define recursive functions whose termination is not trivially provable by Why3.
+
+In order to understand that==,== have a look at the following module which computes the unit summation in the range $[0, n-1]$. This might not be the most traditional way of computing a summation, although, it intentionally resembles the iterative way of computing a summation, starting from 0 up to $n-1$.
+
+```why3
+module OPSummation
+ use int.Int
+
+ function summation (i : int) (iter : int) : int =
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+end
+```
+In this case, if you attempt to open the why3 ide the following error will be shown:
+
+
+
+In fact, `functions` are not supposed to be used in recursive definitions, unless their termination is trivially provable by Why3 such as recursive calls over the tail of a list. ==So, function _can_ be recursive? How is "trivially provable" defined?==
+
+Intead one should use a `let rec ghost function` declaration, which allows to define recursive functions with a **variant** (some expression that repeatedly decreases with each recursive call) that can only be used in the logical namespace.
+
+```why3
+ let rec ghost function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+```
+
+### 2.2.2 Verification Conditions and Requires Clauses
+
+Note that, pure logical functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**. ==Lack of guidelines here. Do we want all functions to specify pre-conditions? If not, do we have a rule to know which ones should?==
+
+Recalling the [definitions](#functions_def)==,== it is clear that `function` and `let rec ghost function` belong to different namespaces, although both of them can only be used at the logical namespace. As a consequence, since `let rec ghost function` is not a definition but an actual executable function, it generates verification conditions for all the functions called inside it that have preconditions.
+
+That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions.
+
+To compare such different behaviors, check the examples below:
+
+**Explicitly need for requires clauses** ==I find this example confusing for two reasons: (1) there is no explicit recommendation, so i do not know _why_ you are reminding me of this specific feature of Why3, (2) I cannot understand what calculate_dY0 does==
+
+```why3
+ (** Helper function to extract element from list at given position **)
+
+ let rec function get_dim (dims : list int) (idx : int) : int
+ requires { 0 <= idx < length dims }
+ variant { dims }
+ = match dims with
+ | Nil -> 0 (* should not happen due to precondition *)
+ | Cons h t -> if idx = 0 then h else get_dim t (idx - 1)
+ end
+```
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ =
+ if Int.(i >= axis) then
+ 1
+ else
+ get_dim x.dims i * calculate_dY0 x axis Int.(i + 1)
+```
+
+This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list.
+
+Consequently, verification conditions will be generated as is depicted in the picture below:
+
+
+
+In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions.
+
+Therefore, the code above should be adapted to include such contracts:
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ (*Need this requires because of get_dim requires*)
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ variant { axis - i }
+ =
+ (...)
+```
+
+
+
+**No need for requires clauses**
+
+```why3
+ function calculate_dims (x : tensor real) (axis : int) : list int
+ =
+ let dY0 = calculate_dY0 x axis 0 in
+ let dY1 = calculate_dY1 x axis axis in
+ Cons dY0 (Cons dY1 Nil)
+```
+
+
+The code written above will not generate any verification conditions, even if the functions referenced inside it explicitly need **requires clauses**.
+
+Therefore, no verification conditions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left panel in the picture above).
+
+### 2.2.3 TypeInvariant Lemmas
+
+When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form.=="sometimes" is a worryingly ambiguous thing to read in a guideline document. If it is well-known Why3 behaviour, add a link to further documentation==
+
+As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties.==Do we have a strict requirement that type invariants must be proven? Where is it listed? Add reference to relevant doc.==
+
+The `calculate_dY0` function defined above is a concrete example of this issue.
+
+The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive:
+
+```why3
+ let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int)
+ requires { positive x.dims }
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ ensures { 0 < calculate_dY0 x axis i }
+ variant { axis - i }
+ =
+ if i >= axis then
+ ()
+ else
+ let d = get_dim x.dims i in
+ calculate_dY0_positive x axis (i + 1)
+```
+
+The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints).
+
+### 2.2.4 Main Operator Function
+
+To enforce traceability, as [previously mentioned](#traceability), the main operator function should capture the constraints present in the informal specification as preconditions (`requires`) while providing postconditions (`ensures`) for every record of the output tensor.
+
+To capture such contracts one needs to define a construct that belongs to the programming namespace. ==Does it mean we have to do this only for the concrete spec? The guidelines above explicitly state that we should use the logical namespace for the abstract spec...== Moreover, since this function is only used to specification purposes it is not supposed to be used in any implementation context. To enforce that this function can only be used at the logical level sign it as **ghost**.==To quote the beginning of Section 2.2 "at the abstract level we should only declare function with the signature function and no contracts should be used." But the rest of Section 2 seems to implicitly stress that contracts are _very important_, which is confusing==
+
+Therefore, the main operator function should be declared as `let rec ghost function` with full contracts.
+
+```why3
+module OPSummation
+ use int.Int
+
+ let rec ghost function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+
+ let ghost function unitSum (n: int) : int =
+ requires { n >= 0 }
+ ensures { result = n }
+ summation 0 n
+end
+```
+
+### Examples
+
+Besides the summation example above, here are some examples of such formalization styles:
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+## 2.3. Contracts on the Main Function
+
+Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec ghost function`. ==This is the first time you state this requirement. Indeed, in Sections 2.2.2 and 2.2.3 many examples of non-main functions containing the require keyword are given==
+
+The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.
+
+On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions:==You literally just said "output constraints (such as shape) will NOT be included". This is extremely contradictory==
+
+| Postcondition | Purpose |
+|:---|:---|
+| `ensures { result.dims = ... }` | Specifies the **output shape** |
+| `ensures { result.data = ... }` | Specifies the **output data** (as a map) |
+| `ensures { result.background = ... }` | Specifies the **background value** |
+
+### Example
+
+```why3
+let ghost function opclip (x l m : tensor real) : tensor real
+ requires { is_scalar_tensor l }
+ requires { is_scalar_tensor m }
+ ensures { result.dims = x.dims } (* ← shape *)
+ ensures { result.data = dclip x l m } (* ← data *)
+ ensures { result.background = x.background } (* ← background *)
+=
+ { dims = x.dims; data = dclip x l m; background = x.background }
+```
+
+## 2.4. Data Function Pattern
+
+Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern==s==:
+
+
+### Anonymous function declaration
+
+Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics.
+
+We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function.
+
+Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**.
+
+That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal).==I see what you mean, but the section you link only gives the order in which functions should be declared in the code, which (as far as I understand the semantic of Why3) has no influence on the order of computation.==
+
+```why3
+let ghost function d (x: tensor a') (output_shape: list int) : data a'
+=
+fun ks ->
+ if valid ks output_shape then
+ ...
+ else
+
+```
+
+The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics.
+
+The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics.
+
+This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor.
+
+### Recursive dimensions constructs
+
+An alternative way would be to define as ==many== recursive helper functions as the number of dimensions and then define the data function based on these recursive functions.
+
+This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof.
+
+As a standard we will stick with the `anonymous function` definition. ==Why propose this alternative if it introduces complexity for no reason? Is there some operator for which the anonymous function approach is impossible?==
+
+### Examples
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+
+## 2.5 Operator tensor types
+
+Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level.
+
+For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors.
+
+However, there are operator whose abstract tensor type can be completely polymorphic.
+
+Take a look ==at== the example below:
+
+```why3
+module OPWhere
+ use tensor.Tensor
+
+ function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+end
+```
+
+
+Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`.
+
+Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**.
+
+Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic.
+
+So whenever possible one should resort to **polymorphic** abstract datatypes.
+
+This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others.
+
+
+# Part 3 — Guidelines for Concrete Formalization
+
+## 3.1. Module Structure
+
+The concrete module should follow this general structure:
+
+```why3
+ module COP
+ (* 1. Imports *)
+ (* 2. Auxiliary helper functions *)
+ (* 3. Main operator function *)
+ end
+```
+
+### 3.2 Auxiliary helper functions
+
+All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well.
+
+Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ...
+
+Under this context, translation means:
+
+- Defining the function in an imperative style
+
+- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping)
+
+To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw):
+
+```why3
+ (* Abstract level *)
+ function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real
+ =
+ let b = get_dim x_p_coords 0 in
+ let c = get_dim x_p_coords 1 in
+ let n = (get_dim x_p_coords 2) - pad_top in
+ let m = (get_dim x_p_coords 3) - pad_left in
+ let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in
+ if valid x_coords x.dims then
+ x.data x_coords
+ else
+ 0.0
+```
+
+```why3
+ (* Concrete level *)
+ let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) =
+ requires { valid_tensor x }
+ requires { x.t_rank = 4 }
+ requires { valid_range x_p_coords 0 4 }
+ requires { writable x_p_coords }
+ requires { in_bounds (value_at x_p_coords 2 - pad_top) }
+ requires { in_bounds (value_at x_p_coords 3 - pad_left) }
+
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+
+ let ref flag = False in
+ let b = x_p_coords[0] in
+ let c = x_p_coords[1] in
+ let n = (x_p_coords[2]) - pad_top in
+ let m = (x_p_coords[3]) - pad_left in
+ let x_coords_array = malloc (to_uint32 4) in
+ if is_not_null x_coords_array then begin
+ flag <- True;
+ x_coords_array[0] <- b;
+ x_coords_array[1] <- c;
+ x_coords_array[2] <- n;
+ x_coords_array[3] <- m;
+
+ assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ;
+
+ let x_coords = coffset x_coords_array x.t_dims x.t_rank in
+ if x_coords >= 0 then begin
+ (x.t_data[x_coords], flag)
+ end
+ else begin
+ ((f32 0.0), flag)
+ end
+ end
+ else begin
+ flag <- False;
+ ((f32 0.0), flag)
+ end
+```
+
+These two functions perform the same exact computation.
+
+To better understand how these two relate one can compare both specifications:
+
+**Coordinates**
+
+- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator.
+
+ - These functions are equivalent and such equivalence proof is already available in the tensors library
+
+- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps:
+
+ - **Firstly**, the appropriate space is allocated with the `malloc` function
+
+ - **If the malloc is successful**, we then set each index of the array with the appropriate value.
+
+ - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause.
+
+**Computation**
+
+- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0.==Aren't we supposed to return the background value, rather than 0?==
+
+- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0.
+
+- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels.==there is no postcondition on the coffset function in the code above==
+
+**Refinement mapping**
+
+- The refinement mapping relation is expressed with an ensures. In this function this ==relation== is expressed by:
+
+ ```
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+ ```
+
+- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels.
+
+- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created.
+
+### 3.3 Main operator function
+
+This is naturally the most important function and the one whith more details to be taken into account.
+
+#### 3.3.1 Contracts
+
+The **main operator function** must include contracts at both levels.
+
+**Requires**
+
+- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.==the guidelines for the abstract specification are covered in Section 2, do not repeat them here==
+
+- On the **concrete level** we should have preconditions for:
+
+ 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor**
+
+ 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**.
+
+ 3. All the **preconditions** already present at the abstract level. The **concrete** ==level== will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change).
+
+**Ensures**
+
+- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**:==already covered in Section 2: remove==
+
+- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form:
+
+ - `ensures { tensor result = (tensor x) ... }`
+
+The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw).
+
+- **Concrete level**
+
+ ```why3
+ let flatten (x r : ctensor) (axis: int32)
+ (* Requires*)
+ (* 1 - Valid tensors *)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+
+ (* 2 - Shape match *)
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+
+ (* 3 - Informal Spec *)
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+
+ (* Ensures *)
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m - 1 do
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k }
+ r.t_data[i] <- x.t_data[i]
+ done;
+
+ assert { tensor r == flatten (tensor x) (to_int axis) }
+ ```
+
+**Requires**
+
+Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors.
+
+Expressing the **shape match** on the other hand is not that trivial.
+There are multiple ways of doing this match.
+The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same.
+To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`.
+
+- `tensor`: Converts a **ctensor** into an **abstract tensor**
+
+- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int`
+
+- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that=='==s why such convertion is important.
+
+ - Takes as inputs: the **pointer** to convert and the **desired size** of conversion
+
+For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level.
+
+Translating the preconditions from the abstract level to the concrete level can be done in two different ways:
+
+ 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions
+
+ 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure.
+
+In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provable than the other, so it is up to the specification author to choose which one to use.==Which guideline should the spec author follow? Prioritise readability? Prioritise provability?== Having said so, the second **precondition** could also have been expressed through abstract terms:
+
+- `requires { size (tensor x) = size (tensor r) }`
+
+To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate ==whether== or not the verification conditions generated are automatically proved. ==you say "can", but do you mean "must" (since these are guidelines)?==
+
+This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them.
+
+Check the following example: ==The following is valuable advice, but not necessarily a "guideline". Perhaps put it in a different separate subsection and clearly mark it as "advice/tutorial". In general, there are a few of such practical advice sections throughout the document. All of them should be clearly marked==
+
+```why3
+ let flatten (x r : ctensor) (axis: int32)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let ghost _ = flatten (tensor x) (to_int axis) in
+ (...)
+```
+
+This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator.
+
+The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved
+
+
+
+If one had forgotten to include this precondition:
+
+```
+requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+```
+
+then, the strategy proposed above would have generated the following verification condition which is not automatically proved:
+
+
+
+By inspecting the task menu, the specification author would be able to understand which precondition is missing and add it to the specification.
+
+
+
+**Ensures**
+
+Our final goal is to prove the **ensures** clause stating the refinement between both levels.
+
+Usually the following pipeline is followed to do so:
+
+$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$
+
+Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof.
+
+### 3.4 Invariants
+
+**Nested Loop Invariants**
+
+Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**.
+
+A loop invariant must satisfy two proof obligations:
+
+1. **Initialization** — the invariant holds for the first iteration.
+
+2. **Preservation** — ==the invariant== continually holds for all iterations up to the last one, inclusive.
+
+When loops are nested, these obligations form a dependency chain:
+
+- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant
+
+- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant.
+
+The following example illustrates this:
+
+```why3
+let ref sum = (f32 0.0) in
+(* ... *)
+for i = 0 to rows - 1 do
+ invariant { sum = 0.0 }
+ for j = 0 to cols - 1 do
+ invariant { sum = 0.0 }
+ for k = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) i j 0 k }
+ (* ... *)
+ done;
+ sum <- 0.0;
+ done;
+done;
+```
+
+The innermost loop (`k`) accumulates a dot product in `sum`. ==the code seems to overwrite sum, rather than accumulate it==
+
+For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. ==the notation $I-j$ appears from nowhere. Can you add it inside the code (e.g. as comments)?== In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore:
+
+$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$
+
+In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$.
+
+### Loop Invariants for Proving Data Refinement
+
+ ==The implicit SONNX guideline here is something like: "the invariants in the concrete specification must be automatically proven by Why3", then the rest is a tutorial on how to achieve it. I would prefer if this distinction is made clear.==
+
+**Example 1 - Unit Summation**
+
+To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range.
+
+The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one.
+
+The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove:
+
+
+
+The goal and the respective logical context are below:
+
+
+
+What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration.
+
+To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level.
+
+Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1:
+
+```why3
+ let rec lemma summation_lemma (i iter : int)
+ requires { i < iter }
+ ensures { 1 + summation (i + 1) iter = summation i iter }
+ variant { iter - i }
+ =
+ if i >= iter then
+ ()
+ else
+ summation_lemma (i + 1) iter
+```
+
+After adding this, the invariant preservation is easily proved.
+
+This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation.
+
+**Example 2 - Dot Product**
+
+The example presented above is a very simple one, but the same strategy can be applied to much more complex operators.
+
+Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator.
+
+The file is available [here](./examples/dot_product.mlw).
+
+Once again the proof is not achievable and the task menu displays the following:
+
+
+
+To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma.
+
+The strategy is essentially the same:
+
+```why3
+ let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int)
+ requires { Int.(i <= k < iter) }
+ variant { Int.(k - i) }
+ ensures {
+ let a_val = a.data (Cons row (Cons k Nil)) in
+ let b_val = b.data (Cons k (Cons col Nil)) in
+ dot_product_rec a b row col i Int.(k + 1) =
+ Real.(dot_product_rec a b row col i k + a_val * b_val)
+ }
+=
+ if i < k then
+ Real.(dot_product_lemma a b row col (i + 1) k iter)
+ else
+ ()
+```
+
+==I don't understand the expression $k + a_val * b_val$ in the code above, why are we mixing indices (k) and data?==
+
+This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma.
+
+
+
+To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables.
+
+We will do so by introducing a ghost block:
+
+```why3
+ghost begin
+ dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter)
+end
+```
+
+After this, the invariant preservation is easily proved.
+
+More information regarding lemmas ==instantiation== in [4.2](#42-ide-transformations-and-prover-hints).
+
+==Example 3== - **Convolution**
+
+The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops. ==Is this a 2D convolution only? As far as I recall, the ONNX operator supports different sizes==
+
+Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates.
+
+Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above.
+
+The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration.
+
+This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate.
+
+The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement.
+
+Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor.
+
+Below are the four loop invariants used in the concrete Conv implementation:
+
+```why3
+ for nn = 0 to n - 1 do
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ r_coords_array[0] <- nn;
+ for mm = 0 to m - 1 do
+ invariant { value_at r_coords_array 0 = nn }
+ invariant {
+ forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ }
+ r_coords_array[1] <- mm;
+ for y_hh = 0 to y_h - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+
+ }
+ r_coords_array[2] <- y_hh;
+ for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+```
+
+The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically.
+
+The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected.
+
+They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten.
+
+This must be asserted explicitly each time.
+
+### The innermost loop
+
+== The header is larger than the parent header (Example 3 - Convolution)==
+
+The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop):
+
+```why3
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+```
+
+The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct:
+
+```why3
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ };
+
+ assert {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ };
+```
+
+With these assertions in place, the proof of the innermost invariant succeeds.
+
+### The outer loops
+
+The preservation of the outer invariants still fails.
+
+To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant:
+
+```why3
+constant yy_hh : int
+
+H3 : 0 <=' yy_hh
+
+H2 : yy_hh <' (y_hh +' 1)
+
+constant yy_ww : int
+
+H1 : 0 <=' yy_ww
+
+H : yy_ww <' int32'int y_w
+
+constant coords : list int =
+ Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int))))
+
+constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank))
+
+------------------------------- Goal --------------------------------
+
+goal c_conv'vc :
+ (value_at (r.t_data) @ offset1 coords dims).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h)
+ (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)
+```
+
+Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant:
+
+```
+LoopInvariant2 :
+ forall yy_hh1:int.
+ 0 <=' yy_hh1 /\ yy_hh1 <' y_hh ->
+ (forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r1.t_data)
+ @ offset1
+ (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0
+ (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3))
+ nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w)
+ (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)))
+```
+
+At first glance, the hypothesis and the goal look nearly identical.
+
+However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`.
+
+The only relationship between `r` and `r1` available in the logical context is:
+
+```why3
+H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data)
+```
+
+Meanwhile, from the innermost loop invariant, the following hypothesis is also available:
+
+```
+LoopInvariant1 :
+ forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r.t_data)
+ @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h)
+ (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left))
+```
+
+The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices.
+
+This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop.
+
+The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant:
+
+```
+for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+
+```
+
+By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant.
+
+However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop.
+
+The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten.
+
+The same pattern of assertions resolves this:
+
+```why3
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ )
+ };
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ )
+ };
+```
+
+Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator.
+
+The completed file is available [here](./examples/conv.mlw).
+
+# Part 4 - Tips, Hints and Strategies
+
+==I did not review the below in detail==
+
+## 4.1. Scope Resolution
+
+When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors.
+
+Take a look at the following example:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { iter - i }
+ =
+ if i < iter then
+ 1.0 + summation (i + 1) iter
+ else
+ 0.0
+end
+```
+
+If you attempt to open the Why3 ide with the above code, you will get the following error:
+
+
+
+This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`.
+
+In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name.
+
+In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules.
+
+Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope.
+
+To solve the issue we will have to specify the scope operations on the `Int` domain:
+
+
+$$(iter - i) \rightarrow Int.(iter - i)$$
+
+$$i < iter \rightarrow Int.(i < iter)$$
+
+$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$
+
+The corrections applied to the code above are the following:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { Int.(iter - i) }
+ =
+ if Int.(i < iter) then
+ 1.0 + summation Int.(i + 1) iter
+ else
+ 0.0
+end
+```
+
+## 4.2. IDE Transformations and Prover Hints
+
+Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals.
+
+Below are techniques we have found useful in practice.
+
+The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE.
+
+---
+
+### Lemma / Axiom Instantiation
+
+When an axiom or lemma is stated universally, for example:
+
+```why3
+axiom test:
+ forall x: int. P(x) -> Q(x)
+```
+
+The automated prover may either fail or take significantly longer to apply it in a specific context.
+
+Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful.
+
+**Example — Mean Value Theorem:**
+
+Consider the following axiom:
+
+```why3
+(* Lagrange Mean Value Theorem *)
+axiom mean_value_theorem_sigmoid:
+ forall x y: real.
+ exists c: real.
+ (x <= c <= y \/ y <= c <= x) /\
+ sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y)
+```
+
+And the following lemma to be proved:
+
+```why3
+lemma lipschitz_sigmoid:
+ forall x y: real.
+ abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y)
+```
+
+To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem.
+
+Instantiating the axiom with the specific variables `x` and `y` greatly helps.
+
+**IDE instantiation syntax:**
+
+```
+instantiate
+```
+
+If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step.
+
+The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it.
+
+This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE.
+
+---
+
+### Instantiation via Lemma Functions
+
+Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved.
+
+This is the preferred approach, as it keeps the proof self-contained and reproducible.
+
+**Example — `valid_bounds_2` lemma function:**
+
+The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions:
+
+```why3
+let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int)
+ requires { p <= q }
+ requires { valid_range ks p q }
+ requires { valid_range ds p q }
+ requires { pdim ds p q }
+ requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i }
+ ensures { Range.valid (islice ks p q) (islice ds p q) }
+ variant { q - p }
+ = if p < q then
+ begin
+ assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) };
+ assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) };
+ valid_bounds_2 ks ds (p+1) q
+ end
+```
+
+Now consider the `dot_product` function, which allocates coordinate arrays at runtime:
+
+```why3
+let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) =
+ requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter }
+ requires { a.t_rank = b.t_rank = 2 }
+ requires { iter >= 0 }
+ requires { row >= 0 /\ row < value_at a.t_dims 0 }
+ requires { col >= 0 /\ col < value_at b.t_dims 1 }
+ (* ... *)
+ let ref sum = (f32 0.0) in
+ let ref flag = False in
+ let a_coords_array = malloc (to_uint32 2) in
+ let b_coords_array = malloc (to_uint32 2) in
+ if is_not_null a_coords_array && is_not_null b_coords_array then begin
+ flag <- True;
+ for i = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) row col 0 i }
+ set_ofs a_coords_array 0 row;
+ set_ofs a_coords_array 1 i;
+ set_ofs b_coords_array 0 i;
+ set_ofs b_coords_array 1 col;
+
+ ghost valid_bounds_2 a_coords_array a.t_dims 0 2;
+ (* Rest of code ... *)
+```
+
+Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own.
+
+Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue.
+
+---
+
+### Function Unfolding
+
+When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle.
+
+Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE.
+
+**`unfold`** expands a specific function by its definition at the point where it appears in the goal.
+
+To use `unfold` transformation, enter the following command in the **"Type command here"** field:
+```
+unfold
+```
+
+Before applying `unfold`:
+
+
+
+After applying `unfold`:
+
+
+
+**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously.
+
+It requires no arguments:
+
+```
+compute_in_goal
+```
+
+Result after applying `compute_in_goal`:
+
+
+
+
+---
+
+## 4.3. How to Debug
+
+"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal.
+
+There are two common root causes:
+
+1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated.
+
+2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces.
+
+The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap.
+
+### Reading the Logical Context
+
+Consider the following goal:
+
+```
+------------------------------- Goal --------------------------------
+
+goal w_cools_calculate'vc :
+ to_extended_real max_value =
+ w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b)
+ (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w)
+ (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom)
+ (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w)
+```
+
+In the logical context, among other hypotheses, we have:
+
+```
+constant x_val : float
+
+constant aux_flag : bool
+
+Ensures4 :
+ aux_flag = True ->
+ coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top)
+ (to_int2 pad_left) = to_extended_real x_val
+
+constant ww1 : int321
+
+constant ww : int = int32'int ww1
+
+H31 : 0 <=' ww
+
+H30 : ww <=' int32'int o2
+
+LoopInvariant :
+ to_extended_real max_value1 =
+ w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c)
+ (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h)
+ (to_int2 str_w)
+
+H1 :
+ w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0
+ (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h)
+ (int32'int dil_w) (int32'int pad_top) (int32'int pad_left)
+ (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h)
+ (int32'int str_w) =
+ max_extended_real
+ (coords_from_X_p (tensor x)
+ (Cons (int32'int b)
+ (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int)))))
+ (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b)
+ ...)
+
+Ensures :
+ to_extended_real (max max_value1 x_val) =
+ max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+
+constant max_value : float
+
+H : max_value = max max_value1 x_val
+```
+
+The logical context is sufficient to prove the goal. Here is the reasoning:
+
+**Left-hand side of the goal.**
+
+From `H`, we know `max_value = max max_value1 x_val`, so:
+
+```
+to_extended_real max_value = to_extended_real (max max_value1 x_val)
+```
+
+Applying `Ensures`:
+
+```
+= max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+```
+
+**Right-hand side of the goal.**
+
+Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`:
+
+```
+w_cools_calculate ... (ww +' 1) ...
+ = max_extended_real
+ (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...)
+```
+
+From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`.
+
+For the first argument, `Ensures4` tells us (given `aux_flag = True`) that:
+
+```
+coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left)
+ = to_extended_real x_val
+```
+
+So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity).
+
+However, Why3 cannot discharge the goal automatically.
+
+**Why Why3 doesn't make the proof**
+
+The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor:
+
+```
+coords_from_X_p (tensor x)
+ (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil))))
+ (int32'int pad_top) (int32'int pad_left)
+```
+
+whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`.
+
+These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own.
+
+The connection must be made explicit with an intermediate assertion:
+
+```why3
+assert { ivector x_p_coords_array 4 =
+ Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) };
+```
+
+This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through.
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-jlf.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-jlf.md
new file mode 100644
index 00000000..b02c3ffa
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-jlf.md
@@ -0,0 +1,1597 @@
+# SONNX — Formalization Guidelines
+
+**A structured guide for writing formal specifications of SONNX operators in Why3**
+
+---
+
+## Table of Contents
+
+- [Part 1 — Formalization Styles](#part-1--formalization-styles)
+ - [1.1 — Abstract Formalization](#11-abstract-formalization)
+ - [1.2 — Concrete Formalization](#12-concrete-formalization)
+ - [1.3 — Link between the two levels](#13-link-between-the-two-levels)
+- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization)
+ - [2.1 — Module Structure](#21-module-structure)
+ - [2.2 — Function Declarations](#22-function-declarations)
+ - [2.3 — Contracts on the Main Function](#23-contracts-on-the-main-function)
+ - [2.4 — Data Function Pattern](#24-data-function-pattern)
+ - [2.5 — Operator tensor types](#25-operator-tensor-types)
+- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization)
+ - [3.1 — Module Structure](#31-module-structure)
+ - [3.2 — Auxiliary helper functions](#32-auxiliary-helper-functions)
+ - [3.3 — Main operator function](#33-main-operator-function)
+ - [3.4 — Invariants](#34-invariants)
+- [Part 4 — Tips, Hints and Strategies](#part-4---tips-hints-and-strategies)
+ - [4.1 — Scope Resolution](#41-scope-resolution)
+ - [4.2 — IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints)
+ - [4.3 — How to Debug](#43-how-to-debug)
+
+
+
+
+
+---
+
+
+> (Jean-Loup) I think that the use of Why3 shall be recomended from the start and that some usefull links shall be provided for readers not familiar with Why3.
+
+# Part 1 — Formalization Styles
+
+In SONNX, every operator is formalized at **two distinct levels**.
+
+> (Jean-Loup) "is" or "must be" or "should be"?
+>
+> (Jean-Loup) To name the levels ": Abstract formalization and concrete formalization."
+
+Each level serves a different purpose and follows a different style.
+
+Understanding both is essential before writing any specification.
+
+## 1.1 Abstract Formalization
+
+At this level the operator must be specified based only on its behavior and **mathematical semantics**.
+
+It should the **as close as possible transalation** of the mathematical definition of the operator - **presented in the informal specification**, being therefore easy to read and understand.
+
+No implementation details should be present at this level, which means that the **tensor' representation** should **mimic the mathematical definition of a tensor** (also similar to the ONNX tensor concept) - a multidimensional array - in which each entry is accessed by its coordinates.
+
+### Tensor
+
+- **Type**: At this level tensors are said to be **polymorphic**.
+ This means that the specifier should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types).
+
+ > (Jean-Loup) I have no idea of what a specifier is. Depending on the targeted reader the term should be defined.
+
+- **Representation**: Tensors are represented as structures containing the following records:
+
+ - `dims` - List of integers representing the shape
+
+ - `data` - A map from coordinates to values
+
+ > (Jean-Loup) May be valid coordinates or in-bounds coordinates instead of just coordinates. "coordinates" seems to be equal to "tensor index". If it is the case, a link to tensor index should be added.
+
+ - `background` - The default value for out-of-bounds coordinates
+
+- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representatio:
+
+ > (Jean-Loup) "representatio" <- "representation"
+
+ 1. **Positive Dimensions**
+
+ All dimensions in the `dims` list of a tensor must be positive integers.
+ From our point of view this will be updated to support **non-negative integers**, therefore allowing for tensors with null dimensions. [***Work in Progress***]
+
+ **Definition:**
+
+ - Predicate `positive`
+
+ ```why3
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+ ```
+ - `invariant { positive dims }`
+
+ > (Jean-Loup) Indicate this is Why3 code in the text. Looking at https://why3.gitlabpages.inria.fr/why3/syntaxref.html , I can't find a definition of Cons and Nil (Even if I intuitively understand...). There is only list and forest examples using Cons and Nil. Are Cons and Nil defined by other predicates? May be the begining of the guidelines should indicate that Why3 should be used and that the reader should first be familiarized with Why3.
+
+ 2. **Valid values**
+
+ Coordinates are either valid for the tensor shape or the value mapped by those coordinates is the `background` value.
+
+ **Definition:**
+
+ - Predicate `valid`
+
+ ```why3
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+ ```
+ - `invariant { forall k. valid k dims \/ data k = background }`
+
+
+
+ These invariants are extremely important as they will sometimes define how the operator should be formalized.
+
+ For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant.
+
+ > (Jean-Loup) Are the predicates positive and valid in a Why3 library? If it is the case, inclusion of the library should be recomended to avoid copy errors.
+
+### Specification Style
+
+Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output).
+
+Looping constructs are forbidden at this level, and instead, **recursive functions** and **mathematical constructs** should be used to define the behavior of the operator.
+
+> (Jean-Loup) The concept of mathematical construct should be defined.
+
+### Importance of this level
+
+It establishes the logical and mathematical specification of the operator, which serves as the **source of truth** for correctness.
+
+It also provides the perfect link between the informal specification and the formalization.
+
+## 1.2 Concrete Formalization
+
+The concrete level describes the **imperative implementation** that will be extracted to C code.
+
+It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory.
+
+### Tensor
+
+- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc..., and the operator should be defined specifically for that type.
+
+- **Representation**: Tensors are represented as pointers to structures that contain:
+
+ - `t_rank`: Number of dimensions
+
+ - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array
+
+ - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...)
+
+
+
+Note that, unlike the previous level, here the tensors are defined based on a concrete target representation - tensors are essentially flat arrays in memory.
+
+> (Jean-Loup) I guess it depends on what is meant by "concrete representation". Asking to ChatGPT the first answer is "yes lists and maps are concrete data structures". May be it should be "target C representation" or "C built in concrete representation".
+
+Moreover, there is no `background` value in this representation - **tensors entries must be accessed only in valid coordinates** (i.e., coordinates that are within the bounds of the tensor shape).
+
+### Specification Style
+
+Throughout this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation.
+
+### Importance of this level
+
+It provides the necessary details to extract executable C code and reason about memory, bounds, and machine integers.
+
+> (Jean-Loup) I think the information in sub-sections "Importance of this level" for abstract and concrete representations should be given at the begining, i.e. before 1.1.
+
+## 1.3 Link between the two levels
+
+### Why is it important to have two levels?
+
+- We need somehow to express that the **concrete implementation** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**.
+
+- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification.
+
+- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes.
+
+- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification.
+
+The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement:
+
+
+
+
+
+
+
+The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**.
+
+Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections.
+
+---
+
+
+# Part 2 — Guidelines for Abstract Formalization
+
+## 2.1. Module Structure
+
+Each abstract module should follow this general structure:
+
+
+```
+module OP
+ (* 1. Imports *)
+ (* 2. Auxiliary predicates *)
+ (* 3. Auxiliary helper functions *)
+ (* 4. Dimension-computing function *)
+ (* 5. Data-computing function *)
+ (* 6. Main operator function *)
+end
+```
+
+Neither all the above sections are mandatory nor its order is fixed, however, it is recommended to follow this structure as much as possible for consistency and readability across different operator specifications.
+
+On the other hand, **4**, **5** and **6** are usually written in such order as each one of them depends on the previous one.
+
+
+## 2.2 Function Declarations
+
+Why3 supports several ways to declare functions, each with different purposes and objectives.
+
+We can have any of the following function signatures:
+
+- `function`
+
+- `let`
+
+- `let function`
+
+- `let ghost function`
+
+- `let rec function`
+
+- `let rec ghost function`
+
+- more information on this in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html)
+
+Ideally, according to Loïc's proposal, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used.
+
+> (Jean-Loup) To remove "according to Loïc's proposal". May be describe shortly the proposal.
+>
+> (Jean-Loup) I don't understand why an abstract formalization of a specification cannot be a predicate using "Cons" + an invariant.
+
+However, this happens to be not always possible.
+
+### 2.2.1 Termination and Variants
+
+In order to understand that have a look at the following module which computes the unit summation in the range $[0, n-1]$.
+
+```why3
+module OPSummation
+ use int.Int
+
+ function summation (i : int) (iter : int) : int =
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+end
+```
+In this case, if you attempt to open the why3 ide the following error will be shown:
+
+
+
+In fact, `functions` are too weak to successfully prove recursion termination and therefore we need to help them prove termination.
+
+To do so, we have to introduce a **variant** (some expression that repeatedly decreases with each recursive call).
+
+However, `function` **does not support contracts** and therefore **variants** are **forbidden**, so we need to switch to `let rec function` and provide the variant there.
+
+```why3
+ let rec function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+end
+```
+
+> (Jean-Loup) In the why3 Langage Reference it is written "Recursive program functions must be defined using let rec." and some examples don't use the keyword "function". Is it possible to have only "let rec summation"? Is there a difference between "let rec" and "let rec function"?
+
+### 2.2.2 Verification Conditions and Requires Clauses
+
+Note that, functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**.
+
+Changing the signature from `function` to `let rec function` will, on the other hand, generate such verification conditions.
+
+That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions.
+
+To compare such different behaviors, check the examples below:
+
+**Explicitly need for requires clauses**
+
+```why3
+ (** Helper function to extract element from list at given position **)
+
+ let rec function get_dim (dims : list int) (idx : int) : int
+ requires { 0 <= idx < length dims }
+ variant { dims }
+ = match dims with
+ | Nil -> 0 (* should not happen due to precondition *)
+ | Cons h t -> if idx = 0 then h else get_dim t (idx - 1)
+ end
+```
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ =
+ if Int.(i >= axis) then
+ 1
+ else
+ get_dim x.dims i * calculate_dY0 x axis Int.(i + 1)
+```
+
+This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list.
+
+Consequently, verification conditions will be raised as is depicted in the picture below:
+
+
+
+In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions.
+
+Therefore, the code above should be adapted to include such contracts:
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ (*Need this requires because of get_dim requires*)
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ variant { axis - i }
+ =
+ (...)
+```
+
+
+
+**No need for requires clauses**
+
+```why3
+ function calculate_dims (x : tensor real) (axis : int) : list int
+ =
+ let dY0 = calculate_dY0 x axis 0 in
+ let dY1 = calculate_dY1 x axis axis in
+ Cons dY0 (Cons dY1 Nil)
+```
+
+
+The code written above will not generate any verification conditions, even if the functions called inside it explicitly need **requires clauses**.
+
+Therefore, no verification condtions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left pannel in the picture above).
+
+### 2.2.3 TypeInvariant Lemmas
+
+When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form.
+
+As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties.
+
+The `calculate_dY0` function defined above is a concrete example of this issue.
+
+The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive:
+
+```why3
+ let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int)
+ requires { positive x.dims }
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ ensures { 0 < calculate_dY0 x axis i }
+ variant { axis - i }
+ =
+ if i >= axis then
+ ()
+ else
+ let d = get_dim x.dims i in
+ calculate_dY0_positive x axis (i + 1)
+```
+
+The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints).
+
+### 2.2.4 Main Operator Function
+
+Moreover, the main operator function cannot be declared as `function` because it needs to have contracts (`requires` / `ensures`) and `function` does not support contracts.
+
+Therefore, the main operator function should be declared as `let ghost function` with full contracts.
+
+```why3
+module OPSummation
+ use int.Int
+
+ let rec function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+
+ let ghost function unitSum (n: int) : int =
+ requires { n >= 0 }
+ ensures { result = n }
+ summation 0 n
+end
+```
+
+### 2.2.5 Guidelines
+
+| Guideline | Details |
+|:---|:---|
+| Use `function` for total functions and whenever it is possible! | A `function` is a logical function. |
+| Use `let rec function` only when recursion needs a variant. | If Why3 cannot prove termination of a `function`, switch to `let rec function` and provide a `variant` clause. |
+| Use `let ghost function` for the **main operator**. | The main function should be a `let ghost function` with full contracts (`requires` / `ensures`). |
+
+### Examples
+
+Besides the summation example above, here are some examples of such formalization styles:
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+## 2.3. Contracts on the Main Function
+
+Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec`.
+
+The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.
+
+On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions:
+
+| Postcondition | Purpose |
+|:---|:---|
+| `ensures { result.dims = ... }` | Specifies the **output shape** |
+| `ensures { result.data = ... }` | Specifies the **output data** (as a map) |
+| `ensures { result.background = ... }` | Specifies the **background value** |
+
+### Example
+
+```why3
+let ghost function opclip (x l m : tensor real) : tensor real
+ requires { is_scalar_tensor l }
+ requires { is_scalar_tensor m }
+ ensures { result.dims = x.dims } (* ← shape *)
+ ensures { result.data = dclip x l m } (* ← data *)
+ ensures { result.background = x.background } (* ← background *)
+=
+ { dims = x.dims; data = dclip x l m; background = x.background }
+```
+
+## 2.4. Data Function Pattern
+
+Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern:
+
+
+### Anonymous function declaration
+
+Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics.
+
+We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function.
+
+Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**.
+
+That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal).
+
+```why3
+let ghost function d (x: tensor a') (output_shape: list int) : data a'
+=
+fun ks ->
+ if valid ks output_shape then
+ ...
+ else
+
+```
+
+The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics.
+
+The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics.
+
+This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor.
+
+### Recursive dimensions constructs
+
+An alternative way would be to define as much recursive helper functions as the number of dimensions and then define the data function based on these recursive functions.
+
+This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof.
+
+As a standard we will stick with the `anonymous function` definition.
+
+### Examples
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+
+## 2.5 Operator tensor types
+
+Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level.
+
+For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors.
+
+However, there are operator whose abstract tensor type can be completely polymorphic.
+
+Take a look ate the example below:
+
+```why3
+module OPWhere
+ use tensor.Tensor
+
+ function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+end
+```
+
+
+Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`.
+
+Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**.
+
+Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic.
+
+So whenever possible one should resort to **polymorphic** abstract datatypes.
+
+This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others.
+
+
+# Part 3 — Guidelines for Concrete Formalization
+
+## 3.1. Module Structure
+
+The concrete module should follow this general structure:
+
+```why3
+ module COP
+ (* 1. Imports *)
+ (* 2. Auxiliary helper functions *)
+ (* 3. Main operator function *)
+ end
+```
+
+### 3.2 Auxiliary helper functions
+
+All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well.
+
+Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ...
+
+Under this context, translation means:
+
+- Defining the function in an imperative style
+
+- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping)
+
+To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw):
+
+```why3
+ (* Abstract level *)
+ function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real
+ =
+ let b = get_dim x_p_coords 0 in
+ let c = get_dim x_p_coords 1 in
+ let n = (get_dim x_p_coords 2) - pad_top in
+ let m = (get_dim x_p_coords 3) - pad_left in
+ let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in
+ if valid x_coords x.dims then
+ x.data x_coords
+ else
+ 0.0
+```
+
+```why3
+ (* Concrete level *)
+ let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) =
+ requires { valid_tensor x }
+ requires { x.t_rank = 4 }
+ requires { valid_range x_p_coords 0 4 }
+ requires { writable x_p_coords }
+ requires { in_bounds (value_at x_p_coords 2 - pad_top) }
+ requires { in_bounds (value_at x_p_coords 3 - pad_left) }
+
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+
+ let ref flag = False in
+ let b = x_p_coords[0] in
+ let c = x_p_coords[1] in
+ let n = (x_p_coords[2]) - pad_top in
+ let m = (x_p_coords[3]) - pad_left in
+ let x_coords_array = malloc (to_uint32 4) in
+ if is_not_null x_coords_array then begin
+ flag <- True;
+ x_coords_array[0] <- b;
+ x_coords_array[1] <- c;
+ x_coords_array[2] <- n;
+ x_coords_array[3] <- m;
+
+ assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ;
+
+ let x_coords = coffset x_coords_array x.t_dims x.t_rank in
+ if x_coords >= 0 then begin
+ (x.t_data[x_coords], flag)
+ end
+ else begin
+ ((f32 0.0), flag)
+ end
+ end
+ else begin
+ flag <- False;
+ ((f32 0.0), flag)
+ end
+```
+
+These two functions perform the same exact computation.
+
+To better understand how these two relate one can compare both specifications:
+
+**Coordinates**
+
+- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator.
+
+ - These functions are equivalent and such equivalence proof is already available in the tensors library
+
+- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps:
+
+ - **Firstly**, the appropriate space is allocated with the `malloc` function
+
+ - **If the malloc is successful**, we then set each index of the array with the appropriate value.
+
+ - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause.
+
+**Computation**
+
+- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0.
+
+- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0.
+
+- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels.
+
+**Refinement mapping**
+
+- The refinement mapping relation is expressed with an ensures. In this function this realtion is expressed by:
+
+ ```
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+ ```
+
+- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels.
+
+- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created.
+
+### 3.3 Main operator function
+
+This is naturally the most important function and the one whith more details to be taken into account.
+
+#### 3.3.1 Contracts
+
+The **main operator function** must include contracts at both levels.
+
+**Requires**
+
+- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.
+
+- On the **concrete level** we should have preconditions for:
+
+ 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor**
+
+ 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**.
+
+ 3. All the **preconditions** already present at the abstract level. The **concrete** will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change).
+
+**Ensures**
+
+- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**:
+
+- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form:
+
+ - `ensures { tensor result = (tensor x) ... }`
+
+The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw).
+
+- **Concrete level**
+
+ ```why3
+ let flatten (x r : ctensor) (axis: int32)
+ (* Requires*)
+ (* 1 - Valid tensors *)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+
+ (* 2 - Shape match *)
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+
+ (* 3 - Informal Spec *)
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+
+ (* Ensures *)
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m - 1 do
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k }
+ r.t_data[i] <- x.t_data[i]
+ done;
+
+ assert { tensor r == flatten (tensor x) (to_int axis) }
+ ```
+
+**Requires**
+
+Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors.
+
+Expressing the **shape match** on the other hand is not that trivial.
+There are multiple ways of doing this match.
+The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same.
+To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`.
+
+- `tensor`: Converts a **ctensor** into an **abstract tensor**
+
+- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int`
+
+- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that´s why such convertion is important.
+
+ - Takes as inputs: the **pointer** to convert and the **desired size** of conversion
+
+For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level.
+
+Translating the preconditions from the abstract level to the concrete level can be done in two different ways:
+
+ 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions
+
+ 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure.
+
+In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provavle than the other, so it is up to the specifier to choose which one to use. Having said so, the second **precondition** could also have been expressed through abstract terms:
+
+- `requires { size (tensor x) = size (tensor r) }`
+
+To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate wether or not the verification conditions generated are automatically proved.
+
+This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them.
+
+Check the following example:
+
+```why3
+ let flatten (x r : ctensor) (axis: int32)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let ghost _ = flatten (tensor x) (to_int axis) in
+ (...)
+```
+
+This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator.
+
+The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved
+
+
+
+If one had forgotten to include this precondition:
+
+```
+requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+```
+
+then, the strategy proposed above would have generated the following verification condition which is not automatically proved:
+
+
+
+By inspecting the task menu, the specifier would be able to understand which precondition is missing and add it to the specification.
+
+
+
+**Ensures**
+
+Our final goal is to prove the **ensures** clause stating the refinement between both levels.
+
+Usually the following pipeline is followed to do so:
+
+$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$
+
+Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof.
+
+### 3.4 Invariants
+
+**Nested Loop Invariants**
+
+Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**.
+
+A loop invariant must satisfy two proof obligations:
+
+1. **Initialization** — the invariant holds for the first iteration.
+
+2. **Preservation** — if the continually holds for all iterations up to the last one, inclusive.
+
+When loops are nested, these obligations form a dependency chain:
+
+- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant
+
+- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant.
+
+The following example illustrates this:
+
+```why3
+let ref sum = (f32 0.0) in
+(* ... *)
+for i = 0 to rows - 1 do
+ invariant { sum = 0.0 }
+ for j = 0 to cols - 1 do
+ invariant { sum = 0.0 }
+ for k = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) i j 0 k }
+ (* ... *)
+ done;
+ sum <- 0.0;
+ done;
+done;
+```
+
+The innermost loop (`k`) accumulates a dot product in `sum`.
+
+For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore:
+
+$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$
+
+In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$.
+
+### Loop Invariants for Proving Data Refinement
+
+**Example 1 - Unit Summation**
+
+To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range.
+
+The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one.
+
+The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove:
+
+
+
+The goal and the respective logical context are below:
+
+
+
+What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration.
+
+To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level.
+
+Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1:
+
+```why3
+ let rec lemma summation_lemma (i iter : int)
+ requires { i < iter }
+ ensures { 1 + summation (i + 1) iter = summation i iter }
+ variant { iter - i }
+ =
+ if i >= iter then
+ ()
+ else
+ summation_lemma (i + 1) iter
+```
+
+After adding this, the invariant preservation is easily proved.
+
+This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation.
+
+**Example 2 - Dot Product**
+
+The example presented above is a very simple one, but the same strategy can be applied to much more complex operators.
+
+Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator.
+
+The file is available [here](./examples/dot_product.mlw).
+
+Once again the proof is not achievable and the task menu displays the following:
+
+
+
+To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma.
+
+The strategy is essentially the same:
+
+```why3
+ let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int)
+ requires { Int.(i <= k < iter) }
+ variant { Int.(k - i) }
+ ensures {
+ let a_val = a.data (Cons row (Cons k Nil)) in
+ let b_val = b.data (Cons k (Cons col Nil)) in
+ dot_product_rec a b row col i Int.(k + 1) =
+ Real.(dot_product_rec a b row col i k + a_val * b_val)
+ }
+=
+ if i < k then
+ Real.(dot_product_lemma a b row col (i + 1) k iter)
+ else
+ ()
+```
+
+This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma.
+
+
+
+To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables.
+
+We will do so by introducing a ghost block:
+
+```why3
+ghost begin
+ dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter)
+end
+```
+
+After this, the invariant preservation is easily proved.
+
+More information regarding lemmas intantiation in [4.2](#42-ide-transformations-and-prover-hints).
+
+**Convolution**
+
+The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops.
+
+Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates.
+
+Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above.
+
+The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration.
+
+This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate.
+
+The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement.
+
+Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor.
+
+Below are the four loop invariants used in the concrete Conv implementation:
+
+```why3
+ for nn = 0 to n - 1 do
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ r_coords_array[0] <- nn;
+ for mm = 0 to m - 1 do
+ invariant { value_at r_coords_array 0 = nn }
+ invariant {
+ forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ }
+ r_coords_array[1] <- mm;
+ for y_hh = 0 to y_h - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+
+ }
+ r_coords_array[2] <- y_hh;
+ for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+```
+
+The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically.
+
+The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected.
+
+They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten.
+
+This must be asserted explicitly each time.
+
+### The innermost loop
+
+The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop):
+
+```why3
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+```
+
+The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct:
+
+```why3
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ };
+
+ assert {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ };
+```
+
+With these assertions in place, the proof of the innermost invariant succeeds.
+
+### The outer loops
+
+The preservation of the outer invariants still fails.
+
+To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant:
+
+```why3
+constant yy_hh : int
+
+H3 : 0 <=' yy_hh
+
+H2 : yy_hh <' (y_hh +' 1)
+
+constant yy_ww : int
+
+H1 : 0 <=' yy_ww
+
+H : yy_ww <' int32'int y_w
+
+constant coords : list int =
+ Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int))))
+
+constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank))
+
+------------------------------- Goal --------------------------------
+
+goal c_conv'vc :
+ (value_at (r.t_data) @ offset1 coords dims).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h)
+ (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)
+```
+
+Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant:
+
+```
+LoopInvariant2 :
+ forall yy_hh1:int.
+ 0 <=' yy_hh1 /\ yy_hh1 <' y_hh ->
+ (forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r1.t_data)
+ @ offset1
+ (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0
+ (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3))
+ nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w)
+ (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)))
+```
+
+At first glance, the hypothesis and the goal look nearly identical.
+
+However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`.
+
+The only relationship between `r` and `r1` available in the logical context is:
+
+```why3
+H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data)
+```
+
+Meanwhile, from the innermost loop invariant, the following hypothesis is also available:
+
+```
+LoopInvariant1 :
+ forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r.t_data)
+ @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h)
+ (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left))
+```
+
+The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices.
+
+This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop.
+
+The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant:
+
+```
+for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+
+```
+
+By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant.
+
+However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop.
+
+The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten.
+
+The same pattern of assertions resolves this:
+
+```why3
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ )
+ };
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ )
+ };
+```
+
+Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator.
+
+The completed file is available [here](./examples/conv.mlw).
+
+# Part 4 - Tips, Hints and Strategies
+
+## 4.1. Scope Resolution
+
+When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors.
+
+Take a look at the following example:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { iter - i }
+ =
+ if i < iter then
+ 1.0 + summation (i + 1) iter
+ else
+ 0.0
+end
+```
+
+If you attempt to open the Why3 ide with the above code, you will get the following error:
+
+
+
+This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`.
+
+In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name.
+
+In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules.
+
+Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope.
+
+To solve the issue we will have to specify the scope operations on the `Int` domain:
+
+
+$$(iter - i) \rightarrow Int.(iter - i)$$
+
+$$i < iter \rightarrow Int.(i < iter)$$
+
+$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$
+
+The corrections applied to the code above are the following:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { Int.(iter - i) }
+ =
+ if Int.(i < iter) then
+ 1.0 + summation Int.(i + 1) iter
+ else
+ 0.0
+end
+```
+
+## 4.2. IDE Transformations and Prover Hints
+
+Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals.
+
+Below are techniques we have found useful in practice.
+
+The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE.
+
+---
+
+### Lemma / Axiom Instantiation
+
+When an axiom or lemma is stated universally, for example:
+
+```why3
+axiom test:
+ forall x: int. P(x) -> Q(x)
+```
+
+The automated prover may either fail or take significantly longer to apply it in a specific context.
+
+Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful.
+
+**Example — Mean Value Theorem:**
+
+Consider the following axiom:
+
+```why3
+(* Lagrange Mean Value Theorem *)
+axiom mean_value_theorem_sigmoid:
+ forall x y: real.
+ exists c: real.
+ (x <= c <= y \/ y <= c <= x) /\
+ sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y)
+```
+
+And the following lemma to be proved:
+
+```why3
+lemma lipschitz_sigmoid:
+ forall x y: real.
+ abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y)
+```
+
+To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem.
+
+Instantiating the axiom with the specific variables `x` and `y` greatly helps.
+
+**IDE instantiation syntax:**
+
+```
+instantiate
+```
+
+If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step.
+
+The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it.
+
+This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE.
+
+---
+
+### Instantiation via Lemma Functions
+
+Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved.
+
+This is the preferred approach, as it keeps the proof self-contained and reproducible.
+
+**Example — `valid_bounds_2` lemma function:**
+
+The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions:
+
+```why3
+let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int)
+ requires { p <= q }
+ requires { valid_range ks p q }
+ requires { valid_range ds p q }
+ requires { pdim ds p q }
+ requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i }
+ ensures { Range.valid (islice ks p q) (islice ds p q) }
+ variant { q - p }
+ = if p < q then
+ begin
+ assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) };
+ assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) };
+ valid_bounds_2 ks ds (p+1) q
+ end
+```
+
+Now consider the `dot_product` function, which allocates coordinate arrays at runtime:
+
+```why3
+let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) =
+ requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter }
+ requires { a.t_rank = b.t_rank = 2 }
+ requires { iter >= 0 }
+ requires { row >= 0 /\ row < value_at a.t_dims 0 }
+ requires { col >= 0 /\ col < value_at b.t_dims 1 }
+ (* ... *)
+ let ref sum = (f32 0.0) in
+ let ref flag = False in
+ let a_coords_array = malloc (to_uint32 2) in
+ let b_coords_array = malloc (to_uint32 2) in
+ if is_not_null a_coords_array && is_not_null b_coords_array then begin
+ flag <- True;
+ for i = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) row col 0 i }
+ set_ofs a_coords_array 0 row;
+ set_ofs a_coords_array 1 i;
+ set_ofs b_coords_array 0 i;
+ set_ofs b_coords_array 1 col;
+
+ ghost valid_bounds_2 a_coords_array a.t_dims 0 2;
+ (* Rest of code ... *)
+```
+
+Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own.
+
+Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue.
+
+---
+
+### Function Unfolding
+
+When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle.
+
+Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE.
+
+**`unfold`** expands a specific function by its definition at the point where it appears in the goal.
+
+To use `unfold` transformation, enter the following command in the **"Type command here"** field:
+```
+unfold
+```
+
+Before applying `unfold`:
+
+
+
+After applying `unfold`:
+
+
+
+**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously.
+
+It requires no arguments:
+
+```
+compute_in_goal
+```
+
+Result after applying `compute_in_goal`:
+
+
+
+
+---
+
+## 4.3. How to Debug
+
+"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal.
+
+There are two common root causes:
+
+1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated.
+
+2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces.
+
+The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap.
+
+### Reading the Logical Context
+
+Consider the following goal:
+
+```
+------------------------------- Goal --------------------------------
+
+goal w_cools_calculate'vc :
+ to_extended_real max_value =
+ w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b)
+ (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w)
+ (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom)
+ (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w)
+```
+
+In the logical context, among other hypotheses, we have:
+
+```
+constant x_val : float
+
+constant aux_flag : bool
+
+Ensures4 :
+ aux_flag = True ->
+ coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top)
+ (to_int2 pad_left) = to_extended_real x_val
+
+constant ww1 : int321
+
+constant ww : int = int32'int ww1
+
+H31 : 0 <=' ww
+
+H30 : ww <=' int32'int o2
+
+LoopInvariant :
+ to_extended_real max_value1 =
+ w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c)
+ (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h)
+ (to_int2 str_w)
+
+H1 :
+ w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0
+ (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h)
+ (int32'int dil_w) (int32'int pad_top) (int32'int pad_left)
+ (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h)
+ (int32'int str_w) =
+ max_extended_real
+ (coords_from_X_p (tensor x)
+ (Cons (int32'int b)
+ (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int)))))
+ (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b)
+ ...)
+
+Ensures :
+ to_extended_real (max max_value1 x_val) =
+ max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+
+constant max_value : float
+
+H : max_value = max max_value1 x_val
+```
+
+The logical context is sufficient to prove the goal. Here is the reasoning:
+
+**Left-hand side of the goal.**
+
+From `H`, we know `max_value = max max_value1 x_val`, so:
+
+```
+to_extended_real max_value = to_extended_real (max max_value1 x_val)
+```
+
+Applying `Ensures`:
+
+```
+= max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+```
+
+**Right-hand side of the goal.**
+
+Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`:
+
+```
+w_cools_calculate ... (ww +' 1) ...
+ = max_extended_real
+ (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...)
+```
+
+From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`.
+
+For the first argument, `Ensures4` tells us (given `aux_flag = True`) that:
+
+```
+coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left)
+ = to_extended_real x_val
+```
+
+So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity).
+
+However, Why3 cannot discharge the goal automatically.
+
+**Why Why3 doesn't make the proof**
+
+The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor:
+
+```
+coords_from_X_p (tensor x)
+ (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil))))
+ (int32'int pad_top) (int32'int pad_left)
+```
+
+whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`.
+
+These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own.
+
+The connection must be made explicit with an intermediate assertion:
+
+```why3
+assert { ivector x_p_coords_array 4 =
+ Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) };
+```
+
+This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through.
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines.md
new file mode 100644
index 00000000..d018ea89
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines.md
@@ -0,0 +1,1571 @@
+# SONNX — Formalization Guidelines
+
+A structured guide for writing formal specifications of SONNX operators in Why3.
+
+This guidelines do not cover the basics of Why3, so familiarity with its syntax, semantics and features is expected.
+
+---
+
+## Table of Contents
+
+- [Part 1 — Formalization Styles](#part-1--formalization-styles)
+ - [1.1 — Abstract Formalization](#11-abstract-formalization)
+ - [1.2 — Concrete Formalization](#12-concrete-formalization)
+ - [1.3 — Link between the two levels](#13-link-between-the-two-levels)
+- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization)
+ - [2.1 — Module Structure](#21-module-structure)
+ - [2.2 — Function Declarations](#22-function-declarations)
+ - [2.3 — Contracts on the Main Function](#23-contracts-on-the-main-function)
+ - [2.4 — Data Function Pattern](#24-data-function-pattern)
+ - [2.5 — Operator tensor types](#25-operator-tensor-types)
+- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization)
+ - [3.1 — Module Structure](#31-module-structure)
+ - [3.2 — Auxiliary helper functions](#32-auxiliary-helper-functions)
+ - [3.3 — Main operator function](#33-main-operator-function)
+ - [3.4 — Invariants](#34-invariants)
+- [Part 4 — Tips, Hints and Strategies](#part-4---tips-hints-and-strategies)
+ - [4.1 — Scope Resolution](#41-scope-resolution)
+ - [4.2 — IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints)
+ - [4.3 — How to Debug](#43-how-to-debug)
+
+
+
+
+
+---
+
+
+
+# Part 1 — Formalization Styles
+
+In SONNX, every operator must be formalized at two distinct levels: abstract formalization and concrete formalization.
+
+Each level serves a different purpose and follows a different style.
+
+We start from an abstract formalization — which captures the mathematical semantics of the operator and serves as the **source of truth** for correctness — and progressively refine it into a concrete formalization that is close enough to the target implementation to be automatically extracted into C code - reasoning about memory, bounds, and machine data types.
+
+Understanding both is essential before writing any specification.
+
+## 1.1 Abstract Formalization
+
+At this level the operator must be specified making no reference to any implementation detail, and therefore it should be **implementation-agnostic**.
+
+It shall be **traceable** to the **informal specification**, being easy to validate, first and easy to read and understand, second. Traceability must be enforced:
+
+- first by keeping the formal specification as close to the informal specification (e.g., stick to the mathematical specification given in the informal specification)
+
+- second by providing explicit link via `[tags]` to specific parts of the informal specification.
+
+No implementation details should be present at this level, which means that the specification of tensors shall remain as abstract as possible, focusing on their mathematical properties.
+
+### Tensors
+
+- **Type**: At this level tensors are specified as **polymorphic structures**, i.e., they are parameterized by the type of the values they contain.
+ This means that the specification author should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types).
+
+- **Representation**: Tensors are represented as structures containing the following records:
+
+ - `dims` - List of integers representing the shape
+
+ - `data` - A map from [indexes](../../../spec/informal/common/definitions.md) to values
+
+ - `background` - The default value for indexes out of the bounds defined by the shape
+
+ A "valid index" is an index compatible with the dimension of the tensor, i.e. the i-th components of the index is strictly positive and strictly less than the i-th dimension of the tensor
+
+ The data mapping is complete since it maps all possible indexes to values, but the value mapped by invalid indexes is the `background` value.
+
+- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representation:
+
+ 1. **Positive Dimensions**
+
+ All dimensions in the `dims` list of a tensor must be **strictly positive integers**.
+ This constraint may be relaxed in a lated version of the specification to support tensors with **null** dimensions. [***Work in Progress***]
+
+ - Predicate `positive`
+
+ ```why3
+ predicate positive (ds : list int) =
+ match ds with
+ | Nil -> true
+ | Cons d ds -> 0 < d /\ positive ds
+ end
+ ```
+ - `invariant { positive dims }`
+
+ 2. **Valid values**
+
+ Indexes are either valid for the tensor shape or the value mapped by those indexes is the `background` value.
+
+ - Predicate `valid`
+
+ ```why3
+ predicate valid (ks ds : list int) =
+ match ks , ds with
+ | Nil , Nil -> true
+ | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+ | _ -> false
+ end
+ ```
+ - `invariant { forall k. valid k dims \/ data k = background }`
+
+
+
+ These invariants are extremely important as they will sometimes define how the operator should be formalized.
+
+ For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant.
+
+ An alternative was to define the data through recursive definitions once the shape is already computed, but this approach is much more complex and usually requires auxiliary lemmas to help the proof.
+
+ The above predicates and invariants are available in the [tensors library](../../../spec/formal/common/libs/tensor/).
+
+### Specification Style
+
+Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output).
+
+Users should specify the operator based on mathematically and recursive definitions, avoiding loops and mutable state, increasing the **provability** and **ease of proof** of this specification.
+
+## 1.2 Concrete Formalization
+
+The concrete level describes the **imperative implementation** that will be extracted to C code.
+
+It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory.
+
+### Tensors
+
+- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc. and the operator should be defined specifically for that type.
+
+- **Representation**: Tensors are represented as pointers to structures that contain:
+
+ - `t_rank`: Number of dimensions
+
+ - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array
+
+ - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...)
+
+
+
+Note that, unlike the previous level, here the tensors are defined based on a target C representation - tensors are essentially flat arrays in memory.
+
+Moreover, there is no `background` value in this representation - **only valid indexes must be used to access values of tensors** (i.e. coordinates that are within the bounds of the tensor shape).
+
+### Specification Style
+
+In this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation.
+
+### Importance of this level
+
+It provides the necessary details to extract executable C code and reason about memory, bounds, and machine data types.
+
+## 1.3 Link between the two levels
+
+### Why is it important to have two levels?
+
+- We need somehow to express that the **concrete specification** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**.
+
+- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification.
+
+- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes.
+
+- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification.
+
+The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement:
+
+
+
+
+
+
+
+The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**.
+
+Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections.
+
+---
+
+
+# Part 2 — Guidelines for Abstract Formalization
+
+## 2.1. Module Structure
+
+Each abstract module should follow this general structure:
+
+
+```
+module OP
+ (* 1. Imports *)
+ (* 2. Auxiliary predicates *)
+ (* 3. Auxiliary helper functions *)
+ (* 4. Dimension-computing function *)
+ (* 5. Data-computing function *)
+ (* 6. Main operator function *)
+end
+```
+
+If any section is not needed, it can be left empty, but the overall structure must be maintained for consistency and readability across different operator specifications.
+
+## 2.2 Function Declarations
+
+Why3 supports several ways to declare functions, each with different purposes and objectives.
+
+In order to fully understand each one of this features please take into account that whyml supports **two different programming namespaces**, a **logical** and a **programming** one, each one of them built upon a different syntax and with different features. For instance both of them support **conjunctions** but while the logical one expresses it through $\land$, the programming one expresses it through the `&&` operator.
+
+
+We can have any of the following function signatures:
+
+- `function`: Belongs to the **logical namespace** and is used to define purely logical functions. Contracts are not supported.
+
+- `let`: Belongs to the **programming namespace** and defines a non recursive function.
+
+- `let rec`: Belongs to the **programming namespace** and defines a recursive function.
+
+- `let function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and allows them to be used at the logical namespace as well.
+
+- `let rec function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and allows them to be used at the logical namespace as well.
+
+- `let ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and **can only** be used at the **logical namespace**.
+
+- `let rec ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and **can only** be used at the **logical namespace**.
+
+- A precise definition of these constructs is given in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html)
+
+Ideally, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used.
+
+> Recall why this is considered as good practice.
+
+### 2.2.1 Termination and Variants
+
+It is not always possible to define all the necessary functions with the `function` construct, especially when we need to define recursive functions whose termination is not trivially provable by Why3.
+
+In order to understand that have a look at the following module which computes the unit summation in the range $[0, n-1]$. This might not be the most traditional way of computing a summation, although, it intentionally resembles the iterative way of computing a summation, starting from 0 up to $n-1$.
+
+```why3
+module OPSummation
+ use int.Int
+
+ function summation (i : int) (iter : int) : int =
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+end
+```
+In this case, if you attempt to open the why3 ide the following error will be shown:
+
+
+
+In fact, `functions` are not supposed to be used in recursive definitions, unless their termination is trivially provable by Why3 such as recursive calls over the tail of a list.
+
+Intead one should use a `let rec ghost function` declaration, which allows to define recursive functions with a **variant** (some expression that repeatedly decreases with each recursive call) that can only be used in the logical namespace.
+
+```why3
+ let rec ghost function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+```
+
+### 2.2.2 Verification Conditions and Requires Clauses
+
+Note that, pure logical functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**.
+
+Recalling to the [definitions](#functions_def) it is clear that `function` and `let rec ghost function` belong to different namespaces, although both of them can only be used at the logical namespace. As a consequence, since `let rec ghost function` is not a definition but an actual executable function, it generates verification conditions for all the functions called inside it that have preconditions.
+
+That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions.
+
+To compare such different behaviors, check the examples below:
+
+**Explicitly need for requires clauses**
+
+```why3
+ (** Helper function to extract element from list at given position **)
+
+ let rec function get_dim (dims : list int) (idx : int) : int
+ requires { 0 <= idx < length dims }
+ variant { dims }
+ = match dims with
+ | Nil -> 0 (* should not happen due to precondition *)
+ | Cons h t -> if idx = 0 then h else get_dim t (idx - 1)
+ end
+```
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ =
+ if Int.(i >= axis) then
+ 1
+ else
+ get_dim x.dims i * calculate_dY0 x axis Int.(i + 1)
+```
+
+This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list.
+
+Consequently, verification conditions will be generated as is depicted in the picture below:
+
+
+
+In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions.
+
+Therefore, the code above should be adapted to include such contracts:
+
+```why3
+ let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int
+ (*Need this requires because of get_dim requires*)
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ variant { axis - i }
+ =
+ (...)
+```
+
+
+
+**No need for requires clauses**
+
+```why3
+ function calculate_dims (x : tensor real) (axis : int) : list int
+ =
+ let dY0 = calculate_dY0 x axis 0 in
+ let dY1 = calculate_dY1 x axis axis in
+ Cons dY0 (Cons dY1 Nil)
+```
+
+
+The code written above will not generate any verification conditions, even if the functions referenced inside it explicitly need **requires clauses**.
+
+Therefore, no verification conditions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left panel in the picture above).
+
+### 2.2.3 TypeInvariant Lemmas
+
+When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form.
+
+As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties.
+
+The `calculate_dY0` function defined above is a concrete example of this issue.
+
+The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive:
+
+```why3
+ let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int)
+ requires { positive x.dims }
+ requires { 0 <= axis <= length x.dims }
+ requires { 0 <= i <= axis }
+ ensures { 0 < calculate_dY0 x axis i }
+ variant { axis - i }
+ =
+ if i >= axis then
+ ()
+ else
+ let d = get_dim x.dims i in
+ calculate_dY0_positive x axis (i + 1)
+```
+
+The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints).
+
+### 2.2.4 Main Operator Function
+
+To enforce traceability, as [previously mentioned](#traceability), the main operator function should capture the constraints present in the informal specification as preconditions (`requires`) while providing postconditions (`ensures`) for every record of the output tensor.
+
+To capture such contracts one needs to define a construct that belongs to the programming namespace. Moreover, since this function is only used to specification purposes it is not supposed to be used in any implementation context. To enforce that this function can only be used at the logical level sign it as **ghost**.
+
+Therefore, the main operator function should be declared as `let rec ghost function` with full contracts.
+
+```why3
+module OPSummation
+ use int.Int
+
+ let rec ghost function summation (i : int) (iter : int) : int =
+ variant { iter - i}
+ if i < iter then
+ 1 + summation (i + 1) iter
+ else
+ 0
+
+ let ghost function unitSum (n: int) : int =
+ requires { n >= 0 }
+ ensures { result = n }
+ summation 0 n
+end
+```
+
+### Examples
+
+Besides the summation example above, here are some examples of such formalization styles:
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+## 2.3. Contracts on the Main Function
+
+Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec ghost function`.
+
+The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.
+
+On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions:
+
+| Postcondition | Purpose |
+|:---|:---|
+| `ensures { result.dims = ... }` | Specifies the **output shape** |
+| `ensures { result.data = ... }` | Specifies the **output data** (as a map) |
+| `ensures { result.background = ... }` | Specifies the **background value** |
+
+### Example
+
+```why3
+let ghost function opclip (x l m : tensor real) : tensor real
+ requires { is_scalar_tensor l }
+ requires { is_scalar_tensor m }
+ ensures { result.dims = x.dims } (* ← shape *)
+ ensures { result.data = dclip x l m } (* ← data *)
+ ensures { result.background = x.background } (* ← background *)
+=
+ { dims = x.dims; data = dclip x l m; background = x.background }
+```
+
+## 2.4. Data Function Pattern
+
+Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern:
+
+
+### Anonymous function declaration
+
+Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics.
+
+We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function.
+
+Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**.
+
+That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal).
+
+```why3
+let ghost function d (x: tensor a') (output_shape: list int) : data a'
+=
+fun ks ->
+ if valid ks output_shape then
+ ...
+ else
+
+```
+
+The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics.
+
+The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics.
+
+This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor.
+
+### Recursive dimensions constructs
+
+An alternative way would be to define as much recursive helper functions as the number of dimensions and then define the data function based on these recursive functions.
+
+This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof.
+
+As a standard we will stick with the `anonymous function` definition.
+
+### Examples
+
+- **Clip**: [Clip abstract specification](./examples/clip.mlw)
+
+- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw)
+
+
+## 2.5 Operator tensor types
+
+Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level.
+
+For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors.
+
+However, there are operator whose abstract tensor type can be completely polymorphic.
+
+Take a look ate the example below:
+
+```why3
+module OPWhere
+ use tensor.Tensor
+
+ function dwhere (c : data bool) (a b : data 'a) : data 'a
+ = fun ks -> if c ks then a ks else b ks
+
+ let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+ requires { a ~= b }
+ requires { c ~ a ~ b }
+ ensures { result ~= a ~= b }
+ ensures { result = dwhere c a b }
+ (*proof*)
+ = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+ (*qed*)
+end
+```
+
+
+Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`.
+
+Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**.
+
+Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic.
+
+So whenever possible one should resort to **polymorphic** abstract datatypes.
+
+This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others.
+
+
+# Part 3 — Guidelines for Concrete Formalization
+
+## 3.1. Module Structure
+
+The concrete module should follow this general structure:
+
+```why3
+ module COP
+ (* 1. Imports *)
+ (* 2. Auxiliary helper functions *)
+ (* 3. Main operator function *)
+ end
+```
+
+### 3.2 Auxiliary helper functions
+
+All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well.
+
+Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ...
+
+Under this context, translation means:
+
+- Defining the function in an imperative style
+
+- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping)
+
+To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw):
+
+```why3
+ (* Abstract level *)
+ function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real
+ =
+ let b = get_dim x_p_coords 0 in
+ let c = get_dim x_p_coords 1 in
+ let n = (get_dim x_p_coords 2) - pad_top in
+ let m = (get_dim x_p_coords 3) - pad_left in
+ let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in
+ if valid x_coords x.dims then
+ x.data x_coords
+ else
+ 0.0
+```
+
+```why3
+ (* Concrete level *)
+ let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) =
+ requires { valid_tensor x }
+ requires { x.t_rank = 4 }
+ requires { valid_range x_p_coords 0 4 }
+ requires { writable x_p_coords }
+ requires { in_bounds (value_at x_p_coords 2 - pad_top) }
+ requires { in_bounds (value_at x_p_coords 3 - pad_left) }
+
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+
+ let ref flag = False in
+ let b = x_p_coords[0] in
+ let c = x_p_coords[1] in
+ let n = (x_p_coords[2]) - pad_top in
+ let m = (x_p_coords[3]) - pad_left in
+ let x_coords_array = malloc (to_uint32 4) in
+ if is_not_null x_coords_array then begin
+ flag <- True;
+ x_coords_array[0] <- b;
+ x_coords_array[1] <- c;
+ x_coords_array[2] <- n;
+ x_coords_array[3] <- m;
+
+ assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ;
+
+ let x_coords = coffset x_coords_array x.t_dims x.t_rank in
+ if x_coords >= 0 then begin
+ (x.t_data[x_coords], flag)
+ end
+ else begin
+ ((f32 0.0), flag)
+ end
+ end
+ else begin
+ flag <- False;
+ ((f32 0.0), flag)
+ end
+```
+
+These two functions perform the same exact computation.
+
+To better understand how these two relate one can compare both specifications:
+
+**Coordinates**
+
+- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator.
+
+ - These functions are equivalent and such equivalence proof is already available in the tensors library
+
+- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps:
+
+ - **Firstly**, the appropriate space is allocated with the `malloc` function
+
+ - **If the malloc is successful**, we then set each index of the array with the appropriate value.
+
+ - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause.
+
+**Computation**
+
+- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0.
+
+- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0.
+
+- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels.
+
+**Refinement mapping**
+
+- The refinement mapping relation is expressed with an ensures. In this function this realtion is expressed by:
+
+ ```
+ ensures { let (value, flag) = result in
+ flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value
+ }
+ ```
+
+- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels.
+
+- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created.
+
+### 3.3 Main operator function
+
+This is naturally the most important function and the one whith more details to be taken into account.
+
+#### 3.3.1 Contracts
+
+The **main operator function** must include contracts at both levels.
+
+**Requires**
+
+- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.
+
+- On the **concrete level** we should have preconditions for:
+
+ 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor**
+
+ 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**.
+
+ 3. All the **preconditions** already present at the abstract level. The **concrete** will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change).
+
+**Ensures**
+
+- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**:
+
+- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form:
+
+ - `ensures { tensor result = (tensor x) ... }`
+
+The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw).
+
+- **Concrete level**
+
+ ```why3
+ let flatten (x r : ctensor) (axis: int32)
+ (* Requires*)
+ (* 1 - Valid tensors *)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+
+ (* 2 - Shape match *)
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+
+ (* 3 - Informal Spec *)
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+
+ (* Ensures *)
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let m = cdim_size r.t_dims r.t_rank in
+ for i = 0 to m - 1 do
+ invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k }
+ r.t_data[i] <- x.t_data[i]
+ done;
+
+ assert { tensor r == flatten (tensor x) (to_int axis) }
+ ```
+
+**Requires**
+
+Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors.
+
+Expressing the **shape match** on the other hand is not that trivial.
+There are multiple ways of doing this match.
+The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same.
+To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`.
+
+- `tensor`: Converts a **ctensor** into an **abstract tensor**
+
+- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int`
+
+- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that´s why such convertion is important.
+
+ - Takes as inputs: the **pointer** to convert and the **desired size** of conversion
+
+For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level.
+
+Translating the preconditions from the abstract level to the concrete level can be done in two different ways:
+
+ 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions
+
+ 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure.
+
+In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provable than the other, so it is up to the specification author to choose which one to use. Having said so, the second **precondition** could also have been expressed through abstract terms:
+
+- `requires { size (tensor x) = size (tensor r) }`
+
+To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate wether or not the verification conditions generated are automatically proved.
+
+This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them.
+
+Check the following example:
+
+```why3
+ let flatten (x r : ctensor) (axis: int32)
+ requires { valid_tensor x }
+ requires { valid_tensor r }
+ requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+ requires { r.t_rank = 2 }
+ requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank }
+ requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims }
+ ensures { tensor r = flatten (tensor x) (to_int axis) }
+ =
+ let ghost _ = flatten (tensor x) (to_int axis) in
+ (...)
+```
+
+This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator.
+
+The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved
+
+
+
+If one had forgotten to include this precondition:
+
+```
+requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in
+ (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized }
+```
+
+then, the strategy proposed above would have generated the following verification condition which is not automatically proved:
+
+
+
+By inspecting the task menu, the specification author would be able to understand which precondition is missing and add it to the specification.
+
+
+
+**Ensures**
+
+Our final goal is to prove the **ensures** clause stating the refinement between both levels.
+
+Usually the following pipeline is followed to do so:
+
+$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$
+
+Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof.
+
+### 3.4 Invariants
+
+**Nested Loop Invariants**
+
+Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**.
+
+A loop invariant must satisfy two proof obligations:
+
+1. **Initialization** — the invariant holds for the first iteration.
+
+2. **Preservation** — if the continually holds for all iterations up to the last one, inclusive.
+
+When loops are nested, these obligations form a dependency chain:
+
+- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant
+
+- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant.
+
+The following example illustrates this:
+
+```why3
+let ref sum = (f32 0.0) in
+(* ... *)
+for i = 0 to rows - 1 do
+ invariant { sum = 0.0 }
+ for j = 0 to cols - 1 do
+ invariant { sum = 0.0 }
+ for k = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) i j 0 k }
+ (* ... *)
+ done;
+ sum <- 0.0;
+ done;
+done;
+```
+
+The innermost loop (`k`) accumulates a dot product in `sum`.
+
+For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore:
+
+$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$
+
+In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$.
+
+### Loop Invariants for Proving Data Refinement
+
+**Example 1 - Unit Summation**
+
+To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range.
+
+The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one.
+
+The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove:
+
+
+
+The goal and the respective logical context are below:
+
+
+
+What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration.
+
+To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level.
+
+Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1:
+
+```why3
+ let rec lemma summation_lemma (i iter : int)
+ requires { i < iter }
+ ensures { 1 + summation (i + 1) iter = summation i iter }
+ variant { iter - i }
+ =
+ if i >= iter then
+ ()
+ else
+ summation_lemma (i + 1) iter
+```
+
+After adding this, the invariant preservation is easily proved.
+
+This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation.
+
+**Example 2 - Dot Product**
+
+The example presented above is a very simple one, but the same strategy can be applied to much more complex operators.
+
+Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator.
+
+The file is available [here](./examples/dot_product.mlw).
+
+Once again the proof is not achievable and the task menu displays the following:
+
+
+
+To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma.
+
+The strategy is essentially the same:
+
+```why3
+ let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int)
+ requires { Int.(i <= k < iter) }
+ variant { Int.(k - i) }
+ ensures {
+ let a_val = a.data (Cons row (Cons k Nil)) in
+ let b_val = b.data (Cons k (Cons col Nil)) in
+ dot_product_rec a b row col i Int.(k + 1) =
+ Real.(dot_product_rec a b row col i k + a_val * b_val)
+ }
+=
+ if i < k then
+ Real.(dot_product_lemma a b row col (i + 1) k iter)
+ else
+ ()
+```
+
+This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma.
+
+
+
+To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables.
+
+We will do so by introducing a ghost block:
+
+```why3
+ghost begin
+ dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter)
+end
+```
+
+After this, the invariant preservation is easily proved.
+
+More information regarding lemmas intantiation in [4.2](#42-ide-transformations-and-prover-hints).
+
+**Convolution**
+
+The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops.
+
+Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates.
+
+Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above.
+
+The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration.
+
+This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate.
+
+The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement.
+
+Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor.
+
+Below are the four loop invariants used in the concrete Conv implementation:
+
+```why3
+ for nn = 0 to n - 1 do
+ invariant {
+ forall nn_: int. 0 <= nn_ < nn ->
+ (forall mm: int. 0 <= mm < m ->
+ (forall yy_hh: int. 0 <= yy_hh < y_h ->
+ (forall yy_ww: int. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ )
+ }
+ r_coords_array[0] <- nn;
+ for mm = 0 to m - 1 do
+ invariant { value_at r_coords_array 0 = nn }
+ invariant {
+ forall mm_. 0 <= mm_ < mm ->
+ (forall yy_hh. 0 <= yy_hh < y_h ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ )
+ }
+ r_coords_array[1] <- mm;
+ for y_hh = 0 to y_h - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+
+ }
+ r_coords_array[2] <- y_hh;
+ for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+```
+
+The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically.
+
+The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected.
+
+They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten.
+
+This must be asserted explicitly each time.
+
+### The innermost loop
+
+The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop):
+
+```why3
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+```
+
+The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct:
+
+```why3
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ };
+
+ assert { forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ };
+
+ assert {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ };
+```
+
+With these assertions in place, the proof of the innermost invariant succeeds.
+
+### The outer loops
+
+The preservation of the outer invariants still fails.
+
+To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant:
+
+```why3
+constant yy_hh : int
+
+H3 : 0 <=' yy_hh
+
+H2 : yy_hh <' (y_hh +' 1)
+
+constant yy_ww : int
+
+H1 : 0 <=' yy_ww
+
+H : yy_ww <' int32'int y_w
+
+constant coords : list int =
+ Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int))))
+
+constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank))
+
+------------------------------- Goal --------------------------------
+
+goal c_conv'vc :
+ (value_at (r.t_data) @ offset1 coords dims).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h)
+ (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)
+```
+
+Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant:
+
+```
+LoopInvariant2 :
+ forall yy_hh1:int.
+ 0 <=' yy_hh1 /\ yy_hh1 <' y_hh ->
+ (forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r1.t_data)
+ @ offset1
+ (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0
+ (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3))
+ nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w)
+ (int32'int dil_h) (int32'int dil_w) (int32'int pad_top)
+ (int32'int pad_left)))
+```
+
+At first glance, the hypothesis and the goal look nearly identical.
+
+However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`.
+
+The only relationship between `r` and `r1` available in the logical context is:
+
+```why3
+H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data)
+```
+
+Meanwhile, from the innermost loop invariant, the following hypothesis is also available:
+
+```
+LoopInvariant1 :
+ forall yy_ww1:int.
+ 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) ->
+ (let coords'unused =
+ Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))
+ in
+ (value_at (r.t_data)
+ @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))))
+ (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real =
+ w_channels_calculate (tensor x) (tensor w)
+ (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2))
+ (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h)
+ (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left))
+```
+
+The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices.
+
+This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop.
+
+The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant:
+
+```
+for y_ww = 0 to y_w - 1 do
+ invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh }
+ invariant {
+ forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left
+ )
+ }
+ invariant {
+ forall yy_ww. 0 <= yy_ww < y_ww ->
+ let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left)
+ }
+ r_coords_array[3] <- y_ww;
+ let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in
+ if flag then begin
+ let r_coords = coffset r_coords_array r.t_dims r.t_rank in
+ r.t_data[r_coords] <- value;
+
+```
+
+By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant.
+
+However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop.
+
+The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten.
+
+The same pattern of assertions resolves this:
+
+```why3
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ coords <> (ivector r_coords_array r.t_rank)
+ )
+ };
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ Range.valid coords (ivector r.t_dims r.t_rank)
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in
+ off <> off1
+ )
+ };
+
+assert { forall yy_hh. 0 <= yy_hh < y_hh ->
+ (forall yy_ww. 0 <= yy_ww < y_w ->
+ let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in
+ let dims = ivector r.t_dims r.t_rank in
+ let off = CFlat.offset coords dims in
+ off <> r_coords
+ )
+ };
+```
+
+Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator.
+
+The completed file is available [here](./examples/conv.mlw).
+
+# Part 4 - Tips, Hints and Strategies
+
+## 4.1. Scope Resolution
+
+When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors.
+
+Take a look at the following example:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { iter - i }
+ =
+ if i < iter then
+ 1.0 + summation (i + 1) iter
+ else
+ 0.0
+end
+```
+
+If you attempt to open the Why3 ide with the above code, you will get the following error:
+
+
+
+This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`.
+
+In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name.
+
+In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules.
+
+Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope.
+
+To solve the issue we will have to specify the scope operations on the `Int` domain:
+
+
+$$(iter - i) \rightarrow Int.(iter - i)$$
+
+$$i < iter \rightarrow Int.(i < iter)$$
+
+$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$
+
+The corrections applied to the code above are the following:
+
+```why3
+module OPSummation
+ use int.Int
+ use real.Real
+
+ let rec function summation (i : int) (iter : int) : real
+ variant { Int.(iter - i) }
+ =
+ if Int.(i < iter) then
+ 1.0 + summation Int.(i + 1) iter
+ else
+ 0.0
+end
+```
+
+## 4.2. IDE Transformations and Prover Hints
+
+Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals.
+
+Below are techniques we have found useful in practice.
+
+The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE.
+
+---
+
+### Lemma / Axiom Instantiation
+
+When an axiom or lemma is stated universally, for example:
+
+```why3
+axiom test:
+ forall x: int. P(x) -> Q(x)
+```
+
+The automated prover may either fail or take significantly longer to apply it in a specific context.
+
+Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful.
+
+**Example — Mean Value Theorem:**
+
+Consider the following axiom:
+
+```why3
+(* Lagrange Mean Value Theorem *)
+axiom mean_value_theorem_sigmoid:
+ forall x y: real.
+ exists c: real.
+ (x <= c <= y \/ y <= c <= x) /\
+ sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y)
+```
+
+And the following lemma to be proved:
+
+```why3
+lemma lipschitz_sigmoid:
+ forall x y: real.
+ abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y)
+```
+
+To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem.
+
+Instantiating the axiom with the specific variables `x` and `y` greatly helps.
+
+**IDE instantiation syntax:**
+
+```
+instantiate
+```
+
+If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step.
+
+The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it.
+
+This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE.
+
+---
+
+### Instantiation via Lemma Functions
+
+Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved.
+
+This is the preferred approach, as it keeps the proof self-contained and reproducible.
+
+**Example — `valid_bounds_2` lemma function:**
+
+The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions:
+
+```why3
+let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int)
+ requires { p <= q }
+ requires { valid_range ks p q }
+ requires { valid_range ds p q }
+ requires { pdim ds p q }
+ requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i }
+ ensures { Range.valid (islice ks p q) (islice ds p q) }
+ variant { q - p }
+ = if p < q then
+ begin
+ assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) };
+ assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) };
+ valid_bounds_2 ks ds (p+1) q
+ end
+```
+
+Now consider the `dot_product` function, which allocates coordinate arrays at runtime:
+
+```why3
+let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) =
+ requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter }
+ requires { a.t_rank = b.t_rank = 2 }
+ requires { iter >= 0 }
+ requires { row >= 0 /\ row < value_at a.t_dims 0 }
+ requires { col >= 0 /\ col < value_at b.t_dims 1 }
+ (* ... *)
+ let ref sum = (f32 0.0) in
+ let ref flag = False in
+ let a_coords_array = malloc (to_uint32 2) in
+ let b_coords_array = malloc (to_uint32 2) in
+ if is_not_null a_coords_array && is_not_null b_coords_array then begin
+ flag <- True;
+ for i = 0 to iter - 1 do
+ invariant { sum = dot_product (tensor a) (tensor b) row col 0 i }
+ set_ofs a_coords_array 0 row;
+ set_ofs a_coords_array 1 i;
+ set_ofs b_coords_array 0 i;
+ set_ofs b_coords_array 1 col;
+
+ ghost valid_bounds_2 a_coords_array a.t_dims 0 2;
+ (* Rest of code ... *)
+```
+
+Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own.
+
+Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue.
+
+---
+
+### Function Unfolding
+
+When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle.
+
+Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE.
+
+**`unfold`** expands a specific function by its definition at the point where it appears in the goal.
+
+To use `unfold` transformation, enter the following command in the **"Type command here"** field:
+```
+unfold
+```
+
+Before applying `unfold`:
+
+
+
+After applying `unfold`:
+
+
+
+**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously.
+
+It requires no arguments:
+
+```
+compute_in_goal
+```
+
+Result after applying `compute_in_goal`:
+
+
+
+
+---
+
+## 4.3. How to Debug
+
+"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal.
+
+There are two common root causes:
+
+1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated.
+
+2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces.
+
+The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap.
+
+### Reading the Logical Context
+
+Consider the following goal:
+
+```
+------------------------------- Goal --------------------------------
+
+goal w_cools_calculate'vc :
+ to_extended_real max_value =
+ w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b)
+ (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w)
+ (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom)
+ (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w)
+```
+
+In the logical context, among other hypotheses, we have:
+
+```
+constant x_val : float
+
+constant aux_flag : bool
+
+Ensures4 :
+ aux_flag = True ->
+ coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top)
+ (to_int2 pad_left) = to_extended_real x_val
+
+constant ww1 : int321
+
+constant ww : int = int32'int ww1
+
+H31 : 0 <=' ww
+
+H30 : ww <=' int32'int o2
+
+LoopInvariant :
+ to_extended_real max_value1 =
+ w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c)
+ (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top)
+ (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h)
+ (to_int2 str_w)
+
+H1 :
+ w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0
+ (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h)
+ (int32'int dil_w) (int32'int pad_top) (int32'int pad_left)
+ (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h)
+ (int32'int str_w) =
+ max_extended_real
+ (coords_from_X_p (tensor x)
+ (Cons (int32'int b)
+ (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int)))))
+ (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b)
+ ...)
+
+Ensures :
+ to_extended_real (max max_value1 x_val) =
+ max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+
+constant max_value : float
+
+H : max_value = max max_value1 x_val
+```
+
+The logical context is sufficient to prove the goal. Here is the reasoning:
+
+**Left-hand side of the goal.**
+
+From `H`, we know `max_value = max max_value1 x_val`, so:
+
+```
+to_extended_real max_value = to_extended_real (max max_value1 x_val)
+```
+
+Applying `Ensures`:
+
+```
+= max_extended_real (to_extended_real max_value1) (to_extended_real x_val)
+```
+
+**Right-hand side of the goal.**
+
+Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`:
+
+```
+w_cools_calculate ... (ww +' 1) ...
+ = max_extended_real
+ (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left))
+ (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...)
+```
+
+From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`.
+
+For the first argument, `Ensures4` tells us (given `aux_flag = True`) that:
+
+```
+coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left)
+ = to_extended_real x_val
+```
+
+So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity).
+
+However, Why3 cannot discharge the goal automatically.
+
+**Why Why3 doesn't make the proof**
+
+The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor:
+
+```
+coords_from_X_p (tensor x)
+ (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil))))
+ (int32'int pad_top) (int32'int pad_left)
+```
+
+whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`.
+
+These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own.
+
+The connection must be made explicit with an intermediate assertion:
+
+```why3
+assert { ivector x_p_coords_array 4 =
+ Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) };
+```
+
+This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through.
\ No newline at end of file
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/after_unfold.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/after_unfold.png
new file mode 100644
index 00000000..52618bc6
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/after_unfold.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/before_unfold.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/before_unfold.png
new file mode 100644
index 00000000..87d48f2a
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/before_unfold.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/compute_in_goal.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/compute_in_goal.png
new file mode 100644
index 00000000..2e90858d
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/compute_in_goal.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product0.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product0.png
new file mode 100644
index 00000000..76bd74e5
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product0.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product1.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product1.png
new file mode 100644
index 00000000..00948758
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product1.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_not_proved.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_not_proved.png
new file mode 100644
index 00000000..5e9ebcd0
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_not_proved.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_proved.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_proved.png
new file mode 100644
index 00000000..21e24a8b
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_proved.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires.png
new file mode 100644
index 00000000..13c1a7b1
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires_1.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires_1.png
new file mode 100644
index 00000000..594827c5
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires_1.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/loop_inv.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/loop_inv.png
new file mode 100644
index 00000000..6dd5f824
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/loop_inv.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/refinement.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/refinement.png
new file mode 100644
index 00000000..0c7dc871
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/refinement.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/scope.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/scope.png
new file mode 100644
index 00000000..fb027d8c
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/scope.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant0.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant0.png
new file mode 100644
index 00000000..b6f0fe73
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant0.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant1.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant1.png
new file mode 100644
index 00000000..21cbda15
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant1.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/termination.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/termination.png
new file mode 100644
index 00000000..5a120217
Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/termination.png differ
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/informal.md b/safety-related-profile/sonnx/ops/docs/guidelines/informal.md
new file mode 100644
index 00000000..b4171eb3
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/informal.md
@@ -0,0 +1,330 @@
+# Introduction
+
+This document gives the guidelines to be followed when writing an operator's non-formal specification. Guidelines for the development of formal specification are given in a [dedicated document](./formal.md).
+
+# Rationales
+
+The non-formal specification is first an entry-point for any *user* of the SONNX profile. Towards that goal, it must explain clearly the purpose of the operator, i.e., the relation between its inputs (arguments and attributes) and its outputs. Towards that goal, it may use whatever means deemed useful including text description, mathematical formulae, graphics, examples.
+
+It must also provide a clear statement about:
+- the usage restrictions for the operator imposed by SONNX,
+- the constraints on the arguments and attributes of the operator (or pre-conditions") that must be satisfied for the operator to be applicable,
+- the possible errors that may raise during the execution of the operator.
+
+In the following paragraphs, we express some of the "principles" that we applied to eleborate the guidelines.
+
+#### About the level of formalization
+The non-formal specifications aims at providing a clear and complete description of the behavior of the subset of operators considered in the SONNX profile. Understandability is favored over formalization, accordingly, the non-formal specification essentially relies on natural language, with mathematical formulae when deemed necessary. In addition, for each operator, a formal specification expressed using a formal language (Why3) is provided separately.
+
+#### About data types
+The non-formal specification is given for all data types considered in SONNX i.e.: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double and float64. In addition, SONNX also provides a specification of each (mathematical) operator for real numbers. This is aimed at facilitating the understanding of the core function of the operator without dealing with implementation-specific issues such as the effect of overflows (for ints and uints) or the handling of special float values (+Inf, -Inf, NaN, 0+, 0-).
+
+#### About floating point numbers
+
+Floating point operations are specified in compliance with the the IEEE 754 standards (IEEE Standard for Floating-Point Arithmetic, IEEE Std 754 2019, IEEE). In particular, care is taken to describe the expected behaviour of mathematical operator when dealing with IEEE special values (+Inf, -Inf, +0+, -0, and NaN).
+
+#### About the compliance with implementation standards
+
+SONNX is essentially a specification effort. As such, it must remain independent from any specific implementation and, instead, must simply express the intended behavior of each operator according to some usual and/or consensual understanding of its semantics. However, tests realized by the SONNX WG on the implementations of some operators (e.g., **MaxPool**) show that the actual behavior sometimes differs significantly from the expected one, due to implementation choices or, sometimes, implementation bugs.
+
+Even if we consider that the SONNX specification shall not "reflect" a specific implementation, it shall preferably stick to the ONNX Runtime implementation when choices have to be made. When, for a given operator, the usual/consensual semantics and the ONNRtime implementation diverge, the SONNX WG choose to indicate this discrepancy. This is for instance the case for the **MaxPool** operator.
+Note also that the semantics of an operator in ONNX runtime may depend on the execution backend (CPU, GPU,...).
+
+#### About specifying and non specifying information and traceability
+
+By default, all the text of the specification is "specifying" (i.e., it expresses a constraint to be followed).
+
+Reference to the specification elements can be done using the section names (tagged `#` in the markdown document).
+
+If a more precise location must be identified, a tag can be used. tags can be nested.
+[spec1]
+This is some traceable text.
+[spec1.1] This is another text. [/spec1.1][spec1]
+
+Additional *non specifying* information may may be given. In that case, it must be enclosed between the following tags:
+
+`[info]`
+
+`[/info]`
+
+For instance:
+[info]
+This is non specifying information.
+[/info]
+
+#### About error conditions
+
+An error condition is a condition leading to an unexpected result. Obviously, if the specification is clear and complete, all possible behaviour should be "expected". However, we consider worth being mentioned any result leading to
+- an "unexpected result", such as a division by zero in the integer domain
+- a NaN in the floating point domain *when this NaN is not simply the propagation of a NaN in the operator inputs*
+
+In some cases, a pre-condition may be stated on the inputs so that, if the pre-condition is satisfied, then no error -- NaN, division by zero, etc. -- can occur. For instance, no division by zero can occur if the condition stating that "the denominator shall not be null" is satisfied. Nevertheless, the possibility for an error to occur will still be signaled to the reader.
+
+Systematically providing pre-conditions on the inputs to prevent error conditions was not considered useful for the end-user since operators are generally used in complex graphs for which verifying the preconditions to prevent the occurrence of error is not achievable in practice.
+
+By default, pre-conditions are supposed to be satisfied for an operator to be defined or, stated the other way round, if at least one precondition is not satisfied, then the behaviour of the operator is undefined. In some cases, a pre-condition can be violated but the behaviour is nevertheless defined. For instance, when applying **MapPool** with no padding, the pooling kernel can be larger than the argument tensor, which violates one precondition. In that case, SONNX states that the operator shall return an empty tensor. This specific behavior must be specified in the error section.
+
+#### About accuracy
+
+The SONNX profile does not specify the accuracy of operators, instead, it provides guidance on how to analyze the propagation and introduction of errors on a given implementation and illustrates the approach on SONNX' reference implementation.
+
+The analysis is carried out in two steps:
+- During step 1, an analysis is carried out to specify bounds on propagated and introduced errors considering the IEEE rounding error. This bounds may not be the tightest one, but a bound that can be determined with a reasonable analysis effort.
+- During step 2, an analysis of the code of an actual implementation (in our case, the reference implementation) is carried out using a tool developed for that purpose by one of the SONNX WG contributor (Franck Vedrine, [CEA List](www.cea.fr/)).
+
+Please refer to the [specific set of guidelines](../../docs/guidelines/accuracy.md) are given for the analysis of accuracy.
+
+# Specification guidelines
+This section is composed of two sub-sections:
+- *General guidelines* defining presentation rules such as fonts, notations, use of tags for tracability, etc.
+- *Structure and Contents of the specification* defining the organization and the contents of the non-formal specification of an operator. This section applies the general guidelines.
+
+## General guidelines
+The non-formal specification is intended for both users *and* implementers of operators who both need to understand what an operator does and how to use it. For instance, the first kind of readers might be satisfied with one or two sentences about the semantics of an operator whereas the second category of readers would like to get all the details of the semantics.
+
+More precisely, the non-formal specification:
+- Is aimed at showing clearly what a given operator is supposed to do,
+- Without calling on a strict formal, mathematical language,
+- Knowing that the exact and complete specification is given in the "formal" specification.
+- May provide diagrams and examples to make things clear.
+- Follows ONNX nomenclature, which includes naming convention, for operator names, types, identifiers of operator inputs, outputs and attribute, etc.
+ - Examples:
+ - The element-wise addition of tensors is $Add$, not $add$
+ - The 16-bit floating-point type is $float16$, not $FP16$
+
+The writer of the non-formal specification must take care to keep it readable and understandable by a ML developer. The recommendations given in the following guidelines target this objective.
+
+### Styling
+- Mathematical objects are represented using *italic*. LaTeX formulae are used.
+- In the text, operator attributes are represented using `this font`.
+- As far as possible, names of arguments and attributes shall be used in mathematical formulae. In the case the name is "too long", another, shorter designation, may be used with a clear statement of the redefinition. If the symbol refers to a greek symbol (e.g., $\text{alpha}$), the symbol itself can be used (e.g., $\alpha$).
+
+### Basic operations
+The specification may use the following operations without first defining them:
+- $+$, $-$, $*$, $/$, $-x$ (negation),
+- $\sin(x)$, $\cos(x)$, $\tan(x)$,
+- $\exp(x)$, $\sqrt x$, $\ln(x)$, $|x|$
+- $\min(x,y)$, $\max(x,y)$
+- logical operators: $\wedge$, $\vee$, $\lnot$
+
+### Naming conventions
+- As far as possible, ONNX names for inputs, outputs, and attributes must be used.
+
+### Notations
+#### Tensors
+- A tensor is always represented in uppercase letters (e.g., $A, B,...,X, Y, Z$).
+- In the case of a variadic operator (e.g., **Concat**), the tensor parameters are designated by an index: $A0$, $A1$, etc. Indexes start at 0 to be consistent with the other use of indexes.
+- The rank of a tensor $T$, i.e., the number of its dimensions, is denoted $rT$.
+- The shape of a tensor $A$ is denoted by a vector $(dA_0, ..., dA_i, ..., dA_n)$ where $dA_i$ refers to the dimension along axis $i$ and $n=rA-1$.
+- For a tensor used as a variadic parameter (denoted $Ai$), the shape is denoted by $(dAi_{0}, dAi_{1}, ...)$.
+- A specific element of a tensor is denoted:
+ - either as: $A[i]$, where $i$ is a [tensor index](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/common/definitions.md#tensor_index)
+ - or as: $A[i_0, i_1, ..., i_{rA-1}]$.
+
+#### Tags
+The non-formal specification makes use of three different types of tags:
+- A **restrictions tag** expresses a restriction with respect to the ONNX standard (see the section about restriction below). They are indicated by tag `[R]` where `` is a number.\
+A synthesis of all restrictions is given in section "Restrictions" (see below).
+- A **constraints tag** expresses a constraint on one or several inputs, outputs, or attributes. They are indicated using `[C]` where `` is a number.
+- A **traceability tag** identifies a specific location in the non-formal specification. These tags are used to establish traceability between the informal and formal specifications. They are indicated by `[T]` where `` is a number.
+
+For instance, here is a tag introducing a constraint relating some input and output tensors:
+>`[C1]` Shape consistency \
+> Statement: Tensor $A$ and $Y$ shall have the same shape.
+
+When part of the documentation refers to a tag, an hyperlink is used. This is achieved using the following elements:
+```
+ `[T1]`
+```
+ to declare the location of the tagged element and
+ ```
+ [`[T1]`](#my_tag_name).
+```
+to refer to the tagged element.
+
+Here is an example:
+`[T1]` This is a tagged paragraph.
+
+This is a reference to the tagged paragraph [`[T1]`](#my_tag_name).
+
+### Types
+- The type names shall be the ones used in the ONNX description of the operators, without surrounding them with "tensor()".
+ - Example: "tensor(double)" in ONNX becomes "double" in the non-formal specification.
+- The data types allowed in SONNX operators are:
+ - IEEE 754 floating-point types: double, float, float16
+ - Signed integer types: int64, int32, int16, int8
+ - Unsigned integer types: uint64, uint32, uint16, uint8
+ - bool
+ - string
+- IEEE 754 floating-point types, i.e., double, float and float16 have the following special numbers:
+ - +0 and -0
+ - +inf and -inf
+ - NaN (Not a Number)
+- All operators applicable to numeric values shall first be specified for values in the domain of real numbers, then specific descriptions shall be given for the other types (float, double, etc.).
+- A description can be applicable to multiple types as long as its **semantics description** remains the same for all types.
+
+## Structure and contents of the specification
+
+This section describes the required structure and contents of the non-formal specification of an operator.
+
+The [specification template](informal_spec_template.md) gives an example of the required structure.
+
+### Contents
+
+This section gives the list of all non-formal specifications of the operator, for each of the applicable types, with hyperlinks to the sections (see [template](informal_spec_template.md)).
+
+- **Op** operator for type real
+- **Op** operator for types <T1>, <T2>,...
+- **Op** operator for types <T1>, <T2>,...
+- etc.
+
+The reference, with link, to the ONNX definition of **Op** shall be inserted in this section. See **Div** example below.
+
+Here is an example for operator **Div**:
+> Contents
+>- **Div** operator for type real
+>- **Div** operator for types float16, float32, float64
+>- **Div** operator for types int8, int16, int32, int64, uint8, uint16, uint32, uint64
+>
+> Based on ONNX [Div version 14](https://onnx.ai/onnx/operators/onnx__Div.html).
+
+The following section must be repeated for each set of types for which the semantics is the same. One section corresponds to one entry in the "Contents" list.
+
+## **Op** (, ,...)
+
+### Signature
+
+Definition of operator **Op** signature:
+
+ $O = \textbf{Op}(X,Y,...,Z)$
+
+ where
+ - $X$: Brief description of argument $X$
+ - $Y$: Brief description of argument $Y$
+ - ...
+ - $O$: Brief description of output
+
+Arguments shall have different names. For instance
+
+$O = \textbf{Op}(X, Y)$
+
+When the same name is used for different arguments such as in
+
+ $O = \textbf{Op}(X0,X1,...,XL)$
+
+ this means that the operator is **variadic**, i.e., it accepts a variable number of arguments. In this example, there are n arguments that are discriminated by their index.
+
+### Restrictions
+This section lists all restrictions applicable to the operator. A restriction is **a limit with respect to the normal usage domain** of the ONNX operator. A restriction may concern the dimension of tensors, the values of attributes, etc.
+
+Some SONNX restrictions apply to all the operators. Therefore, this section shall contain the following markdown link:
+
+`\[General restrictions](../common/general_restrictions.md)`
+
+Restrictions marked as "Transient" are introduced by the working group in order to reduce the specification, proof, etc. effort. Those restrictions, which are not traceable to a need, are aimed at being eventually relaxed. However, in the meantime, both transient and non-transient restrictions are applicable by the operator user or implementer.
+
+Restrictions not marked as "transient" are traced to some [end-user requirement](../../../../deliverables/reqs/reqs.md) using an hyperlink.
+
+An example is given hereafter
+
+| Restriction | Statement | Origin |
+| -------- | ------- | ------- |
+| `[R1]` | Input tensor $X$ has 2 spatial axes | Transient |
+| `[R2]` | Attribute `auto_pad` is restricted to NOTSET | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) |
+
+ ### Specification
+
+ This section contains the specification of the operator. The specification is "informal", ie., it does not use a formal language, even though it usually uses some mathematical formulae. The specification shall be readable, understandable, and self-contained. It can include illustrations if deemed necessary. The objective is that a human being can fully understand the domain, range, and semantic of the operator with no additional information. Stated differently, he/she should be able to implement the operator with no additional information.
+
+The specification shall be composed of the following parts:
+- A short description of the operator. For instance, for the $Conv$ operator:
+
+> Operator **Conv}** computes the convolution of the input tensor $A$ with the kernel $W$ and adds bias $B$ to the result. Two types of convolutions are supported: standard convolution and depthwise convolution.
+
+- A detailed description that:
+ - Uses the notations proposed in section "Notations" of these guidelines
+ - Implements the traceability tags proposed in Section "Tags" of these guidelines
+ - Presents the mathematical formulae, if necessary, according to the following pattern: the complete formula is first given and its atomic elements and sub-expressions are defined afterward, by introducing them with "Where" or "In which".
+ - Insert a blank line before and after each formula so that it renders correctly in the browser.
+
+For instance, for the **Conv** operator:
+
+> $$
+ Y[b, c, m, n] = \sum_{i=0}^{dW_1-1} \sum_{j=0}^{dW_2-1} \sum_{z=0}^{dW_3-1} \\ (X_p[b,i,m \cdot \text{strides}[0]+ j , n \cdot \text{strides}[1]+ z ] \cdot W_d[c, i, j, z]) \\ + B_b[c]
+$$
+> Where
+>- $b \in [0,dY_0-1]$ is the batch index. $dY_0$ is the batch size of output $Y$
+>- $c \in [0,dY_1-1]$ is the data channel. $dY_1$ is the number of data channels of output $Y$
+>- $m \in [0,dY_2-1]$ is the index of the first spatial axis of output $Y$
+>- $n \in [0,dY_3-1]$ is the index of the second spatial axis of output $Y$
+>- etc.
+
+## Examples
+
+The specification must provide examples to illustrate the behaviour of the operator. As far as possible, examples must cover special values, domain bounds, etc., in order to clarify the behaviour of the operator, especially for non trivial cases. If possible, a jupyter notebook allowing the generation of the examples shall be provided (and placed aside the specification document).
+
+When the displayed result is not exact (for instance when using some real or float numbers that cannot be represented exactly), please use the $\approx$ symbol instead of the $=$ symbol.
+
+## Error conditions
+
+This section identifies the conditions that may occur during the execution of the operator leading to unexpected results (e.g., a IEEE special value while there was no special value in the inputs, a runtime error, etc.)
+
+When writing a specification, the writer must identify the following failure conditions:
+- for floating point computations
+ - invalid operation as defined in IEEE 754 section 7.2, i.e.
+ - $0 \times\infty$ or $\infty \times 0$
+ - addition or subtraction or fusedMultiplyAdd: magnitude subtraction of infinities, such as addition $(+\infty, -\infty)$
+ - division $(0, 0)$ or division ($\infty, \infty)$
+ - remainder(x, y), when y is zero or x is infinite and neither is a NaN
+ - square root if the operand is less than zero
+ - etc. Refer to the standard for a complete list of error conditions.
+ - overflows (computations leading to +Inf or -Inf)
+- for integer computations
+ - division by zero,
+ - overflows, i.e., operations leading to a value out of the range (e.g., addition of two large int32 values non representable in int32)
+
+The following rules must be applied.
+ - Restrict the input domain to prevent the occurrence failures if it is not too conservative. for instance, $x \ge 0$ for $sqrt(x)$ is deemed acceptable whereas, using `uint32`, $A\leq2^{31}$ and $B\leq2^{31}$ for $A+B$ is deemed too conservative.
+ - Give the most detailed description of the conditions in which a failure can occur, and the possible expected result. For instance for a matrix multiplication in int32, explain that the accumulator may overflow and may wrap around, leading to an incorrect and inconsistent result. If possible, point out the location in the specification where the error may occur.
+- If no indication is given about occurrence of a "failure", this means that the operator returns a correct value (as per specification) for **any** input value in the domain defined by the type.
+- If applicable and possible, provide "recommendations" about the implementation to prevent failure. For instance, propose to substract $max(Xi)$ to the argument to make the **Softmax** operator more robust.
+
+Note that these rules concern the *specification* of the operation. Therefore, they must be independent from implementation choices. For instance, *generally speaking*, it is always possible for the operation to overflow if the domain is output domain limited (e.g., `int32`), so there must be a warning about this failure condition. Nevertheless, a specific implementation may be failure-free if, for example, the size for the matrices is limited and the accumulator is sufficiently large. In that case, the implementation must give these conditions. Otherwise, the implementation is deemed compliant with the specification.
+
+## Attributes
+This section describes the operator's attributes. This section must be introduced in the section about real numbers. Sections concerning the other types (floats, integers) shall shall not repeat this contents and provide a link to it whenever applicable. See the [template](./informal_spec_template.md) for an example.
+
+### `name`: \
+where `name` is the attribute's name and \ is the attribute's type.
+
+ #### Constraints
+This section gives all constraints applicable to the attribute.
+
+- When a constraint involves several inputs/outputs/attributes, it is only described once when the first input or attribute concerned by the constraint is described. Then, for the other inputs, attributes, or outputs concerned by the same constraint, a cross-reference is given.
+The description is structured as follows:*
+
+ - `[C]` <Title of constraint>
+ - Statement: <Expression of the constraint> or cross-reference to the previous location where this constraint was first introduced.
+ - Rationale: <Justification for the constraint>. When the title and/or the statement of the constraint are sufficiently explicit, the rationale may be omitted.
+
+## Inputs
+
+This section describes the operator's inputs.
+
+### $\text{name}$: \
+
+where $\text{name}$ is the name of the input and \ is the type of the input.
+
+#### Constraints
+Same as for the attributes.
+
+### Outputs
+ This section describes the operator output.
+
+### $\text{name}$: \
+where $\text{name}$ is the output's name and \ is the output's type.
+
+ #### Constraints
+Same as for the inputs.
+
+ End of the document.
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/informal_spec_template.md b/safety-related-profile/sonnx/ops/docs/guidelines/informal_spec_template.md
new file mode 100644
index 00000000..9fc5d8c6
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/informal_spec_template.md
@@ -0,0 +1,100 @@
+*This template only covers one datatype. Do not forget to describe the operator for real numbers and all applicable data types. For structural operators (i.e., operators whose behaviour do not depend on the data type), a single section may suffice.*
+
+*The text in italics gives indications. It shall not be retained the specification document.*
+
+# Contents
+
+- **Op** operator for type [int8](#int8)
+
+
+Based on ONNX [Op version 14](https:/...).
+
+
+# **Op** (int8, int8)
+
+## Signature
+
+$Y = \textbf{Op}(A, B)$
+
+where:
+- $A$: *brief description of the first argument*
+- $B$: *brief description of the second argument*
+- $Y$: *brief description of the output*
+
+## Restrictions
+
+[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) are applicable.
+
+The following specific restrictions apply to the **Op** operator:
+
+| Restriction | Statement | Origin |
+|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------|
+| `[R1]` | *Brief decsription of the restriction* | Transient |
+| `[R2]` | *Brief decsription of the restriction* | [Link to the restriction in the req document](../../../deliverables/reqs/reqs.md#restriction)
+
+*Note: If the restrictions apply to all types, they can be described only once and be replaced by the following text:
+
+`See [Restrictions](#restrictions).`
+
+*See for instance the case of operator [**MaxPool**](../../spec/informal/maxpool/maxpool.md)*.
+
+## Informal specification
+
+Operator **Op** [...] *description of the purpose of the operator* [...]
+
+
+The effect of the operator is illustrated on the following examples.
+
+### Example 1
+*Example of usage of operator **Op**".*
+
+
+## Error conditions
+*Description of "error conditions", i.e.,cases where the value produced may be unexpected.*
+
+## Attributes
+
+### `ATTR`: int8
+
+*Attribute description.*
+
+*Note: if the attribute does not depend on the type of the argument (real, float, etc.), then it can be described only once (e.g., in the section for real numbers).*
+*See for instance the case of operator [**MaxPool**](../../spec/informal/maxpool/maxpool.md)*.
+
+#### Constraints
+
+ - `[C1]` [See constraint (C1) on A](#C1ia). First constraint on $ATTR$
+ - Statement: *Description of the first constraint on `ATTR`.
+
+
+## Inputs
+
+### $\text{A}$: int8
+
+*Description of the first input.*
+
+#### Constraints
+
+ - `[C1]` First constraint on $A$
+ - Statement: Description of the first constraint on $A$
+
+### $\text{B}$: int8
+
+*Description of the second input.*
+
+#### Constraints
+
+ - `[C1]` First constraint on $B$
+ - Statement: Description of the first constraint on $B$
+
+## Outputs
+
+### $\text{Y}$: int8
+
+*Description of the output.*
+
+#### Constraints
+
+ - `[C1]` First constraint on $Y$
+ - Statement: Description of the first constraint on $Y$
+
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/lifecycle.md b/safety-related-profile/sonnx/ops/docs/guidelines/lifecycle.md
new file mode 100644
index 00000000..d05ccc11
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/lifecycle.md
@@ -0,0 +1,22 @@
+The lifecycle of the informal specification, formal specification, and tests item is managed using Github's [SONNX project](https://github.com/users/ericjenn/projects/4)
+
+- I you don't have write access, please ask Eric.
+
+- If you start working on a new operator, create an item in the "Dev in progress" column.
+ - Use the following conventions (replace "informal" by "formal" or "test" depending on the item).
+ - Use
+ - Title: Informal space ""
+ - Description: Develop informal specification of operator [](https://onnx.ai/onnx/operators/onnx__.html)
+ - Assign it to you
+ - Set the label to "Informal", "Formal" or "Test" in order to facilitate filtering.
+- Then, move the item to the appropriate column depending on its state.:
+ - Dev in progress: (the spec / code / ...) is being developed
+ - Ready for review: the item has been created and is ready to be reviewed
+ - Review in progress: the item is being reviewed
+ - Review complete: the item has been reviewed, it is ready for corrections
+ - Correction in progress: the item is being corrected. Once corrected, it moves either to "Completed" if no new review is necessary or to "Ready for review" if a new review is necessary.
+ - Corrections completed: the item has been corrected according to the review. Reviewers can chek that the corrections have been correctly taken into account. Once OK, the item moves to "completed".
+ - Completed
+
+- Items in the kanban are reviewed during the SONNX meetings.
+- Use the Github's "Comment" to make your comments (preferably), or create a file ".md" in a "Reviews" folder of the directory corresponding to the operator (see [here](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/add) for the add operator).
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-em.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-em.md
new file mode 100644
index 00000000..e03f0178
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-em.md
@@ -0,0 +1,477 @@
+**EM: my notes and correction are in bold**
+
+## Numerical Accuracy Specification
+
+### Introduction
+
+#### Objectives and limits
+
+This document provides theory and techniques to write a formal specification for the
+accuracy of numerical components used in neural networks. The result is
+a specification that should be verified by any implementation, **under a narrow set of acceptable assumption. Here is a list of accepted assumptions:**
+
+* floating-point computation **conforming** to the IEEE-754 standard
+* fixed-point computation **[EM: for a given bit width? AFAIK, the ONNX standard only offers integer types, ergo any treatment of integer vs fractional part is left implicit]**
+* concrete or symbolic range for the input values **[EM: do we restict the language in which symbolic ranges/constraints can be expressed? Any first-order logical expression over the reals?]**
+* symbolic constraints over some computations
+
+There **may** exists **different** specifications for the accuracy **of the same operator**. For instance, the specification for the matrix multiplication will be different if one matrix is diagonal or if both matrices are dense with a same order of magnitude for every coefficients.
+
+We will favour short formulas, even if they may seem approximate. **[EM: are they approximate or not?]**
+
+#### Numerical Error
+
+This section provides a tight and verifiable specification of the numerical error
+on the operator's results. **In general, we define the numerical error as** the difference between the current implementation on data with error resulting from previous approximated computations and an ideal algorithm operating on data also coming from previous ideal computations. **I.e. we consider the problem of error accumulation throughout the SONNX graph, rather than studying each operator in isolation**
+
+$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x})$$
+
+This framework decomposes the error into two parts:
+
+* the first, the propagated error, depends on the numerical
+ error and the numerical values of the inputs - in particular, it is independent of the
+ implementation and the storage format
+* the second part, the introduced error, depends on the
+ concrete value of the inputs and the implementation with its storage format.
+
+$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x}) = \textcolor{blue}{(op_{\textit{ideal}}(\overrightarrow{x+e}) - op_{\textit{ideal}}(\overrightarrow{x}))} + \textcolor{red}{(op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x+e}))}$$
+
+The error associated with the result of the operator corresponds to the sum of the propagated errors and the introduced error. **This new error is then propagated to** the next operator.
+
+#### Specification and Verification Strategy
+
+The provided specification **is based on** an over-approximated semantics of the
+numerical error of native computer operations approximating real number
+operations **(e.g. IEEE-754).** In order to preserve the readability of the formulas, the general specification introduces additional (conservative) simplifications compared to the original specifications.
+However, this general specification may be too over-approximated for some specific inputs (**e.g.** tensor representing diagonal matrices). In this case, more precise specific specifications are provided alongside the general specification.
+
+The error specification comes with unit verification scenarios to verify the implementation's conformity. In the absence of value ranges for the inputs, the unit verification scenarios operate on symbolic values and errors to propagate correct formulas throughout the scenario and thus provide a proof for the assertions. In particular, the C implementation generated from the Why3 formal specification must be verified using these scenarios, for example by using symbolic instrumentation libraries.
+
+### Error Propagation
+
+**We split our analysis of numerical error in two parts. First, we analyse the propagation of input error through ideal operators. Then, we analyse the additional error introduced by non-ideal implementations.
+
+#### Ideal Operators
+
+**[EM: this section requires considerable polishing. I leave my suggestions as notes]**
+
+This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of an operator.
+
+It is only for information, since the properties does not depend on the implementation. The formula aims to explain how an input error is amplified by the operator just from its functional description. **[EM: why is it "only for information"? It seems quite important to me: it's one of the two error terms we must compute!]**
+
+**[EM: to make the point more general, why not $f: \mathbb{R}^n \longrightarrow \mathbb{R}^m$?]**
+From the theoretical point of view, let us consider a function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^n$ and
+an existing error for each argument $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$.**[EM: I am not sure this notation is useful. Why not keeping the $x^i+e^i$ notation used above?]**
+
+The **propagated error** of the $f$ function as ideal operator is
+
+$$f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})$$
+
+**[EM: with the simpler notation, this becomes:]**
+$$f(x^0 + e^0, \ldots, x^{n-1} + e^{n-1}) - f(x^0, \ldots, x^{n-1})$$
+**[EM: which would still require the reader to understand that $op_{ideal}$ above has become $f$ here. I would try to minimise the cognitive effort the reader needs to put.]**
+
+Hence if $f$ is derivable two times, the formula**[EM: something is missing here; we cannot differentiate w.r.t. $\delta x^i$ since it is defined as a vector $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$. Furthermore, the definition of $X_{err}\leq 1$ is completely lost on me.]**
+
+$$\sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+ \textit{ where } X_{\textit{err}} = \max(x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{err}}) \leq 1$$
+
+**[EM: I assume we are building a first-order Taylor expansion here w.r.t. the error term. In my simplified notation, it becomes:]**
+$$e_{prop}=\sum_{0 \leq i < n}\frac{\delta }{\delta e^i}f(x^0 + e^0, \ldots, x^{n-1} + e^{n-1})e^i+\mathcal{O}(e_i^2)$$
+**[EM: but it still contains some mysterious terms, e.g. why are we summing over $i$? Shouldn't we compute the full Jacobian matrix, since $f: \mathbb{R}^n \longrightarrow \mathbb{R}^m$? Even for a scalar function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^1$, the Taylor expansion would produce a gradient vector; why not taking its norm, rather than adding the terms (which might cancel out)?]**
+
+is a correct propagated error with the natural following definition for $\mathcal{O}(X^2_{\textit{err}})$:
+
+$$\mathcal{O}(X^2_{\textit{err}}) = \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}$$
+
+If there are no error on the arguments, the propagated error is $0$.
+Otherwise, we **recommend** a first order expression like: **[EM: I vaguely understand the intuition behind taking the L1 norm (absolute value); however, a clearer explanation should be given to the reader (e.g. in terms of soundness of the overapproximation). Also, are we still computing a separate error term for each function output?]**
+
+The absolute value of the propagated error is less or equal than
+
+$$\sum_{0 \leq i < n} \left| \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}}) \right| \times |x^i_{\textit{err}}|$$
+
+##### Example 1 (ideal multiplication)
+
+For the multiplication $f(x, y) = x\times y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$, the **propagated error** $PE(f)$ is
+
+$$\begin{array}{rcl}
+PE(f) & = & (x_{\textit{val}} + x_{\textit{err}})\times (y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} \\
+ & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + x_{\textit{err}}\times y_{\textit{err}}
+\end{array}$$
+
+that is simplified into
+
+$$\begin{array}{rcl}
+PE(f) & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+\end{array}$$
+
+with $\mathcal{O}(X^2_{\textit{err}}) = x_{\textit{err}}\times y_{\textit{err}}$.
+
+Hence, the accuracy definition of the operator will just indicate that
+
+$$\begin{array}{rcl}
+|PE(f)| & \leq & |y_{\textit{val}}| \times |x_{\textit{err}}| + |x_{\textit{val}}|\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})|
+\end{array}$$
+
+The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional, since it is always **[EM: not sure what "optional" means here. That we do not need to estimate the higher-order term because it is small? We might still need to do so if we want a sound over-approximation...]**
+
+$$\begin{array}{rcl}
+ \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\
+ & = & (x_{\textit{val}} + x_{\textit{err}})\times(y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} - (y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}})\\
+ & = & x_{\textit{err}}\times y_{\textit{err}}
+\end{array}$$
+
+##### Example 2 (ideal division)
+
+For the division $g(x, y) = x / y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$, the **propagated error** $PE(g)$ is
+
+$$\begin{array}{rcl}
+PE(g) & = & \frac{x_{\textit{val}} + x_{\textit{err}}}{y_{\textit{val}} + y_{\textit{err}}} - \frac{x_{\textit{val}}}{y_{\textit{val}}} \\
+ & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})}
+\end{array}$$
+
+that is simplified into **[EM: by using the first-order Taylor expansion?]**
+
+$$\begin{array}{rcl}
+PE(g) & = & \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+\end{array}$$
+
+The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional **[EM: ditto]**
+
+$$\begin{array}{rcl}
+ \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\
+ & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} - \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}}\\
+ & = & \frac{x_{\textit{val}}\times y^2_{\textit{err}} - y_{\textit{val}}\times x_{\textit{err}}\times y_{\textit{err}}}{y_{\textit{val}}^2(y_{\textit{val}} + y_{\textit{err}})}
+\end{array}$$
+
+Hence, the accuracy definition of the operator will just indicate that
+
+$$\begin{array}{rcl}
+|PE(g)| & \leq & \frac{1}{|y_{\textit{val}}|}\times |x_{\textit{err}}| + \frac{|x_{\textit{val}}|}{y^2_{\textit{val}}}\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})|
+\end{array}$$
+
+##### Example 3 (ideal matrix multiplication)
+
+Tensor operators require a **more complex** methodology, since the algorithm combine many atomic operations on $\mathbb{R}$. **Again, the objective is deriving a sound** over-approximation of the propagated error.
+
+**[EM: we suddenly switch to code, rather than standard mathematical notation as above. This is not good, since we haven't yet introduced how the code analysis work. In my opinion, examples should always be introduce after the theory.]**
+
+1. Naïve Algorithm Description
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+2. Progressive decoration of the algorithm starting from inner loop **[EM: there is an assumption that the error terms are in the form $|a[i][k]|\leq ae$, rather than element-wise $|a[i][k]|\leq ae[i][j]$. This follows the spirit of simplification stated at the beginning of the document. However, it is a big design choice which (in my experience) will introduce a great deal of over-approximation; thus, it should be given the space it deserves. E.g. create a list of requirements/recommendation and clearly state: "For each variable/tensor $x$ in the SONNX graph, we will compute a single error value $x_e$, such that all variable/tensor entries satisfy $|x[i,j,\ldots,k]|\leq x_e$".]**
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // OAPE(C[i][j]) = 0
+ C[i][j] += A[i][0]*B[0][j];
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ // |PE(C[i][j])| <= a*be + b*ae
+ C[i][j] += A[i][1]*B[1][j];
+ // |PE(C[i][j])| <= 2*a*be + 2*b*ae
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant **[EM: the reader must be well-versed in software verification and static analysis to know what a loop invariant is]**
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // |PE(C[i][j]| = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |PE(C[i][j]| <= k*a*be + k*b*ae
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ If the pattern verify a loop induction, it becomes a loop invariant **[EM: how do we prove it satisfies a loop induction? We do not show it here]**
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // PE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |PE(C[i][j])| <= k*a*be + k*b*ae
+
+ int k = 2;
+ // if |PE(C[i][j])| <= k*a*be + k*b*ae
+ C[i][j] += A[i][k]*B[k][j];
+ ++k;
+ // then |PE(C[i][j])| <= k*a*be + k*b*ae // same formula than the induction hypotheses
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ Then the loop invariant enables to establish the post-condition when exiting the loop **[EM: what is the post condition? We have not specified it]**
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // PE(C[i][j]) = 0
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |OAPE(C[i][j]| <= k*a*be + k*b*ae
+ // |PE(C[i][j])| <= p*a*be + p*b*ae
+ ```
+
+3. Final specification **[EM: shouldn't this be presented at the beginning of Example 3? That way the reader would have an idea of what the objective is.]**
+
+ At the end, we can specify that
+
+ * If A is a matrix of dimension $n \times p$, B a matrix of dimension $p \times q$,
+ * if the absolute value of every coefficient of A is bound by $a$ with an error whose absolute value is bound by $ae$,
+ * if the absolute value of every coefficient of B is bound by $b$ with an error whose absolute value is bound by $be$,
+ * then the propagated error of every coefficient of C is bound by $p\times a\times be + p\times b \times ae$
+
+#### Non-Ideal Operators
+
+This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$,
+the introduced error, where $Y$ is the tensor result of an operator.
+The objective is to provide a specification **over the actual implementation of an operator.** Hence
+
+$$Y_{\textit{err}}^{\textit{intro}} = op_{\textit{impl}}(\overrightarrow{x}) - op_{\textit{ideal}}(\overrightarrow{x})$$
+
+The introduced error is an over-approximation of difference between the implementation result and the ideal result when there is no error on the input.
+
+**[EM: note that we switched back to the notation $op$ instead of $f$. Also, in the document introduction, the implementation error is computed over $\overrightarrow{x+e}$ rather than $\overrightarrow{x}$. The next paragraph reads like an attempt at explaining whether this change of variables makes a difference, but it is not clear enough.]**
+
+From the theoretical point of view, the introduced error depends on the storage format of the result of any intermediate computation. It is always defined as the difference between the implementation result and the ideal result, for which we additionaly substract the propagated error of the previous section.
+
+##### IEEE-754 implementations
+
+**[EM: what about fixed-point/integer/quantised ones? Will we add them to the guidelines later?]
+
+For IEEE-754 format, let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon)
+for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. For every floating-point operator
+whose result $r$ is a normal floating-point number, the introduced error is less or equal than
+$|r| \times \frac{\varepsilon}{2} = |r| \times \textit{\bf u}$ in the standard mode
+round to nearest even. **[EM: this is an oversimplification, as it does not take into account subnormal numbers and catastrophic cancellation. If the result of the operation is $r\approx0$, the actual error might be much larger than $|r|\times\frac{\varepsilon}{2}$]** Moreover if $r \in [a, b]$ with $a$ and $b$ normal numbers (the interval
+may contain denormal numbers), the introduced error is less or equal than
+$\max(|a|, |b|) \times \textit{\bf u}$ in the standard mode round to nearest even.
+There exists more accurate formulas, but such formulas usually take too much
+details into account.**[EM:I approve of our simplification objective, but is our error estimate sound? If not, I feel we should give the readers more details on our design decisions, to convince them that what we are doing is sensible.]**
+
+###### Example 4 (IEEE-754 multiplication)
+
+For the multiplication $f(x, y) = x * y$, the **introduced error** $IE(f)$ should be less or equal than **[EM: are we using "should" as a prescriptive word here (e.g. any SONNX-compliant implementation must satisfy this)? It is the first truly prescriptive statement in this document.]**
+
+$$\begin{array}{rcl}
+|IE(f)| & \leq & |x|\times |y|\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided $|x|\times |y|$ is a normal number **[EM: should we give some extra context in these examples. E.g. "the IEEE-754 standard mandates that any implementation of the multiplication operator $x_{float}\times_{float} y_{float}$ must yield the same result as executing the operations in infinite-precision (ideal) arithmetic and then rounding the result, i.e. $float(x_{float}\times y_{float})$. Hence, the error only depends from the magnitude of the result $|x|\times|y|$ and the representable precision $|u|$."]**
+
+$$\begin{array}{rcl}
+|IE(f)| & \leq & \max(|a|, |b|)\times \max(|c|, |d|)\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number.
+
+$$\begin{array}{rcl}
+|IE(f)| & < & \max(|a|, |b|)\times \max(|c|, |d|)\times\varepsilon
+\end{array}$$
+
+for any floating-point implementation with other rounding mode, provided
+$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number.
+
+
+$$\begin{array}{rcl}
+|IE(f)| & < & 1
+\end{array}$$
+
+for any fixpoint implementation. **[EM: we only mentioned fixed-point implementation en passant so far. This error definition makes me think we are referring to integer implementations, otherwise the error would be fractional. Also, how does ONNX/SONNX deals with integer overflows?]**
+
+###### Example 5 (IEEE-754 division)
+
+For the division $g(x, y) = x / y$, the **introduced error** $IE(g)$ should be less or equal than
+
+$$\begin{array}{rcl}
+|IE(g)| & \leq & \frac{|x|}{|y|}\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$\frac{|x|}{|y|}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & \leq & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & < & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\varepsilon
+\end{array}$$
+
+for any floating-point implementation with other rounding mode, provided
+$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & < & 1
+\end{array}$$
+
+for any fixpoint implementation. **[EM: see the above comments about fixed-point for multiplication (no overflow here).]**
+
+###### Example 6 (IEEE-754 2D matrix multiplication)
+
+Tensor operators require a methodology to find an **over-approximation of the introduced error**, since their algorithms combine many atomic operations. **[EM: many of my comments on Example 3 are also valid here]**
+
+1. Naïve Algorithm Description
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+2. Progressive decoration of the algorithm starting from inner loop **[EM: I do not understand where the term $2*a*b*(1+u)*u$ comes from. Is it a result of the accumulation/addition? More explanation is needed.]**
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ C[i][j] += A[i][0]*B[0][j];
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ // |IE(C[i][j])| <= a*b*u
+ // |C[i][j]| <= a*b*(1+u)
+ C[i][j] += A[i][1]*B[1][j];
+ // |IE(C[i][j])| <= a*b*u + a*b*u + 2*a*b*(1+u)*u = a*b*u*(4+2u)
+ // |C[i][j])| <= (a*b*(1+u) + a*b*(1+u))*(1+u) = 2*a*b*(1+u)²
+ C[i][j] += A[i][2]*B[2][j];
+ // |IE(C[i][j])| <= a*b*u*(4+2u) + a*b*u + (2*a*b*(1+u)² + a*b*(1+u))*u = a*b*u*(8+7u+2u²)
+ // |C[i][j]| <= (2*a*b*(1+u)² + a*b*(1+u))*(1+u) = (3+2*u)*a*b*(1+u)²
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ If the pattern verify a loop induction, it becomes a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+
+ int k = 2;
+ // if |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // and if |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ // if OAPE(C[i][j]) <= k*a*be + k*b*ae
+ C[i][j] += A[i][k]*B[k][j];
+ ++k;
+ // then |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses
+ // and then |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ Then the loop invariant enables to establish the post-condition when exiting the loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ // |IE(C[i][j])| <= (((1+u)^(p+1)-1)/u*(1+u)² - p-1)*a*b // same formula than the induction hypotheses
+ // |C[i][j])| <= ((1+u)^(p+1)-1)/u*a*b*(1+u)²
+ ```
+
+Hence, for the matrix multiplication of A, matrix of dimension $n \times p$ with B, matrix of dimension $p \times q$, the **introduced error** should be less or equal than **[EM: more explanation on this is needed. (1) why is this a sound over-approximation and (2) how can you automatically compute it for an arbitrary implementation?]**
+
+$$\begin{array}{rcl}
+ |IE(C[i][j])| & \leq & \left(\frac{(1+u)^{p+1}-1}{u}\times(1+u)^2 - p-1\right)\times a \times b\\
+ & \leq & \left(\frac{(1+u)^{p+1}-1-(p+1)\times u }{u^2}\times(1+u)^2 - (p+1)\times(2+u)\right)\times a \times b \times u\\
+\end{array}$$
+
+where the absolute value of every coefficient of A is bound by $a$ and the absolute value of every coefficient of B is bound by $b$.
+
+### Unit Verification
+
+**[EM: incomplete section]**
+
+This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields:
+
+* The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations.
+* The `float` field is a symbolic abstract domain for the computed value.
+* The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`.
+* The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`.
+
+# Formal specification guidelines
+
+*To be completed.*
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md
new file mode 100644
index 00000000..e3c299eb
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md
@@ -0,0 +1,481 @@
+# Objectives and limits
+
+## Introduction
+This document provides guidelines to conduct the accuracy analysis of the SONNX numerical operators.
+
+SONNX does not provide a definitive specification of accuracy. Instead, it provides a way to estimate the error of a specific implementation with respect to an ideal algorithm that would implement integer arithmetic or floating point arithmetic according to IEEE754.
+
+Towards that goal, SONNX provides:
+- an analytical expression of the upper bound of the error considering some ideal algorithm applying integer or IEEE 754 arithmetic
+- a tool to estimate numerically an upper bound of the error of a given implementation.
+
+Notes:
+1. There exist multiple ideal algorithms for any operator. In SONNX, we use the algorithm that is given in the informal specification.
+2. There exists an infinite number of upper bounds of the error, some of them being more conservative (less accurate) than others. The accuracy of the estimation first depends on the simplifications done to facilitate calculus. It may also depend on the hypotheses done concerning the domain of the operator arguments. For instance, the estimation of the error bound for a matrix multiplication may be different under the hypothesis that one matrix is diagonal or under the hypothesis that both matrices are dense with a same order of magnitude for every coefficients.
+In SONNX, we estimate accuracies according to the following principles:
+ - we favour manageable formulas (simple and short) even if this leads to very conservative accuracy estimation
+ - we make no hypothesis about the domain of the arguments of operators.
+
+
+# Principles of Numerical Accuracy Estimation
+
+The numerical error is the difference between the implemented algorithm $op_{\textit{impl}}$ applied on data with error resulting from previous approximated computations and an ideal algorithm $op_{\textit{ideal}}$ operating on data also coming from previous ideal computations:
+
+$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x})$$
+
+We decompose the error into
+
+* a propagated error depending on the numerical
+ error and the numerical values of the inputs $-$ in particular, it is independent of the implementation and the storage format
+* a introduced error depending on the
+ concrete value of the inputs and the implementation with its storage format.
+
+$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x}) = \textcolor{blue}{(op_{\textit{ideal}}(\overrightarrow{x+e}) - op_{\textit{ideal}}(\overrightarrow{x}))} + \textcolor{red}{(op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x+e}))}$$
+
+The error associated with the result of the operator corresponds to the sum of the propagated errors and the introduced error and this new error is then propagated by the next operator.
+
+In order to simplify the calculus and preserve the readability of the formulas, the provided estimations may result from the application of conservative simplifications of the native computer operations (e.g., IEEE 754). In some cases, additional hypotheses about the operator arguments (e.g., the fact that the matrices are diagonal) are done alongside the general formulation to provide more accurate estimations.
+
+The error specification comes with unit verification scenarios to verify the implementation's conformity. In the absence of value ranges for the inputs, the unit verification scenarios operate on symbolic values and errors to propagate correct formulas throughout the scenario and thus provide a proof for the assertions. In particular, the C implementation generated from the Why3 formal specification must be verified using these scenarios, for example by using symbolic instrumentation libraries.
+
+## Error Propagation
+
+This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of an operator.
+**It is only for information**, since the properties does not depend on the implementation. The formula aims to explain how an input error is amplified by the operator just from its functional description.
+
+From the theoretical point of view, let us consider a function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^n$ and
+an existing error for each argument $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$.
+
+### Estimation of the propagated error
+The **propagated error** of the $f$ function as ideal operator is
+
+$$f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})$$
+
+Hence if $f$ is derivable two times, the formula
+
+$$\sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+ \textit{ where } X_{\textit{err}} = \max(x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{err}}) \leq 1$$
+
+is a correct propagated error with the natural following definition for $\mathcal{O}(X^2_{\textit{err}})$:
+
+$$\mathcal{O}(X^2_{\textit{err}}) = \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}$$
+
+The absolute value of the propagated error is less or equal than
+
+$$\sum_{0 \leq i < n} \left| \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}}) \right| \times |x^i_{\textit{err}}|$$
+
+### Example 1: Multiplication
+
+Let us consider the multiplication operation $f(x, y) = x\times y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$
+
+For the multiplication, the **propagated error** $PE(f)$ is
+
+$$\begin{array}{rcl}
+PE(f) & = & (x_{\textit{val}} + x_{\textit{err}})\times (y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} \\
+ & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + x_{\textit{err}}\times y_{\textit{err}}
+\end{array}$$
+
+that is simplified into
+
+$$\begin{array}{rcl}
+PE(f) & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+\end{array}$$
+
+with $\mathcal{O}(X^2_{\textit{err}}) = x_{\textit{err}}\times y_{\textit{err}}$.
+
+Hence,
+
+$$\begin{array}{rcl}
+|PE(f)| & \leq & |y_{\textit{val}}| \times |x_{\textit{err}}| + |x_{\textit{val}}|\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})|
+\end{array}$$
+
+### Example 2: Division
+
+Let us consider the division operation $g(x, y) = x / y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$
+
+For the division, the **propagated error** $PE(g)$ is
+
+$$\begin{array}{rcl}
+PE(g) & = & \frac{x_{\textit{val}} + x_{\textit{err}}}{y_{\textit{val}} + y_{\textit{err}}} - \frac{x_{\textit{val}}}{y_{\textit{val}}} \\
+ & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})}
+\end{array}$$
+
+that is simplified into
+
+$$\begin{array}{rcl}
+PE(g) & = & \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}})
+\end{array}$$
+
+The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional
+
+$$\begin{array}{rcl}
+ \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\
+ & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} - \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}}\\
+ & = & \frac{x_{\textit{val}}\times y^2_{\textit{err}} - y_{\textit{val}}\times x_{\textit{err}}\times y_{\textit{err}}}{y_{\textit{val}}^2(y_{\textit{val}} + y_{\textit{err}})}
+\end{array}$$
+
+Hence, the accuracy definition of the operator will just indicate that
+
+$$\begin{array}{rcl}
+|PE(g)| & \leq & \frac{1}{|y_{\textit{val}}|}\times |x_{\textit{err}}| + \frac{|x_{\textit{val}}|}{y^2_{\textit{val}}}\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})|
+\end{array}$$
+
+### Example 3: 2D Matrix multiplication
+
+Tensor operators require a methodology to propose an **over-approximation of the propagated error**, since the algorithm combine many atomic operations on $\mathbb{R}$.
+
+1. Naïve Algorithm Description (as specified in SONNX informal specification)
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+2. Progressive decoration of the algorithm starting from inner loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // OAPE(C[i][j]) = 0
+ C[i][j] += A[i][0]*B[0][j];
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ // |PE(C[i][j])| <= a*be + b*ae
+ C[i][j] += A[i][1]*B[1][j];
+ // |PE(C[i][j])| <= 2*a*be + 2*b*ae
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // |PE(C[i][j]| = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |PE(C[i][j]| <= k*a*be + k*b*ae
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ If the pattern verify a loop induction, it becomes a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // PE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |PE(C[i][j])| <= k*a*be + k*b*ae
+
+ int k = 2;
+ // if |PE(C[i][j])| <= k*a*be + k*b*ae
+ C[i][j] += A[i][k]*B[k][j];
+ ++k;
+ // then |PE(C[i][j])| <= k*a*be + k*b*ae // same formula than the induction hypotheses
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ Then the loop invariant enables to establish the post-condition when exiting the loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae'
+ // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // PE(C[i][j]) = 0
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |OAPE(C[i][j]| <= k*a*be + k*b*ae
+ // |PE(C[i][j])| <= p*a*be + p*b*ae
+ ```
+
+3. Final specification
+
+ At the end, we can specify that
+
+ * If A is a matrix of dimension $n \times p$, B a matrix of dimension $p \times q$,
+ * if the absolute value of every coefficient of A is bound by $a$ with an error whose absolute value is bound by $ae$,
+ * if the absolute value of every coefficient of B is bound by $b$ with an error whose absolute value is bound by $be$,
+ * then the propagated error of every coefficient of C is bound by $p\times a\times be + p\times b \times ae$
+
+## Error Introduction
+
+This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$,
+the introduced error, where $Y$ is the tensor result of an operator.
+The objective is to provide a specification that any implementation should respect. Hence
+
+$$Y_{\textit{err}}^{\textit{intro}} = op_{\textit{impl}}(\overrightarrow{x}) - op_{\textit{ideal}}(\overrightarrow{x})$$
+
+From the theoretical point of view, the introduced error depends on the storage format of the result of any intermediate computation.
+It is always defined as the difference between the implementation result and the ideal result, for which we additionaly
+substract the propagated error of the previous section.
+
+The introduced error is an over-approximation of difference between the implementation result and the ideal result
+when there is no error on the input.
+
+For IEEE-754 format, let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon)
+for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. For every floating-point operator
+whose result $r$ is a normal floating-point number, the introduced error is less or equal than
+$|r| \times \frac{\varepsilon}{2} = |r| \times \textit{\bf u}$ in the standard mode
+round to nearest even. Moreover if $r \in [a, b]$ with $a$ and $b$ normal numbers (the interval
+may contain denormal numbers), the introduced error is less or equal than
+$\max(|a|, |b|) \times \textit{\bf u}$ in the standard mode round to nearest even.
+
+Note that there exists more accurate formulas, but by application of the principles stated introduction, we use a formulation that is remains simple and acceptably conservative.
+
+### Example 1: multiplication
+Let us consider the multiplication operation: $f(x, y) = x * y$
+
+The **introduced error** $IE(f)$ should be less or equal than
+
+$$\begin{array}{rcl}
+|IE(f)| & \leq & |x|\times |y|\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$|x|\times |y|$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(f)| & \leq & \max(|a|, |b|)\times \max(|c|, |d|)\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number.
+
+$$\begin{array}{rcl}
+|IE(f)| & < & \max(|a|, |b|)\times \max(|c|, |d|)\times\varepsilon
+\end{array}$$
+
+for any floating-point implementation with other rounding mode, provided
+$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number.
+
+
+$$\begin{array}{rcl}
+|IE(f)| & < & 1
+\end{array}$$
+
+for any integer implementation.
+
+### Example 2: division
+Let us consider the division operation: $g(x, y) = x / y$
+
+The **introduced error** $IE(g)$ should be less or equal than
+
+$$\begin{array}{rcl}
+|IE(g)| & \leq & \frac{|x|}{|y|}\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$\frac{|x|}{|y|}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & \leq & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\textit{\bf u}
+\end{array}$$
+
+for any floating-point implementation with the standard rounding mode round to nearest even, provided
+$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & < & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\varepsilon
+\end{array}$$
+
+for any floating-point implementation with other rounding mode, provided
+$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number
+
+$$\begin{array}{rcl}
+|IE(g)| & < & 1
+\end{array}$$
+
+for any integer implementation.
+
+### Example 3: 2D Matrix multiplication
+
+Tensor operators require a methodology to find an **over-approximation of the introduced error**,
+since their algorithms combine many atomic operations.
+
+1. Naïve Algorithm Description
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+2. Progressive decoration of the algorithm starting from inner loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ C[i][j] += A[i][0]*B[0][j];
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ // |IE(C[i][j])| <= a*b*u
+ // |C[i][j]| <= a*b*(1+u)
+ C[i][j] += A[i][1]*B[1][j];
+ // The introduced error is the one due to the multiplication
+ // at [0] and at [1]: a*b*u + a*b*u
+ // plus the one due to the addition: (a*b*(1+u)+a*b(1+u))*u
+ // |IE(C[i][j])| <= a*b*u + a*b*u + 2*a*b*(1+u)*u = a*b*u*(4+2u)
+ // |C[i][j])| <= (a*b*(1+u) + a*b*(1+u))*(1+u) = 2*a*b*(1+u)²
+ C[i][j] += A[i][2]*B[2][j];
+ // |IE(C[i][j])| <= a*b*u*(4+2u) + a*b*u + (2*a*b*(1+u)² + a*b*(1+u))*u = a*b*u*(8+7u+2u²)
+ // |C[i][j]| <= (2*a*b*(1+u)² + a*b*(1+u))*(1+u) = (3+2*u)*a*b*(1+u)²
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ for (int k = 2; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ If the pattern verifies a loop induction, it becomes a loop invariant
+
+> Instead of "loop invariant", couln't we use the more "classical" terminology of induction reasoning? induction hypothesis (this is actually what you are using below)
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < 2; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+
+ int k = 2;
+ // if |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b
+ // and if |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ // if OAPE(C[i][j]) <= k*a*be + k*b*ae
+ C[i][j] += A[i][k]*B[k][j];
+ ++k;
+ // then |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses
+ // and then |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ for (int k = 3; k < p; ++k)
+ C[i][j] += A[i][k]*B[k][j];
+ ```
+
+ Then the loop invariant enables to establish the post-condition when exiting the loop
+
+ ```c++
+ // A matrix of dimension n x p, B matrix of dimension p x q.
+ // C result matrix of dimension n x q
+ // the absolute value of every coefficient of A is bound by 'a'
+ // the absolute value of every coefficient of B is bound by 'b'
+ for (int i = 0; i < n; ++i)
+ for (int j = 0; j < q; ++j) {
+ C[i][j] = 0;
+ // IE(C[i][j]) = 0
+ for (int k = 0; k < p; ++k)
+ C[i][j] += A[i][k]*B[l][j];
+ // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses
+ // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)²
+ // |IE(C[i][j])| <= (((1+u)^(p+1)-1)/u*(1+u)² - p-1)*a*b // same formula than the induction hypotheses
+ // |C[i][j])| <= ((1+u)^(p+1)-1)/u*a*b*(1+u)²
+ ```
+
+Hence, for the matrix multiplication of A, matrix of dimension $n \times p$ with B, matrix of dimension $p \times q$, the **introduced error** should be less or equal than
+
+$$\begin{array}{rcl}
+ |IE(C[i][j])| & \leq & \left(\frac{(1+u)^{p+1}-1}{u}\times(1+u)^2 - p-1\right)\times a \times b\\
+ & \leq & \left(\frac{(1+u)^{p+1}-1-(p+1)\times u }{u^2}\times(1+u)^2 - (p+1)\times(2+u)\right)\times a \times b \times u\\
+\end{array}$$
+
+where the absolute value of every coefficient of A is bound by $a$ and the absolute value of every coefficient of B is bound by $b$.
+
+
+> Are we sure to be able to do the same analysis for higher dimension tensors?
+
+## Unit Verification
+
+In the previous sections, we have described how to derive a possible error upper bound considering (i) the semantics of computer arithmetic in integers and floating point numbers and (ii) the algorithm used to define the operator in the informal and formal specifications. In this section, we propose a solution to evaluate the error upper bound on an actual C/C++ implementation. Examples are given for the reference implementation developed in SONNX. The same approach can be applied on a end-user implementation.
+
+The solution uses an abstract type SymbolicDomainError replacing each real number in the Why3 specification. SymbolicDomainError is a data structure with 4 fields:
+
+> Why do you refer to the specification? Should n't we write "replacing each floating point number in the implementation"?
+
+The real field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations.
+The float field is a symbolic abstract domain for the computed value.
+The err field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of float and real.
+The rel_err field is a symbolic abstract domain for the relative error, that is the difference between the possible values of float and real divided by real.
+Tensor A, B;
+
+> It would be nice to give links to the library (e.g., github with user manual).
+
+### Example : division
+
+> It would be nice to give the actual result of the analysis.
+
+```c++
+Tensor A, B;
+
+/* A, B symbolic initialization */
+
+auto result = [&A,&B](auto I) {
+ return (B[I].real != 0) ? A[I] / B[I] :
+ /* undefined */ SymbolicDomainError::undef();
+};
+
+for (auto I : A.indexes()) {
+ auto a = A[I];
+ auto b = B[I];
+ if (b.real != 0 && b.real + b.err != 0) {
+ auto c = result(I);
+ double bound = std::abs(a.err / b.real) +
+ std::abs(a.real * b.err / (b.real * b.real));
+ assert(std::abs(c.err) <= bound + 1e-12);
+ }
+}
+```
+
diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Jean.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Jean.md
new file mode 100644
index 00000000..22a4f7c0
--- /dev/null
+++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Jean.md
@@ -0,0 +1,325 @@
+Reviewer: Jean Souyris
+
+# Introduction
+
+This document gives guidelines to be followed when writing an operator's
+- informal specification
+- formal specifications.
+
+# Informal specification
+
+> Remark 1: I propose to change the title "informal specification" to "Guidelines for the informal specification" or "Informal specification guidelines". Same remark for the future guidelines for the formal specification.
+
+>> OK: done
+
+> Remark 2: I propose to rename the two level-2 sub-sections "General recommendations" and "Structure" into "General guidelines" and "Structure of the informal specification" and to present them here as follows:
+>
+> This section is composed of two sub-sections:
+> - "General guidelines", which expresses general recommendations, defines the use of specific fonts, some notations (e.g., for tensor) and tags (e.g., for constraints), and, finally, specifies how to deal with the numerical types involved in the operator at stake.
+> - "Structure of the informal specification", which defines the structure and contents of the informal specification of an operator. This section makes use of the guidelines expressed in sub-section "General guidelines".
+
+>> OK: done.
+
+## General recommendations
+
+### Keep it simple!
+
+> Remark 1: I propose to change the title "Keep it simple" to "General recommendations"
+>> OK: done
+
+> Remark 2: since we now have precise and detailed guidelines, this section should be adapted. Indeed, the simplicity should come from the strict application of the guidelines. The risk in keeping this section as it is, is to minimize the importance of the informal specification, e.g., part of the verification of the formal one will be performed against it.
+>
+> Proposal for the whole contents of this section:
+>
+> The informal specification is intended for readers who want to know how to use an operator, as well as for the ones who need to implement and verify a neural network from an ONNX model. For instance, the first kind of readers might be satisfied with one or two sentences about the semantics of an operator whereas the second category of readers would like to get all the details of the semantics.
+>
+> More precisely, the informal specification:
+
+> - Is aimed at showing clearly what a given operator is supposed to do,
+> - Without calling on a strict formal, mathematical language,
+> - Knowing that the exact and complete specification is given in the "formal" part.
+> - May provide diagrams and examples to make things clear.
+
+> The writer of the informal specification of an operator shall have constantly in mind the following recommendation: "Keep it simple!", while applying the rules expressed in the guidelines.
+
+> Remove the following five bullets.
+>
+- Basically, the informal specification is a documentation, an "operator user's manual".
+- It is aimed at showing clearly what a given operator is supposed to do, but without calling on a strict formal, mathematical language.
+- The exact and complete specification is given in the "formal" part.
+- The informal specification shall use greek with extreme parsimony ;-)
+- The informal specification can provide diagrams and examples to make things clear.
+
+> End of proposal.
+
+
+>> OK: done
+
+
+### Fonts
+- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "pads" attribute is represented by `pads`.
+
+### Notations
+
+> Remark 1: the term "shape" should be used instead of dimensions.
+
+>> OK: done
+
+- Tensors:
+ - A tensor is always represented in uppercase name (e.g., A, B,...,X, Y, Z).
+ - Input tensors are usually $A$, $B$,...
+ - Output tensor is $Y$
+ - In the case of a variadic operator (e.g., "concat"), the tensor parameters are designated by an index: $X_0$, $X_1$, etc. Indexes start at 0 to be consistent with the other use of indexes.
+ - The dimensions of a tensor $X$ are denoted by a vector $(dX_0, ..., dX_i, ..., dX_n)$ where $dX_i$ refers to the dimension along axis $i$. The index of the first axis is 0.
+ - The numerical errors of a tensor $X$ are always represented by a tensor $X_{\textit{err}}$.
+ It is the difference between the tensor $X_{\textit{impl}}$ computed by an implementation
+ and the infinitly accurate tensor $X_{\textit{real}}$ expressed by the formal specification.
+ In the section on numerical accuracy, the notation $X_{\textit{real}}$ is replaced by $X$ unless it introduces ambiguity.
+ - For a tensor used as a variadic parameter (denoted $X_i$), the dimensions become $(dX_{i,0}, dX_{i,1}, ...)$. This notation is consistent with the way tensor dimensions are encoded in Why3.
+ - In cases where this naming convention does not match the one used by ONNX, a correspondence table may be established (e.g., $dX_2$ corresponds to the "width" of tensor $X$).
+
+### Tags
+The informal specification makes use of three different types of tags:
+- A **restrictions tag** expresses a restriction with respect to the ONNX standard (see the section about restriction below). They are indicated in the text with the tag `[R]` where `` is a number.\
+A synthesis of all restrictions is given in section "Restrictions" (see below).
+- A **constraints tag** expresses a constraint on one or several inputs, output or attribute. They are indicated using `[C]` where `` is a number.
+- A **traceability tag** identifies a specific location in the informal specification. These tags are used to establish a traceability between the informal and formal specification. They are indicated using `T` where `` is a number.
+
+> Remark 1: add [] around `T`.
+
+>> OK: I propose to remove the brackets and keep the non-serif font.
+
+> Remark 2: give an example of the declaration and use of a tag.
+
+>> OK: done
+
+### Types
+- All operators applicable to numeric values shall be specified for values in the domain of real numbers.
+- Specific description may be given for the other types (``float``, ``double``, etc.).
+- A description can be applicable to a set of types as long as its **semantics description** remains the same for all types in the set. A counter example is, for instance, the case of operators applied on ``float`` or ``double`` that may create ``NaNs`` or ``+Inf`` or ``-Inf``. For this reason, they cannot be covered by the specification in $R$.
+- Only the sections that need to be modified are repeated.
+
+> Remark 1: explicit the above sentence. See MatMul.
+
+>> OK: done
+
+## Structure
+
+> Remark 1: I propose to change the title of this section to "Structure of the informal specification"
+
+>> OK: done
+
+The specification on an operator is structured as follows.
+
+> Remark 2: introduce the "Contents" section as in MatMul.
+>
+> Proposal:
+
+> ### Contents
+
+> *This section lists the various per input set of types informal specifications of the operator at stake. Only the first line below is mandatory, i.e., the one for real inputs. Any subsequent line comes from the necessity to specialize the informal specification for the real to machine types*
+
+> - `` [operator (real)](#real)
+> - `` [operator (comma-separated list of types)]
+> - `` [operator (other comma-separated list of types)]
+> - etc
+
+> Example (MatMul):
+>
+> Contents
+- `MatMul` [operator (real)](#real)
+- `MatMul` [operator (FP16, FP32, FP64, BFLOAT16)](#float)
+- `MatMul` [operator (INT4, INT8, INT16, INT32, INT64, UINT4, UINT8, UINT16, UINT32, UINT64)](#int)
+
+> End of proposal.
+
+>> OK: done
+
+### Signature
+*Definition of the operator's signature:*
+
+ ` = (,,...)`
+
+ where
+ - `