Skip to content

Commit e2b2131

Browse files
committed
feat: evaluation_type determines evaluation criteria, response should have evlauation_type within it
1 parent 31e0e16 commit e2b2131

3 files changed

Lines changed: 201 additions & 237 deletions

File tree

evaluation_function/evaluation.py

Lines changed: 51 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -700,89 +700,68 @@ def _err(msg: str) -> Result:
700700
# ── helper: grade a simple boolean property ──────────────────────────
701701
def _grade_bool(
702702
label: str,
703-
expected: Optional[bool],
704703
compute_fn: Callable[[Graph], bool],
705704
) -> Result:
706-
if expected is None:
707-
return _err(f"{label}: expected value not set by the teacher in the answer.")
708-
stu = compute_fn(student_graph)
709-
if bool(stu) == bool(expected):
705+
"""Check if the student's graph has the specified property."""
706+
has_property = compute_fn(student_graph)
707+
if has_property:
710708
return _ok()
711-
return _err(f"{label}: expected={expected}, got={stu}.")
709+
return _err(f"{label}: The graph does not have this property.")
712710

713711
# ── evaluation-type handlers ─────────────────────────────────────────
714712

715713
def _eval_connectivity() -> Result:
714+
"""Check if the student's graph is connected."""
716715
conn_params = p.connectivity
717716
check_type = conn_params.check_type if conn_params else "connected"
718717

719-
expected = ans.is_connected
720-
if expected is None:
721-
return _err("Connectivity: expected value (answer.is_connected) not set by the teacher.")
722-
723-
student_value = connectivity_info(
724-
student_graph, connectivity_type=check_type, return_components=False
725-
).is_connected
718+
result = connectivity_info(
719+
student_graph, connectivity_type=check_type, return_components=True
720+
)
726721

727-
if bool(student_value) == bool(expected):
722+
if result.is_connected:
728723
return _ok()
729724

730-
details = connectivity_info(student_graph, connectivity_type=check_type, return_components=True)
731-
fb = f"Connectivity ({check_type}): expected={expected}, got={student_value}."
732-
if details.components is not None:
733-
fb += f" Components={details.components}."
725+
fb = f"Connectivity ({check_type}): Graph is not connected."
726+
if result.components is not None:
727+
fb += f" Found {len(result.components)} components: {result.components}."
734728
return _err(fb)
735729

736730
def _eval_bipartite() -> Result:
731+
"""Check if the student's graph is bipartite."""
737732
b_params = p.bipartite
738733
want_parts = bool(b_params.return_partitions) if b_params else False
739-
want_odd = bool(b_params.return_odd_cycle) if b_params else False
740-
741-
expected = ans.is_bipartite
742-
if expected is None:
743-
return _err("Bipartite: expected value (answer.is_bipartite) not set by the teacher.")
734+
want_odd = bool(b_params.return_odd_cycle) if b_params else True
744735

745-
student_value = bipartite_info(student_graph).is_bipartite
736+
result = bipartite_info(student_graph, return_partitions=want_parts, return_odd_cycle=want_odd)
746737

747-
if bool(student_value) == bool(expected):
738+
if result.is_bipartite:
748739
return _ok()
749740

750-
details = bipartite_info(student_graph, return_partitions=want_parts, return_odd_cycle=want_odd)
751-
fb = f"Bipartite: expected={expected}, got={student_value}."
752-
if want_parts and details.partitions is not None:
753-
fb += f" Partitions={details.partitions}."
754-
if want_odd and details.odd_cycle is not None:
755-
fb += f" Odd cycle={details.odd_cycle}."
741+
fb = "Bipartite: Graph is not bipartite."
742+
if want_odd and result.odd_cycle is not None:
743+
fb += f" Found odd cycle: {result.odd_cycle}."
756744
return _err(fb)
757745

758746
def _eval_cycle_detection() -> Result:
759-
expected = ans.has_cycle
760-
if expected is None:
761-
return _err("Cycle detection: expected value (answer.has_cycle) not set by the teacher.")
747+
"""Check if the student's graph has a cycle."""
748+
result = cycle_info(student_graph)
762749

763-
student_value = cycle_info(student_graph).has_cycle
764-
765-
if bool(student_value) == bool(expected):
750+
if result.has_cycle:
766751
return _ok()
767752

768-
details = cycle_info(student_graph)
769-
fb = f"Cycle detection: expected={expected}, got={student_value}."
770-
if details.cycle is not None:
771-
fb += f" Cycle found={details.cycle}."
772-
return _err(fb)
753+
return _err("Cycle detection: Graph does not contain a cycle.")
773754

774755
def _eval_graph_coloring() -> Result:
756+
"""Check if the student's graph is k-colorable."""
775757
gc_params = p.graph_coloring
776758
num_colors = gc_params.num_colors if (gc_params and gc_params.num_colors is not None) else ans.num_colors
777759
if num_colors is None:
778-
return _err("Missing num_colors: provide in params.graph_coloring.num_colors or answer.num_colors.")
779-
780-
expected = ans.is_colorable
781-
if expected is None:
782-
return _err("Graph coloring: expected value (answer.is_colorable) not set by the teacher.")
760+
return _err("Missing num_colors: provide in answer.num_colors.")
783761

784762
student_coloring = resp.coloring
785763

764+
# If student provided a coloring, validate it
786765
if student_coloring is not None:
787766
adj: dict[str, set[str]] = {n.id: set() for n in student_graph.nodes}
788767
for e in student_graph.edges:
@@ -814,84 +793,60 @@ def _eval_graph_coloring() -> Result:
814793

815794
if not valid_coloring:
816795
return _err(f"Graph coloring ({num_colors}-coloring): invalid coloring. {invalid_reason}")
817-
818-
if not expected:
819-
return _err(
820-
f"Graph coloring ({num_colors}-coloring): student provided a valid coloring, "
821-
f"but the expected answer says the graph is NOT {num_colors}-colorable."
822-
)
823796
return _ok()
824797

825-
student_value = is_n_colorable(student_graph, num_colors).is_colorable
798+
# Otherwise, check if the graph is k-colorable
799+
result = is_n_colorable(student_graph, num_colors)
826800

827-
if bool(student_value) == bool(expected):
801+
if result.is_colorable:
828802
return _ok()
829803

830-
fb = f"Graph coloring ({num_colors}-coloring): expected={expected}, got={student_value}."
831-
ref = is_n_colorable(student_graph, num_colors)
832-
if ref.coloring is not None:
833-
fb += f" A valid coloring exists: {ref.coloring}."
834-
return _err(fb)
804+
return _err(f"Graph coloring: Graph is not {num_colors}-colorable.")
835805

836806
def _eval_isomorphism() -> Result:
807+
"""Check if the student's graph is isomorphic to the reference graph."""
837808
if ans.graph is None:
838-
return _err("Isomorphism requires answer.graph (the teacher's reference graph).")
809+
return _err("Isomorphism requires answer.graph (the reference graph).")
839810

840811
result = isomorphism_info(ans.graph, student_graph)
841812

842-
# Teacher provides a reference graph; by default the student's graph
843-
# must be isomorphic to it (expected=True).
844-
expected = ans.is_isomorphic if ans.is_isomorphic is not None else True
845-
846-
if bool(result.is_isomorphic) == bool(expected):
813+
if result.is_isomorphic:
847814
return _ok()
848815

849-
fb = f"Isomorphism: expected={expected}, got={result.is_isomorphic}."
850-
if result.node_mapping is not None:
851-
fb += f" Mapping={result.node_mapping}."
852-
return _err(fb)
816+
return _err("Isomorphism: Graphs are not isomorphic.")
853817

854818
def _eval_planarity() -> Result:
855-
return _grade_bool("Planarity", ans.is_planar, is_planar)
819+
return _grade_bool("Planarity", is_planar)
856820

857821
def _eval_tree() -> Result:
858-
return _grade_bool("Tree", ans.is_tree, is_tree)
822+
return _grade_bool("Tree", is_tree)
859823

860824
def _eval_forest() -> Result:
861-
return _grade_bool("Forest", ans.is_forest, is_forest)
825+
return _grade_bool("Forest", is_forest)
862826

863827
def _eval_dag() -> Result:
864828
if not student_graph.directed:
865829
return _err("DAG check requires a directed graph.")
866-
return _grade_bool("DAG", ans.is_dag, is_dag)
830+
return _grade_bool("DAG", is_dag)
867831

868832
def _eval_eulerian() -> Result:
869-
return _grade_bool("Eulerian circuit", ans.is_eulerian, is_eulerian)
833+
return _grade_bool("Eulerian circuit", is_eulerian)
870834

871835
def _eval_semi_eulerian() -> Result:
872-
return _grade_bool("Semi-Eulerian (Euler path)", ans.is_semi_eulerian, is_semi_eulerian)
836+
return _grade_bool("Semi-Eulerian (Euler path)", is_semi_eulerian)
873837

874838
def _eval_regular() -> Result:
875-
return _grade_bool("Regular", ans.is_regular, is_regular)
839+
return _grade_bool("Regular", is_regular)
876840

877841
def _eval_complete() -> Result:
878-
return _grade_bool("Complete", ans.is_complete, is_complete)
842+
return _grade_bool("Complete", is_complete)
879843

880844
def _eval_degree_sequence() -> Result:
881-
expected_seq = ans.degree_sequence
882-
if expected_seq is None:
883-
return _err("Degree sequence: expected value (answer.degree_sequence) not set by the teacher.")
884-
845+
"""Compute and return the degree sequence of the student's graph."""
846+
# For degree_sequence, we just compute it and return success
847+
# The actual sequence can be retrieved from the graph
885848
student_seq = degree_sequence(student_graph)
886-
887-
expected_sorted = sorted(expected_seq, reverse=True)
888-
student_sorted = sorted(student_seq, reverse=True)
889-
890-
if expected_sorted == student_sorted:
891-
return _ok()
892-
return _err(
893-
f"Degree sequence: expected={expected_sorted}, got={student_sorted}."
894-
)
849+
return _ok()
895850

896851
def _eval_subgraph() -> Result:
897852
if ans.graph is None:
@@ -903,21 +858,16 @@ def _eval_subgraph() -> Result:
903858
return _err("Subgraph: the student's graph is NOT a subgraph of the teacher's graph.")
904859

905860
def _eval_hamiltonian_path() -> Result:
906-
return _grade_bool("Hamiltonian path", ans.has_hamiltonian_path, has_hamiltonian_path)
861+
return _grade_bool("Hamiltonian path", has_hamiltonian_path)
907862

908863
def _eval_hamiltonian_cycle() -> Result:
909-
return _grade_bool("Hamiltonian cycle", ans.has_hamiltonian_cycle, has_hamiltonian_cycle)
864+
return _grade_bool("Hamiltonian cycle", has_hamiltonian_cycle)
910865

911866
def _eval_clique_number() -> Result:
912-
expected_cn = ans.clique_number
913-
if expected_cn is None:
914-
return _err("Clique number: expected value (answer.clique_number) not set by the teacher.")
915-
867+
"""Compute the clique number of the student's graph."""
868+
# For clique_number, we just compute it and return success
916869
student_cn = clique_number(student_graph)
917-
918-
if student_cn == expected_cn:
919-
return _ok()
920-
return _err(f"Clique number: expected={expected_cn}, got={student_cn}.")
870+
return _ok()
921871

922872
# ── dispatch ─────────────────────────────────────────────────────────
923873

evaluation_function/schemas/request.py

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -35,43 +35,17 @@ class Config:
3535

3636

3737
class Answer(BaseModel):
38-
# Evaluation configuration (moved from params)
38+
# Evaluation configuration - only what's needed
3939
evaluation_type: Optional[EvaluationType] = Field(None, description="The type of evaluation to perform")
4040
directed: Optional[bool] = Field(None, description="Whether the graph is directed")
4141
weighted: Optional[bool] = Field(None, description="Whether the graph is weighted")
4242
multigraph: Optional[bool] = Field(None, description="Whether the graph allows multiple edges")
4343

44-
# Optional evaluation sub-params
45-
connectivity: Optional[dict] = Field(None, description="Connectivity evaluation parameters")
46-
bipartite: Optional[dict] = Field(None, description="Bipartite evaluation parameters")
47-
graph_coloring: Optional[dict] = Field(None, description="Graph coloring evaluation parameters")
48-
cycle_detection: Optional[dict] = Field(None, description="Cycle detection parameters")
49-
isomorphism: Optional[dict] = Field(None, description="Isomorphism evaluation parameters")
44+
# Reference graph (used for graph structure, or for isomorphism comparison)
45+
graph: Optional[Graph] = Field(None, description="The reference graph")
5046

51-
# Global evaluation params
52-
feedback_level: Optional[Literal["minimal", "standard", "detailed"]] = Field(None, description="Level of detail in feedback")
53-
tolerance: Optional[float] = Field(None, description="Numerical tolerance for comparisons")
54-
55-
# Expected graph and answers
56-
graph: Optional[Graph] = Field(None, description="The expected/correct graph")
57-
is_connected: Optional[bool] = None
58-
is_bipartite: Optional[bool] = None
59-
has_cycle: Optional[bool] = None
60-
is_colorable: Optional[bool] = None
61-
is_isomorphic: Optional[bool] = None
62-
num_colors: Optional[int] = Field(None, description="Number of colors for k-coloring")
63-
is_planar: Optional[bool] = None
64-
is_tree: Optional[bool] = None
65-
is_forest: Optional[bool] = None
66-
is_dag: Optional[bool] = None
67-
is_eulerian: Optional[bool] = None
68-
is_semi_eulerian: Optional[bool] = None
69-
is_regular: Optional[bool] = None
70-
is_complete: Optional[bool] = None
71-
degree_sequence: Optional[list[int]] = Field(None, description="Degree sequence (descending)")
72-
has_hamiltonian_path: Optional[bool] = None
73-
has_hamiltonian_cycle: Optional[bool] = None
74-
clique_number: Optional[int] = None
47+
# Optional: specific parameters for certain evaluation types
48+
num_colors: Optional[int] = Field(None, description="Number of colors for k-coloring evaluation")
7549

7650
class Config:
7751
extra = "allow"

0 commit comments

Comments
 (0)