How to get pyomo VarData/values from slice of indexed variable? - python

I'm working with pyomo variables indexed by multiple sets. I've created slices along some sets and would like to use these slices to access the variable values, given indices of the sliced-along sets.
Code that I hoped would work is:
from pyomo.environ import *
m = ConcreteModel()
m.time = Set(initialize=[1,2,3])
m.space = Set(initialize=[1,2,3])
m.comp = Set(initialize=['a','b','c'])
m.v = Var(m.time, m.space, m.comp, initialize=1)
slice = m.v[:, :, 'a']
for x in m.space:
value_list = []
for t in m.time:
value_list.append(slice[t, x].value)
# write value_list to csv file
But this gives me:
>>> value_list
[<pyomo.core.base.indexed_component_slice._IndexedComponent_slice object at 0x7f4db9104a58>, <pyomo.core.base.indexed_component_slice._IndexedComponent_slice object at 0x7f4db9104a58>, <pyomo.core.base.indexed_component_slice._IndexedComponent_slice object at 0x7f4db9104a58>]
instead of a list of values, as I hoped.
Is it possible to access values corresponding to variables slices from only the wildcard indices?
I tried using some of the methods of _IndexedComponent_slice, without success. For example:
>>> for item in slice.wildcard_items(): item
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/rparker1/Idaes/pyomo/pyomo/core/base/indexed_component_slice.py", line 194, in <genexpr>
return ((_iter.get_last_index_wildcards(), _) for _ in _iter)
File "/home/rparker1/Idaes/pyomo/pyomo/core/base/indexed_component_slice.py", line 350, in __next__
_comp = _comp.__getitem__( _call[1] )
AttributeError: '_GeneralVarData' object has no attribute '__getitem__'
I would expect some method to give me a dictionary mapping wildcard indices to vardata objects, but could not find one. Any help finding such a dictionary or other solution is much appreciated.

_IndexedComponent_slice objects are a bit tricky, in that they are designed to work with hierarchical models. As such, they should be thought of as more of a special iterator and not as a view into a dictionary. In particular, these "slice-like" objects defer the resolution of __getitem__, __getattr__, and __call__ until iteration time. So, when you say slice.value, that attribute lookup doesn't actually occur until you iterate over the slice.
The easiest way to get the variable values is to iterate over the slice:
value_list = list(m.v[:, :, 'a'].value)
If you want a new component that you can treat in a dictionary-like manner (just like the original Var), then you want to create a Reference component using the slice:
r = Reference(m.v[:, :, 'a'])
These can be attached to a model like any other component, and (for regular slices) will adopt the ctype of the referred-to objects (so in this case, r will look and act just like a Var).

Related

TypeError: 'bool' object is not iterable eventhough using another variable

First post ever :)
I know that the if statement returns a Boolean value. But I am not using it to iterate a list, I'm using x. I just want all other variables (type = IntVar) to be set to 0 except for i which has to remain 1. Thanks in advance, my first ever time here...
def clear():
variables=[var, var0, var1, var2, var3, var4,var5,var6, var7, var8]
for i in variables:
if i.get() == 1:
x = variables.index(i)
for y in variables in range(0,x-1) and range(x,9):
y.set(0)
My original code is 500 lines long so not ideal to post it full here.
Full traceback:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Thomas Jence\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "G:\Mrs. Odewale\Computing coursework\options.py", line 187, in clear9
for y in variables in range(0,x-1) and range(x,9):
TypeError: 'bool' object is not iterable
for y in variables in range(0,x-1) and range(x,9):
is interpreted as (split up with parentheses and by line to make groupings clear):
for y in (
variables in (
range(0, x-1) and range(x,9)
)
):
The innermost parentheses then determine if range(0, x-1) is empty or not, and use it if it's not, and use range(x,9) if it is. The next layer then tests if variables is in the "winning" range. Then you effectively do:
for y in False:
because variables is a list, so it's definitely not in any range.
I don't know what you're trying to do, but you need to reconsider that line.
Not familiar with python, but as a standard coding practice you never want to change values in an array while you are iterating through them.
Instead create a new array, which has same values as the existing array. iterate the old array while setting values of the new array, and then after the loop, set the your array to the new values of the new array.

Object is enumerable but not indexable?

Problem summary and question
I'm trying to look at some of the data inside an object that can be enumerated over but not indexed. I'm still newish to python, but I don't understand how this is possible.
If you can enumerate it, why can't you access the index through the same way enumerate does? And if not, is there a way to access the items individually?
The actual example
import tensorflow_datasets as tfds
train_validation_split = tfds.Split.TRAIN.subsplit([6, 4])
(train_data, validation_data), test_data = tfds.load(
name="imdb_reviews",
split=(train_validation_split, tfds.Split.TEST),
as_supervised=True)
Take a select subset of the dataset
foo = train_data.take(5)
I can iterate over foo with enumerate:
[In] for i, x in enumerate(foo):
print(i)
which generates the expected output:
0
1
2
3
4
But then, when I try to index into it foo[0] I get this error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-44-2acbea6d9862> in <module>
----> 1 foo[0]
TypeError: 'TakeDataset' object does not support indexing
Python only allows these things if the class has methods for them:
__getitem__ is required for the [] syntax.
__iter__ and __next__1 are required to iterate.
Any class can define one without defining the other. __getattr__ is usually not defined if it would be inefficient.
1 __next__ is required on the class returned by __iter__.
This is a result of foo being iterable, but not having a __getitem__ function. You can use itertools.isslice to get the nth element of an iterable like so
import itertools
def nth(iterable, n, default=None):
"Returns the nth item or a default value"
return next(itertools.islice(iterable, n, None), default)
In Python, instances of custom classes can implement enumeration through special (or "dunder") __iter__ method. Perhaps this class implements __iter__ but not __getitem__.
Dunder overview: https://dbader.org/blog/python-dunder-methods
Specs for an __iter__ method: https://docs.python.org/3/library/stdtypes.html#typeiter

How to find types of all attributes of an object in Python?

I have a tensor
x = torch.tensor([1, 2, 3])
I did this
len(dir(x))
which gave this,
464
I want to know how many of these 464 attributes are builtin_function_or_method, or method, or any other type.
how do I list the type of the attributes of a tensor?
help(x) generates some basic documentation on whatever you pass in. It'll tell you the type of the object, attributes, methods on it, etc.
Usually, the attributes you are not supposed to access start with _ or __. So, [att for att in dir(x) if not att.startswith('_')]
If you want to exclude functions too, add and not callable(att) to the condition.
this is what I did to get types of all attributes of a tensor
import modules, create tensor
import torch
from collections import defaultdict
x = torch.tensor([1., 2., 3.])
below list comprehension gives a list of attributes along with their types
a = [(f'x.{i}', type(getattr(x, i))) for i in dir(x)]
using defaultdict, made a dictionary, which stores attributes according to type.
e = defaultdict(list)
for i, j in a.items():
e[j].append(i)

strange error using AttrDict on Python 2.7

I am getting a weird recurring error using AttrDict 2.0 on Python 2.7. The weird part is that transitive assignment seems to break, but only when using AttrDict.
What's happening is that I want to instantiate a new list on an object if it doesn't exist and then append data to it.
If I use AttrDict, the list somehow gets transformed into a tuple and I get an exception.
from attrdict import AttrDict
class Test(object):
pass
try:
for cls_ in [Test,AttrDict]:
foo = cls_()
print ("\ntesting with class %s" % (cls_))
#this
chk = foo.li = getattr(foo, "li", None) or []
print(" type(chk):%s, id(chk):%s" % (type(chk),id(chk)))
print(" type(foo.li):%s, id(foo.li):%s" % (type(foo.li),id(foo.li)))
foo.li.append(3)
print (" success appending with class %s: foo.li:%s" % (cls_, foo.li))
except (Exception,) as e:
# pdb.set_trace()
raise
Now check out the output, when I use the Test class vs when I use AttrDict.
testing with class <class '__main__.Test'>
type(chk):<type 'list'>, id(chk):4465207704
type(foo.li):<type 'list'>, id(foo.li):4465207704
success appending with class <class '__main__.Test'>: foo.li:[3]
With the custom Test class, as expected, chk and foo.li are both lists and have the same id. append works.
Looking at the pass using AttrDict, id does not match and foo.li is a tuple rather than a list.
testing with class <class 'attrdict.dictionary.AttrDict'>
type(chk):<type 'list'>, id(chk):4465207848
type(foo.li):<type 'tuple'>, id(foo.li):4464595080
Traceback (most recent call last):
File "test_attrdict2.py", line 25, in <module>
test()
File "test_attrdict2.py", line 18, in test
foo.li.append(3)
AttributeError: 'tuple' object has no attribute 'append'
Is attrdict assignment actually returning some kind of property/accessor object that gets changed the 2nd time you access it?
Took #abartnet's suggestion:
from attrdict import AttrDict
a = AttrDict()
a.li = []
print(a.li)
output:
()
OK, but even if that points to some weird behavior on AttrDict's end, how is it the transitive assignment does not assign the tuple as well?
reworked:
from attrdict import AttrDict
a = AttrDict()
b = a.li = []
print("a.li:", a.li)
print("b:",b)
output:
('a.li:', ())
('b:', [])
This is part of the automatic recursiveness of AttrDict. Which is explained better in the inline help (which you can find here in the source) than in the README:
If a values which is accessed as an attribute is a Sequence-type (and is not a string/bytes), it will be converted to a _sequence_type with any mappings within it converted to Attrs.
In other words, in order to auto-convert any dict or other mappings recursively inside your AttrDict to AttrDict values when doing attribute access, it also converts all sequences to (by default) tuple. This is a little weird, but appears to be intentional and somewhat-documented behavior, not a bug.
>>> a = AttrDict()
>>> a._sequence_type
tuple
>>> a.li = []
>>> a.li
()
The more flexible AttrMap type lets you specify the sequence type, and documents that you can disable this recursive remapping stuff by passing None:
>>> a = AttrMap(sequence_type=None)
>>> a.li = []
>>> a.li
[]
But of course AttrMap isn't a dict (although it is a collections.abc.MutableMapping, and more generally it duck-types as a dict-like type).
OK, but even if that points to some weird behavior on AttrDict's end, how is it the transitive assignment does not assign the tuple as well?
Because that's not how chained assignment works. Oversimplifying a bit:
target1 = target2 = value
… is not equivalent to this:
target2 = value
target1 = target2
… but to this:
target2 = value
target1 = value
The best way to understand why that's true: targets aren't expressions, and therefore don't have values. Sure, often the exact same sequence of tokens would be valid as an expression elsewhere in the grammar, but that sequence of tokens never gets evaluated as an expression anywhere in an assignment statement—otherwise, simple things like d['spam'] = 'eggs' would have to raise an exception if d['spam'] didn't exist.
Also, a.li = [] doesn't actually assign tuple([]) anywhere; it actually stores the [] internally, and does the tuple(…) later, when you try to access a.li. You can't really tell that for sure without reading the source, but when you consider that a['li'] gives you [] rather than (), it pretty much has to be true. And, in fact:
>>> li = []
>>> a.li = li
>>> a['li'] is li
True

Can you assign object instances directly to a list in Python?

I've defined a class called Space, meant to represent spaces on a 9x9 board. I want to generate 81 instances at the beginning of the program by assigning them to elements of a list that I intend to use as a 2D array.
Here's the class:
class Space:
def __init__(self, x_coord, y_coord):
self.row = x_coord
self.column = y_coord
Here's one of my attempts to assign instances to a list (also called space):
for i in xrange(1,9):
for j in xrange(1,9):
space[i][j] = Space(i,j)
My goal here is to be able to reference instances using the list indices as if they were coordinates, ie space[1][2].value = 3
Is what I'm trying to do possible? Is there a better way to generate instances en masse? Thanks!
Edit: (My response below in more readable form)
The script runs, but it looks like my list is not defined when I try to access it in the command prompt
>>> space[1][2].value = 3
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
NameError: name 'space' is not defined
>>> space[1][1]
Traceback (most recent call last):
File "<interactive input>", line 1, in <module>
NameError: name 'space' is not defined
It sounds like you're never creating the space lists you're trying to use to hold your Space objects. Here's one way to do it, by starting with an empty list and appending the values into the list as you go:
space = [] # initially empty list
for i in range(9):
column = [] # create an empty list to hold a column of values
for j in range(9):
column.append(Space(i, j)) # add the object to the column
space.append(column) # add the column to the outer list
A cleaner, more "Pythonic" solution is to use nested list comprehensions, as in isedev's answer:
space = [[Space(i, j) for j in range(9)] for i in range(9)]
As a final note, using the same word with only capitalization differences for different things in your code is probably a bad idea. It can be OK to use a variable named foo to hold a single Foo instance, but a better name would indicate what the instance is for, e.g. foo_for_frobbing. In your case, board might be a better name than space for your list of Space instances.
You'll have to use 0-based indexing, but this will achieve what you want:
space = [[Space(i,j) for j in range(9)] for i in range(9)]

Categories

Resources