Skip to content

Commit 14ee347

Browse files
araffinAl NejatiroccivicMiffyli
authored
Update from mirror (#1112)
* Faster tests * Add github workflow * Faster test * Fix MPI dependency * Faster HER tests + fix CI * No specific python version for pytype * Fixes + add badge * Fix multiprocessing error * Separate TD3 test * Add tolerance for deterministic check * Better tests for saving/loading * Remove unnecessary check * Add comment about VecEnv start method * Make pytype happy * Debug MPI * Add MPI step * Move MPI tests outside pytest * Two processes for GitHub CI * Deactivate check moments * Fix import error * Copy version.txt to docker container (#2) * Copy version.txt to docker container * Update changelog * Update Max username Co-authored-by: Al Nejati <[email protected]> * Fixed typo (#3) * Fixed typo * Update changelog.rst Co-authored-by: Rouslan Placella <[email protected]> * Warn users to switch to Stable Baselines3 (#4) * Warn users to switch to Stable Baselines3 * Fix for pytype * Fix other pytype error * Clarify update warning * Remove python 3.5 build * Allow test to fail * Flaky TD3 test * Fix argument Co-authored-by: Anssi <[email protected]> Co-authored-by: Al Nejati <[email protected]> Co-authored-by: Rouslan Placella <[email protected]> Co-authored-by: Anssi <[email protected]>
1 parent 259f278 commit 14ee347

34 files changed

+321
-137
lines changed

.github/workflows/ci.yml

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3+
4+
name: CI
5+
6+
on:
7+
push:
8+
branches: [ master ]
9+
pull_request:
10+
branches: [ master ]
11+
12+
jobs:
13+
build:
14+
# Skip CI if [ci skip] in the commit message
15+
if: "! contains(toJSON(github.event.commits.*.message), '[ci skip]')"
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
python-version: [3.6] # Deactivate 3.5 build as it is not longer maintained
20+
21+
steps:
22+
- uses: actions/checkout@v2
23+
- name: Set up Python ${{ matrix.python-version }}
24+
uses: actions/setup-python@v2
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
- name: Install dependencies
28+
run: |
29+
sudo apt-get install libopenmpi-dev
30+
python -m pip install --upgrade pip
31+
pip install wheel
32+
pip install .[mpi,tests,docs]
33+
# Use headless version
34+
pip install opencv-python-headless
35+
- name: MPI
36+
run: |
37+
# check MPI
38+
mpirun -h
39+
python -c "import mpi4py; print(mpi4py.__version__)"
40+
mpirun --allow-run-as-root -np 2 python -m stable_baselines.common.mpi_adam
41+
mpirun --allow-run-as-root -np 2 python -m stable_baselines.ppo1.experiments.train_cartpole
42+
mpirun --allow-run-as-root -np 2 python -m stable_baselines.common.mpi_running_mean_std
43+
# MPI requires 3 processes to run the following code
44+
# but will throw an error on GitHub CI as there is only two threads
45+
# mpirun --allow-run-as-root -np 3 python -c "from stable_baselines.common.mpi_moments import _helper_runningmeanstd; _helper_runningmeanstd()"
46+
47+
- name: Build the doc
48+
run: |
49+
make doc
50+
- name: Type check
51+
run: |
52+
make type
53+
- name: Test with pytest
54+
run: |
55+
# Prevent issues with multiprocessing
56+
DEFAULT_START_METHOD=fork make pytest

Dockerfile

+2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@ RUN apt-get -y update \
2525
ENV CODE_DIR /root/code
2626
ENV VENV /root/venv
2727

28+
COPY ./stable_baselines/version.txt ${CODE_DIR}/stable-baselines/stable_baselines/version.txt
2829
COPY ./setup.py ${CODE_DIR}/stable-baselines/setup.py
30+
2931
RUN \
3032
pip install pip --upgrade && \
3133
pip install virtualenv && \

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pytest:
44

55
# Type check
66
type:
7-
pytype
7+
pytype -j auto
88

99
# Build the doc
1010
doc:

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
**WARNING: This package is in maintenance mode, please use [Stable-Baselines3 (SB3)](https://github.com/DLR-RM/stable-baselines3) for an up-to-date version. You can find a [migration guide](https://stable-baselines3.readthedocs.io/en/master/guide/migration.html) in SB3 documentation.**
2+
13
<img src="docs/\_static/img/logo.png" align="right" width="40%"/>
24

3-
[![Build Status](https://travis-ci.com/hill-a/stable-baselines.svg?branch=master)](https://travis-ci.com/hill-a/stable-baselines) [![Documentation Status](https://readthedocs.org/projects/stable-baselines/badge/?version=master)](https://stable-baselines.readthedocs.io/en/master/?badge=master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3bcb4cd6d76a4270acb16b5fe6dd9efa)](https://www.codacy.com/app/baselines_janitors/stable-baselines?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=hill-a/stable-baselines&amp;utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/3bcb4cd6d76a4270acb16b5fe6dd9efa)](https://www.codacy.com/app/baselines_janitors/stable-baselines?utm_source=github.com&utm_medium=referral&utm_content=hill-a/stable-baselines&utm_campaign=Badge_Coverage)
5+
[![CI](https://github.com/hill-a/stable-baselines/workflows/CI/badge.svg)](https://github.com/hill-a/stable-baselines/actions) [![Build Status](https://travis-ci.com/hill-a/stable-baselines.svg?branch=master)](https://travis-ci.com/hill-a/stable-baselines) [![Documentation Status](https://readthedocs.org/projects/stable-baselines/badge/?version=master)](https://stable-baselines.readthedocs.io/en/master/?badge=master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3bcb4cd6d76a4270acb16b5fe6dd9efa)](https://www.codacy.com/app/baselines_janitors/stable-baselines?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=hill-a/stable-baselines&amp;utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/3bcb4cd6d76a4270acb16b5fe6dd9efa)](https://www.codacy.com/app/baselines_janitors/stable-baselines?utm_source=github.com&utm_medium=referral&utm_content=hill-a/stable-baselines&utm_campaign=Badge_Coverage)
46

57
# Stable Baselines
68

@@ -218,7 +220,7 @@ To cite this repository in publications:
218220

219221
## Maintainers
220222

221-
Stable-Baselines is currently maintained by [Ashley Hill](https://github.com/hill-a) (aka @hill-a), [Antonin Raffin](https://araffin.github.io/) (aka [@araffin](https://github.com/araffin)), [Maximilian Ernestus](https://github.com/erniejunior) (aka @erniejunior), [Adam Gleave](https://github.com/adamgleave) (@AdamGleave) and [Anssi Kanervisto](https://github.com/Miffyli) (@Miffyli).
223+
Stable-Baselines is currently maintained by [Ashley Hill](https://github.com/hill-a) (aka @hill-a), [Antonin Raffin](https://araffin.github.io/) (aka [@araffin](https://github.com/araffin)), [Maximilian Ernestus](https://github.com/ernestum) (aka @ernestum), [Adam Gleave](https://github.com/adamgleave) (@AdamGleave) and [Anssi Kanervisto](https://github.com/Miffyli) (@Miffyli).
222224

223225
**Important Note: We do not do technical support, nor consulting** and don't answer personal questions per email.
224226

docs/conf.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def __getattr__(cls, name):
5252
# -- Project information -----------------------------------------------------
5353

5454
project = 'Stable Baselines'
55-
copyright = '2018-2020, Stable Baselines'
55+
copyright = '2018-2021, Stable Baselines'
5656
author = 'Stable Baselines Contributors'
5757

5858
# The short X.Y version
@@ -125,7 +125,7 @@ def __getattr__(cls, name):
125125

126126

127127
def setup(app):
128-
app.add_stylesheet("css/baselines_theme.css")
128+
app.add_css_file("css/baselines_theme.css")
129129

130130
# Theme options are theme-specific and customize the look and feel of a theme
131131
# further. For a list of options available for each theme, see the

docs/index.rst

+11-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,17 @@ Welcome to Stable Baselines docs! - RL Baselines Made Easy
99
`Stable Baselines <https://github.com/hill-a/stable-baselines>`_ is a set of improved implementations
1010
of Reinforcement Learning (RL) algorithms based on OpenAI `Baselines <https://github.com/openai/baselines>`_.
1111

12+
13+
.. warning::
14+
15+
This package is in maintenance mode, please use `Stable-Baselines3
16+
(SB3)`_ for an up-to-date version. You can find a `migration guide`_ in
17+
SB3 documentation.
18+
19+
20+
.. _Stable-Baselines3 (SB3): https://github.com/DLR-RM/stable-baselines3
21+
.. _migration guide: https://stable-baselines3.readthedocs.io/en/master/guide/migration.html
22+
1223
Github repository: https://github.com/hill-a/stable-baselines
1324

1425
RL Baselines Zoo (collection of pre-trained agents): https://github.com/araffin/rl-baselines-zoo
@@ -18,12 +29,6 @@ RL Baselines zoo also offers a simple interface to train, evaluate agents and do
1829
You can read a detailed presentation of Stable Baselines in the
1930
Medium article: `link <https://medium.com/@araffin/stable-baselines-a-fork-of-openai-baselines-reinforcement-learning-made-easy-df87c4b2fc82>`_
2031

21-
.. note::
22-
23-
Stable-Baselines3 (PyTorch edition) beta is now online: https://github.com/DLR-RM/stable-baselines3
24-
25-
26-
.. Example of internal link: :ref:`ppo2`
2732

2833
Main differences with OpenAI Baselines
2934
--------------------------------------

docs/misc/changelog.rst

+20-6
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@ Changelog
66
For download links, please look at `Github release page <https://github.com/hill-a/stable-baselines/releases>`_.
77

88

9-
Pre-Release 2.10.2a1 (WIP)
9+
Release 2.10.2 (2021-04-05)
1010
---------------------------
1111

12+
.. warning::
13+
14+
This package is in maintenance mode, please use `Stable-Baselines3
15+
(SB3)`_ for an up-to-date version. You can find a `migration guide`_ in
16+
SB3 documentation.
17+
18+
19+
.. _Stable-Baselines3 (SB3): https://github.com/DLR-RM/stable-baselines3
20+
.. _migration guide: https://stable-baselines3.readthedocs.io/en/master/guide/migration.html
21+
1222

1323
Breaking Changes:
1424
^^^^^^^^^^^^^^^^^
@@ -21,12 +31,14 @@ Bug Fixes:
2131
^^^^^^^^^^
2232
- Fixed calculation of the log probability of Diagonal Gaussian distribution
2333
when using ``action_probability()`` method (@SVJayanthi, @sunshineclt)
34+
- Fixed docker image build (@anj1)
2435

2536
Deprecations:
2637
^^^^^^^^^^^^^
2738

2839
Others:
2940
^^^^^^^
41+
- Faster tests, switched to GitHub CI
3042

3143
Documentation:
3244
^^^^^^^^^^^^^^
@@ -93,6 +105,7 @@ Documentation:
93105
- Added Slime Volleyball project (@hardmaru)
94106
- Added a table of the variables accessible from the ``on_step`` function of the callbacks for each algorithm (@PartiallyTyped)
95107
- Fix typo in README.md (@ColinLeongUDRI)
108+
- Fix typo in gail.rst (@roccivic)
96109

97110
Release 2.10.0 (2020-03-11)
98111
---------------------------
@@ -548,12 +561,12 @@ Release 2.4.0 (2019-01-17)
548561
Release 2.3.0 (2018-12-05)
549562
--------------------------
550563

551-
- added support for storing model in file like object. (thanks to @erniejunior)
564+
- added support for storing model in file like object. (thanks to @ernestum)
552565
- fixed wrong image detection when using tensorboard logging with DQN
553566
- fixed bug in ppo2 when passing non callable lr after loading
554567
- fixed tensorboard logging in ppo2 when nminibatches=1
555-
- added early stoppping via callback return value (@erniejunior)
556-
- added more flexible custom mlp policies (@erniejunior)
568+
- added early stoppping via callback return value (@ernestum)
569+
- added more flexible custom mlp policies (@ernestum)
557570

558571

559572
Release 2.2.1 (2018-11-18)
@@ -726,11 +739,11 @@ Maintainers
726739
-----------
727740

728741
Stable-Baselines is currently maintained by `Ashley Hill`_ (aka @hill-a), `Antonin Raffin`_ (aka `@araffin`_),
729-
`Maximilian Ernestus`_ (aka @erniejunior), `Adam Gleave`_ (`@AdamGleave`_) and `Anssi Kanervisto`_ (aka `@Miffyli`_).
742+
`Maximilian Ernestus`_ (aka @ernestum), `Adam Gleave`_ (`@AdamGleave`_) and `Anssi Kanervisto`_ (aka `@Miffyli`_).
730743

731744
.. _Ashley Hill: https://github.com/hill-a
732745
.. _Antonin Raffin: https://araffin.github.io/
733-
.. _Maximilian Ernestus: https://github.com/erniejunior
746+
.. _Maximilian Ernestus: https://github.com/ernestum
734747
.. _Adam Gleave: https://gleave.me/
735748
.. _@araffin: https://github.com/araffin
736749
.. _@AdamGleave: https://github.com/adamgleave
@@ -749,3 +762,4 @@ Thanks to @bjmuld @iambenzo @iandanforth @r7vme @brendenpetersen @huvar @abhiskk
749762
@MarvineGothic @jdossgollin @SyllogismRXS @rusu24edward @jbulow @Antymon @seheevic @justinkterry @edbeeching
750763
@flodorner @KuKuXia @NeoExtended @PartiallyTyped @mmcenta @richardwu @tirafesi @caburu @johannes-dornheim @kvenkman @aakash94
751764
@enderdead @hardmaru @jbarsce @ColinLeongUDRI @shwang @YangRui2015 @sophiagu @OGordon100 @SVJayanthi @sunshineclt
765+
@roccivic @anj1

docs/modules/gail.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ to recover a cost function and then learn a policy.
1111

1212
Learning a cost function from expert demonstrations is called Inverse Reinforcement Learning (IRL).
1313
The connection between GAIL and Generative Adversarial Networks (GANs) is that it uses a discriminator that tries
14-
to seperate expert trajectory from trajectories of the learned policy, which has the role of the generator here.
14+
to separate expert trajectory from trajectories of the learned policy, which has the role of the generator here.
1515

1616
.. note::
1717

scripts/run_tests.sh

-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
#!/bin/bash
22
python -m pytest --cov-config .coveragerc --cov-report html --cov-report term --cov=. -v
3-
pytype

setup.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ filterwarnings =
1919

2020
[pytype]
2121
inputs = stable_baselines
22-
python_version = 3.5
22+
; python_version = 3.5

setup.py

+2
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ def find_tf_dependency():
4646

4747

4848
long_description = """
49+
**WARNING: This package is in maintenance mode, please use [Stable-Baselines3 (SB3)](https://github.com/DLR-RM/stable-baselines3) for an up-to-date version. You can find a [migration guide](https://stable-baselines3.readthedocs.io/en/master/guide/migration.html) in SB3 documentation.**
50+
4951
[![Build Status](https://travis-ci.com/hill-a/stable-baselines.svg?branch=master)](https://travis-ci.com/hill-a/stable-baselines) [![Documentation Status](https://readthedocs.org/projects/stable-baselines/badge/?version=master)](https://stable-baselines.readthedocs.io/en/master/?badge=master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/3bcb4cd6d76a4270acb16b5fe6dd9efa)](https://www.codacy.com/app/baselines_janitors/stable-baselines?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=hill-a/stable-baselines&amp;utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/3bcb4cd6d76a4270acb16b5fe6dd9efa)](https://www.codacy.com/app/baselines_janitors/stable-baselines?utm_source=github.com&utm_medium=referral&utm_content=hill-a/stable-baselines&utm_campaign=Badge_Coverage)
5052
5153
# Stable Baselines

stable_baselines/__init__.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import warnings
23

34
from stable_baselines.a2c import A2C
45
from stable_baselines.acer import ACER
@@ -23,6 +24,11 @@
2324
del mpi4py
2425

2526
# Read version from file
26-
version_file = os.path.join(os.path.dirname(__file__), 'version.txt')
27-
with open(version_file, 'r') as file_handler:
27+
version_file = os.path.join(os.path.dirname(__file__), "version.txt")
28+
with open(version_file, "r") as file_handler:
2829
__version__ = file_handler.read().strip()
30+
31+
32+
warnings.warn(
33+
"stable-baselines is in maintenance mode, please use [Stable-Baselines3 (SB3)](https://github.com/DLR-RM/stable-baselines3) for an up-to-date version. You can find a [migration guide](https://stable-baselines3.readthedocs.io/en/master/guide/migration.html) in SB3 documentation."
34+
)

stable_baselines/a2c/a2c.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ def _run(self):
349349
mb_states = self.states
350350
ep_infos = []
351351
for _ in range(self.n_steps):
352-
actions, values, states, _ = self.model.step(self.obs, self.states, self.dones)
352+
actions, values, states, _ = self.model.step(self.obs, self.states, self.dones) # pytype: disable=attribute-error
353353
mb_obs.append(np.copy(self.obs))
354354
mb_actions.append(actions)
355355
mb_values.append(values)
@@ -389,7 +389,7 @@ def _run(self):
389389
mb_masks = mb_dones[:, :-1]
390390
mb_dones = mb_dones[:, 1:]
391391
true_rewards = np.copy(mb_rewards)
392-
last_values = self.model.value(self.obs, self.states, self.dones).tolist()
392+
last_values = self.model.value(self.obs, self.states, self.dones).tolist() # pytype: disable=attribute-error
393393
# discount/bootstrap off value fn
394394
for n, (rewards, dones, value) in enumerate(zip(mb_rewards, mb_dones, last_values)):
395395
rewards = rewards.tolist()

stable_baselines/common/base_class.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -940,7 +940,7 @@ def load(cls, load_path, env=None, custom_objects=None, **kwargs):
940940
"Stored kwargs: {}, specified kwargs: {}".format(data['policy_kwargs'],
941941
kwargs['policy_kwargs']))
942942

943-
model = cls(policy=data["policy"], env=None, _init_setup_model=False)
943+
model = cls(policy=data["policy"], env=None, _init_setup_model=False) # pytype: disable=not-instantiable
944944
model.__dict__.update(data)
945945
model.__dict__.update(kwargs)
946946
model.set_env(env)
@@ -1048,7 +1048,7 @@ def load(cls, load_path, env=None, custom_objects=None, **kwargs):
10481048
"Stored kwargs: {}, specified kwargs: {}".format(data['policy_kwargs'],
10491049
kwargs['policy_kwargs']))
10501050

1051-
model = cls(policy=data["policy"], env=None, _init_setup_model=False)
1051+
model = cls(policy=data["policy"], env=None, _init_setup_model=False) # pytype: disable=not-instantiable
10521052
model.__dict__.update(data)
10531053
model.__dict__.update(kwargs)
10541054
model.set_env(env)

stable_baselines/common/runners.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __init__(self, *, env: Union[gym.Env, VecEnv], model: 'BaseRLModel', n_steps
3030
self.obs = np.zeros((n_envs,) + env.observation_space.shape, dtype=env.observation_space.dtype.name)
3131
self.obs[:] = env.reset()
3232
self.n_steps = n_steps
33-
self.states = model.initial_state
33+
self.states = model.initial_state # pytype: disable=attribute-error
3434
self.dones = [False for _ in range(n_envs)]
3535
self.callback = None # type: Optional[BaseCallback]
3636
self.continue_training = True

stable_baselines/common/vec_env/subproc_vec_env.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import os
12
import multiprocessing
23
from collections import OrderedDict
34
from typing import Sequence
@@ -77,6 +78,14 @@ def __init__(self, env_fns, start_method=None):
7778
self.closed = False
7879
n_envs = len(env_fns)
7980

81+
# In some cases (like on GitHub workflow machine when running tests),
82+
# "forkserver" method results in an "connection error" (probably due to mpi)
83+
# We allow to bypass the default start method if an environment variable
84+
# is specified by the user
85+
if start_method is None:
86+
start_method = os.environ.get("DEFAULT_START_METHOD")
87+
88+
# No DEFAULT_START_METHOD was specified, start_method may still be None
8089
if start_method is None:
8190
# Fork is not a thread safe method (see issue #217)
8291
# but is more user friendly (does not require to wrap the code in

stable_baselines/logger.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ def __init__(self, filename):
132132
def writekvs(self, kvs):
133133
for key, value in sorted(kvs.items()):
134134
if hasattr(value, 'dtype'):
135-
if value.shape == () or len(value) == 1:
135+
if value.shape == () or len(value) == 1: # pytype: disable=attribute-error
136136
# if value is a dimensionless numpy array or of length 1, serialize as a float
137137
kvs[key] = float(value)
138138
else:
139139
# otherwise, a value is a numpy array, serialize as a list or nested lists
140-
kvs[key] = value.tolist()
140+
kvs[key] = value.tolist() # pytype: disable=attribute-error
141141
self.file.write(json.dumps(kvs) + '\n')
142142
self.file.flush()
143143

stable_baselines/ppo2/ppo2.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,7 @@ def _run(self):
469469
mb_states = self.states
470470
ep_infos = []
471471
for _ in range(self.n_steps):
472-
actions, values, self.states, neglogpacs = self.model.step(self.obs, self.states, self.dones)
472+
actions, values, self.states, neglogpacs = self.model.step(self.obs, self.states, self.dones) # pytype: disable=attribute-error
473473
mb_obs.append(self.obs.copy())
474474
mb_actions.append(actions)
475475
mb_values.append(values)
@@ -503,7 +503,7 @@ def _run(self):
503503
mb_values = np.asarray(mb_values, dtype=np.float32)
504504
mb_neglogpacs = np.asarray(mb_neglogpacs, dtype=np.float32)
505505
mb_dones = np.asarray(mb_dones, dtype=np.bool)
506-
last_values = self.model.value(self.obs, self.states, self.dones)
506+
last_values = self.model.value(self.obs, self.states, self.dones) # pytype: disable=attribute-error
507507
# discount/bootstrap off value fn
508508
mb_advs = np.zeros_like(mb_rewards)
509509
true_reward = np.copy(mb_rewards)

stable_baselines/version.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.10.2a1
1+
2.10.2

0 commit comments

Comments
 (0)