Skip to content

Commit

Permalink
feat: add Sweep example fibonacci model (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
roseDickinson authored Oct 9, 2024
1 parent ee429b7 commit 29f5ebf
Show file tree
Hide file tree
Showing 7 changed files with 300 additions and 0 deletions.
1 change: 1 addition & 0 deletions simple-example--fibonacci-model/model_definition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ metadata:
licence: https://creativecommons.org/licenses/by/4.0/
rights: open
spec:
command: ["python", "/src/main.py"]
inputs:
parameters:
- name: SEQUENCE_LENGTH
Expand Down
8 changes: 8 additions & 0 deletions sweep-example--fibonacci-model/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Use the official Python 3.8 image from Dockerhub to provide a Python environment
FROM python:3.8

# Copy the contents from the local ./src/ directory to the output container's /src/ directory
COPY ./src /src

# Tell Docker how to run the Model when it starts the container.
CMD ["python", "/src/main.py"]
98 changes: 98 additions & 0 deletions sweep-example--fibonacci-model/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Fibonacci Model

This is an example model. Here you should describe what the model does.

This particular model generates a Fibonacci sequence and saves it to a file. e.g.:

```
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, ...]
```

The model has been modified slightly when compared with the simple fibonacci model
to allow this Model to function with Sweep steps

You can set three inputs:

- SEQUENCE_LENGTH - Dictates the count of numbers in the generated sequence
- SEQUENCE_F0 - The first number to start the Fibonacci sequence with.
- SEQUENCE_F1 - The second number to start the Fibonacci sequence with.

The model generates a JSON file containing the sequence, the name of the JSON file will
be prefixed with the sequence length, f0 start number and f1 start number. This modification
to the files name such that it depends upon the parameters is what allows the Model to work
with the Sweep step:

Filename - `{SEQUENCE_LENGTH}-{SEQUENCE_F0}-{SEQUENCE_F1}-sequence.json`
```json
{
"sequence": [0, 1, 1, 2, 3, 5]
}
```

## Technical Info

This model pulls in several environment variables and passes them into a piece of Python
code. The Python code is built into a Docker image using the
[provided Dockerfile](./Dockerfile).

There are five files here:

- _[main.py](./src/main.py)_ - This is the file initially called by DAFNI. It is referenced in the
Dockerfile.
- _[work.py](./src/work.py)_ - This contains the main code for the model. Here, as an example, it is a
simple Fibonacci generator
- _[Dockerfile](./Dockerfile)_ - Builds the container that will be run by DAFNI
- _[model_definition.yaml](./model_definition.yaml)_ - A machine-readable file used to define the model.
This information will be shown to other users who may wish to use your model.
- _README.md_ - This helpful file. It should contain detailed information about the model.

## Dependencies

This model requires [Python](https://www.python.org/) and
[Docker](https://www.docker.com/) to be installed in order to build and run locally.

## Running the Model

You can run this example model from within the "simple-example" folder by doing the
following:

```bash
python ./src/main.py
```

You can also run this model using Docker from within the same folder by doing:

```bash
docker build -t fibonacci-model .
docker run fibonacci-model
```

You don't need to run the build step every time you want to run the model, you only need
to re-run it if changes are made to the Dockerfile or any of the files that go into the
Docker image.

You can adjust the way it runs with the environment variables:

```bash
docker run -e SEQUENCE_LENGTH=50 fibonacci-model
```

or

```bash
docker run -e SEQUENCE_LENGTH=50 -e SEQUENCE_F0=1 -e SEQUENCE_F1=3 fibonacci-model
```

## Uploading to DAFNI

You will need to create a file from your docker image to upload it. Check out the detailed instructions online at [Docs](https://docs.secure.dafni.rl.ac.uk/docs/how-to/models/how-to-upload-a-model/) but you can create it with:

```bash
docker save -o fibonacci-model.tar finbonacci-model
```

You can then also compress it before uploading:

```bash
gzip example-model.tar
```
46 changes: 46 additions & 0 deletions sweep-example--fibonacci-model/model_definition.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
kind: M
api_version: v1beta3
metadata:
display_name: Fibonacci for Sweep Steps
name: fibonacci-sweep
publisher: DAFNI Example Models
contact_point_name: DAFNI
contact_point_email: [email protected]
summary: Generates a Fibonacci sequence
description: >
An example Model designed for use with DAFNI Sweep Steps.
This generates a Fibonacci sequence and saves it to a json file.
The name of the json file will be prefixed in the following way:
{sequence length}-{f0 start number}-{f1 start number}-sequence.json
this is to allow this Model to work in Sweep steps.
licence: https://creativecommons.org/licenses/by/4.0/
rights: open
spec:
command: ["python", "/src/main.py"]
inputs:
parameters:
- name: SEQUENCE_LENGTH
title: Sequence length
description: The number of items in the sequence.
type: integer
default: 20
min: 2
required: true
- name: SEQUENCE_F0
title: F0 start number
description: The initial start value for the sequence.
type: integer
default: 0
required: true
- name: SEQUENCE_F1
title: F1 start number
description: The second start value for the sequence.
type: integer
default: 1
required: true
outputs:
datasets:
- name: sequence.json
type: json
description: A Fibonacci sequence output from the Fibonacci Example Model.
64 changes: 64 additions & 0 deletions sweep-example--fibonacci-model/src/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import json
import os
import sys
from pathlib import Path

from work import do_work

SEQUENCE_LENGTH_DEFAULT = 20
SEQUENCE_LENGTH_MINIMUM = 2
SEQUENCE_F0_DEFAULT = 0
SEQUENCE_F1_DEFAULT = 1

output_folder = Path("/data/outputs/")
output_file_name = "sequence.json"


def main():

print("Starting fibonacci model")

# Read values from environment variables but use default values if they don't exist
sequence_length = os.getenv("SEQUENCE_LENGTH", SEQUENCE_LENGTH_DEFAULT)
sequence_f0 = os.getenv("SEQUENCE_F0", SEQUENCE_F0_DEFAULT)
sequence_f1 = os.getenv("SEQUENCE_F1", SEQUENCE_F1_DEFAULT)

# Check each of the values are in an acceptable format for this model
if not is_int(sequence_length):
sys.exit("Error: SEQUENCE_LENGTH must be a whole number")

sequence_length = int(sequence_length)
if sequence_length < SEQUENCE_LENGTH_MINIMUM:
sys.exit(
f"Error: SEQUENCE_LENGTH must be a minimum of {SEQUENCE_LENGTH_MINIMUM}"
)

if not is_int(sequence_f0):
sys.exit("Error: SEQUENCE_F0 must be whole number")

if not is_int(sequence_f1):
sys.exit("Error: SEQUENCE_F1 must be whole number")

# Call main work
sequence = do_work(sequence_length, int(sequence_f0), int(sequence_f1))

# Output the results to a file
output_folder.mkdir(parents=True, exist_ok=True)
output_file = output_folder.joinpath(
f"{sequence_length}-{sequence_f0}-{sequence_f1}-{output_file_name}"
)
output_file.write_text(json.dumps({"sequence": sequence}))

print("Finished fibonacci model")


def is_int(value):
try:
int(value)
return True
except ValueError:
return False


if __name__ == "__main__":
main()
59 changes: 59 additions & 0 deletions sweep-example--fibonacci-model/src/metadata.json.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"@context": [
"metadata-v1"
],
"@type": "dcat:Dataset",
"dct:title": "Published from file",
"dct:description": "Dataset-description",
"dct:identifier": [],
"dct:subject": "Biota",
"dcat:theme": [],
"dct:language": "en",
"dcat:keyword": [
"At least one needed"
],
"dct:conformsTo": {
"@id": null,
"@type": "dct:Standard",
"label": null
},
"dct:spatial": {
"@id": null,
"@type": "dct:Location",
"rdfs:label": null
},
"geojson": {},
"dct:PeriodOfTime": {
"type": "dct:PeriodOfTime",
"time:hasBeginning": null,
"time:hasEnd": null
},
"dct:accrualPeriodicity": null,
"dct:created": "2021-01-18T09:51:56.096719Z",
"dct:creator": [
{
"@type": "foaf:Organization",
"@id": null,
"foaf:name": "smile",
"internalID": null
}
],
"dct:publisher": {
"@id": null,
"@type": "foaf:Organization",
"foaf:name": null,
"internalID": null
},
"dcat:contactPoint": {
"@type": "vcard:Organization",
"vcard:fn": null,
"vcard:hasEmail": null
},
"dct:license": {
"@type": "LicenseDocument",
"@id": "https: //creativecommons.org/licences/by/4.0/",
"rdfs:label": null
},
"dct:rights": null,
"dafni_version_note": "Initial Dataset version"
}
24 changes: 24 additions & 0 deletions sweep-example--fibonacci-model/src/work.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def do_work(number, first, second):
"""
Call the main processing part of the model
"""

print("Generating Fibonacci sequence.")
print(f"Using: length={number}, first={first}, second={second}")

sequence = fibonacci_sequence(number, first, second)

print("Sequence: ", sequence)

return sequence


def fibonacci_sequence(length, f0=0, f1=1):
"""
Simple Fibonacci sequence generator
"""
sequence = [f0, f1]
for pos in range(0, length - 2):
sequence.append(sequence[pos] + sequence[pos + 1])

return sequence

0 comments on commit 29f5ebf

Please sign in to comment.