diff --git a/eop/bayes.py b/eop/bayes.py index 8c57a9a5..28956d80 100644 --- a/eop/bayes.py +++ b/eop/bayes.py @@ -40,21 +40,23 @@ class BayesOpeningQuote(OpeningQuote): "author" : "Dennis V. Lindley", } -class IntroducePokerHand(PiCreatureScene): +class IntroducePokerHand(PiCreatureScene, SampleSpaceScene): CONFIG = { "community_cards_center" : 1.5*DOWN, "community_card_values" : ["AS", "QH", "10H", "2C", "5H"], "your_hand_values" : ["JS", "KC"], } def construct(self): - self.force_skipping() - self.add_cards() - # self.indicate_straight() + self.indicate_straight() self.show_flush_potential() self.compute_flush_probability() self.show_flush_sample_space() + self.talk_through_sample_space() self.place_high_bet() + self.change_belief() + self.move_community_cards_out_of_the_way() + self.name_bayes_rule() def add_cards(self): you, her = self.you, self.her @@ -86,6 +88,7 @@ class IntroducePokerHand(PiCreatureScene): self.dither() self.community_cards = community_cards + self.deck = deck def indicate_straight(self): you = self.you @@ -234,7 +237,6 @@ class IntroducePokerHand(PiCreatureScene): color = BLUE, buff = SMALL_BUFF ) - self.revert_to_original_skipping_status() self.play(LaggedStart(FadeIn, equation)) self.dither(2) self.play( @@ -262,23 +264,159 @@ class IntroducePokerHand(PiCreatureScene): you, her = self.you, self.her percentage = self.percentage - sample_space = SampleSpace() - sample_space.add_title() + sample_space = self.get_sample_space() + sample_space.add_title("Your belief") sample_space.move_to(VGroup(you.hand, her.hand)) sample_space.to_edge(UP, buff = MED_SMALL_BUFF) - sample_space.shift(RIGHT) p = 1./22 - top_part, bottom_part = sample_space.divide_horizontally( + sample_space.divide_horizontally( p, colors = [SuitSymbol.CONFIG["red"], BLUE_E] ) + top_label, bottom_label = sample_space.get_side_labels([ + percentage.get_tex_string(), "95.5\\%" + ]) - self.revert_to_original_skipping_status() - self.play(FadeIn(sample_space)) + self.play( + FadeIn(sample_space), + ReplacementTransform(percentage, top_label[1]) + ) + self.play(*map(GrowFromCenter, [ + label[0] for label in top_label, bottom_label + ])) + self.dither(2) + self.play(Write(bottom_label[1])) + self.dither(2) + + self.sample_space = sample_space + + def talk_through_sample_space(self): + her = self.her + sample_space = self.sample_space + top_part, bottom_part = self.sample_space.horizontal_parts + + flush_hands, non_flush_hands = hand_lists = [ + [self.get_hand(her, keys) for keys in key_list] + for key_list in [ + [("3H", "8H"), ("4H", "AH"), ("JH", "KH")], + [("AC", "6D"), ("3D", "6S"), ("JH", "4C")], + ] + ] + for hand_list, part in zip(hand_lists, [top_part, bottom_part]): + self.play(Indicate(part, scale_factor = 1)) + for hand in hand_list: + hand.save_state() + hand.scale(0.01) + hand.move_to(part.get_right()) + self.play(hand.restore) + self.dither() self.dither() - self.add(top_part, bottom_part) + self.play(*map(FadeOut, it.chain(*hand_lists))) def place_high_bet(self): - pass + you, her = self.you, self.her + pre_money = VGroup(*[ + VGroup(*[ + TexMobject("\\$") + for x in range(10) + ]).arrange_submobjects(RIGHT, buff = SMALL_BUFF) + for y in range(4) + ]).arrange_submobjects(UP, buff = SMALL_BUFF) + money = VGroup(*it.chain(*pre_money)) + money.highlight(GREEN) + money.scale(0.8) + money.next_to(her.hand, DOWN) + for dollar in money: + dollar.save_state() + dollar.scale(0.01) + dollar.move_to(her.get_boundary_point(RIGHT)) + dollar.set_fill(opacity = 0) + + self.play(LaggedStart( + ApplyMethod, + money, + lambda m : (m.restore,), + run_time = 5, + )) + self.play(you.change_mode, "confused") + self.dither() + + self.money = money + + def change_belief(self): + numbers = VGroup(*[ + label[1] + for label in self.sample_space.horizontal_parts.labels + ]) + rect = Rectangle(stroke_width = 0) + rect.set_fill(BLACK, 1) + rect.stretch_to_fit_width(numbers.get_width()) + rect.stretch_to_fit_height(self.sample_space.get_height()) + rect.move_to(numbers, UP) + + self.play(FadeIn(rect)) + self.change_horizontal_division( + 0.2, + run_time = 3, + rate_func = there_and_back, + added_anims = [Animation(rect)] + ) + self.play(FadeOut(rect)) + + def move_community_cards_out_of_the_way(self): + cards = self.community_cards + cards.generate_target() + cards.target.arrange_submobjects( + RIGHT, buff = -cards[0].get_width() + MED_SMALL_BUFF, + ) + cards.target.move_to(self.deck) + cards.target.to_edge(LEFT) + + self.sample_space.add(self.sample_space.horizontal_parts.labels) + + self.play( + self.deck.scale, 0.7, + self.deck.next_to, cards.target, UP, + self.deck.to_edge, LEFT, + self.sample_space.shift, 3*DOWN, + MoveToTarget(cards) + ) + + def name_bayes_rule(self): + title = TextMobject("Bayes' rule") + title.highlight(BLUE) + title.to_edge(UP) + subtitle = TextMobject("Update ", "prior ", "beliefs") + subtitle.scale(0.8) + subtitle.next_to(title, DOWN) + prior_word = subtitle.get_part_by_tex("prior") + numbers = VGroup(*[ + label[1] for label in self.sample_space.horizontal_parts.labels + ]) + rect = SurroundingRectangle(numbers, color = GREEN) + arrow = Arrow(prior_word.get_bottom(), rect.get_top()) + arrow.highlight(GREEN) + + words = TextMobject( + "Maybe she really \\\\ does have a flush $\\dots$", + alignment = "" + ) + words.scale(0.7) + words.next_to(self.money, DOWN, aligned_edge = LEFT) + + self.play( + Write(title, run_time = 2), + self.you.change_mode, "pondering" + ) + self.dither() + self.play(FadeIn(subtitle)) + self.play(prior_word.highlight, GREEN) + self.play( + ShowCreation(rect), + ShowCreation(arrow) + ) + self.dither(3) + self.play(Write(words)) + self.dither(3) ###### @@ -337,9 +475,14 @@ class HowDoesPokerWork(TeacherStudentsScene): self.change_student_modes(*["confused"]*3) self.dither(2) -class ShowCountingArgument(IntroducePokerHand): +class YourGutKnowsBayesRule(TeacherStudentsScene): def construct(self): - pass + self.teacher_says( + "Your gut knows \\\\ Bayes' rule.", + run_time = 1 + ) + self.change_student_modes("confused", "gracious", "guilty") + self.dither(3) diff --git a/mobject/tex_mobject.py b/mobject/tex_mobject.py index e238938f..bdda943a 100644 --- a/mobject/tex_mobject.py +++ b/mobject/tex_mobject.py @@ -63,7 +63,6 @@ class TexMobject(SVGMobject): #specialized path_string mobject return TexSymbol(path_string) - def generate_points(self): SVGMobject.generate_points(self) if len(self.args) > 1: diff --git a/topics/probability.py b/topics/probability.py index ccfd23bb..3b634e04 100644 --- a/topics/probability.py +++ b/topics/probability.py @@ -1,12 +1,52 @@ from helpers import * +from scene import Scene + +from animation.animation import Animation +from animation.transform import Transform + from mobject import Mobject from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint from mobject.svg_mobject import SVGMobject -from mobject.tex_mobject import TextMobject, TexMobject +from mobject.tex_mobject import TextMobject, TexMobject, Brace from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon +EPSILON = 0.0001 + +class SampleSpaceScene(Scene): + def get_sample_space(self, **config): + self.sample_space = SampleSpace(**config) + return self.sample_space + + def add_sample_space(self, **config): + self.add(self.get_sample_space(**config)) + + def change_horizontal_division(self, p_list, **kwargs): + assert(hasattr(self.sample_space, "horizontal_parts")) + added_anims = kwargs.pop("added_anims", []) + new_division_kwargs = kwargs.pop("new_division_kwargs", {}) + added_label_kwargs = kwargs.pop("label_kwargs", {}) + + curr_parts = self.sample_space.horizontal_parts + new_division_kwargs["colors"] = [ + part.get_color() for part in curr_parts + ] + new_parts = self.sample_space.get_horizontal_division( + p_list, **new_division_kwargs + ) + anims = [Transform(curr_parts, new_parts)] + if hasattr(curr_parts, "labels"): + label_kwargs = curr_parts.label_kwargs + label_kwargs.update(added_label_kwargs) + new_labels = self.sample_space.get_subdivision_labels( + new_parts, **label_kwargs + ) + anims.append(Transform(curr_parts.labels, new_labels)) + anims += added_anims + + self.play(*anims, **kwargs) + class SampleSpace(VGroup): CONFIG = { @@ -16,7 +56,8 @@ class SampleSpace(VGroup): "fill_color" : DARK_GREY, "fill_opacity" : 0.8, "stroke_width" : 0, - } + }, + "default_label_scale_val" : 0.7, } def __init__(self, **kwargs): VGroup.__init__(self, **kwargs) @@ -31,31 +72,84 @@ class SampleSpace(VGroup): title_mob.next_to(self.full_space, UP, buff = buff) self.title = title_mob self.add(title_mob) + + def add_label(self, label): + self.label = label - def divide_along_dimension(self, p, dim, colors): + def get_division_along_dimension(self, p_list, dim, colors, vect): + p_list = list(tuplify(p_list)) + if abs(1.0 - sum(p_list)) > EPSILON: + p_list.append(1.0 - sum(p_list)) + colors = color_gradient(colors, len(p_list)) perp_dim = 1-dim - if dim == 0: - vects = [UP, DOWN] - else: - vects = [LEFT, RIGHT] + + last_point = self.full_space.get_edge_center(-vect) parts = VGroup() - for factor, vect, color in zip([p, 1-p], vects, colors): - part = self.full_space.copy() + for factor, color in zip(p_list, colors): + part = SampleSpace() part.set_fill(color, 1) + part.replace(self.full_space, stretch = True) part.stretch(factor, perp_dim) - part.move_to(self.full_space, vect) + part.move_to(last_point, -vect) + last_point = part.get_edge_center(vect) parts.add(part) return parts - def divide_horizontally(self, p, colors = [GREEN_E, RED_E]): - result = self.divide_along_dimension(p, 0, colors) - self.top_part, self.bottom_part = result - return result + def get_horizontal_division( + self, p_list, + colors = [GREEN_E, BLUE], + vect = DOWN + ): + return self.get_division_along_dimension(p_list, 0, colors, vect) + + def get_vertical_division( + self, p_list, + colors = [MAROON_B, YELLOW], + vect = RIGHT + ): + return self.get_division_along_dimension(p_list, 1, colors, vect) + + def divide_horizontally(self, *args, **kwargs): + self.horizontal_parts = self.get_horizontal_division(*args, **kwargs) + self.add(self.horizontal_parts) + + def divide_vertically(self, *args, **kwargs): + self.vertical_parts = self.get_vertical_division(*args, **kwargs) + self.add(self.vertical_parts) + + def get_subdivision_labels(self, parts, labels, direction, buff = SMALL_BUFF): + label_brace_groups = VGroup() + for label, part in zip(labels, parts): + brace = Brace(part, direction, min_num_quads = 1, buff = buff) + label_mob = TexMobject(label) + label_mob.scale(self.default_label_scale_val) + label_mob.next_to(brace, direction, buff) + full_label = VGroup(brace, label_mob) + part.add_label(full_label) + label_brace_groups.add(full_label) + parts.labels = label_brace_groups + parts.label_kwargs = { + "labels" : labels, + "direction" : direction, + "buff" : buff, + } + return label_brace_groups + + def get_side_labels(self, labels, direction = LEFT, **kwargs): + assert(hasattr(self, "horizontal_parts")) + parts = self.horizontal_parts + return self.get_subdivision_labels(parts, labels, direction, **kwargs) + + def get_top_labels(self, labels, **kwargs): + assert(hasattr(self, "vertical_parts")) + parts = self.vertical_parts + return self.get_subdivision_labels(parts, labels, UP, **kwargs) + + def get_bototm_labels(self, labels, **kwargs): + assert(hasattr(self, "vertical_parts")) + parts = self.vertical_parts + return self.get_subdivision_labels(parts, labels, DOWN, **kwargs) - def divide_vertically(self, p, colors = [MAROON_B, YELLOW]): - result = self.divide_along_dimension(p, 1, colors) - self.left_part, self.right_part = result - return result ### Cards ### @@ -225,7 +319,8 @@ class PlayingCard(VGroup): ) sub_rect.move_to(self) - pi_color = average_color(symbol.get_color(), GREY) + # pi_color = average_color(symbol.get_color(), GREY) + pi_color = symbol.get_color() pi_mode = { "J" : "plain", "Q" : "thinking", @@ -305,3 +400,22 @@ class SuitSymbol(SVGMobject): self.set_stroke(width = 0) self.set_fill(color, 1) self.scale_to_fit_height(self.height) + + + + + + + + + + + + + + + + + + +