Parameters
Short introduction to attrs
sequencing relies on the attrs package for defining and instantiating classes with many parameters. attrs allows you to define classes with minimal boilerplate by automating the creation of important methods like __init__, __repr__, __str__, and comparison methods like __eq__. For full details on attrs, check out the documentation.
A class defined using attrs must be decorated with @attr.s (or attr.attrs). In place of an explicit __init__ method, attributes of a class decorated with @attr.s can have instance attributes defined at the class level using attr.ib() (or attr.attrib()). Adding attributes to a class using attr.ib() has many advantages:
Attributes may be required or have a default value
Defaults can be defined either with a specific value or with a “factory” function that generates a default value when called
Attributes can have a
typeassociated with them, or aconverterfunction that converts the user-specified value into the desired formatAttributes can have
validatorswhich raise an error if an invalid value is provided
For the full list of options see attr.ib()
Parameterized
sequencing builds on the functionality of attrs using a class called Parameterized. Parameterized objects must have a name and can have any number of parameters, which can be created using the functions defined in sequencing.parameters, or by using attrs directly via attr.ib().
Parameterized offers the following convenient features:
Recursive
get()andset()methods for getting and setting attributes of nestedParameterizedobjects.Methods for converting a
Parameterizedobject into a Pythondict, and creating a newParameterizedobject from adict.Methods for serializing a
Parameterizedobject tojsonand creating a newParameterizedobject fromjson.
Notes:
Subclasses of
Parameterizedmust be decorated with@attr.sSubclasses of
Parameterizedcan define aninitialize()method, which takes no arguments. It will be called on instantiation after theattrs-generated__init__method (see **attrs_post_init** for more details). If defined, the subclass’initialize()method should always callsuper().initialize()to ensure that the superclass is correctly initialized.
[1]:
import json
from pprint import pprint
import attr
from sequencing.parameters import (
Parameterized, StringParameter, BoolParameter, ListParameter, DictParameter,
IntParameter, FloatParameter, NanosecondParameter, GigahertzParameter, RadianParameter,
)
[2]:
@attr.s
class Engine(Parameterized):
cylinders = IntParameter(4)
displacement = FloatParameter(2, unit='liter')
current_rpm = FloatParameter(0, unit='rpm')
turbo_charged = BoolParameter(False)
@attr.s
class Transmission(Parameterized):
manual = BoolParameter(False)
num_gears = IntParameter(5)
current_gear = IntParameter(1)
def initialize(self):
super().initialize()
# Add private attributes in initialize()
self._is_broken = True
@property
def has_clutch(self):
return self.manual
def shift_to(self, gear):
if gear not in range(self.num_gears+1):
# 0 is reverse
raise ValueError(f'Cannot shift into gear {gear}')
if abs(gear - self.current_gear) > 1:
raise ValueError('Cannot skip gears')
self.current_gear = gear
@attr.s
class Car(Parameterized):
VALID_CHASSIS = ['sedan', 'coupe', 'hatchback', 'suv']
chassis = StringParameter('sedan', validator=attr.validators.in_(VALID_CHASSIS))
num_doors = IntParameter(4, validator=attr.validators.in_([2,4]))
miles_per_gallon = FloatParameter(30, unit='mpg')
engine = attr.ib(factory=lambda: Engine('engine'))
transmission = attr.ib(factory=lambda: Transmission('transmission'))
[3]:
car = Car('car') # All parameters other than name are optional because they have defaults
print(car)
Car(name='car', cls='__main__.Car', chassis='sedan', num_doors=4, miles_per_gallon=30.0, engine=Engine(name='engine', cls='__main__.Engine', cylinders=4, displacement=2.0, current_rpm=0.0, turbo_charged=False), transmission=Transmission(name='transmission', cls='__main__.Transmission', manual=False, num_gears=5, current_gear=1))
[4]:
pprint(car.as_dict())
{'chassis': 'sedan',
'cls': '__main__.Car',
'engine': {'cls': '__main__.Engine',
'current_rpm': 0.0,
'cylinders': 4,
'displacement': 2.0,
'name': 'engine',
'turbo_charged': False},
'miles_per_gallon': 30.0,
'name': 'car',
'num_doors': 4,
'transmission': {'cls': '__main__.Transmission',
'current_gear': 1,
'manual': False,
'name': 'transmission',
'num_gears': 5}}
[5]:
car2 = Car.from_dict(car.as_dict())
print(car == car2)
True
[6]:
car.get('engine.displacement') == {'engine.displacement': car.engine.displacement}
[6]:
True
[7]:
car.set_param('engine.displacement', 2.5)
car.get_param('engine.displacement') == car.engine.displacement == 2.5
[7]:
True
[8]:
car.set(engine__displacement=3.0)
print(car.get('engine.displacement'))
{'engine.displacement': 3.0}
[9]:
print(f'RPM: {car.engine.current_rpm}, gear: {car.transmission.current_gear}')
with car.temporarily_set(engine__current_rpm=4000, transmission__current_gear=3):
print(f'RPM: {car.engine.current_rpm}, gear: {car.transmission.current_gear}')
print(f'RPM: {car.engine.current_rpm}, gear: {car.transmission.current_gear}')
RPM: 0.0, gear: 1
RPM: 4000, gear: 3
RPM: 0.0, gear: 1
[10]:
try:
convertible = Car('convertible', chassis='convertible')
except ValueError as e:
print('ValueError:', e)
ValueError: ("'chassis' must be in ['sedan', 'coupe', 'hatchback', 'suv'] (got 'convertible')", Attribute(name='chassis', default='sedan', validator=<in_ validator with options ['sedan', 'coupe', 'hatchback', 'suv']>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=<class 'str'>, kw_only=False, inherited=False, on_setattr=None), ['sedan', 'coupe', 'hatchback', 'suv'], 'convertible')
[11]:
try:
three_door = Car('three_door', num_doors=3)
except ValueError as e:
print('ValueError:', e)
ValueError: ("'num_doors' must be in [2, 4] (got 3)", Attribute(name='num_doors', default=4, validator=<in_ validator with options [2, 4]>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type=None, converter=<class 'int'>, kw_only=False, inherited=False, on_setattr=None), [2, 4], 3)
[12]:
from qutip.ipynbtools import version_table
version_table()
[12]:
| Software | Version |
|---|---|
| QuTiP | 4.7.0 |
| Numpy | 1.23.2 |
| SciPy | 1.9.1 |
| matplotlib | 3.5.3 |
| Cython | 0.29.32 |
| Number of CPUs | 2 |
| BLAS Info | OPENBLAS |
| IPython | 8.4.0 |
| Python | 3.8.6 (default, Oct 19 2020, 15:10:29) [GCC 7.5.0] |
| OS | posix [linux] |
| Tue Aug 30 19:26:45 2022 UTC | |
[ ]: