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

Extend flattener to allow multiple sets #1768

Merged
merged 48 commits into from
Jan 27, 2021
Merged
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
9954229
function to create a slice for a particular set
Robbybp Dec 2, 2020
ea53e7a
function for generating sliced components
Robbybp Dec 2, 2020
3494653
function to get flattened components
Robbybp Dec 2, 2020
30cbc19
bug fix for higher-dimen, non-product sets
Robbybp Dec 2, 2020
92fdb51
add test with Constraint.Skip
Robbybp Dec 2, 2020
543e1b1
cross product sets in a single function call
Robbybp Dec 2, 2020
903c05a
Catch errors when attempting to iterate over entire index set
Robbybp Dec 2, 2020
c13a87e
comments
Robbybp Dec 8, 2020
e844ec0
comments, mostly
Robbybp Dec 15, 2020
349bb77
tests for new flattener
Robbybp Dec 15, 2020
89e6961
catch invalid component after call
Robbybp Dec 15, 2020
f38eaab
extract first entry of len-1 index for compatibility with flatten==False
Robbybp Dec 15, 2020
bf47abd
test with multi-dim sets when flatten==False
Robbybp Dec 15, 2020
92bd30f
beginning test for flattening along a 2-d set
Robbybp Dec 16, 2020
391855a
a few lines for debugging
Robbybp Dec 18, 2020
afa91de
Merge branch 'master' of https://github.com/Pyomo/pyomo into flatten-…
Robbybp Dec 18, 2020
6b30f95
lines for debugging
Robbybp Dec 20, 2020
f1b4686
comments
Robbybp Dec 20, 2020
a1bfa4a
typo
Robbybp Dec 20, 2020
2a6ea20
tests that flatten over multi-dimensional sets
Robbybp Dec 20, 2020
986e774
comment about an edge case
Robbybp Dec 20, 2020
6eef67d
remove commented code
Robbybp Dec 31, 2020
1d9fed6
add index_map argument that does nothing yet
Robbybp Dec 31, 2020
8b7f828
use indices that were specified to descend into blocks
Robbybp Dec 31, 2020
1ba53d8
use supplied indices when all sets must be sliced
Robbybp Jan 1, 2021
91a45e4
tests that use supplied indices
Robbybp Jan 1, 2021
7564598
checkout changes from other branch
Robbybp Jan 2, 2021
f8722ff
remove test for KeyError with blocks
Robbybp Jan 2, 2021
2cb6b18
remove comments and comment out unused `except KeyError`
Robbybp Jan 2, 2021
7694ba1
remove done todos
Robbybp Jan 2, 2021
d9f18b1
add test for flattening three sets
Robbybp Jan 3, 2021
11e14f8
allow indices argument in flatten_dae_components
Robbybp Jan 3, 2021
bbdde0b
remove index_stack argument
Robbybp Jan 3, 2021
3cf3f42
docstrings
Robbybp Jan 3, 2021
248c0c1
remove unused code
Robbybp Jan 3, 2021
f00e1be
docstring
Robbybp Jan 3, 2021
157291c
remove new code not needed by this branch
Robbybp Jan 3, 2021
b43e67f
Merge branch 'master' of https://github.com/Pyomo/pyomo into flatten-…
Robbybp Jan 4, 2021
79805fc
Merge branch 'master' into flatten-multiple
blnicho Jan 15, 2021
c7db719
Merge branch 'master' into flatten-multiple
jsiirola Jan 19, 2021
cef6bfe
Python 2.7 compatibility fixes
jsiirola Jan 19, 2021
467e745
Merge branch 'master' into flatten-multiple
jsiirola Jan 19, 2021
c4fb599
Merge branch 'master' of https://github.com/Pyomo/pyomo into flatten-…
Robbybp Jan 22, 2021
0f20f27
Merge branch 'flatten-multiple' of https://github.com/Robbybp/pyomo i…
Robbybp Jan 26, 2021
2314548
updating docstrings and comments per review
Robbybp Jan 26, 2021
0507a52
reference issue for slicing higher dimen sets with flatten==False
Robbybp Jan 26, 2021
bcd22e6
more descriptive model names
Robbybp Jan 26, 2021
7c26253
rename _slice to slice_
Robbybp Jan 26, 2021
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
Prev Previous commit
Next Next commit
function for generating sliced components
  • Loading branch information
Robbybp committed Dec 2, 2020
commit ea53e7a3ec71af2de1c16b18cd2643ee8f0a6f40
139 changes: 139 additions & 0 deletions pyomo/dae/flatten.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,142 @@ class NotAnIndex(object):
"""
pass

def _fill_indices_from_product(partial_index_list, product):
"""
`partial_index_list` is a list of indices, each corresponding to a
set. If an entry in `partial_index_list` is `NotAnIndex`, that
slot will get filled in by an entry from `setprod`.
blnicho marked this conversation as resolved.
Show resolved Hide resolved

`product` is a `SetProduct` with as many "factors" as there are
missing indices in `partial_index_list`.
"""
# We will manipulate `normalize_index.flatten`.
# Store its original value so we can reset it when we're done.
_normalize_index_flatten = normalize_index.flatten
normalize_index.flatten = False
for index in product:
# Since `normalize_index.flatten` is False, `index` is a
# scalar or (tuple of (scalars or tuples)). Conveniently,
# each entry in the tuple belongs to a single factor set.
#
# To simplify some later code we convert scalar to tuple.
if type(index) is not tuple:
index = (index,)
# We need to generate a new index for every entry of `product`,
# and want to reuse `partial_index_list` as a starting point,
# so we copy it here.
filled_index = partial_index_list.copy()
j = 0
for i, val in enumerate(filled_index):
if val is NotAnIndex:
filled_index[i] = index[j]
# We have made `index` a tuple so the above is valid.
j += 1
# Make sure `partial_index_list` has the same number of vacancies
# as `product` has factors. Not _strictly_ necessary.
assert j == len(index)
filled_index = tuple(filled_index)

normalize_index.flatten = True
# `filled_index` can now be used in a rational way...
yield filled_index
normalize_index.flatten = False
# Want to get the unflattened factors when we advance the
# `product` iterator.

# Reset `normalize_index.flatten`
normalize_index.flatten = _normalize_index_flatten

def generate_sliced_components(b, index_stack, _slice, sets, ctype):
"""
blnicho marked this conversation as resolved.
Show resolved Hide resolved
`b` is a _BlockData object.

`index_stack` is a list of indices "above" `b` in the
hierarchy. Note that `b` is a data object, so any index
of its parent component should be included in the stack.

`_slice` is the slice generated so far. Our goal here is to
yield extensions to `_slice` at this level of the hierarchy.
Robbybp marked this conversation as resolved.
Show resolved Hide resolved

`ctype` is the type we are looking for.
"""
for c in b.component_objects(ctype, descend_into=False):
subsets = list(c.index_set().subsets())
temp_idx = [get_slice_for_set(s) if s in sets else NotAnIndex
for s in subsets]
new_sets = [s for s in subsets if s in sets]
other_sets = [s for s in subsets if s not in sets]
sliced_sets = index_stack + new_sets

c_is_indexed = not (len(other_sets) == 1 and
other_sets[0] is UnindexedComponent_set)
# Why not just c.is_indexed()

# We have extended our "index stack;" now we must extend
# our slice.

if other_sets and c_is_indexed:
cross_prod = other_sets[0]
for s in other_sets[1:]:
cross_prod *= s

for new_index in _fill_indices_from_product(temp_idx, cross_prod):
c_slice = getattr(_slice, c.local_name)[new_index]
yield sliced_sets, c_slice
else:
# `c` is indexed only by sets we would like to slice.
# At this point we could just yield sliced_sets, new_slice
idx = Ellipsis if c_is_indexed else None
c_slice = getattr(_slice, c.local_name)[idx]
# ^ This could fail if we try to apply the same code to
# data objects rather than just simple components.
yield sliced_sets, c_slice

# We now descend into subblocks
for sub in b.component_objects(pyo.Block, descend_into=False):
subsets = list(sub.index_set().subsets())
temp_idx = [get_slice_for_set(s) if s in sets else NotAnIndex
for s in subsets]
new_sets = [s for s in subsets if s in sets]
other_sets = [s for s in subsets if s not in sets]

# Extend stack with new matched indices.
index_stack.extend(new_sets)

if other_sets:
cross_prod = other_sets[0]
for s in other_sets[1:]:
cross_prod *= s

for new_index in _fill_indices_from_product(temp_idx, cross_prod):
sub_slice = getattr(_slice, sub.local_name)[new_index]
if type(sub_slice) is IndexedComponent_slice:
# Get the first index of the sliced subblock to descend
# into. TODO: Should probably allow some "representative
# index" to be specified for each set so we don't miss
# components that are skipped at endpoints.
data = next(iter(sub_slice))
else:
# sub_slice is a block data object
data = sub_slice
for st, v in generate_sliced_components(data, index_stack,
sub_slice, sets, ctype):
yield tuple(st), v
else:
# Either sub is a simple component, or we are slicing
# all of its sets.
idx = Ellipsis if new_sets else None
sub_slice = getattr(_slice, sub.local_name)[idx]
data = next(iter(sub_slice))
# ^ This works as sub_slice is either a slice or a simple
# component.
for st, v in generate_sliced_components(data, index_stack,
sub_slice, sets, ctype):
yield tuple(st), v

# pop the index sets of the block whose sub-components
# we just finished iterating over.
for s in subsets:
if s in sets:
index_stack.pop()