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

Add reporter.TableCallback for direct retrieval of table output #66

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 7 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@

## [[unpublished]](https://github.com/mlange-42/arche-model/compare/v0.8.0...main)

### Breaking changes

* Renames `reporter.Callback` to `reporter.RowCallback` (#66)

### Features

* Adds option `Final` to `reporter.Callback` to report data once on finalize instead of on ticks (#65)
* Adds option `Final` to `reporter.RowCallback` to report data once on finalize instead of on ticks (#65)
* Adds `reporter.TableCallback` for direct retrieval of table observer output in Go code (#66)

## [[v0.8.1]](https://github.com/mlange-42/arche-model/compare/v0.8.0...v0.8.1)

Expand All @@ -16,7 +21,7 @@

### Features

* Adds `reporter.Callback` for direct retrieval of observer output in Go code (#61)
* Adds `reporter.Callback` for direct retrieval of row observer output in Go code (#61)

### Bugfixes

Expand Down
53 changes: 48 additions & 5 deletions reporter/callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import (
"github.com/mlange-42/arche/ecs"
)

// Callback reporter calling a function on each update.
type Callback struct {
// RowCallback reporter calling a function on each update, using an [observer.Row].
type RowCallback struct {
Observer observer.Row // Observer to get data from.
UpdateInterval int // Update interval in model ticks.
HeaderCallback func(header []string) // Called with the header of the observer during initialization.
Expand All @@ -16,7 +16,7 @@ type Callback struct {
}

// Initialize the system
func (s *Callback) Initialize(w *ecs.World) {
func (s *RowCallback) Initialize(w *ecs.World) {
s.Observer.Initialize(w)
if s.UpdateInterval == 0 {
s.UpdateInterval = 1
Expand All @@ -28,7 +28,7 @@ func (s *Callback) Initialize(w *ecs.World) {
}

// Update the system
func (s *Callback) Update(w *ecs.World) {
func (s *RowCallback) Update(w *ecs.World) {
s.Observer.Update(w)

if !s.Final && s.step%int64(s.UpdateInterval) == 0 {
Expand All @@ -40,7 +40,50 @@ func (s *Callback) Update(w *ecs.World) {
}

// Finalize the system
func (s *Callback) Finalize(w *ecs.World) {
func (s *RowCallback) Finalize(w *ecs.World) {
if !s.Final {
return
}
values := s.Observer.Values(w)
s.Callback(int(s.step), values)
}

// RowCallback reporter calling a function on each update, using an [observer.Table].
type TableCallback struct {
Observer observer.Table // Observer to get data from.
UpdateInterval int // Update interval in model ticks.
HeaderCallback func(header []string) // Called with the header of the observer during initialization.
Callback func(step int, table [][]float64) // Called with step and data table on each update (subject to UpdateInterval).
Final bool // Whether Callback should be called on finalization only, instead of on every tick.
step int64
}

// Initialize the system
func (s *TableCallback) Initialize(w *ecs.World) {
s.Observer.Initialize(w)
if s.UpdateInterval == 0 {
s.UpdateInterval = 1
}
if s.HeaderCallback != nil {
s.HeaderCallback(s.Observer.Header())
}
s.step = 0
}

// Update the system
func (s *TableCallback) Update(w *ecs.World) {
s.Observer.Update(w)

if !s.Final && s.step%int64(s.UpdateInterval) == 0 {
values := s.Observer.Values(w)
s.Callback(int(s.step), values)
}

s.step++
}

// Finalize the system
func (s *TableCallback) Finalize(w *ecs.World) {
if !s.Final {
return
}
Expand Down
52 changes: 48 additions & 4 deletions reporter/callback_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import (
"github.com/stretchr/testify/assert"
)

func ExampleCallback() {
func ExampleRowCallback() {
// Create a new model.
m := model.New()

data := [][]float64{}

// Add a Print reporter with an Observer.
m.AddSystem(&reporter.Callback{
m.AddSystem(&reporter.RowCallback{
Observer: &ExampleObserver{},
Callback: func(step int, row []float64) {
data = append(data, row)
Expand All @@ -36,11 +36,37 @@ func ExampleCallback() {
// [[1 2 3] [1 2 3] [1 2 3]]
}

func TestCallbackFinal(t *testing.T) {
func ExampleTableCallback() {
// Create a new model.
m := model.New()

data := [][]float64{}

// Add a Print reporter with an Observer.
m.AddSystem(&reporter.TableCallback{
Observer: &ExampleSnapshotObserver{},
Callback: func(step int, table [][]float64) {
data = append(data, table...)
},
HeaderCallback: func(header []string) {},
})

// Add a termination system that ends the simulation.
m.AddSystem(&system.FixedTermination{Steps: 3})

// Run the simulation.
m.Run()

fmt.Println(data)
// Output:
// [[1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]]
}

func TestRowCallbackFinal(t *testing.T) {
m := model.New()
counter := 0

m.AddSystem(&reporter.Callback{
m.AddSystem(&reporter.RowCallback{
Observer: &ExampleObserver{},
Callback: func(step int, row []float64) {
counter++
Expand All @@ -53,3 +79,21 @@ func TestCallbackFinal(t *testing.T) {

assert.Equal(t, 1, counter)
}

func TestTableCallbackFinal(t *testing.T) {
m := model.New()
counter := 0

m.AddSystem(&reporter.TableCallback{
Observer: &ExampleSnapshotObserver{},
Callback: func(step int, table [][]float64) {
counter++
},
HeaderCallback: func(header []string) {},
Final: true,
})
m.AddSystem(&system.FixedTermination{Steps: 3})
m.Run()

assert.Equal(t, 1, counter)
}
Loading