-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
major updates
- Loading branch information
Showing
28 changed files
with
1,144 additions
and
1,083 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,10 @@ | ||
[run] | ||
branch = True | ||
source = . | ||
|
||
[report] | ||
exclude_lines = | ||
if __name__ == '__main__': | ||
|
||
[html] | ||
directory = coverage_html_report |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
name: CI Build and Test | ||
|
||
on: | ||
push: | ||
branches: | ||
- main # Run on push to the main branch | ||
pull_request: | ||
branches: | ||
- main # Run on pull requests to the main branch | ||
workflow_dispatch: | ||
|
||
jobs: | ||
build-and-test: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
# Step 1: Check out the repository | ||
- name: Check out the repository | ||
uses: actions/checkout@v4 | ||
|
||
# Step 2: Set up Python environment | ||
- name: Set up Python | ||
uses: actions/setup-python@v4 | ||
with: | ||
python-version: '3.10' | ||
|
||
- name: Install Python Dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install -r test_requirements.txt | ||
- name: Run Python Tests with Coverage | ||
run: | | ||
pytest --maxfail=5 --disable-warnings --cov=. --cov-report=xml | ||
- name: Upload Python Coverage to Codecov | ||
uses: codecov/codecov-action@v4 | ||
with: | ||
flags: python | ||
env: | ||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} | ||
|
||
# Step 3: Install C++ build tools | ||
- name: Install C++ Build Tools | ||
run: sudo apt-get update && sudo apt-get install -y build-essential cmake g++ lcov | ||
|
||
# Step 4: Configure and Build C++ Project | ||
- name: Configure and Build C++ Project | ||
run: | | ||
mkdir -p build | ||
cd build | ||
cmake -DCMAKE_CXX_FLAGS="--coverage" .. # Enable coverage flags | ||
make | ||
# Step 5: Run C++ Tests | ||
- name: Run C++ Tests | ||
run: | | ||
cd build | ||
./NNet # Replace `NNet` with your executable name | ||
# Step 6: Generate C++ Coverage Report | ||
- name: Generate C++ Coverage Report | ||
run: | | ||
cd build | ||
lcov --capture --directory . --output-file coverage.info | ||
lcov --list coverage.info | ||
# Step 7: Upload C++ Coverage to Codecov | ||
- name: Upload C++ Coverage to Codecov | ||
uses: codecov/codecov-action@v4 | ||
with: | ||
flags: cpp | ||
files: build/coverage.info # Path to the coverage report | ||
env: | ||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
cmake_minimum_required(VERSION 3.10) | ||
|
||
# Project name | ||
project(NNet) | ||
|
||
# Set C++ standard | ||
set(CMAKE_CXX_STANDARD 17) | ||
set(CMAKE_CXX_STANDARD_REQUIRED True) | ||
|
||
# Add source directory | ||
include_directories(cpp) | ||
|
||
# Add the executable | ||
add_executable(NNet | ||
cpp/main.cpp | ||
cpp/nnet.cpp | ||
) | ||
|
||
# Enable code coverage flags for GCC/Clang | ||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") | ||
message(STATUS "Enabling code coverage flags") | ||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage") | ||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,86 +1,102 @@ | ||
import numpy as np | ||
import sys | ||
import onnx | ||
import numpy as np | ||
from onnx import helper, numpy_helper, TensorProto | ||
from NNet.utils.readNNet import readNNet | ||
from NNet.utils.normalizeNNet import normalizeNNet | ||
import argparse | ||
from typing import Optional | ||
|
||
def nnet2onnx( | ||
nnetFile: str, | ||
onnxFile: Optional[str] = "", | ||
outputVar: str = "y_out", | ||
inputVar: str = "X", | ||
normalizeNetwork: bool = False | ||
) -> None: | ||
""" | ||
Convert a .nnet file to ONNX format. | ||
def nnet2onnx(nnetFile, onnxFile="", outputVar = "y_out", inputVar="X", normalizeNetwork=False): | ||
''' | ||
Convert a .nnet file to onnx format | ||
Args: | ||
nnetFile: (string) .nnet file to convert to onnx | ||
onnxFile: (string) Optional, name for the created .onnx file | ||
outputName: (string) Optional, name of the output variable in onnx | ||
normalizeNetwork: (bool) If true, adapt the network weights and biases so that | ||
networks and inputs do not need to be normalized. Default is False. | ||
''' | ||
if normalizeNetwork: | ||
weights, biases = normalizeNNet(nnetFile) | ||
else: | ||
weights, biases = readNNet(nnetFile) | ||
|
||
nnetFile (str): Path to the .nnet file to convert. | ||
onnxFile (Optional[str]): Optional, name for the created .onnx file. Defaults to the same name as the .nnet file. | ||
outputVar (str): Optional, name of the output variable in ONNX. Defaults to 'y_out'. | ||
inputVar (str): Name of the input variable in ONNX. Defaults to 'X'. | ||
normalizeNetwork (bool): If True, adapt the network weights and biases so that networks and inputs | ||
do not need normalization. Defaults to False. | ||
""" | ||
try: | ||
if normalizeNetwork: | ||
weights, biases = normalizeNNet(nnetFile) | ||
else: | ||
weights, biases = readNNet(nnetFile) | ||
except FileNotFoundError: | ||
print(f"Error: The file {nnetFile} was not found.") | ||
sys.exit(1) | ||
|
||
inputSize = weights[0].shape[1] | ||
outputSize = weights[-1].shape[0] | ||
numLayers = len(weights) | ||
# Default onnx filename if none specified | ||
if onnxFile=="": | ||
onnxFile = nnetFile[:-4]+'onnx' | ||
# Initialize graph | ||
inputs = [helper.make_tensor_value_info(inputVar, TensorProto.FLOAT, [inputSize])] | ||
|
||
# Default ONNX filename if none specified | ||
if not onnxFile: | ||
onnxFile = f"{nnetFile[:-5]}.onnx" | ||
|
||
# Initialize the graph | ||
inputs = [helper.make_tensor_value_info(inputVar, TensorProto.FLOAT, [inputSize])] | ||
outputs = [helper.make_tensor_value_info(outputVar, TensorProto.FLOAT, [outputSize])] | ||
operations = [] | ||
initializers = [] | ||
# Loop through each layer of the network and add operations and initializers | ||
|
||
# Build the ONNX model layer by layer | ||
for i in range(numLayers): | ||
|
||
# Use outputVar for the last layer | ||
outputName = "H%d"%i | ||
if i==numLayers-1: | ||
# Use the output variable name for the last layer | ||
outputName = f"H{i}" | ||
if i == numLayers - 1: | ||
outputName = outputVar | ||
|
||
# Weight matrix multiplication | ||
operations.append(helper.make_node("MatMul",["W%d"%i,inputVar],["M%d"%i])) | ||
initializers.append(numpy_helper.from_array(weights[i].astype(np.float32),name="W%d"%i)) | ||
# Bias add | ||
operations.append(helper.make_node("Add",["M%d"%i,"B%d"%i],[outputName])) | ||
initializers.append(numpy_helper.from_array(biases[i].astype(np.float32),name="B%d"%i)) | ||
# Use Relu activation for all layers except the last layer | ||
if i<numLayers-1: | ||
operations.append(helper.make_node("Relu",["H%d"%i],["R%d"%i])) | ||
inputVar = "R%d"%i | ||
# Create the graph and model in onnx | ||
graph_proto = helper.make_graph(operations,"nnet2onnx_Model",inputs, outputs,initializers) | ||
operations.append(helper.make_node("MatMul", [f"W{i}", inputVar], [f"M{i}"])) | ||
initializers.append(numpy_helper.from_array(weights[i].astype(np.float32), name=f"W{i}")) | ||
|
||
# Bias addition | ||
operations.append(helper.make_node("Add", [f"M{i}", f"B{i}"], [outputName])) | ||
initializers.append(numpy_helper.from_array(biases[i].astype(np.float32), name=f"B{i}")) | ||
|
||
# Apply ReLU activation to all layers except the last | ||
if i < numLayers - 1: | ||
operations.append(helper.make_node("Relu", [f"H{i}"], [f"R{i}"])) | ||
inputVar = f"R{i}" | ||
|
||
# Create the graph and model in ONNX format | ||
graph_proto = helper.make_graph(operations, "nnet2onnx_Model", inputs, outputs, initializers) | ||
model_def = helper.make_model(graph_proto) | ||
|
||
# Print statements | ||
print("Converted NNet model at %s"%nnetFile) | ||
print(" to an ONNX model at %s"%onnxFile) | ||
|
||
# Additional print statements if desired | ||
#print("\nReadable GraphProto:\n") | ||
#print(helper.printable_graph(graph_proto)) | ||
|
||
# Save the ONNX model | ||
# Print success message | ||
print(f"Converted NNet model at {nnetFile} to an ONNX model at {onnxFile}") | ||
|
||
# Save the ONNX model to file | ||
onnx.save(model_def, onnxFile) | ||
|
||
|
||
if __name__ == '__main__': | ||
# Read user inputs and run nnet2onnx function for different numbers of inputs | ||
if len(sys.argv)>1: | ||
nnetFile = sys.argv[1] | ||
if len(sys.argv)>2: | ||
onnxFile = sys.argv[2] | ||
if len(sys.argv)>3: | ||
outputName = argv[3] | ||
nnet2onnx(nnetFile,onnxFile,outputName) | ||
else: nnet2onnx(nnetFile,onnxFile) | ||
else: nnet2onnx(nnetFile) | ||
else: | ||
print("Need to specify which .nnet file to convert to ONNX!") | ||
|
||
def main(): | ||
# Parse command-line arguments | ||
parser = argparse.ArgumentParser(description="Convert a .nnet file to ONNX format.") | ||
parser.add_argument("nnetFile", type=str, help="The .nnet file to convert") | ||
parser.add_argument("--onnxFile", type=str, default="", help="Optional: Name of the output ONNX file") | ||
parser.add_argument("--outputVar", type=str, default="y_out", help="Optional: Name of the output variable") | ||
parser.add_argument("--inputVar", type=str, default="X", help="Optional: Name of the input variable") | ||
parser.add_argument("--normalize", action="store_true", help="Normalize network weights and biases") | ||
|
||
args = parser.parse_args() | ||
|
||
# Call the nnet2onnx function with parsed arguments | ||
nnet2onnx( | ||
nnetFile=args.nnetFile, | ||
onnxFile=args.onnxFile, | ||
outputVar=args.outputVar, | ||
inputVar=args.inputVar, | ||
normalizeNetwork=args.normalize | ||
) | ||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.