Passing the parameters as args requires that you create and expose your programs as simple command-line applications.

Creating command-line applications is a powerful way of exposing work, it forces users to think about how to structure and organize programs, it provides documentation for the expected inputs and performs checks that other users can benefit from when interacting with a program.

Passing params to a simple program

Your program can be written in any language, the only requirements to use this method for passing configurations (inputs/outputs), is that you need to provide an interface to consume arguments.

Let’s consider the echo command, you can past this content directly to Polyaxon UI or you can save it under echo.yaml:

version: 1.1
kind: component
run:
  kind: job
  container:
    image: busybox:stable
    command: ["echo", "This is a test"]

This is a simple program that prints information, you can run it using Polyaxon CLI:

polyaxon run -f echo.yaml -l

In order to run this program with multiple values, we can keep changing the message manually or we can expose the message to print as an input and pass it as an argument:

version: 1.1
kind: component
inputs:
- name: message
  type: str
run:
  kind: job
  container:
    image: busybox:stable
    command: ["echo", "{{ message }}"]

Now you can run multiple version of this example without changing the polyaxonfile:

polyaxon run -f echo.yaml -P message="test 1" -l
polyaxon run -f echo.yaml -P message="test 2" -l

You can also notice that Polyaxon will track the input and will show it in the UI and the CLI:

polyaxon ops get -uid UUID

Run inputs:

-------  ------
message  test 1
-------  ------

....

Multirun

Sometimes users might need to run the same job and pass different params, one way to do that is by invoking the CLI multiple times, another way is to use -HP(--hparams) instead of -P(--params).

To pass test 1 and test 2 to our program without invoking the CLI multiple times:

polyaxon run -f echo.yaml -HP message='choice:["test 1","test 2"]'

This command will automatically create a grid search with the following matrix configuration:

matrix:
  kind: grid
  params:
    message:
      kind: choice
      value:
        - test 1
        - test 2
  concurrency: 1

You should notice that the CLI uses the following format: kind:value to pass hyperparameters, in this case it passes the choice kind. Another important aspect to notice is that matrix is of kind grid search and it runs the operations sequentially by setting the concurrency to 1. You can configure those options via CLI as well by passing the following extra arguments --matrix-kind, --matrix-concurrency, and --matrix-num-runs.

The CLI arg -HP is a nice way to avoid creating configuration files when iterating, however if you are to create a complex operation with multiple inputs/outputs and complex matrix defintion, we suggest that you use a proper Polyaxonfile. See the intro for the hyperparameter tuning in this section and the optimization engine reference.

Creating a custom program

Since most Polyaxon’s users are data-scientists or machine learning engineers, they generally write their programs in Python, so the content of these tutorials will be in Python as well.

This is a simple application that prints your inputs, in this example we will use the argparse package to consume the parameters, but the same logic can be used with python-fire, click, or any other library of your choice.

import argparse


if __name__ == '__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument(
        '--message',
        type=str,
        default="Default message")

    args = parser.parse_args()

    print(args.message)

We can adjust our previous polyaxonfile to run this python program, in order to avoid uploading code or cloning a repo, we will pass the python code inline:

Let’s save this changes under echo.yaml:

version: 1.1
kind: component
inputs:
- name: message
  type: str
run:
  kind: job
  init:
    - file:
        content: |
          import argparse

          if __name__ == '__main__':
              parser = argparse.ArgumentParser()

              parser.add_argument(
                  '--message',
                  type=str,
                  default="Default message")

              args = parser.parse_args()

              print(args.message)

        filename: echo.py
  container:
    image: polyaxon/polyaxon-quick-start
    workingDir: "{{ globals.artifacts_path }}"
    command: [python3, -u, echo.py]
    args: ["{{ params.message.as_arg }}"]

To run this example:

polyaxon run -f echo.yaml -P message="test 1" -l

Explaining as_args

The new echo.yaml file is a bit more complex, and it introduced too many new concepts.

You can notice that we are passing the params with the args field, you can also notice that we used {{ params.message.as_arg }} which is the equivalent of --message={{ message }}.

Note: For more information about params, please check the specification section

Explaining init

For the purpose of this example, we provided our program as an init file, we could have created a directory with this structure:

- echo-example/
  - echo.yaml
  - echo.py

Where the echo.py contains:

import argparse

if __name__ == '__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument(
        '--message',
        type=str,
        default="Default message")

    args = parser.parse_args()

    print(args.message)

And the echo.yaml contains:

version: 1.1
kind: component
inputs:
- name: message
  type: str
run:
  kind: job
  container:
    image: polyaxon/polyaxon-quick-start
    workingDir: "{{ globals.run_artifacts_path }}/uploads"
    command: [python3, -u, echo.py]
    args: ["{{ params.message.as_arg }}"]

In that case we could have used the --upload/-u flag to upload the code necessary for running the component:

polyaxon run -u -f echo.yaml -P message="test 1" -l

Note: You can learn more about how to iterate with upload, git, and inline scripts in the iterative process introduction section.

Multiple params

Let’s extend the previous example to require multiple parameters:

import argparse


if __name__ == '__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument(
        '--message1',
        type=str,
        default="Default message1")

    parser.add_argument(
        '--message2',
        type=str,
        default="Default message2")

    parser.add_argument(
        '--message3',
        type=str,
        default="Default message3")

    args = parser.parse_args()

    print(args.message1)
    print(args.message2)
    print(args.message3)

And the echo.yaml component:

version: 1.1
kind: component
inputs:
- name: message1
  type: str
- name: message2
  type: str
- name: message3
  type: str
run:
  kind: job
  container:
    image: polyaxon/polyaxon-quick-start
    workingDir: "{{ globals.artifacts_path }}"
    command: [python3, -u, echo.py]
    args: ["{{ params.message1.as_arg }}", "{{ params.message2.as_arg }}", "{{ params.message3.as_arg }}"]

The updated component now has a longer args section, and based on the logic your building the args list could be longer. If you are running Polyaxon v1.9.1 or higher, you can request all params as args in a single line:

version: 1.1
kind: component
inputs:
- name: message1
  type: str
- name: message2
  type: str
- name: message3
  type: str
run:
  kind: job
  container:
    image: polyaxon/polyaxon-quick-start
    workingDir: "{{ globals.artifacts_path }}"
    command: [python3, -u, echo.py]
    args: "{{ params.as_args }}"

The value "{{ params.as_args }}" is equivalent to ["{{ params.message1.as_arg }}", "{{ params.message2.as_arg }}", "{{ params.message3.as_arg }}"].