def index(self, indexer: Indexer):
        ret = []
        triangles = indexer.index_by_type(Triangle)
        exist_relations = set()
        two_sums = indexer.index_by_type(TwoSum)
        for two_sum in two_sums:
            exist_relations.add(two_sum.relationship.id)
        for tri in triangles:
            known_angles = []
            for angle in tri.angles:
                angle_A_cond = indexer.index_value_condition(angle, 'angle')
                if angle_A_cond.attr_value is not None:
                    known_angles.append(angle_A_cond)

            if len(known_angles) >= 2:
                pairs = itertools.product(known_angles, known_angles)
                for angle_B_cond, angle_C_cond in pairs:
                    id_ = f'{angle_B_cond.obj.id}.angle_{angle_C_cond.obj.id}.angle'
                    if id_ in exist_relations:
                        continue
                    two_sum_relation = TwoSum(
                        id_, angle_B_cond.obj, 'angle', angle_C_cond.obj,
                        'angle',
                        angle_B_cond.attr_value + angle_C_cond.attr_value)
                    r = RelationshipBased(two_sum_relation)
                    ret.append([[angle_B_cond, angle_C_cond], r])

        return ret
Ejemplo n.º 2
0
 def _find_common_vertex_angle(self, cva_cond: RelationshipBased,
                               indexer: Indexer):
     r = cva_cond.relationship
     vertex = r.vertex
     ends = r.ends
     size = len(ends)
     for i in range(size):
         for j in range(i + 1, size):
             for k in range(j + 1, size):
                 angle_ij = indexer.index_angle_by_points(
                     ends[i], vertex, ends[j])
                 angle_jk = indexer.index_angle_by_points(
                     ends[j], vertex, ends[k])
                 angle_ik = indexer.index_angle_by_points(
                     ends[i], vertex, ends[k])
                 cond_ik = indexer.index_value_condition(angle_ik, 'angle')
                 if cond_ik.attr_value is None:
                     continue
                 eq_cond = index_equivalent_value(indexer, angle_ij,
                                                  'angle', angle_jk,
                                                  'angle')
                 if eq_cond is None:
                     continue
                 cond_ij = indexer.index_value_condition(angle_ij, 'angle')
                 cond_jk = indexer.index_value_condition(angle_jk, 'angle')
                 unkown_cond = [
                     c for c in [cond_ij, cond_jk] if c.attr_value is None
                 ]
                 if not unkown_cond:
                     continue
                 return [[eq_cond, cond_ik], unkown_cond]
     return None
Ejemplo n.º 3
0
 def _find_common_vertex_angle(self, cva_cond: RelationshipBased,
                               indexer: Indexer):
     r = cva_cond.relationship
     vertex = r.vertex
     ends = r.ends
     size = len(ends)
     for i in range(size):
         for j in range(i + 1, size):
             for k in range(j + 1, size):
                 angle_ij = indexer.index_angle_by_points(
                     ends[i], vertex, ends[j])
                 angle_jk = indexer.index_angle_by_points(
                     ends[j], vertex, ends[k])
                 angle_ik = indexer.index_angle_by_points(
                     ends[i], vertex, ends[k])
                 cond_ij = indexer.index_value_condition(angle_ij, 'angle')
                 cond_jk = indexer.index_value_condition(angle_jk, 'angle')
                 cond_ik = indexer.index_value_condition(angle_ik, 'angle')
                 if sum([
                         cond_ij.attr_value is None,
                         cond_jk.attr_value is None,
                         cond_ik.attr_value is None
                 ]) == 1:
                     return [[cva_cond, cond_ij, cond_jk], (cond_ik)]
     return None
 def __init__(self, entity: Entity, conditions: List[Condition], target: Target):
     if len(entity.children) == 0:
         raise ValueError("Problem entity is empty!")
     self.entity = entity
     self.conditions = conditions
     self.target = target
     # Initialize deduction graph.
     self.graph = DeductionGraph(conditions, target)
     # Build indexer
     self.indexer = Indexer(entity, self.graph)
Ejemplo n.º 5
0
 def index(self, indexer: Indexer):
     ret = []
     rt_conds = indexer.index_by_type(IsRightTriangle)
     for cond in rt_conds:
         r = cond.relationship
         angle = r.right_angle
         angle_cond = indexer.index_value_condition(angle, 'angle')
         if angle_cond.attr_value is None:
             ret.append([[cond], angle_cond])
     return ret
def basic_pattern():
    entity, target, conditions = get_problem()

    graph = DeductionGraph(conditions, target)
    indexer = Indexer(entity, graph)
    pattern = TrianglePattern(angle_A=AttributeState.KNOWN,
                              angle_B=AttributeState.KNOWN,
                              angle_C=AttributeState.KNOWN)
    conditions, entitis = indexer.index_by_pattern(pattern, True)
    print(entitis)
    print(conditions)
    def index(self, indexer: Indexer):
        """Find interior angles on the same side from parallel relationship."""
        ret = []


        def find_alternate_angles(link_col, col1, col2, p1, p2):
            p1_index = link_col.index(p1)
            p2_index = link_col.index(p2)
            reverse = lambda x: -1 if x == 0 else 0
            compare = p1_index < p2_index
            p1_direction = -1 if compare else 0

            for k in [0, -1]:
                if link_col[p1_direction] == p1 or col1[k] == p1:
                    continue
                if link_col[reverse(p1_direction)] == p2  or col2[k] == p2:
                    continue

                angle1 = indexer.index_angle_by_points(\
                    link_col[p1_direction], p1, col1[k])
                angle2 = indexer.index_angle_by_points(\
                    link_col[reverse(p1_direction)], p2, col2[k])
                a1_cond = indexer.index_value_condition(angle1, 'angle')
                a2_cond = indexer.index_value_condition(angle2, 'angle')
                if a1_cond.attr_value is None \
                        and a2_cond.attr_value is not None:
                    ret.append([[a2_cond], a1_cond])
                elif a1_cond.attr_value is not None \
                        and a2_cond.attr_value is None:
                    ret.append([[a1_cond], a2_cond])


        conds = indexer.index_by_type(Parallel)
        for cond in conds:
            r = cond.relationship
            line1, line2 = r.line1, r.line2
            # col is a list of point entity.
            col1 = indexer.index_collineation_by_line(line1)
            col2 = indexer.index_collineation_by_line(line2)
            if r.reverse:
                col2 = list(reversed(col2))
            for p1 in col1:
                for p2 in col2:
                    # Link line is the line links two parallel lines.
                    link_line = indexer.index_line_by_points(p1, p2)
                    # One link line can generate four corresponding angles at most.
                    if link_line is None:
                        continue
                    link_col = indexer.index_collineation_by_line(link_line)
                    find_alternate_angles(link_col, col1, col2, p1, p2)

        return ret
Ejemplo n.º 8
0
 def index(self, indexer: Indexer):
     ret = []
     rt_conds = indexer.index_by_type(IsRightTriangle)
     for cond in rt_conds:
         r = cond.relationship
         hypotenuse = r.hypotenuse
         legs = r.legs
         a = indexer.index_value_condition(legs[0], 'length')
         b = indexer.index_value_condition(legs[1], 'length')
         c = indexer.index_value_condition(hypotenuse, 'length')
         if attr_value_known_num([a, b, c]) == 2:
             ret.append([[cond, a, b], c])
     return ret
 def index(self, indexer: Indexer):
     ret = []
     e_conds = indexer.index_by_type(IsEquilateralTriangle)
     for cond in e_conds:
         r = cond.relationship
         tg = []
         for angle in r.triangle.angles:
             angle_cond = indexer.index_value_condition(angle, 'angle')
             if angle_cond.attr_value is None:
                 tg.append(angle_cond)
         for t in tg:
             ret.append([[cond], t])
     return ret
class Problem(object):
    
    def __init__(self, entity: Entity, conditions: List[Condition], target: Target):
        if len(entity.children) == 0:
            raise ValueError("Problem entity is empty!")
        self.entity = entity
        self.conditions = conditions
        self.target = target
        # Initialize deduction graph.
        self.graph = DeductionGraph(conditions, target)
        # Build indexer
        self.indexer = Indexer(entity, self.graph)
    
    def set_entity(self, entity: Entity) -> None:
        self.entity = entity

    def set_conditions(self, conditions: List[Condition]) -> None:
        self.conditions = conditions
        
    @property
    def solved(self):
        return self.graph.solved
    
    def deduct(self, theorem):
        # Traverse all [sources, target] pair that meet requirement of theorem.
        for srcs, tg in theorem.index(self.indexer):
            srcs, tg = theorem.deduct(srcs, tg)
            # There can be multiple targets.
            if not isinstance(tg, list):
                tg = [tg]
            for tg_ in tg:
                self.graph.expand(tg_)
                tg_.from_conditions = srcs
                tg_.from_theorem = theorem
                self.indexer.update_index(tg_)
                
    def is_valid(self, theorem):
        """Determine whether the given theorem is valid."""
        return True if theorem.index(self.indexer) else False
                
    def solving_path(self):
        return self.graph.solving_path()
    
    def solving_steps(self):
        return self.graph.solving_steps()
    
    def plain_word_answer(self):
        return self.graph.plain_word_answer()
    
    def show_graph(self):
        self.graph.show_graph()
    def index(self, indexer: Indexer):
        ret = []
        nls_conds = indexer.index_by_type(NLineSector)
        for cond in nls_conds:
            r = cond.relationship
            # Existence of ratio condition is guaranteed.
            ratio_cond = indexer.index_value_condition(r, 'ratio')
            ratio = ratio_cond.attr_value
            
            near_point, split_point, far_point = r.three_points
            line_near = indexer.index_line_by_points(near_point, split_point)
            line_far = indexer.index_line_by_points(far_point, split_point)
            line_full = indexer.index_line_by_points(near_point, far_point)
            
            near_cond = indexer.index_value_condition(line_near, 'length')
            far_cond = indexer.index_value_condition(line_far, 'length')
            full_cond = indexer.index_value_condition(line_full, 'length')
            
            if full_cond.attr_value is None:
                if near_cond.attr_value is not None:
                    ret.append([[near_cond, ratio_cond, 1/ratio], full_cond])
                elif far_cond.attr_value is not None:
                    ret.append([[far_cond, ratio_cond, 1/(1-ratio)], full_cond])
            if near_cond.attr_value is None:
                if full_cond.attr_value is not None:
                    ret.append([[full_cond, ratio_cond, ratio], near_cond])
                elif far_cond.attr_value is not None:
                    ret.append([[far_cond, ratio_cond, ratio/(1-ratio)], near_cond])
            if far_cond.attr_value is None:
                if full_cond.attr_value is not None:
                    ret.append([[full_cond, ratio_cond, 1-ratio], far_cond])
                elif near_cond.attr_value is not None:
                    ret.append([[near_cond, ratio_cond, (1-ratio)/ratio], far_cond])

        return ret
 def index(self, indexer: Indexer):
     ret = []
     conds = indexer.index_by_type(OppositeVerticalAngle)
     for cond in conds:
         r = cond.relationship
         angle1_cond = indexer.index_value_condition(r.angle1, 'angle')
         angle2_cond = indexer.index_value_condition(r.angle2, 'angle')
         if angle1_cond.attr_value is not None and \
                 angle2_cond.attr_value is None:
             ret.append([[cond, angle1_cond], angle2_cond])
         elif angle1_cond.attr_value is None and \
                 angle2_cond.attr_value is not None:
             ret.append([[cond, angle2_cond], angle1_cond])
     return ret
    def index(self, indexer: Indexer):
        ret = []
        two_sums = indexer.index_by_type(TwoSum)
        for two_sum in two_sums:
            r = two_sum.relationship
            cond1 = indexer.index_value_condition(r.entity1, r.attr1, False)
            cond2 = indexer.index_value_condition(r.entity2, r.attr2, False)
            if cond1 is None and cond2 is not None:
                cond1 = indexer.index_value_condition(r.entity1, r.attr1)
                ret.append([[two_sum, cond2], cond1])
            elif cond1 is not None and cond2 is None:
                cond2 = indexer.index_value_condition(r.entity2, r.attr2)
                ret.append([[two_sum, cond1], cond2])

        return ret
 def index(self, indexer: Indexer):
     ret = []
     st_conds = indexer.index_by_type(SimilarTriangle)
     for cond in st_conds:
         r = cond.relationship
         for angle1, angle2 in r.cor_angle:
             a1_cond = indexer.index_value_condition(angle1, 'angle')
             a2_cond = indexer.index_value_condition(angle2, 'angle')
             if a1_cond.attr_value is None \
                     and a2_cond.attr_value is not None:
                 ret.append([[cond, a2_cond], a1_cond])
             elif a1_cond.attr_value is not None \
                     and a2_cond.attr_value is None:
                 ret.append([[cond, a1_cond], a2_cond])
     return ret
Ejemplo n.º 15
0
    def index(self, indexer: Indexer):
        ret = []
        i_conds = indexer.index_by_type(IsIsoscelesTriangle)

        for cond in i_conds:
            r = cond.relationship
            base_angle = r.base_angle
            a0_cond = indexer.index_value_condition(base_angle[0], 'angle')
            a1_cond = indexer.index_value_condition(base_angle[1], 'angle')
            if a0_cond.attr_value is None \
                    and a1_cond.attr_value is not None:
                ret.append([[cond, a1_cond], a0_cond])
            elif a0_cond.attr_value is not None \
                    and a1_cond.attr_value is None:
                ret.append([[cond, a0_cond], a1_cond])
        return ret
Ejemplo n.º 16
0
 def index(self, indexer: Indexer):
     ret = []
     st_conds = indexer.index_by_type(SimilarTriangle)
     
     for cond in st_conds:
         r = cond.relationship
         for side1, side2 in r.cor_sides:
             side1_cond = indexer.index_value_condition(side1, 'length')
             side2_cond = indexer.index_value_condition(side2, 'length')
             if side1_cond.attr_value is not None \
                     and side2_cond.attr_value is not None:
                 ratio = indexer.index_value_condition(r, 'ratio')
                 if ratio.attr_value is None:
                     ret.append([[cond, side1_cond, side2_cond], ratio])
                 break
     return ret
 def index(self, indexer: Indexer):
     ret = []
     triangles = indexer.index_by_type(Triangle)
     for th in triangles:
         side1 = indexer.index_value_condition(th.side1, 'length')
         if side1.attr_value is None:
             continue
         side2 = indexer.index_value_condition(th.side2, 'length')
         if side2.attr_value is None:
             continue
         side3 = indexer.index_value_condition(th.side3, 'length')
         if side3.attr_value is None:
             continue
         ret.append([[side1, side2, side3],
                     AttributeValue(th, **{'circumference': None})])
     return ret
 def index(self, indexer: Indexer):
     ret = []
     i_conds = indexer.index_by_type(IsIsoscelesTriangle)
     
     for cond in i_conds:
         r = cond.relationship
         h = r.hypotenuse
         h0_cond = indexer.index_value_condition(h[0], 'length')
         h1_cond = indexer.index_value_condition(h[1], 'length')
         if h0_cond.attr_value is None \
                 and h1_cond.attr_value is not None:
             ret.append([[cond, h1_cond], h0_cond])
         elif h0_cond.attr_value is not None \
                 and h1_cond.attr_value is None:
             ret.append([[cond, h0_cond], h1_cond])
     return ret
Ejemplo n.º 19
0
 def index(self, indexer: Indexer):
     ret = []
     st_conds = indexer.index_by_type(SimilarTriangle)
     for cond in st_conds:
         r = cond.relationship
         ratio_cond = indexer.index_value_condition(r, 'ratio')
         if ratio_cond.attr_value is None:
             continue
         for side1, side2 in r.cor_sides:
             s1_cond = indexer.index_value_condition(side1, 'length')
             s2_cond = indexer.index_value_condition(side2, 'length')
             if sum(
                 [s1_cond.attr_value is None,
                  s2_cond.attr_value is None]) == 1:
                 ret.append([[cond, s1_cond, ratio_cond], s2_cond])
     return ret
Ejemplo n.º 20
0
 def index(self, indexer: Indexer):
     ret = []
     triangles = indexer.index_by_type(Triangle)
     for th in triangles:
         side1 = indexer.index_value_condition(th.side1, 'length')
         if side1.attr_value is None: 
             continue
         side2 = indexer.index_value_condition(th.side2, 'length')
         if side2.attr_value is None: 
             continue
         side3 = indexer.index_value_condition(th.side3, 'length')
         if side3.attr_value is None: 
             continue
         area = indexer.index_value_condition(th, 'area')
         if area.attr_value is None:
             ret.append([[side1, side2, side3], area])
     return ret
    def index(self, indexer: Indexer):
        ret = []
        r_conds = indexer.index_by_type(IsRightTriangle)
        triangles = indexer.index_by_type(Triangle)
        right_triangles = [cond.relationship.triangle for cond in r_conds]

        for th in triangles:
            if th in right_triangles:
                continue
            for angle in [th.angle1, th.angle2, th.angle3]:
                a_cond = indexer.index_value_condition(angle, 'angle')
                if a_cond.attr_value is not None and a_cond.attr_value == 90:
                    rt = IsRightTriangle(th.id, th, angle)
                    rt_cond = RelationshipBased(rt)
                    ret.append([[a_cond], rt_cond])
                    break
        return ret
Ejemplo n.º 22
0
 def index(self, indexer: Indexer):
     conditions = []
     cvas = indexer.index_by_type(CommonVertexAngle)
     for cva_cond in cvas:
         cond = self._find_common_vertex_angle(cva_cond, indexer)
         if cond is not None:
             conditions.append(cond)
     return conditions
 def index(self, indexer: Indexer):
     conditions = []
     cols = indexer.index_by_type(Collineation)
     for col_cond in cols:
         cond = self._find_lines_by_collineation(col_cond, indexer)
         if cond is not None:
             conditions.append(cond)
     return conditions
Ejemplo n.º 24
0
 def index(self, indexer: Indexer):
     ret = []
     triangles = indexer.index_by_type(Triangle)
     exist_relations = {}
     two_sums = indexer.index_by_type(TwoSum)
     for two_sum in two_sums:
         exist_relations[two_sum.relationship.id] = two_sum
     for tri in triangles:
         for angle in tri.angles:
             angle_A_cond = indexer.index_value_condition(angle, 'angle')
             if angle_A_cond.attr_value is None:
                 angle_B, angle_C = [a for a in tri.angles if a != angle]
                 id_ = f'{angle_B.id}.angle_{angle_C.id}.angle'
                 if id_ in exist_relations:
                     two_angle_sum = exist_relations[id_]
                     ret.append([[two_angle_sum], angle_A_cond])
     return ret
Ejemplo n.º 25
0
    def index(self, indexer: Indexer):
        pattern = TrianglePattern(line_AB=AttributeState.KNOWN,
                                  line_AC=AttributeState.KNOWN,
                                  line_BC=AttributeState.KNOWN,
                                  angle_A=AttributeState.UNKNOWN)

        repleced_patterns = indexer.index_by_pattern(pattern)
        return [([p.line_AB, p.line_AC, p.line_BC], p.angle_A) \
                for p in repleced_patterns]
Ejemplo n.º 26
0
 def index(self, indexer: Indexer):
     ret = []
     e_conds = indexer.index_by_type(IsEquilateralTriangle)
     for cond in e_conds:
         r = cond.relationship
         pre = []
         tg = []
         for side in r.triangle.sides:
             side_cond = indexer.index_value_condition(side, 'length')
             if side_cond.attr_value is None:
                 tg.append(side_cond)
             else:
                 pre.append(side_cond)    
         if pre:
             pre = [cond] + pre
             for t in tg:
                 ret.append([pre, t])
     return ret
Ejemplo n.º 27
0
 def index(self, indexer: Indexer):
     ret = []
     rt_conds = indexer.index_by_type(IsRightTriangle)
     for cond in rt_conds:
         r = cond.relationship
         triangle = r.triangle
         area_cond = indexer.index_value_condition(triangle, 'area')
         # If area is already known, skip.
         if area_cond.attr_value is not None:
             continue
         leg1_cond = indexer.index_value_condition(r.legs[0], 'length')
         if leg1_cond.attr_value is None:
             continue
         leg2_cond = indexer.index_value_condition(r.legs[1], 'length')
         if leg2_cond.attr_value is None:
             continue
         ret.append([[cond, leg1_cond, leg2_cond], area_cond])
     return ret
Ejemplo n.º 28
0
    def index(self, indexer: Indexer):
        """Find corresponding angles from parallel relationship."""
        ret = []

        def find_corresponding_angles(link_col, col1, col2, p1, p2):
            for i in [0, -1]:
                for j in [0, -1]:
                    if link_col[i] == p1 or col1[j] == p1:
                        continue
                    if link_col[i] == p2 or col2[j] == p2:
                        continue
                    angle1 = indexer.index_angle_by_points(\
                        link_col[i], p1, col1[j])
                    angle2 = indexer.index_angle_by_points(\
                        link_col[i], p2, col2[j])
                    a1_cond = indexer.index_value_condition(angle1, 'angle')
                    a2_cond = indexer.index_value_condition(angle2, 'angle')
                    if a1_cond.attr_value is None \
                            and a2_cond.attr_value is not None:
                        ret.append([[a2_cond], a1_cond])
                    elif a1_cond.attr_value is not None \
                            and a2_cond.attr_value is None:
                        ret.append([[a1_cond], a2_cond])

        conds = indexer.index_by_type(Parallel)
        for cond in conds:
            r = cond.relationship
            line1, line2 = r.line1, r.line2
            # col is a list of point entity.
            col1 = indexer.index_collineation_by_line(line1)
            col2 = indexer.index_collineation_by_line(line2)
            if r.reverse:
                col2 = list(reversed(col2))
            for p1 in col1:
                for p2 in col2:
                    # Link line is the line links two parallel lines.
                    link_line = indexer.index_line_by_points(p1, p2)
                    # One link line can generate four corresponding angles at most.
                    if link_line is None:
                        continue
                    link_col = indexer.index_collineation_by_line(link_line)
                    find_corresponding_angles(link_col, col1, col2, p1, p2)

        return ret
 def index(self, indexer: Indexer):
     ret = []
     conds = indexer.index_by_type(Perpendicular)
     for cond in conds:
         r = cond.relationship
         line1 = r.line1
         line2 = r.line2
         if r.foot_point is None:
             r.foot_point = indexer.index_line_intersection(line1, line2)
         foot_point = r.foot_point
         for p1 in [line1.end1, line1.end2]:
             for p2 in [line2.end1, line2.end2]:
                 if p1 == foot_point or p2 == foot_point:
                     continue
                 angle = indexer.index_angle_by_points(p1, foot_point, p2)
                 angle_cond = indexer.index_value_condition(angle, 'angle')
                 if angle_cond.attr_value is None:
                     ret.append([[cond], AttributeValue(angle, **{'angle': None})])
     return ret
Ejemplo n.º 30
0
 def index(self, indexer: Indexer):
     ret = []
     conds = indexer.index_by_type(SupplementaryAngle)
     exist_relations = set()
     two_sums = indexer.index_by_type(TwoSum)
     for two_sum in two_sums:
         exist_relations.add(two_sum.relationship.id)
     for cond in conds:
         r = cond.relationship
         angle1_cond = indexer.index_value_condition(r.angle1, 'angle')
         angle2_cond = indexer.index_value_condition(r.angle2, 'angle')
         id_ = f'{angle1_cond.obj.id}.angle_{angle2_cond.obj.id}.angle'
         if id_ in exist_relations:
             continue
         two_sum_relation = TwoSum(id_, angle1_cond.obj, 'angle',
                                   angle2_cond.obj, 'angle', 180)
         r = RelationshipBased(two_sum_relation)
         ret.append([[cond], r])
     return ret