Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR tests #22

Merged
merged 8 commits into from
Jan 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .github/workflows/linux-tutorials-test-on-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
---
name: PR tests

on:
pull_request:

permissions:
contents: read

jobs:
tutorial-test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10']
tutorial: ['LocalStorage', 'DatasetUsage', 'DatasetCreation']
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies and run tutorials
run: |
cd tests/${{ matrix.tutorial }}
pip install -r requirements.txt
pip uninstall -y minari
pip install -e ../..
for f in *.py; do python "$f"; done
6 changes: 2 additions & 4 deletions minari/storage/hosting.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ def download_dataset(dataset_name: str):
print(
f"Dataset not found locally. Downloading {dataset_name} from Farama servers..."
)
project_id = "dogwood-envoy-367012"
bucket_name = "minari"
storage_client = storage.Client(project=project_id)
storage_client = storage.Client.create_anonymous_client()

bucket = storage_client.bucket(bucket_name)

Expand All @@ -55,9 +54,8 @@ def download_dataset(dataset_name: str):


def list_remote_datasets():
project_id = "dogwood-envoy-367012"
bucket_name = "minari"
storage_client = storage.Client(project=project_id)
storage_client = storage.Client.create_anonymous_client()

bucket = storage_client.bucket(bucket_name)
blobs = bucket.list_blobs()
Expand Down
115 changes: 115 additions & 0 deletions tests/DatasetCreation/dataset_creation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# pyright: basic, reportOptionalMemberAccess=false

import json

import gymnasium as gym
import numpy as np
from gymnasium.utils.serialize_spec_stack import serialise_spec_stack

from minari.dataset import MinariDataset

# 1. Get permissions to upload to GCP
# This test is called on PR, so we skip the GCP part - the GCP part is tested only on merge to main due to security reasons.

# 2. Standard Gymnasium procedure to collect data into whatever replay buffer you want
env = gym.make("FetchReach-v3")

environment_stack = serialise_spec_stack(
env.spec_stack
) # Get the environment specification stack for reproducibility

env.reset()
replay_buffer = {
"episode": np.array([]),
"observation": np.array([]),
"action": np.array([]),
"reward": np.array([]),
"terminated": np.array([]),
"truncated": np.array([]),
}
dataset_name = "FetchReach_v3_example-dataset"

num_episodes = 4


assert env.spec.max_episode_steps is not None, "Max episode steps must be defined"

replay_buffer = {
"episode": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=np.int32
),
"observation": np.array(
[[0] * 13] * env.spec.max_episode_steps * num_episodes,
dtype=np.float32,
),
"action": np.array(
[[0] * 4] * env.spec.max_episode_steps * num_episodes, dtype=np.float32
),
"reward": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=np.float32
),
"terminated": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=bool
),
"truncated": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=bool
),
}

total_steps = 0
for episode in range(num_episodes):
episode_step = 0
observation, info = env.reset()
terminated = False
truncated = False
while not terminated and not truncated:
action = env.action_space.sample() # User-defined policy function
observation, reward, terminated, truncated, info = env.step(action)
replay_buffer["episode"][total_steps] = np.array(episode)
replay_buffer["observation"][total_steps] = np.concatenate(
(
np.array(observation["observation"]),
np.array(observation["desired_goal"]),
)
)
replay_buffer["action"][total_steps] = np.array(action)
replay_buffer["reward"][total_steps] = np.array(reward)
replay_buffer["terminated"][total_steps] = np.array(terminated)
replay_buffer["truncated"][total_steps] = np.array(truncated)
episode_step += 1
total_steps += 1

env.close()

replay_buffer["episode"] = replay_buffer["episode"][:total_steps]
replay_buffer["observation"] = replay_buffer["observation"][:total_steps]
replay_buffer["action"] = replay_buffer["action"][:total_steps]
replay_buffer["reward"] = replay_buffer["reward"][:total_steps]
replay_buffer["terminated"] = replay_buffer["terminated"][:total_steps]
replay_buffer["truncated"] = replay_buffer["truncated"][:total_steps]


# 3. Convert the replay buffer to a MinariDataset
dataset = MinariDataset(
dataset_name=dataset_name,
algorithm_name="random_policy",
environment_name="FetchReach-v3",
environment_stack=json.dumps(environment_stack),
seed_used=42, # For the simplicity of this example, we're not actually using a seed. Naughty us!
code_permalink="https://github.com/Farama-Foundation/Minari/blob/f095bfe07f8dc6642082599e07779ec1dd9b2667/tutorials/LocalStorage/local_storage.py",
author="WillDudley",
author_email="[email protected]",
observations=replay_buffer["observation"],
actions=replay_buffer["action"],
rewards=replay_buffer["reward"],
terminations=replay_buffer["terminated"],
truncations=replay_buffer["truncated"],
)

print("Dataset generated!")

# 4. Save the dataset locally
dataset.save()

# 5. Upload the dataset to GCP
# This test is called on PR, so we skip the GCP part - the GCP part is tested only on merge to main due to security reasons.
4 changes: 4 additions & 0 deletions tests/DatasetCreation/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
gymnasium-robotics
cython
#minari
git+https://github.com/WillDudley/Gymnasium.git@spec_stack#egg=gymnasium
24 changes: 24 additions & 0 deletions tests/DatasetUsage/dataset_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# pyright: basic, reportGeneralTypeIssues=false

import json

import gymnasium as gym
from gymnasium.utils.serialize_spec_stack import deserialise_spec_stack

import minari

dataset = minari.download_dataset("LunarLander_v2_remote-test-dataset")

print("*" * 60, " Dataset Structure")
print(f"Dataset attributes: {dataset.__dir__()}\n")
print(f"Episode attributes: {dataset.episodes[0].__dir__()}\n")
print(f"Transition attributes: {dataset.episodes[0].transitions[0].__dir__()}\n")

print("*" * 60, " Examples")
print(f"Shape of observations: {dataset.observations.shape}\n")
print(f"Return of third episode: {dataset.episodes[2].compute_return()}\n")
print(f"21st action of fifth episode: {dataset.episodes[4].transitions[20].action}\n")

reconstructed_environment = gym.make(
deserialise_spec_stack(json.loads(dataset.environment_stack))
)
4 changes: 4 additions & 0 deletions tests/DatasetUsage/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
numpy
cython
#minari
git+https://github.com/WillDudley/Gymnasium.git@spec_stack#egg=gymnasium[box2d]
116 changes: 116 additions & 0 deletions tests/LocalStorage/local_storage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# pyright: basic, reportOptionalMemberAccess=false, reportOptionalSubscript=false
import json

import gymnasium as gym
import numpy as np
from gymnasium.utils.serialize_spec_stack import serialise_spec_stack

import minari
from minari.dataset import MinariDataset


def generate_dataset(dataset_name: str):
num_episodes = 10

env = gym.make("LunarLander-v2", render_mode="rgb_array")
environment_stack = serialise_spec_stack(env.spec_stack)
observation, info = env.reset(seed=42)

assert env.spec.max_episode_steps is not None, "Max episode steps must be defined"

replay_buffer = {
"episode": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=np.int32
),
"observation": np.array(
[[0] * env.observation_space.shape[0]]
* env.spec.max_episode_steps
* num_episodes,
dtype=np.float32,
),
"action": np.array(
[[0] * 2] * env.spec.max_episode_steps * num_episodes, dtype=np.float32
),
"reward": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=np.float32
),
"terminated": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=bool
),
"truncated": np.array(
[[0]] * env.spec.max_episode_steps * num_episodes, dtype=bool
),
}

total_steps = 0
for episode in range(num_episodes):
episode_step = 0
observation, info = env.reset()
terminated = False
truncated = False
while not terminated and not truncated:
action = env.action_space.sample() # User-defined policy function
observation, reward, terminated, truncated, info = env.step(action)
replay_buffer["episode"][total_steps] = np.array(episode)
replay_buffer["observation"][total_steps] = np.array(observation)
replay_buffer["action"][total_steps] = np.array(action)
replay_buffer["reward"][total_steps] = np.array(reward)
replay_buffer["terminated"][total_steps] = np.array(terminated)
replay_buffer["truncated"][total_steps] = np.array(truncated)
episode_step += 1
total_steps += 1

env.close()

replay_buffer["episode"] = replay_buffer["episode"][:total_steps]
replay_buffer["observation"] = replay_buffer["observation"][:total_steps]
replay_buffer["action"] = replay_buffer["action"][:total_steps]
replay_buffer["reward"] = replay_buffer["reward"][:total_steps]
replay_buffer["terminated"] = replay_buffer["terminated"][:total_steps]
replay_buffer["truncated"] = replay_buffer["truncated"][:total_steps]

ds = MinariDataset(
dataset_name=dataset_name,
algorithm_name="random_policy",
environment_name="LunarLander-v2",
environment_stack=json.dumps(environment_stack),
seed_used=42, # For the simplicity of this example, we're not actually using a seed. Naughty us!
code_permalink="https://github.com/Farama-Foundation/Minari/blob/f095bfe07f8dc6642082599e07779ec1dd9b2667/tutorials/LocalStorage/local_storage.py",
author="WillDudley",
author_email="[email protected]",
observations=replay_buffer["observation"],
actions=replay_buffer["action"],
rewards=replay_buffer["reward"],
terminations=replay_buffer["terminated"],
truncations=replay_buffer["truncated"],
)

print("Dataset generated!")

return ds


if __name__ == "__main__":
dataset_name = "LunarLander_v2_test-dataset"

print("\n Generate dataset as standard")
generated_dataset = generate_dataset(dataset_name)

print("\n Save dataset to local storage")
generated_dataset.save()

print(
"\n Listing datasets in local storage, we should see the dataset we just generated"
)
minari.list_local_datasets()

print("\n We can load the dataset from local storage as follows")
loaded_dataset = minari.load_dataset(dataset_name)

print("\n We can delete the dataset from local storage as follows")
minari.delete_dataset(dataset_name)

print(
"\n Listing datasets in local storage, we should now no longer see the dataset we just generated"
)
minari.list_local_datasets()
8 changes: 8 additions & 0 deletions tests/LocalStorage/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#gymnasium[box2d] # todo: vc
cython
#minari
h5py
structlog
tensorboardX
typing_extensions
git+https://github.com/WillDudley/Gymnasium.git@spec_stack#egg=gymnasium[box2d]
12 changes: 2 additions & 10 deletions tutorials/DatasetUsage/dataset_usage.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import base64
# pyright: basic, reportGeneralTypeIssues=false

import json
import os

import gymnasium as gym
from gymnasium.utils.serialize_spec_stack import deserialise_spec_stack

import minari

GCP_DATASET_ADMIN = os.environ["GCP_DATASET_ADMIN"]

credentials_json = base64.b64decode(GCP_DATASET_ADMIN).decode("utf8").replace("'", '"')
with open("credentials.json", "w") as f:
f.write(credentials_json)

os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "./credentials.json"

dataset = minari.download_dataset("LunarLander_v2_remote-test-dataset")

print("*" * 60, " Dataset Structure")
Expand Down