From 00d8fcf95b5aec58cb9903f2b5155ee18721a9a0 Mon Sep 17 00:00:00 2001 From: bruno-f-cruz <7049351+bruno-f-cruz@users.noreply.github.com> Date: Tue, 3 Dec 2024 02:56:07 -0800 Subject: [PATCH] Add first example --- .../src/bonsai/Extensions/Experiment.cs | 353 ++++++++++++++++++ .../src/json/experiment-example.json | 14 + .../src/json/experiment-schema.json | 58 +++ .../src/python/first_model.py | 47 +++ 4 files changed, 472 insertions(+) create mode 100644 _topics/_08_reproducible_research_practices/src/bonsai/Extensions/Experiment.cs create mode 100644 _topics/_08_reproducible_research_practices/src/json/experiment-example.json create mode 100644 _topics/_08_reproducible_research_practices/src/json/experiment-schema.json create mode 100644 _topics/_08_reproducible_research_practices/src/python/first_model.py diff --git a/_topics/_08_reproducible_research_practices/src/bonsai/Extensions/Experiment.cs b/_topics/_08_reproducible_research_practices/src/bonsai/Extensions/Experiment.cs new file mode 100644 index 0000000..b46a582 --- /dev/null +++ b/_topics/_08_reproducible_research_practices/src/bonsai/Extensions/Experiment.cs @@ -0,0 +1,353 @@ +//---------------------- +// +// Generated using the NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0) (http://NJsonSchema.org) +// +//---------------------- + + +namespace Experiment +{ + #pragma warning disable // Disable all warnings + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.4.0.0 (Newtonsoft.Json v13.0.0.0, YamlDotNet v13.0.0.0)")] + [Bonsai.CombinatorAttribute()] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + public partial class Trial + { + + private double _interTrialInterval = 1D; + + private int _rewardAmount = 1; + + public Trial() + { + } + + protected Trial(Trial other) + { + _interTrialInterval = other._interTrialInterval; + _rewardAmount = other._rewardAmount; + } + + /// + /// Interval between trials in seconds + /// + [Newtonsoft.Json.JsonPropertyAttribute("inter_trial_interval")] + [YamlDotNet.Serialization.YamlMemberAttribute(Alias="inter_trial_interval")] + [System.ComponentModel.DescriptionAttribute("Interval between trials in seconds")] + public double InterTrialInterval + { + get + { + return _interTrialInterval; + } + set + { + _interTrialInterval = value; + } + } + + /// + /// Amount of reward given to the animal + /// + [Newtonsoft.Json.JsonPropertyAttribute("reward_amount")] + [YamlDotNet.Serialization.YamlMemberAttribute(Alias="reward_amount")] + [System.ComponentModel.DescriptionAttribute("Amount of reward given to the animal")] + public int RewardAmount + { + get + { + return _rewardAmount; + } + set + { + _rewardAmount = value; + } + } + + public System.IObservable Process() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new Trial(this))); + } + + public System.IObservable Process(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new Trial(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("inter_trial_interval = " + _interTrialInterval + ", "); + stringBuilder.Append("reward_amount = " + _rewardAmount); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(" "); + } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.4.0.0 (Newtonsoft.Json v13.0.0.0, YamlDotNet v13.0.0.0)")] + [Bonsai.CombinatorAttribute()] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Source)] + public partial class Experiment + { + + private string _animalId; + + private System.Collections.Generic.List _trials = new System.Collections.Generic.List(); + + private int? _rngSeed; + + public Experiment() + { + } + + protected Experiment(Experiment other) + { + _animalId = other._animalId; + _trials = other._trials; + _rngSeed = other._rngSeed; + } + + /// + /// ID of the animal + /// + [Newtonsoft.Json.JsonPropertyAttribute("animal_id", Required=Newtonsoft.Json.Required.Always)] + [YamlDotNet.Serialization.YamlMemberAttribute(Alias="animal_id")] + [System.ComponentModel.DescriptionAttribute("ID of the animal")] + public string AnimalId + { + get + { + return _animalId; + } + set + { + _animalId = value; + } + } + + /// + /// List of trials in the experiment + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + [Newtonsoft.Json.JsonPropertyAttribute("trials", Required=Newtonsoft.Json.Required.Always)] + [YamlDotNet.Serialization.YamlMemberAttribute(Alias="trials")] + [System.ComponentModel.DescriptionAttribute("List of trials in the experiment")] + public System.Collections.Generic.List Trials + { + get + { + return _trials; + } + set + { + _trials = value; + } + } + + /// + /// Seed for the random number generator + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + [Newtonsoft.Json.JsonPropertyAttribute("rng_seed")] + [YamlDotNet.Serialization.YamlMemberAttribute(Alias="rng_seed")] + [System.ComponentModel.DescriptionAttribute("Seed for the random number generator")] + public int? RngSeed + { + get + { + return _rngSeed; + } + set + { + _rngSeed = value; + } + } + + public System.IObservable Process() + { + return System.Reactive.Linq.Observable.Defer(() => System.Reactive.Linq.Observable.Return(new Experiment(this))); + } + + public System.IObservable Process(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, _ => new Experiment(this)); + } + + protected virtual bool PrintMembers(System.Text.StringBuilder stringBuilder) + { + stringBuilder.Append("animal_id = " + _animalId + ", "); + stringBuilder.Append("trials = " + _trials + ", "); + stringBuilder.Append("rng_seed = " + _rngSeed); + return true; + } + + public override string ToString() + { + System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(); + stringBuilder.Append(GetType().Name); + stringBuilder.Append(" { "); + if (PrintMembers(stringBuilder)) + { + stringBuilder.Append(" "); + } + stringBuilder.Append("}"); + return stringBuilder.ToString(); + } + } + + + /// + /// Serializes a sequence of data model objects into JSON strings. + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.4.0.0 (Newtonsoft.Json v13.0.0.0, YamlDotNet v13.0.0.0)")] + [System.ComponentModel.DescriptionAttribute("Serializes a sequence of data model objects into JSON strings.")] + [Bonsai.CombinatorAttribute()] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Transform)] + public partial class SerializeToJson + { + + private System.IObservable Process(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, value => Newtonsoft.Json.JsonConvert.SerializeObject(value)); + } + + public System.IObservable Process(System.IObservable source) + { + return Process(source); + } + + public System.IObservable Process(System.IObservable source) + { + return Process(source); + } + } + + + /// + /// Deserializes a sequence of JSON strings into data model objects. + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.4.0.0 (Newtonsoft.Json v13.0.0.0, YamlDotNet v13.0.0.0)")] + [System.ComponentModel.DescriptionAttribute("Deserializes a sequence of JSON strings into data model objects.")] + [System.ComponentModel.DefaultPropertyAttribute("Type")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Transform)] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + public partial class DeserializeFromJson : Bonsai.Expressions.SingleArgumentExpressionBuilder + { + + public DeserializeFromJson() + { + Type = new Bonsai.Expressions.TypeMapping(); + } + + public Bonsai.Expressions.TypeMapping Type { get; set; } + + public override System.Linq.Expressions.Expression Build(System.Collections.Generic.IEnumerable arguments) + { + var typeMapping = (Bonsai.Expressions.TypeMapping)Type; + var returnType = typeMapping.GetType().GetGenericArguments()[0]; + return System.Linq.Expressions.Expression.Call( + typeof(DeserializeFromJson), + "Process", + new System.Type[] { returnType }, + System.Linq.Enumerable.Single(arguments)); + } + + private static System.IObservable Process(System.IObservable source) + { + return System.Reactive.Linq.Observable.Select(source, value => Newtonsoft.Json.JsonConvert.DeserializeObject(value)); + } + } + + + /// + /// Serializes a sequence of data model objects into YAML strings. + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.4.0.0 (Newtonsoft.Json v13.0.0.0, YamlDotNet v13.0.0.0)")] + [System.ComponentModel.DescriptionAttribute("Serializes a sequence of data model objects into YAML strings.")] + [Bonsai.CombinatorAttribute()] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Transform)] + public partial class SerializeToYaml + { + + private System.IObservable Process(System.IObservable source) + { + return System.Reactive.Linq.Observable.Defer(() => + { + var serializer = new YamlDotNet.Serialization.SerializerBuilder() + .Build(); + return System.Reactive.Linq.Observable.Select(source, value => serializer.Serialize(value)); + }); + } + + public System.IObservable Process(System.IObservable source) + { + return Process(source); + } + + public System.IObservable Process(System.IObservable source) + { + return Process(source); + } + } + + + /// + /// Deserializes a sequence of YAML strings into data model objects. + /// + [System.CodeDom.Compiler.GeneratedCodeAttribute("Bonsai.Sgen", "0.4.0.0 (Newtonsoft.Json v13.0.0.0, YamlDotNet v13.0.0.0)")] + [System.ComponentModel.DescriptionAttribute("Deserializes a sequence of YAML strings into data model objects.")] + [System.ComponentModel.DefaultPropertyAttribute("Type")] + [Bonsai.WorkflowElementCategoryAttribute(Bonsai.ElementCategory.Transform)] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + [System.Xml.Serialization.XmlIncludeAttribute(typeof(Bonsai.Expressions.TypeMapping))] + public partial class DeserializeFromYaml : Bonsai.Expressions.SingleArgumentExpressionBuilder + { + + public DeserializeFromYaml() + { + Type = new Bonsai.Expressions.TypeMapping(); + } + + public Bonsai.Expressions.TypeMapping Type { get; set; } + + public override System.Linq.Expressions.Expression Build(System.Collections.Generic.IEnumerable arguments) + { + var typeMapping = (Bonsai.Expressions.TypeMapping)Type; + var returnType = typeMapping.GetType().GetGenericArguments()[0]; + return System.Linq.Expressions.Expression.Call( + typeof(DeserializeFromYaml), + "Process", + new System.Type[] { returnType }, + System.Linq.Enumerable.Single(arguments)); + } + + private static System.IObservable Process(System.IObservable source) + { + return System.Reactive.Linq.Observable.Defer(() => + { + var serializer = new YamlDotNet.Serialization.DeserializerBuilder() + .Build(); + return System.Reactive.Linq.Observable.Select(source, value => + { + var reader = new System.IO.StringReader(value); + var parser = new YamlDotNet.Core.MergingParser(new YamlDotNet.Core.Parser(reader)); + return serializer.Deserialize(parser); + }); + }); + } + } +} \ No newline at end of file diff --git a/_topics/_08_reproducible_research_practices/src/json/experiment-example.json b/_topics/_08_reproducible_research_practices/src/json/experiment-example.json new file mode 100644 index 0000000..c12af00 --- /dev/null +++ b/_topics/_08_reproducible_research_practices/src/json/experiment-example.json @@ -0,0 +1,14 @@ +{ + "animal_id": "my_mouse", + "trials": [ + { + "inter_trial_interval": 1.0, + "reward_amount": 1 + }, + { + "inter_trial_interval": 0.5, + "reward_amount": 0 + } + ], + "rng_seed": null +} \ No newline at end of file diff --git a/_topics/_08_reproducible_research_practices/src/json/experiment-schema.json b/_topics/_08_reproducible_research_practices/src/json/experiment-schema.json new file mode 100644 index 0000000..2c680af --- /dev/null +++ b/_topics/_08_reproducible_research_practices/src/json/experiment-schema.json @@ -0,0 +1,58 @@ +{ + "definitions": { + "Trial": { + "properties": { + "inter_trial_interval": { + "default": 1.0, + "description": "Interval between trials in seconds", + "minimum": 0.0, + "title": "Inter Trial Interval", + "type": "number" + }, + "reward_amount": { + "default": 1, + "description": "Amount of reward given to the animal", + "minimum": 0, + "title": "Reward Amount", + "type": "integer" + } + }, + "title": "Trial", + "type": "object" + } + }, + "properties": { + "animal_id": { + "description": "ID of the animal", + "title": "Animal Id", + "type": "string" + }, + "trials": { + "description": "List of trials in the experiment", + "items": { + "$ref": "#/definitions/Trial" + }, + "title": "Trials", + "type": "array" + }, + "rng_seed": { + "default": null, + "description": "Seed for the random number generator", + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ], + "title": "Rng Seed" + } + }, + "required": [ + "animal_id", + "trials" + ], + "title": "Experiment", + "type": "object" +} \ No newline at end of file diff --git a/_topics/_08_reproducible_research_practices/src/python/first_model.py b/_topics/_08_reproducible_research_practices/src/python/first_model.py new file mode 100644 index 0000000..f84178b --- /dev/null +++ b/_topics/_08_reproducible_research_practices/src/python/first_model.py @@ -0,0 +1,47 @@ +from pydantic import BaseModel, Field +from typing import List, Optional +from _utils import export_schema, bonsai_sgen, BonsaiSgenSerializers +from pathlib import Path + + +class Trial(BaseModel): + inter_trial_interval: float = Field( + default=1.0, ge=0, description="Interval between trials in seconds" + ) + reward_amount: int = Field( + default=1, ge=0, description="Amount of reward given to the animal" + ) + + +class Experiment(BaseModel): + animal_id: str = Field(description="ID of the animal") + trials: List[Trial] = Field(description="List of trials in the experiment") + rng_seed: Optional[int] = Field( + default=None, description="Seed for the random number generator" + ) + + +if __name__ == "__main__": + json_schema = export_schema(Experiment) + name = (Experiment.__name__).lower() + schema_path = Path(rf"src/json/{name}-schema.json") + with open(schema_path, "w", encoding="utf-8") as f: + f.write(json_schema) + + bonsai_sgen( + schema_path=schema_path, + output_path=Path(rf"src/bonsai/Extensions/{name.capitalize()}.cs"), + namespace=name.capitalize(), + serializer=[BonsaiSgenSerializers.JSON, BonsaiSgenSerializers.YAML], + ) + + experiment_example = Experiment( + animal_id="my_mouse", + trials=[ + Trial(inter_trial_interval=1.0, reward_amount=1), + Trial(inter_trial_interval=0.5, reward_amount=0), + ], + ) + + with open(rf"src/json/{name}-example.json", "w", encoding="utf-8") as f: + f.write(experiment_example.model_dump_json(indent=2))