-
-
Notifications
You must be signed in to change notification settings - Fork 116
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
created a weighted scorer and started writing tests #243
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -147,6 +147,83 @@ def create_score_array(self, time_resolution=1*u.minute): | |
score_array *= constraint(self.observer, targets, times) | ||
return score_array | ||
|
||
def weighted_score_array(self, time_resolution=1*u.minute): | ||
""" | ||
For each block, this returns a score for each time using the | ||
formula (score*weight+score*weight+...)/(weight+weight+..) | ||
""" | ||
# note: with this method, a non-boolean constraint with weight | ||
# zero has the exact same effect if it was boolean | ||
start = self.schedule.start_time | ||
end = self.schedule.end_time | ||
times = time_grid_from_range((start, end), time_resolution) | ||
# create an array to hold all of the scores | ||
score_array = np.zeros((len(self.blocks), len(times))) | ||
# create an array to record where any of the constraints are zero | ||
constraint_zeros = np.ones((len(self.blocks), len(times)), dtype=int) | ||
local_constraints = [] | ||
weights = [] | ||
for i, block in enumerate(self.blocks): | ||
weights.append(0) | ||
local_constraints.append([]) | ||
# schedulers put global constraints into ._all_constraints | ||
# so we can use .constraints for the local constraints | ||
if block.constraints: | ||
for constraint in block.constraints: | ||
local_constraints[i].append(constraint.__class__.__name__) | ||
applied_score = constraint(self.observer, [block.target], | ||
times=times)[0] | ||
if constraint.boolean_constraint: | ||
# add to the mask designating where the score is zero | ||
# if either is 0, constraint_zeros becomes 0 | ||
constraint_zeros[i] &= applied_score | ||
# TODO: make a default weight=1 and merge these | ||
elif constraint.weight: | ||
constraint_zeros[i][(applied_score == 0)] = 0 | ||
weight = constraint.weight | ||
weights[i] += weight | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason that you add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The weight is per block (i.e. |
||
score_array[i] += applied_score * weight | ||
else: | ||
constraint_zeros[i][(applied_score == 0)] = 0 | ||
weights[i] += 1 | ||
score_array[i] += applied_score | ||
targets = [block.target for block in self.blocks] | ||
for constraint in self.global_constraints: | ||
skip_global = [] | ||
for i, block in enumerate(local_constraints): | ||
if constraint.__class__.__name__ in block: | ||
skip_global.append(i) | ||
global_score = constraint(self.observer, targets, times) | ||
if constraint.boolean_constraint: | ||
# This should apply to every block (if the global fails, then | ||
# the local should have failed anyway) | ||
constraint_zeros &= global_score | ||
elif constraint.weight: | ||
weight = constraint.weight | ||
for i, score in enumerate(global_score): | ||
if i not in skip_global: | ||
weights[i] += weight | ||
score_array[i] += score*weight | ||
constraint_zeros[i][(score == 0)] = 0 | ||
else: | ||
for i, score in enumerate(global_score): | ||
if i not in skip_global: | ||
weights[i] += 1 | ||
score_array[i] += score | ||
constraint_zeros[i][(score == 0)] = 0 | ||
|
||
for i, scores in enumerate(score_array): | ||
if weights[i]: | ||
scores *= 1/float(weights[i]) | ||
else: | ||
# if no weight, then nothing was added to the score_array | ||
# just use the zeros (squaring 0 and 1 gives 0 and 1) | ||
scores += constraint_zeros[i] | ||
# considering the else above, score_array would be constraint_zeros^2 | ||
# which is fine since all of its values should be 0 or 1 | ||
score_array *= constraint_zeros | ||
return score_array | ||
|
||
@classmethod | ||
def from_start_end(cls, blocks, observer, start_time, end_time, | ||
global_constraints=[]): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -265,3 +265,30 @@ def test_scorer(): | |
scores = scorer.create_score_array(time_resolution=20 * u.minute) | ||
# the ``global_constraint``: constraint2 should have applied to the blocks | ||
assert np.array_equal(c2, scores) | ||
|
||
|
||
def test_weighted_scorer(): | ||
times = time_grid_from_range(Time(['2016-02-06 00:00', '2016-02-06 08:00']), | ||
time_resolution=20 * u.minute) | ||
constraint = AirmassConstraint(max=2, boolean_constraint=False) | ||
constraint.weight = .8 | ||
c = constraint(apo, [vega, rigel], times) | ||
block = ObservingBlock(vega, 1 * u.hour, 0, constraints=[constraint]) | ||
block2 = ObservingBlock(rigel, 1 * u.hour, 0, constraints=[constraint]) | ||
scorer = Scorer.from_start_end([block, block2], apo, Time('2016-02-06 00:00'), | ||
Time('2016-02-06 08:00')) | ||
scores = scorer.weighted_score_array(time_resolution=20 * u.minute) | ||
# due to float multiplication and division c and scores are not exactly equal | ||
assert np.array_equal(np.round(c, 10), np.round(scores, 10)) | ||
|
||
constraint2 = MoonIlluminationConstraint(max=.6, boolean_constraint=False) | ||
constraint2.weight = .7 | ||
c2 = constraint2(apo, [vega, rigel], times) | ||
block = ObservingBlock(vega, 1 * u.hour, 0, constraints=[constraint, constraint2]) | ||
block2 = ObservingBlock(rigel, 1 * u.hour, 0, constraints=[constraint]) | ||
scorer = Scorer.from_start_end([block, block2], apo, Time('2016-02-06 00:00'), | ||
Time('2016-02-06 08:00')) | ||
scores = scorer.weighted_score_array(time_resolution=20 * u.minute) | ||
assert all(scores[0] - (c[0] * .8 + c2[0] * .7)/1.5) | ||
np.array_equal(np.round(scores[0], 10), np.round((c[0] * .8 + c2[0] * .7)/1.5, 10)) | ||
assert np.array_equal(np.round(scores[1], 10), np.round(c[1], 10)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kvyh Hello, I just started an internship with the goal of creating an optimized scheduler for astronomic observations with astroplan. I am interested in adding weight to the various constraints and I just found this post but this was 6 years ago and not implemented , I was wondering if this methode of adding weight works ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be addressed before merge?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but we need to decide if we want weighting to be part of the constraints package (add a kwarg to
Constraint
) or the scheduling package (constraint_weights
kwarg forObservingBlock
andglobal_constraint_weights
kwarg forSchedule
). During the implementation of either method it would be very easy to set the default weight and fix this TODO.