Infinity Core Package#

Sometimes more control is needed than is provided by the high-level workflows reviewed so far. In this case, the Infinity Core Python package – our software that sits in between direct REST API interaction and the high-level workflows – is the backbone of front-end access to the Infinity API. Whether you are using our workflows or are interested in developing your own code, being familiar with Infinity Core is very helpful.

What is the purpose of Infinity Core?#

Infinity Core wraps all the functionality available via our REST API, but in a higher-level and developer-friendly way. It also provides tools to enable more complex data science workflows.

What are the key abstractions provided by Infinity Core?#

Two fundamental concepts introduced by Infinity Core are that of a Session and a Batch. Each is implemented as a Python class in Infinity Core and almost all functionality for interacting with the REST API you’ll need is available as a method on one or the other.

  • An API Session. The fundamental workflow for light- and power-users alike is to generate 1 or more batches of synthetic data targeting a specific generator. An Infinity Core Session wraps this process and provides various ergonomic facilities. Users create a new session by providing their authentication token and a target generator. A session is then minted with behavior specialized for the selected generator. The session allows for more efficient synthetic data generation with fewer errors and less boilerplate code.

  • A synthetic data Batch. Whether producing 1 preview image or 1,000 videos, or using any of our application-specific generators, there is only one way to generate data with our API – submit a batch. At its core, a batch is defined by a list of parameters for a target generator, but Infinity Core provides many abstractions for generating batches, validating their parameters, submitting them to the API, querying their status in the cloud, downloading and analyzing results, and reconstituting old batches for further use.

Note

In the next section, we will walk through core features of Infinity Core with explanatory code snippets. The last section highlights and links to end-to-end example notebooks built with Infinity Core.

Core Infinity Tools#

The primary role of Infinity Core is to facilitate interaction with the Infinity REST API using the Session and Batch tools.

Using a Session#

The infinity_core.session submodule has a generic Session class for interacting with the API:

from infinity_core.session import Session

sesh = Session(token="API_TOKEN", generator="visionfit-flagship-v0.1.0", server="https://api.toinfinity.ai/")

Get parameter options#

The session can print important metadata about job parameters helpful when constructing a batch to submit. This includes the type of each parameter, constraints (min and max values, finite choices, etc.), and the default value for each parameter.

from pprint import pprint
pprint(sesh.parameter_info)

Specify job parameters with random sampling#

A Session provides the ability to specify details about job parameters you care about and randomly sample those you don’t using the randomize_unspecified_params method. A standard way of preparing a batch to submit to the API is to call this function in a loop or otherwise append new job parameters to a Python list, one for each job you would like in the batch. The unspecified parameters will be uniformly sampled from their valid ranges or options.

In contrast to sample_input, which we introduced in high-level workflows, randomize_unspecified_params will always sample each parameter uniformly from its valid range. Whereas randomize_unspecified_params is generic and can be used to synthesize parameters for any generator, sample_input is generator-specific and draws from parameters distributions that are hand-crafted, and thus is not part of the generic Infinity Core package.

Note

Depending on the generator, sample_input may take into consideration more complex joint constraints that may exist with certain parameters. randomize_unspecified_params cannot do this.

In this example, we specify fixed values for some parameters, we randomly sample lighting_power using our own custom distribution, and we uniformly sample the other parameters from their valid ranges by leveraging the randomize_unspecified_params method.

num_jobs = 10
light_min = sesh.parameter_info["lighting_power"]["options"]["min"]
light_max = sesh.parameter_info["lighting_power"]["options"]["max"]
job_params = [
    sesh.randomize_unspecified_params(dict(
        exercise = "UPPERCUT-LEFT",
        num_reps = 1,
        lighting_power = random.uniform(light_min, light_max),
    )) for _ in range(num_jobs)
]

Specify job parameters without random sampling#

While randomize_unspecified_params is convenient, you may not want to randomly sample unspecified parameters at all. You can always directly construct job parameters as a list of dictionaries without using randomize_unspecified_params.

If you submit a list of dictionaries to the API, any unspecified parameters in each job parameter dictionary will be set to their default values.

job_params = [
    {"num_reps": 1, 
    "lighting_power": random.uniform(light_min, light_max)}
    for _ in range(num_jobs)
]

# Unspecified params are set to their default values
batch = sesh.submit(
    job_params=job_params,
    is_preview=True,
    batch_name = "example with default values",
)

Print the default value associated with each parameter:

pprint(sesh.parameter_info)

Estimate samples#

We can estimate the number of samples (frames) a given batch would generate before submitting the batch for execution. In general, it is not possible to predict exact sample estimates for generators that introduce randomness at runtime. However, the accuracy of the estimate increases with the size of the batch.

samples_by_job = sesh.estimate_samples(job_params, is_preview=False)
total_frames = sum(samples_by_job)

Note that if is_preview=True in the call to estimate_samples, the number of frames will exactly equal the number of jobs in the batch, since previews are single-frame by definition.

Submit a batch#

We can submit a list of job parameters to the API. The submit method will first validate the job parameters and raise an exception if any errors or invalid values are encountered. If the submission is successful, a Batch object is returned. It is possible to submit single-frame previews with is_preview=True or videos with is_preview=False.

batch = sesh.submit(job_params, is_preview=True, batch_name="example preview batch")

Note that if a batch submission is estimated to exceed the number of samples remaining in the user account, the submission will fail with a descriptive error message. In this case, you can (1) purchase more samples via the API dashboard, (2) reduce the number of jobs in the batch, or (3) reduce the number of samples per job by changing the job parameters. When each job completes, the sample estimate for the job is credited back to the sample balance, and the actual number of samples rendered is deducted from the balance.

Using a Batch#

The returned Batch object from submission has many utility methods associated with it.

Get static information about the batch#

View the list of the UUIDs for each job in the batch:

print(batch.job_ids)

View the list of job parameters for each job in the batch:

print(batch.job_params)

Get the number of jobs in the batch:

print(batch.num_jobs)

Get dynamic information about the batch#

See how many jobs are still processing, if any:

print(batch.get_num_jobs_remaining())

Get detailed batch metadata from the API:

print(batch.get_batch_data())

Get metadata only for a specific job in the batch:

target_job = batch.job_ids[0]
print(batch.get_job_summary_data(job_id = target_job))

Check for job completion: non-blocking and blocking#

At any time, we can check if a batch has completed processing in the cloud with a non-blocking call to get_num_jobs_remaining. You can also await completion of all jobs – this will block the notebook cell (or more generally, current Python process) until completion.

Query for batch completion in a non-blocking way:

print(f"Finished? {'Y' if batch.get_num_jobs_remaining() == 0 else 'N'}")

Block all Python processes while awaiting completion of the batch. Set a 60 minute timeout:

completed_jobs = batch.await_completion(timeout = 60 * 60)

Get completed job info and download results#

Get a list of the currently completed (without error) jobs:

completed_jobs = batch.get_valid_completed_jobs()

Download currently completed jobs to the specified path:

batch.download(path = "../tmp")

Additional Infinity Core Usage#

On top of the batch and session concepts, Infinity Core provides convenience methods that may prove helpful. Additionally, we show how Pandas Dataframes can be used to analyze, review, and update job parameters.

Manually validate the job parameters#

The submission methods will check validity of job parameters, but you can also manually check at any time.

errors_by_job = sesh.validate_job_params(final_job_params)
assert all([e is None for e in errors_by_job])

Use pandas dataframes#

DataFrame libraries such as pandas can easily be used to analyze, review, and update job parameters.

import pandas as pd
df = pd.DataFrame.from_records(job_params)
df.head()
# ... modify dataframe ...
final_job_params = df.to_dict("records")
batch = sesh.submit(
    job_params=final_job_params,
    is_preview=False,
    batch_name="modified batch using pandas df"
)

Example Notebooks#

You can find example notebooks in infinity-workflows that showcase Infinity Core functionality. These can be used as a convenient jumping off point for more complex and custom usage of the Infinity API.

VisionFit#

Submit and Download Batch Demo#

The submit_and_download_batch_demo.ipynb notebook walks through the basics of building, submitting, downloading, and analyzing a large batch of synthetic data.

Parameter Sweep Demo#

The parameter_sweep_demo.ipynb notebook walks through a common use case of wanting to sweep one or more parameters against a previously submitted job or jobs.