Skip to content

Commit

Permalink
Add example EOS package and modify example config, update eos pkg cre…
Browse files Browse the repository at this point in the history
…ate, update docs
  • Loading branch information
aangelos28 committed Oct 25, 2024
1 parent bf2aace commit 47ac0d7
Show file tree
Hide file tree
Showing 17 changed files with 237 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,5 @@ cython_debug/
/config.yml

/user/*
!/user/example/
!/user/.gitkeep
11 changes: 5 additions & 6 deletions config.example.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
user_dir: ./user
labs:
- lab1
- lab2
- multiplication_lab
experiments:
- experiment1
- experiment2
- optimize_multiplication

log_level: INFO

# EOS orchestrator's internal web API server configuration
web_api:
host: localhost
port: 8070

# EOS database configuration
# EOS database (MongoDB) configuration
db:
host: localhost
port: 27017
username: ""
password: ""

# EOS file database configuration
# EOS file database (MinIO) configuration
file_db:
host: localhost
port: 9004
Expand Down
6 changes: 5 additions & 1 deletion docs/user-guide/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ After installation, you need to configure external services such as MongoDB and

1. Configure External Services
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We provide a Docker Compose file that can run the external services.
We provide a Docker Compose file that can run the external services. You do not need to install external services
manually, just provide configuration values and Docker Compose will take care of the rest.

Copy the example environment file:

Expand All @@ -26,3 +27,6 @@ Copy the example configuration file:
cp config.example.yml config.yml
Edit `config.yml`. Ensure that credentials are provided for the MongoDB and MinIO services.

By default, EOS loads the "multiplication_lab" laboratory and the "optimize_multiplication" experiment from an example
EOS package. Feel free to change this.
17 changes: 16 additions & 1 deletion docs/user-guide/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ We provide a Docker Compose file that can set up all of these services for you.
^^^^^^^^^^^^^^
PDM is used as the project manager for EOS, making it easier to install dependencies and build it.

See the `PDM documentation <https://pdm-project.org/en/latest/>`_ for more information or if you encounter any issues.

.. tab-set::

.. tab-item:: Linux/Mac
Expand All @@ -38,7 +40,20 @@ PDM is used as the project manager for EOS, making it easier to install dependen
git clone https://github.com/UNC-Robotics/eos
3. Install Dependencies
3. Make a Virtual Environment
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
We create a virtual environment to isolate the dependencies of EOS from the rest of the system. The virtual environment
is created in a ``env`` directory inside the EOS repository directory. Feel free to use PDM to manage the virtual
environment instead. Other sections of the documentation will assume that you are using a virtual environment located
inside the EOS repository directory.

.. code-block:: shell
cd eos # Navigate to the cloned repository
python3 -m venv env
source env/bin/activate
4. Install Dependencies
^^^^^^^^^^^^^^^^^^^^^^^
Navigate to the cloned repository and run:

Expand Down
8 changes: 7 additions & 1 deletion eos/cli/pkg_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ def create_package(
) -> None:
"""Create a new package with the specified name in the user directory."""
package_dir = Path(user_dir) / name
subdirs = ["common", "devices", "tasks", "labs", "experiments"]
subdirs = ["devices", "tasks", "labs", "experiments"]

try:
package_dir.mkdir(parents=True, exist_ok=False)
for subdir in subdirs:
(package_dir / subdir).mkdir()

# Create README.md with just the package name
readme_content = f"# {name}"
readme_path = package_dir / "README.md"
readme_path.write_text(readme_content)

typer.echo(f"Successfully created package '{name}' in {package_dir}")
except FileExistsError:
typer.echo(f"Error: Package '{name}' already exists in {user_dir}", err=True)
Expand Down
17 changes: 17 additions & 0 deletions user/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Example EOS Package
This is a very simple EOS package that implements an experiment for finding the smallest number that when multiplied by
two factors is as close as possible to 1024.

## Experiments
The package contains the **optimize_multiplication** experiment which works as explained above.

## Laboratories
The package defines a very basic laboratory containing a "multiplier" and an "analyzer" device.

## Devices
1. **Multiplier**: Provides a function for multiplying two numbers.
2. **Analyzer**: Provides a function for producing a score on how close we are to the objective of the experiment.

## Tasks
1. **Multiply**: Multiplies two numbers using the multiplier device.
2. **Score Multiplication**: Scores the multiplication using the analyzer device.
19 changes: 19 additions & 0 deletions user/example/devices/analyzer/device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Any

from eos.devices.base_device import BaseDevice


class AnalyzerDevice(BaseDevice):
"""Analyzes the multiplication result to produce a loss."""

async def _initialize(self, initialization_parameters: dict[str, Any]) -> None:
pass

async def _cleanup(self) -> None:
pass

async def _report(self) -> dict[str, Any]:
pass

def analyze_result(self, number: int, product: int) -> int:
return number + 100 * abs(product - 1024)
2 changes: 2 additions & 0 deletions user/example/devices/analyzer/device.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
type: analyzer
description: A device for analyzing the result of the multiplication of some numbers and computing a loss.
19 changes: 19 additions & 0 deletions user/example/devices/multiplier/device.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from typing import Any

from eos.devices.base_device import BaseDevice


class MultiplierDevice(BaseDevice):
"""Multiplies two numbers."""

async def _initialize(self, initialization_parameters: dict[str, Any]) -> None:
pass

async def _cleanup(self) -> None:
pass

async def _report(self) -> dict[str, Any]:
pass

def multiply(self, a: int, b: int) -> int:
return a * b
2 changes: 2 additions & 0 deletions user/example/devices/multiplier/device.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
type: multiplier
description: A device for multiplying two numbers
35 changes: 35 additions & 0 deletions user/example/experiments/optimize_multiplication/experiment.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
type: optimize_multiplication
description: An experiment for finding the smallest number that when multiplied by two factors yields 1024

labs:
- multiplication_lab

tasks:
- id: mult_1
type: Multiplication
devices:
- lab_id: multiplication_lab
id: multiplier
parameters:
number: eos_dynamic
factor: eos_dynamic

- id: mult_2
type: Multiplication
devices:
- lab_id: multiplication_lab
id: multiplier
dependencies: [ mult_1 ]
parameters:
number: mult_1.product
factor: eos_dynamic

- id: score_multiplication
type: Score Multiplication
devices:
- lab_id: multiplication_lab
id: analyzer
dependencies: [ mult_1, mult_2 ]
parameters:
number: mult_1.number
product: mult_2.product
27 changes: 27 additions & 0 deletions user/example/experiments/optimize_multiplication/optimizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from bofire.data_models.acquisition_functions.acquisition_function import qLogNEI
from bofire.data_models.enum import SamplingMethodEnum
from bofire.data_models.features.continuous import ContinuousOutput
from bofire.data_models.features.discrete import DiscreteInput
from bofire.data_models.objectives.identity import MinimizeObjective

from eos.optimization.abstract_sequential_optimizer import AbstractSequentialOptimizer
from eos.optimization.sequential_bayesian_optimizer import BayesianSequentialOptimizer


def eos_create_campaign_optimizer() -> tuple[dict, type[AbstractSequentialOptimizer]]:
constructor_args = {
"inputs": [
DiscreteInput(key="mult_1.number", values=list(range(2, 34))),
DiscreteInput(key="mult_1.factor", values=list(range(2, 18))),
DiscreteInput(key="mult_2.factor", values=list(range(2, 18))),
],
"outputs": [
ContinuousOutput(key="score_multiplication.loss", objective=MinimizeObjective(w=1.0)),
],
"constraints": [],
"acquisition_function": qLogNEI(),
"num_initial_samples": 5,
"initial_sampling_method": SamplingMethodEnum.SOBOL,
}

return constructor_args, BayesianSequentialOptimizer
10 changes: 10 additions & 0 deletions user/example/labs/multiplication_lab/lab.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
type: multiplication_lab
description: An example laboratory for testing multiplication

devices:
multiplier:
type: multiplier
computer: eos_computer
analyzer:
type: analyzer
computer: eos_computer
15 changes: 15 additions & 0 deletions user/example/tasks/multiplication/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from eos.tasks.base_task import BaseTask


class MultiplicationTask(BaseTask):
async def _execute(
self,
devices: BaseTask.DevicesType,
parameters: BaseTask.ParametersType,
containers: BaseTask.ContainersType,
) -> BaseTask.OutputType:
multiplier = devices.get_all_by_type("multiplier")[0]
product = multiplier.multiply(parameters["number"], parameters["factor"])
output_parameters = {"product": product}

return output_parameters, None, None
21 changes: 21 additions & 0 deletions user/example/tasks/multiplication/task.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
type: Multiplication
description: This task takes a number and a factor and multiplies them together using a "multiplier" device.

device_types:
- multiplier

input_parameters:
number:
type: integer
unit: none
description: The number to multiply.
factor:
type: integer
unit: none
description: The factor to multiply the number by.

output_parameters:
product:
type: integer
unit: none
description: The product of the number and the factor.
15 changes: 15 additions & 0 deletions user/example/tasks/score_multiplication/task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from eos.tasks.base_task import BaseTask


class ScoreMultiplicationTask(BaseTask):
async def _execute(
self,
devices: BaseTask.DevicesType,
parameters: BaseTask.ParametersType,
containers: BaseTask.ContainersType,
) -> BaseTask.OutputType:
analyzer = devices.get_all_by_type("analyzer")[0]
loss = analyzer.analyze_result(parameters["number"], parameters["product"])
output_parameters = {"loss": loss}

return output_parameters, None, None
21 changes: 21 additions & 0 deletions user/example/tasks/score_multiplication/task.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
type: Score Multiplication
description: Scores multiplication based on how close the product is to 1024 and how small the initial number is using an "analyzer" device.

device_types:
- analyzer

input_parameters:
number:
type: integer
unit: none
description: The number that was multiplied with some factors.
product:
type: integer
unit: none
description: The final product after multiplying with some factors.

output_parameters:
loss:
type: integer
unit: none
description: The multiplication loss. Captures how far the product is from 1024 and how large the initial number is.

0 comments on commit 47ac0d7

Please sign in to comment.