Your First Optimization with globalMOO

This guide will take you from zero to your first working optimization in about 15 minutes. We'll create a simple but complete example that demonstrates the key features of globalMOO.

Try it interactively: Follow along with this guide in our Interactive Google Colab Notebook

What You'll Build

We'll optimize a simple function with two inputs to achieve desired outputs. This example will teach you:

  • How to set up authentication

  • How to create a model and project

  • How to run an optimization

  • How to interpret and use the results

Prerequisites

  • Python 3.10 or higher

  • A globalMOO API key (get one at https://api.globalmoo.ai/)

  • Basic Python knowledge

Step 1: Installation and Setup

  1. Install the SDK (use the appropriate package manager for your language):

pip install globalmoo-sdk
  1. Create a new file and add the necessary imports and setup:

import logging
from typing import List
from dotenv import load_dotenv

from globalmoo.client import Client
from globalmoo.request.create_model import CreateModel
from globalmoo.request.create_project import CreateProject
from globalmoo.request.load_output_cases import LoadOutputCases
from globalmoo.request.load_objectives import LoadObjectives
from globalmoo.request.suggest_inverse import SuggestInverse
from globalmoo.request.load_inversed_output import LoadInversedOutput
from globalmoo.enums.input_type import InputType
from globalmoo.enums.objective_type import ObjectiveType

# Configure logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('httpx').setLevel(logging.WARNING)
logger = logging.getLogger(__name__)

# Load environment variables
load_dotenv()

# Initialize client using environment variables
client = Client()

Or if you prefer to specify credentials directly:

from globalmoo.credentials import Credentials
credentials = Credentials(
    api_key="your-api-key",
    base_uri="https://api.globalmoo.ai/api"
)
client = Client(credentials=credentials)

Step 2: Define Your Function

Let's optimize a simple linear function with 3 inputs and 5 outputs:

def my_function(inputs: List[float]) -> List[float]:
    """Simple linear function that we want to optimize."""
    v01 = inputs[0]
    v02 = inputs[1]
    v03 = inputs[2]
    
    o01 = v01 + v02 + v03                    # Sum of all inputs
    o02 = (v01 - 2.0) + (v02 - 2.0) + v03    # Shifted sum
    o03 = (v01 - 2.0) + v02 + (v03 - 2.0)    # Different shifted sum
    o04 = v01 + (v02 - 2.0) + (v03 - 2.0)    # Another variation
    o05 = 3.0 * v01 + 2.0 * v03 + 1.0 * v03  # Weighted sum
    
    return [o01, o02, o03, o04, o05]

Step 3: Create a Model and Project

# Model configuration
MODEL_NAME = "My First Optimization"
INPUT_COUNT = 3
OUTPUT_COUNT = 5
MIN_VALUES = [0.0, 0.0, 0.0]
MAX_VALUES = [10.0, 10.0, 10.0]
INPUT_TYPES = [InputType.FLOAT] * INPUT_COUNT

# Create a model
model = client.execute_request(CreateModel(
    name=MODEL_NAME
))
logger.info(f"Created model with ID: {model.id}")

# Create a project with input specifications
project = client.execute_request(CreateProject(
    model_id=model.id,
    input_count=INPUT_COUNT,
    minimums=MIN_VALUES,
    maximums=MAX_VALUES,
    input_types=INPUT_TYPES,
    categories=[]  # Empty list since we have no categorical variables
))
logger.info(f"Created project with ID: {project.id}")

Step 4: Run Initial Learning Cases

# Get input cases from the project
input_cases = project.input_cases
logger.info(f"Received {len(input_cases)} input cases")

# Compute outputs for each input case
output_cases = [my_function(single_case) for single_case in input_cases]
logger.info(f"Computed {len(output_cases)} output cases")

# Create trial with the computed outputs
trial = client.execute_request(LoadOutputCases(
    model_id=model.id,
    project_id=project.id,
    output_count=OUTPUT_COUNT,
    output_cases=output_cases
))
logger.info(f"Successfully created trial with ID: {trial.id}")

Step 5: Set Optimization Goals

# Define our target values and how we want to achieve them
TRUTHCASE = [5.4321, 5.4321, 5.4321]       # The inputs we're trying to find
objectives = my_function(TRUTHCASE)          # The outputs we want to match

# Define how precise we want each objective to be
OBJECTIVE_TYPES = [ObjectiveType.PERCENT] * OUTPUT_COUNT  # Use percentage-based matching
PERCENT_BELOW = [-1.0] * OUTPUT_COUNT                    # Allow 1% below target
PERCENT_ABOVE = [ 1.0] * OUTPUT_COUNT                    # Allow 1% above target

# By convention, the last entry in input_cases is the center of the search space,
# which serves as a good starting point for the optimization
initial_input = input_cases[-1]
initial_output = output_cases[-1]

# Initialize the optimization
objective = client.execute_request(LoadObjectives(
    model_id=model.id,
    project_id=project.id,
    trial_id=trial.id,
    objectives=objectives,                # What outputs we want
    objective_types=OBJECTIVE_TYPES,      # How to match each output
    initial_input=initial_input,          # Where to start from
    initial_output=initial_output,        # Its corresponding output
    minimum_bounds=PERCENT_BELOW,      # How far below target is acceptable
    maximum_bounds=PERCENT_ABOVE,     # How far above target is acceptable
    desired_l1_norm=0.0               # Required, defaults to 0.0
))
logger.info("Initialized inverse optimization")

Other available objective types include:

  • ObjectiveType.EXACT: Match exactly within L1 norm

  • ObjectiveType.VALUE: Match within absolute error bounds

  • ObjectiveType.LESS_THAN: Keep output below target

  • ObjectiveType.LESS_THAN_EQUAL: Keep output at or below target

  • ObjectiveType.GREATER_THAN: Keep output above target

  • ObjectiveType.GREATER_THAN_EQUAL: Keep output at or above target

  • ObjectiveType.MINIMIZE: Minimize output down to target

  • ObjectiveType.MAXIMIZE: Maximize output up to target

Step 6: Run the Optimization Loop

# Run optimization loop
max_iterations = 10

for iteration in range(max_iterations):
    # Get next suggested experiment
    inverse = client.execute_request(SuggestInverse(
        model_id=model.id,
        project_id=project.id,
        trial_id=trial.id,
        objective_id=objective.id
    ))
    logger.info(f"Iteration {iteration + 1}: Received suggestion")
    
    # Run the experiment
    next_output = my_function(inverse.input)
    
    # Load the experimental results
    inverse = client.execute_request(LoadInversedOutput(
        model_id=model.id,
        project_id=project.id,
        trial_id=trial.id,
        objective_id=objective.id,
        output=next_output
    ))

    # Log detailed results
    logger.info("Current solution details:")
    logger.info(f"  Input: {[f'{x:.4f}' for x in inverse.input]}")
    logger.info(f"  Output: {[f'{x:.4f}' for x in next_output]}")
    logger.info(f"  Target: {[f'{x:.4f}' for x in objectives]}")
    
    if inverse.results:
        for result in inverse.results:
            logger.info(f"\nObjective {result.number}:")
            logger.info(f"  Type: {result.type}")
            logger.info(f"  Error: {result.error:.6f}")
            logger.info(f"  Satisfied: {'✓' if result.satisfied else '✗'}")
            logger.info(f"  Detail: {result.detail}")
    
    # Check if we've found a satisfactory solution
    if inverse.should_stop():
        if inverse.satisfied_at:
            logger.info("Found satisfactory solution!")
        else:
            logger.info(f"Search stopped: {inverse.get_stop_reason().description()}")
        break

    logger.info(f"Completed iteration {iteration + 1}")

# Report final results
logger.info("\nFinal Results:")
if inverse.satisfied_at:
    logger.info("Solution satisfied all objectives!")
    logger.info("Satisfaction details:")
    for i, (satisfied, detail) in enumerate(zip(inverse.get_satisfaction_status(), inverse.get_result_details())):
        logger.info(f"  Objective {i}: {'✓' if satisfied else '✗'} - {detail}")
else:
    logger.info("Solution did not satisfy all objectives")
    logger.info("Status per objective:")
    for i, (satisfied, detail) in enumerate(zip(inverse.get_satisfaction_status(), inverse.get_result_details())):
        logger.info(f"  Objective {i}: {'✓' if satisfied else '✗'} - {detail}")

logger.info(f"\nFinal solution:")
logger.info(f"  Input values: {[f'{x:.4f}' for x in inverse.input]}")
logger.info(f"  Output values: {[f'{x:.4f}' for x in next_output]}")
logger.info(f"  Target values: {[f'{x:.4f}' for x in objectives]}")
logger.info(f"  Error values: {[f'{e:.6f}' for e in inverse.get_objective_errors()]}")

# Don't forget to close the client
client.http_client.close()
logger.info("Closed client connection")

Complete Code

The complete code is available in the examples directory as linear_example.py.

What's Next?

Now that you have your first optimization working, you can:

  1. Try different objective types (EXACT, VALUE, LESS_THAN, etc.)

  2. Learn about different input types (integers, categories) in the tutorials

  3. Explore more complex optimization scenarios

  4. Check out the example gallery for real-world applications

Common Issues

  1. Authentication errors

    • Check your API key in .env file or credentials

    • Ensure you're using the correct base URI

  2. Optimization not converging

    • Try increasing max_iterations

    • Check if your target values are achievable

    • Consider relaxing your objective criteria

  3. Need help?

    • Contact support@globalmoo.com

    • Check our documentation at https://docs.globalmoo.ai

Last updated